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

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

Added ndg.security.server.wsgi.attributeauthority.AttributeAuthorityMiddleware?: sets an ndg.security.server.attributeauthority.AttributeAuthority? instance as a key in environ.

  • 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, propFileSection='DEFAULT',
453                         propPrefix='attributeauthority.', 
454                         bReadMapConfig=True):
455        """Create new NDG Attribute Authority instance from the property file
456        settings
457
458        @type propFilePath: string
459        @param propFilePath: path to file containing Attribute Authority
460        configuration parameters.  It defaults to $NDGSEC_AA_PROPFILEPATH or
461        if not set, $NDGSEC_DIR/conf/attributeAuthority.cfg
462        @type propFileSection: basestring
463        @param propFileSection: section of properties file to read from.
464        properties files
465        @type propPrefix: basestring
466        @param propPrefix: set a prefix for filtering attribute authority
467        property names - useful where properties are being parsed from a file
468        section containing parameter names for more than one application
469        @type bReadMapConfig: boolean
470        @param bReadMapConfig: by default the Map Configuration file is
471        read.  Set this flag to False to override.
472        """
473           
474        attributeAuthority = AttributeAuthority()
475        if propFileSection:
476            attributeAuthority.propFileSection = propFileSection
477           
478        if propPrefix:
479            attributeAuthority.propPrefix = propPrefix
480
481        attributeAuthority.propFilePath = propFilePath           
482        attributeAuthority.readProperties()
483        attributeAuthority.initialise(bReadMapConfig=bReadMapConfig)
484   
485        return attributeAuthority
486
487       
488    @classmethod
489    def fromProperties(cls, propPrefix='attributeauthority.', 
490                       bReadMapConfig=True, **prop):
491        """Create new NDG Attribute Authority instance from input property
492        keywords
493
494        @type propPrefix: basestring
495        @param propPrefix: set a prefix for filtering attribute authority
496        property names - useful where properties are being parsed from a file
497        section containing parameter names for more than one application
498        @type bReadMapConfig: boolean
499        @param bReadMapConfig: by default the Map Configuration file is
500        read.  Set this flag to False to override.
501        """
502        attributeAuthority = AttributeAuthority()
503        if propPrefix:
504            attributeAuthority.propPrefix = propPrefix
505               
506        attributeAuthority.setProperties(**prop)
507        attributeAuthority.initialise(bReadMapConfig=bReadMapConfig)
508       
509        return attributeAuthority
510   
511    def initialise(self, bReadMapConfig=True):
512        """Convenience method for set up of Attribute Interface, map
513        configuration and PKI"""
514       
515        # Read the Map Configuration file
516        if bReadMapConfig:
517            self.readMapConfig()
518
519        # Instantiate Certificate object
520        log.debug("Reading and checking Attribute Authority X.509 cert. ...")
521        self.cert = X509Cert.Read(self.signingCertFilePath)
522
523        # Check it's valid
524        try:
525            self.cert.isValidTime(raiseExcep=True)
526           
527        except Exception, e:
528            raise AttributeAuthorityError("Attribute Authority's certificate "
529                                          "is invalid: %s" % e)
530       
531        # Check CA certificate
532        log.debug("Reading and checking X.509 CA certificate ...")
533        for caCertFile in self.caCertFilePathList:
534            caCert = X509Cert(caCertFile)
535            caCert.read()
536           
537            try:
538                caCert.isValidTime(raiseExcep=True)
539               
540            except Exception, e:
541                raise AttributeAuthorityError('CA certificate "%s" is '
542                                              'invalid: %s'% (caCert.dn, e))
543       
544        # Issuer details - serialise using the separator string set in the
545        # properties file
546        self.issuer = self.cert.dn.serialise(separator=self.dnSeparator)
547
548        self.issuerSerialNumber = self.cert.serialNumber
549       
550        # Load user - user attribute look-up plugin
551        self.initAttributeInterface()
552       
553        attCertFilePath = os.path.join(self.attCertDir, self.attCertFileName)
554               
555        # Rotating file handler used for logging attribute certificates
556        # issued.
557        self.attCertLog = AttCertLog(attCertFilePath,
558                                     backUpCnt=self.attCertFileLogCnt)
559
560    def setProperties(self, **prop):
561        """Set configuration from an input property dictionary
562        @type prop: dict
563        @param prop: properties dictionary containing configuration items
564        to be set
565        """
566        lenPropPrefix = len(self.propPrefix)
567       
568        # '+ 1' allows for the dot separator
569        lenAttributeInterfacePrefix = len(
570                            AttributeAuthority.ATTRIBUTE_INTERFACE_KEYNAME) + 1
571       
572        for name, val in prop.items():
573            if name.startswith(self.propPrefix):
574                name = name[lenPropPrefix:]
575           
576            if name.startswith(AttributeAuthority.ATTRIBUTE_INTERFACE_KEYNAME):
577                name = name[lenAttributeInterfacePrefix:]
578                self.attributeInterfaceCfg[name] = val
579                continue
580           
581            if name not in AttributeAuthority.propertyDefaults:
582                raise AttributeError('Invalid attribute name "%s"' % name)
583           
584            if isinstance(val, basestring):
585                val = os.path.expandvars(val)
586           
587            if isinstance(AttributeAuthority.propertyDefaults[name], list):
588                val = AttributeAuthority.CONFIG_LIST_SEP_PAT.split(val)
589               
590            # This makes an implicit call to the appropriate property method
591            try:
592                setattr(self, name, val)
593            except AttributeError:
594                raise AttributeError("Can't set attribute \"%s\"" % name)         
595           
596    def readProperties(self):
597        '''Read the properties files and do some checking/converting of input
598        values
599        '''
600        if not os.path.isfile(self.propFilePath):
601            raise IOError('Error parsing properties file "%s": No such file' % 
602                          self.propFilePath)
603           
604        defaultItems = {'here': os.path.dirname(self.propFilePath)}
605       
606        cfg = CaseSensitiveConfigParser(defaults=defaultItems)
607        cfg.read(self.propFilePath)
608       
609        cfgItems = dict([(name, val) 
610                         for name, val in cfg.items(self.propFileSection)
611                         if name != 'here'])
612        self.setProperties(**cfgItems)
613
614    def initAttributeInterface(self):
615        '''Load host sites custom user roles interface to enable the AA to
616        # assign roles in an attribute certificate on a getAttCert request'''
617        classProperties = {}
618        classProperties.update(self.attributeInterfaceCfg)
619       
620        modName = classProperties.pop('modName')
621        className = classProperties.pop('className') 
622       
623        # file path may be omitted   
624        modFilePath = classProperties.pop('modFilePath', None) 
625                     
626        self.__attributeInterface = instantiateClass(modName,
627                                             className,
628                                             moduleFilePath=modFilePath,
629                                             objectType=AttributeInterface,
630                                             classProperties=classProperties)
631
632    def setPropFilePath(self, val=None):
633        """Set properties file from input or based on environment variable
634        settings
635       
636        @type val: basestring
637        @param val: properties file path"""
638        log.debug("Setting property file path")
639        if not val:
640            if 'NDGSEC_AA_PROPFILEPATH' in os.environ:
641                val = os.environ['NDGSEC_AA_PROPFILEPATH']
642               
643            elif 'NDGSEC_DIR' in os.environ:
644                val = os.path.join(os.environ['NDGSEC_DIR'], 
645                                   AttributeAuthority.DEFAULT_CONFIG_DIRNAME,
646                                   AttributeAuthority.DEFAULT_PROPERTY_FILENAME)
647            else:
648                raise AttributeError('Unable to set default Attribute '
649                                     'Authority properties file path: neither '
650                                     '"NDGSEC_AA_PROPFILEPATH" or "NDGSEC_DIR"'
651                                     ' environment variables are set')
652               
653        if not isinstance(val, basestring):
654            raise AttributeError("Input Properties file path "
655                                 "must be a valid string.")
656     
657        self.__propFilePath = os.path.expandvars(val)
658        log.debug("Path set to: %s" % val)
659       
660    def getPropFilePath(self):
661        '''Get the properties file path
662       
663        @rtype: basestring
664        @return: properties file path'''
665        return self.__propFilePath
666       
667    # Also set up as a property
668    propFilePath = property(fset=setPropFilePath,
669                            fget=getPropFilePath,
670                            doc="path to file containing Attribute Authority "
671                                "configuration parameters.  It defaults to "
672                                "$NDGSEC_AA_PROPFILEPATH or if not set, "
673                                "$NDGSEC_DIR/conf/attributeAuthority.cfg")   
674   
675    def setPropFileSection(self, val=None):
676        """Set section name to read properties from ini file.  This is set from
677        input or based on environment variable setting
678        NDGSEC_AA_PROPFILESECTION
679       
680        @type val: basestring
681        @param val: section name"""
682        log.debug("Setting property file section name")
683        if not val:
684            val = os.environ.get('NDGSEC_AA_PROPFILESECTION', '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.__propFileSection = val
691        log.debug("Properties file section set to: %s" % val)
692       
693    def getPropFileSection(self):
694        '''Get the section name to extract properties from an ini file -
695        DOES NOT apply to XML file properties
696       
697        @rtype: basestring
698        @return: section name'''
699        return self.__propFileSection
700       
701    # Also set up as a property
702    propFileSection = property(fset=setPropFileSection,
703                               fget=getPropFileSection,
704                               doc="Set the file section name for ini file "
705                                   "properties")   
706   
707    def setPropPrefix(self, val=None):
708        """Set prefix for properties read from ini file.  This is set from
709        input or based on environment variable setting
710        NDGSEC_AA_PROPFILEPREFIX
711       
712        DOES NOT apply to XML file properties
713       
714        @type val: basestring
715        @param val: section name"""
716        log.debug("Setting property file section name")
717        if val is None:
718            val = os.environ.get('NDGSEC_AA_PROPFILEPREFIX', 'DEFAULT')
719               
720        if not isinstance(val, basestring):
721            raise AttributeError("Input Properties file section name "
722                                 "must be a valid string.")
723     
724        self.__propPrefix = val
725        log.debug("Properties file section set to: %s" % val)
726       
727    def getPropPrefix(self):
728        '''Get the prefix name used for properties in an ini file -
729        DOES NOT apply to XML file properties
730       
731        @rtype: basestring
732        @return: section name'''
733        log.debug("Getting property file prefix")
734        return self.__propPrefix
735   
736       
737    # Also set up as a property
738    propPrefix = property(fset=setPropPrefix,
739                          fget=getPropPrefix,
740                          doc="Set a prefix for ini file properties")   
741
742    def getAttCert(self,
743                   userId=None,
744                   holderX509Cert=None,
745                   holderX509CertFilePath=None,
746                   userAttCert=None,
747                   userAttCertFilePath=None):
748
749        """Request a new Attribute Certificate for use in authorisation
750
751        getAttCert([userId=uid][holderX509Cert=x509Cert|
752                    holderX509CertFilePath=x509CertFile, ]
753                   [userAttCert=cert|userAttCertFilePath=certFile])
754         
755        @type userId: string
756        @param userId: identifier for the user who is entitled to the roles
757        in the certificate that is issued.  If this keyword is omitted, then
758        the userId will be set to the DN of the holder.
759       
760        holder = the holder of the certificate - an inidividual user or an
761        organisation to which the user belongs who vouches for that user's ID
762       
763        userId = the identifier for the user who is entitled to the roles
764        specified in the Attribute Certificate that is issued.
765                 
766        @type holderX509Cert: string / ndg.security.common.X509.X509Cert type
767        @param holderX509Cert: base64 encoded string containing proxy cert./
768        X.509 cert object corresponding to the ID who will be the HOLDER of
769        the Attribute Certificate that will be issued.  - Normally, using
770        proxy certificates, the holder and user ID are the same but there
771        may be cases where the holder will be an organisation ID.  This is the
772        case for NDG security with the DEWS project
773       
774        @param holderX509CertFilePath: string
775        @param holderX509CertFilePath: file path to proxy/X.509 certificate of
776        candidate holder
777     
778        @type userAttCert: string or AttCert type
779        @param userAttCert: externally provided attribute certificate from
780        another data centre.  This is only necessary if the user is not
781        registered with this attribute authority.
782                       
783        @type userAttCertFilePath: string
784        @param userAttCertFilePath: alternative to userAttCert except pass
785        in as a file path to an attribute certificate instead.
786       
787        @rtype: AttCert
788        @return: new attribute certificate"""
789
790        log.debug("Calling getAttCert ...")
791       
792        # Read candidate Attribute Certificate holder's X.509 certificate
793        try:
794            if holderX509CertFilePath is not None:
795                                   
796                # Certificate input as a file
797                holderX509Cert = X509Cert()
798                holderX509Cert.read(holderX509CertFilePath)
799               
800            elif isinstance(holderX509Cert, basestring):
801
802                # Certificate input as string text
803                holderX509Cert = X509Cert.Parse(holderX509Cert)
804               
805            elif not isinstance(holderX509Cert, (X509Cert, None.__class__)):
806                raise AttributeAuthorityError("Holder X.509 Certificate must "
807                                              "be set to valid type: a file "
808                                              "path, string, X509 object or "
809                                              "None")           
810        except Exception, e:
811            log.error("Holder X.509 certificate: %s" % e)
812            raise
813
814
815        # Check certificate hasn't expired
816        if holderX509Cert:
817            log.debug("Checking candidate holder X.509 certificate ...")
818            try:
819                holderX509Cert.isValidTime(raiseExcep=True)
820               
821            except Exception, e:
822                log.error("User X.509 certificate is invalid: " + e)
823                raise
824
825           
826        # If no user ID is input, set id from holder X.509 certificate DN
827        # instead
828        if not userId:
829            if not holderX509Cert:
830                raise AttributeAuthorityError("If no user ID is set a holder "
831                                              "X.509 certificate must be "
832                                              "present")
833            try:
834                userId = holderX509Cert.dn.serialise(\
835                                         separator=self.dnSeparator) 
836            except Exception, e:
837                log.error("Setting user Id from holder certificate DN: %s" % e)
838                raise
839       
840        # Make a new Attribute Certificate instance passing in certificate
841        # details for later signing
842        attCert = AttCert()
843
844        # First certificate in list contains the public key corresponding to
845        # the private key
846        attCert.certFilePathList = [self.signingCertFilePath] + \
847                                                                self.caCertFilePathList
848             
849        # Check for expiry of each certificate                   
850        for x509Cert in attCert.certFilePathList:
851            X509Cert.Read(x509Cert).isValidTime(raiseExcep=True)
852                                                               
853        attCert.signingKeyFilePath = self.signingPriKeyFilePath
854        attCert.signingKeyPwd = self.signingPriKeyPwd
855       
856       
857        # Set holder's Distinguished Name if a holder X.509 certificate was
858        # input
859        if holderX509Cert:
860            try:
861                attCert['holder'] = holderX509Cert.dn.serialise(
862                                        separator=self.dnSeparator)           
863            except Exception, e:
864                 log.error("Holder X.509 Certificate DN: %s" % e)
865                 raise
866           
867        # Set Issuer details from Attribute Authority
868        issuerDN = self.cert.dn
869        try:
870            attCert['issuer'] = \
871                    issuerDN.serialise(separator=self.dnSeparator)           
872        except Exception, e:
873            log.error("Issuer X.509 Certificate DN: %s" % e)
874            raise 
875           
876        attCert['issuerName'] = self.name
877        attCert['issuerSerialNumber'] = self.issuerSerialNumber
878
879        attCert['userId'] = userId
880       
881        # Set validity time
882        try:
883            attCert.setValidityTime(
884                        lifetime=self.attCertLifetime,
885                        notBeforeOffset=self.attCertNotBeforeOff)
886
887            # Check against the holder X.509 certificate's expiry if set
888            if holderX509Cert:
889                dtHolderCertNotAfter = holderX509Cert.notAfter
890               
891                if attCert.getValidityNotAfter(asDatetime=True) > \
892                   dtHolderCertNotAfter:
893   
894                    # Adjust the attribute certificate's expiry date time
895                    # so that it agrees with that of the certificate
896                    # ... but also make ensure that the not before skew is
897                    # still applied
898                    attCert.setValidityTime(dtNotAfter=dtHolderCertNotAfter,
899                            notBeforeOffset=self.attCertNotBeforeOff)
900           
901        except Exception, e:
902            log.error("Error setting attribute certificate validity time: %s" %
903                      e)
904            raise 
905
906        # Check name is registered with this Attribute Authority - if no
907        # user roles are found, the user is not registered
908        userRoles = self.getRoles(userId)
909        if userRoles:
910            # Set as an Original Certificate
911            #
912            # User roles found - user is registered with this data centre
913            # Add roles for this user for this data centre
914            attCert.addRoles(userRoles)
915
916            # Mark new Attribute Certificate as an original
917            attCert['provenance'] = AttCert.origProvenance
918
919        else:           
920            # Set as a Mapped Certificate
921            #
922            # No roles found - user is not registered with this data centre
923            # Check for an externally provided certificate from another
924            # trusted data centre
925            if userAttCertFilePath:
926               
927                # Read externally provided certificate
928                try:
929                    userAttCert = AttCertRead(userAttCertFilePath)
930                   
931                except Exception, e:
932                    raise AttributeAuthorityError("Reading external Attribute "
933                                                  "Certificate: %s" % e)                           
934            elif userAttCert:
935                # Allow input as a string but convert to
936                if isinstance(userAttCert, basestring):
937                    userAttCert = AttCertParse(userAttCert)
938                   
939                elif not isinstance(userAttCert, AttCert):
940                    raise AttributeAuthorityError(
941                        "Expecting userAttCert as a string or AttCert type")       
942            else:
943                raise AttributeAuthorityAccessDenied('User "%s" is not '
944                    'registered and no external attribute certificate is '
945                    'available to make a mapping.' % userId)
946
947
948            # Check it's an original certificate - mapped certificates can't
949            # be used to make further mappings
950            if userAttCert.isMapped():
951                raise AttributeAuthorityError("External Attribute Certificate "
952                                              "must have an original "
953                                              "provenance in order "
954                                              "to make further mappings.")
955
956
957            # Check it's valid and signed
958            try:
959                # Give path to CA cert to allow check
960                userAttCert.certFilePathList = self.caCertFilePathList
961                userAttCert.isValid(raiseExcep=True)
962               
963            except Exception, e:
964                raise AttributeAuthorityError("Invalid Remote Attribute "
965                                        "Certificate: " + str(e))       
966
967
968            # Check that's it's holder matches the candidate holder
969            # certificate DN
970            if holderX509Cert and userAttCert.holderDN != holderX509Cert.dn:
971                raise AttributeAuthorityError("User certificate and Attribute "
972                                        'Certificate DNs don\'t match: "%s"'
973                                        ' and "%s"' % (holderX509Cert.dn, 
974                                                       userAttCert.holderDN))
975           
976 
977            # Get roles from external Attribute Certificate
978            trustedHostRoles = userAttCert.roles
979
980
981            # Map external roles to local ones
982            localRoles = self.mapRemoteRoles2LocalRoles(
983                                                    userAttCert['issuerName'],
984                                                    trustedHostRoles)
985            if not localRoles:
986                raise AttributeAuthorityAccessDenied("No local roles mapped "
987                                               "to the %s roles: %s" % 
988                                               (userAttCert['issuerName'], 
989                                                ', '.join(trustedHostRoles)))
990
991            attCert.addRoles(localRoles)
992           
993           
994            # Mark new Attribute Certificate as mapped
995            attCert.provenance = AttCert.mappedProvenance
996
997            # Copy the user Id from the external AC
998            attCert.userId = userAttCert.userId
999           
1000            # End set mapped certificate block
1001
1002        try:
1003            # Digitally sign certificate using Attribute Authority's
1004            # certificate and private key
1005            attCert.applyEnvelopedSignature()
1006           
1007            # Check the certificate is valid
1008            attCert.isValid(raiseExcep=True)
1009           
1010            # Write out certificate to keep a record of it for auditing
1011            #attCert.write()
1012            self.__attCertLog.info(attCert)
1013           
1014            log.info('Issued an Attribute Certificate to "%s" with roles: '
1015                     '"%s"' % (userId, '", "'.join(attCert.roles)))
1016
1017            # Return the cert to caller
1018            return attCert
1019       
1020        except Exception, e:
1021            raise AttributeAuthorityError('New Attribute Certificate "%s": %s'%
1022                                          (attCert.filePath, e))
1023           
1024    def _getMapConfigFilePath(self):
1025        return self.__mapConfigFilePath
1026   
1027    def _setMapConfigFilePath(self, val):
1028        if not isinstance(val, basestring):
1029            raise AttributeAuthorityConfigError("Input Map Configuration "
1030                                                "file path must be a "
1031                                                "valid string.")
1032        self.__mapConfigFilePath = val
1033         
1034    mapConfigFilePath = property(fget=_getMapConfigFilePath,
1035                                 fset=_setMapConfigFilePath,
1036                                 doc="File path for Role Mapping configuration") 
1037       
1038    def readMapConfig(self):
1039        """Parse Map Configuration file.
1040        """
1041       
1042        log.debug("Reading map configuration file ...")
1043       
1044        try:
1045            tree = ElementTree.parse(self.mapConfigFilePath)
1046            rootElem = tree.getroot()
1047           
1048        except IOError, e:
1049            raise AttributeAuthorityConfigError('Error parsing Map '
1050                                                'Configuration file "%s": %s' % 
1051                                                (e.filename, e.strerror))         
1052        except Exception, e:
1053            raise AttributeAuthorityConfigError('Error parsing Map '
1054                                                'Configuration file: "%s": %s'% 
1055                                                (self.mapConfigFilePath, e))
1056       
1057        trustedElem = rootElem.findall('trusted')
1058        if not trustedElem: 
1059            # Make an empty list so that for loop block below is skipped
1060            # without an error 
1061            trustedElem = ()
1062
1063        # Dictionaries:
1064        # 1) to hold all the data
1065        self.__mapConfig = {'thisHost': {}, 'trustedHosts': {}}
1066
1067        # ... look-up
1068        # 2) hosts corresponding to a given role and
1069        # 3) roles of external data centre to this data centre
1070        self.__localRole2TrustedHost = {}
1071        self.__localRole2RemoteRole = {}
1072        self.__remoteRole2LocalRole = {}
1073
1074        # Information about this host
1075        try:
1076            thisHostElem = rootElem.findall('thisHost')[0]
1077           
1078        except Exception, e:
1079            raise AttributeAuthorityConfigError('"thisHost" tag not found in '
1080                                                'Map Configuration file "%s"' % 
1081                                                self.mapConfigFilePath)
1082
1083        try:
1084            hostName = thisHostElem.attrib.values()[0]
1085           
1086        except Exception, e:
1087            raise AttributeAuthorityConfigError('"name" attribute of '
1088                                                '"thisHost" element not found '
1089                                                'in Map Configuration file '
1090                                                '"%s"' % 
1091                                                self.mapConfigFilePath)
1092
1093        # hostname is also stored in the AA's config file in the 'name' tag. 
1094        # Check the two match as the latter is copied into Attribute
1095        # Certificates issued by this AA
1096        #
1097        # TODO: would be better to rationalise this so that the hostname is
1098        # stored in one place only.
1099        #
1100        # P J Kershaw 14/06/06
1101        if hostName != self.name:
1102            raise AttributeAuthorityError('"name" attribute of "thisHost" '
1103                                          'element in Map Configuration file '
1104                                          'doesn\'t match "name" element in '
1105                                          'properties file.')
1106       
1107        # Information for THIS Attribute Authority
1108        self.__mapConfig['thisHost'][hostName] = {}
1109
1110        for k, v in AttributeAuthority.mapConfigHostDefaults.items():
1111            val = thisHostElem.findtext(k)
1112            if val is None and v == NotImplemented:
1113                raise AttributeAuthorityConfigError('<thisHost> option <%s> '
1114                                                    'must be set.' % k)
1115            self.__mapConfig['thisHost'][hostName][k] = val     
1116       
1117        # Information about trusted hosts
1118        for elem in trustedElem:
1119            try:
1120                trustedHost = elem.attrib.values()[0]
1121               
1122            except Exception, e:
1123                raise AttributeAuthorityConfigError('Error reading trusted '
1124                                                    'host name: %s' % e)
1125 
1126            # Add signatureFile and list of roles
1127            #
1128            # (Currently Optional) additional tag allows query of the URI
1129            # where a user would normally login at the trusted host.  Added
1130            # this feature to allow users to be forwarded to their home site
1131            # if they are accessing a secure resource and are not
1132            # authenticated
1133            #
1134            # P J Kershaw 25/05/06
1135            self.__mapConfig['trustedHosts'][trustedHost] = {}
1136            for k, v in AttributeAuthority.mapConfigHostDefaults.items():
1137                val = thisHostElem.findtext(k)
1138                if val is None and v == NotImplemented:
1139                    raise AttributeAuthorityConfigError('<trustedHost> option '
1140                                                        '<%s> must be set.'%k)
1141                   
1142                self.__mapConfig['trustedHosts'][trustedHost][k] = \
1143                                                        elem.findtext(k)   
1144
1145            roleElem = elem.findall('role')
1146            if roleElem:
1147                # Role keyword value requires special parsing before
1148                # assignment
1149                self.__mapConfig['trustedHosts'][trustedHost]['role'] = \
1150                                        [dict(i.items()) for i in roleElem]
1151            else:
1152                # It's possible for trust relationships to not contain any
1153                # role mapping.  e.g. a site's login service trusting other
1154                # sites login requests
1155                self.__mapConfig['trustedHosts'][trustedHost]['role'] = []
1156                       
1157            self.__localRole2RemoteRole[trustedHost] = {}
1158            self.__remoteRole2LocalRole[trustedHost] = {}
1159           
1160            for role in self.__mapConfig['trustedHosts'][trustedHost]['role']:
1161                try:
1162                    localRole = role['local']
1163                    remoteRole = role['remote']
1164                except KeyError, e:
1165                    raise AttributeAuthorityError('Reading map configuration '
1166                                                  ' file "%s": no element '
1167                                                  '"%s" for host "%s"' % 
1168                                                (self.mapConfigFilePath, 
1169                                                 e, 
1170                                                 trustedHost))
1171                   
1172                # Role to host look-up
1173                if localRole in self.__localRole2TrustedHost:
1174                   
1175                    if trustedHost not in \
1176                       self.__localRole2TrustedHost[localRole]:
1177                        self.__localRole2TrustedHost[localRole].\
1178                                                        append(trustedHost)                       
1179                else:
1180                    self.__localRole2TrustedHost[localRole] = [trustedHost]
1181
1182
1183                # Trusted Host to local role and trusted host to trusted role
1184                # map look-ups
1185                try:
1186                    self.__remoteRole2LocalRole[trustedHost][remoteRole].\
1187                                                            append(localRole)                 
1188                except KeyError:
1189                    self.__remoteRole2LocalRole[trustedHost][remoteRole] = \
1190                                                                [localRole]
1191                   
1192                try:
1193                    self.__localRole2RemoteRole[trustedHost][localRole].\
1194                                                            append(remoteRole)                 
1195                except KeyError:
1196                    self.__localRole2RemoteRole[trustedHost][localRole] = \
1197                                                                [remoteRole]
1198       
1199        # Store trusted host info look-up for retrieval by getTrustedHostInfo
1200        # method                                                                         
1201        #
1202        # Nb. {}.fromkeys([...]).keys() is a fudge to get unique elements
1203        # from a list i.e. convert the list elements to a dict eliminating
1204        # duplicated elements and convert the keys back into a list.
1205        self._trustedHostInfo = dict(
1206        [
1207            (
1208                k, 
1209                {
1210                    'siteName':             v['siteName'],
1211                    'aaURI':                v['aaURI'], 
1212                    'aaDN':                 v['aaDN'], 
1213                    'loginURI':             v['loginURI'], 
1214                    'loginServerDN':        v['loginServerDN'], 
1215                    'loginRequestServerDN': v['loginRequestServerDN'], 
1216                    'role':                 {}.fromkeys([role['remote'] 
1217                                                         for role in v['role']]
1218                                                       ).keys()
1219                }
1220            ) for k, v in self.__mapConfig['trustedHosts'].items()
1221        ])
1222
1223        log.info('Loaded map configuration file "%s"' % self.mapConfigFilePath)
1224       
1225       
1226    def getRoles(self, userId):
1227        """Get the roles available to the registered user identified userId.
1228
1229        @type dn: string
1230        @param dn: user identifier - could be a X500 Distinguished Name
1231        @return: list of roles for the given user ID"""
1232
1233        log.debug('Calling getRoles for user "%s" ...' % userId)
1234       
1235        # Call to AttributeInterface derived class.  Each Attribute Authority
1236        # should define it's own roles class derived from AttributeInterface to
1237        # define how roles are accessed
1238        try:
1239            return self.__attributeInterface.getRoles(userId)
1240
1241        except Exception, e:
1242            raise AttributeAuthorityError("Getting user roles: %s" % e)
1243       
1244       
1245    def _getHostInfo(self):
1246        """Return the host that this Attribute Authority represents: its ID,
1247        the user login URI and WSDL address.  Call this method via the
1248        'hostInfo' property
1249       
1250        @rtype: dict
1251        @return: dictionary of host information derived from the map
1252        configuration"""
1253       
1254        return self.__mapConfig['thisHost']
1255       
1256    hostInfo = property(fget=_getHostInfo, 
1257                        doc="Return information about this host")
1258       
1259       
1260    def getTrustedHostInfo(self, role=None):
1261        """Return a dictionary of the hosts that have trust relationships
1262        with this AA.  The dictionary is indexed by the trusted host name
1263        and contains AA service, login URIs and the roles that map to the
1264        given input local role.
1265
1266        @type role: string
1267        @param role: if set, return trusted hosts that having a mapping set
1268        for this role.  If no role is input, return all the AA's trusted hosts
1269        with all their possible roles
1270
1271        @rtype: dict
1272        @return: dictionary of the hosts that have trust relationships
1273        with this AA.  It returns an empty dictionary if role isn't
1274        recognised"""
1275               
1276        log.debug('Calling getTrustedHostInfo with role = "%s" ...' % role) 
1277                                 
1278        if not self.__mapConfig or not self.__localRole2RemoteRole:
1279            # This Attribute Authority has no trusted hosts
1280            raise AttributeAuthorityNoTrustedHosts("The %s Attribute "
1281                                                   "Authority has no trusted "
1282                                                   "hosts" % 
1283                                                   self.name)
1284
1285
1286        if role is None:
1287            # No role input - return all trusted hosts with their service URIs
1288            # and the remote roles they map to
1289            return self._trustedHostInfo
1290
1291        else:           
1292            # Get trusted hosts for given input local role       
1293            try:
1294                trustedHosts = self.__localRole2TrustedHost[role]
1295            except:
1296                raise AttributeAuthorityNoMatchingRoleInTrustedHosts(
1297                    'None of the trusted hosts have a mapping to the '
1298                    'input role "%s"' % role)
1299   
1300   
1301            # Get associated Web service URI and roles for the trusted hosts
1302            # identified and return as a dictionary indexed by host name
1303            trustedHostInfo = dict(
1304       [(
1305            host, 
1306            {
1307                'siteName': self.__mapConfig['trustedHosts'][host]['siteName'],
1308                'aaURI':    self.__mapConfig['trustedHosts'][host]['aaURI'],
1309                'aaDN':     self.__mapConfig['trustedHosts'][host]['aaDN'],
1310                'loginURI': self.__mapConfig['trustedHosts'][host]['loginURI'],
1311                'loginServerDN': 
1312                        self.__mapConfig['trustedHosts'][host]['loginServerDN'],
1313                'loginRequestServerDN': 
1314                self.__mapConfig['trustedHosts'][host]['loginRequestServerDN'],
1315                'role':     self.__localRole2RemoteRole[host][role]
1316            }
1317        ) for host in trustedHosts])
1318                         
1319            return trustedHostInfo
1320       
1321       
1322    def mapRemoteRoles2LocalRoles(self, trustedHost, trustedHostRoles):
1323        """Map roles of trusted hosts to roles for this data centre
1324
1325        @type trustedHost: string
1326        @param trustedHost: name of external trusted data centre
1327        @type trustedHostRoles: list
1328        @param trustedHostRoles:   list of external roles to map
1329        @return: list of mapped roles"""
1330
1331        if not self.__remoteRole2LocalRole:
1332            raise AttributeAuthorityError("Roles map is not set - ensure " 
1333                                    "readMapConfig() has been called.")
1334
1335
1336        # Check the host name is a trusted one recorded in the map
1337        # configuration
1338        if not self.__remoteRole2LocalRole.has_key(trustedHost):
1339            return []
1340
1341        # Add local roles, skipping if no mapping is found
1342        localRoles = []
1343        for trustedRole in trustedHostRoles:
1344            if trustedRole in self.__remoteRole2LocalRole[trustedHost]:
1345                localRoles.extend(
1346                        self.__remoteRole2LocalRole[trustedHost][trustedRole])
1347               
1348        return localRoles
1349
1350
1351from logging.handlers import RotatingFileHandler
1352
1353# Inherit directly from Logger
1354_loggerClass = logging.getLoggerClass()
1355class AttCertLog(_loggerClass, object):
1356    """Log each Attribute Certificate issued using a rotating file handler
1357    so that the number of files held can be managed"""
1358   
1359    def __init__(self, attCertFilePath, backUpCnt=1024):
1360        """Set up a rotating file handler to log ACs issued.
1361        @type attCertFilePath: string
1362        @param attCertFilePath: set where to store ACs.  Set from
1363        AttributeAuthority properties file.
1364       
1365        @type backUpCnt: int
1366        @param backUpCnt: set the number of files to store before rotating
1367        and overwriting old files."""
1368       
1369        if not isinstance(backUpCnt, int):
1370            raise TypeError('Expecting int type for "backUpCnt" keyword')
1371       
1372        # Inherit from Logger class
1373        super(AttCertLog, self).__init__(name='', level=logging.INFO)
1374                           
1375        # Set a format for messages so that only the content of the AC is
1376        # logged, nothing else.
1377        formatter = logging.Formatter(fmt="", datefmt="")
1378
1379        # maxBytes is set to one so that only one AC will be written before
1380        # rotation to the next file
1381        fileLog = RotatingFileHandler(attCertFilePath, 
1382                                      maxBytes=1, 
1383                                      backupCount=backUpCnt)
1384        fileLog.setFormatter(formatter)           
1385        self.addHandler(fileLog)
1386                       
1387class AttributeInterfaceError(Exception):
1388    """Exception handling for NDG Attribute Authority User Roles interface
1389    class."""
1390
1391
1392class AttributeInterface(object):
1393    """An abstract base class to define the user roles interface to an
1394    Attribute Authority.
1395
1396    Each NDG data centre should implement a derived class which implements
1397    the way user roles are provided to its representative Attribute Authority.
1398   
1399    Roles are expected to indexed by user Distinguished Name (DN).  They
1400    could be stored in a database or file."""
1401
1402    # User defined class may wish to specify a URI for a database interface or
1403    # path for a user roles configuration file
1404    def __init__(self, **prop):
1405        """User Roles base class - derive from this class to define
1406        roles interface to Attribute Authority
1407       
1408        @type prop: dict
1409        @param prop: custom properties to pass to this class
1410        """
1411
1412
1413    def getRoles(self, userId):
1414        """Virtual method - Derived method should return the roles for the
1415        given user's Id or else raise an exception
1416       
1417        @type userId: string
1418        @param userId: user identity e.g. user Distinguished Name
1419        @rtype: list
1420        @return: list of roles for the given user ID"""
1421        raise NotImplementedError(
1422            self.getRoles.__doc__.replace('\n       ',''))
1423                         
Note: See TracBrowser for help on using the repository browser.