Changeset 7359 for TI12-security


Ignore:
Timestamp:
24/08/10 16:39:39 (9 years ago)
Author:
pjkersha
Message:

Incomplete - task 2: XACML-Security Integration

  • simplified credential wallet - now a single class SAMLAssertionWallet for caching assertions containing authorisation decision statements and/or attribute statements
Location:
TI12-security/trunk/NDGSecurity/python
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • TI12-security/trunk/NDGSecurity/python/ndg_security_common/ndg/security/common/credentialwallet.py

    r7358 r7359  
    306306            setattr(self, optName, val) 
    307307          
    308     def addCredentials(self, key, credentials, verifyCredentials=True): 
     308    def addCredentials(self, key, assertions, verifyCredentials=True): 
    309309        """Add a new assertion to the list of assertion credentials held. 
    310310 
    311         @type credentials: iterable 
    312         @param credential: list of SAML assertions for a given issuer 
     311        @type assertions: iterable 
     312        @param assertions: list of SAML assertions for a given issuer 
    313313        @type key: basestring 
    314314        @param key: key by which these credentials should be referred to 
     
    317317        by calling isValidCredential method. 
    318318        """         
    319         for credential in credentials: 
    320             if not isinstance(credential, Assertion): 
     319        for assertion in assertions: 
     320            if not isinstance(assertion, Assertion): 
    321321                raise TypeError("Input credentials must be %r type; got %r" % 
    322                                 (Assertion, credential)) 
     322                                (Assertion, assertion)) 
    323323                 
    324             elif verifyCredentials and not self.isValidCredential(credential): 
     324            elif verifyCredentials and not self.isValidCredential(assertion): 
    325325                raise CredentialWalletError("Validity time error with " 
    326                                             "assertion %r" % credential) 
    327          
    328         # Check to see if there are existing credentials held under the same key 
    329         # If so, compare the expiry time.  The one with the latest expiry will  
    330         # be retained and the other ignored 
    331         bUpdateCred = True 
    332         if key in self.__assertionsMap: 
    333             # There is an existing certificate held with the same issuing 
    334             # host name as the new certificate 
    335             credentialOld = self.__assertionsMap[key].credential 
    336  
    337             # If the new certificate has an earlier expiry time then ignore it 
    338             bUpdateCred = (credential.conditions.notOnOrAfter >  
    339                            credentialOld.conditions.notOnOrAfter) 
    340              
    341         if bUpdateCred: 
    342             self.__assertionsMap[key] = credentials 
     326                                            "assertion %r" % assertion) 
     327         
     328        # Any existing credentials are overwritten 
     329        self.__assertionsMap[key] = assertions 
    343330 
    344331    def retrieveCredentials(self, key): 
     
    360347        for k, v in self.__assertionsMap.items(): 
    361348            creds = [credential for credential in v 
    362                      if not self.isValidCredential(credential)] 
     349                     if self.isValidCredential(credential)] 
    363350            if len(creds) > 0: 
    364                 self.__assertionsMap[k] = TypedList(Assertion) 
    365                 self.__assertionsMap[k].extend(creds) 
     351                self.__assertionsMap[k] = creds 
    366352            else: 
    367353                del self.__assertionsMap[k] 
     
    409395             
    410396        return _dict 
    411  
    412  
    413 class SAMLAttributeWallet(SAMLAssertionWallet): 
    414     """Wallet with functionality for retrieving assertions containing attribute  
    415     statements based on endpoint URI and issuer name 
    416     """ 
    417     __slots__ = ("__assertionsMapKeyedByURI",) 
    418      
    419     def __init__(self): 
    420         super(SAMLAttributeWallet, self).__init__() 
    421         self.__assertionsMapKeyedByURI = {} 
    422  
    423     def retrieveCredentialsByURI(self, issuerEndpoint): 
    424         """Get Property method for credentials keyed by issuing service URI 
    425         Credentials dict is read-only but also see addCredentials method 
    426          
    427         @rtype: dict 
    428         @return: cached ACs indexed by issuing service URI""" 
    429         return self.__assertionsMapKeyedByURI.get(issuerEndpoint) 
    430                 
    431     def audit(self): 
    432         """Check the credentials held in the wallet removing any that have 
    433         expired or are otherwise invalid.""" 
    434  
    435         log.debug("SAMLAssertionWallet.audit ...") 
    436          
    437         for issuerName, issuerEntry in self.__assertionsMap.items(): 
    438             if not self.isValidCredential(issuerEntry.credential): 
    439                 foundMatch = False 
    440                 endpoint = None 
    441                 for endpoint, v in self.__assertionsMapKeyedByURI.items(): 
    442                     if v.issuerName.value == issuerName: 
    443                         foundMatch = True 
    444                         break 
    445                  
    446                 if foundMatch: 
    447                     self.__assertionsMapKeyedByURI.pop(endpoint, None) 
    448                      
    449                 del self.__assertionsMap[issuerName] 
    450      
    451     def __getstate__(self): 
    452         '''Enable pickling for use with beaker.session''' 
    453         _dict = super(SAMLAttributeWallet, self).__getstate__() 
    454          
    455         for attrName in SAMLAttributeWallet.__slots__: 
    456             # Ugly hack to allow for derived classes setting private member 
    457             # variables 
    458             if attrName.startswith('__'): 
    459                 attrName = "_SAMLAttributeWallet" + attrName 
    460                  
    461             _dict[attrName] = getattr(self, attrName) 
    462              
    463         return _dict                    
    464      
    465      
    466 class SAMLAuthzDecisionWallet(SAMLAssertionWallet): 
    467     """Wallet with functionality for retrieving assertions containing  
    468     authorisation decision statements.  Credentials are keyed by resource Id""" 
    469     __slots__ = () 
    470      
    471     def addCredentials(self, assertions, verifyCredential=True): 
    472         """Add new assertions from a given issuer 
    473  
    474         @type assertions: SAML assertion 
    475         @param assertions: new assertion to be added 
    476         @type verifyCredential: bool 
    477         @param verifyCredential: if set to True, test validity of credential 
    478         by calling isValidCredential method. 
    479          
    480         @rtype: bool 
    481         @return: True if credential was added otherwise False.  - If an 
    482         existing certificate from the same issuer has a later expiry it will 
    483         take precedence and the new input certificate is ignored.""" 
    484          
    485         # Check input 
    486         if not isinstance(assertion, Assertion): 
    487             raise CredentialWalletError("Input assertion must be an " 
    488                                         "%r type object" % Assertion)         
    489  
    490         if verifyCredential and not self.isValidCredential(assertion): 
    491             raise CredentialWalletError("Validity time error with assertion %r" 
    492                                         % assertion) 
    493          
    494         # Check to see if there is an existing decision statement held for the 
    495         # same resource ID.  If so, compare the expiry time.  The one with the  
    496         # latest expiry will be retained and the other ignored 
    497         bUpdateCred = True 
    498          
    499         # Get the resource ID from the authorisation decision statement 
    500         # Nb. Assumes a SINGLE statement 
    501         if len(assertion.authzDecisionStatements) != 1: 
    502             raise CredentialWalletError("Expecting a single authorisation " 
    503                                         "decision statement passed with the " 
    504                                         "input assertion; found %r", 
    505                                         len(assertion.authzDecisionStatements)) 
    506          
    507         resourceId = assertion.authzDecisionStatements[0].resource         
    508         if resourceId in self.__assertionsMap: 
    509             # There is an existing certificate held with the same issuing 
    510             # host name as the new certificate 
    511             assertionOld = self.__assertionsMap[resourceId].credential 
    512  
    513             # If the new certificate has an earlier expiry time then ignore it 
    514             bUpdateCred = (assertion.conditions.notOnOrAfter >  
    515                            assertionOld.conditions.notOnOrAfter) 
    516  
    517         if bUpdateCred: 
    518             thisCredential = CredentialContainer(Assertion) 
    519             thisCredential.credential = assertion 
    520              
    521             if assertion.issuer.value: 
    522                 thisCredential.issuerName = assertion.issuer.value 
    523                  
    524             self.__assertionsMap[resourceId] = thisCredential 
    525                  
    526     def __getstate__(self): 
    527         '''Enable pickling for use with beaker.session''' 
    528         _dict = super(SAMLAuthzDecisionWallet, self).__getstate__() 
    529          
    530         for attrName in SAMLAuthzDecisionWallet.__slots__: 
    531             # Ugly hack to allow for derived classes setting private member 
    532             # variables 
    533             if attrName.startswith('__'): 
    534                 attrName = "_SAMLAuthzDecisionWallet" + attrName 
    535                  
    536             _dict[attrName] = getattr(self, attrName) 
    537              
    538         return _dict         
    539397         
    540398         
  • TI12-security/trunk/NDGSecurity/python/ndg_security_server/ndg/security/server/wsgi/authz/pep.py

    r7357 r7359  
    2121from ndg.security.server.wsgi.session import (SessionMiddlewareBase,  
    2222                                              SessionHandlerMiddleware) 
    23 from ndg.security.common.credentialwallet import SAMLAuthzDecisionWallet 
     23from ndg.security.common.credentialwallet import SAMLAssertionWallet 
    2424from ndg.security.common.utils import str2Bool 
    2525 
     
    261261                
    262262        if self.cacheDecisions: 
    263             self._cacheAuthzDecision(assertion) 
     263            self._cacheAuthzDecision(request.url, [assertion]) 
    264264             
    265265        # If got through to here then all is well, call next WSGI middleware/app 
     
    289289         
    290290         
    291     def _cacheAuthzDecision(self, assertion): 
     291    def _cacheAuthzDecision(self, resourceId, assertions): 
    292292        """Cache an authorisation decision from a response retrieved from the  
    293293        authorisation service.  This is invoked only if cacheDecisions boolean 
    294294        is set to True 
    295295         
    296         @param assertion: SAML assertion containing authorisation decision 
    297         @type assertion: ndg.saml.saml2.core.Assertion 
     296        @param resourceId: search for decisions for this resource Id 
     297        @type resourceId: basestring 
     298        @param assertions: list of SAML assertions containing authorisation  
     299        decision statements 
     300        @type assertions: iterable 
    298301        """ 
    299302        walletKeyName = self.__class__.CREDENTIAL_WALLET_SESSION_KEYNAME 
    300303        credWallet = self.session.get(walletKeyName) 
    301304        if credWallet is None: 
    302             credWallet = SAMLAuthzDecisionWallet() 
    303          
    304         credWallet.addCredential(assertion) 
     305            credWallet = SAMLAssertionWallet() 
     306         
     307        credWallet.addCredentials(resourceId, assertions) 
    305308        self.session[walletKeyName] = credWallet 
    306309        self.session.save() 
  • TI12-security/trunk/NDGSecurity/python/ndg_security_server/ndg/security/server/xacml/pip/saml_pip.py

    r7358 r7359  
    6767        if 'wallet' not in self.__session: 
    6868            self.__session['wallet'] = SAMLAssertionWallet() 
     69        else: 
     70            # Prune expired assertions 
     71            self.__session['wallet'].audit() 
    6972     
    7073    def add(self, assertions, issuerEndpoint): 
  • TI12-security/trunk/NDGSecurity/python/ndg_security_test/ndg/security/test/unit/credentialwallet/test_credentialwallet.py

    r7356 r7359  
    3030from ndg.security.test.unit import BaseTestCase 
    3131from ndg.security.common.utils.etree import prettyPrint 
    32 from ndg.security.common.credentialwallet import (SAMLAttributeWallet, 
    33                                                   SAMLAuthzDecisionWallet) 
     32from ndg.security.common.credentialwallet import SAMLAssertionWallet 
    3433 
    3534 
     
    9493        return AssertionElementTree.fromXML(assertionElem) 
    9594 
    96     def _addCredential(self): 
    97         wallet = SAMLAttributeWallet()    
    98         wallet.addCredential(self.assertion, issuerEndpoint=\ 
    99         self.__class__.SITEA_ATTRIBUTEAUTHORITY_SAML_URI) 
     95    def _addCredentials(self): 
     96        wallet = SAMLAssertionWallet()    
     97        wallet.addCredentials(self.__class__.SITEA_ATTRIBUTEAUTHORITY_SAML_URI, 
     98                              [self.assertion]) 
    10099        return wallet 
    101100     
    102     def test01AddCredential(self): 
    103         wallet = self._addCredential() 
    104          
    105         self.assert_(len(wallet.credentials) == 1) 
    106         self.assert_(self.__class__.SITEA_ATTRIBUTEAUTHORITY_SAML_URI in \ 
    107                      wallet.credentialsKeyedByURI) 
    108         self.assert_(self.__class__.SITEA_SAML_ISSUER_NAME in \ 
    109                      wallet.credentials) 
    110          
    111         assertion = wallet.credentials[ 
    112             self.__class__.SITEA_SAML_ISSUER_NAME 
    113         ].credential 
     101    def test01AddCredentials(self): 
     102        wallet = self._addCredentials() 
     103        k = self.__class__.SITEA_ATTRIBUTEAUTHORITY_SAML_URI 
     104        self.assert_(len(wallet.retrieveCredentials(k)) == 1) 
     105        assertions = wallet.retrieveCredentials( 
     106                            self.__class__.SITEA_ATTRIBUTEAUTHORITY_SAML_URI) 
     107        self.assert_(assertions) 
    114108         
    115109        print("SAML Assertion:\n%s" %  
    116               prettyPrint(AssertionElementTree.toXML(assertion))) 
     110              prettyPrint(AssertionElementTree.toXML(assertions[0]))) 
    117111     
    118112    def test02VerifyCredential(self): 
    119         wallet = SAMLAttributeWallet() 
     113        wallet = SAMLAssertionWallet() 
    120114        self.assert_(wallet.isValidCredential(self.assertion)) 
    121115         
     
    130124        self.assert_(not wallet.isValidCredential(futureAssertion)) 
    131125         
    132     def test03AuditCredential(self): 
     126    def test03AuditCredentials(self): 
    133127        # Add a short lived credential and ensure it's removed when an audit 
    134128        # is carried to prune expired credentials 
    135129        shortExpiryAssertion = self._createAssertion(validityDuration=1) 
    136         wallet = SAMLAttributeWallet() 
    137         wallet.addCredential(shortExpiryAssertion) 
    138          
    139         self.assert_(len(wallet.credentials) == 1) 
     130        wallet = SAMLAssertionWallet() 
     131        wallet.addCredentials('a', [shortExpiryAssertion]) 
     132         
     133        self.assert_(wallet.retrieveCredentials('a')) 
    140134        sleep(2) 
    141135        wallet.audit() 
    142         self.assert_(len(wallet.credentials) == 0) 
     136        self.assert_(wallet.retrieveCredentials('a') is None) 
    143137 
    144138    def test04ClockSkewTolerance(self): 
     
    146140        # a clock skew of  
    147141        shortExpiryAssertion = self._createAssertion(validityDuration=1) 
    148         wallet = SAMLAttributeWallet() 
     142        wallet = SAMLAssertionWallet() 
    149143         
    150144        # Set a tolerance of five seconds 
    151145        wallet.clockSkewTolerance = 5.*60*60 
    152         wallet.addCredential(shortExpiryAssertion) 
    153          
    154         self.assert_(len(wallet.credentials) == 1) 
     146        wallet.addCredentials('a', [shortExpiryAssertion]) 
     147         
     148        self.assert_(wallet.retrieveCredentials('a')) 
    155149        sleep(2) 
    156150        wallet.audit() 
    157         self.assert_(len(wallet.credentials) == 1) 
     151        self.assert_(wallet.retrieveCredentials('a')) 
    158152         
    159153    def test05ReplaceCredential(self): 
    160154        # Replace an existing credential from a given institution with a more 
    161155        # up to date one 
    162         wallet = self._addCredential() 
    163         self.assert_(len(wallet.credentials) == 1) 
     156        k = self.__class__.SITEA_ATTRIBUTEAUTHORITY_SAML_URI 
     157        wallet = self._addCredentials() 
     158        self.assert_(len(wallet.retrieveCredentials(k)) == 1) 
    164159         
    165160        newAssertion = self._createAssertion()   
    166161 
    167         wallet.addCredential(newAssertion) 
    168         self.assert_(len(wallet.credentials) == 1) 
     162        wallet.addCredentials(k, [newAssertion]) 
     163        self.assert_(len(wallet.retrieveCredentials(k)) == 1) 
    169164        self.assert_(newAssertion.conditions.notOnOrAfter == \ 
    170                      wallet.credentials[ 
    171                         self.__class__.SITEA_SAML_ISSUER_NAME 
    172                     ].credential.conditions.notOnOrAfter) 
    173          
    174     def test06CredentialsFromSeparateSites(self): 
    175         wallet = self._addCredential() 
    176         wallet.addCredential(self._createAssertion(issuerName="MySite")) 
    177         self.assert_(len(wallet.credentials) == 2) 
     165                     wallet.retrieveCredentials(k)[0].conditions.notOnOrAfter) 
     166         
     167    def test06CredentialsFromSeparateKeys(self): 
     168        wallet = self._addCredentials() 
     169        wallet.addCredentials("MySite", 
     170                              [self._createAssertion(issuerName="MySite"), 
     171                               self._createAssertion()]) 
     172        self.assert_(len(wallet.retrieveCredentials("MySite")) == 2) 
     173        k = self.__class__.SITEA_ATTRIBUTEAUTHORITY_SAML_URI 
     174        self.assert_(len(wallet.retrieveCredentials(k)) == 1) 
    178175 
    179176    def test07Pickle(self): 
    180         wallet = self._addCredential() 
     177        wallet = self._addCredentials() 
    181178        outFile = open(self.__class__.PICKLE_FILEPATH, 'w') 
    182179        pickle.dump(wallet, outFile) 
     
    185182        inFile = open(self.__class__.PICKLE_FILEPATH) 
    186183        unpickledWallet = pickle.load(inFile) 
    187         self.assert_(unpickledWallet.credentialsKeyedByURI.get( 
    188             self.__class__.SITEA_ATTRIBUTEAUTHORITY_SAML_URI)) 
    189          
    190         self.assert_(unpickledWallet.credentials.items()[0][1].issuerName == \ 
    191                      BaseTestCase.SITEA_SAML_ISSUER_NAME) 
     184         
     185        assertions = unpickledWallet.retrieveCredentials( 
     186            self.__class__.SITEA_ATTRIBUTEAUTHORITY_SAML_URI) 
     187        self.assert_(assertions) 
     188         
     189        self.assert_(assertions[0].issuer.value == \ 
     190                     self.__class__.SITEA_SAML_ISSUER_NAME) 
    192191 
    193192    def test08CreateFromConfig(self): 
    194         wallet = SAMLAttributeWallet.fromConfig( 
     193        wallet = SAMLAssertionWallet.fromConfig( 
    195194                                self.__class__.CONFIG_FILEPATH) 
    196195        self.assert_(wallet.clockSkewTolerance == timedelta(seconds=0.01)) 
     
    243242        return AssertionElementTree.fromXML(assertionElem) 
    244243                     
    245     def _addCredential(self): 
    246         wallet = SAMLAuthzDecisionWallet()    
    247         wallet.addCredential(self.assertion) 
     244    def _addCredentials(self): 
     245        wallet = SAMLAssertionWallet()    
     246        wallet.addCredentials(self.__class__.RESOURCE_ID, [self.assertion]) 
    248247        return wallet 
    249248     
    250     def test01AddCredential(self): 
    251         wallet = self._addCredential() 
    252          
    253         self.assert_(len(wallet.credentials) == 1) 
    254         self.assert_(self.__class__.RESOURCE_ID in wallet.credentials) 
    255  
    256         assertion = wallet.credentials[self.__class__.RESOURCE_ID].credential 
     249    def test01AddCredentials(self): 
     250        wallet = self._addCredentials() 
     251         
     252        self.assert_( 
     253            len(wallet.retrieveCredentials(self.__class__.RESOURCE_ID)) == 1) 
     254 
     255        assertion = wallet.retrieveCredentials(self.__class__.RESOURCE_ID)[-1] 
    257256         
    258257        print("SAML Assertion:\n%s" %  
     
    260259     
    261260    def test02VerifyCredential(self): 
    262         wallet = SAMLAttributeWallet() 
     261        wallet = SAMLAssertionWallet() 
    263262        self.assert_(wallet.isValidCredential(self.assertion)) 
    264263         
     
    272271 
    273272        self.assert_(not wallet.isValidCredential(futureAssertion)) 
    274          
    275     def test03AuditCredential(self): 
    276         # Add a short lived credential and ensure it's removed when an audit 
    277         # is carried to prune expired credentials 
    278         shortExpiryAssertion = self._createAssertion(validityDuration=1) 
    279         wallet = SAMLAttributeWallet() 
    280         wallet.addCredential(shortExpiryAssertion) 
    281          
    282         self.assert_(len(wallet.credentials) == 1) 
    283         sleep(2) 
    284         wallet.audit() 
    285         self.assert_(len(wallet.credentials) == 0) 
    286  
    287     def test04ClockSkewTolerance(self): 
    288         # Add a short lived credential but with the wallet set to allow for 
    289         # a clock skew of  
    290         shortExpiryAssertion = self._createAssertion(validityDuration=1) 
    291         wallet = SAMLAuthzDecisionWallet() 
    292          
    293         # Set a tolerance of five seconds 
    294         wallet.clockSkewTolerance = 5.*60*60 
    295         wallet.addCredential(shortExpiryAssertion) 
    296          
    297         self.assert_(len(wallet.credentials) == 1) 
    298         sleep(2) 
    299         wallet.audit() 
    300         self.assert_(len(wallet.credentials) == 1) 
    301          
    302     def test05ReplaceCredential(self): 
    303         # Replace an existing credential from a given institution with a more 
    304         # up to date one 
    305         wallet = self._addCredential() 
    306         self.assert_(len(wallet.credentials) == 1) 
    307          
    308         newAssertion = self._createAssertion()   
    309  
    310         wallet.addCredential(newAssertion) 
    311         self.assert_(len(wallet.credentials) == 1) 
    312         self.assert_(newAssertion.conditions.notOnOrAfter == \ 
    313                      wallet.credentials[ 
    314                         self.__class__.RESOURCE_ID 
    315                     ].credential.conditions.notOnOrAfter) 
    316273 
    317274    def test06Pickle(self): 
    318         wallet = self._addCredential() 
     275        wallet = self._addCredentials() 
    319276        outFile = open(self.__class__.PICKLE_FILEPATH, 'w') 
    320277        pickle.dump(wallet, outFile) 
     
    323280        inFile = open(self.__class__.PICKLE_FILEPATH) 
    324281        unpickledWallet = pickle.load(inFile) 
    325         self.assert_(unpickledWallet.credentials.get( 
     282        self.assert_(unpickledWallet.retrieveCredentials( 
    326283                                                    self.__class__.RESOURCE_ID)) 
    327284         
    328285    def test07CreateFromConfig(self): 
    329         wallet = SAMLAttributeWallet.fromConfig( 
     286        wallet = SAMLAssertionWallet.fromConfig( 
    330287                                self.__class__.CONFIG_FILEPATH) 
    331288        self.assert_(wallet.clockSkewTolerance == timedelta(seconds=0.01)) 
Note: See TracChangeset for help on using the changeset viewer.