source: TI12-security/trunk/python/NDG/SessionClient.py @ 737

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

ndgSetup.sh: fixed slight typo.

mapConfig.xml: added pubKey tag to allow client to Attribute Authority to use it to encrypt
outbound messages to it.

ndgSessionClient.py:

  • include code to set public key of Attribute Authority so that Session Manager can encrypt

messages to it.

  • -r/--req-autho option now requires the AA WSDL URI. -a is now used to set the AA pub key
  • see previous point.

AttAuthorityIO.py:

  • Changed tag 'clntCert' to 'encrCert' so as to be consistent with SessionMgrIO.py code.

attAuthority_services_server.py:

  • Moved encrypt/decrypt code here from AttAuthority? class to be consistent with

sessionMgr_services_server.py.

AttAuthority?.py:

  • Now inherits from dict to allow convenient access to properties file parameters as dictionary

items.

  • Added code to include pubKey tag from mapConfig file in trustedHostInfo returned from

getTrustedHostInfo.

SessionMgrIO.py:

output XML.

  • Shifted test code into separate file in Tests/

SessionClient?.py:

  • Added aaPubKey to reqAuthorisation method - see above re. passing AA public key for

encryption of messages.

sessionMgr_services_server.py:

  • Changes to comments.

Session.py:

private key info of client to allow encrypt of responses from other WSs that SessionMgr? calls.
These are actually passed into CredWallet? instance of UserSession?.

  • AA Public key is passed into reqAuthorisation. This is written to a temp file for use by

XMLSec encryption code.

CredWallet?.py:

  • CredWalletAuthorisationDenied? - make sure extAttCertList gets set to []
  • Added pub/private functionality for encryption of messages to and from Attribute Authorities.
  • reqAuthorisation and getAATrustedHostInfo methods - read in client public key using

straight open/read: using X509Cert.asString() misses out the actual MIME encoded cert text(!)

  • Changed reqAuthorisation() - a required role is now optional with mapFromTrustedHosts flag set.

It does help though with finding a suitable AttCert? for mapping.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1#!/usr/bin/env python
2
3"""NDG Session client - makes requests for authentication and authorisation
4
5NERC Data Grid Project
6
7P J Kershaw 16/08/05
8
9Copyright (C) 2005 CCLRC & NERC
10
11This software may be distributed under the terms of the Q Public License,
12version 1.0 or later.
13"""
14
15cvsID = '$Id$'
16
17from ZSI import ServiceProxy
18import sys
19import os
20from Cookie import SimpleCookie
21
22from X509 import *
23from SessionMgrIO import *
24
25# Handle retrieval of public key cert for Session Manager at remote location
26import tempfile
27import urllib
28
29#_____________________________________________________________________________
30class SessionClientError(Exception):
31    """Exception handling for SessionClient class"""
32    def __init__(self, msg):
33        self.__msg = msg
34         
35    def __str__(self):
36        return self.__msg
37
38
39#_____________________________________________________________________________       
40class SessionClient(object):
41    """Client interface to Session Manager Web Service"""
42   
43    #_________________________________________________________________________
44    def __init__(self, 
45                 smWSDL=None, 
46                 smPubKeyURI=None,
47                 clntPubKeyFilePath=None,
48                 clntPriKeyFilePath=None,
49                 traceFile=None):
50        """
51        smWSDL:                  WSDL URI for Session Manager WS.  Setting it
52                                 will set the Service Proxy
53        smPubKeyURI:   
54                                 Public key of Session Manager used to encrypt
55                                 the outgoing message if required - set as a
56                                 path on the local file system or as a URI
57        clntPubKeyFilePath:      Public key of client.  This is passed to the
58                                 Session Manager so that it can encrypt
59                                 responses.  WARNING: if not set, responses
60                                 are returned as clear text
61        clntPriKeyFilePath:      Private key of client.  If clntPubKeyFilePath
62                                 is set, the private key is needed to decrypt
63                                 the response from the Session Manager
64        traceFile:               set to file object such as sys.stderr to
65                                 give extra WS debug information"""
66
67        self.__smSrv = None
68        self.__smWSDL = None
69        self.__smPubKeyFilePath = None
70        self.__smPubKeyURI = None
71        self.__clntPubKeyFilePath = None
72        self.__clntPubKey = None
73        self.__clntPriKeyFilePath = None
74       
75        self.__smPubKeyTempFile = None
76       
77       
78        if smWSDL:
79            self.__setSMwsdl(smWSDL)
80           
81        if smPubKeyURI:
82            self.__setSMpubKeyURI(smPubKeyURI)
83           
84        if clntPriKeyFilePath:
85            self.__setClntPriKeyFilePath(clntPriKeyFilePath)
86           
87        if clntPubKeyFilePath:
88            if clntPriKeyFilePath is None:
89                raise SessionClientError(\
90                    "A Client private key file is required as well a " + \
91                    "public key")
92                   
93            self.__setClntPubKeyFilePath(clntPubKeyFilePath)
94
95           
96        self.__traceFile = traceFile
97
98         
99        # Instantiate Session Manager WS proxy
100        if self.__smWSDL:
101            self.serviceProxy()
102       
103
104    #_________________________________________________________________________
105    def __setSMwsdl(self, smWSDL):
106       
107        if not isinstance(smWSDL, basestring):
108            raise SessionClientError(\
109                            "Session Manager WSDL URI must be a valid string")
110       
111        self.__smWSDL = smWSDL
112       
113    smWSDL = property(fset=__setSMwsdl, doc="Set Session Manager WSDL URI")
114
115
116    #_________________________________________________________________________
117    def __setSMpubKeyURI(self, smPubKeyURI):
118       
119        if not isinstance(smPubKeyURI, basestring):
120            raise SessionClientError(\
121                "Session Manager public key URI must be a valid string")
122       
123        self.__smPubKeyURI = smPubKeyURI
124        self.__smPubKeyFilePath = None
125       
126    smPubKeyURI = property(fset=__setSMpubKeyURI,
127                           doc="Set Session Manager public key URI")
128
129 
130    #_________________________________________________________________________
131    def __setClntPubKeyFilePath(self, clntPubKeyFilePath):
132       
133        if not isinstance(clntPubKeyFilePath, basestring):
134            raise SessionClientError(\
135                "Client public key file path must be a valid string")
136       
137        self.__clntPubKeyFilePath = clntPubKeyFilePath
138        try:
139            self.__clntPubKey = open(self.__clntPubKeyFilePath).read()
140           
141        except IOError, (errNo, errMsg):
142            raise optparse.OptionValueError(\
143                    "Reading certificate file \"%s\": %s" % (value, errMsg))
144                               
145        except Exception, e:
146            raise optparse.OptionValueError(\
147                    "Reading certificate file \"%s\": %s" % (value, str(e)))
148       
149    clntPubKeyFilePath = property(fset=__setClntPubKeyFilePath,
150                                  doc="File path for client public key")
151
152 
153    #_________________________________________________________________________
154    def __setClntPriKeyFilePath(self, clntPriKeyFilePath):
155       
156        if not isinstance(clntPriKeyFilePath, basestring):
157            raise SessionClientError(\
158                "Client public key file path must be a valid string")
159       
160        self.__clntPriKeyFilePath = clntPriKeyFilePath
161       
162    clntPriKeyFilePath = property(fset=__setClntPriKeyFilePath,
163                                  doc="File path for client private key")
164
165
166    #_________________________________________________________________________
167    def __convSMpubKeyURI2File(self):
168        """Retrieve the public key from the URI"""
169       
170        # Don't proceed unless URI was set - user may have set public key via
171        # smPubKeyFilePath instead
172        if self.__smPubKeyURI is None:
173            return
174       
175        # If no http prefix, assume a local file
176        if self.__smPubKeyURI[:5] != "http:":
177            self.__smPubKeyFilePath = self.__smPubKeyURI
178            return
179       
180        try:
181            self.__smPubKeyTempFile = tempfile.NamedTemporaryFile()
182           
183            (self.__smPubKeyFilePath, httpResp) = \
184                            urllib.urlretrieve(self.__smPubKeyURI,
185                                               self.__smPubKeyTempFile.name)
186                                         
187        except Exception, e:
188            raise SessionClientError("Error retrieving Session Manager "+\
189                                     "public key from \"%s\": %s" % \
190                                     (self.__smPubKeyURI, str(e)))
191
192        # Expecting plain text format for returned public key file
193        # 404 error would come back as 'text/html'
194        if 'text/plain' not in httpResp['Content-type']:
195            raise SessionClientError("Error retrieving Session Manager "+\
196                "public key from \"%s\": expecting \"plain/text\"" % \
197                self.__smPubKeyURI)
198   
199       
200    #_________________________________________________________________________
201    def serviceProxy(self, smWSDL=None):
202        """Set the WS proxy for the Session Manager"""
203        if smWSDL:
204            self.__setSMwsdl(smWSDL)
205
206        try:
207            self.__smSrv = ServiceProxy(self.__smWSDL, 
208                                        use_wsdl=True, 
209                                        tracefile=self.__traceFile)
210        except Exception, e:
211            raise SessionClientError("Initialising WSDL Service Proxy: " + \
212                                     str(e))
213
214                                   
215    #_________________________________________________________________________
216    def addUser(self,
217                userName,
218                pPhrase=None,
219                pPhraseFilePath=None,
220                clntPriKeyPwd=None):
221        """Register a new user
222       
223        userName:                the username for the new user
224        pPhrase:                 user's pass-phrase
225        pPhraseFilePath:         a file containing the user's pass-phrase. 
226                                 Use this as an alternative to pPhrase keyword
227        clntPriKeyPwd:           pass-phrase if any for the client's private
228                                 key used to decrypt response from
229                                 Session Manager
230        """
231   
232        if pPhrase is None:
233            try:
234                pPhrase = open(pPhraseFilePath).read().strip()
235           
236            except Exception, e:
237                raise SessionClientError("Pass-phrase not defined: " + str(e))
238
239
240        # If Public key was set by URI, retrieve to local temp file
241        self.__convSMpubKeyURI2File()
242           
243   
244        # Make request for new user
245        try:   
246            addUserReq = AddUserReq(userName=userName, 
247                                pPhrase=pPhrase,
248                                encrCert=self.__clntPubKey,
249                                encrPubKeyFilePath=self.__smPubKeyFilePath) 
250
251            # Pass encrypted request
252            resp = self.__smSrv.addUser(addUserReq=addUserReq())
253                       
254            addUserResp = AddUserResp(xmlTxt=resp['addUserResp'],
255                                encrPriKeyFilePath=self.__clntPriKeyFilePath,
256                                encrPriKeyPwd=clntPriKeyPwd)
257                           
258            if 'errMsg' in addUserResp and addUserResp['errMsg']:
259                raise SessionClientError(addUserResp['errMsg'])
260           
261        except Exception, e:
262            raise SessionClientError("Error adding new user: " + str(e))
263   
264       
265    #_________________________________________________________________________   
266    def connect(self,
267                userName,
268                pPhrase=None,
269                pPhraseFilePath=None,
270                getCookie=True,
271                createServerSess=False,
272                clntPriKeyPwd=None):
273        """Request a new user session from the Session Manager
274       
275        userName:                the username of the user to connect
276        pPhrase:                 user's pass-phrase
277        pPhraseFilePath:         a file containing the user's pass-phrase. 
278                                 Use this as an alternative to pPhrase
279                                 keyword.
280                                 
281        getCookie:               If set to true, return a cookie to be set in
282                                 a web browser client.  Otherwise, return a
283                                 proxy certificate.
284                                 
285        createServerSess:        If set to True, the SessionMgr will create
286                                 and manage a session for the user but note,
287                                 this flag is ignored and set to True if
288                                 getCookie is set. 
289                                 
290                                 For command line case, where getCookie is
291                                 False, it's possible to choose to have a
292                                 client or server side session using this
293                                 keyword.
294        clntPriKeyPwd:           pass-phrase if any for the client's private
295                                 key used to decrypt response from
296                                 Session Manager."""
297   
298        if pPhrase is None:
299            try:
300                pPhrase = open(pPhraseFilePath).read().strip()
301           
302            except Exception, e:
303                raise SessionClientError("Pass-phrase not defined: " + str(e))
304
305
306        # If Public key was set by URI, retrieve otherwise get from
307        # smPubKeyFilePath
308        self.__convSMpubKeyURI2File()
309
310       
311        # Make connection
312        try: 
313            connectReq = ConnectReq(userName=userName, 
314                                pPhrase=pPhrase,
315                                getCookie=getCookie,
316                                createServerSess=createServerSess,
317                                encrCert=self.__clntPubKey,
318                                encrPubKeyFilePath=self.__smPubKeyFilePath) 
319   
320            # Pass encrypted request
321            resp = self.__smSrv.connect(connectReq=connectReq())
322           
323            connectResp = ConnectResp(xmlTxt=resp['connectResp'],
324                                encrPriKeyFilePath=self.__clntPriKeyFilePath,
325                                encrPriKeyPwd=clntPriKeyPwd)
326                           
327            if 'errMsg' in connectResp and connectResp['errMsg']:
328                raise Exception(connectResp['errMsg'])
329           
330            if 'sessCookie' in connectResp:
331                return connectResp['sessCookie']
332           
333            elif 'proxyCert' in connectResp:
334                return connectResp['proxyCert']
335           
336            else:
337               raise SessionClientError(\
338               "Neither \"sessCookie\" or \"proxyCert\" found in response")
339               
340        except Exception, e:
341            raise SessionClientError(\
342                            "Error connecting to Session Manager: " + str(e))
343   
344   
345    #_________________________________________________________________________
346    def reqAuthorisation(self,
347                         sessCookie=None,
348                         proxyCert=None,
349                         aaWSDL=None,
350                         aaPubKey=None,
351                         reqRole=None,
352                         mapFromTrustedHosts=False,
353                         rtnExtAttCertList=False,
354                         extAttCertList=None,
355                         extTrustedHostList=None,
356                         clntPriKeyPwd=None):   
357        """Request authorisation from NDG Session Manager Web Service.
358       
359        sessCookie:            session cookie returned from call to connect()
360                               for a browser client.  Input as a string or
361                               SimpleCookie type.
362        proxyCert:             proxy certificate - use as ID instead of
363                               a cookie in the case of a command line client.
364        aaWSDL:                WSDL URI for Attribute Authority WS.
365        aaPubKey:              The Session Manager uses the Public key of the
366                               Attribute Authority to encrypt requests to it.
367        reqRole:               The required role for access to a data set.
368                               This can be left out in which case the
369                               Attribute Authority just returns whatever
370                               Attribute Certificate it has for the user
371        mapFromTrustedHosts:   Allow a mapped Attribute Certificate to be
372                               created from a user certificate from another
373                               trusted host.
374        rtnExtAttCertList:     Set this flag True so that if authorisation is
375                               denied, a list of potential attribute
376                               certificates for mapping may be returned.
377        extAttCertList:        A list of Attribute Certificates from other
378                               trusted hosts from which the target Attribute
379                               Authority can make a mapped certificate
380        extTrustedHostList:    A list of trusted hosts that can be used to
381                               get Attribute Certificates for making a mapped
382                               AC.
383        clntPriKeyPwd:         pass-phrase if any for the client's private
384                               key used to decrypt response from
385                               Session Manager.
386        """
387
388        if sessCookie and isinstance(sessCookie, basestring):
389            try:
390                sessCookie = SimpleCookie(sessCookie)
391            except Exception, e:
392                raise SessionClientError("Error parsing session cookie: " + \
393                                         str(e))
394
395
396        # If Public key was set by URI, retrieve otherwise get from
397        # smPubKeyFilePath
398        self.__convSMpubKeyURI2File()
399
400           
401        # Make authorisation request
402        try:
403            authReq = AuthorisationReq(aaWSDL=aaWSDL,
404                             aaPubKey=aaPubKey,
405                             sessID=sessCookie['NDG-ID1'].value, 
406                             encrSessMgrWSDLuri=sessCookie['NDG-ID2'].value,
407                             encrSessMgrPubKeyURI=sessCookie['NDG-ID3'].value,
408                             proxyCert=proxyCert,
409                             reqRole=reqRole,
410                             mapFromTrustedHosts=mapFromTrustedHosts,
411                             rtnExtAttCertList=rtnExtAttCertList,
412                             extAttCertList=extAttCertList,
413                             extTrustedHostList=extTrustedHostList,
414                             encrCert=self.__clntPubKey,
415                             encrPubKeyFilePath=self.__smPubKeyFilePath) 
416                                           
417            resp = self.__smSrv.reqAuthorisation(authorisationReq=authReq())
418           
419            authResp = AuthorisationResp(xmlTxt=resp['authorisationResp'],
420                                encrPriKeyFilePath=self.__clntPriKeyFilePath,
421                                encrPriKeyPwd=clntPriKeyPwd)
422            return authResp
423           
424        except Exception, e:
425            raise SessionClientError(\
426                                "Error in authorisation request: " + str(e))
Note: See TracBrowser for help on using the repository browser.