Ignore:
Timestamp:
24/08/10 15:34:07 (9 years ago)
Author:
pjkersha
Message:

Incomplete - task 2: XACML-Security Integration

  • added caching capability to Policy Information Point. This enables the PIP to retrieve previously cached assertions from an Attribute Authority optimising performance. Caching is done with beaker.session but instead of indexing based on a cookie, it's based on the subject Id i.e. for ESG, a user's OpenID.
File:
1 edited

Legend:

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

    r7357 r7358  
    2222from ConfigParser import ConfigParser 
    2323 
     24try: 
     25    from abc import ABCMeta, abstractmethod 
     26except ImportError: 
     27    # Allow for Python version < 2.6 
     28    ABCMeta = type 
     29    abstractmethod = lambda f: f 
     30     
    2431from ndg.saml.utils import SAMLDateTime 
    2532from ndg.saml.saml2.core import Assertion 
    2633 
     34from ndg.security.common.utils import TypedList 
    2735from ndg.security.common.utils.configfileparsers import (      
    2836                                                    CaseSensitiveConfigParser,) 
     
    3846    to enable writing to the log""" 
    3947 
    40            
    41 class _MetaCredentialWallet(type): 
    42     """Enable CredentialWallet to have read only class variables e.g. 
    43      
    44     print CredentialWallet.accessDenied  
    45      
    46     ... is allowed but, 
    47      
    48     CredentialWallet.accessDenied = None 
    49      
    50     ... raises - AttributeError: can't set attribute""" 
    51      
    52     def _getAccessDenied(cls): 
    53         '''accessDenied get method''' 
    54         return False 
    55      
    56     accessDenied = property(fget=_getAccessDenied) 
    57      
    58     def _getAccessGranted(cls): 
    59         '''accessGranted get method''' 
    60         return True 
    61      
    62     accessGranted = property(fget=_getAccessGranted) 
    63  
    6448 
    6549class CredentialContainer(object): 
    6650    """Container for cached credentials""" 
    6751    ID_ATTRNAME = 'id' 
    68     ITEM_ATTRNAME = 'credential' 
     52    ITEM_ATTRNAME = 'credentials' 
    6953    ISSUERNAME_ATTRNAME = 'issuerName' 
    7054    CREDENTIAL_TYPE_ATTRNAME = 'type' 
     
    8468         
    8569        self.__id = -1 
    86         self.__credential = None 
     70        self.__assertionsMap = None 
    8771        self.__issuerName = None 
    8872 
     
    11397                      "set to -1 for new credentials") 
    11498 
    115     def _getCredential(self): 
    116         return self.__credential 
    117  
    118     def _setCredential(self, value): 
     99    def _getCredentials(self): 
     100        return self.__assertionsMap 
     101 
     102    def _setCredentials(self, value): 
    119103        # Safeguard type attribute referencing for unpickling process - this 
    120104        # method may be called before type attribute has been set 
     
    123107                        None) 
    124108         
    125         if _type is not None and not isinstance(value, _type): 
    126             raise TypeError('Expecting %r type for "credential" attribute; ' 
    127                             'got %r' % type(value)) 
    128         self.__credential = value 
    129  
    130     credential = property(_getCredential,  
    131                           _setCredential,  
    132                           doc="Credential object") 
     109        if (_type is not None and  
     110            not isinstance(value, TypedList) and 
     111            value.elementType != _type): 
     112            raise TypeError('Expecting TypedList(%s) type for "credentials" ' 
     113                            'attribute; got %r' % (_type, type(value))) 
     114        self.__assertionsMap = value 
     115 
     116    credentials = property(_getCredentials, _setCredentials,  
     117                           doc="Credentials object") 
    133118 
    134119    def _getIssuerName(self): 
     
    140125    issuerName = property(_getIssuerName,  
    141126                          _setIssuerName,  
    142                           doc="Name of issuer of the credential") 
     127                          doc="Name of issuer of the credentials") 
    143128 
    144129    def __getstate__(self): 
     
    162147    """  
    163148    CONFIG_FILE_OPTNAMES = ("userId", ) 
    164     __slots__ = ("__credentials", "__userId") 
     149    __metaclass__ = ABCMeta 
     150    __slots__ = ("__userId", ) 
    165151     
    166152    def __init__(self): 
    167153        self.__userId = None 
    168         self.__credentials = {} 
    169154 
    170155    @classmethod 
     
    193178        raise NotImplementedError(CredentialWalletBase.parseConfig.__doc__) 
    194179 
    195     def addCredential(self,  
    196                       credential,  
    197                       issuerEndpoint=None, 
    198                       bUpdateCredentialRepository=True): 
     180    @abstractmethod 
     181    def addCredentials(self, key, credentials): 
    199182        """Add a new attribute certificate to the list of credentials held. 
    200183 
    201         @type credential: determined by derived class implementation e.g. 
    202         SAML assertion 
    203         @param credential: new attribute Certificate to be added 
    204         @type issuerEndpoint: basestring 
    205         @param issuerEndpoint: input the URI of issuing service from 
    206         which credential was retrieved.  This is added to a dict to enable  
    207         access to a given Attribute Certificate keyed by issuing service  
    208         URI. See the getCredential method. 
    209         @type bUpdateCredentialRepository: bool 
    210         @param bUpdateCredentialRepository: if set to True, and a repository  
    211         exists it will be updated with the new credentials also 
    212          
    213         @rtype: bool 
    214         @return: True if certificate was added otherwise False.  - If an 
    215         existing certificate from the same issuer has a later expiry it will 
    216         take precedence and the new input certificate is ignored.""" 
    217         raise NotImplementedError(CredentialWalletBase.addCredential.__doc__) 
    218              
     184        @type key: basestring 
     185        @param key: key to use to retrieve the credential 
     186        @type credentials: determined by derived class implementation e.g. 
     187        list of SAML assertions 
     188        @param credentials: new credentials to be added 
     189        """ 
     190        raise NotImplementedError(CredentialWalletBase.addCredentials.__doc__) 
     191             
     192    @abstractmethod 
    219193    def audit(self): 
    220194        """Check the credentials held in the wallet removing any that have 
     
    222196        raise NotImplementedError(CredentialWalletBase.audit.__doc__) 
    223197 
     198    @abstractmethod 
    224199    def updateCredentialRepository(self, auditCred=True): 
    225200        """Copy over non-persistent credentials held by wallet into the 
     
    232207                    CredentialWalletBase.updateCredentialRepository.__doc__) 
    233208         
    234     def _getCredentials(self): 
    235         """Get Property method.  Credentials dict is read-only but also see  
    236         addCredential method 
    237          
    238         @rtype: dict 
    239         @return: cached ACs indesed by issuing organisation name""" 
    240         return self.__credentials 
    241  
    242     # Publish attribute 
    243     credentials = property(fget=_getCredentials, 
    244                            doc="List of credentials linked to issuing " 
    245                                "authorities") 
     209    @abstractmethod 
     210    def retrieveCredentials(self, key): 
     211        """Retrieve Credentials corresponding to the given key 
     212         
     213        @rtype: list 
     214        @return: cached credentials indexed by key""" 
     215        return self.__credentials.get(issuer) 
    246216         
    247217    def _getUserId(self): 
     
    281251    CONFIG_FILE_OPTNAMES = CredentialWalletBase.CONFIG_FILE_OPTNAMES + ( 
    282252                           "clockSkewTolerance", ) 
    283     __slots__ = ("__clockSkewTolerance",) 
     253    __slots__ = ("__clockSkewTolerance", "__assertionsMap") 
    284254 
    285255    def __init__(self): 
    286256        super(SAMLAssertionWallet, self).__init__() 
    287257        self.__clockSkewTolerance = timedelta(seconds=0.) 
     258        self.__assertionsMap = {} 
    288259     
    289260    def _getClockSkewTolerance(self): 
     
    334305                 
    335306            setattr(self, optName, val) 
     307          
     308    def addCredentials(self, key, credentials, verifyCredentials=True): 
     309        """Add a new assertion to the list of assertion credentials held. 
     310 
     311        @type credentials: iterable 
     312        @param credential: list of SAML assertions for a given issuer 
     313        @type key: basestring 
     314        @param key: key by which these credentials should be referred to 
     315        @type verifyCredential: bool 
     316        @param verifyCredential: if set to True, test validity of credential 
     317        by calling isValidCredential method. 
     318        """         
     319        for credential in credentials: 
     320            if not isinstance(credential, Assertion): 
     321                raise TypeError("Input credentials must be %r type; got %r" % 
     322                                (Assertion, credential)) 
     323                 
     324            elif verifyCredentials and not self.isValidCredential(credential): 
     325                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 
     343 
     344    def retrieveCredentials(self, key): 
     345        """Retrieve credentials for the given key 
     346         
     347        @param key: key index to credentials to retrieve 
     348        @type key: basestring 
     349        @rtype: iterable / None type if none found for key 
     350        @return: cached credentials indexed by input key 
     351        """ 
     352        return self.__assertionsMap.get(key) 
    336353                         
    337354    def audit(self): 
     
    341358        log.debug("SAMLAssertionWallet.audit ...") 
    342359         
    343         for issuerName, issuerEntry in self.credentials.items(): 
    344             if not self.isValidCredential(issuerEntry.credential): 
    345                 del self.credentials[issuerName] 
     360        for k, v in self.__assertionsMap.items(): 
     361            creds = [credential for credential in v 
     362                     if not self.isValidCredential(credential)] 
     363            if len(creds) > 0: 
     364                self.__assertionsMap[k] = TypedList(Assertion) 
     365                self.__assertionsMap[k].extend(creds) 
     366            else: 
     367                del self.__assertionsMap[k] 
    346368 
    347369    def isValidCredential(self, assertion): 
     
    371393        return True 
    372394     
     395    # Implement abstract method 
     396    updateCredentialRepository = lambda self: None 
     397     
    373398    def __getstate__(self): 
    374399        '''Enable pickling for use with beaker.session''' 
     
    390415    statements based on endpoint URI and issuer name 
    391416    """ 
    392     __slots__ = ("__credentialsKeyedByURI",) 
     417    __slots__ = ("__assertionsMapKeyedByURI",) 
    393418     
    394419    def __init__(self): 
    395420        super(SAMLAttributeWallet, self).__init__() 
    396         self.__credentialsKeyedByURI = {} 
    397  
    398     def _getCredentialsKeyedByURI(self): 
     421        self.__assertionsMapKeyedByURI = {} 
     422 
     423    def retrieveCredentialsByURI(self, issuerEndpoint): 
    399424        """Get Property method for credentials keyed by issuing service URI 
    400         Credentials dict is read-only but also see addCredential method 
     425        Credentials dict is read-only but also see addCredentials method 
    401426         
    402427        @rtype: dict 
    403428        @return: cached ACs indexed by issuing service URI""" 
    404         return self.__credentialsKeyedByURI 
    405      
    406     # Publish attribute 
    407     credentialsKeyedByURI = property(fget=_getCredentialsKeyedByURI, 
    408                                      doc="List of credentials " 
    409                                          "linked to issuing service URI") 
    410           
    411     def addCredential(self,  
    412                       credential,  
    413                       issuerEndpoint=None, 
    414                       verifyCredential=True): 
    415         """Add a new assertion to the list of assertion credentials held. 
    416  
    417         @type credential: SAML assertion 
    418         @param credential: new assertion to be added 
    419         @type issuerEndpoint: basestring 
    420         @param issuerEndpoint: input the issuing service URI from 
    421         which credential was retrieved.  This is added to a dict to enable  
    422         access to a given Attribute Certificate keyed by issuing service  
    423         URI. See the getCredential method. 
    424         @type verifyCredential: bool 
    425         @param verifyCredential: if set to True, test validity of credential 
    426         by calling isValidCredential method. 
    427          
    428         @rtype: bool 
    429         @return: True if credential was added otherwise False.  - If an 
    430         existing certificate from the same issuer has a later expiry it will 
    431         take precedence and the new input certificate is ignored.""" 
    432          
    433         # Check input 
    434         if not isinstance(credential, Assertion): 
    435             raise CredentialWalletError("Input credential must be an " 
    436                                         "%r type object" % Assertion)         
    437  
    438         if verifyCredential and not self.isValidCredential(credential): 
    439             raise CredentialWalletError("Validity time error with assertion %r" 
    440                                         % credential) 
    441          
    442         # Check to see if there is an existing Attribute Certificate held 
    443         # that was issued by the same host.  If so, compare the expiry time. 
    444         # The one with the latest expiry will be retained and the other 
    445         # ignored 
    446         bUpdateCred = True 
    447         if credential.issuer is None: 
    448             raise AttributeError("Adding SAML assertion to wallet: no issuer " 
    449                                  "set") 
    450              
    451         issuerName = credential.issuer.value 
    452          
    453         if issuerName in self.credentials: 
    454             # There is an existing certificate held with the same issuing 
    455             # host name as the new certificate 
    456             credentialOld = self.credentials[issuerName].credential 
    457  
    458             # If the new certificate has an earlier expiry time then ignore it 
    459             bUpdateCred = (credential.conditions.notOnOrAfter >  
    460                            credentialOld.conditions.notOnOrAfter) 
    461  
    462         if bUpdateCred: 
    463             thisCredential = CredentialContainer(Assertion) 
    464             thisCredential.credential = credential 
    465             thisCredential.issuerName = issuerName 
    466              
    467             self.credentials[issuerName] = thisCredential 
    468              
    469             if issuerEndpoint: 
    470                 self.credentialsKeyedByURI[ 
    471                     issuerEndpoint] = thisCredential 
    472                          
     429        return self.__assertionsMapKeyedByURI.get(issuerEndpoint) 
     430                
    473431    def audit(self): 
    474432        """Check the credentials held in the wallet removing any that have 
     
    477435        log.debug("SAMLAssertionWallet.audit ...") 
    478436         
    479         for issuerName, issuerEntry in self.credentials.items(): 
     437        for issuerName, issuerEntry in self.__assertionsMap.items(): 
    480438            if not self.isValidCredential(issuerEntry.credential): 
    481439                foundMatch = False 
    482440                endpoint = None 
    483                 for endpoint, v in self.credentialsKeyedByURI.items(): 
     441                for endpoint, v in self.__assertionsMapKeyedByURI.items(): 
    484442                    if v.issuerName.value == issuerName: 
    485443                        foundMatch = True 
     
    487445                 
    488446                if foundMatch: 
    489                     self.credentialsKeyedByURI.pop(endpoint, None) 
     447                    self.__assertionsMapKeyedByURI.pop(endpoint, None) 
    490448                     
    491                 del self.credentials[issuerName] 
     449                del self.__assertionsMap[issuerName] 
    492450     
    493451    def __getstate__(self): 
     
    511469    __slots__ = () 
    512470     
    513     def addCredential(self, assertion, verifyCredential=True): 
    514         """Add a new assertion to the list of assertion credentials held. 
    515  
    516         @type assertion: SAML assertion 
    517         @param assertion: new assertion to be added 
     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 
    518476        @type verifyCredential: bool 
    519477        @param verifyCredential: if set to True, test validity of credential 
     
    548506         
    549507        resourceId = assertion.authzDecisionStatements[0].resource         
    550         if resourceId in self.credentials: 
     508        if resourceId in self.__assertionsMap: 
    551509            # There is an existing certificate held with the same issuing 
    552510            # host name as the new certificate 
    553             assertionOld = self.credentials[resourceId].credential 
     511            assertionOld = self.__assertionsMap[resourceId].credential 
    554512 
    555513            # If the new certificate has an earlier expiry time then ignore it 
     
    564522                thisCredential.issuerName = assertion.issuer.value 
    565523                 
    566             self.credentials[resourceId] = thisCredential 
     524            self.__assertionsMap[resourceId] = thisCredential 
    567525                 
    568526    def __getstate__(self): 
     
    590548    credentials.  It enables retrieval of attribute certificates from a user's 
    591549    previous session(s)""" 
    592          
     550    __metaclass__ = ABCMeta 
     551     
     552    @abstractmethod 
    593553    def __init__(self, propFilePath=None, dbPPhrase=None, **prop): 
    594554        """Initialise Credential Repository abstract base class.  Derive from  
     
    613573            self.__init__.__doc__.replace('\n       ','')) 
    614574 
    615  
     575    @abstractmethod 
    616576    def addUser(self, userId, dn=None): 
    617577        """A new user to Credentials Repository 
     
    623583        raise NotImplementedError( 
    624584            self.addUser.__doc__.replace('\n       ','')) 
    625  
    626                              
     585            
     586    @abstractmethod 
    627587    def auditCredentials(self, userId=None, **attCertValidKeys): 
    628588        """Check the attribute certificates held in the repository and delete 
     
    638598            self.auditCredentials.__doc__.replace('\n       ','')) 
    639599 
    640  
    641     def getCredentials(self, userId): 
     600    @abstractmethod 
     601    def retrieveCredentials(self, userId): 
    642602        """Get the list of credentials for a given users DN 
    643603         
     
    645605        @param userId: users userId, name or X.509 cert. distinguished name 
    646606        @rtype: list  
    647         @return: list of Attribute Certificates""" 
     607        @return: list of credentials""" 
    648608        raise NotImplementedError( 
    649609            self.getCredentials.__doc__.replace('\n       ','')) 
    650610 
    651          
    652     def addCredentials(self, userId, attCertList): 
    653         """Add new attribute certificates for a user.  The user must have 
     611    @abstractmethod         
     612    def addCredentials(self, userId, credentialsList): 
     613        """Add credentials for a user.  The user must have 
    654614        been previously registered in the repository 
    655615 
    656616        @type userId: string 
    657617        @param userId: users userId, name or X.509 cert. distinguished name 
    658         @type attCertList: list 
    659         @param attCertList: list of attribute certificates""" 
     618        @type credentialsList: list 
     619        @param credentialsList: list of credentials 
     620        """ 
    660621        raise NotImplementedError( 
    661622            self.addCredentials.__doc__.replace('\n       ','')) 
     
    675636        """Null Credential Repository addUser placeholder""" 
    676637 
    677     def getCredentials(self, userId): 
     638    def retrieveCredentials(self, userId): 
    678639        """Null Credential Repository getCredentials placeholder""" 
    679640        return [] 
Note: See TracChangeset for help on using the changeset viewer.