source: TI12-security/trunk/python/ndg.security.common/ndg/security/common/SessionMgrIO.py @ 1771

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/ndg.security.common/ndg/security/common/SessionMgrIO.py@1771
Revision 1771, 23.7 KB checked in by pjkersha, 13 years ago (diff)

Replaced references to 'PubKey?' with 'Cert' as this is more accurate - they refer to X.509 certs which
contain public keys.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1"""NDG Session Manager 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) 2006 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
16# For new line symbol
17import os
18
19# Use _getframe to allow setting of attributes by derived class methods
20import sys
21
22# Filter out xml headers from returned attribute certificates in
23# AuthorisationResp
24import re
25
26from XMLMsg import *
27
28# For use with AuthorisationResp class
29from AttCert import *
30 
31
32#_____________________________________________________________________________
33class ConnectReqError(XMLMsgError):   
34    """Exception handling for NDG SessionMgr WS connect request class."""
35    pass
36
37
38#_____________________________________________________________________________
39class ConnectReq(XMLMsg):
40    """For client to Session Manager WS connect(): formats inputs for request
41    into XML and encrypts.
42   
43    For Session Manager enables decryption of result"""
44   
45    # Override base class class variables
46    xmlTagTmpl = {  "userName":              "",
47                    "pPhrase":               "",
48                    "proxyCert":             "",
49                    "sessID":                "",
50                    "getCookie":             "",
51                    "createServerSess":      "",
52                    "encrCert":              ""    }
53                       
54
55    def __init__(self, **xmlMsgKeys):
56        """XML for sending encrypted credentials to Session Manager connect
57       
58        xmlMsgKeys:    keywords for XMLMsg super-class.  If XML tags are
59                       input as keywords then 'userName' and 'pPhrase' or
60                       'proxyCert' or 'sessID' must be input as the bare
61                       minimum required for SessionMgr connect request.
62        """
63       
64                               
65        # Check for encrypted text or valid credentials
66        if 'encrXMLtxt' not in xmlMsgKeys and 'xmlTxt' not in xmlMsgKeys:           
67
68            # XML tags input as keywords expected - check minimum
69            # required are present for SessionMgr connect request
70            if 'userName' not in xmlMsgKeys and 'pPhrase' not in xmlMsgKeys:
71                if 'proxyCert' not in xmlMsgKeys:
72                    if 'sessID' not in xmlMsgKeys:
73                        raise ConnectReqError(\
74                            "Credentials must be: \"userName\" and " + \
75                            "\"pPhrase\" or \"proxyCert\" or \"sessID\"")
76               
77        # Allow user credentials to be access like dictionary keys
78        super(ConnectReq, self).__init__(**xmlMsgKeys)
79
80
81    #_________________________________________________________________________
82    def parseXML(self):
83        """Override base class implementation to include extra code
84        to convert boolean"""
85       
86        rootElem = super(ConnectReq, self).parseXML(rtnRootElem=True)
87       
88        if 'getCookie' in self:
89            self['getCookie'] = bool(int(self['getCookie']))
90           
91        if 'createServerSess' in self:
92            self['createServerSess'] = bool(int(self['createServerSess']))
93
94
95#_____________________________________________________________________________
96class ConnectRespError(XMLMsgError):   
97    """Exception handling for NDG SessionMgr WS connect response class."""
98    pass
99
100
101#_____________________________________________________________________________
102class ConnectResp(XMLMsg):
103    """For client to Session Manager WS connect(): formats connect response
104    from SessionMgr.
105   
106    For client, enables decryption of response"""
107   
108    # Override base class class variables
109    xmlTagTmpl = {  "proxyCert":   "",
110                    "sessCookie":  "",
111                    "errMsg":      ""    }
112                       
113
114    def __init__(self, **xmlMsgKeys):
115        """XML for receiving credentials from Session Manager connect call
116       
117        xmlMsgKeys:    keywords for XMLMsg super-class.  If XML tags are
118                       input as keywords then 'proxyCert' or 'sessCookie'
119                       must be set.                       
120        """       
121       
122        # Allow user credentials to be access like dictionary keys
123        super(ConnectResp, self).__init__(**xmlMsgKeys)
124       
125                               
126        # Check for valid output credentials
127        # XML tags input as keywords expected - check minimum
128        # required are present for SessionMgr connect response
129        if 'proxyCert' not in self and 'sessCookie' not in self:           
130
131            # If no proxy cert or session cookie then it must be an error
132            if 'errMsg' not in self:
133                raise ConnectRespError(\
134                "Connect response document must contain: \"proxyCert\"" + \
135                " or \"sessCookie\" keywords")
136   
137
138#_____________________________________________________________________________
139class AddUserReqError(XMLMsgError):   
140    """Exception handling for NDG SessionMgr WS connect request class."""
141    pass
142
143
144#_____________________________________________________________________________
145class AddUserReq(XMLMsg):
146    """For client to Session Manager WS addUser(): formats inputs for request
147    into XML and encrypts.
148   
149    For Session Manager enables decryption of result"""
150   
151    # Override base class class variables
152    xmlTagTmpl = {  "userName":    "",
153                    "pPhrase":     "",
154                    "encrCert":    ""    }
155                       
156
157    def __init__(self, **xmlMsgKeys):
158        """XML for sending encrypted credentials to Session Manager connect
159       
160        xmlMsgKeys:    keywords for XMLMsg super-class.  If XML tags are
161                       input as keywords then 'userName' and 'pPhrase'
162                       must be input as the bare minimum required for
163                       SessionMgr addUser request.
164        """
165       
166                               
167        # Check for encrypted text or valid credentials
168        if 'encrXMLtxt' not in xmlMsgKeys and 'xmlTxt' not in xmlMsgKeys:           
169
170            # XML tags input as keywords expected - check minimum
171            # required are present for SessionMgr connect request
172            if 'userName' not in xmlMsgKeys and 'pPhrase' not in xmlMsgKeys:
173                raise AddUserReqError(\
174                    "Credentials must include \"userName\" and \"pPhrase\"")
175               
176        # Allow user credentials to be access like dictionary keys
177        super(AddUserReq, self).__init__(**xmlMsgKeys)
178     
179
180#_____________________________________________________________________________
181class AddUserRespError(XMLMsgError):   
182    """Exception handling for NDG SessionMgr WS addUser response class."""
183    pass
184
185
186#_____________________________________________________________________________
187class AddUserResp(XMLMsg):
188    """For client to Session Manager WS connect(): formats addUser response
189    from SessionMgr.
190   
191    For client, enables decryption of response"""
192   
193    # Override base class class variables
194    xmlTagTmpl = {"errMsg": ""}
195                       
196
197    def __init__(self, **xmlMsgKeys):
198        """XML for returning error status from Session Manager addUser()
199       
200        xmlMsgKeys:    keywords for XMLMsg super-class.  If XML tags are
201                       input as keywords then 'proxyCert' or 'sessCookie'
202                       must be set.
203        """
204               
205        # Allow user credentials to be access like dictionary keys
206        super(AddUserResp, self).__init__(**xmlMsgKeys)
207       
208                               
209        # Check valid output
210        if 'errMsg' not in self:           
211
212            # XML tags input as keywords expected - check minimum
213            # required are present for SessionMgr connect response
214            raise AddUserRespError(\
215                "AddUser response must contain \"errMsg\" keyword")
216       
217
218#_____________________________________________________________________________
219class AuthorisationReqError(XMLMsgError):   
220    """Exception handling for NDG SessionMgr WS authorisation request class.
221    """
222    pass
223
224
225#_____________________________________________________________________________
226class AuthorisationReq(XMLMsg):
227    """For client to Session Manager WS reqAuthorisation(): formats inputs for
228    request into XML and encrypts.
229   
230    Session Manager enables decryption of result"""
231   
232    # Override base class class variables
233    xmlTagTmpl = {  "sessID":                 "",
234                    "encrSessMgrWSDLuri":     "",
235                    "proxyCert":              "",
236                    "aaWSDL":                 "",
237                    "aaCert":               "",
238                    "reqRole":                "",
239                    "mapFromTrustedHosts":    "",
240                    "rtnExtAttCertList":      "",
241                    "extAttCertList":         "",
242                    "extTrustedHostList":     "",
243                    "encrCert":               ""    }
244                   
245    # Regular expressions for parsing AttCerts from XML
246    __attCertPat = re.compile(\
247                    '<attributeCertificate>.*?</attributeCertificate>', re.S)
248                   
249    __extACListPat = re.compile('<extAttCertList>.*</extAttCertList>', re.S)
250
251
252    def __init__(self, **xmlMsgKeys):
253        """XML for sending encrypted credentials to Session Manager
254        authorisation request
255       
256        xmlMsgKeys:    keywords for XMLMsg super-class.  If XML tags are
257                       input as keywords then 'userName' and 'pPhrase' or
258                       'proxyCert' or 'sessID' must be input as the bare
259                       minimum required for SessionMgr connect request.
260        """
261                       
262        # Allow user credentials to be access like dictionary keys
263        super(AuthorisationReq, self).__init__(**xmlMsgKeys)
264
265                               
266        # Check for encrypted text or valid credentials
267        if 'sessID' not in self and 'encrSessMgrWSDLuri' not in self:
268            if 'proxyCert' not in self:
269                raise AuthorisationReqError(\
270                    "Authorisation request must include the credentials: " + \
271                    "\"sessID\" and \"encrSessMgrWSDLuri\" or \"proxyCert\"")
272               
273        if 'aaWSDL' not in self:           
274            raise AuthorisationReqError(\
275                "Authorisation request must include: \"aaWSDL\"")
276
277
278    #_________________________________________________________________________
279    def update(self, extAttCertList=None, **xmlTags):
280        """Override base class implementation to include extra code
281        to allow setting of extAttCertList tag"""
282
283        def setAttCert(attCert=None):
284            if isinstance(attCert, basestring):
285                return AttCertParse(attCert)
286           
287            elif isinstance(attCert, AttCert):
288                return attCert
289           
290            elif attCert is not None:
291                raise TypeError(\
292                    "extAttCertList must contain string or AttCert types")
293
294        if extAttCertList is not None:
295            if isinstance(extAttCertList, list):
296                 
297                # Join into single string and filter out XML headers as 
298                # ElementTree doesn't like these nested into a doc                       
299                extAttCertList = map(setAttCert, extAttCertList)
300           
301            elif extAttCertList == '':
302                extAttCertList = []
303            else:
304                raise TypeError("\"extAttCertList\" must be of list type")
305               
306        # Call super class update with revised attribute certificate list
307        super(AuthorisationReq, self).update(extAttCertList=extAttCertList,
308                                           **xmlTags)
309
310
311    #_________________________________________________________________________
312    def updateXML(self, **xmlTags):
313        """Override base class implementation to include extra code
314        to include extAttCertList tag"""
315       
316        # Update dictionary
317        self.update(**xmlTags)
318
319        # Create XML formatted string ready for encryption
320        try:
321            xmlTxt = self.xmlHdr + os.linesep + \
322                "<" + self.__class__.__name__ + ">" + os.linesep
323               
324            for tag, val in self.items():
325                if isinstance(val, list):
326                    # List of Attribute Certificates from other trusted hosts
327                    #
328                    # Call AttCert parse and return as Element type to append
329                    # as branches
330                    text = os.linesep.join([ac.asString(stripXMLhdr=True) \
331                                            for ac in val]) 
332               
333                elif isinstance(val, bool):
334                    text = "%d" % val
335                   
336                elif val is None:
337                    # Leave out altogether if set to None
338                    continue
339                else:       
340                    text = val
341
342                xmlTxt += "    <%s>%s</%s>%s" % (tag, text, tag, os.linesep)
343               
344            xmlTxt += "</" + self.__class__.__name__ + ">" + os.linesep   
345            self.xmlTxt = xmlTxt
346
347        except Exception, e:
348            raise XMLMsgError("Creating XML: %s" % e)
349
350
351    #_________________________________________________________________________
352    def parseXML(self):
353        """Override base class implementation to include extra code
354        to convert boolean"""
355       
356        super(AuthorisationReq, self).parseXML()
357       
358        if 'mapFromTrustedHosts' in self:
359            self['mapFromTrustedHosts']=bool(int(self['mapFromTrustedHosts']))
360           
361        if 'rtnExtAttCertList' in self: 
362            self['rtnExtAttCertList'] = bool(int(self['rtnExtAttCertList']))
363
364        # Nb. ElementTree parses attCert XML, it adds 'ns0' namespaces to all
365        # the digital signatue elements - this invalidates the signature (!)
366        #
367        # Fudge: re-read in using regular expressions
368                   
369        if 'extAttCertList' in self:
370            if self['extAttCertList'] == '':
371                self['extAttCertList'] = []
372                return
373           
374            try:               
375                # Find <extAttCertList>...</extAttCertList>
376                extACListTxt = \
377                    self.__class__.__extACListPat.findall(self.xmlTxt)[0] 
378               
379                # Split list and convert into AttCert objects             
380                self['extAttCertList'] = \
381                    [AttCertParse(acMat.group()) \
382                     for acMat in \
383                     self.__class__.__attCertPat.finditer(extACListTxt)]
384               
385            except Exception, e:
386                raise AuthorisationRespError(\
387                        "Error parsing Attribute Certificate List: " + str(e))
388
389
390#_____________________________________________________________________________
391class AuthorisationRespError(XMLMsgError):   
392    """Exception handling for NDG SessionMgr WS connect response class."""
393    pass
394
395
396#_____________________________________________________________________________
397class AuthorisationResp(XMLMsg):
398    """For client to Session Manager WS connect(): formats authorisation
399    response from SessionMgr.
400   
401    For client, enables decryption of response"""
402   
403    # Override base class class variables
404    xmlTagTmpl = {  "attCert":           "",
405                    "extAttCertList":    "",
406                    "statCode":          "",
407                    "errMsg":            ""    }
408
409    accessGranted = 'AccessGranted'   
410    accessDenied = 'AccessDenied'
411    accessError = 'AccessError'
412   
413    # Regular expressions for parsing AttCerts from XML
414    __attCertPat = re.compile(\
415                    '<attributeCertificate>.*?</attributeCertificate>', re.S)
416                   
417    __extACListPat = re.compile('<extAttCertList>.*</extAttCertList>', re.S)
418    __acPat = re.compile('<attCert>.*?</attCert>', re.S)
419
420
421    def __init__(self, **xmlMsgKeys):
422        """XML for receiving output from Session Manager authorisation call
423       
424        xmlMsgKeys:    keywords for XMLMsg super-class.  If XML tags are
425                       input as keywords then 'errMsg' or 'statCode'
426                       must be set.
427        """       
428       
429        # Allow user credentials to be access like dictionary keys
430        super(AuthorisationResp, self).__init__(**xmlMsgKeys)
431       
432                               
433        # Check for valid output credentials
434        if 'statCode' not in self:           
435
436            # XML tags input as keywords expected - check minimum
437            # required are present for SessionMgr connect response
438            raise AuthorisationRespError(\
439                "Authorisation response must contain \"statCode\" keyword")
440
441
442    #_________________________________________________________________________
443    def update(self, attCert=None, extAttCertList=None, **xmlTags):
444        """Override base class implementation to include extra code
445        to allow setting of extAttCertList tag"""
446
447        def setAttCert(attCert=None):
448            if isinstance(attCert, basestring):
449                return AttCertParse(attCert)
450           
451            elif isinstance(attCert, AttCert):
452                return attCert
453           
454            elif attCert is not None:
455                raise TypeError(\
456                    "extAttCertList must contain string or AttCert types")
457
458        if extAttCertList is not None:
459            if isinstance(extAttCertList, list):
460                 
461                # Join into single string and filter out XML headers as 
462                # ElementTree doesn't like these nested into a doc                       
463                extAttCertList = map(setAttCert, extAttCertList)
464           
465            elif extAttCertList == '':
466                extAttCertList = []
467            else:
468                raise TypeError("\"extAttCertList\" must be of list type")
469               
470        # Call super class update with revised attribute certificate list
471        super(AuthorisationResp, self).update(attCert=setAttCert(attCert),
472                                              extAttCertList=extAttCertList,
473                                              **xmlTags)
474
475
476    #_________________________________________________________________________
477    def updateXML(self, **xmlTags):
478        """Override base class implementation to include extra code
479        to include extAttCertList tag"""
480       
481        # Update dictionary
482        self.update(**xmlTags)
483       
484        # Create XML formatted string ready for encryption
485        try:
486            xmlTxt = self.xmlHdr + os.linesep + \
487                "<" + self.__class__.__name__ + ">" + os.linesep
488               
489            for tag, val in self.items():
490                if isinstance(val, AttCert):
491                    # Attribute Certificate received from Attribute Authority
492                    #
493                    # Remove any XML header -
494                    # update() call will have converted val to AttCert type
495                    text = val.asString(stripXMLhdr=True)
496                 
497                elif isinstance(val, list):
498                    # List of Attribute Certificates from other trusted hosts
499                    #
500                    # Call AttCert parse and return as Element type to append
501                    # as branches
502                    text = os.linesep.join([ac.asString(stripXMLhdr=True) \
503                                            for ac in val]) 
504               
505                elif isinstance(val, bool):
506                    text = "%d" % val
507                   
508                elif val is None:
509                    # Leave out altogether if set to None
510                    continue
511                else:       
512                    text = val
513
514                xmlTxt += "    <%s>%s</%s>%s" % (tag, text, tag, os.linesep)
515               
516            xmlTxt += "</" + self.__class__.__name__ + ">" + os.linesep   
517            self.xmlTxt = xmlTxt
518
519        except Exception, e:
520            raise XMLMsgError("Creating XML: %s" % e)
521
522
523    #_________________________________________________________________________
524    def parseXML(self):
525        """Override base class implementation to include extra code
526        to parse extAttCertList tag"""
527       
528        super(AuthorisationResp, self).parseXML()
529
530        # Nb. ElementTree parses attCert XML, it adds 'ns0' namespaces to all
531        # the digital signatue elements - this invalidates the signature (!)
532        #
533        # Fudge: re-read in using regular expressions
534                   
535        if 'extAttCertList' in self:
536            if self['extAttCertList'] == '':
537                self['extAttCertList'] = []
538                return
539           
540            try:               
541                # Find <extAttCertList>...</extAttCertList>
542                extACListTxt = \
543                    self.__class__.__extACListPat.findall(self.xmlTxt)[0] 
544               
545                # Split list and convert into AttCert objects             
546                self['extAttCertList'] = \
547                    [AttCertParse(acMat.group()) \
548                     for acMat in \
549                     self.__class__.__attCertPat.finditer(extACListTxt)]
550               
551            except Exception, e:
552                raise AuthorisationRespError(\
553                        "Error parsing Attribute Certificate List: " + str(e))
554                                         
555        # Check for 'attCert' set - if so reset as a single string and convert
556        # to AttCert type. 
557        if 'attCert' in self and self['attCert']:
558            try:
559                acTxt = self.__class__.__acPat.findall(self.xmlTxt)[0]
560               
561                self['attCert'] = \
562                AttCertParse(self.__class__.__attCertPat.findall(acTxt)[0])
563               
564            except Exception, e:
565                raise AuthorisationRespError(\
566                    "Error parsing Attribute Certificate: " + str(e)) 
567 
568
569#_____________________________________________________________________________
570class CertReqError(XMLMsgError):   
571    """Exception handling for NDG SessionMgr WS getCert request class."""
572    pass
573
574
575#_____________________________________________________________________________
576class PubKeyReq(XMLMsg):
577    """For client to Session Manager WS getPubKey(): formats inputs for
578    request into XML"""   
579    pass
580
581
582#_____________________________________________________________________________
583class PubKeyRespError(XMLMsgError):   
584    """Exception handling for NDG SessionMgr WS getPubKey response class."""
585    pass
586
587
588#_____________________________________________________________________________
589class PubKeyResp(XMLMsg):
590    """For client to Session Manager WS getPubKey(): formats getPubKey
591    response from SessionMgr"""
592   
593    # Override base class class variables
594    xmlTagTmpl = {"cert": "", "errMsg": ""}
595                       
596
597    def __init__(self, **xmlMsgKeys):
598        """XML for receiving credentials from Session Manager getPubKey call
599       
600        xmlMsgKeys:    keywords for XMLMsg super-class.  If XML tags are
601                       input as keywords then 'proxyCert' or 'sessCookie'
602                       must be set.                       
603        """       
604       
605        # Allow user credentials to be access like dictionary keys
606        super(PubKeyResp, self).__init__(**xmlMsgKeys)
607       
608                               
609        # Check for valid output credentials
610        # XML tags input as keywords expected - check minimum
611        # required are present for SessionMgr getPubKey response
612        if 'cert' not in self and 'errMsg' not in self:           
613            raise PubKeyRespError(\
614                "PubKey response document must contain: \"cert\"" + \
615                " or \"errMsg\" keywords")
616   
Note: See TracBrowser for help on using the repository browser.