Ignore:
Timestamp:
05/01/10 09:45:34 (11 years ago)
Author:
pjkersha
Message:
  • Refactored PEP result handler code from authz into separate ndg.security.server.wsgi.authz.result_handler package
  • Refactored session handling classes from ndg.security.server.wsgi.authn to new ndg.security.server.wsgi.session module
File:
1 edited

Legend:

Unmodified
Added
Removed
  • TI12-security/trunk/NDGSecurity/python/ndg_security_server/ndg/security/server/wsgi/authn.py

    r6246 r6264  
    2222from paste.request import construct_url, parse_querystring 
    2323import authkit.authenticate 
    24  
    25 from ndg.security.server.wsgi import NDGSecurityMiddlewareBase, \ 
    26     NDGSecurityMiddlewareError, NDGSecurityMiddlewareConfigError         
     24from authkit.authenticate.multi import MultiHandler 
     25 
     26from ndg.security.server.wsgi import (NDGSecurityMiddlewareBase,  
     27                                      NDGSecurityMiddlewareError,  
     28                                      NDGSecurityMiddlewareConfigError)  
     29from ndg.security.server.wsgi.session import (SessionMiddlewareBase, 
     30                                              SessionHandlerMiddleware)   
     31 
    2732 
    2833class AuthnException(NDGSecurityMiddlewareError): 
     
    4449     
    4550class HTTPBasicAuthMiddleware(NDGSecurityMiddlewareBase): 
    46     '''HTTP Basic Authentication Middleware 
    47      
     51    '''HTTP Basic Authentication Middleware  
    4852    ''' 
    4953     
     
    178182# AuthKit based HTTP basic authentication plugin not currently needed but may  
    179183# need resurrecting 
    180  
    181 #from authkit.permissions import UserIn 
    182 #from ndg.security.server.wsgi.utils.sessionmanagerclient import \ 
    183 #    WSGISessionManagerClient 
    184 #             
    185 #class HTTPBasicAuthentication(object): 
    186 #    '''Authkit based HTTP Basic Authentication.   __call__ defines a  
    187 #    validation function to fit with the pattern for the AuthKit interface 
    188 #    ''' 
    189 #     
    190 #    def __init__(self): 
    191 #        self._userIn = UserIn([]) 
    192 #         
    193 #    def __call__(self, environ, username, password): 
    194 #        """validation function""" 
    195 #        try: 
    196 #            client = WSGISessionManagerClient(environ=environ, 
    197 #                                environKeyName=self.sessionManagerFilterID) 
    198 #            res = client.connect(username, passphrase=password) 
    199 # 
    200 #            if username not in self._userIn.users: 
    201 #                self._userIn.users += [username] 
    202 #             
    203 #            # TODO: set session 
    204 #                 
    205 #        except Exception, e: 
    206 #            return False 
    207 #        else: 
    208 #            return True 
    209  
    210  
    211 class SessionMiddlewareBase(NDGSecurityMiddlewareBase): 
    212     """Base class for Authentication redirect middleware and Session Handler 
    213     middleware 
    214      
    215     @type propertyDefaults: dict 
    216     @cvar propertyDefaults: valid configuration property keywords  
    217     """    
    218     propertyDefaults = { 
    219         'sessionKey': 'beaker.session.ndg.security' 
    220     } 
    221     
    222     _isAuthenticated = lambda self: \ 
    223         SessionMiddlewareBase.USERNAME_SESSION_KEYNAME in \ 
    224         self.environ.get(self.sessionKey, ()) 
    225          
    226     isAuthenticated = property(fget=_isAuthenticated, 
    227                                doc='boolean to indicate is user logged in') 
     184from authkit.permissions import UserIn 
     185             
     186class HTTPBasicAuthentication(object): 
     187    '''Authkit based HTTP Basic Authentication.   __call__ defines a  
     188    validation function to fit with the pattern for the AuthKit interface 
     189    ''' 
     190     
     191    def __init__(self): 
     192        self._userIn = UserIn([]) 
     193         
     194    def __call__(self, environ, username, password): 
     195        """AuthKit HTTP Basic Auth validation function - return True/False""" 
     196        raise NotImplementedError() 
    228197 
    229198         
     
    253222    propertyDefaults.update(AuthnRedirectMiddleware.propertyDefaults) 
    254223     
    255  
    256224    TRIGGER_HTTP_STATUS_CODE = '401' 
    257225    MIDDLEWARE_ID = 'AuthnRedirectInitiatorMiddleware' 
     
    269237        dictionary 
    270238        ''' 
    271         self._redirectURI = None 
     239        self.__redirectURI = None 
    272240        super(AuthnRedirectInitiatorMiddleware, self).__init__(app,  
    273241                                                               global_conf,  
    274242                                                               **app_conf) 
    275          
     243 
    276244    @NDGSecurityMiddlewareBase.initCall 
    277245    def __call__(self, environ, start_response): 
     
    292260            raise TypeError("Redirect URI must be set to string type")    
    293261          
    294         self._redirectURI = uri 
     262        self.__redirectURI = uri 
    295263         
    296264    def _getRedirectURI(self): 
    297         return self._redirectURI 
     265        return self.__redirectURI 
    298266     
    299267    redirectURI = property(fget=_getRedirectURI, 
    300                        fset=_setRedirectURI, 
    301                        doc="URI to redirect to if user is not authenticated") 
     268                           fset=_setRedirectURI, 
     269                           doc="URI to redirect to if user is not " 
     270                               "authenticated") 
    302271 
    303272    def _setRedirectResponse(self): 
     
    404373        return super(AuthKitRedirectResponseMiddleware, self).__call__(environ, 
    405374                                                                start_response) 
    406         
    407  
    408 class SessionHandlerMiddlewareError(AuthnException): 
    409     """Base exception for SessionHandlerMiddleware""" 
    410              
    411 class SessionHandlerMiddlewareConfigError(SessionHandlerMiddlewareError): 
    412     """Configuration errors from SessionHandlerMiddleware""" 
    413      
    414 class OpenIdAXConfigError(SessionHandlerMiddlewareError): 
    415     """Error parsing OpenID Ax (Attribute Exchange) parameters""" 
    416      
    417      
    418 class SessionHandlerMiddleware(SessionMiddlewareBase): 
    419     '''Middleware to: 
    420     - establish user session details following redirect from OpenID Relying  
    421     Party sign-in or SSL Client authentication 
    422     - end session redirecting back to referrer URI following call to a logout 
    423     URI as implemented in AuthKit 
    424     ''' 
    425     AX_SESSION_KEYNAME = 'openid.ax' 
    426     SM_URI_SESSION_KEYNAME = 'sessionManagerURI' 
    427     ID_SESSION_KEYNAME = 'sessionId' 
    428     PEP_CTX_SESSION_KEYNAME = 'pepCtx' 
    429     CREDENTIAL_WALLET_SESSION_KEYNAME = 'credentialWallet' 
    430      
    431     SESSION_KEYNAMES = ( 
    432         SessionMiddlewareBase.USERNAME_SESSION_KEYNAME,  
    433         SM_URI_SESSION_KEYNAME,  
    434         ID_SESSION_KEYNAME,  
    435         PEP_CTX_SESSION_KEYNAME,  
    436         CREDENTIAL_WALLET_SESSION_KEYNAME 
    437     ) 
    438      
    439     AX_KEYNAME = 'ax' 
    440     SM_URI_AX_KEYNAME = 'value.sessionManagerURI.1' 
    441     SESSION_ID_AX_KEYNAME = 'value.sessionId.1' 
    442      
    443     AUTHKIT_COOKIE_SIGNOUT_PARAMNAME = 'authkit.cookie.signoutpath' 
    444     SIGNOUT_PATH_PARAMNAME = 'signoutPath' 
    445     SESSION_KEY_PARAMNAME = 'sessionKey' 
    446     propertyDefaults = { 
    447         SIGNOUT_PATH_PARAMNAME: None, 
    448         SESSION_KEY_PARAMNAME: 'beaker.session.ndg.security' 
    449     } 
    450      
    451     AUTH_TKT_SET_USER_ENVIRON_KEYNAME = 'paste.auth_tkt.set_user' 
    452      
    453     PARAM_PREFIX = 'sessionHandler.' 
    454      
    455     def __init__(self, app, global_conf, prefix=PARAM_PREFIX, **app_conf): 
    456         ''' 
    457         @type app: callable following WSGI interface 
    458         @param app: next middleware application in the chain       
    459         @type global_conf: dict         
    460         @param global_conf: PasteDeploy global configuration dictionary 
    461         @type prefix: basestring 
    462         @param prefix: prefix for configuration items 
    463         @type app_conf: dict         
    464         @param app_conf: PasteDeploy application specific configuration  
    465         dictionary 
    466         ''' 
    467         signoutPathParamName = prefix + \ 
    468                                 SessionHandlerMiddleware.SIGNOUT_PATH_PARAMNAME 
    469          
    470         if signoutPathParamName not in app_conf: 
    471             authKitSignOutPath = app_conf.get( 
    472                     SessionHandlerMiddleware.AUTHKIT_COOKIE_SIGNOUT_PARAMNAME) 
    473              
    474             if authKitSignOutPath: 
    475                 app_conf[signoutPathParamName] = authKitSignOutPath 
    476                  
    477                 log.info('Set signoutPath=%s from "%s" setting',  
    478                      authKitSignOutPath, 
    479                      SessionHandlerMiddleware.AUTHKIT_COOKIE_SIGNOUT_PARAMNAME) 
    480             else: 
    481                 raise SessionHandlerMiddlewareConfigError( 
    482                                         '"signoutPath" parameter is not set') 
    483              
    484         super(SessionHandlerMiddleware, self).__init__(app, 
    485                                                        global_conf, 
    486                                                        prefix=prefix,  
    487                                                        **app_conf) 
    488          
    489     @NDGSecurityMiddlewareBase.initCall 
    490     def __call__(self, environ, start_response): 
    491         """Manage setting of session from AuthKit following OpenID Relying 
    492         Party sign in and manage logout 
    493          
    494         @type environ: dict 
    495         @param environ: WSGI environment variables dictionary 
    496         @type start_response: function 
    497         @param start_response: standard WSGI start response function 
    498         """ 
    499         log.debug("SessionHandlerMiddleware.__call__ ...") 
    500          
    501         session = environ.get(self.sessionKey) 
    502         if session is None: 
    503             raise SessionHandlerMiddlewareConfigError( 
    504                    'SessionHandlerMiddleware.__call__: No beaker session key ' 
    505                    '"%s" found in environ' % self.sessionKey) 
    506          
    507         if self.signoutPath and self.pathInfo == self.signoutPath: 
    508             log.debug("SessionHandlerMiddleware.__call__: caught sign out " 
    509                       "path [%s]", self.signoutPath) 
    510              
    511             referrer = environ.get('HTTP_REFERER') 
    512             if referrer is not None: 
    513                 def _start_response(status, header, exc_info=None): 
    514                     """Alter the header to send a redirect to the logout 
    515                     referrer address""" 
    516                     filteredHeader = [(field, val) for field, val in header  
    517                                       if field.lower() != 'location']         
    518                     filteredHeader.extend([('Location', referrer)]) 
    519                     return start_response(self.getStatusMessage(302),  
    520                                           filteredHeader, 
    521                                           exc_info) 
    522                      
    523             else: 
    524                 log.error('No referrer set for redirect following logout') 
    525                 _start_response = start_response 
    526                  
    527             # Clear user details from beaker session 
    528             for keyName in self.__class__.SESSION_KEYNAMES: 
    529                 session.pop(keyName, None) 
    530             session.save() 
    531         else: 
    532             log.debug("SessionHandlerMiddleware.__call__: checking for " 
    533                       "REMOTE_* environment variable settings set by OpenID " 
    534                       "Relying Party signin...") 
    535              
    536             if SessionHandlerMiddleware.USERNAME_SESSION_KEYNAME not in session\ 
    537                and SessionHandlerMiddleware.USERNAME_ENVIRON_KEYNAME in environ: 
    538                 log.debug("SessionHandlerMiddleware.__call__: updating session " 
    539                           "username=%s", environ[ 
    540                             SessionHandlerMiddleware.USERNAME_ENVIRON_KEYNAME]) 
    541                  
    542                 session[SessionHandlerMiddleware.USERNAME_SESSION_KEYNAME 
    543                         ] = environ[ 
    544                             SessionHandlerMiddleware.USERNAME_ENVIRON_KEYNAME] 
    545                 session.save() 
    546                  
    547             remoteUserData = environ.get( 
    548                         SessionHandlerMiddleware.USERDATA_ENVIRON_KEYNAME, '')     
    549             if remoteUserData: 
    550                 log.debug("SessionHandlerMiddleware.__call__: found " 
    551                           "REMOTE_USER_DATA=%s, set from OpenID Relying Party " 
    552                           "signin",  
    553                           environ[ 
    554                               SessionHandlerMiddleware.USERDATA_ENVIRON_KEYNAME 
    555                           ]) 
    556                  
    557                 # eval is safe here because AuthKit cookie is signed and  
    558                 # AuthKit middleware checks for tampering 
    559                 if (SessionHandlerMiddleware.SM_URI_SESSION_KEYNAME not in  
    560                     session or  
    561                     SessionHandlerMiddleware.ID_SESSION_KEYNAME not in session): 
    562                      
    563                     axData = eval(remoteUserData) 
    564                     if (isinstance(axData, dict) and  
    565                         SessionHandlerMiddleware.AX_KEYNAME in axData): 
    566                          
    567                         ax = axData[SessionHandlerMiddleware.AX_KEYNAME] 
    568                          
    569                         # Save attributes keyed by attribute name 
    570                         session[ 
    571                             SessionHandlerMiddleware.AX_SESSION_KEYNAME 
    572                         ] = SessionHandlerMiddleware._parseOpenIdAX(ax) 
    573                          
    574                         log.debug("SessionHandlerMiddleware.__call__: updated " 
    575                                   "session with OpenID AX values: %r" %  
    576                                   session[ 
    577                                     SessionHandlerMiddleware.AX_SESSION_KEYNAME 
    578                                   ]) 
    579                              
    580                         # Save Session Manager specific attributes 
    581                         sessionManagerURI = ax.get( 
    582                                 SessionHandlerMiddleware.SM_URI_AX_KEYNAME) 
    583                              
    584                         session[SessionHandlerMiddleware.SM_URI_SESSION_KEYNAME 
    585                                 ] = sessionManagerURI 
    586      
    587                         sessionId = ax.get( 
    588                                 SessionHandlerMiddleware.SESSION_ID_AX_KEYNAME) 
    589                         session[SessionHandlerMiddleware.ID_SESSION_KEYNAME 
    590                                 ] = sessionId 
    591                                  
    592                         session.save() 
    593                          
    594                         log.debug("SessionHandlerMiddleware.__call__: updated " 
    595                                   "session " 
    596                                   "with sessionManagerURI=%s and " 
    597                                   "sessionId=%s",  
    598                                   sessionManagerURI,  
    599                                   sessionId) 
    600                      
    601                 # Reset cookie removing user data 
    602                 setUser = environ[ 
    603                     SessionHandlerMiddleware.AUTH_TKT_SET_USER_ENVIRON_KEYNAME] 
    604                 setUser( 
    605                     session[SessionHandlerMiddleware.USERNAME_SESSION_KEYNAME]) 
    606             else: 
    607                 log.debug("SessionHandlerMiddleware.__call__: REMOTE_USER_DATA " 
    608                           "is not set") 
    609  
    610             _start_response = start_response 
    611              
    612         return self._app(environ, _start_response) 
    613      
    614     @staticmethod                     
    615     def _parseOpenIdAX(ax): 
    616         """Return a dictionary of attribute exchange attributes parsed from the  
    617         OpenID Provider response set in the REMOTE_USER_DATA AuthKit environ 
    618         key 
    619          
    620         @param ax: dictionary of AX parameters - format of keys is e.g. 
    621         count.paramName, value.paramName.<n>, type.paramName 
    622         @type ax: dict 
    623         @return: dictionary of parameters keyed by parameter with values for 
    624         each parameter a tuple of count.paramName values 
    625         @rtype: dict 
    626         """ 
    627          
    628         # Copy Attributes into session 
    629         outputKeys = [k.replace('type.', '') for k in ax.keys() 
    630                       if k.startswith('type.')] 
    631          
    632         output = {} 
    633         for outputKey in outputKeys: 
    634             axCountKeyName = 'count.' + outputKey 
    635             axCount = int(ax[axCountKeyName]) 
    636              
    637             axValueKeyPrefix = 'value.%s.' % outputKey 
    638             output[outputKey] = tuple([v for k, v in ax.items()  
    639                                        if k.startswith(axValueKeyPrefix)]) 
    640              
    641             nVals = len(output[outputKey]) 
    642             if nVals != axCount: 
    643                 raise OpenIdAXConfigError('Got %d parameters for AX attribute ' 
    644                                           '"%s"; but "%s" AX key is set to %d' 
    645                                           % (nVals, 
    646                                              axCountKeyName, 
    647                                              axCountKeyName, 
    648                                              axCount)) 
    649                                               
    650         return output 
    651  
    652  
    653 from authkit.authenticate.multi import MultiHandler 
     375 
    654376 
    655377class AuthenticationMiddlewareConfigError(NDGSecurityMiddlewareConfigError): 
Note: See TracChangeset for help on using the changeset viewer.