source: TI12-security/trunk/python/ndg_security_server/ndg/security/server/wsgi/authn.py @ 5838

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/ndg_security_server/ndg/security/server/wsgi/authn.py@5838
Revision 5838, 27.1 KB checked in by pjkersha, 10 years ago (diff)

Unit tested HTTPBasicAuthMiddleware. This will be used together MyProxyClientMiddleware? to make a myproxy-logon web service interface.

Line 
1"""Module containing:
2 * HTTP Basic Authentication Middleware
3 * middleware to enable redirection to OpenID Relying Party for login
4 * logout middleware for deleting AuthKit cookie and redirecting back to
5   referrer
6 
7NERC DataGrid Project
8"""
9__author__ = "P J Kershaw"
10__date__ = "13/01/09"
11__copyright__ = "(C) 2009 Science and Technology Facilities Council"
12__license__ = "BSD - see LICENSE file in top-level directory"
13__contact__ = "Philip.Kershaw@stfc.ac.uk"
14__revision__ = "$Id$"
15import logging
16log = logging.getLogger(__name__)
17
18import base64
19import httplib
20import urllib
21from urlparse import urlsplit
22from paste.request import construct_url
23from paste.request import parse_querystring
24from beaker.middleware import SessionMiddleware
25import authkit.authenticate
26
27from ndg.security.server.wsgi import NDGSecurityMiddlewareBase, \
28    NDGSecurityMiddlewareError, NDGSecurityMiddlewareConfigError       
29
30class AuthnException(NDGSecurityMiddlewareError):
31    """Base exception for this module"""
32   
33   
34class HTTPBasicAuthMiddlewareError(AuthnException):
35    """Base exception type for HTTPBasicAuthMiddleware"""
36   
37   
38class HTTPBasicAuthMiddlewareConfigError(NDGSecurityMiddlewareConfigError):
39    """Configuration error with HTTP Basic Auth middleware"""
40
41
42class HTTPBasicAuthUnauthorized(HTTPBasicAuthMiddlewareError): 
43    """Raise from custom authentication interface in order to set HTTP
44    401 Unuathorized response"""
45   
46   
47class HTTPBasicAuthMiddleware(NDGSecurityMiddlewareBase):
48    '''HTTP Basic Authentication Middleware
49   
50    '''
51   
52    AUTHN_FUNC_ENV_KEYNAME = ('ndg.security.server.wsgi.authn.'
53                              'HTTPBasicAuthMiddleware.authenticate')
54    AUTHN_FUNC_ENV_KEYNAME_PARAMNAME = 'authnFunc'       
55    PARAM_PREFIX = 'http.auth.basic'
56    HTTP_HDR_FIELDNAME = 'basic'
57    FIELD_SEP = ':'
58    AUTHZ_ENV_KEYNAME = 'HTTP_AUTHORIZATION'
59   
60    def __init__(self, app, app_conf, prefix=PARAM_PREFIX, **local_conf):
61        self.__authnFuncEnvironKeyName = None
62       
63        super(HTTPBasicAuthMiddleware, self).__init__(app, app_conf, 
64                                                      **local_conf)
65        paramName = prefix + \
66                    HTTPBasicAuthMiddleware.AUTHN_FUNC_ENV_KEYNAME_PARAMNAME
67                   
68        self.authnFuncEnvironKeyName = local_conf.get(paramName,
69                                HTTPBasicAuthMiddleware.AUTHN_FUNC_ENV_KEYNAME)
70
71    def _getAuthnFuncEnvironKeyName(self):
72        return self.__authnFuncEnvironKeyName
73
74    def _setAuthnFuncEnvironKeyName(self, value):
75        if not isinstance(value, basestring):
76            raise TypeError('Expecting string type for '
77                            '"authnFuncEnvironKeyName"; got %r type' % 
78                            type(value))
79        self.__authnFuncEnvironKeyName = value
80
81    authnFuncEnvironKeyName = property(fget=_getAuthnFuncEnvironKeyName, 
82                                       fset=_setAuthnFuncEnvironKeyName, 
83                                       doc="key name in environ for the "
84                                           "custom authentication function "
85                                           "used by this class")
86
87    def _parseCredentials(self):
88        """Extract username and password from HTTP_AUTHORIZATION environ key
89       
90        @rtype: tuple
91        @return: username and password.  If the key is not set or the auth
92        method is not basic return a two element tuple with elements both set
93        to None
94        """
95        basicAuthHdr = self.environ.get(
96                                    HTTPBasicAuthMiddleware.AUTHZ_ENV_KEYNAME)
97        if basicAuthHdr is None:
98            log.debug("No %r setting in environ: skipping HTTP Basic Auth",
99                      HTTPBasicAuthMiddleware.AUTHZ_ENV_KEYNAME)
100            return None, None
101                       
102        method, encodedCreds = basicAuthHdr.split(None, 1)
103        if method.lower() != HTTPBasicAuthMiddleware.HTTP_HDR_FIELDNAME:
104            log.debug("Auth method is %r not %r: skipping request",
105                      method, HTTPBasicAuthMiddleware.HTTP_HDR_FIELDNAME)
106            return None, None
107           
108        creds = base64.decodestring(encodedCreds)
109        username, password = creds.split(HTTPBasicAuthMiddleware.FIELD_SEP, 1)
110        return username, password
111
112    @NDGSecurityMiddlewareBase.initCall
113    def __call__(self, environ, start_response):
114        """Authenticate based HTTP header elements as specified by the HTTP
115        Basic Authentication spec."""
116        log.debug("HTTPBasicAuthNMiddleware.__call__ ...")
117       
118        authenticate = environ.get(self.authnFuncEnvironKeyName)
119        if authenticate is None:
120            # HTTP 500 default is right for this error
121            raise HTTPBasicAuthMiddlewareConfigError("No authentication "
122                                                     "function set in environ")
123           
124        username, password = self._parseCredentials()
125        if username is None:
126            return self._setErrorResponse(code=httplib.UNAUTHORIZED)
127       
128        try:
129            authenticate(environ, username, password)
130               
131        except HTTPBasicAuthUnauthorized, e:
132            log.error(e)
133            return self._setErrorResponse(code=httplib.UNAUTHORIZED)
134        else:
135            return self._app(environ, start_response)
136
137
138# AuthKit based HTTP basic authentication plugin not currently needed but may
139# need resurrecting
140
141#from authkit.permissions import UserIn
142#from ndg.security.server.wsgi.utils.sessionmanagerclient import \
143#    WSGISessionManagerClient
144#           
145#class HTTPBasicAuthentication(object):
146#    '''Authkit based HTTP Basic Authentication.   __call__ defines a
147#    validation function to fit with the pattern for the AuthKit interface
148#    '''
149#   
150#    def __init__(self):
151#        self._userIn = UserIn([])
152#       
153#    def __call__(self, environ, username, password):
154#        """validation function"""
155#        try:
156#            client = WSGISessionManagerClient(environ=environ,
157#                                environKeyName=self.sessionManagerFilterID)
158#            res = client.connect(username, passphrase=password)
159#
160#            if username not in self._userIn.users:
161#                self._userIn.users += [username]
162#           
163#            # TODO: set session
164#               
165#        except Exception, e:
166#            return False
167#        else:
168#            return True
169
170
171class SessionMiddlewareBase(NDGSecurityMiddlewareBase):
172    """Base class for Authentication redirect middleware and Session Handler
173    middleware
174   
175    @type propertyDefaults: dict
176    @cvar propertyDefaults: valid configuration property keywords
177    """   
178    propertyDefaults = {
179        'sessionKey': 'beaker.session.ndg.security'
180    }
181   
182    _isAuthenticated = lambda self: \
183        SessionMiddlewareBase.USERNAME_SESSION_KEYNAME in \
184        self.environ.get(self.sessionKey, ())
185       
186    isAuthenticated = property(fget=_isAuthenticated,
187                               doc='boolean to indicate is user logged in')
188
189       
190class AuthnRedirectMiddleware(SessionMiddlewareBase):
191    """Base class for Authentication HTTP redirect initiator and redirect
192    response WSGI middleware
193
194    @type RETURN2URI_ARGNAME: basestring
195    @cvar RETURN2URI_ARGNAME: name of URI query argument used to pass the
196    return to URI between initiator and consumer classes"""
197    RETURN2URI_ARGNAME = 'ndg.security.r'
198
199
200class AuthnRedirectInitiatorMiddleware(AuthnRedirectMiddleware):
201    '''Middleware to initiate a redirect to another URI if a user is not
202    authenticated i.e. security cookie is not set
203   
204    AuthKit.authenticate.middleware must be in place upstream of this
205    middleware.  AuthenticationMiddleware wrapper handles this.
206   
207    @type propertyDefaults: dict
208    @cvar propertyDefaults: valid configuration property keywords   
209    '''
210    propertyDefaults = {
211        'redirectURI': None,
212    }
213    propertyDefaults.update(AuthnRedirectMiddleware.propertyDefaults)
214   
215
216    TRIGGER_HTTP_STATUS_CODE = '401'
217    MIDDLEWARE_ID = 'AuthnRedirectInitiatorMiddleware'
218
219    def __init__(self, app, global_conf, **app_conf):
220        '''
221        @type app: callable following WSGI interface
222        @param app: next middleware application in the chain     
223        @type global_conf: dict       
224        @param global_conf: PasteDeploy global configuration dictionary
225        @type prefix: basestring
226        @param prefix: prefix for configuration items
227        @type app_conf: dict       
228        @param app_conf: PasteDeploy application specific configuration
229        dictionary
230        '''
231        self._redirectURI = None
232        super(AuthnRedirectInitiatorMiddleware, self).__init__(app, 
233                                                               global_conf, 
234                                                               **app_conf)
235       
236    @NDGSecurityMiddlewareBase.initCall
237    def __call__(self, environ, start_response):
238        '''Invoke redirect if user is not authenticated'''
239       
240        log.debug("AuthnRedirectInitiatorMiddleware.__call__ ...")
241       
242        if self.isAuthenticated:
243            # Call next app in stack
244            return self._app(environ, start_response)       
245        else:
246            # User is not authenticated - Redirect to OpenID Relying Party URI
247            # for user OpenID entry
248            return self._setRedirectResponse()
249   
250    def _setRedirectURI(self, uri):
251        if not isinstance(uri, basestring):
252            raise TypeError("Redirect URI must be set to string type")   
253         
254        self._redirectURI = uri
255       
256    def _getRedirectURI(self):
257        return self._redirectURI
258   
259    redirectURI = property(fget=_getRedirectURI,
260                       fset=_setRedirectURI,
261                       doc="URI to redirect to if user is not authenticated")
262
263    def _setRedirectResponse(self):
264        """Construct a redirect response adding in a return to address in a
265        URI query argument
266       
267        @rtype: basestring
268        @return: redirect response
269        """       
270        return2URI = construct_url(self.environ)
271        quotedReturn2URI = urllib.quote(return2URI, safe='')
272        return2URIQueryArg = urllib.urlencode(
273                    {AuthnRedirectInitiatorMiddleware.RETURN2URI_ARGNAME: 
274                     quotedReturn2URI})
275
276        redirectURI = self.redirectURI
277       
278        if '?' in redirectURI:
279            if redirectURI.endswith('&'):
280                redirectURI += return2URIQueryArg
281            else:
282                redirectURI += '&' + return2URIQueryArg
283        else:
284            if redirectURI.endswith('?'):
285                redirectURI += return2URIQueryArg
286            else:
287                redirectURI += '?' + return2URIQueryArg
288         
289        # Call NDGSecurityMiddlewareBase.redirect utility method     
290        return self.redirect(redirectURI)
291       
292    @classmethod
293    def checker(cls, environ, status, headers):
294        """Set the MultiHandler checker function for triggering this
295        middleware.  In this case, it's a HTTP 401 Unauthorized response
296        detected in the middleware chain
297        """
298        if status.startswith(cls.TRIGGER_HTTP_STATUS_CODE):
299            log.debug("%s.checker caught status [%s]: invoking authentication"
300                      " handler", cls.__name__, cls.TRIGGER_HTTP_STATUS_CODE)
301            return True
302        else:
303            log.debug("%s.checker skipping status [%s]", cls.__name__, status)
304            return False
305
306
307class AuthnRedirectResponseMiddleware(AuthnRedirectMiddleware):
308    """Compliment to AuthnRedirectInitiatorMiddleware
309    functioning as the opposite end of the HTTP redirect interface.  It
310    performs the following tasks:
311    - Detect a redirect URI set in a URI query argument and copy it into
312    a user session object.
313    - Redirect back to the redirect URI once a user is authenticated
314   
315    Also see,
316    ndg.security.server.wsgi.openid.relyingparty.OpenIDRelyingPartyMiddleware
317    which performs a similar function.
318    """
319    @NDGSecurityMiddlewareBase.initCall
320    def __call__(self, environ, start_response):
321        session = environ[self.sessionKey]
322       
323        # Check for return to address in URI query args set by
324        # AuthnRedirectInitiatorMiddleware in application code stack
325        if environ['REQUEST_METHOD'] == "GET":
326            params = dict(parse_querystring(environ))
327        else:
328            params = {}
329       
330        # Store the return URI query argument in a beaker session
331        quotedReferrer = params.get(self.__class__.RETURN2URI_ARGNAME, '')
332        referrerURI = urllib.unquote(quotedReferrer)
333        if referrerURI:
334            session[self.__class__.RETURN2URI_ARGNAME] = referrerURI
335            session.save()
336           
337        # Check for a return URI setting in the beaker session and if the user
338        # is authenticated, redirect to this URL deleting the beaker session
339        # URL setting
340        return2URI = session.get(self.__class__.RETURN2URI_ARGNAME)   
341        if self.isAuthenticated and return2URI:
342            del session[self.__class__.RETURN2URI_ARGNAME]
343            session.save()
344            return self.redirect(return2URI)
345
346        return self._app(environ, start_response)
347
348
349class AuthKitRedirectResponseMiddleware(AuthnRedirectResponseMiddleware):
350    """Overload isAuthenticated method in parent class to set Authenticated
351    state based on presence of AuthKit 'REMOTE_USER' environ variable
352    """
353    _isAuthenticated = lambda self: \
354        AuthnRedirectResponseMiddleware.USERNAME_ENVIRON_KEYNAME in self.environ
355       
356    isAuthenticated = property(fget=_isAuthenticated,
357                               doc="Boolean indicating if AuthKit "
358                                   "'REMOTE_USER' environment variable is set")
359    def __init__(self, app, app_conf, **local_conf):
360        super(AuthKitRedirectResponseMiddleware, self).__init__(app, app_conf,
361                                                                **local_conf)
362    @NDGSecurityMiddlewareBase.initCall
363    def __call__(self, environ, start_response):
364        return super(AuthKitRedirectResponseMiddleware, self).__call__(environ,
365                                                                start_response)
366       
367
368class SessionHandlerMiddlewareError(AuthnException):
369    """Base exception for SessionHandlerMiddleware"""
370           
371class SessionHandlerMiddlewareConfigError(SessionHandlerMiddlewareError):
372    """Configuration errors from SessionHandlerMiddleware"""
373   
374class OpenIdAXConfigError(SessionHandlerMiddlewareError):
375    """Error parsing OpenID Ax (Attribute Exchange) parameters"""
376   
377   
378class SessionHandlerMiddleware(SessionMiddlewareBase):
379    '''Middleware to:
380    - establish user session details following redirect from OpenID Relying
381    Party sign-in or SSL Client authentication
382    - end session redirecting back to referrer URI following call to a logout
383    URI as implemented in AuthKit
384    '''
385    AX_SESSION_KEYNAME = 'openid.ax'
386    SM_URI_SESSION_KEYNAME = 'sessionManagerURI'
387    ID_SESSION_KEYNAME = 'sessionId'
388    PEP_CTX_SESSION_KEYNAME = 'pepCtx'
389    CREDENTIAL_WALLET_SESSION_KEYNAME = 'credentialWallet'
390   
391    SESSION_KEYNAMES = (
392        SessionMiddlewareBase.USERNAME_SESSION_KEYNAME, 
393        SM_URI_SESSION_KEYNAME, 
394        ID_SESSION_KEYNAME, 
395        PEP_CTX_SESSION_KEYNAME, 
396        CREDENTIAL_WALLET_SESSION_KEYNAME
397    )
398   
399    AX_KEYNAME = 'ax'
400    SM_URI_AX_KEYNAME = 'value.sessionManagerURI.1'
401    SESSION_ID_AX_KEYNAME = 'value.sessionId.1'
402   
403    AUTHKIT_COOKIE_SIGNOUT_PARAMNAME = 'authkit.cookie.signoutpath'
404    SIGNOUT_PATH_PARAMNAME = 'signoutPath'
405    SESSION_KEY_PARAMNAME = 'sessionKey'
406    propertyDefaults = {
407        SIGNOUT_PATH_PARAMNAME: None,
408        SESSION_KEY_PARAMNAME: 'beaker.session.ndg.security'
409    }
410   
411    AUTH_TKT_SET_USER_ENVIRON_KEYNAME = 'paste.auth_tkt.set_user'
412   
413    PARAM_PREFIX = 'sessionHandler.'
414   
415    def __init__(self, app, global_conf, prefix=PARAM_PREFIX, **app_conf):
416        '''
417        @type app: callable following WSGI interface
418        @param app: next middleware application in the chain     
419        @type global_conf: dict       
420        @param global_conf: PasteDeploy global configuration dictionary
421        @type prefix: basestring
422        @param prefix: prefix for configuration items
423        @type app_conf: dict       
424        @param app_conf: PasteDeploy application specific configuration
425        dictionary
426        '''
427        signoutPathParamName = prefix + \
428                                SessionHandlerMiddleware.SIGNOUT_PATH_PARAMNAME
429       
430        if signoutPathParamName not in app_conf:
431            authKitSignOutPath = app_conf.get(
432                    SessionHandlerMiddleware.AUTHKIT_COOKIE_SIGNOUT_PARAMNAME)
433           
434            if authKitSignOutPath:
435                app_conf[signoutPathParamName] = authKitSignOutPath
436               
437                log.info('Set signoutPath=%s from "%s" setting', 
438                     authKitSignOutPath,
439                     SessionHandlerMiddleware.AUTHKIT_COOKIE_SIGNOUT_PARAMNAME)
440            else:
441                raise SessionHandlerMiddlewareConfigError(
442                                        '"signoutPath" parameter is not set')
443           
444        super(SessionHandlerMiddleware, self).__init__(app,
445                                                       global_conf,
446                                                       prefix=prefix, 
447                                                       **app_conf)
448       
449    @NDGSecurityMiddlewareBase.initCall
450    def __call__(self, environ, start_response):
451        """Manage setting of session from AuthKit following OpenID Relying
452        Party sign in and manage logout
453       
454        @type environ: dict
455        @param environ: WSGI environment variables dictionary
456        @type start_response: function
457        @param start_response: standard WSGI start response function
458        """
459        log.debug("SessionHandlerMiddleware.__call__ ...")
460       
461        session = environ.get(self.sessionKey)
462        if session is None:
463            raise SessionHandlerMiddlewareConfigError(
464                   'SessionHandlerMiddleware.__call__: No beaker session key '
465                   '"%s" found in environ' % self.sessionKey)
466       
467        if self.signoutPath and self.pathInfo == self.signoutPath:
468            log.debug("SessionHandlerMiddleware.__call__: caught sign out "
469                      "path [%s]", self.signoutPath)
470           
471            referrer = environ.get('HTTP_REFERER')
472            if referrer is not None:
473                def _start_response(status, header, exc_info=None):
474                    """Alter the header to send a redirect to the logout
475                    referrer address"""
476                    filteredHeader = [(field, val) for field, val in header
477                                      if field.lower() != 'location']       
478                    filteredHeader.extend([('Location', referrer)])
479                    return start_response(self.getStatusMessage(302), 
480                                          filteredHeader,
481                                          exc_info)
482                   
483            else:
484                log.error('No referrer set for redirect following logout')
485                _start_response = start_response
486               
487            # Clear user details from beaker session
488            for keyName in self.__class__.SESSION_KEYNAMES:
489                session.pop(keyName, None)
490            session.save()
491        else:
492            log.debug("SessionHandlerMiddleware.__call__: checking for "
493                      "REMOTE_* environment variable settings set by OpenID "
494                      "Relying Party signin...")
495           
496            if SessionHandlerMiddleware.USERNAME_SESSION_KEYNAME not in session\
497               and SessionHandlerMiddleware.USERNAME_ENVIRON_KEYNAME in environ:
498                log.debug("SessionHandlerMiddleware: updating session "
499                          "username=%s", environ[
500                            SessionHandlerMiddleware.USERNAME_ENVIRON_KEYNAME])
501               
502                session[SessionHandlerMiddleware.USERNAME_SESSION_KEYNAME
503                        ] = environ[
504                            SessionHandlerMiddleware.USERNAME_ENVIRON_KEYNAME]
505                session.save()
506               
507            remoteUserData = environ.get(
508                        SessionHandlerMiddleware.USERDATA_ENVIRON_KEYNAME, '')   
509            if remoteUserData:
510                log.debug("SessionHandlerMiddleware: found REMOTE_USER_DATA="
511                          "%s, set from OpenID Relying Party signin",
512                          environ[
513                          SessionHandlerMiddleware.USERDATA_ENVIRON_KEYNAME])
514               
515                # eval is safe here because AuthKit cookie is signed and
516                # AuthKit middleware checks for tampering
517                if SessionHandlerMiddleware.SM_URI_SESSION_KEYNAME not in \
518                   session or \
519                   SessionHandlerMiddleware.ID_SESSION_KEYNAME not in session:
520                   
521                    axData = eval(remoteUserData)
522                    if isinstance(axData, dict) and \
523                       SessionHandlerMiddleware.AX_KEYNAME in axData:
524                       
525                        ax = axData[SessionHandlerMiddleware.AX_KEYNAME]
526                       
527                        # Save attributes keyed by attribute name
528                        session[
529                            SessionHandlerMiddleware.AX_SESSION_KEYNAME
530                        ] = SessionHandlerMiddleware._parseOpenIdAX(ax)
531                       
532                        log.debug("SessionHandlerMiddleware: updated session "
533                                  "with OpenID AX values: %r" % 
534                                  session[
535                                    SessionHandlerMiddleware.AX_SESSION_KEYNAME
536                                  ])
537                           
538                        # Save Session Manager specific attributes
539                        sessionManagerURI = ax.get(
540                                SessionHandlerMiddleware.SM_URI_AX_KEYNAME)
541                           
542                        session[SessionHandlerMiddleware.SM_URI_SESSION_KEYNAME
543                                ] = sessionManagerURI
544   
545                        sessionId = ax.get(
546                                SessionHandlerMiddleware.SESSION_ID_AX_KEYNAME)
547                        session[SessionHandlerMiddleware.ID_SESSION_KEYNAME
548                                ] = sessionId
549                               
550                        session.save()
551                       
552                        log.debug("SessionHandlerMiddleware: updated session "
553                                  "with sessionManagerURI=%s and "
554                                  "sessionId=%s", 
555                                  sessionManagerURI, 
556                                  sessionId)
557                   
558                # Reset cookie removing user data
559                setUser = environ[
560                    SessionHandlerMiddleware.AUTH_TKT_SET_USER_ENVIRON_KEYNAME]
561                setUser(
562                    session[SessionHandlerMiddleware.USERNAME_SESSION_KEYNAME])
563
564            _start_response = start_response
565           
566        return self._app(environ, _start_response)
567   
568    @staticmethod                   
569    def _parseOpenIdAX(ax):
570        """Return a dictionary of attribute exchange attributes parsed from the
571        OpenID Provider response set in the REMOTE_USER_DATA AuthKit environ
572        key
573       
574        @param ax: dictionary of AX parameters - format of keys is e.g.
575        count.paramName, value.paramName.<n>, type.paramName
576        @type ax: dict
577        @return: dictionary of parameters keyed by parameter with values for
578        each parameter a tuple of count.paramName values
579        @rtype: dict
580        """
581       
582        # Copy Attributes into session
583        outputKeys = [k.replace('type.', '') for k in ax.keys()
584                      if k.startswith('type.')]
585       
586        output = {}
587        for outputKey in outputKeys:
588            axCountKeyName = 'count.' + outputKey
589            axCount = int(ax[axCountKeyName])
590           
591            axValueKeyPrefix = 'value.%s.' % outputKey
592            output[outputKey] = tuple([v for k, v in ax.items() 
593                                       if k.startswith(axValueKeyPrefix)])
594           
595            nVals = len(output[outputKey])
596            if nVals != axCount:
597                raise OpenIdAXConfigError('Got %d parameters for AX attribute '
598                                          '"%s"; but "%s" AX key is set to %d'
599                                          % (nVals,
600                                             axCountKeyName,
601                                             axCountKeyName,
602                                             axCount))
603                                             
604        return output
605
606
607from authkit.authenticate.multi import MultiHandler
608
609class AuthenticationMiddlewareConfigError(NDGSecurityMiddlewareConfigError):
610    '''Authentication Middleware Configuration error'''
611
612
613class AuthenticationMiddleware(MultiHandler, NDGSecurityMiddlewareBase):
614    '''Top-level class encapsulates session and authentication handlers
615    in this module
616   
617    Handler to intercept 401 Unauthorized HTTP responses and redirect to an
618    authentication URI.  This class also implements a redirect handler to
619    redirect back to the referrer if logout is invoked.
620    '''
621
622    def __init__(self, app, global_conf, prefix='', **app_conf):
623        '''
624        @type app: callable following WSGI interface
625        @param app: next middleware application in the chain     
626        @type global_conf: dict       
627        @param global_conf: PasteDeploy global configuration dictionary
628        @type prefix: basestring
629        @param prefix: prefix for configuration items
630        @type app_conf: dict       
631        @param app_conf: PasteDeploy application specific configuration
632        dictionary
633        '''
634       
635        # Set logout URI parameter from AuthKit settings if not otherwise set
636        sessionHandlerPrefix = prefix + SessionHandlerMiddleware.PARAM_PREFIX       
637        app = SessionHandlerMiddleware(app, 
638                                       global_conf, 
639                                       prefix=sessionHandlerPrefix,
640                                       **app_conf)
641       
642        # Remove session handler middleware specific parameters
643        for k in app_conf.keys():
644            if k.startswith(sessionHandlerPrefix):
645                del app_conf[k]
646       
647        app = authkit.authenticate.middleware(app, app_conf)       
648       
649        MultiHandler.__init__(self, app)
650
651        # Redirection middleware is invoked based on a check method which
652        # catches HTTP 401 responses.
653        self.add_method(AuthnRedirectInitiatorMiddleware.MIDDLEWARE_ID, 
654                        AuthnRedirectInitiatorMiddleware.filter_app_factory, 
655                        global_conf,
656                        prefix=prefix,
657                        **app_conf)
658       
659        self.add_checker(AuthnRedirectInitiatorMiddleware.MIDDLEWARE_ID, 
660                         AuthnRedirectInitiatorMiddleware.checker)
Note: See TracBrowser for help on using the repository browser.