source: TI12-security/trunk/python/ndg.security.server/ndg/security/server/attributeauthority.py @ 5643

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/ndg.security.server/ndg/security/server/attributeauthority.py@5643
Revision 5643, 56.1 KB checked in by pjkersha, 11 years ago (diff)

Working Attribute Authority unit test with refactored attribute properties.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1"""NDG Attribute Authority server side code
2
3handles security user attribute (role) allocation
4
5NERC Data Grid Project
6"""
7__author__ = "P J Kershaw"
8__date__ = "15/04/05"
9__copyright__ = "(C) 2009 Science and Technology Facilities Council"
10__license__ = "BSD - see LICENSE file in top-level directory"
11__contact__ = "Philip.Kershaw@stfc.ac.uk"
12__revision__ = '$Id:attributeauthority.py 4367 2008-10-29 09:27:59Z pjkersha $'
13
14import types
15
16
17# Create unique names for attribute certificates
18import tempfile
19import os
20
21# Alter system path for dynamic import of user roles class
22import sys
23
24# For parsing of properties file
25try: # python 2.5
26    from xml.etree import cElementTree as ElementTree
27except ImportError:
28    # if you've installed it yourself it comes this way
29    import cElementTree as ElementTree
30
31import logging
32log = logging.getLogger(__name__)
33
34import re
35
36from ndg.security.common.utils import TypedList
37from ndg.security.common.utils.configfileparsers import \
38    CaseSensitiveConfigParser
39   
40# X509 Certificate handling
41from ndg.security.common.X509 import X509Cert
42
43# NDG Attribute Certificate
44from ndg.security.common.AttCert import AttCert, AttCertRead, AttCertParse
45
46from ndg.security.common.utils.configfileparsers import \
47    readAndValidateProperties
48from ndg.security.common.utils.classfactory import instantiateClass
49
50class AttributeAuthorityError(Exception):
51    """Exception handling for NDG Attribute Authority class."""
52    def __init__(self, msg):
53        log.error(msg)
54        Exception.__init__(self, msg)
55
56class AttributeAuthorityConfigError(Exception):
57    """NDG Attribute Authority error with configuration. e.g. properties file
58    directory permissions or role mapping file"""
59    def __init__(self, msg):
60        log.error(msg)
61        Exception.__init__(self, msg)
62       
63class AttributeAuthorityAccessDenied(AttributeAuthorityError):
64    """NDG Attribute Authority - access denied exception.
65
66    Raise from getAttCert method where no roles are available for the user
67    but that the request is otherwise valid.  In all other error cases raise
68    AttributeAuthorityError"""   
69
70class AttributeAuthorityNoTrustedHosts(AttributeAuthorityError):
71    """Raise from getTrustedHosts if there are no trusted hosts defined in
72    the map configuration"""
73
74class AttributeAuthorityNoMatchingRoleInTrustedHosts(AttributeAuthorityError):
75    """Raise from getTrustedHosts if there is no mapping to any of the
76    trusted hosts for the given input role name"""
77
78
79class AttributeAuthority(object):
80    """NDG Attribute Authority - service for allocation of user authorization
81    tokens - attribute certificates.
82   
83    @type propertyDefaults: dict
84    @cvar propertyDefaults: valid configuration property keywords
85   
86    @type attributeInterfacePropertyDefaults: dict
87    @cvar attributeInterfacePropertyDefaults: valid configuration property
88    keywords for the Attribute Interface plugin
89   
90    @type mapConfigHostDefaults: dict
91    @cvar mapConfigHostDefaults: valid configuration property
92    keywords for the Map Configuration XML Host element
93   
94    @type DEFAULT_CONFIG_DIRNAME: string
95    @cvar DEFAULT_CONFIG_DIRNAME: configuration directory under $NDGSEC_DIR -
96    default location for properties file
97   
98    @type DEFAULT_PROPERTY_FILENAME: string
99    @cvar DEFAULT_PROPERTY_FILENAME: default file name for properties file
100    under DEFAULT_CONFIG_DIRNAME
101   
102    @type ATTRIBUTE_INTERFACE_KEYNAME: basestring
103    @param ATTRIBUTE_INTERFACE_KEYNAME: attribute interface parameters key
104    name - see initAttributeInterface for details
105    """
106
107    # Code designed from NERC Data Grid Enterprise and Information Viewpoint
108    # documents.
109    #
110    # Also, draws from Neil Bennett's ACServer class used in the Java
111    # implementation of NDG Security
112
113    DEFAULT_CONFIG_DIRNAME = "conf"
114    DEFAULT_PROPERTY_FILENAME = "attributeAuthority.cfg"
115    ATTRIBUTE_INTERFACE_KEYNAME = 'attributeInterface'
116    CONFIG_LIST_SEP_PAT = re.compile(',\W*')
117   
118    attributeInterfacePropertyDefaults = {
119        'modFilePath':  '',
120        'modName':      '',
121        'className':    ''
122    }
123   
124    # valid configuration property keywords with accepted default values. 
125    # Values set to not NotImplemented here denote keys which must be specified
126    # in the config
127    propertyDefaults = { 
128        'name':                 '',
129        'signingCertFilePath':  '',
130        'signingPriKeyFilePath':'',
131        'signingPriKeyPwd':     None,
132        'caCertFilePathList':   [],
133        'attCertLifetime':      -1,
134        'attCertNotBeforeOff':  0,
135        'attCertFileName':      '',
136        'attCertFileLogCnt':    0,
137        'mapConfigFilePath':    '',
138        'attCertDir':           '',
139        'dnSeparator':          '/',
140        ATTRIBUTE_INTERFACE_KEYNAME: attributeInterfacePropertyDefaults
141    }
142   
143    mapConfigHostDefaults = {
144        'siteName':                 None,
145        'aaURI':                    NotImplemented,
146        'aaDN':                     NotImplemented,
147        'loginURI':                 NotImplemented,
148        'loginServerDN':            NotImplemented,
149        'loginRequestServerDN':     NotImplemented
150    }
151
152    def __init__(self):
153        """Create new Attribute Authority instance"""
154        log.info("Initialising service ...")
155       
156        # Initial config file property based attributes
157        for name, val in AttributeAuthority.propertyDefaults.items():
158            setattr(self, '_AttributeAuthority__%s' % name, val)
159       
160        self.__caCertFilePathList = TypedList(basestring)
161       
162        self.__propFilePath = None       
163        self.__propFileSection = 'DEFAULT'
164        self.__propPrefix = ''
165       
166        # Initialise role mapping look-ups - These are set in readMapConfig()
167        self.__mapConfig = None
168        self.__localRole2RemoteRole = None
169        self.__remoteRole2LocalRole = None
170       
171        self.__cert = None
172       
173        # Issuer details - serialise using the separator string set in the
174        # properties file
175        self.__issuer = None
176        self.__issuerSerialNumber = None
177        self.__attCertLog = None
178        self.__name = None
179       
180        self.__attributeInterfaceCfg = {}
181
182    def _getMapConfig(self):
183        return self.__mapConfig
184
185    def _getCert(self):
186        return self.__cert
187
188    def _getIssuer(self):
189        return self.__issuer
190
191    def _getIssuerSerialNumber(self):
192        return self.__issuerSerialNumber
193
194    def _getAttCertLog(self):
195        return self.__attCertLog
196
197    def _getName(self):
198        return self.__name
199
200    def _getAttCertLifetime(self):
201        return self.__attCertLifetime
202
203    def _getAttCertNotBeforeOff(self):
204        return self.__attCertNotBeforeOff
205
206    def _getAttCertDir(self):
207        return self.__attCertDir
208
209    def _getAttributeInterface(self):
210        return self.__attributeInterface
211
212    def _getMapConfigFile(self):
213        return self.__mapConfigFile
214
215    def _getName(self):
216        return self.__name
217
218    def _getTrustedHostInfo(self):
219        return self.__trustedHostInfo
220
221    def _setCert(self, value):
222        if not isinstance(value, X509Cert):
223            raise TypeError('Expecting %r type for "cert"; got %r' %
224                            (X509Cert, type(value)))
225           
226        self.__cert = value
227
228    def _setIssuer(self, value):
229        self.__issuer = value
230
231    def _setIssuerSerialNumber(self, value):
232        if not isinstance(value, (long, int)):
233            raise TypeError('Expecting long or int type for "name"; got %r' %
234                            type(value))
235        self.__issuerSerialNumber = value
236
237    def _setAttCertLog(self, value):
238        if not isinstance(value, AttCertLog):
239            raise TypeError('Expecting %r type for "attCertLog"; got %r' %
240                            (AttCertLog, type(value)))
241        self.__attCertLog = value
242
243    def _setName(self, value):
244        if not isinstance(value, basestring):
245            raise TypeError('Expecting string type for "name"; got %r' %
246                            type(value))
247        self.__name = value
248
249    def _setAttCertLifetime(self, value):
250        if isinstance(value, float):
251            self.__attCertLifetime = value
252           
253        elif isinstance(value, (basestring, int, long)):
254            self.__attCertLifetime = float(value)
255        else:
256            raise TypeError('Expecting float, int, long or string type for '
257                            '"attCertLifetime"; got %r' % type(value))
258
259    def _setAttCertNotBeforeOff(self, value):
260        if isinstance(value, float):
261            self.__attCertNotBeforeOff = value
262           
263        elif isinstance(value, (basestring, int, long)):
264            self.__attCertNotBeforeOff = float(value)
265        else:
266            raise TypeError('Expecting float, int, long or string type for '
267                            '"attCertNotBeforeOff"; got %r' % type(value))
268
269    def _setAttCertDir(self, value):
270        if not isinstance(value, basestring):
271            raise TypeError('Expecting string type for "attCertDir"; got %r' % 
272                            type(value))
273
274        # Check directory path
275        try:
276            dirList = os.listdir(value)
277
278        except OSError, osError:
279            raise AttributeAuthorityConfigError('Invalid directory path for '
280                                                'Attribute Certificates store '
281                                                '"%s": %s' % 
282                                                (value, osError.strerror))
283        self.__attCertDir = value
284
285    def _setMapConfigFilePath(self, value):
286        self.__mapConfigFilePath = value
287
288    def _setTrustedHostInfo(self, value):
289        self.__trustedHostInfo = value
290
291    def _get_caCertFilePathList(self):
292        return self.__caCertFilePathList
293
294    def _set_caCertFilePathList(self, val):
295        if not isinstance(val, (list, tuple)):
296            raise TypeError('Expecting list or tuple type for '
297                            '"caCertFilePathList"; got %r' % type(val))
298           
299        # Overwrite any original settings
300        self.__caCertFilePathList = TypedList(basestring)
301       
302        # Update with new items
303        self.__caCertFilePathList += val
304   
305    caCertFilePathList = property(fget=_get_caCertFilePathList,
306                                  fset=_set_caCertFilePathList,
307                                  doc="list of file paths for CA certificates "
308                                      "used to validate an Attribute "
309                                      "Certificate")
310   
311    def _get_signingCertFilePath(self):
312        return self.__signingCertFilePath
313   
314    def _set_signingCertFilePath(self, value):
315        if not isinstance(value, basestring):
316            raise TypeError('Expecting string type for "signingCertFilePath"; '
317                            'got %r' % type(value))
318        self.__signingCertFilePath = value
319         
320    signingCertFilePath = property(fget=_get_signingCertFilePath, 
321                                   fset=_set_signingCertFilePath,
322                                   doc="X.509 certificate used for Attribute "
323                                       "certificate signature")
324   
325    def _get_signingPriKeyFilePath(self):
326        return self.__signingPriKeyFilePath
327   
328    def _set_signingPriKeyFilePath(self, value):
329        if not isinstance(value, basestring):
330            raise TypeError('Expecting string type for '
331                            '"signingPriKeyFilePath"; got %r' % type(value))
332        self.__signingPriKeyFilePath = value
333         
334    signingPriKeyFilePath = property(fget=_get_signingPriKeyFilePath, 
335                                     fset=_set_signingPriKeyFilePath,
336                                     doc="File Path for private key used to "
337                                         "sign Attribute certificate")
338   
339    def _get_signingPriKeyPwd(self):
340        return self.__signingPriKeyPwd
341   
342    def _set_signingPriKeyPwd(self, value):
343        if not isinstance(value, (type(None), basestring)):
344            raise TypeError('Expecting string or None type for '
345                            '"signingPriKeyPwd"; got %r' % type(value))
346        self.__signingPriKeyPwd = value
347         
348    signingPriKeyPwd = property(fget=_get_signingPriKeyPwd, 
349                                fset=_set_signingPriKeyPwd,
350                                doc="Password for private key file used to "
351                                    "for Attribute certificate signature")
352
353    def _get_attributeInterfaceCfg(self):
354        return self.__attributeInterfaceCfg
355   
356    attributeInterfaceCfg = property(fget=_get_attributeInterfaceCfg,
357                                     doc="Settings for Attribute Interface "
358                                         "initialisation")
359   
360    def _get_attCertFileName(self):
361        return self.__attCertFileName
362   
363    def _set_attCertFileName(self, value):
364        if not isinstance(value, basestring):
365            raise TypeError('Expecting string type for "attCertFileName"; got '
366                            '%r' % type(value))
367           
368        self.__attCertFileName = value
369         
370    attCertFileName = property(fget=_get_attCertFileName, 
371                                fset=_set_attCertFileName,
372                                doc="Attribute certificate file name for log "
373                                    "initialisation")
374   
375    def _get_attCertFileLogCnt(self):
376        return self.__attCertFileLogCnt
377   
378    def _set_attCertFileLogCnt(self, value):
379        if isinstance(value, int):
380            self.__attCertFileLogCnt = value
381        elif isinstance(value, basestring):
382            self.__attCertFileLogCnt = int(value)
383        else:
384            raise TypeError('Expecting int or string type for '
385                            '"attCertFileLogCnt"; got %r' % type(value))
386         
387    attCertFileLogCnt = property(fget=_get_attCertFileLogCnt, 
388                                 fset=_set_attCertFileLogCnt,
389                                 doc="Counter for Attribute Certificate log "
390                                     "rotating file handler")
391   
392    def _get_dnSeparator(self):
393        return self.__dnSeparator
394   
395    def _set_dnSeparator(self, value):
396        if not isinstance(value, basestring):
397            raise TypeError('Expecting string type for "dnSeparator"; got '
398                            '%r' % type(value))
399        self.__dnSeparator = value
400         
401    dnSeparator = property(fget=_get_dnSeparator, 
402                           fset=_set_dnSeparator,
403                           doc="Distinguished Name separator character used "
404                               "with X.509 Certificate issuer certificate")
405   
406    mapConfig = property(fget=_getMapConfig, 
407                         doc="MapConfig object")
408
409    cert = property(fget=_getCert, 
410                    fset=_setCert, 
411                    doc="X.509 Issuer Certificate")
412
413    issuer = property(fget=_getIssuer, 
414                      fset=_setIssuer, 
415                      doc="Issuer name")
416
417    issuerSerialNumber = property(fget=_getIssuerSerialNumber, 
418                                  fset=_setIssuerSerialNumber, 
419                                  doc="Issuer Serial Number")
420
421    attCertLog = property(fget=_getAttCertLog,
422                          fset=_setAttCertLog, 
423                          doc="Attribute certificate logging object")
424
425    name = property(fget=_getName, 
426                    fset=_setName, 
427                    doc="Issuer organisation name")
428
429    attCertLifetime = property(fget=_getAttCertLifetime, 
430                               fset=_setAttCertLifetime, 
431                               doc="Attribute certificate lifetime")
432
433    attCertNotBeforeOff = property(fget=_getAttCertNotBeforeOff, 
434                                   fset=_setAttCertNotBeforeOff, 
435                                   doc="Attribute certificate clock skew in "
436                                       "seconds")
437
438    attCertDir = property(fget=_getAttCertDir, 
439                          fset=_setAttCertDir, 
440                          doc="Attribute certificate log directory")
441
442    attributeInterface = property(fget=_getAttributeInterface, 
443                                  doc="Attribute Interface object")
444
445    name = property(fget=_getName, fset=_setName, doc="Organisation Name")
446
447    trustedHostInfo = property(fget=_getTrustedHostInfo, 
448                               fset=_setTrustedHostInfo, 
449                               doc="Dictionary of trusted organisations")
450       
451    @classmethod
452    def fromPropertyFile(cls, propFilePath=None, bReadMapConfig=True):
453        """Create new NDG Attribute Authority instance from the property file
454        settings
455
456        @type propFilePath: string
457        @param propFilePath: path to file containing Attribute Authority
458        configuration parameters.  It defaults to $NDGSEC_AA_PROPFILEPATH or
459        if not set, $NDGSEC_DIR/conf/attributeAuthority.cfg
460        - if the filename ends with 'xml', it is assumed to be in the xml
461        format
462        - otherwise it is assumed to be a flat text 'ini' type file
463        @type propFileSection: basestring
464        @param propFileSection: section of properties file to read from.  This
465        applies to ini format files only and is ignored for XML format
466        properties files
467        @type bReadMapConfig: boolean
468        @param bReadMapConfig: by default the Map Configuration file is
469        read.  Set this flag to False to override.
470        """
471           
472        attributeAuthority = AttributeAuthority()
473        attributeAuthority.propFilePath = propFilePath
474           
475        attributeAuthority.readProperties()
476
477        # Read the Map Configuration file
478        if bReadMapConfig:
479            attributeAuthority.readMapConfig()
480
481        # Instantiate Certificate object
482        log.debug("Reading and checking Attribute Authority X.509 cert. ...")
483        attributeAuthority.cert = X509Cert.Read(
484                                        attributeAuthority.signingCertFilePath)
485
486        # Check it's valid
487        try:
488            attributeAuthority.cert.isValidTime(raiseExcep=True)
489           
490        except Exception, e:
491            raise AttributeAuthorityError("Attribute Authority's certificate "
492                                          "is invalid: %s" % e)
493       
494        # Check CA certificate
495        log.debug("Reading and checking X.509 CA certificate ...")
496        for caCertFile in attributeAuthority.caCertFilePathList:
497            caCert = X509Cert(caCertFile)
498            caCert.read()
499           
500            try:
501                caCert.isValidTime(raiseExcep=True)
502               
503            except Exception, e:
504                raise AttributeAuthorityError('CA certificate "%s" is '
505                                              'invalid: %s'% (caCert.dn, e))
506       
507        # Issuer details - serialise using the separator string set in the
508        # properties file
509        attributeAuthority.issuer = attributeAuthority.cert.dn.serialise(
510                                    separator=attributeAuthority.dnSeparator)
511
512        attributeAuthority.issuerSerialNumber = \
513                                        attributeAuthority.cert.serialNumber
514       
515        # Load user - user attribute look-up plugin
516        attributeAuthority.initAttributeInterface()
517       
518        attCertFilePath = os.path.join(attributeAuthority.attCertDir, 
519                                       attributeAuthority.attCertFileName)
520               
521        # Rotating file handler used for logging attribute certificates
522        # issued.
523        attributeAuthority.attCertLog = AttCertLog(attCertFilePath,
524                                backUpCnt=attributeAuthority.attCertFileLogCnt)
525   
526        return attributeAuthority
527
528    def setProperties(self, **prop):
529        """Set configuration from an input property dictionary
530        @type prop: dict
531        @param prop: properties dictionary containing configuration items
532        to be set
533        """
534        lenPropPrefix = len(self.propPrefix)
535       
536        # '+ 1' allows for the dot separator
537        lenAttributeInterfacePrefix = len(
538                            AttributeAuthority.ATTRIBUTE_INTERFACE_KEYNAME) + 1
539       
540        for name, val in prop.items():
541            if name.startswith(self.propPrefix):
542                name = name[lenPropPrefix:]
543           
544            if name.startswith(AttributeAuthority.ATTRIBUTE_INTERFACE_KEYNAME):
545                name = name[lenAttributeInterfacePrefix:]
546                self.attributeInterfaceCfg[name] = val
547                continue
548           
549            if name not in AttributeAuthority.propertyDefaults:
550                raise AttributeError('Invalid attribute name "%s"' % name)
551           
552            if isinstance(val, basestring):
553                val = os.path.expandvars(val)
554           
555            if isinstance(AttributeAuthority.propertyDefaults[name], list):
556                val = AttributeAuthority.CONFIG_LIST_SEP_PAT.split(val)
557               
558            # This makes an implicit call to the appropriate property method
559            try:
560                setattr(self, name, val)
561            except AttributeError:
562                raise AttributeError("Can't set attribute \"%s\"" % name)         
563           
564    def readProperties(self):
565        '''Read the properties files and do some checking/converting of input
566        values
567        '''
568        if not os.path.isfile(self.propFilePath):
569            raise IOError('Error parsing properties file "%s": No such file' % 
570                          self.propFilePath)
571           
572        defaultItems = {'here': os.path.dirname(self.propFilePath)}
573       
574        cfg = CaseSensitiveConfigParser(defaults=defaultItems)
575        cfg.read(self.propFilePath)
576       
577        cfgItems = dict([(name, val) 
578                         for name, val in cfg.items(self.propFileSection)
579                         if name != 'here'])
580        self.setProperties(**cfgItems)
581
582    def initAttributeInterface(self):
583        '''Load host sites custom user roles interface to enable the AA to
584        # assign roles in an attribute certificate on a getAttCert request'''
585        classProperties = {}
586        classProperties.update(self.attributeInterfaceCfg)
587       
588        modName = classProperties.pop('modName')
589        className = classProperties.pop('className')     
590        modFilePath = classProperties.pop('modFilePath') 
591                     
592        self.__attributeInterface = instantiateClass(modName,
593                                         className,
594                                         moduleFilePath=modFilePath,
595                                         objectType=AttributeInterface,
596                                         classProperties=classProperties)
597
598    def setPropFilePath(self, val=None):
599        """Set properties file from input or based on environment variable
600        settings
601       
602        @type val: basestring
603        @param val: properties file path"""
604        log.debug("Setting property file path")
605        if not val:
606            if 'NDGSEC_AA_PROPFILEPATH' in os.environ:
607                val = os.environ['NDGSEC_AA_PROPFILEPATH']
608               
609            elif 'NDGSEC_DIR' in os.environ:
610                val = os.path.join(os.environ['NDGSEC_DIR'], 
611                                   AttributeAuthority.DEFAULT_CONFIG_DIRNAME,
612                                   AttributeAuthority.DEFAULT_PROPERTY_FILENAME)
613            else:
614                raise AttributeError('Unable to set default Attribute '
615                                     'Authority properties file path: neither '
616                                     '"NDGSEC_AA_PROPFILEPATH" or "NDGSEC_DIR"'
617                                     ' environment variables are set')
618               
619        if not isinstance(val, basestring):
620            raise AttributeError("Input Properties file path "
621                                 "must be a valid string.")
622     
623        self.__propFilePath = os.path.expandvars(val)
624        log.debug("Path set to: %s" % val)
625       
626    def getPropFilePath(self):
627        '''Get the properties file path
628       
629        @rtype: basestring
630        @return: properties file path'''
631        return self.__propFilePath
632       
633    # Also set up as a property
634    propFilePath = property(fset=setPropFilePath,
635                            fget=getPropFilePath,
636                            doc="path to file containing Attribute Authority "
637                                "configuration parameters.  It defaults to "
638                                "$NDGSEC_AA_PROPFILEPATH or if not set, "
639                                "$NDGSEC_DIR/conf/attributeAuthority.cfg")   
640   
641    def setPropFileSection(self, val=None):
642        """Set section name to read properties from ini file.  This is set from
643        input or based on environment variable setting
644        NDGSEC_AA_PROPFILESECTION
645       
646        @type val: basestring
647        @param val: section name"""
648        log.debug("Setting property file section name")
649        if not val:
650            val = os.environ.get('NDGSEC_AA_PROPFILESECTION', 'DEFAULT')
651               
652        if not isinstance(val, basestring):
653            raise AttributeError("Input Properties file section name "
654                                 "must be a valid string.")
655     
656        self.__propFileSection = val
657        log.debug("Properties file section set to: %s" % val)
658       
659    def getPropFileSection(self):
660        '''Get the section name to extract properties from an ini file -
661        DOES NOT apply to XML file properties
662       
663        @rtype: basestring
664        @return: section name'''
665        return self.__propFileSection
666       
667    # Also set up as a property
668    propFileSection = property(fset=setPropFileSection,
669                               fget=getPropFileSection,
670                               doc="Set the file section name for ini file "
671                                   "properties")   
672   
673    def setPropPrefix(self, val=None):
674        """Set prefix for properties read from ini file.  This is set from
675        input or based on environment variable setting
676        NDGSEC_AA_PROPFILEPREFIX
677       
678        DOES NOT apply to XML file properties
679       
680        @type val: basestring
681        @param val: section name"""
682        log.debug("Setting property file section name")
683        if val is None:
684            val = os.environ.get('NDGSEC_AA_PROPFILEPREFIX', 'DEFAULT')
685               
686        if not isinstance(val, basestring):
687            raise AttributeError("Input Properties file section name "
688                                 "must be a valid string.")
689     
690        self.__propPrefix = val
691        log.debug("Properties file section set to: %s" % val)
692       
693    def getPropPrefix(self):
694        '''Get the prefix name used for properties in an ini file -
695        DOES NOT apply to XML file properties
696       
697        @rtype: basestring
698        @return: section name'''
699        log.debug("Getting property file prefix")
700        return self.__propPrefix
701   
702       
703    # Also set up as a property
704    propPrefix = property(fset=setPropPrefix,
705                          fget=getPropPrefix,
706                          doc="Set a prefix for ini file properties")   
707
708    def getAttCert(self,
709                   userId=None,
710                   holderX509Cert=None,
711                   holderX509CertFilePath=None,
712                   userAttCert=None,
713                   userAttCertFilePath=None):
714
715        """Request a new Attribute Certificate for use in authorisation
716
717        getAttCert([userId=uid][holderX509Cert=x509Cert|
718                    holderX509CertFilePath=x509CertFile, ]
719                   [userAttCert=cert|userAttCertFilePath=certFile])
720         
721        @type userId: string
722        @param userId: identifier for the user who is entitled to the roles
723        in the certificate that is issued.  If this keyword is omitted, then
724        the userId will be set to the DN of the holder.
725       
726        holder = the holder of the certificate - an inidividual user or an
727        organisation to which the user belongs who vouches for that user's ID
728       
729        userId = the identifier for the user who is entitled to the roles
730        specified in the Attribute Certificate that is issued.
731                 
732        @type holderX509Cert: string / ndg.security.common.X509.X509Cert type
733        @param holderX509Cert: base64 encoded string containing proxy cert./
734        X.509 cert object corresponding to the ID who will be the HOLDER of
735        the Attribute Certificate that will be issued.  - Normally, using
736        proxy certificates, the holder and user ID are the same but there
737        may be cases where the holder will be an organisation ID.  This is the
738        case for NDG security with the DEWS project
739       
740        @param holderX509CertFilePath: string
741        @param holderX509CertFilePath: file path to proxy/X.509 certificate of
742        candidate holder
743     
744        @type userAttCert: string or AttCert type
745        @param userAttCert: externally provided attribute certificate from
746        another data centre.  This is only necessary if the user is not
747        registered with this attribute authority.
748                       
749        @type userAttCertFilePath: string
750        @param userAttCertFilePath: alternative to userAttCert except pass
751        in as a file path to an attribute certificate instead.
752       
753        @rtype: AttCert
754        @return: new attribute certificate"""
755
756        log.debug("Calling getAttCert ...")
757       
758        # Read candidate Attribute Certificate holder's X.509 certificate
759        try:
760            if holderX509CertFilePath is not None:
761                                   
762                # Certificate input as a file
763                holderX509Cert = X509Cert()
764                holderX509Cert.read(holderX509CertFilePath)
765               
766            elif isinstance(holderX509Cert, basestring):
767
768                # Certificate input as string text
769                holderX509Cert = X509Cert.Parse(holderX509Cert)
770               
771            elif not isinstance(holderX509Cert, (X509Cert, None.__class__)):
772                raise AttributeAuthorityError("Holder X.509 Certificate must "
773                                              "be set to valid type: a file "
774                                              "path, string, X509 object or "
775                                              "None")           
776        except Exception, e:
777            log.error("Holder X.509 certificate: %s" % e)
778            raise
779
780
781        # Check certificate hasn't expired
782        if holderX509Cert:
783            log.debug("Checking candidate holder X.509 certificate ...")
784            try:
785                holderX509Cert.isValidTime(raiseExcep=True)
786               
787            except Exception, e:
788                log.error("User X.509 certificate is invalid: " + e)
789                raise
790
791           
792        # If no user ID is input, set id from holder X.509 certificate DN
793        # instead
794        if not userId:
795            if not holderX509Cert:
796                raise AttributeAuthorityError("If no user ID is set a holder "
797                                              "X.509 certificate must be "
798                                              "present")
799            try:
800                userId = holderX509Cert.dn.serialise(\
801                                         separator=self.dnSeparator) 
802            except Exception, e:
803                log.error("Setting user Id from holder certificate DN: %s" % e)
804                raise
805       
806        # Make a new Attribute Certificate instance passing in certificate
807        # details for later signing
808        attCert = AttCert()
809
810        # First certificate in list contains the public key corresponding to
811        # the private key
812        attCert.certFilePathList = [self.signingCertFilePath] + \
813                                                                self.caCertFilePathList
814             
815        # Check for expiry of each certificate                   
816        for x509Cert in attCert.certFilePathList:
817            X509Cert.Read(x509Cert).isValidTime(raiseExcep=True)
818                                                               
819        attCert.signingKeyFilePath = self.signingPriKeyFilePath
820        attCert.signingKeyPwd = self.signingPriKeyPwd
821       
822       
823        # Set holder's Distinguished Name if a holder X.509 certificate was
824        # input
825        if holderX509Cert:
826            try:
827                attCert['holder'] = holderX509Cert.dn.serialise(
828                                        separator=self.dnSeparator)           
829            except Exception, e:
830                 log.error("Holder X.509 Certificate DN: %s" % e)
831                 raise
832           
833        # Set Issuer details from Attribute Authority
834        issuerDN = self.cert.dn
835        try:
836            attCert['issuer'] = \
837                    issuerDN.serialise(separator=self.dnSeparator)           
838        except Exception, e:
839            log.error("Issuer X.509 Certificate DN: %s" % e)
840            raise 
841           
842        attCert['issuerName'] = self.name
843        attCert['issuerSerialNumber'] = self.issuerSerialNumber
844
845        attCert['userId'] = userId
846       
847        # Set validity time
848        try:
849            attCert.setValidityTime(
850                        lifetime=self.attCertLifetime,
851                        notBeforeOffset=self.attCertNotBeforeOff)
852
853            # Check against the holder X.509 certificate's expiry if set
854            if holderX509Cert:
855                dtHolderCertNotAfter = holderX509Cert.notAfter
856               
857                if attCert.getValidityNotAfter(asDatetime=True) > \
858                   dtHolderCertNotAfter:
859   
860                    # Adjust the attribute certificate's expiry date time
861                    # so that it agrees with that of the certificate
862                    # ... but also make ensure that the not before skew is
863                    # still applied
864                    attCert.setValidityTime(dtNotAfter=dtHolderCertNotAfter,
865                            notBeforeOffset=self.attCertNotBeforeOff)
866           
867        except Exception, e:
868            log.error("Error setting attribute certificate validity time: %s" %
869                      e)
870            raise 
871
872        # Check name is registered with this Attribute Authority - if no
873        # user roles are found, the user is not registered
874        userRoles = self.getRoles(userId)
875        if userRoles:
876            # Set as an Original Certificate
877            #
878            # User roles found - user is registered with this data centre
879            # Add roles for this user for this data centre
880            attCert.addRoles(userRoles)
881
882            # Mark new Attribute Certificate as an original
883            attCert['provenance'] = AttCert.origProvenance
884
885        else:           
886            # Set as a Mapped Certificate
887            #
888            # No roles found - user is not registered with this data centre
889            # Check for an externally provided certificate from another
890            # trusted data centre
891            if userAttCertFilePath:
892               
893                # Read externally provided certificate
894                try:
895                    userAttCert = AttCertRead(userAttCertFilePath)
896                   
897                except Exception, e:
898                    raise AttributeAuthorityError("Reading external Attribute "
899                                                  "Certificate: %s" % e)                           
900            elif userAttCert:
901                # Allow input as a string but convert to
902                if isinstance(userAttCert, basestring):
903                    userAttCert = AttCertParse(userAttCert)
904                   
905                elif not isinstance(userAttCert, AttCert):
906                    raise AttributeAuthorityError(
907                        "Expecting userAttCert as a string or AttCert type")       
908            else:
909                raise AttributeAuthorityAccessDenied('User "%s" is not '
910                    'registered and no external attribute certificate is '
911                    'available to make a mapping.' % userId)
912
913
914            # Check it's an original certificate - mapped certificates can't
915            # be used to make further mappings
916            if userAttCert.isMapped():
917                raise AttributeAuthorityError("External Attribute Certificate "
918                                              "must have an original "
919                                              "provenance in order "
920                                              "to make further mappings.")
921
922
923            # Check it's valid and signed
924            try:
925                # Give path to CA cert to allow check
926                userAttCert.certFilePathList = self.caCertFilePathList
927                userAttCert.isValid(raiseExcep=True)
928               
929            except Exception, e:
930                raise AttributeAuthorityError("Invalid Remote Attribute "
931                                        "Certificate: " + str(e))       
932
933
934            # Check that's it's holder matches the candidate holder
935            # certificate DN
936            if holderX509Cert and userAttCert.holderDN != holderX509Cert.dn:
937                raise AttributeAuthorityError("User certificate and Attribute "
938                                        'Certificate DNs don\'t match: "%s"'
939                                        ' and "%s"' % (holderX509Cert.dn, 
940                                                       userAttCert.holderDN))
941           
942 
943            # Get roles from external Attribute Certificate
944            trustedHostRoles = userAttCert.roles
945
946
947            # Map external roles to local ones
948            localRoles = self.mapRemoteRoles2LocalRoles(
949                                                    userAttCert['issuerName'],
950                                                    trustedHostRoles)
951            if not localRoles:
952                raise AttributeAuthorityAccessDenied("No local roles mapped "
953                                               "to the %s roles: %s" % 
954                                               (userAttCert['issuerName'], 
955                                                ', '.join(trustedHostRoles)))
956
957            attCert.addRoles(localRoles)
958           
959           
960            # Mark new Attribute Certificate as mapped
961            attCert.provenance = AttCert.mappedProvenance
962
963            # Copy the user Id from the external AC
964            attCert.userId = userAttCert.userId
965           
966            # End set mapped certificate block
967
968        try:
969            # Digitally sign certificate using Attribute Authority's
970            # certificate and private key
971            attCert.applyEnvelopedSignature()
972           
973            # Check the certificate is valid
974            attCert.isValid(raiseExcep=True)
975           
976            # Write out certificate to keep a record of it for auditing
977            #attCert.write()
978            self.__attCertLog.info(attCert)
979           
980            log.info('Issued an Attribute Certificate to "%s" with roles: '
981                     '"%s"' % (userId, '", "'.join(attCert.roles)))
982
983            # Return the cert to caller
984            return attCert
985       
986        except Exception, e:
987            raise AttributeAuthorityError('New Attribute Certificate "%s": %s'%
988                                          (attCert.filePath, e))
989           
990    def _getMapConfigFilePath(self):
991        return self.__mapConfigFilePath
992   
993    def _setMapConfigFilePath(self, val):
994        if not isinstance(val, basestring):
995            raise AttributeAuthorityConfigError("Input Map Configuration "
996                                                "file path must be a "
997                                                "valid string.")
998        self.__mapConfigFilePath = val
999         
1000    mapConfigFilePath = property(fget=_getMapConfigFilePath,
1001                                 fset=_setMapConfigFilePath,
1002                                 doc="File path for Role Mapping configuration") 
1003       
1004    def readMapConfig(self):
1005        """Parse Map Configuration file.
1006        """
1007       
1008        log.debug("Reading map configuration file ...")
1009       
1010        try:
1011            tree = ElementTree.parse(self.mapConfigFilePath)
1012            rootElem = tree.getroot()
1013           
1014        except IOError, e:
1015            raise AttributeAuthorityConfigError('Error parsing properties '
1016                                                'file "%s": %s' % 
1017                                                (e.filename,e.strerror))         
1018        except Exception, e:
1019            raise AttributeAuthorityConfigError('Error parsing Map '
1020                                                'Configuration file: "%s": %s'% 
1021                                                (self.mapConfigFilePath, 
1022                                                 e))
1023
1024           
1025        trustedElem = rootElem.findall('trusted')
1026        if not trustedElem: 
1027            # Make an empty list so that for loop block below is skipped
1028            # without an error 
1029            trustedElem = ()
1030
1031        # Dictionaries:
1032        # 1) to hold all the data
1033        self.__mapConfig = {'thisHost': {}, 'trustedHosts': {}}
1034
1035        # ... look-up
1036        # 2) hosts corresponding to a given role and
1037        # 3) roles of external data centre to this data centre
1038        self._localRole2TrustedHost = {}
1039        self.__localRole2RemoteRole = {}
1040        self.__remoteRole2LocalRole = {}
1041
1042
1043        # Information about this host
1044        try:
1045            thisHostElem = rootElem.findall('thisHost')[0]
1046           
1047        except Exception, e:
1048            raise AttributeAuthorityConfigError('"thisHost" tag not found in '
1049                                                'Map Configuration file "%s"' % 
1050                                                self.mapConfigFilePath)
1051
1052        try:
1053            hostName = thisHostElem.attrib.values()[0]
1054           
1055        except Exception, e:
1056            raise AttributeAuthorityConfigError('"name" attribute of '
1057                                                '"thisHost" element not found '
1058                                                'in Map Configuration file '
1059                                                '"%s"' % 
1060                                                self.mapConfigFilePath)
1061
1062
1063        # hostname is also stored in the AA's config file in the 'name' tag. 
1064        # Check the two match as the latter is copied into Attribute
1065        # Certificates issued by this AA
1066        #
1067        # TODO: would be better to rationalise this so that the hostname is
1068        # stored in one place only.
1069        #
1070        # P J Kershaw 14/06/06
1071        if hostName != self.name:
1072            raise AttributeAuthorityError('"name" attribute of "thisHost" '
1073                                          'element in Map Configuration file '
1074                                          'doesn\'t match "name" element in '
1075                                          'properties file.')
1076       
1077        # Information for THIS Attribute Authority
1078        self.__mapConfig['thisHost'][hostName] = {}
1079
1080        for k, v in AttributeAuthority.mapConfigHostDefaults.items():
1081            val = thisHostElem.findtext(k)
1082            if val is None and v == NotImplemented:
1083                raise AttributeAuthorityConfigError('<thisHost> option <%s> '
1084                                                    'must be set.' % k)
1085            self.__mapConfig['thisHost'][hostName][k] = val
1086               
1087       
1088        # Information about trusted hosts
1089        for elem in trustedElem:
1090            try:
1091                trustedHost = elem.attrib.values()[0]
1092               
1093            except Exception, e:
1094                raise AttributeAuthorityConfigError('Error reading trusted '
1095                                                    'host name: %s' % e)
1096
1097           
1098            # Add signatureFile and list of roles
1099            #
1100            # (Currently Optional) additional tag allows query of the URI
1101            # where a user would normally login at the trusted host.  Added
1102            # this feature to allow users to be forwarded to their home site
1103            # if they are accessing a secure resource and are not
1104            # authenticated
1105            #
1106            # P J Kershaw 25/05/06
1107            self.__mapConfig['trustedHosts'][trustedHost] = {}
1108            for k, v in AttributeAuthority.mapConfigHostDefaults.items():
1109                val = thisHostElem.findtext(k)
1110                if val is None and v == NotImplemented:
1111                    raise AttributeAuthorityConfigError('<trustedHost> option '
1112                                                        '<%s> must be set.'%k)
1113                   
1114                self.__mapConfig['trustedHosts'][trustedHost][k] = \
1115                                                        elem.findtext(k)   
1116
1117            roleElem = elem.findall('role')
1118            if roleElem:
1119                # Role keyword value requires special parsing before
1120                # assignment
1121                self.__mapConfig['trustedHosts'][trustedHost]['role'] = \
1122                                        [dict(i.items()) for i in roleElem]
1123            else:
1124                # It's possible for trust relationships to not contain any
1125                # role mapping.  e.g. a site's login service trusting other
1126                # sites login requests
1127                self.__mapConfig['trustedHosts'][trustedHost]['role'] = []
1128                       
1129            self.__localRole2RemoteRole[trustedHost] = {}
1130            self.__remoteRole2LocalRole[trustedHost] = {}
1131           
1132            for role in self.__mapConfig['trustedHosts'][trustedHost]['role']:
1133                try:
1134                    localRole = role['local']
1135                    remoteRole = role['remote']
1136                except KeyError, e:
1137                    raise AttributeAuthorityError('Reading map configuration '
1138                                                  ' file "%s": no element '
1139                                                  '"%s" for host "%s"' % 
1140                                                (self.mapConfigFilePath, 
1141                                                 e, 
1142                                                 trustedHost))
1143                   
1144                # Role to host look-up
1145                if localRole in self._localRole2TrustedHost:
1146                   
1147                    if trustedHost not in \
1148                       self._localRole2TrustedHost[localRole]:
1149                        self._localRole2TrustedHost[localRole].\
1150                                                        append(trustedHost)                       
1151                else:
1152                    self._localRole2TrustedHost[localRole] = [trustedHost]
1153
1154
1155                # Trusted Host to local role and trusted host to trusted role
1156                # map look-ups
1157                try:
1158                    self.__remoteRole2LocalRole[trustedHost][remoteRole].\
1159                                                            append(localRole)                 
1160                except KeyError:
1161                    self.__remoteRole2LocalRole[trustedHost][remoteRole] = \
1162                                                                [localRole]
1163                   
1164                try:
1165                    self.__localRole2RemoteRole[trustedHost][localRole].\
1166                                                            append(remoteRole)                 
1167                except KeyError:
1168                    self.__localRole2RemoteRole[trustedHost][localRole] = \
1169                                                                [remoteRole]
1170       
1171        # Store trusted host info look-up for retrieval by getTrustedHostInfo
1172        # method                                                                         
1173        #
1174        # Nb. {}.fromkeys([...]).keys() is a fudge to get unique elements
1175        # from a list i.e. convert the list elements to a dict eliminating
1176        # duplicated elements and convert the keys back into a list.
1177        self._trustedHostInfo = dict(
1178        [
1179            (
1180                k, 
1181                {
1182                    'siteName':             v['siteName'],
1183                    'aaURI':                v['aaURI'], 
1184                    'aaDN':                 v['aaDN'], 
1185                    'loginURI':             v['loginURI'], 
1186                    'loginServerDN':        v['loginServerDN'], 
1187                    'loginRequestServerDN': v['loginRequestServerDN'], 
1188                    'role':                 {}.fromkeys([role['remote'] 
1189                                                         for role in v['role']]
1190                                                       ).keys()
1191                }
1192            ) for k, v in self.__mapConfig['trustedHosts'].items()
1193        ])
1194
1195        log.info('Loaded map configuration file "%s"' % self.mapConfigFilePath)
1196       
1197       
1198    def getRoles(self, userId):
1199        """Get the roles available to the registered user identified userId.
1200
1201        @type dn: string
1202        @param dn: user identifier - could be a X500 Distinguished Name
1203        @return: list of roles for the given user ID"""
1204
1205        log.debug('Calling getRoles for user "%s" ...' % userId)
1206       
1207        # Call to AttributeInterface derived class.  Each Attribute Authority
1208        # should define it's own roles class derived from AttributeInterface to
1209        # define how roles are accessed
1210        try:
1211            return self.__attributeInterface.getRoles(userId)
1212
1213        except Exception, e:
1214            raise AttributeAuthorityError("Getting user roles: %s" % e)
1215       
1216       
1217    def _getHostInfo(self):
1218        """Return the host that this Attribute Authority represents: its ID,
1219        the user login URI and WSDL address.  Call this method via the
1220        'hostInfo' property
1221       
1222        @rtype: dict
1223        @return: dictionary of host information derived from the map
1224        configuration"""
1225       
1226        return self.__mapConfig['thisHost']
1227       
1228    hostInfo = property(fget=_getHostInfo, 
1229                        doc="Return information about this host")
1230       
1231       
1232    def getTrustedHostInfo(self, role=None):
1233        """Return a dictionary of the hosts that have trust relationships
1234        with this AA.  The dictionary is indexed by the trusted host name
1235        and contains AA service, login URIs and the roles that map to the
1236        given input local role.
1237
1238        @type role: string
1239        @param role: if set, return trusted hosts that having a mapping set
1240        for this role.  If no role is input, return all the AA's trusted hosts
1241        with all their possible roles
1242
1243        @rtype: dict
1244        @return: dictionary of the hosts that have trust relationships
1245        with this AA.  It returns an empty dictionary if role isn't
1246        recognised"""
1247               
1248        log.debug('Calling getTrustedHostInfo with role = "%s" ...' % role) 
1249                                 
1250        if not self.__mapConfig or not self.__localRole2RemoteRole:
1251            # This Attribute Authority has no trusted hosts
1252            raise AttributeAuthorityNoTrustedHosts("The %s Attribute "
1253                                                   "Authority has no trusted "
1254                                                   "hosts" % 
1255                                                   self.name)
1256
1257
1258        if role is None:
1259            # No role input - return all trusted hosts with their service URIs
1260            # and the remote roles they map to
1261            return self._trustedHostInfo
1262
1263        else:           
1264            # Get trusted hosts for given input local role       
1265            try:
1266                trustedHosts = self._localRole2TrustedHost[role]
1267            except:
1268                raise AttributeAuthorityNoMatchingRoleInTrustedHosts(
1269                    'None of the trusted hosts have a mapping to the '
1270                    'input role "%s"' % role)
1271   
1272   
1273            # Get associated Web service URI and roles for the trusted hosts
1274            # identified and return as a dictionary indexed by host name
1275            trustedHostInfo = dict(
1276       [(
1277            host, 
1278            {
1279                'siteName': self.__mapConfig['trustedHosts'][host]['siteName'],
1280                'aaURI':    self.__mapConfig['trustedHosts'][host]['aaURI'],
1281                'aaDN':     self.__mapConfig['trustedHosts'][host]['aaDN'],
1282                'loginURI': self.__mapConfig['trustedHosts'][host]['loginURI'],
1283                'loginServerDN': 
1284                        self.__mapConfig['trustedHosts'][host]['loginServerDN'],
1285                'loginRequestServerDN': 
1286                self.__mapConfig['trustedHosts'][host]['loginRequestServerDN'],
1287                'role':     self.__localRole2RemoteRole[host][role]
1288            }
1289        ) for host in trustedHosts])
1290                         
1291            return trustedHostInfo
1292       
1293       
1294    def mapRemoteRoles2LocalRoles(self, trustedHost, trustedHostRoles):
1295        """Map roles of trusted hosts to roles for this data centre
1296
1297        @type trustedHost: string
1298        @param trustedHost: name of external trusted data centre
1299        @type trustedHostRoles: list
1300        @param trustedHostRoles:   list of external roles to map
1301        @return: list of mapped roles"""
1302
1303        if not self.__remoteRole2LocalRole:
1304            raise AttributeAuthorityError("Roles map is not set - ensure " 
1305                                    "readMapConfig() has been called.")
1306
1307
1308        # Check the host name is a trusted one recorded in the map
1309        # configuration
1310        if not self.__remoteRole2LocalRole.has_key(trustedHost):
1311            return []
1312
1313        # Add local roles, skipping if no mapping is found
1314        localRoles = []
1315        for trustedRole in trustedHostRoles:
1316            if trustedRole in self.__remoteRole2LocalRole[trustedHost]:
1317                localRoles.extend(
1318                        self.__remoteRole2LocalRole[trustedHost][trustedRole])
1319               
1320        return localRoles
1321
1322
1323from logging.handlers import RotatingFileHandler
1324
1325# Inherit directly from Logger
1326_loggerClass = logging.getLoggerClass()
1327class AttCertLog(_loggerClass, object):
1328    """Log each Attribute Certificate issued using a rotating file handler
1329    so that the number of files held can be managed"""
1330   
1331    def __init__(self, attCertFilePath, backUpCnt=1024):
1332        """Set up a rotating file handler to log ACs issued.
1333        @type attCertFilePath: string
1334        @param attCertFilePath: set where to store ACs.  Set from
1335        AttributeAuthority properties file.
1336       
1337        @type backUpCnt: int
1338        @param backUpCnt: set the number of files to store before rotating
1339        and overwriting old files."""
1340       
1341        if not isinstance(backUpCnt, int):
1342            raise TypeError('Expecting int type for "backUpCnt" keyword')
1343       
1344        # Inherit from Logger class
1345        super(AttCertLog, self).__init__(name='', level=logging.INFO)
1346                           
1347        # Set a format for messages so that only the content of the AC is
1348        # logged, nothing else.
1349        formatter = logging.Formatter(fmt="", datefmt="")
1350
1351        # maxBytes is set to one so that only one AC will be written before
1352        # rotation to the next file
1353        fileLog = RotatingFileHandler(attCertFilePath, 
1354                                      maxBytes=1, 
1355                                      backupCount=backUpCnt)
1356        fileLog.setFormatter(formatter)           
1357        self.addHandler(fileLog)
1358                       
1359class AttributeInterfaceError(Exception):
1360    """Exception handling for NDG Attribute Authority User Roles interface
1361    class."""
1362
1363
1364class AttributeInterface(object):
1365    """An abstract base class to define the user roles interface to an
1366    Attribute Authority.
1367
1368    Each NDG data centre should implement a derived class which implements
1369    the way user roles are provided to its representative Attribute Authority.
1370   
1371    Roles are expected to indexed by user Distinguished Name (DN).  They
1372    could be stored in a database or file."""
1373
1374    # User defined class may wish to specify a URI for a database interface or
1375    # path for a user roles configuration file
1376    def __init__(self, **prop):
1377        """User Roles base class - derive from this class to define
1378        roles interface to Attribute Authority
1379       
1380        @type prop: dict
1381        @param prop: custom properties to pass to this class
1382        """
1383
1384
1385    def getRoles(self, userId):
1386        """Virtual method - Derived method should return the roles for the
1387        given user's Id or else raise an exception
1388       
1389        @type userId: string
1390        @param userId: user identity e.g. user Distinguished Name
1391        @rtype: list
1392        @return: list of roles for the given user ID"""
1393        raise NotImplementedError(
1394            self.getRoles.__doc__.replace('\n       ',''))
1395                         
Note: See TracBrowser for help on using the repository browser.