Ignore:
Timestamp:
23/08/10 16:32:14 (9 years ago)
Author:
pjkersha
Message:

Incomplete - task 2: XACML-Security Integration

  • implemented caching of authorisation decision statements in the PEP to cut down on calls to authorisation service.
Location:
TI12-security/trunk/NDGSecurity/python/ndg_security_server/ndg/security/server
Files:
4 edited

Legend:

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

    r7287 r7357  
    2222 
    2323from ndg.security.common.utils.classfactory import importClass 
    24 from ndg.security.common.credentialwallet import SAMLCredentialWallet 
     24from ndg.security.common.credentialwallet import SAMLAttributeWallet 
    2525from ndg.security.server.wsgi import NDGSecurityMiddlewareBase 
    2626from ndg.security.server.wsgi.authz.pep import SamlPepFilter 
  • TI12-security/trunk/NDGSecurity/python/ndg_security_server/ndg/security/server/wsgi/authz/pep.py

    r7287 r7357  
    2121from ndg.security.server.wsgi.session import (SessionMiddlewareBase,  
    2222                                              SessionHandlerMiddleware) 
     23from ndg.security.common.credentialwallet import SAMLAuthzDecisionWallet 
     24from ndg.security.common.utils import str2Bool 
    2325 
    2426 
     
    4547    AUTHZ_DECISION_QUERY_PARAMS_PREFIX = 'authzDecisionQuery.' 
    4648    SESSION_KEY_PARAM_NAME = 'sessionKey' 
     49    CACHE_DECISIONS_PARAM_NAME = 'cacheDecisions' 
    4750     
    4851    CREDENTIAL_WALLET_SESSION_KEYNAME = \ 
     
    5356    PARAM_NAMES = ( 
    5457        'authzServiceURI', 
    55         'sessionKey' 
     58        'sessionKey', 
     59        'cacheDecisions' 
    5660    ) 
    5761    __slots__ = ( 
     
    7074        self.__authzServiceURI = None 
    7175        self.__sessionKey = None 
     76        self.__cacheDecisions = False 
    7277 
    7378    def _getClient(self): 
     
    116121                          doc="environ key name for Beaker session object") 
    117122 
     123    def _getCacheDecisions(self): 
     124        return self.__cacheDecisions 
     125 
     126    def _setCacheDecisions(self, value): 
     127        if isinstance(value, basestring): 
     128            self.__cacheDecisions = str2Bool(value) 
     129        elif isinstance(value, bool): 
     130            self.__cacheDecisions = value 
     131        else: 
     132            raise TypeError('Expecting bool/string type for "cacheDecisions" ' 
     133                            'attribute; got %r' % type(value)) 
     134         
     135    cacheDecisions = property(_getCacheDecisions, _setCacheDecisions,  
     136                              doc="Set to True to make the session cache " 
     137                                  "authorisation decisions returned from the " 
     138                                  "Authorisation Service") 
     139     
    118140    def initialise(self, prefix='', **kw): 
    119141        '''Initialise object from keyword settings 
     
    135157            value = kw.get(paramName) 
    136158            if value is None: 
    137                 raise SamlPepFilterConfigError('Missing option %r' %  
    138                                                    paramName) 
     159                raise SamlPepFilterConfigError('Missing option %r' % paramName) 
     160             
    139161            setattr(self, name, value) 
    140162                     
     
    177199         
    178200        request = webob.Request(environ) 
    179         self.client.resourceURI = request.url 
    180          
    181         # Nb. user may not be logged in hence REMOTE_USER is not set 
    182         self.client.subjectID = request.remote_user or '' 
    183          
    184         samlAuthzResponse = self.client.send(uri=self.__authzServiceURI) 
    185  
    186         # Record the result in the user's session to enable later  
    187         # interrogation by any result handler Middleware 
    188         self.setSession(self.client.query, samlAuthzResponse) 
     201         
     202        # Check for cached decision 
     203        if self.cacheDecisions: 
     204            cachedAssertion = self._retrieveAuthzDecision(request.url) 
     205        else: 
     206            cachedAssertion = None    
     207             
     208        if cachedAssertion is not None: 
     209            assertions = (cachedAssertion,) 
     210        else:  
     211            # No stored decision in cache, invoke the authorisation service    
     212            self.client.resourceURI = request.url 
     213             
     214            # Nb. user may not be logged in hence REMOTE_USER is not set 
     215            self.client.subjectID = request.remote_user or '' 
     216             
     217            samlAuthzResponse = self.client.send(uri=self.__authzServiceURI) 
     218            assertions = samlAuthzResponse.assertions 
     219             
     220            # Record the result in the user's session to enable later  
     221            # interrogation by any result handler Middleware 
     222            self.setSession(self.client.query, samlAuthzResponse) 
     223         
    189224         
    190225        # Set HTTP 403 Forbidden response if any of the decisions returned are 
     
    192227        failDecisions = (DecisionType.DENY, DecisionType.INDETERMINATE) 
    193228         
    194         for assertion in samlAuthzResponse.assertions: 
     229        # Review decision statement(s) in assertions and enforce the decision 
     230        assertion = None 
     231        for assertion in assertions: 
    195232            for authzDecisionStatement in assertion.authzDecisionStatements: 
    196233                if authzDecisionStatement.decision.value in failDecisions: 
     
    211248                    return response(environ, start_response) 
    212249 
     250        if assertion is None: 
     251            log.error("No assertions set in authorisation decision response " 
     252                      "from %r", self.authzServiceURI) 
     253             
     254            response.body = ('An error occurred retrieving an access decision ' 
     255                             'for %r for user %r' % ( 
     256                                             self.client.resourceURI, 
     257                                             self.client.subjectID)) 
     258            response.content_type = 'text/plain' 
     259            log.info(response.body) 
     260            return response(environ, start_response)      
     261                
     262        if self.cacheDecisions: 
     263            self._cacheAuthzDecision(assertion) 
     264             
    213265        # If got through to here then all is well, call next WSGI middleware/app 
    214266        return self._app(environ, start_response) 
    215267 
     268    def _retrieveAuthzDecision(self, resourceId): 
     269        """Return assertion containing authorisation decision for the given 
     270        resource ID. 
     271         
     272        @param resourceId: search for decisions for this resource Id 
     273        @type resourceId: basestring 
     274        @return: assertion containing authorisation decision for the given 
     275        resource ID or None if no wallet has been set or no assertion was  
     276        found matching the input resource Id 
     277        @rtype: ndg.saml.saml2.core.Assertion / None type 
     278        """ 
     279        # Get reference to wallet 
     280        walletKeyName = self.__class__.CREDENTIAL_WALLET_SESSION_KEYNAME 
     281        credWallet = self.session.get(walletKeyName) 
     282         
     283        # Wallet has a dictionary of credential objects keyed by resource ID 
     284        credentials = getattr(credWallet, 'credentials', {}) 
     285         
     286        # Retrieve assertion from Credential object 
     287        assertion = getattr(credentials.get(resourceId), 'credential', None) 
     288        return assertion 
     289         
     290         
     291    def _cacheAuthzDecision(self, assertion): 
     292        """Cache an authorisation decision from a response retrieved from the  
     293        authorisation service.  This is invoked only if cacheDecisions boolean 
     294        is set to True 
     295         
     296        @param assertion: SAML assertion containing authorisation decision 
     297        @type assertion: ndg.saml.saml2.core.Assertion 
     298        """ 
     299        walletKeyName = self.__class__.CREDENTIAL_WALLET_SESSION_KEYNAME 
     300        credWallet = self.session.get(walletKeyName) 
     301        if credWallet is None: 
     302            credWallet = SAMLAuthzDecisionWallet() 
     303         
     304        credWallet.addCredential(assertion) 
     305        self.session[walletKeyName] = credWallet 
     306        self.session.save() 
     307         
    216308    def setSession(self, request, response, save=True): 
    217309        """Set PEP context information in the Beaker session using standard key 
    218         names 
     310        names.  This is a snapshot of the last request and the response  
     311        received.  It can be used by downstream middleware to provide contextual 
     312        information about authorisation decisions 
    219313         
    220314        @param session: beaker session 
  • TI12-security/trunk/NDGSecurity/python/ndg_security_server/ndg/security/server/xacml/ctx_handler/saml_ctx_handler.py

    r7350 r7357  
    232232            return None 
    233233        else: 
    234             return self.__issuerProxy.value 
     234            return self.__issuerProxy.format 
    235235 
    236236    def _setIssuerFormat(self, value): 
     
    461461        assertion.version = SAMLVersion(SAMLVersion.VERSION_20) 
    462462        assertion.id = str(uuid4()) 
     463         
     464        assertion.issuer = _saml.Issuer() 
     465        assertion.issuer.value = self.issuerName 
     466        assertion.issuer.format = self.issuerFormat 
    463467         
    464468        now = datetime.utcnow() 
  • TI12-security/trunk/NDGSecurity/python/ndg_security_server/ndg/security/server/xacml/pip/saml_pip.py

    r7350 r7357  
    234234        @return: attribute values found for query subject or None if none  
    235235        could be found 
    236         @raise PIPConfigException: if attribute ID -> Attribute Authority mapping is 
    237         empty   
     236        @raise PIPConfigException: if attribute ID -> Attribute Authority  
     237        mapping is empty   
    238238        """ 
    239239         
     
    275275                if attribute.attributeId == self.subjectAttributeId: 
    276276                    if len(attribute.attributeValues) != 1: 
    277                         raise Exception("Expecting a single attribute value " 
    278                                         "for query subject ID") 
     277                        raise PIPRequestCtxException("Expecting a single " 
     278                                                     "attribute value " 
     279                                                     "for query subject ID") 
    279280                    subjectId = attribute.attributeValues[0].value 
    280281                    break 
     
    294295        # query 
    295296        attributeFormat = attributeDesignator.dataType 
     297        attributeId = attributeDesignator.attributeId 
     298         
    296299        samlAttribute = SamlAttribute() 
    297300        samlAttribute.name = attributeDesignator.attributeId 
     
    321324        # format specified and copy into XACML attributes       
    322325        xacmlAttribute = XacmlAttribute() 
    323         xacmlAttribute.attributeId = attributeDesignator.attributeId 
     326        xacmlAttribute.attributeId = attributeId 
    324327        xacmlAttribute.dataType = attributeFormat 
    325328         
     
    342345         
    343346        # Update the XACML request context subject with the new attributes 
    344         xacmlCtxSubject.attributes.append(xacmlAttribute) 
     347        matchFound = False 
     348        for attr in xacmlCtxSubject.attributes: 
     349            matchFound = attr.attributeId == attributeId 
     350            if matchFound: 
     351                newAttrVals = [attrVal  
     352                               for attrVal in xacmlAttribute.attributeValues  
     353                               if attrVal not in attr.attributeValues] 
     354                attr.attributeValues.extend(newAttrVals) 
     355                break 
     356             
     357        if not matchFound: 
     358            xacmlCtxSubject.attributes.append(xacmlAttribute) 
    345359         
    346360        # Return the attributes to the caller to comply with the interface 
Note: See TracChangeset for help on using the changeset viewer.