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

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/NDG/AttAuthorityIO.py@1176
Revision 1176, 21.0 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 Web Services helper classes for I/O between client
2and server
3
4NERC Data Grid Project
5
6P J Kershaw 14/12/05
7
8Copyright (C) 2005 CCLRC & NERC
9
10This software may be distributed under the terms of the Q Public License,
11version 1.0 or later.
12"""
13
14reposID = '$Id$'
15       
16from XMLMsg import *
17
18# For use with AuthorisationResp class
19from AttCert import *
20
21#_____________________________________________________________________________
22class AuthorisationReqError(XMLMsgError):   
23    """Exception handling for NDG AttAuthority WS authorisation request class.
24    """
25    pass
26
27
28#_____________________________________________________________________________
29class AuthorisationReq(XMLMsg):
30    """For client to Attribute Authority WS reqAuthorisation(): formats inputs
31    for request into XML and encrypts.
32   
33    Attribute Authority enables decryption of result"""
34   
35    # Override base class class variables
36    xmlTagTmpl = {  "proxyCert":    "",
37                    "userAttCert":  "",
38                    "encrCert":     ""    }
39                   
40    xmlMandTags = ["proxyCert"]
41                               
42                               
43    #_________________________________________________________________________
44    def update(self, userAttCert=None, **xmlTags):
45        """Override base class implementation to include extra code
46        to allow setting of userAttCert tag"""
47
48        if userAttCert:
49            if isinstance(userAttCert, basestring):
50                attCert = AttCertParse(userAttCert)
51           
52            elif isinstance(userAttCert, AttCert):
53                attCert = userAttCert
54            else:
55                raise TypeError(\
56                    "userAttCert keyword must contain string or AttCert type")
57                       
58        else:
59            attCert = None
60               
61        # Call super class update with revised attribute certificate list
62        super(self.__class__, self).update(userAttCert=attCert, **xmlTags)
63                                           
64                                           
65    #_________________________________________________________________________
66    def updateXML(self, **xmlTags):
67        """Override base class implementation to include extra code
68        to allow attribute certificate to be set from a string or AttCert
69        type"""
70       
71        # Update dictionary
72        self.update(**xmlTags)
73       
74        # Create XML formatted string ready for encryption
75        try:
76            xmlTxt = self.xmlHdr + os.linesep + \
77                "<" + self.__class__.__name__ + ">" + os.linesep
78               
79            for tag, val in self.items():
80                if tag == "userAttCert":
81                    # Remove any XML header -
82                    # update() call will have converted val to AttCert type
83                    val = val.asString(stripXMLhdr=True)
84                   
85                xmlTxt += "    <%s>%s</%s>%s" % (tag, val, tag, os.linesep)
86                   
87            xmlTxt += "</" + self.__class__.__name__ + ">" + os.linesep   
88            self.xmlTxt = xmlTxt
89           
90        except Exception, e:
91            raise XMLMsgError("Creating XML: %s" % e)
92
93
94    #_________________________________________________________________________
95    def parseXML(self):
96        """Override base class implementation to include extra code
97        to parse userAttCert tag - if left with the default, elementtree
98        adds extra "ns0" namespaces which invalidate the signature(!)"""
99       
100        rootElem = super(self.__class__, self).parseXML(rtnRootElem=True)
101        if 'userAttCert' in self:
102
103            # Convert attribute certificate to AttCert instance
104            try:
105                attCertPat = re.compile(\
106                    '<attributeCertificate>.*</attributeCertificate>', re.S)
107                attCertTxt = attCertPat.findall(self.xmlTxt)[0]
108               
109                self['userAttCert'] = AttCertParse(attCertTxt)
110               
111            except Exception, e:
112                raise AuthorisationRespError(\
113                    "Error parsing Attribute Certificate: " + str(e)) 
114
115
116#_____________________________________________________________________________
117class AuthorisationRespError(XMLMsgError):   
118    """Exception handling for NDG AttAuthority WS connect response class."""
119    pass
120
121
122#_____________________________________________________________________________
123class AuthorisationResp(XMLMsg):
124    """For client to Attribute Authority WS reqAuthorisation(): formats
125    authorisation response from AttAuthority.
126   
127    For client, enables decryption of response"""
128   
129    # Override base class class variables
130    xmlTagTmpl = {  "credential":        "",
131                    "statCode":          "",
132                    "errMsg":            ""    }
133
134    xmlMandTags = ["statCode"]
135   
136    accessGranted = 'AccessGranted'   
137    accessDenied = 'AccessDenied'
138    accessError = 'AccessError'
139
140
141    def __init__(self, **xmlMsgKeys):
142        """XML for receiving output from Attribute Authority authorisation
143        call
144       
145        xmlMsgKeys:    keywords for XMLMsg super-class.  If XML tags are
146                       input as keywords then 'errMsg' or 'statCode'
147                       must be set.
148        """       
149       
150        # Allow credentials to be accessed like dictionary keys
151        super(self.__class__, self).__init__(**xmlMsgKeys)
152       
153        if 'credential' not in self and 'errMsg' not in self:
154            raise AuthorisationRespError(\
155                                'Expecting "credential" or "errMsg" keywords')
156                               
157                               
158    #_________________________________________________________________________
159    def update(self, credential=None, **xmlTags):
160        """Override base class implementation to include extra code
161        to allow setting of extAttCertList tag"""
162
163        if credential:
164            if isinstance(credential, basestring):
165                attCert = AttCertParse(credential)
166           
167            elif isinstance(credential, AttCert):
168                attCert = credential
169            else:
170                raise TypeError(\
171                    "credential keyword must contain string or AttCert type")
172                       
173        else:
174            attCert = None
175               
176        # Call super class update with revised attribute certificate list
177        super(self.__class__, self).update(credential=attCert, **xmlTags)
178                                           
179                                           
180    #_________________________________________________________________________
181    def updateXML(self, **xmlTags):
182        """Override base class implementation to include extra code
183        to allow attribute certificate to be set from a string or AttCert
184        type"""
185       
186        # Update dictionary
187        self.update(**xmlTags)
188       
189        # Create XML formatted string ready for encryption
190        try:
191            xmlTxt = self.xmlHdr + os.linesep + \
192                "<" + self.__class__.__name__ + ">" + os.linesep
193               
194            for tag, val in self.items():
195                if tag == "credential":
196                    # Remove any XML header -
197                    # update() call will have converted val to AttCert type
198                    val = val.asString(stripXMLhdr=True)
199                   
200                xmlTxt += "    <%s>%s</%s>%s" % (tag, val, tag, os.linesep)
201                   
202            xmlTxt += "</" + self.__class__.__name__ + ">" + os.linesep   
203            self.xmlTxt = xmlTxt
204           
205#            rootNode = ElementTree.Element(self.__class__.__name__)
206#            rootNode.tail = os.linesep
207#           
208#            for tag in xmlTags:
209#                # ElementTree tostring doesn't like bool types
210#                elem = ElementTree.SubElement(rootNode, tag)
211#                elem.tail = os.linesep
212#               
213#                if isinstance(self[tag], bool):
214#                    elem.text = "%d" % self[tag]
215#               
216#                elif tag == 'credential':
217#
218#                    # str() will convert self[tag] correctly if it is an
219#                    # AttCert type
220#                    attCertElem = ElementTree.XML(str(self[tag]))
221#                    attCertElem.tail = os.linesep
222#                    elem.append(attCertElem)
223#                else:       
224#                    elem.text = self[tag]
225#                     
226#            self.xmlTxt = self.xmlHdr + os.linesep + \
227#                                                ElementTree.tostring(rootNode)
228        except Exception, e:
229            raise XMLMsgError("Creating XML: %s" % e)
230
231
232    #_________________________________________________________________________
233    def parseXML(self):
234        """Override base class implementation to include extra code
235        to parse extAttCertList tag - if left with the default, elementtree
236        adds extra "ns0" namespaces which invalidate the signature(!)"""
237       
238        rootElem = super(self.__class__, self).parseXML(rtnRootElem=True)
239        if 'credential' in self:
240
241            # Convert attribute certificate to AttCert instance
242            try:
243                attCertPat = re.compile(\
244                    '<attributeCertificate>.*</attributeCertificate>', re.S)
245                attCertTxt = attCertPat.findall(self.xmlTxt)[0]
246               
247                self['credential'] = AttCertParse(attCertTxt)
248               
249            except Exception, e:
250                raise AuthorisationRespError(\
251                    "Error parsing Attribute Certificate: " + str(e)) 
252
253
254#_____________________________________________________________________________
255class HostInfoReqError(XMLMsgError):   
256    """Exception handling for NDG AttAuthority WS GetHostInfo request
257    class."""
258    pass
259
260
261#_____________________________________________________________________________
262class HostInfoReq(XMLMsg):
263    """For client to Attribute Authority WS GetHostInfo(): formats
264    inputs for request into XML and encrypts.
265   
266    Attribute Authority enables decryption of result"""
267   
268    # Override base class class variables
269    xmlTagTmpl = {"encrCert": ""}
270
271
272#_____________________________________________________________________________
273class HostInfoRespError(XMLMsgError):   
274    """Exception handling for NDG AttAuthority WS GetHostInfo response
275    class."""
276    pass
277
278
279#_____________________________________________________________________________
280class HostInfoResp(XMLMsg):                             
281    """For client to Attribute Authority WS getTrustedInfo(): formats
282    response from AttAuthority.
283   
284    For client, enables decryption of response"""
285   
286    # Override base class class variables
287    xmlTagTmpl = {"thisHost": "", "errMsg": ""}
288
289
290    def __init__(self, **xmlMsgKeys):
291        """XML for receiving output from Attribute Authority authorisation
292        call
293       
294        xmlMsgKeys:    keywords for XMLMsg super-class.  If XML tags are
295                       input as keywords then 'errMsg' or 'statCode'
296                       must be set.
297        """       
298       
299        # Allow user credentials to be access like dictionary keys
300        super(self.__class__, self).__init__(**xmlMsgKeys)
301       
302       
303    #_________________________________________________________________________
304    def updateXML(self, **xmlTags):
305        """Override base class implementation to include extra code
306        to allow attribute certificate to be set from a string or AttCert
307        type"""
308       
309        # Update dictionary
310        self.update(**xmlTags)
311       
312        # Create XML formatted string ready for encryption
313        try:
314            xmlTxt = self.xmlHdr + os.linesep + \
315                "<" + self.__class__.__name__ + ">" + os.linesep
316               
317            if "thisHost" in xmlTags:
318                host, hostInfo = xmlTags['thisHost'].items()[0]
319                xmlTxt += """
320    <thisHost>
321        <name>%s</name>
322        <wsdl>%s</wsdl>
323        <loginURI>%s</loginURI>
324    </thisHost>""" % (host, hostInfo['wsdl'], hostInfo['loginURI'])
325
326            if "errMsg" in xmlTags:
327                xmlTxt += "    <errMsg>%s</errMsg>%s" % \
328                                            (xmlTags['errMsg'], os.linesep)
329                   
330            xmlTxt += "</" + self.__class__.__name__ + ">" + os.linesep   
331            self.xmlTxt = xmlTxt
332           
333
334        except Exception, e:
335            raise XMLMsgError("Creating XML: %s" % e)
336
337
338    #_________________________________________________________________________
339    def parseXML(self):
340        """Override base class implementation to include extra code
341        to parse trusted hosts info"""
342       
343        rootElem = super(self.__class__, self).parseXML(rtnRootElem=True)
344       
345        if 'thisHost' not in self:
346            raise HostInfoRespError, \
347                "\"thisHost\" tag is missing from the host info response"
348       
349        self['thisHost'] = {}
350       
351        hostElem = rootElem.find('thisHost')
352        if not hostElem:
353            raise HostInfoRespError, "No \"thisHost\" tag found in response"
354         
355        try:
356            hostName = hostElem.find('name').text.strip()
357           
358            # Add key for trusted host name
359            self['thisHost'][hostName] = {}
360           
361            # Add WSDL URI, loginURI and role set for that host
362            self['thisHost'][hostName]['wsdl'] = \
363                                        hostElem.find('wsdl').text.strip()
364                                       
365            self['thisHost'][hostName]['loginURI'] = \
366                                        hostElem.find('loginURI').text.strip()             
367        except Exception, e:
368            raise HostInfoRespError, \
369                        "Error parsing tag \"%s\" in host info response: %s" \
370                        % (trusted.tag, str(e))
371
372
373#_____________________________________________________________________________
374class TrustedHostInfoReqError(XMLMsgError):   
375    """Exception handling for NDG AttAuthority WS GetTrustedHostInfo request
376    class."""
377    pass
378
379
380#_____________________________________________________________________________
381class TrustedHostInfoReq(XMLMsg):
382    """For client to Attribute Authority WS GetTrustedHostInfo(): formats
383    inputs for request into XML and encrypts.
384   
385    Attribute Authority enables decryption of result"""
386   
387    # Override base class class variables
388    xmlTagTmpl = {"role": "", "encrCert": ""}
389
390
391#_____________________________________________________________________________
392class TrustedHostInfoRespError(XMLMsgError):   
393    """Exception handling for NDG AttAuthority WS GetTrustedHostInfo response
394    class."""
395    pass
396
397
398#_____________________________________________________________________________
399class TrustedHostInfoResp(XMLMsg):                             
400    """For client to Attribute Authority WS getTrustedInfo(): formats
401    response from AttAuthority.
402   
403    For client, enables decryption of response"""
404   
405    # Override base class class variables
406    xmlTagTmpl = {"trustedHosts": "", "errMsg": ""}
407
408
409    def __init__(self, **xmlMsgKeys):
410        """XML for receiving output from Attribute Authority authorisation
411        call
412       
413        xmlMsgKeys:    keywords for XMLMsg super-class.  If XML tags are
414                       input as keywords then 'errMsg' or 'statCode'
415                       must be set.
416        """       
417       
418        # Allow user credentials to be access like dictionary keys
419        super(self.__class__, self).__init__(**xmlMsgKeys)
420       
421       
422    #_________________________________________________________________________
423    def updateXML(self, **xmlTags):
424        """Override base class implementation to include extra code
425        to allow attribute certificate to be set from a string or AttCert
426        type"""
427       
428        # Update dictionary
429        self.update(**xmlTags)
430       
431        # Create XML formatted string ready for encryption
432        try:
433            xmlTxt = self.xmlHdr + os.linesep + \
434                "<" + self.__class__.__name__ + ">" + os.linesep
435               
436            if "trustedHosts" in xmlTags:
437                xmlTxt += "    <trustedHosts>%s" % os.linesep
438               
439                for host, hostInfo in xmlTags['trustedHosts'].items():
440                    xmlTxt += "        <trusted name=\"%s\">" % host
441                    xmlTxt += os.linesep
442                    xmlTxt += "            <wsdl>%s</wsdl>" % hostInfo['wsdl']
443                    xmlTxt += "            <loginURI>%s</loginURI>" % \
444                                                        hostInfo['loginURI']
445                    xmlTxt += os.linesep
446                    xmlTxt += "            <roleSet>" + os.linesep
447                    xmlTxt += ''.join(["                <role>%s</role>%s" % \
448                                        (role, os.linesep) \
449                                        for role in hostInfo['role']])
450                    xmlTxt += "            </roleSet>" + os.linesep                   
451                    xmlTxt += "        </trusted>" + os.linesep
452
453                xmlTxt += "    </trustedHosts>%s" % os.linesep
454
455            if "errMsg" in xmlTags:
456                xmlTxt += "    <errMsg>%s</errMsg>%s" % \
457                                            (xmlTags['errMsg'], os.linesep)
458                   
459            xmlTxt += "</" + self.__class__.__name__ + ">" + os.linesep   
460            self.xmlTxt = xmlTxt
461           
462
463        except Exception, e:
464            raise XMLMsgError("Creating XML: %s" % e)
465
466
467    #_________________________________________________________________________
468    def parseXML(self):
469        """Override base class implementation to include extra code
470        to parse trusted hosts info"""
471       
472        rootElem = super(self.__class__, self).parseXML(rtnRootElem=True)
473       
474        if 'trustedHosts' not in self:
475            # Some error occured on the server such that trustedHosts wasn't
476            # set
477            return
478       
479        self['trustedHosts'] = {}
480       
481        trustedHostsElem = rootElem.find('trustedHosts')
482        if not trustedHostsElem:
483            # No trusted hosts were found
484            return
485         
486        for trusted in trustedHostsElem:
487            try:
488                host = trusted.items()[0][1]
489               
490                # Add key for trusted host name
491                self['trustedHosts'][host] = {}
492               
493                # Add WSDL URI, loginURI and role set for that host
494                self['trustedHosts'][host]['wsdl'] = \
495                                            trusted.find('wsdl').text.strip()
496                                           
497                self['trustedHosts'][host]['loginURI'] = \
498                                        trusted.find('loginURI').text.strip()
499                                           
500                self['trustedHosts'][host]['role'] = \
501                    [role.text.strip() for role in trusted.find('roleSet')]
502                 
503            except Exception, e:
504                raise TrustedHostInfoRespError(\
505                "Error parsing tag \"%s\" in trusted host info response: %s" \
506                % (trusted.tag, str(e)))
507 
508
509#_____________________________________________________________________________
510class PubKeyReqError(XMLMsgError):   
511    """Exception handling for NDG SessionMgr WS getPubKey request class."""
512    pass
513
514
515#_____________________________________________________________________________
516class PubKeyReq(XMLMsg):
517    """For client to Session Manager WS getPubKey(): formats inputs for
518    request into XML"""   
519    pass
520
521
522#_____________________________________________________________________________
523class PubKeyRespError(XMLMsgError):   
524    """Exception handling for NDG SessionMgr WS getPubKey response class."""
525    pass
526
527
528#_____________________________________________________________________________
529class PubKeyResp(XMLMsg):
530    """For client to Session Manager WS getPubKey(): formats getPubKey
531    response from SessionMgr"""
532   
533    # Override base class class variables
534    xmlTagTmpl = {"pubKey": "", "errMsg": ""}
535                       
536
537    def __init__(self, **xmlMsgKeys):
538        """XML for receiving credentials from Session Manager getPubKey call
539       
540        xmlMsgKeys:    keywords for XMLMsg super-class.  If XML tags are
541                       input as keywords then 'proxyCert' or 'sessCookie'
542                       must be set.                       
543        """       
544       
545        # Allow user credentials to be access like dictionary keys
546        super(self.__class__, self).__init__(**xmlMsgKeys)
547       
548                               
549        # Check for valid output credentials
550        # XML tags input as keywords expected - check minimum
551        # required are present for SessionMgr getPubKey response
552        if 'pubKey' not in self and 'errMsg' not in self:           
553            raise PubKeyRespError(\
554                "PubKey response document must contain: \"pubKey\"" + \
555                " or \"errMsg\" keywords")
556               
Note: See TracBrowser for help on using the repository browser.