source: TI12-security/trunk/python/ndg_security_server/ndg/security/server/wsgi/authz.py @ 6043

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

Unit tested MSI PDP with per attribute entry attribute authority addresses.

Line 
1"""WSGI Policy Enforcement Point Package
2
3NERC DataGrid Project
4"""
5__author__ = "P J Kershaw"
6__date__ = "16/01/2009"
7__copyright__ = "(C) 2009 Science and Technology Facilities Council"
8__contact__ = "Philip.Kershaw@stfc.ac.uk"
9__revision__ = "$Id$"
10__license__ = "BSD - see LICENSE file in top-levle directory"
11import logging
12log = logging.getLogger(__name__)
13from time import time
14from urlparse import urlunsplit
15from httplib import UNAUTHORIZED, FORBIDDEN
16
17from ndg.security.common.utils.classfactory import importClass
18from ndg.security.common.X509 import X500DN
19from ndg.security.common.utils.m2crypto import SSLClientProxy
20
21from ndg.security.common.credentialwallet import (CredentialWallet,
22                                                  SamlCredentialWallet)
23from ndg.security.server.wsgi import (NDGSecurityMiddlewareBase, 
24                                      NDGSecurityMiddlewareConfigError)
25
26from ndg.security.server.wsgi import (NDGSecurityMiddlewareBase, 
27                                      NDGSecurityMiddlewareConfigError)
28from ndg.security.server.wsgi.authn import (SessionMiddlewareBase, 
29                                            SessionHandlerMiddlewareBase)
30
31from ndg.security.common.authz.msi import (Policy, PIP, PIPBase, 
32                                           PIPAttributeQuery, 
33                                           PIPAttributeResponse, PDP, Request, 
34                                           Response, Resource, Subject)
35
36
37class PEPResultHandlerMiddleware(SessionMiddlewareBase):
38    """This middleware is invoked if access is denied to a given resource.  It
39    is incorporated into the call stack by passing it in to a MultiHandler
40    instance.  The MultiHandler is configured in the AuthorizationMiddleware
41    class below.  The MultiHandler is passed a checker method which determines
42    whether to allow access, or call this interface.   The checker is
43    implemented in the AuthorizationHandler.  See below ...
44   
45    This class can be overridden to define custom behaviour for the access
46    denied response e.g. include an interface to enable users to register for
47    the dataset from which they have been denied access.  See
48    AuthorizationMiddleware pepResultHandler keyword.
49   
50    SessionMiddlewareBase base class defines user session key and
51    isAuthenticated property
52    """
53   
54    def __init__(self, app, global_conf, prefix='', **app_conf):
55        '''
56        @type app: callable following WSGI interface
57        @param app: next middleware application in the chain     
58        @type global_conf: dict       
59        @param global_conf: PasteDeploy global configuration dictionary
60        @type prefix: basestring
61        @param prefix: prefix for configuration items
62        @type app_conf: dict       
63        @param app_conf: PasteDeploy application specific configuration
64        dictionary
65        '''
66        super(PEPResultHandlerMiddleware, self).__init__(app,
67                                                         global_conf,
68                                                         prefix=prefix,
69                                                         **app_conf)
70               
71    @NDGSecurityMiddlewareBase.initCall
72    def __call__(self, environ, start_response):
73       
74        log.debug("PEPResultHandlerMiddleware.__call__ ...")
75       
76        self.session = self.environ.get(self.sessionKey)
77        if not self.isAuthenticated:
78            # This check is included as a precaution: this condition should be
79            # caught be the AuthNRedirectHandlerMiddleware or PEPFilter
80            log.warning("PEPResultHandlerMiddleware: user is not "
81                        "authenticated - setting HTTP 401 response")
82            return self._setErrorResponse(code=UNAUTHORIZED)
83        else:
84            # Get response message from PDP recorded by PEP
85            pepCtx = self.session.get('pepCtx', {})
86            pdpResponse = pepCtx.get('response')
87            msg = getattr(pdpResponse, 'message', '')
88               
89            response = ("Access is forbidden for this resource:%s"
90                        "Please check with your site administrator that you "
91                        "have the required access privileges." % 
92                        msg.join(('\n\n',)*2))
93
94            return self._setErrorResponse(code=FORBIDDEN, msg=response)
95
96
97class PEPFilterError(Exception):
98    """Base class for PEPFilter exception types"""
99   
100class PEPFilterConfigError(PEPFilterError):
101    """Configuration related error for PEPFilter"""
102
103class PEPFilter(SessionMiddlewareBase):
104    """PEP (Policy Enforcement Point) WSGI Middleware.  The PEP enforces
105    access control decisions made by the PDP (Policy Decision Point).  In
106    this case, it follows the WSG middleware filter pattern and is configured
107    in a pipeline upstream of the application(s) which it protects.  if an
108    access denied decision is made, the PEP enforces this by returning a
109    403 Forbidden HTTP response without the application middleware executing
110   
111    SessionMiddlewareBase base class defines user session key and
112    isAuthenticated property
113    """
114    TRIGGER_HTTP_STATUS_CODE = str(FORBIDDEN)
115    MIDDLEWARE_ID = 'PEPFilter'
116    POLICY_PARAM_PREFIX = 'policy.'
117    SESSION_KEYNAME = 'sessionKey'
118    POLICY_FILEPATH_PARAMNAME = 'filePath'
119   
120    def __init__(self, app, global_conf, prefix='', **local_conf):
121        """Initialise the PIP (Policy Information Point) and PDP (Policy
122        Decision Point).  The PDP makes access control decisions based on
123        a given policy.  The PIP manages the retrieval of user credentials on
124        behalf of the PDP
125       
126        @type app: callable following WSGI interface
127        @param app: next middleware application in the chain     
128        @type global_conf: dict       
129        @param global_conf: PasteDeploy global configuration dictionary
130        @type prefix: basestring
131        @param prefix: prefix for configuration items
132        @type local_conf: dict       
133        @param local_conf: PasteDeploy application specific configuration
134        dictionary
135       
136        """       
137        # Initialise the PDP reading in the policy
138        policyCfg = PEPFilter._filterKeywords(local_conf, 
139                                              PEPFilter.POLICY_PARAM_PREFIX)
140        self.policyFilePath = policyCfg[PEPFilter.POLICY_FILEPATH_PARAMNAME]
141        policy = Policy.Parse(policyCfg[PEPFilter.POLICY_FILEPATH_PARAMNAME])
142       
143        # Initialise the Policy Information Point to None.  This object is
144        # created and set later.  See AuthorizationMiddleware.
145        self.pdp = PDP(policy, None)
146       
147        self.sessionKey = local_conf.get(PEPFilter.SESSION_KEYNAME, 
148                                         PEPFilter.propertyDefaults[
149                                                    PEPFilter.SESSION_KEYNAME])
150       
151        super(PEPFilter, self).__init__(app,
152                                        global_conf,
153                                        prefix=prefix,
154                                        **local_conf)
155
156    @NDGSecurityMiddlewareBase.initCall
157    def __call__(self, environ, start_response):
158        """
159        @type environ: dict
160        @param environ: WSGI environment variables dictionary
161        @type start_response: function
162        @param start_response: standard WSGI start response function
163        @rtype: iterable
164        @return: response
165        """
166        session = environ.get(self.sessionKey)
167        if session is None:
168            raise PEPFilterConfigError('No beaker session key "%s" found in '
169                                       'environ' % self.sessionKey)
170           
171        queryString = environ.get('QUERY_STRING', '')
172        resourceURI = urlunsplit(('', '', self.pathInfo, queryString, ''))
173       
174        # Check for a secured resource
175        matchingTargets = self._getMatchingTargets(resourceURI)
176        targetMatch = len(matchingTargets) > 0
177        if not targetMatch:
178            log.debug("PEPFilter.__call__: granting access - no matching URI "
179                      "path target was found in the policy for URI path [%s]", 
180                      resourceURI)
181            return self._app(environ, start_response)
182
183        log.debug("PEPFilter.__call__: found matching target(s):\n\n %s\n"
184                  "\nfrom policy file [%s] for URI Path=[%s]\n",
185                  '\n'.join(["RegEx=%s" % t for t in matchingTargets]), 
186                  self.policyFilePath,
187                  resourceURI)
188       
189        if not self.isAuthenticated:
190            log.info("PEPFilter.__call__: user is not authenticated - setting "
191                     "HTTP 401 response ...")
192           
193            # Set a 401 response for an authentication handler to capture
194            return self._setErrorResponse(code=UNAUTHORIZED)
195       
196        log.debug("PEPFilter.__call__: creating request to call PDP to check "
197                  "user authorisation ...")
198       
199        # Make a request object to pass to the PDP
200        request = Request()
201        request.subject[Subject.USERID_NS] = session['username']
202       
203        # IdP Session Manager specific settings:
204        #
205        # The following won't be set if the IdP running the OpenID Provider
206        # hasn't also deployed a Session Manager.  In this case, the
207        # Attribute Authority will be queried directly from here without a
208        # remote Session Manager intermediary to cache credentials
209        request.subject[Subject.SESSIONID_NS] = session.get('sessionId')
210        request.subject[Subject.SESSIONMANAGERURI_NS] = session.get(
211                                                        'sessionManagerURI')
212        request.resource[Resource.URI_NS] = resourceURI
213
214       
215        # Call the PDP
216        response = self.pdp.evaluate(request)       
217       
218        # Record the result in the user's session to enable later
219        # interrogation by the AuthZResultHandlerMiddleware
220        session['pepCtx'] = {'request': request, 'response': response,
221                             'timestamp': time()}
222        session.save()
223       
224        if response.status == Response.DECISION_PERMIT:
225            log.info("PEPFilter.__call__: PDP granted access for URI path "
226                     "[%s] using policy [%s]", 
227                     resourceURI, 
228                     self.policyFilePath)
229           
230            return self._app(environ, start_response)
231        else:
232            log.info("PEPFilter.__call__: PDP returned a status of [%s] "
233                     "denying access for URI path [%s] using policy [%s]", 
234                     response.decisionValue2String[response.status],
235                     resourceURI,
236                     self.policyFilePath) 
237           
238            # Trigger AuthZResultHandlerMiddleware by setting a response
239            # with HTTP status code equal to the TRIGGER_HTTP_STATUS_CODE class
240            # attribute value
241            triggerStatusCode = int(PEPFilter.TRIGGER_HTTP_STATUS_CODE)
242            return self._setErrorResponse(code=triggerStatusCode)
243
244    def _getMatchingTargets(self, resourceURI):
245        """This method may only be called following __call__ as __call__
246        updates the pathInfo property
247       
248        @type resourceURI: basestring
249        @param resourceURI: the URI of the requested resource
250        @rtype: list
251        @return: return list of policy target objects matching the current
252        path
253        """
254        matchingTargets = [target for target in self.pdp.policy.targets
255                           if target.regEx.match(resourceURI) is not None]
256        return matchingTargets
257
258    def multiHandlerInterceptFactory(self):
259        """Return a checker function for use with AuthKit's MultiHandler.
260        MultiHandler can be used to catch HTTP 403 Forbidden responses set by
261        an application and call middleware (AuthZResultMiddleware) to handle
262        the access denied message.
263        """
264       
265        def multiHandlerIntercept(environ, status, headers):
266            """AuthKit MultiHandler checker function to intercept
267            unauthorised response status codes from applications to be
268            protected.  This function's definition is embedded into a
269            factory method so that this function has visibility to the
270            PEPFilter object's attributes if required.
271           
272            @type environ: dict
273            @param environ: WSGI environment dictionary
274            @type status: basestring
275            @param status: HTTP response code set by application middleware
276            that this intercept function is to protect
277            @type headers: list
278            @param headers: HTTP response header content"""
279           
280            if status.startswith(PEPFilter.TRIGGER_HTTP_STATUS_CODE):
281                log.debug("PEPFilter: found [%s] status for URI path [%s]: "
282                          "invoking access denied response",
283                          PEPFilter.TRIGGER_HTTP_STATUS_CODE,
284                          environ['PATH_INFO'])
285                return True
286            else:
287                # No match - it's publicly accessible
288                log.debug("PEPFilter: the return status [%s] for this URI "
289                          "path [%s] didn't match the trigger status [%s]",
290                          status,
291                          environ['PATH_INFO'],
292                          PEPFilter.TRIGGER_HTTP_STATUS_CODE)
293                return False
294       
295        return multiHandlerIntercept
296       
297    @staticmethod
298    def _filterKeywords(conf, prefix):
299        filteredConf = {}
300        prefixLen = len(prefix)
301        for k, v in conf.items():
302            if k.startswith(prefix):
303                filteredConf[k[prefixLen:]] = conf.pop(k)
304               
305        return filteredConf
306
307    def _getPDP(self):
308        if self._pdp is None:
309            raise TypeError("PDP object has not been initialised")
310        return self._pdp
311   
312    def _setPDP(self, pdp):
313        if not isinstance(pdp, (PDP, None.__class__)):
314            raise TypeError("Expecting %s or None type for pdp; got %r" %
315                            (PDP.__class__.__name__, pdp))
316        self._pdp = pdp
317
318    pdp = property(fget=_getPDP,
319                   fset=_setPDP,
320                   doc="Policy Decision Point object makes access control "
321                       "decisions on behalf of the PEP")
322
323   
324class PIPMiddlewareError(Exception):
325    """Base class for Policy Information Point WSGI middleware exception types
326    """
327   
328class PIPMiddlewareConfigError(PIPMiddlewareError):
329    """Configuration related error for Policy Information Point WSGI middleware
330    """
331   
332class NdgPIPMiddleware(PIP, NDGSecurityMiddlewareBase):
333    '''Extend Policy Information Point to enable caching of credentials in
334    a CredentialWallet object held in beaker.session
335    '''
336    ENVIRON_KEYNAME = 'ndg.security.server.wsgi.authz.NdgPIPMiddleware'
337       
338    propertyDefaults = {
339        'sessionKey': 'beaker.session.ndg.security',
340    }
341    propertyDefaults.update(NDGSecurityMiddlewareBase.propertyDefaults)
342 
343    def __init__(self, app, global_conf, prefix='', **local_conf):
344        '''
345        @type app: callable following WSGI interface
346        @param app: next middleware application in the chain     
347        @type global_conf: dict       
348        @param global_conf: PasteDeploy global configuration dictionary
349        @type prefix: basestring
350        @param prefix: prefix for configuration items
351        @type local_conf: dict       
352        @param local_conf: PasteDeploy application specific configuration
353        dictionary
354        '''
355       
356        # Pre-process list items splitting as needed
357        if isinstance(local_conf.get('caCertFilePathList'), basestring):
358            local_conf[
359                'caCertFilePathList'] = NDGSecurityMiddlewareBase.parseListItem(
360                                            local_conf['caCertFilePathList'])
361           
362        if isinstance(local_conf.get('sslCACertFilePathList'), basestring):
363            local_conf[
364                'sslCACertFilePathList'
365                ] = NDGSecurityMiddlewareBase.parseListItem(
366                                        local_conf['sslCACertFilePathList'])
367           
368        PIP.__init__(self, prefix=prefix, **local_conf)
369       
370        for k in local_conf.keys():
371            if k.startswith(prefix):
372                del local_conf[k]
373               
374        NDGSecurityMiddlewareBase.__init__(self,
375                                           app,
376                                           global_conf,
377                                           prefix=prefix,
378                                           **local_conf)
379       
380    def __call__(self, environ, start_response):
381        """Take a copy of the session object so that it is in scope for
382        _getAttributeCertificate call and add this instance to the environ
383        so that the PEPFilter can retrieve it and pass on to the PDP
384       
385        @type environ: dict
386        @param environ: WSGI environment variables dictionary
387        @type start_response: function
388        @param start_response: standard WSGI start response function
389        @rtype: iterable
390        @return: response
391        """
392        self.session = environ.get(self.sessionKey)
393        if self.session is None:
394            raise NdgPIPMiddlewareConfigError('No beaker session key "%s" found '
395                                           'in environ' % self.sessionKey)
396        environ[NdgPIPMiddleware.ENVIRON_KEYNAME] = self
397       
398        return self._app(environ, start_response)
399               
400    def _getAttributeCertificate(self, attributeAuthorityURI, **kw):
401        '''Extend base class implementation to make use of the CredentialWallet
402        Attribute Certificate cache held in the beaker session.  If no suitable
403        certificate is present invoke default behaviour and retrieve an
404        Attribute Certificate from the Attribute Authority or Session Manager
405        specified
406
407        @type attributeAuthorityURI: basestring
408        @param attributeAuthorityURI: URI to Attribute Authority service
409        @type username: basestring
410        @param username: subject user identifier - could be an OpenID       
411        @type sessionId: basestring
412        @param sessionId: Session Manager session handle
413        @type sessionManagerURI: basestring
414        @param sessionManagerURI: URI to remote session manager service
415        @rtype: ndg.security.common.AttCert.AttCert
416        @return: Attribute Certificate containing user roles
417        '''
418        # Check for a wallet in the current session - if not present, create
419        # one.  See ndg.security.server.wsgi.authn.SessionHandlerMiddleware
420        # for session keys.  The 'credentialWallet' key is deleted along with
421        # any other security keys when the user logs out
422        if not 'credentialWallet' in self.session:
423            log.debug("NdgPIPMiddleware._getAttributeCertificate: adding a "
424                      "Credential Wallet to user session [%s] ...",
425                      self.session['username'])
426           
427            self.session['credentialWallet'] = CredentialWallet(
428                                            userId=self.session['username'])
429            self.session.save()
430           
431        # Take reference to wallet for efficiency
432        credentialWallet = self.session['credentialWallet']   
433       
434        # Check for existing credentials cached in wallet           
435        credentialItem = credentialWallet.credentialsKeyedByURI.get(
436                                                    attributeAuthorityURI, {})
437       
438        attrCert = credentialItem.credential
439        if attrCert is not None:
440            log.debug("NdgPIPMiddleware._getAttributeCertificate: retrieved "
441                      "existing Attribute Certificate cached in Credential "
442                      "Wallet for user session [%s]",
443                      self.session['username'])
444
445            # Existing cached credential found - skip call to remote Session
446            # Manager / Attribute Authority and return this certificate instead
447            return attrCert
448        else:   
449            attrCert = PIP._getAttributeCertificate(self,
450                                                    attributeAuthorityURI,
451                                                    **kw)
452           
453            log.debug("NdgPIPMiddleware._getAttributeCertificate: updating "
454                      "Credential Wallet with retrieved Attribute "
455                      "Certificate for user session [%s]",
456                      self.session['username'])
457       
458            # Update the wallet with this Attribute Certificate so that it's
459            # cached for future calls
460            credentialWallet.addCredential(attrCert,
461                                attributeAuthorityURI=attributeAuthorityURI)
462           
463            return attrCert
464   
465
466class SamlPIPMiddleware(PIPBase, NDGSecurityMiddlewareBase):
467    '''Extend Policy Information Point to enable caching of SAML credentials in
468    a CredentialWallet object held in beaker.session
469    '''
470    ENVIRON_KEYNAME = 'ndg.security.server.wsgi.authz.SamlPIPMiddleware'
471       
472    propertyDefaults = {
473        'sessionKey': 'beaker.session.ndg.security',
474    }
475    propertyDefaults.update(NDGSecurityMiddlewareBase.propertyDefaults)
476 
477    CREDENTIAL_WALLET_SESSION_KEYNAME = \
478        SessionHandlerMiddlewareBase.CREDENTIAL_WALLET_SESSION_KEYNAME
479    USERNAME_SESSION_KEYNAME = \
480        SessionHandlerMiddlewareBase.USERNAME_SESSION_KEYNAME
481         
482    def __init__(self, app, global_conf, prefix='', **local_conf):
483        '''
484        @type app: callable following WSGI interface
485        @param app: next middleware application in the chain     
486        @type global_conf: dict       
487        @param global_conf: PasteDeploy global configuration dictionary
488        @type prefix: basestring
489        @param prefix: prefix for configuration items
490        @type local_conf: dict       
491        @param local_conf: PasteDeploy application specific configuration
492        dictionary
493        '''
494        # Hold SSL Attribute Authority connection settings in SSL Context Proxy
495        # object
496        self.__sslCtxProxy = SSLContextProxy()
497       
498        nameOffset = len(prefix)
499        for k in local_conf.keys():
500            if k.startswith(prefix):
501                val = local_conf.pop(k)
502                name = k[nameOffset:]
503                setattr(self.__sslCtxProxy, name, val)
504               
505        NDGSecurityMiddlewareBase.__init__(self, app, {})
506     
507    @property
508    def sslCtxProxy(self):
509        """SSL Context Proxy object used for setting up an SSL Context for
510        queries to the Attribute Authority
511        """
512        return self.__sslCtxProxy
513   
514           
515    def __call__(self, environ, start_response):
516        """Take a copy of the session object so that it is in scope for
517        _getAttributeCertificate call and add this instance to the environ
518        so that the PEPFilter can retrieve it and pass on to the PDP
519       
520        @type environ: dict
521        @param environ: WSGI environment variables dictionary
522        @type start_response: function
523        @param start_response: standard WSGI start response function
524        @rtype: iterable
525        @return: response
526        """
527        self.session = environ.get(self.sessionKey)
528        if self.session is None:
529            raise SamlPIPMiddlewareConfigError('No beaker session key "%s" '
530                                               'found in environ' % 
531                                               self.sessionKey)
532        environ[SamlPIPMiddleware.ENVIRON_KEYNAME] = self
533       
534        return self._app(environ, start_response)
535   
536    def attributeQuery(self, attributeQuery):
537        """Query the Attribute Authority specified in the request to retrieve
538        the attributes if any corresponding to the subject
539       
540        @type attributeResponse: PIPAttributeQuery
541        @param attributeResponse:
542        @rtype: PIPAttributeResponse
543        @return: response containing the attributes retrieved from the
544        Attribute Authority"""
545       
546        attributeAuthorityURI = attributeQuery[
547                                    PIPAttributeQuery.ATTRIBUTEAUTHORITY_NS]
548       
549        log.debug("SamlPIPMiddleware: received attribute query: %r", 
550                  attributeQuery)
551               
552        # Check for a wallet in the current session - if not present, create
553        # one.  See ndg.security.server.wsgi.authn.SessionHandlerMiddleware
554        # for session keys.  The 'credentialWallet' key is deleted along with
555        # any other security keys when the user logs out
556        credentialWalletKeyName = \
557            SamlPIPMiddleware.CREDENTIAL_WALLET_SESSION_KEYNAME
558        usernameKeyName = SamlPIPMiddleware.USERNAME_SESSION_KEYNAME
559           
560        if not credentialWalletKeyName in self.session:
561            log.debug("SamlPIPMiddleware.attributeQuery: adding a "
562                      "Credential Wallet to user session [%s] ...",
563                      self.session[usernameKeyName])
564           
565            credentialWallet = SamlCredentialWallet()
566            credentialWallet.userId = self.session[usernameKeyName]
567            credentialWallet.sslCtxProxy.copy(self.sslCtxProxy)
568           
569            self.session[credentialWalletKeyName] = credentialWallet
570            self.session.save()
571        else:   
572            # Take reference to wallet for efficiency
573            credentialWallet = self.session[credentialWalletKeyName]   
574       
575        # Check for existing credentials cached in wallet           
576        credentialItem = credentialWallet.credentialsKeyedByURI.get(
577                                                    attributeAuthorityURI)
578        if credentialItem is None:
579            # No assertion is cached - make a fresh query
580            credentialWallet.attributeAuthorityURI = attributeAuthorityURI
581            credentialWallet.attributeQuery()
582            credentialItem = credentialWallet.credentialsKeyedByURI.get(
583                                                    attributeAuthorityURI)
584           
585            log.debug("SamlPIPMiddleware.attributeQuery: updating Credential "
586                      "Wallet with retrieved SAML Attribute Assertion "
587                      "for user session [%s]",
588                      self.session[usernameKeyName])
589        else:
590            log.debug("SamlPIPMiddleware.attributeQuery: retrieved existing "
591                      "SAML Attribute Assertion cached in Credential Wallet "
592                      "for user session [%s]",
593                      self.session[usernameKeyName])
594
595        attributeResponse = PIPAttributeResponse()
596        attributeResponse[Subject.ROLES_NS] = [
597            attribute.value
598            for attribute in credentialItem.credential.attributes
599        ]
600       
601        log.debug("SamlPIPMiddleware.attributeQuery response: %r", 
602                  attributeResponse)
603       
604        return attributeResponse
605   
606           
607from authkit.authenticate.multi import MultiHandler
608
609class AuthorizationMiddlewareError(Exception):
610    """Base class for AuthorizationMiddleware exceptions"""
611   
612class AuthorizationMiddlewareConfigError(Exception):
613    """AuthorizationMiddleware configuration related exceptions"""
614   
615class AuthorizationMiddleware(NDGSecurityMiddlewareBase):
616    '''Handler to call Policy Enforcement Point middleware to intercept
617    requests and enforce access control decisions.  Add THIS class to any
618    WSGI middleware chain ahead of the application(s) which it is to
619    protect.  Use in conjunction with
620    ndg.security.server.wsgi.authn.AuthenticationMiddleware
621    '''
622    PEP_PARAM_PREFIX = 'pep.filter.'
623    PIP_PARAM_PREFIX = 'pip.'
624    PEP_RESULT_HANDLER_PARAMNAME = "pepResultHandler"
625   
626    def __init__(self, app, global_conf, prefix='', **app_conf):
627        """Set-up Policy Enforcement Point to enforce access control decisions
628        based on the URI path requested and/or the HTTP response code set by
629        application(s) to be protected.  An AuthKit MultiHandler is setup to
630        handle the latter.  PEPResultHandlerMiddleware handles the output
631        set following an access denied decision
632        @type app: callable following WSGI interface
633        @param app: next middleware application in the chain     
634        @type global_conf: dict       
635        @param global_conf: PasteDeploy global configuration dictionary
636        @type prefix: basestring
637        @param prefix: prefix for configuration items
638        @type app_conf: dict       
639        @param app_conf: PasteDeploy application specific configuration
640        dictionary
641        """
642        authzPrefix = prefix + AuthorizationMiddleware.PEP_PARAM_PREFIX
643        pepFilter = PEPFilter(app,
644                              global_conf,
645                              prefix=authzPrefix,
646                              **app_conf)
647        pepInterceptFunc = pepFilter.multiHandlerInterceptFactory()
648       
649        # Slot in the Policy Information Point in the WSGI stack at this point
650        # so that it can take a copy of the beaker session object from environ
651        # ahead of the PDP's request to it for an Attribute Certificate
652        pipPrefix = AuthorizationMiddleware.PIP_PARAM_PREFIX
653        pipFilter = SamlPIPMiddleware(pepFilter,
654                                      global_conf,
655                                      prefix=pipPrefix,
656                                      **app_conf)
657        pepFilter.pdp.pip = pipFilter
658       
659        app = MultiHandler(pipFilter)
660
661        pepResultHandlerClassName = app_conf.pop(
662                prefix+AuthorizationMiddleware.PEP_RESULT_HANDLER_PARAMNAME, 
663                None)
664        if pepResultHandlerClassName is None:
665            pepResultHandler = PEPResultHandlerMiddleware
666        else:
667            pepResultHandler = importClass(pepResultHandlerClassName,
668                                        objectType=PEPResultHandlerMiddleware)
669           
670        app.add_method(PEPFilter.MIDDLEWARE_ID,
671                       pepResultHandler.filter_app_factory,
672                       global_conf,
673                       prefix=prefix,
674                       **app_conf)
675       
676        app.add_checker(PEPFilter.MIDDLEWARE_ID, pepInterceptFunc)               
677       
678        super(AuthorizationMiddleware, self).__init__(app,
679                                                      global_conf,
680                                                      prefix=prefix,
681                                                      **app_conf)
682               
Note: See TracBrowser for help on using the repository browser.