source: TI12-security/trunk/python/NDG/AttAuthority.py @ 1176

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/NDG/AttAuthority.py@1176
Revision 1176, 31.4 KB checked in by pjkersha, 14 years ago (diff)

Changes to incoporate new getHostInfo Attribute Authority WS method.

Tests/AttAuthorityIOtest.py: new unit test test method

Tests/SecurityClientTest?.py: minor changes to test settings

dist/NDG-Security-0.68.tar.gz: new distribution

www/html/attAuthority.wsdl: updated WSDL contains getHostInfo method.

conf/mapConfig.xml: contains new tags for information about the service provider of the AA e.g. loginURI,
service provider name. This is used by the new getHostInfo WS method.

conf/attAuthorityProperties.xml: remove old commented out tags.

NDG/AttAuthorityIO.py: added HostInfo?* classes for handling getHostInfo WS method I/O.

NDG/attAuthority_services_server.py and NDG/attAuthority_services.py: updated inline with WSDL changes.

NDG/AttAuthority.py:

  • readMapConfig updated to include new 'thisHost' tags.
  • self.mapConfig dictionary re-ordered to include top level keys 'thisHost' and 'trustedHosts'
  • New hostInfo property

NDG/AttCert.py: trivial fixes to commenting

NDG/XMLMsg.py: simplify error message for "Invalid keywords set for update..." error

NDG/CredWallet.py:

  • Client public key is now read in at the point where the corresponding pub key file path is set - i.e. in

setClntPubKeyFilePath method. This means the equivalent code in reqAuthorisation is not needed.

  • reqAuthorisation method has a new flag refreshAttCert. If set, the wallet is checked first for an existing

AC issued by the target AA. If found this is returned, and the call to the AA is skipped.

NDG/SecurityClient.py: added AttAuthorityClient?.getHostInfo WS wrapper method.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1"""NDG Attribute Authority handles security authentication and authorization
2
3NERC Data Grid Project
4
5P J Kershaw 15/04/05
6
7Copyright (C) 2005 CCLRC & NERC
8
9This software may be distributed under the terms of the Q Public License,
10version 1.0 or later.
11"""
12
13reposID = '$Id$'
14
15import types
16
17
18# Create unique names for attribute certificates
19import tempfile
20import os
21
22# Alter system path for dynamic import of user roles class
23import sys
24
25# For parsing of properties file
26import cElementTree as ElementTree
27
28# X509 Certificate handling
29from X509 import *
30
31# NDG Attribute Certificate
32from AttCert import *
33
34# Format for XML messages passed over WS
35from AttAuthorityIO import *
36
37
38#_____________________________________________________________________________
39class AttAuthorityError(Exception):
40    """Exception handling for NDG Attribute Authority class."""
41   
42    def __init__(self, msg):
43        self.__msg = msg
44         
45    def __str__(self):
46        return self.__msg
47
48
49
50
51#_____________________________________________________________________________
52class AttAuthorityAccessDenied(AttAuthorityError):
53    """NDG Attribute Authority - access denied exception.
54
55    Raise from authorise method where no roles are available for the user
56    but that the request is otherwise valid.  In all other error cases raise
57    AttAuthorityError"""   
58    pass
59
60
61
62#_____________________________________________________________________________
63class AttAuthority(dict):
64
65    """NDG Attribute Authority - server for user authentication/authorization.
66    """
67
68    # Code designed from NERC Data Grid Enterprise and Information Viewpoint
69    # documents.
70    #
71    # Also, draws from Neil Bennett's ACServer class used in the Java
72    # implementation of NDG Security
73
74    # valid configuration property keywords
75    __validKeys = [ 'name',
76                    'keyFile',
77                    'keyPwd',
78                    'certFile',
79                    'caCertFile',
80                    'attCertLifeTime',
81                    'attCertNotBeforeOff',
82                    'attCertFilePfx',
83                    'attCertFileSfx',
84                    'mapConfigFile',
85                    'attCertDir',
86                    'dnSeparator',
87                    'usrRolesModFilePath',
88                    'usrRolesModName',
89                    'usrRolesClassName',
90                    'usrRolesPropFile']
91   
92    def __init__(self, propFilePath, bReadMapConfig=True):
93        """Create new NDG Attribute Authority instance
94
95        propFilePath:   path to file containing Attribute Authority
96                        configuration parameters.
97        bReadMapConfig: by default the Map Configuration file is read.  Set
98                        this flag to False to override.
99        """
100
101        # Base class initialisation
102        dict.__init__(self)
103       
104        if not isinstance(propFilePath, basestring):
105            raise AttAuthorityError("Input Properties file path " + \
106                                    "must be a valid string.")
107
108
109        # Initialise role mapping look-ups - These are set in readMapConfig()
110        self.__mapConfig = None
111        self.__localRole2RemoteRole = None
112        self.__remoteRole2LocalRole = None
113
114
115        # Configuration file properties are held together in a dictionary
116        self.__prop = {}
117
118        # Read Attribute Authority Properties file
119        self.readProperties(propFilePath)
120
121        # Read the Map Configuration file
122        if bReadMapConfig:
123            self.readMapConfig()
124
125        # Instantiate Certificate object
126        self.__cert = X509Cert(self.__prop['certFile'])
127        self.__cert.read()
128
129        # Check it's valid
130        try:
131            self.__cert.isValidTime(raiseExcep=True)
132           
133        except Exception, e:
134            raise AttAuthorityError(\
135                    "Attribute Authority's certificate is invalid: " + str(e))
136       
137        # Check CA certificate
138        caCert = X509Cert(self.__prop['caCertFile'])
139        caCert.read()
140       
141        try:
142            caCert.isValidTime(raiseExcep=True)
143           
144        except Exception, e:
145            raise AttAuthorityError("CA certificate is invalid: " + str(e))
146       
147        # Issuer details - serialise using the separator string set in the
148        # properties file
149        self.__issuer = \
150            self.__cert.dn.serialise(separator=self.__prop['dnSeparator'])
151
152        self.__issuerSerialNumber = self.__cert.serialNumber
153
154       
155        # Set-up user roles interface
156        try:
157            try:
158                # Temporarily extend system path ready for import
159                sysPathBak = sys.path[:]
160                sys.path.append(self.__prop['usrRolesModFilePath'])
161               
162                # Import module name specified in properties file
163                usrRolesMod = __import__(self.__prop['usrRolesModName'],
164                                         globals(),
165                                         locals(),
166                                         [self.__prop['usrRolesClassName']])
167   
168                usrRolesClass = eval('usrRolesMod.' + \
169                                     self.__prop['usrRolesClassName'])
170            finally:
171                sys.path[:] = sysPathBak
172                               
173        except Exception, e:
174            raise AttAuthorityError('Importing User Roles module: %s' % e)
175
176        # Check class inherits from AAUserRoles abstract base class
177        if not issubclass(usrRolesClass, AAUserRoles):
178            raise AttAuthorityError(\
179                "User Roles class %s must be derived from AAUserRoles" % \
180                self.__prop['usrRolesClassName'])
181
182
183        # Instantiate custom class
184        try:
185            self.__usrRoles = usrRolesClass(self.__prop['usrRolesPropFile'])
186           
187        except Exception, e:
188            raise AttAuthorityError(\
189                "Error instantiating User Roles interface: " + str(e))
190     
191       
192    #_________________________________________________________________________
193    # Methods for Attribute Authority dictionary like behaviour       
194    def __delitem__(self, key):
195        self.__class__.__name__ + " keys cannot be removed"       
196        raise KeyError('Keys cannot be deleted from '+self.__class__.__name__)
197
198
199    def __getitem__(self, key):
200        self.__class__.__name__ + """ behaves as data dictionary of Attribute
201        Authority properties
202        """
203        if key not in self.__prop:
204            raise KeyError("Invalid key " + key)
205       
206        return self.__prop[key]
207       
208
209    def clear(self):
210        raise KeyError("Data cannot be cleared from "+self.__class__.__name__)
211   
212    def keys(self):
213        return self.__prop.keys()
214
215    def items(self):
216        return self.__prop.items()
217
218    def values(self):
219        return self.__prop.values()
220
221    def has_key(self, key):
222        return self.__prop.has_key(key)
223
224    # 'in' operator
225    def __contains__(self, key):
226        return key in self.__prop
227
228       
229    #_________________________________________________________________________
230    def authorise(self,
231                  reqXMLtxt=None, 
232                  proxyCertFilePath=None,
233                  userAttCertFilePath=None,
234                  **reqKeys):
235
236        """Request a new Attribute Certificate for authorisation
237
238        reqXMLtxt:              input keywords as tags in formatted XML string
239                                String must follow format for
240                                AttAuthorityIO.AuthorisationReq class to
241                                parse.
242                               
243        proxyCertFilePath|proxyCert:
244
245                                user's proxy certificate use appropriate
246                                keyword for input as a file path or as the
247                                text content respectively.
248                               
249                                Nb. proxyCert is set via reqKeys
250                               
251        userAttCertFilePath|userAttCert:
252       
253                                externally provided attribute certificate
254                                from another data centre.  This is only
255                                necessary if the user is not registered with
256                                this attribute authority.
257
258                                Pass in either the file path or a string
259                                containing the certificate XML content.
260                               
261                                Nb. userAttCert is set via reqKeys
262                                """
263
264        if reqXMLtxt is not None:
265            # Parse XML text into keywords corresponding to the input
266            # parameters
267            if not isinstance(reqXMLtxt, basestring):
268                raise AttAuthorityError(\
269                            "XML Authorisation request must be a string")
270                                       
271            # Parse and decrypt as necessary
272            try:
273                # 1st assume that the request was encrypted
274                reqKeys = AuthorisationReq(encrXMLtxt=reqXMLtxt,
275                                    encrPriKeyFilePath=self.__prop['keyFile'],
276                                    encrPriKeyPwd=self.__prop['keyPwd'])
277            except Exception, e:
278               
279                # Error occured decrypting - Trying parsing again, but this
280                # time assuming non-encrypted
281                try:
282                    reqKeys = AuthorisationReq(xmlTxt=reqXMLtxt)
283                   
284                except Exception, e:
285                    raise AttAuthorityError(\
286                        "Error parsing authorisation request: %s" % e)
287
288
289        # Read proxy certificate
290        try:
291            usrProxyCert = X509Cert()
292           
293            if proxyCertFilePath is not None and \
294               isinstance(proxyCertFilePath, basestring):
295
296                # Proxy Certificate input as a file
297                usrProxyCert.read(proxyCertFilePath)
298               
299            elif reqKeys['proxyCert'] is not None:
300
301                # Proxy Certificate input as string text
302                usrProxyCert.parse(reqKeys['proxyCert'])
303
304            else:
305                raise AttAuthorityError(\
306                    "no input proxy certificate file path or file text")
307           
308        except Exception, e:
309            raise AttAuthorityError("User Proxy Certificate: %s" % e)
310
311
312        # Check proxy certificate hasn't expired
313        try:
314            usrProxyCert.isValidTime(raiseExcep=True)
315           
316        except Exception, e:
317            raise AttAuthorityError("User Proxy Certificate is invalid: " + \
318                                    str(e))
319
320           
321        # Get Distinguished name from certificate as an X500DN type
322        usrDN = usrProxyCert.dn
323       
324       
325        # Make a new Attribute Certificate instance passing in certificate
326        # details for later signing
327        #
328        # Nb. new attribute certificate file path is created from the
329        # Credentials Repository
330        certFilePathList = [self.__prop['certFile'],self.__prop['caCertFile']]
331        attCert = AttCert(self.__newAttCertFilePath(),
332                          signingKeyFilePath=self.__prop['keyFile'],
333                          certFilePathList=certFilePathList)
334
335
336        # Set holder's (user's) Distinguished Name
337        try:
338            attCert['holder'] = \
339                        usrDN.serialise(separator=self.__prop['dnSeparator'])
340           
341        except Exception, e:
342            raise AttAuthorityError("User DN: %s" % e)
343
344       
345        # Set Issuer details from Attribute Authority
346        issuerDN = self.__cert.dn
347        try:
348            attCert['issuer'] = \
349                    issuerDN.serialise(separator=self.__prop['dnSeparator'])
350           
351        except Exception, e:
352            raise AttAuthorityError("Issuer DN: %s" % e)
353       
354        attCert['issuerName'] = self.__prop['name']
355        attCert['issuerSerialNumber'] = self.__issuerSerialNumber
356
357
358        # Set validity time
359        try:
360            attCert.setValidityTime(\
361                        lifeTime=self.__prop['attCertLifeTime'],
362                        notBeforeOffset=self.__prop['attCertNotBeforeOff'])
363
364            # Check against the proxy certificate's expiry
365            dtUsrProxyNotAfter = usrProxyCert.notAfter
366           
367            if attCert.getValidityNotAfter(asDatetime=True) > \
368               dtUsrProxyNotAfter:
369
370                # Adjust the attribute certificate's expiry date time
371                # so that it agrees with that of the proxy certificate
372                attCert.setValidityTime(dtNotAfter=dtUsrProxyNotAfter)
373           
374        except Exception, e:
375            raise AttAuthorityError("Error setting validity time: %s" % e)
376       
377
378        # Check name is registered with this Attribute Authority - if no
379        # user roles are found, the user is not registered
380        usrRoles = self.getRoles(str(usrDN))
381        if usrRoles:           
382            # Set as an Original Certificate
383            #
384            # User roles found - user is registered with this data centre
385            # Add roles for this user for this data centre
386            attCert.addRoles(usrRoles)
387
388            # Mark new Attribute Certificate as an original
389            attCert['provenance'] = AttCert.origProvenance
390
391        else:           
392            # Set as a Mapped Certificate
393            #
394            # No roles found - user is not registered with this data centre
395            # Check for an externally provided certificate from another
396            # trusted data centre
397            if userAttCertFilePath:
398               
399                # Read externally provided certificate
400                try:
401                    extAttCert = AttCertRead(userAttCertFilePath)
402                   
403                except Exception, e:
404                    raise AttAuthorityError(\
405                            "Reading external Attribute Certificate: %s" + e)
406                               
407            elif 'userAttCert' in reqKeys and reqKeys['userAttCert']:
408                extAttCert = reqKeys['userAttCert']
409               
410            else:
411                raise AttAuthorityAccessDenied(\
412                    "User \"%s\" is not registered " % attCert['holder'] + \
413                    "and no external attribute certificate is available " + \
414                    "to make a mapping.")
415
416
417            # Check it's an original certificate - mapped certificates can't
418            # be used to make further mappings
419            if extAttCert.isMapped():
420                raise AttAuthorityError(\
421                    "External Attribute Certificate must have an " + \
422                    "original provenance in order to make further mappings.")
423
424
425            # Check it's valid and signed
426            try:
427                # Give path to CA cert to allow check
428                extAttCert.isValid(raiseExcep=True,
429                                   certFilePathList=self.__prop['caCertFile'])
430               
431            except Exception, e:
432                raise AttAuthorityError(\
433                            "Invalid Remote Attribute Certificate: " + str(e))       
434
435
436            # Check that's it's holder matches the user certificate DN
437            try:
438                holderDN = X500DN(dn=extAttCert['holder'])
439               
440            except Exception, e:
441                raise AttAuthorityError(\
442                    "Error creating X500DN for holder: %s" + e)
443           
444            if holderDN != usrDN:
445                raise AttAuthorityError(\
446                    "User certificate and Attribute Certificate DNs " + \
447                    "don't match: " + str(usrDN) + " and " + str(holderDN))
448           
449 
450            # Get roles from external Attribute Certificate
451            trustedHostRoles = extAttCert.getRoles()
452
453
454            # Map external roles to local ones
455            localRoles = self.mapRemoteRoles2LocalRoles(\
456                                                    extAttCert['issuerName'],
457                                                    trustedHostRoles)
458            if not localRoles:
459                raise AttAuthorityAccessDenied, \
460                    "No local roles mapped to the %s roles: %s" % \
461                    (extAttCert['issuerName'], ', '.join(trustedHostRoles))
462
463            attCert.addRoles(localRoles)
464           
465           
466            # Mark new Attribute Certificate as mapped
467            attCert['provenance'] = AttCert.mappedProvenance
468
469            # End set mapped certificate block
470           
471
472        try:
473            # Digitally sign certificate using Attribute Authority's
474            # certificate and private key
475            attCert.sign(signingKeyPwd=self.__prop['keyPwd'])
476           
477            # Check the certificate is valid
478            attCert.isValid(raiseExcep=True)
479           
480            # Write out certificate to keep a record of it for auditing
481            attCert.write()
482
483            # Return the cert to caller
484            return attCert
485       
486        except Exception, e:
487            raise AttAuthorityError("New Attribute Certificate \"%s\": %s" % \
488                                    (attCert.filePath, e))
489
490   
491
492
493    def readProperties(self, propFilePath=None):
494
495        """Read the configuration properties for the Attribute Authority
496
497        propFilePath: file path to properties file
498        """
499       
500        if propFilePath is not None:
501            if not isinstance(propFilePath, basestring):
502                raise AttAuthorityError("Input Properties file path " + \
503                                        "must be a valid string.")
504           
505            self.__propFilePath = propFilePath
506
507
508        try:
509            tree = ElementTree.parse(self.__propFilePath)
510           
511        except IOError, ioErr:
512            raise AttAuthorityError(\
513                                "Error parsing properties file \"%s\": %s" % \
514                                (ioErr.filename, ioErr.strerror))
515
516       
517        aaProp = tree.getroot()
518
519        # Copy properties from file as member variables
520        prop = dict([(elem.tag, elem.text) for elem in aaProp])
521
522
523        # Check for missing properties
524        propKeys = prop.keys()
525        missingKeys = [key for key in AttAuthority.__validKeys \
526                       if key not in propKeys]
527        if missingKeys != []:
528            raise AttAuthorityError("The following properties are " + \
529                                    "missing from the properties file: " + \
530                                    ', '.join(missingKeys))
531
532        # Strip white space - apart from fields where may be required
533        for key in prop:
534            if key != 'keyPwd' and prop[key]: 
535                prop[key] = prop[key].strip()
536               
537            # Check for environment variables in file paths
538            tagCaps = key.upper()
539            if 'FILE' in tagCaps or 'PATH' in tagCaps or 'DIR' in tagCaps:
540                prop[key] = os.path.expandvars(prop[key])
541 
542 
543        # Ensure Certificate time parameters are converted to numeric type
544        prop['attCertLifeTime'] = float(prop['attCertLifeTime'])
545        prop['attCertNotBeforeOff'] = float(prop['attCertNotBeforeOff'])
546         
547        self.__prop = prop
548
549       
550        # Check directory path
551        try:
552            dirList = os.listdir(self.__prop['attCertDir'])
553
554        except OSError, osError:
555            raise AttAuthorityError(\
556                "Invalid directory path Attribute Certificates store: " + \
557                osError.strerror)
558
559       
560       
561       
562    def readMapConfig(self, mapConfigFilePath=None):
563        """Parse Map Configuration file.
564
565        mapConfigFilePath:  file path for map configuration file.  If omitted,
566                            use member variable __mapConfigFilePath.
567        """
568       
569        if mapConfigFilePath is not None:
570            if not isinstance(mapConfigFilePath, basestring):
571                raise AttAuthorityError("Input Map Configuration file path "+\
572                                        "must be a valid string.")
573           
574            self.__prop['mapConfigFile'] = mapConfigFilePath
575
576       
577        tree = ElementTree.parse(self.__prop['mapConfigFile'])
578        rootElem = tree.getroot()
579
580        trustedElem = rootElem.findall('trusted')
581
582        # Dictionaries:
583        # 1) to hold all the data
584        self.__mapConfig = {'thisHost': {}, 'trustedHosts': {}}
585
586        # ... look-up
587        # 2) hosts corresponding to a given role and
588        # 3) roles of external data centre to this data centre
589        self.__localRole2TrustedHost = {}
590        self.__localRole2RemoteRole = {}
591        self.__remoteRole2LocalRole = {}
592
593
594        # Information about this host
595        try:
596            thisHostElem = rootElem.findall('thisHost')[0]
597           
598        except Exception, e:
599            raise AttAuthorityError, \
600            "\"thisHost\" tag not found in Map Configuration file \"%s\"" % \
601            self.__prop['mapConfigFile']
602
603        try:
604            hostName = thisHostElem.attrib.values()[0]
605           
606        except Exception, e:
607            raise AttAuthorityError, "\"name\" attribute of \"thisHost\" " + \
608                        "tag not found in Map Configuration file \"%s\"" % \
609                        self.__prop['mapConfigFile']
610
611
612        # hostname is also stored in the AA's config file in the 'name' tag. 
613        # Check the two match as the latter is copied into Attribute
614        # Certificates issued by this AA
615        #
616        # TODO: would be better to rationalise this so that the hostname is
617        # stored in one place only.
618        #
619        # P J Kershaw 14/06/06
620        if hostName != self.__prop['name']:
621            raise AttAuthorityError, "\"name\" attribute of \"thisHost\" " + \
622                "tag in Map Configuration file doesn't match config file " + \
623                "\"name\" tag"
624       
625        self.__mapConfig['thisHost'][hostName] = \
626        {
627            'loginURI':     thisHostElem.findtext('loginURI'),
628            'wsdl':         thisHostElem.findtext('wsdl')
629        }       
630       
631       
632        # Information about trusted hosts
633        for elem in trustedElem:
634
635            roleElem = elem.findall('role')
636            if not roleElem:
637                raise AttAuthorityError("\"role\" tag not found in \"%s\"" % \
638                                        self.__prop['mapConfigFile'])
639
640            try:
641                trustedHost = elem.attrib.values()[0]
642               
643            except Exception, e:
644                raise AttAuthorityError, \
645                                    "Error reading trusted host name: %s" % e
646
647           
648            # Add signatureFile and list of roles
649            #
650            # (Currently Optional) additional tag allows query of the URI
651            # where a user would normally login at the trusted host.  Added
652            # this feature to allow users to be forwarded to their home site
653            # if they are accessing a secure resource and are not
654            # authenticated
655            #
656            # P J Kershaw 25/05/06
657            self.__mapConfig['trustedHosts'][trustedHost] = \
658            {
659                'loginURI':     elem.findtext('loginURI'),
660                'wsdl':         elem.findtext('wsdl'),
661                'role':         [dict(i.items()) for i in roleElem]
662            }
663
664                   
665            self.__localRole2RemoteRole[trustedHost] = {}
666            self.__remoteRole2LocalRole[trustedHost] = {}
667           
668            for role in self.__mapConfig['trustedHosts'][trustedHost]['role']:
669
670                localRole = role['local']
671                remoteRole = role['remote']
672               
673                # Role to host look-up
674                if localRole in self.__localRole2TrustedHost:
675                   
676                    if trustedHost not in \
677                       self.__localRole2TrustedHost[localRole]:
678                        self.__localRole2TrustedHost[localRole].\
679                                                        append(trustedHost)                       
680                else:
681                    self.__localRole2TrustedHost[localRole] = [trustedHost]
682
683
684                # Trusted Host to local role and trusted host to trusted role
685                # map look-ups
686                try:
687                    self.__remoteRole2LocalRole[trustedHost][remoteRole].\
688                                                            append(localRole)                 
689                except KeyError:
690                    self.__remoteRole2LocalRole[trustedHost][remoteRole] = \
691                                                                [localRole]
692                   
693                try:
694                    self.__localRole2RemoteRole[trustedHost][localRole].\
695                                                            append(remoteRole)                 
696                except KeyError:
697                    self.__localRole2RemoteRole[trustedHost][localRole] = \
698                                                                [remoteRole]                 
699 
700
701           
702    def usrIsRegistered(self, usrDN):
703        """Check a particular user is registered with the Data Centre that the
704        Attribute Authority represents"""
705        return self.__usrRoles.usrIsRegistered(usrDN)
706       
707
708
709
710
711    def getRoles(self, dn):
712        """Get the roles available to the registered user identified usrDN.
713        """
714
715        # Call to AAUserRoles derived class.  Each Attribute Authority
716        # should define it's own roles class derived from AAUserRoles to
717        # define how roles are accessed
718        try:
719            return self.__usrRoles.getRoles(dn)
720
721        except Exception, e:
722            raise AttAuthorityError("Getting user roles: %s" % e)
723
724
725
726   
727    def __getHostInfo(self):
728        """Return the host that this Attribute Authority represents: its ID,
729        the user login URI and WSDL address.  Call this method via the
730        'hostInfo' property"""
731       
732        return self.__mapConfig['thisHost']
733       
734    hostInfo = property(fget=__getHostInfo, 
735                        doc="Return information about this host")
736
737
738
739   
740    def getTrustedHostInfo(self, role=None):
741        """Return a dictionary of the hosts that have trust relationships
742        with this AA.  The dictionary is indexed by the trusted host name
743        and contains WSDL URIs and the roles that map to the
744        given input local role.
745
746        If no role is input, return all the AA's trusted hosts with all
747        their possible roles
748
749        Returns emoty dictionary if role isn't recognised"""
750                                         
751        if not self.__localRole2RemoteRole:
752            raise AttAuthorityError("Roles to host look-up is not set - " + \
753                                    "ensure readMapConfig() has been called.")
754
755
756        if role is None:
757            # No role input - return all trusted hosts with their WSDL URIs
758            # and the remote roles they map to
759            #
760            # Nb. {}.fromkeys([...]).keys() is a fudge to get unique elements
761            # from a list i.e. convert the list elements to a dict eliminating
762            # duplicated elements and convert the keys bacl into a list.
763            trustedHostInfo = dict(\
764            [\
765                (\
766                    k, \
767                    {
768                        'wsdl':        v['wsdl'], \
769                        'loginURI':    v['loginURI'], \
770                        'role':        {}.fromkeys(\
771                            [\
772                                role['remote'] for role in v['role']
773                            ]\
774                        ).keys()
775                    }
776                ) for k, v in self.__mapConfig['trustedHosts'].items()
777            ])
778
779        else:           
780            # Get trusted hosts for given input local role       
781            try:
782                trustedHosts = self.__localRole2TrustedHost[role]
783            except:
784                return {}
785   
786   
787            # Get associated WSDL URI and roles for the trusted hosts
788            # identified and return as a dictionary indexed by host name
789            trustedHostInfo = dict(\
790   [(\
791        host, \
792        {
793            'wsdl':     self.__mapConfig['trustedHosts'][host]['wsdl'],
794            'loginURI': self.__mapConfig['trustedHosts'][host]['loginURI'],
795            'role':     self.__localRole2RemoteRole[host][role]
796        }\
797    ) for host in trustedHosts])
798                         
799        return trustedHostInfo
800
801
802
803
804    def mapRemoteRoles2LocalRoles(self, trustedHost, trustedHostRoles):
805        """Map roles of trusted hosts to roles for this data centre
806
807        trustedHost:        name of external trusted data centre
808        trustedHostRoles:   list of external roles to map"""
809
810        if not self.__remoteRole2LocalRole:
811            raise AttAuthorityError("Roles map is not set - ensure " + \
812                                    "readMapConfig() has been called.")
813
814
815        # Check the host name is a trusted one recorded in the map
816        # configuration
817        if not self.__remoteRole2LocalRole.has_key(trustedHost):
818            return []
819
820        # Add local roles, skipping if no mapping is found
821        localRoles = []
822        for trustedRole in trustedHostRoles:
823            if trustedRole in self.__remoteRole2LocalRole[trustedHost]:
824                localRoles.extend(\
825                        self.__remoteRole2LocalRole[trustedHost][trustedRole])
826               
827        return localRoles
828
829
830
831
832    def __newAttCertFilePath(self):
833        """Create a new unique attribute certificate file path"""
834       
835        attCertFd, attCertFilePath = \
836                   tempfile.mkstemp(suffix=self.__prop['attCertFileSfx'],
837                                    prefix=self.__prop['attCertFilePfx'],
838                                    dir=self.__prop['attCertDir'],
839                                    text=True)
840
841        # The file is opened - close using the file descriptor returned in the
842        # first element of the tuple
843        os.close(attCertFd)
844
845        # The file path is the 2nd element
846        return attCertFilePath
847
848
849
850
851#_____________________________________________________________________________
852class AAUserRolesError(Exception):
853
854    """Exception handling for NDG Attribute Authority User Roles interface
855    class."""
856   
857    def __init__(self, msg):
858        self.__msg = msg
859         
860    def __str__(self):
861        return self.__msg
862
863
864
865#_____________________________________________________________________________
866class AAUserRoles:
867
868    """An abstract base class to define the user roles interface to an
869    Attribute Authority.
870
871    Each NDG data centre should implement a derived class which implements
872    the way user roles are provided to its representative Attribute Authority.
873   
874    Roles are expected to indexed by user Distinguished Name (DN).  They
875    could be stored in a database or file."""
876
877    # User defined class may wish to specify a URI for a database interface or
878    # path for a user roles configuration file
879    def __init__(self, dbURI=None, filePath=None):
880        """User Roles abstract base class - derive from this class to define
881        roles interface to Attribute Authority"""
882        raise NotImplementedError(\
883            self.__init__.__doc__.replace('\n       ',''))
884
885
886    def usrIsRegistered(self, dn):
887        """Derived method should return True if user is known otherwise
888        False"""
889        raise NotImplementedError(
890            self.UserIsRegistered.__doc__.replace('\n       ',''))
891
892
893    def getRoles(self, dn):
894        """Derived method should return the roles for the given user's
895        DN or else raise an exception"""
896        raise NotImplementedError(
897            self.getRoles.__doc__.replace('\n       ',''))
898                         
Note: See TracBrowser for help on using the repository browser.