source: TI12-security/trunk/python/ndg.security.common/ndg/security/common/SessionMgr/__init__.py @ 2746

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

ndg.security.server/ndg/security/server/conf/sessionMgrProperties.xml:

  • don't comment out hostname instead include by default

ndg.security.server/ndg/security/server/SessionMgr/init.py:

  • fixed comment typo

ndg.security.server/ndg/security/server/MyProxy.py:

to prevent setting of OpenSSL config file without the required file name and
directory path.

ndg.security.test/ndg/security/test/AttCert/attCertTest.cfg,
ndg.security.test/ndg/security/test/AttCert/AttCertTest.py:

  • fixed unit tests for AC signature verification. certFilePathList can now

be set to include CA certs. to verify the X.509 cert. used in the signature

ndg.security.test/ndg/security/test/SessionMgr/SessionMgrClientTest.py:

  • fix: extAttCertList is no longer returned in getAttCert calls to SM client.

ndg.security.test/ndg/security/test/SessionMgr/sessionMgrClientTest.cfg:

  • tests with services on glue

ndg.security.common/ndg/security/common/XMLSec.py:

  • fixed verifyEnvelopedSignature so that it is now possible to verify the

X.509 cert. in the signature against it's issuing CA cert.

ndg.security.common/ndg/security/common/SessionMgr/init.py:

  • modified getAttCert call so that extAttCertList is no longer passed back in

the returned tuple but is instead included as an attribute of the
AttributeRequestDenied? exception type.

  • updated pydoc for getAttCert method

ndg.security.common/ndg/security/common/AttAuthority/init.py:

  • typo fix - doesn't affect execution

ndg.security.common/ndg/security/common/CredWallet.py:

  • updates to getAttCert call pydoc
  • and getAttCert exception handling
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1#!/usr/bin/env python
2"""NDG Security client - client interface classes to Session Manager
3
4Make requests for authentication and authorisation
5
6NERC Data Grid Project
7
8P J Kershaw 24/04/06
9
10Copyright (C) 2006 CCLRC & NERC
11
12This software may be distributed under the terms of the Q Public License,
13version 1.0 or later.
14"""
15__all__ = ['SessionMgr_services', 'SessionMgr_services_types']
16
17import sys
18import os
19
20# Determine https http transport
21import urlparse
22
23from ZSI.wstools.Utility import HTTPResponse
24
25from ndg.security.common.SessionCookie import SessionCookie
26from ndg.security.common.wsSecurity import SignatureHandler
27from ndg.security.common.X509 import *
28from ndg.security.common.AttCert import AttCert, AttCertParse
29from ndg.security.common.m2CryptoSSLUtility import HTTPSConnection, \
30    HostCheck
31from SessionMgr_services import SessionMgrServiceLocator
32
33
34#_____________________________________________________________________________
35class SessionMgrClientError(Exception):
36    """Exception handling for SessionMgrClient class"""
37
38#_____________________________________________________________________________
39class AttributeRequestDenied(Exception):
40    """Raise when a getAttCert call to the Attribute Authority is denied"""
41   
42    def __init__(self, msg=None, extAttCertList=[]):
43        """Raise exception for attribute request denied with option to give
44        caller hint to certificates that could used to try to obtain a
45        mapped certificate
46       
47        @type msg: string
48        @keyword msg: error message
49       
50        @type extAttCertList: list
51        @keyword extAttCertList: list of candidate Attribute Certificates that
52        could be used to try to get a mapped certificate from the target
53        Attribute Authority"""
54
55        self.__msg = msg
56       
57        # Prevent None type setting
58        self.__extAttCertList = []
59        if extAttCertList is not None:
60            for ac in extAttCertList:
61                if isinstance(ac, basestring):
62                    ac = AttCertParse(ac)
63                elif not isinstance(ac, AttCert):
64                    raise SessionMgrClientError, \
65                        "Input external Attribute Cert. must be AttCert type"
66                         
67                self.__extAttCertList += [ac]
68       
69    def __str__(self):
70        return self.__msg
71
72    def __getMsg(self):
73        """Get message text"""
74        return self.__msg
75
76    msg = property(fget=__getMsg, doc="Error message text")
77       
78    def __getExtAttCertList(self):
79        """Return list of candidate Attribute Certificates that could be used
80        to try to get a mapped certificate from the target Attribute Authority
81        """
82        return self.__extAttCertList
83
84    extAttCertList = property(fget=__getExtAttCertList,
85                              doc="list of candidate Attribute " + \
86                              "Certificates that could be used " + \
87                              "to try to get a mapped certificate " + \
88                              "from the target Attribute Authority")
89
90#_____________________________________________________________________________       
91class SessionMgrClient(object):
92    """Client interface to Session Manager Web Service"""
93   
94    #_________________________________________________________________________
95    def __init__(self, 
96                 uri=None, 
97                 tracefile=None,
98                 sslCACertList=[],
99                 sslCACertFilePathList=[],
100                 sslPeerCertCN=None, 
101                 setSignatureHandler=True,
102                 **signatureHandlerKw):
103        """
104        @type uri: string
105        @keyword uri: URI for Session Manager WS.  Setting it will set the
106        Service Proxy
107               
108        @type tracefile: file stream type
109        @param tracefile: set to file object such as sys.stderr to give extra
110        WS debug information
111       
112        @type sslCACertList: list
113        @keyword sslCACertList: This keyword is for use with SSL connections
114        only.  Set a list of one or more CA certificates.  The peer cert.
115        must verify against at least one of these otherwise the connection
116        is dropped.
117       
118        @type sslCACertFilePathList: list
119        @keyword sslCACertFilePathList: the same as the above except CA certs
120        can be passed as a list of file paths to read from
121       
122        @type sslPeerCertCN: string
123        @keyword sslPeerCertCN: set an alternate CommonName to match with peer
124        cert.  This keyword is for use with SSL connections only.
125       
126        @type setSignatureHandler: bool
127        @param setSignatureHandler: flag to determine whether to apply
128        WS-Security Signature Handler or not
129
130        @type signatureHandlerKw: dict
131        @param signatureHandlerKw: keywords to configure signature handler"""
132
133        self.__srv = None
134        self.__uri = None
135        self._transdict = {}       
136       
137        if uri:
138            self.__setURI(uri)
139
140        if sslPeerCertCN:
141            self.__setSSLPeerCertCN(sslPeerCertCN)
142       
143        if sslCACertList:
144            self.__setSSLCACertList(sslCACertList)
145        elif sslCACertFilePathList:
146            self.__setSSLCACertFilePathList(sslCACertFilePathList)
147
148        # WS-Security Signature handler - set only if any of the keywords were
149        # set
150        if setSignatureHandler:
151            self.__signatureHandler = SignatureHandler(**signatureHandlerKw)
152        else:
153            self.__signatureHandler = None
154       
155        self.__tracefile = tracefile
156
157         
158        # Instantiate Session Manager WS ZSI client
159        if self.__uri:
160            self.initService()
161       
162
163    #_________________________________________________________________________
164    def __setURI(self, uri):
165        """Set URI for service
166        @type uri: string
167        @param uri: URI for service to connect to"""
168       
169        if not isinstance(uri, basestring):
170            raise SessionMgrClientError, \
171                             "Session Manager WSDL URI must be a valid string"
172       
173        self.__uri = uri
174        try:
175            scheme = urlparse.urlparse(self.__uri)[0]
176        except TypeError:
177            raise AttributeAuthorityClientError, \
178                "Error parsing transport type from URI"
179               
180        if scheme == "https":
181            self._transport = HTTPSConnection
182        else:
183            self._transport = None
184           
185            # Ensure SSL settings are cancelled
186            self.__setSSLPeerCertCN(None)
187       
188    uri = property(fset=__setURI, doc="Set Session Manager URI")
189
190
191    #_________________________________________________________________________
192    def __setSSLPeerCertCN(self, cn):
193        """For use with HTTPS connections only.  Specify the Common
194        Name to match with Common Name of the peer certificate.  This is not
195        needed if the peer cert CN = peer hostname"""
196        if self._transport != HTTPSConnection:
197            return
198       
199        if self._transdict.get('postConnectionCheck'):
200            self._transdict['postConnectionCheck'].peerCertCN = cn
201        else:
202            self._transdict['postConnectionCheck'] = HostCheck(peerCertCN=cn)
203
204    sslPeerCertCN = property(fset=__setSSLPeerCertCN, 
205doc="for https connections, set CN of peer cert if other than peer hostname")
206
207
208    #_________________________________________________________________________
209    def __setSSLCACertList(self, caCertList):
210        """For use with HTTPS connections only.  Specify CA certs to one of
211        which the peer cert must verify its signature against"""
212        if self._transport != HTTPSConnection:
213            return
214       
215        if self._transdict.get('postConnectionCheck'):
216            self._transdict['postConnectionCheck'].caCertList = caCertList
217        else:
218            self._transdict['postConnectionCheck'] = \
219                                            HostCheck(caCertList=caCertList)
220
221    sslCACertList = property(fset=__setSSLCACertList, 
222doc="for https connections, set list of CA certs from which to verify peer cert")
223
224
225    #_________________________________________________________________________
226    def __setSSLCACertFilePathList(self, caCertFilePathList):
227        """For use with HTTPS connections only.  Specify CA certs to one of
228        which the peer cert must verify its signature against"""
229        if self._transport != HTTPSConnection:
230            return
231       
232        if self._transdict.get('postConnectionCheck'):
233            self._transdict['postConnectionCheck'].caCertFilePathList = \
234                                            caCertFilePathList
235        else:
236            self._transdict['postConnectionCheck'] = \
237                            HostCheck(caCertFilePathList=caCertFilePathList)
238
239    sslCACertFilePathList = property(fset=__setSSLCACertFilePathList, 
240                                     doc=\
241"for https connections, set list of CA cert files from which to verify peer cert")
242
243
244    #_________________________________________________________________________
245    def __setSignatureHandler(self, signatureHandler):
246        """Set SignatureHandler object property method - set to None to for no
247        digital signature and verification"""
248        if signatureHandler is not None and \
249           not isinstance(signatureHandler, SignatureHandler):
250            raise AttributeError, \
251    "Signature Handler must be %s type or None for no message security" % \
252        "ndg.security.common.wsSecurity.SignatureHandler"
253                           
254        self.__signatureHandler = signatureHandler
255
256
257    #_________________________________________________________________________
258    def __getSignatureHandler(self):
259        "Get SignatureHandler object property method"
260        return self.__signatureHandler
261   
262    signatureHandler = property(fget=__getSignatureHandler,
263                                fset=__setSignatureHandler,
264                                doc="SignatureHandler object")
265   
266       
267    #_________________________________________________________________________
268    def initService(self, uri=None):
269        """Set the WS client for the Session Manager"""
270        if uri:
271            self.__setURI(uri)
272   
273        # WS-Security Signature handler object is passed to binding
274        try:
275            locator = SessionMgrServiceLocator()
276            self.__srv = locator.getSessionMgr(self.__uri,
277                                       sig_handler=self.__signatureHandler,
278                                       tracefile=self.__tracefile,
279                                         transport=self._transport,
280                                         transdict=self._transdict)
281        except HTTPResponse, e:
282            raise SessionMgrClientError, \
283                "Initialising Service for \"%s\": %s %s" % \
284                (self.__uri, e.status, e.reason)
285
286                                   
287    #_________________________________________________________________________
288    def addUser(self,
289                username,
290                passphrase=None,
291                passphraseFilePath=None,
292                clntPriKeyPwd=None):
293        """Register a new user
294       
295        username:                the username for the new user
296        passphrase:                 user's pass-phrase
297        passphraseFilePath:         a file containing the user's pass-phrase. 
298                                 Use this as an alternative to passphrase keyword
299        clntPriKeyPwd:           pass-phrase if any for the client's private
300                                 key used to decrypt response from
301                                 Session Manager
302        """
303   
304        if passphrase is None:
305            try:
306                passphrase = open(passphraseFilePath).read().strip()
307           
308            except Exception, e:
309                raise SessionMgrClientError, "Pass-phrase not defined: " + \
310                                            str(e)
311           
312   
313        # Make request for new user
314        try:   
315            self.__srv.addUser(username, passphrase)
316
317        except Exception, e:
318            raise SessionMgrClientError, "Adding new user: " + str(e)
319   
320       
321    #_________________________________________________________________________   
322    def connect(self,
323                username,
324                passphrase=None,
325                passphraseFilePath=None,
326                createServerSess=True):
327        """Request a new user session from the Session Manager
328       
329        @type username: string
330        @param username: the username of the user to connect
331       
332        @type passphrase: string
333        @keyword passphrase: user's pass-phrase
334       
335        @type passphraseFilePath: string
336        @keyword passphraseFilePath: a file containing the user's pass-phrase. 
337        Use this as an alternative to passphrase keyword.
338                                 
339        @type createServerSess: bool
340        @keyword createServerSess: If set to True, the SessionMgr will create
341        and manage a session for the user.  For non-browser client case, it's
342        possible to choose to have a client or server side session using this
343        keyword.  If set to False sessID returned will be None
344       
345        @rtype: tuple
346        @return proxy cert, proxy private key, user cert and sessID all as
347        strings but sessID will be None if the createServerSess keyword is
348        False"""
349   
350        if passphrase is None:
351            try:
352                passphrase = open(passphraseFilePath).read().strip()
353           
354            except Exception, e:
355                raise SessionMgrClientError, "Pass-phrase not defined: " + \
356                                             str(e)
357
358        # Make connection
359        res = self.__srv.connect(username, passphrase, createServerSess)
360
361        # Convert from unicode because unicode causes problems with
362        # M2Crypto private key load
363        return tuple([isinstance(i,unicode) and str(i) or i for i in res])
364   
365       
366    #_________________________________________________________________________   
367    def disconnect(self, userCert=None, sessID=None):
368        """Delete an existing user session from the Session Manager
369       
370        disconnect([userCert=c]|[sessID=i])
371       
372        @type userCert: string                 
373        @keyword userCert: user's certificate used to identifier which session
374        to disconnect.  This arg is not needed if the message is signed with
375        the user cert or if sessID is set. 
376                               
377        @type sessID: string
378        @keyword sessID: session ID.  Input this as an alternative to userCert
379        This arg is not needed if the message is signed with the user cert or
380        if userCert keyword is."""
381
382        # Make connection
383        self.__srv.disconnect(userCert, sessID)
384   
385   
386    #_________________________________________________________________________
387    def getAttCert(self,
388                   proxyCert=None,
389                   sessID=None,
390                   attAuthorityURI=None,
391                   attAuthorityCert=None,
392                   reqRole=None,
393                   mapFromTrustedHosts=True,
394                   rtnExtAttCertList=False,
395                   extAttCertList=[],
396                   extTrustedHostList=[]):   
397        """Request NDG Session Manager Web Service to retrieve an Attribute
398        Certificate from the given Attribute Authority and cache it in the
399        user's credential wallet held by the session manager.
400       
401        ac = getAttCert([sessID=i]|[proxyCert=p][key=arg, ...])
402         
403        @raise AttributeRequestDenied: this is raised if the request is
404        denied because the user is not registered with the Attribute
405        Authority.  In this case, a list of candidate attribute certificates
406        may be returned which could be used to retry with a request for a
407        mapped AC.  These are assigned to the raised exception's
408        extAttCertList attribute
409             
410        @type proxyCert: string
411        @keyword proxyCert: proxy certificate - use as ID instead of session
412        ID.  This can be omitted if the message is signed with a proxy
413        certificate.  In this case the proxy certificate is passed in the
414        BinarySecurityToken of the WS-Security header
415       
416        @type sessID: string
417        @keyword sessID: session ID.  Input this as an alternative to
418        proxyCert in the case of a browser client.
419       
420        @type attAuthorityURI: string
421        @keyword attAuthorityURI: URI for Attribute Authority WS.
422       
423        @type attAuthorityCert: string
424        @keyword attAuthorityCert: The Session Manager uses the Public key of
425        the Attribute Authority to encrypt requests to it.
426       
427        @type reqRole: string
428        @keyword reqRole: The required role for access to a data set.  This
429        can be left out in which case the Attribute Authority just returns
430        whatever Attribute Certificate it has for the user
431       
432        @type mapFromTrustedHosts: bool
433        @keyword mapFromTrustedHosts: Allow a mapped Attribute Certificate to
434        be created from a user certificate from another trusted host.
435       
436        @type rtnExtAttCertList: bool
437        @keyword rtnExtAttCertList: Set this flag True so that if the
438        attribute request is denied, a list of potential attribute
439        certificates for mapping may be returned.
440       
441        @type extAttCertList: list
442        @keyword extAttCertList: A list of Attribute Certificates from other
443        trusted hosts from which the target Attribute Authority can make a
444        mapped certificate
445       
446        @type extTrustedHostList: list
447        @keyword extTrustedHostList: A list of trusted hosts that can be used
448        to get Attribute Certificates for making a mapped AC.
449       
450        @rtype: ndg.security.common.AttCert.AttCert
451        @return: if successful, an attribute certificate."""
452       
453        # Make request
454        attCert, msg, extAttCertList = self.__srv.getAttCert(proxyCert,
455                                                       sessID, 
456                                                       attAuthorityURI,
457                                                       attAuthorityCert,
458                                                       reqRole,
459                                                       mapFromTrustedHosts,
460                                                       rtnExtAttCertList,
461                                                       extAttCertList,
462                                                       extTrustedHostList)
463        if not attCert:
464            raise AttributeRequestDenied, msg, extAttCertList
465       
466        return AttCertParse(attCert)
467   
468                                   
469    #_________________________________________________________________________
470    def getX509Cert(self):
471        """Retrieve the public key of the Session Manager"""
472        return self.__srv.getX509Cert()
473                           
Note: See TracBrowser for help on using the repository browser.