source: TI12-security/trunk/python/ndg_security_common/ndg/security/common/credentialwallet.py @ 6062

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/ndg_security_common/ndg/security/common/credentialwallet.py@6062
Revision 6062, 84.7 KB checked in by pjkersha, 10 years ago (diff)
  • Working SamlPIPMiddleware - the Policy Information Point with an interface to the SAML Attribute Authority. The PIP retrieves user credential information for the PDP.
  • Fixed ndg.security.test.unit.wsgi.authz.test_authz unit tests for the above.
  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1"""Credential Wallet classes
2
3NERC DataGrid Project
4"""
5__author__ = "P J Kershaw"
6__date__ = "30/11/05"
7__copyright__ = "(C) 2009 Science and Technology Facilities Council"
8__license__ = "BSD - see LICENSE file in top-level directory"
9__contact__ = "Philip.Kershaw@stfc.ac.uk"
10__revision__ = '$Id:credentialwallet.py 4378 2008-10-29 10:30:14Z pjkersha $'
11
12import logging
13log = logging.getLogger(__name__)
14logging.basicConfig(level=logging.DEBUG)
15
16import os
17import warnings
18
19# Check Attribute Certificate validity times
20from datetime import datetime, timedelta
21
22from ConfigParser import ConfigParser
23
24from saml.utils import SAMLDateTime
25from saml.saml2.core import Assertion
26
27# Access Attribute Authority's web service using ZSI - allow pass if not
28# loaded since it's possible to make AttributeAuthority instance locally
29# without using the WS
30aaImportError = True
31try:
32    # AttributeAuthority client package resides with CredentialWallet module in
33    # ndg.security.common
34    from ndg.security.common.attributeauthority import (
35        AttributeAuthorityClient, AttributeAuthorityClientError, 
36        AttributeRequestDenied, NoMatchingRoleInTrustedHosts)
37    aaImportError = False
38except ImportError:
39    log.warning('Loading CredentialWallet without SOAP interface imports')
40
41# Likewise - may not want to use WS and use AttributeAuthority locally in which
42# case no need to import it
43try:
44    from ndg.security.server.attributeauthority import (AttributeAuthority, 
45        AttributeAuthorityError, AttributeAuthorityAccessDenied)
46    aaImportError = False
47except:
48    log.warning('Loading CredentialWallet without Attribute Authority '
49                'interface imports')
50
51if aaImportError:
52    raise ImportError("Either AttributeAuthority or AttributeAuthorityClient "
53                      "classes must be present to allow interoperation with "
54                      "Attribute Authorities")
55
56# Authentication X.509 Certificate
57from ndg.security.common.X509 import X509Cert
58from M2Crypto import X509, BIO, RSA
59
60# Authorisation - attribute certificate
61from ndg.security.common.AttCert import AttCert, AttCertError
62from ndg.security.common.wssecurity.signaturehandler.dom import SignatureHandler
63
64# generic parser to read INI/XML properties file
65from ndg.security.common.utils.configfileparsers import \
66                                                INIPropertyFileWithValidation
67
68from ndg.security.common.utils import TypedList
69from ndg.security.common.utils.configfileparsers import (     
70                                                    CaseSensitiveConfigParser,)
71
72
73class _CredentialWalletException(Exception):   
74    """Generic Exception class for CredentialWallet module.  Overrides
75    Exception to enable writing to the log"""
76    def __init__(self, *arg, **kw):
77        if len(arg) > 0:
78            log.error(arg[0])
79           
80        Exception.__init__(self, *arg, **kw)
81
82
83class CredentialWalletError(_CredentialWalletException):   
84    """Exception handling for NDG Credential Wallet class.  Overrides Exception
85    to enable writing to the log"""
86
87
88class CredentialWalletAttributeRequestDenied(CredentialWalletError):   
89    """Handling exception where CredentialWallet is denied authorisation by an
90    Attribute Authority.
91 
92    @type __extAttCertList: list
93    @ivar __extAttCertList: list of candidate Attribute Certificates that
94    could be used to try to get a mapped certificate from the target
95    Attribute Authority
96   
97    @type __trustedHostInfo: dict
98    @ivar __trustedHostInfo: dictionary indexed by host name giving
99    details of Attribute Authority URI and roles for trusted hosts"""
100   
101    def __init__(self, *args, **kw):
102        """Raise exception for attribute request denied with option to give
103        caller hint to certificates that could used to try to obtain a
104        mapped certificate
105       
106        @type extAttCertList: list
107        @param extAttCertList: list of candidate Attribute Certificates that
108        could be used to try to get a mapped certificate from the target
109        Attribute Authority
110       
111        @type trustedHostInfo: dict
112        @param trustedHostInfo: dictionary indexed by host name giving
113        details of Attribute Authority URI and roles for trusted hosts"""
114       
115        self.__trustedHostInfo = kw.pop('trustedHostInfo', {})
116        self.__extAttCertList = kw.pop('extAttCertList', [])
117           
118        CredentialWalletError.__init__(self, *args, **kw)
119
120    def _getTrustedHostInfo(self):
121        """Get message text"""
122        return self.__trustedHostInfo
123
124    trustedHostInfo = property(fget=_getTrustedHostInfo, 
125                               doc="URI and roles details for trusted hosts")
126       
127    def _getExtAttCertList(self):
128        """Return list of candidate Attribute Certificates that could be used
129        to try to get a mapped certificate from the target Attribute Authority
130        """
131        return self.__extAttCertList
132
133    extAttCertList = property(fget=_getExtAttCertList,
134                              doc="list of candidate Attribute Certificates "
135                              "that could be used to try to get a mapped "
136                              "certificate from the target Attribute "
137                              "Authority")
138
139         
140class _MetaCredentialWallet(type):
141    """Enable CredentialWallet to have read only class variables e.g.
142   
143    print CredentialWallet.accessDenied
144   
145    ... is allowed but,
146   
147    CredentialWallet.accessDenied = None
148   
149    ... raises - AttributeError: can't set attribute"""
150   
151    def _getAccessDenied(cls):
152        '''accessDenied get method'''
153        return False
154   
155    accessDenied = property(fget=_getAccessDenied)
156   
157    def _getAccessGranted(cls):
158        '''accessGranted get method'''
159        return True
160   
161    accessGranted = property(fget=_getAccessGranted)
162
163class CredentialContainer(object):
164    """Container for cached credentials"""
165    ID_ATTRNAME = 'id'
166    ITEM_ATTRNAME = 'credential'
167    ISSUERNAME_ATTRNAME = 'issuerName'
168    ATTRIBUTE_AUTHORITY_URI_ATTRNAME = 'attributeAuthorityURI'
169    CREDENTIAL_TYPE_ATTRNAME = 'type'
170   
171    __slots__ = (
172        ID_ATTRNAME,
173        ITEM_ATTRNAME,
174        ISSUERNAME_ATTRNAME,
175        ATTRIBUTE_AUTHORITY_URI_ATTRNAME,
176        CREDENTIAL_TYPE_ATTRNAME
177    )
178    __slots__ += tuple(["_CredentialContainer__%s" % n for n in __slots__])
179   
180    def __init__(self, type=None):
181        self.__type = None
182        self.type = type
183       
184        self.__id = -1
185        self.__credential = None
186        self.__issuerName = None
187        self.__attributeAuthorityURI = None
188
189    def _getType(self):
190        return self.__type
191
192    def _setType(self, value):
193        if not isinstance(value, type):
194            raise TypeError('Expecting %r for "type" attribute; got %r' %
195                            (type, type(value)))       
196        self.__type = value
197
198    type = property(_getType, _setType, 
199                    doc="Type for credential - set to None for any type")
200
201    def _getId(self):
202        return self.__id
203
204    def _setId(self, value):
205        if not isinstance(value, int):
206            raise TypeError('Expecting int type for "id" attribute; got %r' %
207                            type(value))
208        self.__id = value
209
210    id = property(_getId, 
211                  _setId, 
212                  doc="Numbered identifier for credential - "
213                      "set to -1 for new credentials")
214
215    def _getCredential(self):
216        return self.__credential
217
218    def _setCredential(self, value):
219        if self.type is not None and not isinstance(value, self.type):
220            raise TypeError('Expecting %r type for "credential" attribute; '
221                            'got %r' % type(value))
222        self.__credential = value
223
224    credential = property(_getCredential, 
225                          _setCredential, 
226                          doc="Credential object")
227
228    def _getIssuerName(self):
229        return self.__issuerName
230
231    def _setIssuerName(self, value):
232        self.__issuerName = value
233
234    issuerName = property(_getIssuerName, 
235                          _setIssuerName, 
236                          doc="Name of issuer of the credential")
237
238    def _getAttributeAuthorityURI(self):
239        return self.__attributeAuthorityURI
240
241    def _setAttributeAuthorityURI(self, value):
242        """String or None type are allowed - The URI may be set to None if
243        a local Attribute Authority instance is being invoked rather
244        one hosted via a remote URI
245        """
246        if not isinstance(value, (basestring, type(None))):
247            raise TypeError('Expecting string or None type for '
248                            '"attributeAuthorityURI"; got %r instead' % 
249                            type(value))
250        self.__attributeAuthorityURI = value
251
252    attributeAuthorityURI = property(_getAttributeAuthorityURI,
253                                     _setAttributeAuthorityURI, 
254                                     doc="Attribute Authority Service URI")
255
256    def __getstate__(self):
257        '''Enable pickling'''
258        return dict([(attrName, getattr(self, attrName))
259                     for attrName in self.__class__.__slots__])
260       
261    def __setstate__(self, attrDict):
262        '''Enable pickling for use with beaker.session'''
263        for attr, val in attrDict.items():
264            setattr(self, attr, val)
265       
266
267class CredentialWalletBase(object):
268    """Abstract base class for NDG and SAML Credential Wallet implementations
269    """ 
270    CONFIG_FILE_OPTNAMES = ("userId", )
271       
272    __slots__ = (
273        "credentials",
274        "credentialsKeyedByURI",
275    )
276    __slots__ += CONFIG_FILE_OPTNAMES
277    __slots__ += tuple(["_CredentialWalletBase__%s" % name
278                        for name in __slots__])
279    del name
280   
281    def __init__(self):
282        self.__userId = None
283        self.__credentials = {}
284        self.__credentialsKeyedByURI = {}
285
286    @classmethod
287    def fromConfig(cls, cfg, **kw):
288        '''Alternative constructor makes object from config file settings
289        @type cfg: basestring /ConfigParser derived type
290        @param cfg: configuration file path or ConfigParser type object
291        @rtype: ndg.security.common.credentialWallet.SAMLCredentialWallet
292        @return: new instance of this class
293        '''
294        credentialWallet = cls()
295        credentialWallet.parseConfig(cfg, **kw)
296
297    def parseConfig(self, cfg, prefix='', section='DEFAULT'):
298        '''Virtual method defines interface to read config file settings
299        @type cfg: basestring /ConfigParser derived type
300        @param cfg: configuration file path or ConfigParser type object
301        @type prefix: basestring
302        @param prefix: prefix for option names e.g. "certExtApp."
303        @type section: baestring
304        @param section: configuration file section from which to extract
305        parameters.
306        '''
307        raise NotImplementedError(CredentialWalletBase.parseConfig.__doc__)
308
309    def addCredential(self, 
310                      credential, 
311                      attributeAuthorityURI=None,
312                      bUpdateCredentialRepository=True):
313        """Add a new attribute certificate to the list of credentials held.
314
315        @type credential: determined by derived class implementation e.g.
316        SAML assertion
317        @param credential: new attribute Certificate to be added
318        @type attributeAuthorityURI: basestring
319        @param attributeAuthorityURI: input the Attribute Authority URI from
320        which credential was retrieved.  This is added to a dict to enable
321        access to a given Attribute Certificate keyed by Attribute Authority
322        URI. See the getCredential method.
323        @type bUpdateCredentialRepository: bool
324        @param bUpdateCredentialRepository: if set to True, and a repository
325        exists it will be updated with the new credentials also
326       
327        @rtype: bool
328        @return: True if certificate was added otherwise False.  - If an
329        existing certificate from the same issuer has a later expiry it will
330        take precedence and the new input certificate is ignored."""
331        raise NotImplementedError(CredentialWalletBase.addCredential.__doc__)
332           
333    def audit(self):
334        """Check the credentials held in the wallet removing any that have
335        expired or are otherwise invalid."""
336        raise NotImplementedError(CredentialWalletBase.audit.__doc__)
337
338    def updateCredentialRepository(self, auditCred=True):
339        """Copy over non-persistent credentials held by wallet into the
340        perminent repository.
341       
342        @type auditCred: bool
343        @param auditCred: filter existing credentials in the repository
344        removing invalid ones"""
345        raise NotImplementedError(
346                    CredentialWalletBase.updateCredentialRepository.__doc__)
347       
348    def _getCredentials(self):
349        """Get Property method.  Credentials dict is read-only but also see
350        addCredential method
351       
352        @rtype: dict
353        @return: cached ACs indesed by issuing organisation name"""
354        return self.__credentials
355
356    # Publish attribute
357    credentials = property(fget=_getCredentials,
358                           doc="List of credentials linked to issuing "
359                               "authorities")
360
361    def _getCredentialsKeyedByURI(self):
362        """Get Property method for credentials keyed by Attribute Authority URI
363        Credentials dict is read-only but also see addCredential method
364       
365        @rtype: dict
366        @return: cached ACs indexed by issuing Attribute Authority"""
367        return self.__credentialsKeyedByURI
368   
369    # Publish attribute
370    credentialsKeyedByURI = property(fget=_getCredentialsKeyedByURI,
371                                     doc="List of Attribute Certificates "
372                                         "linked to attribute authority URI")
373       
374    def _getUserId(self):
375        return self.__userId
376
377    def _setUserId(self, value):
378        if not isinstance(value, basestring):
379            raise TypeError('Expecting string type for "userId"; got %r '
380                            'instead' % type(value))
381        self.__userId = value
382
383    userId = property(_getUserId, _setUserId, 
384                      doc="User Identity for this wallet")
385
386    def __getstate__(self):
387        '''Enable pickling for use with beaker.session'''
388        return dict([(attrName, getattr(self, attrName))
389                     for attrName in self.__class__.__slots__])
390       
391    def __setstate__(self, attrDict):
392        '''Enable pickling for use with beaker.session'''
393        for attr, val in attrDict.items():
394            setattr(self, attr, val)
395
396
397class SAMLCredentialWallet(CredentialWalletBase):
398    """CredentialWallet for Earth System Grid supporting caching of SAML
399    Attribute Assertions
400    """
401    __slots__ = ()
402   
403    CREDENTIAL_REPOSITORY_NOT_SUPPORTED_MSG = ("SAMLCredentialWallet doesn't "
404                                               "support the "
405                                               "CredentialRepository "
406                                               "interface")
407
408    @classmethod
409    def fromConfig(cls, cfg, **kw):
410        '''Alternative constructor makes object from config file settings
411        @type cfg: basestring /ConfigParser derived type
412        @param cfg: configuration file path or ConfigParser type object
413        @rtype: ndg.security.common.credentialWallet.SAMLCredentialWallet
414        @return: new instance of this class
415        '''
416        credentialWallet = cls()
417        credentialWallet.parseConfig(cfg, **kw)
418       
419        return credentialWallet
420
421    def parseConfig(self, cfg, prefix='', section='DEFAULT'):
422        '''Read config file settings
423        @type cfg: basestring /ConfigParser derived type
424        @param cfg: configuration file path or ConfigParser type object
425        @type prefix: basestring
426        @param prefix: prefix for option names e.g. "certExtApp."
427        @type section: baestring
428        @param section: configuration file section from which to extract
429        parameters.
430        ''' 
431        if isinstance(cfg, basestring):
432            cfgFilePath = os.path.expandvars(cfg)
433            _cfg = CaseSensitiveConfigParser()
434            _cfg.read(cfgFilePath)
435           
436        elif isinstance(cfg, ConfigParser):
437            _cfg = cfg   
438        else:
439            raise AttributeError('Expecting basestring or ConfigParser type '
440                                 'for "cfg" attribute; got %r type' % type(cfg))
441       
442        prefixLen = len(prefix)
443        for optName, val in _cfg.items(section):
444            if prefix and optName.startswith(prefix):
445                optName = optName[prefixLen:]
446               
447            setattr(self, optName, val)
448
449    def addCredential(self, 
450                      credential, 
451                      attributeAuthorityURI=None,
452                      bUpdateCredentialRepository=False):
453        """Add a new assertion to the list of assertion credentials held.
454
455        @type credential: SAML assertion
456        @param credential: new assertion to be added
457        @type attributeAuthorityURI: basestring
458        @param attributeAuthorityURI: input the Attribute Authority URI from
459        which credential was retrieved.  This is added to a dict to enable
460        access to a given Attribute Certificate keyed by Attribute Authority
461        URI. See the getCredential method.
462        @type bUpdateCredentialRepository: bool
463        @param bUpdateCredentialRepository: if set to True, and a repository
464        exists it will be updated with the new credentials also
465       
466        @rtype: bool
467        @return: True if certificate was added otherwise False.  - If an
468        existing certificate from the same issuer has a later expiry it will
469        take precedence and the new input certificate is ignored."""
470       
471        # Check input
472        if not isinstance(credential, Assertion):
473            raise CredentialWalletError("Input credential must be an "
474                                        "%r type object" % Assertion)       
475
476        if not self.isValidCredential(credential):
477            raise CredentialWalletError("Validity time error with assertion %r"
478                                        % assertion)
479       
480        # Check to see if there is an existing Attribute Certificate held
481        # that was issued by the same host.  If so, compare the expiry time.
482        # The one with the latest expiry will be retained and the other
483        # ingored
484        bUpdateCred = True
485        issuerName = credential.issuer.value
486       
487        if issuerName in self.credentials:
488            # There is an existing certificate held with the same issuing
489            # host name as the new certificate
490            credentialOld = self.credentials[issuerName].credential
491
492            # If the new certificate has an earlier expiry time then ignore it
493            bUpdateCred = (credential.conditions.notOnOrAfter > 
494                           credentialOld.conditions.notOnOrAfter)
495
496        if bUpdateCred:
497            thisCredential = CredentialContainer(Assertion)
498            thisCredential.credential = credential
499            thisCredential.issuerName = issuerName
500            thisCredential.attributeAuthorityURI = attributeAuthorityURI
501           
502            self.credentials[issuerName] = thisCredential
503           
504            if attributeAuthorityURI:
505                self.credentialsKeyedByURI[
506                    attributeAuthorityURI] = thisCredential
507
508            # Update the Credentials Repository - the permanent store of user
509            # authorisation credentials.  This allows credentials for previous
510            # sessions to be re-instated
511            if bUpdateCredentialRepository:
512                self.updateCredentialRepository()
513
514        # Flag to caller to indicate whether the input certificate was added
515        # to the credentials or an exsiting certificate from the same issuer
516        # took precedence
517        return bUpdateCred
518                       
519    def audit(self):
520        """Check the credentials held in the wallet removing any that have
521        expired or are otherwise invalid."""
522
523        log.debug("SAMLCredentialWallet.audit ...")
524       
525        for issuerName, issuerEntry in self.credentials.items():
526            if not self.isValidCredential(issuerEntry.credential):
527                self.credentialsKeyedByURI.pop(
528                    issuerEntry.attributeAuthorityURI,
529                    None)
530                   
531                del self.credentials[issuerName]
532
533    def updateCredentialRepository(self, auditCred=True):
534        """No Credential Repository support is required"""
535        msg = SAMLCredentialWallet.CREDENTIAL_REPOSITORY_NOT_SUPPORTED_MSG
536        log.warning(msg)
537        warnings.warn(msg)
538
539    def isValidCredential(self, assertion):
540        """Validate SAML assertion time validity"""
541        utcNow = datetime.utcnow()
542        if utcNow < assertion.conditions.notBefore:
543            msg = ('The current clock time [%s] is before the SAML Attribute '
544                   'Response assertion conditions not before time [%s]' % 
545                   (SAMLDateTime.toString(utcNow),
546                    assertion.conditions.notBefore))
547            log.warning(msg)
548            return False
549           
550        if utcNow >= assertion.conditions.notOnOrAfter:
551            msg = ('The current clock time [%s] is on or after the SAML '
552                   'Attribute Response assertion conditions not on or after '
553                   'time [%s]' % 
554                   (SAMLDateTime.toString(utcNow),
555                    assertion.conditions.notOnOrAfter))
556            log.warning(msg)
557            return False
558           
559        return True
560           
561   
562class NDGCredentialWallet(CredentialWalletBase):
563    """Volatile store of user credentials associated with a user session
564   
565    @type userX509Cert: string / M2Crypto.X509.X509 /
566    ndg.security.common.X509.X509Cert
567    @ivar userX509Cert: X.509 certificate for user (property attribute)
568   
569    @type userPriKey: string / M2Crypto.RSA.RSA
570    @ivar userPriKey: private key for user cert (property attribute)
571   
572    @type issuingX509Cert: string / ndg.security.common.X509.X509Cert
573    @ivar issuingX509Cert: X.509 cert for issuer of user cert (property
574    attribute)
575   
576    @type attributeAuthorityURI: string
577    @ivar attributeAuthorityURI: URI of Attribute Authority to make
578    requests to.  Setting this ALSO creates an AttributeAuthorityClient
579    instance _attributeAuthorityClnt.  - See attributeAuthorityURI property for
580    details. (property attribute)
581   
582    @type attributeAuthority: ndg.security.server.attributeauthority.AttributeAuthority
583    @ivar attributeAuthority: Attribute Authority to make requests to. 
584    attributeAuthorityURI takes precedence over this keyword i.e. if an
585    attributeAuthorityURI has been set, then calls are made to the AA web
586    service at this location rather to any self.attributeAuthority running
587    locally. (property attribute)
588   
589    @type caCertFilePathList: string (for single file), list or tuple
590    @ivar caCertFilePathList: Certificate Authority's certificates - used
591    in validation of signed Attribute Certificates and WS-Security
592    signatures of incoming messages.  If not set here, it must
593    be input in call to getAttCert. (property attribute)
594           
595    @type credentialRepository: instance of CredentialRepository derived
596    class
597    @ivar credentialRepository: Credential Repository instance.   (property
598    attribute).  If not set, defaults to NullCredentialRepository type - see
599    class below...
600
601   
602    @type mapFromTrustedHosts: bool
603    @ivar mapFromTrustedHosts sets behaviour for getAttCert().  If
604    set True and authorisation fails with the given Attribute Authority,
605    attempt to get authorisation using Attribute Certificates issued by
606    other trusted AAs. (property attribute)
607   
608    @type rtnExtAttCertList: bool
609    @ivar rtnExtAttCertList: behaviour for getAttCert().  If True, and
610    authorisation fails with the given Attribute Authority, return a list
611    of Attribute Certificates from other trusted AAs which could be used
612    to obtain a mapped Attribute Certificate on a subsequent authorisation
613    attempt. (property attribute)
614   
615    @type attCertRefreshElapse: float / int
616    @ivar attCertRefreshElapse: used by getAttCert to determine
617    whether to replace an existing AC in the cache with a fresh one.  If
618    the existing one has less than attCertRefreshElapse time in seconds
619    left before expiry then replace it. (property attribute)
620   
621    @type wssCfgKw: dict
622    @ivar wssCfgKw: keywords to WS-Security SignatureHandler
623    used for Credential Wallet's SOAP interface to Attribute Authorities.
624    (property attribute)
625           
626    @type _credentialRepository: ndg.security.common.CredentialRepository or
627    derivative
628    @ivar _credentialRepository: reference to Credential Repository object. 
629    An optional non-volatile cache for storage of wallet info which can be
630    later restored. (Don't reference directly - see equivalent property
631    attribute)
632
633    @type _mapFromTrustedHosts: bool
634    @ivar _mapFromTrustedHosts: if true, allow a mapped attribute certificate
635    to obtained in a getAttCert call.  Set false to prevent mappings.
636    (Don't reference directly - see equivalent property attribute)
637
638    @type _rtnExtAttCertList: bool
639    @ivar _rtnExtAttCertList: if true, return a list of external attribute
640    certificates from getAttCert call. (Don't reference directly - see
641    equivalent property attribute)
642
643    @type __dn: ndg.security.common.X509.X500DN
644    @ivar __dn: distinguished name from user certificate.  (Don't reference
645    directly - see equivalent property attribute)
646
647    @type _credentials: dict       
648    @ivar _credentials: Credentials are stored as a dictionary one element per
649    attribute certificate held and indexed by certificate issuer name.
650    (Don't reference directly - see equivalent property attribute)
651
652    @type _caCertFilePathList: basestring, list, tuple or None
653    @ivar _caCertFilePathList: file path(s) to CA certificates.  If None
654    then the input is quietly ignored.  See caCertFilePathList property.
655    (Don't reference directly - see equivalent property attribute)
656
657    @type _userX509Cert: ndg.security.common.X509.X509Cert
658    @ivar _userX509Cert: X.509 user certificate instance.
659    (Don't reference directly - see equivalent property attribute)
660
661    @type _issuingX509Cert: ndg.security.common.X509.X509Cert
662    @ivar _issuingX509Cert: X.509 user certificate instance.
663    (Don't reference directly - see equivalent property attribute)
664 
665    @type _userPriKey: M2Crypto.RSA.RSA
666    @ivar _userPriKey: Private key used to sign outbound message.
667    (Don't reference directly - see equivalent property attribute)
668    """
669
670    __metaclass__ = _MetaCredentialWallet
671
672    propertyDefaults = dict(
673        userX509Cert=None,
674        userX509CertFilePath=None,
675        userPriKey=None,
676        userPriKeyFilePath=None,
677        issuingX509Cert=None,
678        issuingX509CertFilePath=None,
679        caCertFilePathList=[],
680        sslCACertFilePathList=[],
681        attributeAuthority=None,
682        credentialRepository=None,
683        mapFromTrustedHosts=False,
684        rtnExtAttCertList=True,
685        attCertRefreshElapse=7200,
686        wssCfgFilePath=None,
687        wssCfgSection='DEFAULT',
688        wssCfgPrefix='',
689        wssCfgKw={})
690   
691    _protectedAttrs = [
692        '_userX509Cert',
693        '_userX509CertFilePath',
694        '_userPriKey',
695        '_userPriKeyFilePath',
696        '_userPriKeyPwd',
697        '_issuingX509Cert',
698        '_issuingX509CertFilePath',
699        '_attributeAuthorityClnt',
700        '_attributeAuthority',
701        '_attributeAuthorityURI',
702        '_caCertFilePathList',
703        '_mapFromTrustedHosts',
704        '_rtnExtAttCertList',
705        '_attCertRefreshElapse',
706        '_cfg',
707        '_dn'
708    ]
709   
710    __slots__ = propertyDefaults.keys() + _protectedAttrs
711   
712    def __init__(self, 
713                 cfg=None, 
714                 cfgFileSection='DEFAULT', 
715                 cfgPrefix='', 
716                 wssCfgKw={},
717                 **kw):
718        """Create store of user credentials for their current session
719
720        @type cfg: string / ConfigParser object
721        @param cfg: if a string type, this is interpreted as the file path to
722        a configuration file, otherwise it will be treated as a ConfigParser
723        object
724        @type cfgSection: string
725        @param cfgSection: sets the section name to retrieve config params
726        from
727        @type cfgPrefix: basestring
728        @param cfgPrefix: apply a prefix to all NDGCredentialWallet config params
729        so that if placed in a file with other parameters they can be
730        distinguished
731        @type cfgKw: dict
732        @param cfgKw: set parameters as key value pairs."""
733
734        log.debug("Calling NDGCredentialWallet.__init__ ...")
735
736        super(NDGCredentialWallet, self).__init__()
737       
738        # Initialise attributes - 1st protected ones
739        attr = {}.fromkeys(NDGCredentialWallet._protectedAttrs)
740       
741        # ... then properties
742        attr.update(NDGCredentialWallet.propertyDefaults)
743        for k, v in attr.items():
744            setattr(self, k, v)
745           
746        # Update attributes from a config file
747        if cfg:
748            self.parseConfig(cfg, section=cfgFileSection, prefix=cfgPrefix)
749
750        # Update attributes from keywords passed - set user private key
751        # password first if it's present.  This is to avoid an error setting
752        # the private key
753        self.userPriKeyPwd = kw.pop('userPriKeyPwd', None)
754        for k, v in kw.items():
755            setattr(self, k, v)
756
757        # Get the distinguished name from the user certificate
758        if self._userX509Cert:
759            self._dn = self._userX509Cert.dn.serialise()
760
761        # Make a connection to the Credentials Repository
762        if self.credentialRepository is None:
763            log.info('Applying default CredentialRepository %r for user '
764                     '"%s"' % (NullCredentialRepository, self.userId))
765            self.credentialRepository = NullCredentialRepository()
766        else:
767            log.info('Checking CredentialRepository for credentials for user '
768                     '"%s"' % self.userId)
769           
770            if not issubclass(self.credentialRepository, CredentialRepository):
771                raise CredentialWalletError("Input Credential Repository "
772                                            "instance must be of a class "
773                                            "derived from "
774                                            "\"CredentialRepository\"")
775   
776       
777            # Check for valid attribute certificates for the user
778            try:
779                self.credentialRepository.auditCredentials(self.userId)
780                userCred=self.credentialRepository.getCredentials(self.userId)
781   
782            except Exception, e:
783                log.error("Error updating wallet with credentials from "
784                          "repository: %s" % e)
785                raise
786   
787   
788            # Update wallet with attribute certificates stored in the
789            # repository.  Store ID and certificate instantiated as an AttCert
790            # type
791            try:
792                for cred in userCred: 
793                    attCert = AttCert.Parse(cred.attCert)
794                    issuerName = attCert['issuerName']
795                   
796                    self.credentials[issuerName] = {'id':cred.id, 
797                                                     'attCert':attCert}   
798            except Exception, e:
799                try:
800                    raise CredentialWalletError("Error parsing Attribute "
801                        "Certificate ID '%s' retrieved from the " 
802                        "Credentials Repository: %s" % (cred.id, e))           
803                except:
804                    raise CredentialWalletError("Error parsing Attribute "
805                                          "Certificate retrieved from the "
806                                          "Credentials Repository: %s:" % e)
807           
808            # Filter out expired or otherwise invalid certificates
809            self.audit()
810   
811    def parseConfig(self, cfg, prefix='', section='DEFAULT'):
812        '''Extract parameters from cfg config object'''
813       
814        if isinstance(cfg, basestring):
815            cfgFilePath = os.path.expandvars(cfg)
816            self._cfg = None
817        else:
818            cfgFilePath = None
819            self._cfg = cfg
820           
821        # Configuration file properties are held together in a dictionary
822        readAndValidate = INIPropertyFileWithValidation()
823        prop = readAndValidate(cfgFilePath,
824                               cfg=self._cfg,
825                               validKeys=NDGCredentialWallet.propertyDefaults,
826                               prefix=prefix,
827                               sections=(section,))
828       
829        # Keep a copy of config for use by WS-Security SignatureHandler parser
830        if self._cfg is None:
831            self._cfg = readAndValidate.cfg
832       
833        # Copy prop dict into object attributes - __slots__ definition and
834        # property methods will ensure only the correct attributes are set
835        # Set user private key password first if it's present.  This is to
836        # avoid an error setting the private key
837        self.userPriKeyPwd = prop.pop('userPriKeyPwd', None)
838        for key, val in prop.items():
839            setattr(self, key, val)
840
841
842    def _getAttCertRefreshElapse(self):
843        """Get property method for Attribute Certificate wallet refresh time
844        @rtype: float or int
845        @return: "elapse time in seconds"""
846        return self._attCertRefreshElapse
847   
848    def _setAttCertRefreshElapse(self, val):
849        """Set property method for Attribute Certificate wallet refresh time
850        @type val: float or int
851        @param val: "elapse time in seconds"""
852        if isinstance(val, (float, int)):
853            self._attCertRefreshElapse = val
854           
855        elif isinstance(val, basestring):
856            self._attCertRefreshElapse = float(val)
857        else:
858            raise AttributeError("Expecting int, float or string type input "
859                                 "for attCertRefreshElapse")
860           
861    attCertRefreshElapse = property(fget=_getAttCertRefreshElapse, 
862                                    fset=_setAttCertRefreshElapse,
863                                    doc="If an existing one has AC less than "
864                                        "attCertRefreshElapse time in seconds "
865                                        "left before expiry then replace it")
866   
867    def _setX509Cert(self, cert):
868        """filter and convert input cert to signing verifying cert set
869        property methods.  For signingCert, set to None if it is not to be
870        included in the SOAP header.  For verifyingCert, set to None if this
871        cert can be expected to be retrieved from the SOAP header of the
872        message to be verified
873       
874        @type: ndg.security.common.X509.X509Cert / M2Crypto.X509.X509 /
875        string or None
876        @param cert: X.509 certificate. 
877       
878        @rtype ndg.security.common.X509.X509Cert
879        @return X.509 certificate object"""
880       
881        if cert is None or isinstance(cert, X509Cert):
882            # ndg.security.common.X509.X509Cert type / None
883            return cert
884           
885        elif isinstance(cert, X509.X509):
886            # M2Crypto.X509.X509 type
887            return X509Cert(m2CryptoX509=cert)
888           
889        elif isinstance(cert, basestring):
890            return X509Cert.Parse(cert)
891       
892        else:
893            raise AttributeError("X.509 Cert. must be type: "
894                                 "ndg.security.common.X509.X509Cert, "
895                                 "M2Crypto.X509.X509 or a base64 encoded "
896                                 "string")
897
898    def _setUserX509Cert(self, userX509Cert):
899        "Set property method for X.509 user cert."
900        self._userX509Cert = self._setX509Cert(userX509Cert)       
901
902    def _getUserX509Cert(self):
903        """Get user cert X509Cert instance"""
904        return self._userX509Cert
905
906    userX509Cert = property(fget=_getUserX509Cert,
907                            fset=_setUserX509Cert,
908                            doc="X.509 user certificate instance")
909 
910    def _setUserX509CertFilePath(self, filePath):
911        "Set user X.509 cert file path property method"
912       
913        if isinstance(filePath, basestring):
914            filePath = os.path.expandvars(filePath)
915            self._userX509Cert = X509Cert.Read(filePath)
916           
917        elif filePath is not None:
918            raise AttributeError("User X.509 cert. file path must be a valid "
919                                 "string")
920       
921        self._userX509CertFilePath = filePath
922               
923    userX509CertFilePath = property(fset=_setUserX509CertFilePath,
924                                    doc="File path to user X.509 cert.")
925   
926    def _setIssuingX509Cert(self, issuingX509Cert):
927        "Set property method for X.509 user cert."
928        self._issuingX509Cert = self._setX509Cert(issuingX509Cert)
929       
930    def _getIssuingX509Cert(self):
931        """Get user cert X509Cert instance"""
932        return self._issuingX509Cert
933
934    issuingX509Cert = property(fget=_getIssuingX509Cert,
935                               fset=_setIssuingX509Cert,
936                               doc="X.509 user certificate instance")
937 
938    def _setIssuerX509CertFilePath(self, filePath):
939        "Set user X.509 cert file path property method"
940       
941        if isinstance(filePath, basestring):
942            filePath = os.path.expandvars(filePath)
943            self._issuerX509Cert = X509Cert.Read(filePath)
944           
945        elif filePath is not None:
946            raise AttributeError("User X.509 cert. file path must be a valid "
947                                 "string")
948       
949        self._issuerX509CertFilePath = filePath
950               
951    issuerX509CertFilePath = property(fset=_setIssuerX509CertFilePath,
952                                      doc="File path to user X.509 cert. "
953                                          "issuing cert.")     
954
955    def _getUserPriKey(self):
956        "Get method for user private key"
957        return self._userPriKey
958   
959    def _setUserPriKey(self, userPriKey):
960        """Set method for user private key
961       
962        Nb. if input is a string, userPriKeyPwd will need to be set if
963        the key is password protected.
964       
965        @type userPriKey: M2Crypto.RSA.RSA / string
966        @param userPriKey: private key used to sign message"""
967       
968        if userPriKey is None:
969            self._userPriKey = None
970        elif isinstance(userPriKey, basestring):
971            pwdCallback = lambda *ar, **kw: self._userPriKeyPwd
972            self._userPriKey = RSA.load_key_string(userPriKey,
973                                                   callback=pwdCallback)
974        elif isinstance(userPriKey, RSA.RSA):
975            self._userPriKey = userPriKey         
976        else:
977            raise AttributeError("user private key must be a valid "
978                                 "M2Crypto.RSA.RSA type or a string")
979               
980    userPriKey = property(fget=_getUserPriKey,
981                          fset=_setUserPriKey,
982                          doc="User private key if set, used to sign outbound "
983                              "messages to Attribute authority")
984
985    def _setUserPriKeyFilePath(self, filePath):
986        "Set user private key file path property method"
987       
988        if isinstance(filePath, basestring):
989            filePath = os.path.expandvars(filePath)
990            try:
991                # Read Private key to sign with   
992                priKeyFile = BIO.File(open(filePath)) 
993                pwdCallback = lambda *ar, **kw: self._userPriKeyPwd
994                self._userPriKey = RSA.load_key_bio(priKeyFile, 
995                                                    callback=pwdCallback)   
996            except Exception, e:
997                raise AttributeError("Setting user private key: %s" % e)
998       
999        elif filePath is not None:
1000            raise AttributeError("Private key file path must be a valid "
1001                                 "string or None")
1002       
1003        self._userPriKeyFilePath = filePath
1004       
1005    userPriKeyFilePath = property(fset=_setUserPriKeyFilePath,
1006                                  doc="File path to user private key")
1007 
1008    def _setUserPriKeyPwd(self, userPriKeyPwd):
1009        "Set method for user private key file password"
1010        if userPriKeyPwd is not None and not isinstance(userPriKeyPwd, 
1011                                                        basestring):
1012            raise AttributeError("Signing private key password must be None "
1013                                 "or a valid string")
1014       
1015        # Explicitly convert to string as M2Crypto OpenSSL wrapper fails with
1016        # unicode type
1017        self._userPriKeyPwd = str(userPriKeyPwd)
1018
1019    def _getUserPriKeyPwd(self):
1020        "Get property method for user private key"
1021        return self._userPriKeyPwd
1022       
1023    userPriKeyPwd = property(fset=_setUserPriKeyPwd,
1024                             fget=_getUserPriKeyPwd,
1025                             doc="Password protecting user private key file")
1026       
1027    def _getCACertFilePathList(self):
1028        """Get CA cert or certs used to validate AC signatures and signatures
1029        of peer SOAP messages.
1030       
1031        @rtype caCertFilePathList: basestring, list or tuple
1032        @return caCertFilePathList: file path(s) to CA certificates."""
1033        return self._caCertFilePathList
1034   
1035    def _setCACertFilePathList(self, caCertFilePathList):
1036        """Set CA cert or certs to validate AC signatures, signatures
1037        of Attribute Authority SOAP responses and SSL connections where
1038        AA SOAP service is run over SSL.
1039       
1040        @type caCertFilePathList: basestring, list, tuple or None
1041        @param caCertFilePathList: file path(s) to CA certificates.  If None
1042        then the input is quietly ignored."""
1043       
1044        if isinstance(caCertFilePathList, basestring):
1045           self._caCertFilePathList = [caCertFilePathList]
1046           
1047        elif isinstance(caCertFilePathList, list):
1048           self._caCertFilePathList = caCertFilePathList
1049           
1050        elif isinstance(caCertFilePathList, tuple):
1051           self._caCertFilePathList = list(caCertFilePathList)
1052
1053        elif caCertFilePathList is not None:
1054            raise TypeError('Expecting string/list/tuple or None type for '
1055                            '"caCertFilePathList"; got %r type' % 
1056                            type(caCertFilePathList))     
1057       
1058    caCertFilePathList = property(fget=_getCACertFilePathList,
1059                                  fset=_setCACertFilePathList,
1060                                  doc="CA Certificates - used for "
1061                                      "verification of AC and SOAP message "
1062                                      "signatures")
1063
1064    def _getAttributeAuthorityURI(self):
1065        return self._attributeAuthorityURI
1066
1067    def _setAttributeAuthorityURI(self, value):
1068        """String or None type are allowed - The URI may be set to None to
1069        flag that a local Attribute Authority instance is being invoked rather
1070        one hosted via a remote URI
1071        """
1072        if not isinstance(value, (basestring, type(None))):
1073            raise TypeError('Expecting string or None type for '
1074                            '"attributeAuthorityURI"; got %r instead' % 
1075                            type(value))
1076           
1077        self._attributeAuthorityURI = value
1078         
1079        if value is not None:     
1080            # Re-initialize local instance
1081            self._attributeAuthority = NDGCredentialWallet.propertyDefaults[
1082                                                        'attributeAuthority']
1083
1084    attributeAuthorityURI = property(fget=_getAttributeAuthorityURI,
1085                                     fset=_setAttributeAuthorityURI,
1086                                     doc="Attribute Authority address - "
1087                                         "setting also sets up "
1088                                         "AttributeAuthorityClient instance!")
1089
1090    def _getAttributeAuthority(self):
1091        """Get property method for Attribute Authority Web Service client
1092        instance.  Use attributeAuthorityURI propert to set up
1093        attributeAuthorityClnt
1094       
1095        @rtype attributeAuthority: ndg.security.server.attributeauthority.AttributeAuthority
1096        @return attributeAuthority: Attribute Authority instance"""
1097        return self._attributeAuthority
1098
1099    def _setAttributeAuthority(self, attributeAuthority):
1100        """Set property method for Attribute Authority Web Service instance to
1101        connect to.  This method ALSO RESETS attributeAuthorityURI - the
1102        address of a remote Attribute Authority - to None
1103       
1104        @type attributeAuthority: ndg.security.server.attributeauthority.AttributeAuthority
1105        @param attributeAuthority: Attribute Authority instance.
1106        """
1107        if not isinstance(attributeAuthority, (AttributeAuthority, type(None))):
1108            raise AttributeError("Expecting %r or None type for "
1109                                 "\"attributeAuthority\" attribute; got %r" % 
1110                                 (AttributeAuthority, type(attributeAuthority)))
1111           
1112        self._attributeAuthority = attributeAuthority
1113       
1114        # Re-initialize setting for remote service
1115        self.attributeAuthorityURI = None
1116           
1117    attributeAuthority = property(fget=_getAttributeAuthority,
1118                                  fset=_setAttributeAuthority, 
1119                                  doc="Attribute Authority instance")
1120
1121
1122    def _getMapFromTrustedHosts(self):
1123        """Get property method for boolean flag - if set to True it allows
1124        role mapping to be attempted when connecting to an Attribute Authority
1125       
1126        @type mapFromTrustedHosts: bool
1127        @param mapFromTrustedHosts: set to True to try role mapping in AC
1128        requests to Attribute Authorities"""
1129        return self._mapFromTrustedHosts
1130
1131    def _setMapFromTrustedHosts(self, mapFromTrustedHosts):
1132        """Set property method for boolean flag - if set to True it allows
1133        role mapping to be attempted when connecting to an Attribute Authority
1134       
1135        @type mapFromTrustedHosts: bool
1136        @param mapFromTrustedHosts: Attribute Authority Web Service."""
1137        if not isinstance(mapFromTrustedHosts, bool):
1138            raise AttributeError("Expecting %r for mapFromTrustedHosts "
1139                                 "attribute" % bool)
1140           
1141        self._mapFromTrustedHosts = mapFromTrustedHosts
1142           
1143    mapFromTrustedHosts = property(fget=_getMapFromTrustedHosts,
1144                                   fset=_setMapFromTrustedHosts, 
1145                                   doc="Set to True to enable mapped AC "
1146                                       "requests")
1147
1148    def _getRtnExtAttCertList(self):
1149        """Get property method for Attribute Authority Web Service client
1150        instance.  Use rtnExtAttCertListURI propert to set up
1151        rtnExtAttCertListClnt
1152       
1153        @type rtnExtAttCertList: bool
1154        @param rtnExtAttCertList: """
1155        return self._rtnExtAttCertList
1156
1157    def _setRtnExtAttCertList(self, rtnExtAttCertList):
1158        """Set property method for boolean flag - when a AC request fails,
1159        return a list of candidate ACs that could be used to re-try with in
1160        order to get mapped AC.
1161       
1162        @type rtnExtAttCertList: bool
1163        @param rtnExtAttCertList: set to True to configure getAttCert to return
1164        a list of ACs that could be used in a re-try to get a mapped AC from
1165        the target Attribute Authority."""
1166        if not isinstance(rtnExtAttCertList, bool):
1167            raise AttributeError("Expecting %r for rtnExtAttCertList "
1168                                 "attribute" % bool)
1169           
1170        self._rtnExtAttCertList = rtnExtAttCertList
1171           
1172    rtnExtAttCertList = property(fget=_getRtnExtAttCertList,
1173                                 fset=_setRtnExtAttCertList, 
1174                                 doc="Set to True to enable mapped AC "
1175                                     "requests")
1176
1177    def isValid(self, **x509CertKeys):
1178        """Check wallet's user cert.  If expired return False
1179       
1180        @type **x509CertKeys: dict
1181        @param **x509CertKeys: keywords applying to
1182        ndg.security.common.X509.X509Cert.isValidTime method"""
1183        if self._userX509Cert is not None:
1184            return self._userX509Cert.isValidTime(**x509CertKeys)
1185        else:
1186            log.warning("NDGCredentialWallet.isValid: no user certificate set in "
1187                        "wallet")
1188            return True
1189
1190    def addCredential(self, 
1191                      attCert, 
1192                      attributeAuthorityURI=None,
1193                      bUpdateCredentialRepository=True):
1194        """Add a new attribute certificate to the list of credentials held.
1195
1196        @type attCert:
1197        @param attCert: new attribute Certificate to be added
1198        @type attributeAuthorityURI: basestring
1199        @param attributeAuthorityURI: input the Attribute Authority URI from
1200        which attCert was retrieved.  This is added to a dict to enable access
1201        to a given Attribute Certificate keyed by Attribute Authority URI.
1202        See the getCredential method.
1203        @type bUpdateCredentialRepository: bool
1204        @param bUpdateCredentialRepository: if set to True, and a repository
1205        exists it will be updated with the new credentials also
1206       
1207        @rtype: bool
1208        @return: True if certificate was added otherwise False.  - If an
1209        existing certificate from the same issuer has a later expiry it will
1210        take precedence and the new input certificate is ignored."""
1211
1212        # Check input
1213        if not isinstance(attCert, AttCert):
1214            raise CredentialWalletError("Credential must be an %r type object" %
1215                                        AttCert)
1216           
1217        # Check certificate validity
1218        try:
1219            attCert.isValid(raiseExcep=True)
1220           
1221        except AttCertError, e:
1222            raise CredentialWalletError("Adding Credential: %s" % e)
1223
1224        # Check to see if there is an existing Attribute Certificate held
1225        # that was issued by the same host.  If so, compare the expiry time.
1226        # The one with the latest expiry will be retained and the other
1227        # ingored
1228        bUpdateCred = True
1229        issuerName = attCert['issuerName']
1230       
1231        if issuerName in self.credentials:
1232            # There is an existing certificate held with the same issuing
1233            # host name as the new certificate
1234            attCertOld = self.credentials[issuerName].credential
1235
1236            # Get expiry times in datetime format to allow comparison
1237            dtAttCertOldNotAfter = attCertOld.getValidityNotAfter(\
1238                                                            asDatetime=True)
1239            dtAttCertNotAfter = attCert.getValidityNotAfter(asDatetime=True)
1240
1241            # If the new certificate has an earlier expiry time then ignore it
1242            bUpdateCred = dtAttCertNotAfter > dtAttCertOldNotAfter
1243
1244               
1245        if bUpdateCred:
1246            thisCredential = CredentialContainer(AttCert)
1247            thisCredential.credential = attCert
1248            thisCredential.issuerName = issuerName
1249            thisCredential.attributeAuthorityURI = attributeAuthorityURI
1250           
1251            self.credentials[issuerName] = thisCredential
1252           
1253            if attributeAuthorityURI:
1254                self.credentialsKeyedByURI[
1255                    attributeAuthorityURI] = thisCredential
1256           
1257            # Update the Credentials Repository - the permanent store of user
1258            # authorisation credentials.  This allows credentials for previous
1259            # sessions to be re-instated
1260            if self.credentialRepository and bUpdateCredentialRepository:
1261                self.updateCredentialRepository()
1262
1263        # Flag to caller to indicate whether the input certificate was added
1264        # to the credentials or an exsiting certificate from the same issuer
1265        # took precedence
1266        return bUpdateCred
1267           
1268
1269    def audit(self):
1270        """Check the credentials held in the wallet removing any that have
1271        expired or are otherwise invalid."""
1272
1273        log.debug("NDGCredentialWallet.audit ...")
1274       
1275        # Nb. No signature check is carried out.  To do a check, access is
1276        # needed to the cert of the CA that issued the Attribute Authority's
1277        # cert
1278        #
1279        # P J Kershaw 12/09/05
1280        for key, val in self.credentials.items():
1281            if not val.credential.isValid(chkSig=False):
1282                del self.credentials[key]
1283
1284    def updateCredentialRepository(self, auditCred=True):
1285        """Copy over non-persistent credentials held by wallet into the
1286        perminent repository.
1287       
1288        @type auditCred: bool
1289        @param auditCred: filter existing credentials in the repository
1290        removing invalid ones"""
1291
1292        log.debug("NDGCredentialWallet.updateCredentialRepository ...")
1293       
1294        if not self.credentialRepository:
1295            raise CredentialWalletError("No Credential Repository has been "
1296                                        "created for this wallet")
1297                           
1298        # Filter out invalid certs unless auditCred flag is explicitly set to
1299        # false
1300        if auditCred: self.audit()
1301
1302        # Update the database - only add new entries i.e. with an ID of -1
1303        attCertList = [i.credential for i in self.credentials.values() 
1304                       if i.id == -1]
1305
1306        self.credentialRepository.addCredentials(self.userId, attCertList)
1307
1308    def _createAttributeAuthorityClnt(self, attributeAuthorityURI):
1309        """Set up a client to an Attribute Authority with the given URI
1310       
1311        @type attributeAuthorityURI: string
1312        @param attributeAuthorityURI: Attribute Authority Web Service URI.
1313
1314        @rtype: ndg.security.common.attributeauthority.AttributeAuthorityClient
1315        @return: new Attribute Authority client instance"""
1316
1317        log.debug('NDGCredentialWallet._createAttributeAuthorityClnt for '
1318                  'service: "%s"' % attributeAuthorityURI)
1319
1320        attributeAuthorityClnt = AttributeAuthorityClient(
1321                            uri=attributeAuthorityURI,
1322                            sslCACertFilePathList=self.sslCACertFilePathList,
1323                            cfg=self.wssCfgFilePath or self._cfg,
1324                            cfgFileSection=self.wssCfgSection,
1325                            cfgFilePrefix=self.wssCfgPrefix,
1326                            **(self.wssCfgKw or {}))
1327       
1328        # If a user certificate is set, use this to sign messages instead of
1329        # the default settings in the WS-Security config. 
1330        if attributeAuthorityClnt.signatureHandler is not None and \
1331           self.userPriKey is not None:
1332            if self.issuingX509Cert is not None:
1333                # Pass a chain of certificates -
1334                # Initialise WS-Security signature handling to pass
1335                # BinarySecurityToken containing user cert and cert for user
1336                # cert issuer
1337                attributeAuthorityClnt.signatureHandler.reqBinSecTokValType = \
1338                            SignatureHandler.binSecTokValType["X509PKIPathv1"]
1339                attributeAuthorityClnt.signatureHandler.signingCertChain = (
1340                                    self.issuingX509Cert, self.userX509Cert)               
1341
1342                attributeAuthorityClnt.signatureHandler.signingPriKey = \
1343                                                            self.userPriKey
1344            elif self.userX509Cert is not None:
1345                # Pass user cert only - no need to pass a cert chain. 
1346                # This type of token is more likely to be supported by the
1347                # various WS-Security toolkits
1348                attributeAuthorityClnt.signatureHandler.reqBinSecTokValType = \
1349                                    SignatureHandler.binSecTokValType["X509v3"]
1350                attributeAuthorityClnt.signatureHandler.signingCert = \
1351                                                            self.userX509Cert
1352
1353                attributeAuthorityClnt.signatureHandler.signingPriKey = \
1354                                                            self.userPriKey
1355
1356        return attributeAuthorityClnt
1357
1358
1359    def _getAttCert(self, 
1360                    attributeAuthorityURI=None, 
1361                    attributeAuthority=None,
1362                    extAttCert=None):       
1363        """Wrapper to Attribute Authority attribute certificate request.  See
1364        getAttCert for the classes' public interface.
1365       
1366        If successful, a new attribute certificate is issued to the user
1367        and added into the wallet
1368       
1369        @type attributeAuthorityURI: string
1370        @param attributeAuthorityURI: to call as a web service, specify the URI
1371        for the Attribute Authority.
1372       
1373        @type attributeAuthority: ndg.security.server.attributeauthority.AttributeAuthority
1374        @param attributeAuthority: Alternative to attributeAuthorityURI - to
1375        run on the local machine, specify a local Attribute Authority
1376        instance.
1377
1378        @type extAttCert: ndg.security.common.AttCert.AttCert
1379        @param extAttCert: an existing Attribute Certificate which can
1380        be used to making a mapping should the user not be registered with the
1381        Attribute Authority"""
1382     
1383        log.debug("NDGCredentialWallet._getAttCert ...")
1384       
1385       
1386        # If a user cert. is present, ignore the user ID setting.  The
1387        # Attribute Authority will set the userId field of the
1388        # Attribute Certificate based on the DN of the user certificate
1389        if self.userX509Cert:
1390            userId = str(self.userX509Cert.dn)
1391        else:
1392            userId = self.userId
1393           
1394        if attributeAuthority is not None and \
1395           attributeAuthorityURI is not None:
1396            raise KeyError("Both attributeAuthorityURI and attributeAuthority "
1397                           "keywords have been set")
1398       
1399        if attributeAuthority is None:
1400            attributeAuthority = self.attributeAuthority
1401           
1402        if attributeAuthorityURI is None:
1403            attributeAuthorityURI = self.attributeAuthorityURI
1404           
1405        # Set a client alias according to whether the Attribute Authority is
1406        # being called locally or as a remote service
1407        if attributeAuthorityURI is not None:
1408            # Call Remote Service at given URI
1409            aaInterface = self._createAttributeAuthorityClnt(
1410                                                        attributeAuthorityURI)                           
1411            log.debug('NDGCredentialWallet._getAttCert for remote Attribute '
1412                      'Authority service: "%s" ...' % attributeAuthorityURI)
1413               
1414        elif attributeAuthority is not None:
1415            # Call local based Attribute Authority with settings from the
1416            # configuration file attributeAuthority
1417            aaInterface = attributeAuthority
1418            log.debug('NDGCredentialWallet._getAttCert for local Attribute '
1419                      'Authority: "%r" ...' % attributeAuthority)
1420        else:
1421            raise CredentialWalletError("Error requesting attribute: "
1422                                        "certificate a URI or Attribute "
1423                                        "Authority instance must be specified")
1424       
1425        try:
1426            # Request a new attribute certificate from the Attribute
1427            # Authority
1428            attCert = aaInterface.getAttCert(userId=userId,
1429                                             userAttCert=extAttCert)
1430           
1431            log.info('Granted Attribute Certificate from issuer DN = "%s"'%
1432                     attCert.issuerDN)
1433           
1434        except (AttributeAuthorityAccessDenied, AttributeRequestDenied), e:
1435            # AttributeAuthorityAccessDenied is raised if
1436            # aaInterface is a local AA instance and
1437            # AttributeRequestDenied is raised for a client to a remote AA
1438            # service
1439            raise CredentialWalletAttributeRequestDenied(str(e))
1440                   
1441        except Exception, e:
1442            raise CredentialWalletError("Requesting attribute certificate: %s"%
1443                                        e)
1444
1445        # Update attribute Certificate instance with CA's certificate ready
1446        # for signature check in addCredential()
1447        if self._caCertFilePathList is None:
1448            raise CredentialWalletError("No CA certificate has been set")
1449       
1450        attCert.certFilePathList = self._caCertFilePathList
1451
1452       
1453        # Add credential into wallet
1454        #
1455        # Nb. if the certificates signature is invalid, it will be rejected
1456        log.debug("Adding credentials into wallet...")
1457        self.addCredential(attCert)
1458       
1459        return attCert
1460
1461    def _getAAHostInfo(self, 
1462                       attributeAuthority=None,
1463                       attributeAuthorityURI=None):
1464        """Wrapper to Attribute Authority getHostInfo
1465       
1466        _getAAHostInfo([attributeAuthority=f|attributeAuthorityURI=u])
1467                   
1468        @type userRole: string
1469        @param userRole: get hosts which have a mapping to this role
1470       
1471        @type attributeAuthorityURI: string
1472        @param attributeAuthorityURI: to call as a web service, specify the URI
1473        for the Attribute Authority.
1474       
1475        @type attributeAuthority: string
1476        @param attributeAuthority: Alternative to attributeAuthorityURI - to
1477        run on the local machine, specify the local Attribute Authority
1478        instance.
1479        """
1480
1481        if attributeAuthority is None:
1482            attributeAuthority = self.attributeAuthority
1483           
1484        if attributeAuthorityURI is None:
1485            attributeAuthorityURI = self.attributeAuthorityURI
1486       
1487        log.debug('NDGCredentialWallet._getAAHostInfo for service: "%s" ...' % 
1488                  attributeAuthorityURI or attributeAuthority)
1489           
1490        # Set a client alias according to whether the Attribute Authority is
1491        # being called locally or asa remote service
1492        if attributeAuthorityURI is not None:
1493            # Call Remote Service at given URI
1494            attributeAuthorityClnt = self._createAttributeAuthorityClnt(
1495                                                    attributeAuthorityURI)
1496
1497        elif attributeAuthority is not None:
1498            # Call local based Attribute Authority with settings from the
1499            # configuration file attributeAuthority
1500            attributeAuthorityClnt = attributeAuthority
1501           
1502        else:
1503            raise CredentialWalletError("Error requesting trusted hosts info: " 
1504                                        "a URI or Attribute Authority " 
1505                                        "configuration file must be specified")
1506           
1507        try:
1508            # Request a new attribute certificate from the Attribute
1509            # Authority
1510            return attributeAuthorityClnt.getHostInfo()
1511           
1512        except Exception, e:
1513            log.error("Requesting host info: %s" % e)
1514            raise
1515
1516    def _getAATrustedHostInfo(self, 
1517                              userRole=None,
1518                              attributeAuthority=None,
1519                              attributeAuthorityURI=None):
1520        """Wrapper to Attribute Authority getTrustedHostInfo
1521       
1522        _getAATrustedHostInfo([userRole=r, ][attributeAuthority=f|
1523                              attributeAuthorityURI=u])
1524                   
1525        @type userRole: string
1526        @param userRole: get hosts which have a mapping to this role
1527       
1528        @type attributeAuthorityURI: string
1529        @param attributeAuthorityURI: to call as a web service, specify the URI
1530        for the Attribute Authority.
1531       
1532        @type attributeAuthority: string
1533        @param attributeAuthority: Alternative to attributeAuthorityURI - to
1534        run on the local machine, specify the local Attribute Authority
1535        instance.
1536        """
1537
1538        if attributeAuthority is None:
1539            attributeAuthority = self.attributeAuthority
1540           
1541        if attributeAuthorityURI is None:
1542            attributeAuthorityURI = self.attributeAuthorityURI
1543       
1544        log.debug('NDGCredentialWallet._getAATrustedHostInfo for role "%s" and '
1545                  'service: "%s" ...' % (userRole, 
1546                                attributeAuthorityURI or attributeAuthority))
1547           
1548        # Set a client alias according to whether the Attribute Authority is
1549        # being called locally or asa remote service
1550        if attributeAuthorityURI is not None:
1551            # Call Remote Service at given URI
1552            attributeAuthorityClnt = self._createAttributeAuthorityClnt(
1553                                                    attributeAuthorityURI)
1554
1555        elif attributeAuthority is not None:
1556            # Call local based Attribute Authority with settings from the
1557            # configuration file attributeAuthority
1558            attributeAuthorityClnt = attributeAuthority
1559           
1560        else:
1561            raise CredentialWalletError("Error requesting trusted hosts info: " 
1562                                        "a URI or Attribute Authority " 
1563                                        "configuration file must be specified")
1564           
1565        try:
1566            # Request a new attribute certificate from the Attribute
1567            # Authority
1568            return attributeAuthorityClnt.getTrustedHostInfo(role=userRole)
1569           
1570        except Exception, e:
1571            log.error("Requesting trusted host info: %s" % e)
1572            raise
1573
1574    def getAttCert(self,
1575                   reqRole=None,
1576                   attributeAuthority=None,
1577                   attributeAuthorityURI=None,
1578                   mapFromTrustedHosts=None,
1579                   rtnExtAttCertList=None,
1580                   extAttCertList=None,
1581                   extTrustedHostList=None,
1582                   refreshAttCert=False,
1583                   attCertRefreshElapse=None):
1584       
1585        """Get an Attribute Certificate from an Attribute Authority.  If this
1586        fails try to make a mapped Attribute Certificate by using a certificate
1587        from another host which has a trust relationship to the Attribute
1588        Authority in question.
1589
1590        getAttCert([reqRole=r, ][attributeAuthority=a|attributeAuthorityURI=u,]
1591                   [mapFromTrustedHosts=m, ]
1592                   [rtnExtAttCertList=e, ][extAttCertList=el, ]
1593                   [extTrustedHostList=et, ][refreshAttCert=ra])
1594                 
1595        The procedure is:
1596
1597        1) Try attribute request using user certificate
1598        2) If the Attribute Authority (AA) doesn't recognise the certificate,
1599        find out any other hosts which have a trust relationship to the AA.
1600        3) Look for Attribute Certificates held in the wallet corresponding
1601        to these hosts.
1602        4) If no Attribute Certificates are available, call the relevant
1603        hosts' AAs to get certificates
1604        5) Finally, use these new certificates to try to obtain a mapped
1605        certificate from the original AA
1606        6) If this fails access is denied     
1607                   
1608        @type reqRole: string
1609        @param reqRole: the required role to get access for
1610       
1611        @type attributeAuthorityURI: string
1612        @param attributeAuthorityURI: to call as a web service, specify the URI
1613        for the Attribute Authority.
1614       
1615        @type attributeAuthority: string
1616        @param attributeAuthority: Altenrative to attributeAuthorityURI - to
1617        run on the local machine, specify a local Attribute Authority
1618        instance.
1619                               
1620        @type mapFromTrustedHosts: bool / None     
1621        @param mapFromTrustedHosts: if request fails via the user's cert
1622        ID, then it is possible to get a mapped certificate by using
1623        certificates from other AA's.  Set this flag to True, to allow this
1624        second stage of generating a mapped certificate from the certificate
1625        stored in the wallet credentials.
1626
1627        If set to False, it is possible to return the list of certificates
1628        available for mapping and then choose which one or ones to use for
1629        mapping by re-calling getAttCert with extAttCertList set to these
1630        certificates.
1631       
1632        Defaults to None in which case self._mapFromTrustedHosts is not
1633        altered
1634
1635        The list is returned via CredentialWalletAttributeRequestDenied
1636        exception.  If no value is set, the default value held in
1637        self.mapFromTrustedHosts is used
1638
1639        @type rtnExtAttCertList: bool / None
1640        @param rtnExtAttCertList: If request fails, make a list of
1641        candidate certificates from other Attribute Authorities which the user
1642        could use to retry and get a mapped certificate.
1643                               
1644        If mapFromTrustedHosts is set True this flags value is overriden and
1645        effectively set to True.
1646
1647        If no value is set, the default value held in self._rtnExtAttCertList
1648        is used.
1649                               
1650        The list is returned via a CredentialWalletAttributeRequestDenied
1651        exception object.
1652                               
1653        @type extAttCertList: list
1654        @param extAttCertList: Attribute Certificate or list of certificates
1655        from other Attribute Authorities.  These can be used to get a mapped
1656        certificate if access fails based on the user's certificate
1657        credentials.  They are tried out in turn until access is granted so
1658        the order of the list decides the order in which they will be tried
1659
1660        @type extTrustedHostList:
1661        @param extTrustedHostList: same as extAttCertList keyword, but
1662        instead of providing Attribute Certificates, give a list of Attribute
1663        Authority hosts.  These will be matched up to Attribute Certificates
1664        held in the wallet.  Matching certificates will then be used to try to
1665        get a mapped Attribute Certificate.
1666       
1667        @type refreshAttCert: bool
1668        @param refreshAttCert: if set to True, the attribute request
1669        will go ahead even if the wallet already contains an Attribute
1670        Certificate from the target Attribute Authority.  The existing AC in
1671        the wallet will be replaced by the new one obtained from this call.
1672                               
1673        If set to False, this method will check to see if an AC issued by the
1674        target AA already exists in the wallet.  If so, it will return this AC
1675        to the caller without proceeding to make a call to the AA.
1676       
1677        @type attCertRefreshElapse: float / int
1678        @param attCertRefreshElapse: determine whether to replace an
1679        existing AC in the cache with a fresh one.  If the existing one has
1680        less than attCertRefreshElapse time in seconds left before expiry then
1681        replace it.
1682       
1683        @rtype: ndg.security.common.AttCert.AttCert
1684        @return: Attribute Certificate retrieved from Attribute Authority"""
1685       
1686        log.debug("NDGCredentialWallet.getAttCert ...")
1687       
1688        # Both these assignments are calling set property methods implicitly!
1689        if attributeAuthorityURI:
1690            self.attributeAuthorityURI = attributeAuthorityURI
1691           
1692        if attributeAuthority is not None:
1693            self.attributeAuthority = attributeAuthority
1694           
1695        if not refreshAttCert and self.credentials:
1696            # Refresh flag is not set so it's OK to check for any existing
1697            # Attribute Certificate in the wallet whose issuerName match the
1698            # target AA's name
1699           
1700            # Find out the site ID for the target AA by calling AA's host
1701            # info WS method
1702            log.debug("NDGCredentialWallet.getAttCert - check AA site ID ...")
1703            try:
1704                hostInfo = self._getAAHostInfo()
1705                aaName = hostInfo.keys()[0]
1706            except Exception, e:
1707                raise CredentialWalletError("Getting host info: %s" % e)
1708           
1709            # Look in the wallet for an AC with the same issuer name
1710            if aaName in self.credentials:
1711                # Existing Attribute Certificate found in wallet - Check that
1712                # it will be valid for at least the next 2 hours
1713                if attCertRefreshElapse is not None:
1714                    self.attCertRefreshElapse = attCertRefreshElapse
1715                   
1716                dtNow = datetime.utcnow() + \
1717                        timedelta(seconds=self.attCertRefreshElapse)
1718               
1719                attCert = self.credentials[aaName]['attCert']
1720                if attCert.isValidTime(dtNow=dtNow):
1721                    log.info("Retrieved an existing %s AC from the wallet" % 
1722                             aaName)
1723                    return attCert
1724                     
1725        # Check for settings from input, if not set use previous settings
1726        # made
1727        if mapFromTrustedHosts is not None:
1728            self.mapFromTrustedHosts = mapFromTrustedHosts
1729
1730        if rtnExtAttCertList is not None:
1731            self.rtnExtAttCertList = rtnExtAttCertList
1732
1733        # Check for list of external trusted hosts (other trusted NDG data
1734        # centres)
1735        if extTrustedHostList:
1736            log.info("Checking for ACs in wallet matching list of trusted "
1737                     "hosts set: %s" % extTrustedHostList)
1738           
1739            if not self.mapFromTrustedHosts:
1740                raise CredentialWalletError("A list of trusted hosts has been " 
1741                                      "input but mapping from trusted hosts "
1742                                      "is set to disallowed")
1743           
1744            if isinstance(extTrustedHostList, basestring):
1745                extTrustedHostList = [extTrustedHostList]
1746
1747            # Nb. Any extAttCertList is overriden by extTrustedHostList being
1748            # set
1749            extAttCertList = [self.credentials[hostName]['attCert'] 
1750                              for hostName in extTrustedHostList
1751                              if hostName in self.credentials]
1752
1753        # Set an empty list to trigger an AttributeError by initialising it to
1754        # None
1755        if extAttCertList == []:
1756            extAttCertList = None
1757           
1758        # Repeat authorisation attempts until succeed or means are exhausted
1759        while True:
1760           
1761            # Check for candidate certificates for mapping
1762            try:
1763                # If list is set get the next cert
1764                extAttCert = extAttCertList.pop()
1765
1766            except AttributeError:
1767                log.debug("No external Attribute Certificates - trying "
1768                          "request without mapping...")
1769                # No List set - attempt request without
1770                # using mapping from trusted hosts
1771                extAttCert = None
1772                           
1773            except IndexError:
1774               
1775                # List has been emptied without attribute request succeeding -
1776                # give up
1777                errMsg = ("Attempting to obtained a mapped certificate: "
1778                          "no external attribute certificates are available")
1779                   
1780                # Add the exception form the last call to the Attribute
1781                # Authority if an error exists
1782                try:
1783                    errMsg += ": %s" % attributeRequestDenied
1784                except NameError:
1785                    pass
1786
1787                raise CredentialWalletAttributeRequestDenied(errMsg)
1788                                                   
1789               
1790            # Request Attribute Certificate from Attribute Authority
1791            try:
1792                attCert = self._getAttCert(extAttCert=extAttCert)               
1793                # Access granted
1794                return attCert
1795           
1796            except CredentialWalletAttributeRequestDenied, \
1797                   attributeRequestDenied:
1798                if not self.mapFromTrustedHosts and not self.rtnExtAttCertList:
1799                    log.debug("Creating a mapped certificate option is not "
1800                              "set - raising "
1801                              "CredentialWalletAttributeRequestDenied "
1802                              "exception saved from earlier")
1803                    raise attributeRequestDenied
1804
1805                if isinstance(extAttCertList, list):
1806                    # An list of attribute certificates from trusted hosts
1807                    # is present continue cycling through this until one of
1808                    # them is accepted and a mapped certificate can be derived
1809                    log.debug("AC request denied - but external ACs available "
1810                              "to try mapped AC request ...")
1811                    continue
1812                             
1813                #  Use the input required role and the AA's trusted host list
1814                # to identify attribute certificates from other hosts which
1815                # could be used to make a mapped certificate
1816                log.debug("Getting a list of trusted hosts for mapped AC "
1817                          "request ...")
1818                try:
1819                    trustedHostInfo = self._getAATrustedHostInfo(reqRole)
1820                   
1821                except NoMatchingRoleInTrustedHosts, e:
1822                    raise CredentialWalletAttributeRequestDenied(
1823                        'Can\'t get a mapped Attribute Certificate for '
1824                        'the "%s" role' % reqRole)
1825               
1826                except Exception, e:
1827                    raise CredentialWalletError("Getting trusted hosts: %s"%e)
1828
1829                if not trustedHostInfo:
1830                    raise CredentialWalletAttributeRequestDenied(
1831                        "Attribute Authority has no trusted hosts with "
1832                        "which to make a mapping")
1833
1834                # Initialise external certificate list here - if none are
1835                # found IndexError will be raised on the next iteration and
1836                # an access denied error will be raised
1837                extAttCertList = []
1838
1839                # Look for Attribute Certificates with matching issuer host
1840                # names
1841                log.debug("Checking wallet for ACs issued by one of the "
1842                          "trusted hosts...")
1843                for hostName in self.credentials:
1844
1845                    # Nb. Candidate certificates for mappings must have
1846                    # original provenance and contain at least one of the
1847                    # required roles
1848                    attCert = self.credentials[hostName].credential
1849                   
1850                    if hostName in trustedHostInfo and attCert.isOriginal():                       
1851                        for role in attCert.roles:
1852                            if role in trustedHostInfo[hostName]['role']:                               
1853                                extAttCertList.append(attCert)
1854
1855                if not extAttCertList:
1856                    log.debug("No wallet ACs matched any of the trusted "
1857                              "hosts.  - Try request for an AC from a "
1858                              "trusted host ...")
1859                   
1860                    # No certificates in the wallet matched the trusted host
1861                    # and required roles
1862                    #
1863                    # Try each host in turn in order to get a certificate with
1864                    # the required credentials in order to do a mapping
1865                    for host, info in trustedHostInfo.items():
1866                        try:
1867                            # Try request to trusted host
1868                            extAttCert = self._getAttCert(
1869                                        attributeAuthorityURI=info['aaURI'])
1870
1871                            # Check the certificate contains at least one of
1872                            # the required roles
1873                            if [True for r in extAttCert.roles
1874                                if r in info['role']]:
1875                               extAttCertList.append(extAttCert)
1876
1877                               # For efficiency, stop once obtained a valid
1878                               # cert - but may want complete list for user to
1879                               # choose from
1880                               #break
1881                               
1882                        except Exception, e:
1883                            # ignore any errors and continue
1884                            log.warning('AC request to trusted host "%s"' 
1885                                        ' resulted in: %s' % (info['aaURI'],e))
1886                           
1887                if not extAttCertList:                       
1888                    raise CredentialWalletAttributeRequestDenied(
1889                        "No certificates are available with which to "
1890                        "make a mapping to the Attribute Authority")
1891
1892
1893                if not self.mapFromTrustedHosts:
1894                   
1895                    # Exit here returning the list of candidate certificates
1896                    # that could be used to make a mapped certificate
1897                    msg = ("User is not registered with Attribute "
1898                           "Authority - retry using one of the returned "
1899                           "Attribute Certificates obtained from other " 
1900                           "trusted hosts")
1901                         
1902                    raise CredentialWalletAttributeRequestDenied(msg,
1903                                            extAttCertList=extAttCertList,
1904                                            trustedHostInfo=trustedHostInfo)
1905       
1906       
1907class CredentialRepositoryError(_CredentialWalletException):   
1908    """Exception handling for NDG Credential Repository class."""
1909
1910
1911class CredentialRepository(object):
1912    """CredentialWallet's abstract interface class to a Credential Repository.
1913    The Credential Repository is abstract store of user currently valid user
1914    credentials.  It enables retrieval of attribute certificates from a user's
1915    previous session(s)"""
1916       
1917    def __init__(self, propFilePath=None, dbPPhrase=None, **prop):
1918        """Initialise Credential Repository abstract base class.  Derive from
1919        this class to define Credentail Repository interface Credential
1920        Wallet
1921
1922        If the connection string or properties file is set a connection
1923        will be made
1924
1925        @type dbPPhrase: string
1926        @param dbPPhrase: pass-phrase to database if applicable
1927       
1928        @type propFilePath: string
1929        @param propFilePath: file path to a properties file.  This could
1930        contain configuration parameters for the repository e.g.  database
1931        connection parameters
1932       
1933        @type **prop: dict
1934        @param **prop: any other keywords required
1935        """
1936        raise NotImplementedError(
1937            self.__init__.__doc__.replace('\n       ',''))
1938
1939
1940    def addUser(self, userId, dn=None):
1941        """A new user to Credentials Repository
1942       
1943        @type userId: string
1944        @param userId: userId for new user
1945        @type dn: string
1946        @param dn: users Distinguished Name (optional)"""
1947        raise NotImplementedError(
1948            self.addUser.__doc__.replace('\n       ',''))
1949
1950                           
1951    def auditCredentials(self, userId=None, **attCertValidKeys):
1952        """Check the attribute certificates held in the repository and delete
1953        any that have expired
1954
1955        @type userId: basestring/list or tuple
1956        @param userId: audit credentials for the input user ID or list of IDs
1957        @type attCertValidKeys: dict
1958        @param **attCertValidKeys: keywords which set how to check the
1959        Attribute Certificate e.g. check validity time, XML signature, version
1960         etc.  Default is check validity time only - See AttCert class"""
1961        raise NotImplementedError(
1962            self.auditCredentials.__doc__.replace('\n       ',''))
1963
1964
1965    def getCredentials(self, userId):
1966        """Get the list of credentials for a given users DN
1967       
1968        @type userId: string
1969        @param userId: users userId, name or X.509 cert. distinguished name
1970        @rtype: list
1971        @return: list of Attribute Certificates"""
1972        raise NotImplementedError(
1973            self.getCredentials.__doc__.replace('\n       ',''))
1974
1975       
1976    def addCredentials(self, userId, attCertList):
1977        """Add new attribute certificates for a user.  The user must have
1978        been previously registered in the repository
1979
1980        @type userId: string
1981        @param userId: users userId, name or X.509 cert. distinguished name
1982        @type attCertList: list
1983        @param attCertList: list of attribute certificates"""
1984        raise NotImplementedError(
1985            self.addCredentials.__doc__.replace('\n       ',''))
1986
1987
1988class NullCredentialRepository(CredentialRepository):
1989    """Implementation of Credential Repository interface with empty stubs. 
1990    Use this class in the case where no Credential Repository is required"""
1991   
1992    def __init__(self, propFilePath=None, dbPPhrase=None, **prop):
1993        pass
1994
1995    def addUser(self, userId):
1996        pass
1997                           
1998    def auditCredentials(self, **attCertValidKeys):
1999        pass
2000
2001    def getCredentials(self, userId):
2002        return []
2003       
2004    def addCredentials(self, userId, attCertList):
2005        pass
Note: See TracBrowser for help on using the repository browser.