source: TI12-security/trunk/python/ndg.security.server/ndg/security/server/SessionMgr/__init__.py @ 2136

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

python/ndg.security.server/setup.py:

  • comment out Twisted from install - won't do egg install
  • updated long description

python/ndg.security.server/ndg/security/server/AttAuthority/server-config.tac:

  • added verifyingCertFilePath keyword to SignatureHandler? initialisation
  • added SSL capability

python/conf/attAuthorityProperties.xml,
python/ndg.security.test/ndg/security/test/AttAuthority/siteAAttAuthorityProperties.xml,
python/ndg.security.test/ndg/security/test/AttAuthority/siteBAttAuthorityProperties.xml,
python/ndg.security.server/ndg/security/server/AttAuthority/init.py:
added element names for reading SSL settings from properties file.

python/ndg.security.server/ndg/security/server/SessionMgr/server-config.tac:
added verifyingCertFilePath keyword to SignatureHandler? initialisation

python/conf/sessionMgrProperties.xml,
python/ndg.security.test/ndg/security/test/SessionMgr/sessionMgrProperties.xml,
python/ndg.security.server/ndg/security/server/SessionMgr/init.py:
added clntCertFile properties file element name for setting certificate for
verifying incoming SOAP messages.

python/ndg.security.server/ndg/security/server/SessionMgr/Makefile:
corrected typo.

python/ndg.security.server/ndg/security/server/MyProxy.py:
Put OpenSSLConfig and OpenSSLConfigError classes into their own package
'openssl' so that they can also be used by the Certificate Authority client.

python/www/html/certificateAuthority.wsdl,
python/ndg.security.server/ndg/security/server/ca/CertificateAuthority_services_server.py,
python/ndg.security.common/ndg/security/common/ca/CertificateAuthority_services_types.py,
python/ndg.security.common/ndg/security/common/ca/CertificateAuthority_services.py: updated operations to issueCert, revokeCert and getCRL.

python/ndg.security.test/ndg/security/test/AttAuthority/attAuthorityClientTest.cfg: changed address of service to connect to.

python/ndg.security.test/ndg/security/test/SessionMgr/sessionMgrClientTest.cfg:
alternative username connection settings

python/ndg.security.common/ndg/security/common/AttAuthority/init.py:
fixed typos in error message and comments.

ython/ndg.security.common/ndg/security/common/XMLSec.py: changed call to
getAttributeNodeNS to getAttributeNode for retrieving reference element URI
attribute.

python/ndg.security.common/ndg/security/common/ca/init.py: code for
Certificate Authority client

python/ndg.security.common/ndg/security/common/wsSecurity.py:

  • tidied up imports
  • added properties for setting keywords to reference and SignedInfo? C14N
  • changed sign method so that it is truely configurable allow use of inclusive or exclusive C14N based on the keywords set for reference and SignedInfo? C14N calls.
  • swapped calls to getAttributeNodeNS with getAttributeNode where appropriate.

java/DEWS/AttAuthority/appClientModule/META-INF/ibm-webservicesclient-bnd.xmi,
java/DEWS/AttAuthority/build/classes/META-INF/ibm-webservicesclient-bnd.xmi:
updated to that request generator correctly places X.509 cert in
BinarySecurityToken? element.

java/DEWS/AttAuthority/appClientModule/Main.java,
java/DEWS/AttAuthority/appClientjava/DEWS/AttAuthority/appClientModule/META-INF/ibm-webservicesclient-bnd.xmiModule/Main.java:
include calls to getX509Cert and getAttCert methods.

java/DEWS/SessionMgr/build/classes/META-INF/ibm-webservicesclient-bnd.xmi,
java/DEWS/SessionMgr/appClientModule/META-INF/ibm-webservicesclient-bnd.xmi:
updates for testing Session MAnager client

java/DEWS/SessionMgr/appClientModule/Main.java: switched username setting.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1"""NDG Session Management and security includes UserSession,
2SessionMgr, Credentials Repository classes.
3
4NERC Data Grid Project
5
6@author P J Kershaw 02/06/05
7
8@copyright (C) 2006 CCLRC & NERC
9
10@license This software may be distributed under the terms of the Q Public
11License, version 1.0 or later.
12"""
13
14reposID = '$Id$'
15
16# Modify sys.path when carrying out dynamic import for Credential Repository
17import sys
18
19# Time module for use with cookie expiry
20from time import strftime
21from datetime import datetime
22
23# For parsing of properties files
24import cElementTree as ElementTree
25
26# Base 64 encode session IDs if returned in strings - urandom's output may
27# not be suitable for printing!
28import base64
29
30# Session Manager URI in cookie
31from Crypto.Cipher import AES
32
33# Check Session Manager URI is encrypted
34from urllib import urlopen
35
36# Credential Wallet
37from ndg.security.common.CredWallet import CredWallet, CredRepos, \
38    CredWalletError, CredWalletAttributeRequestDenied
39
40from ndg.security.common.X509 import X509CertParse
41
42# MyProxy server interface
43from ndg.security.server.MyProxy import *
44
45# Use client package to allow redirection of authorisation requests and
46# to retrieve Attribute Authority public key
47from ndg.security.common.SessionMgr import SessionMgrClient
48
49# Placing of session ID on client
50from ndg.security.common.SessionCookie import SessionCookie
51
52# Use in SessionMgr __redirectAttCertReq to retrieve and store Public
53# key
54import tempfile
55import urllib
56
57#_____________________________________________________________________________
58class UserSessionError(Exception):   
59    """Exception handling for NDG User Session class."""
60   
61
62#_____________________________________________________________________________
63# Inheriting from 'object' allows Python 'new-style' class with Get/Set
64# access methods
65class UserSession(object):
66    """Session details - created when a user logs into NDG"""
67
68    #_________________________________________________________________________
69    def __init__(self, *credWalletArgs, **credWalletKeys):
70        """Initialise UserSession with args and keywords to CredWallet"""
71               
72       
73        # Each User Session has one or more browser sessions associated with
74        # it.  These are stored in a list
75        self.__sessIDlist = []
76        self.addNewSessID()
77        self.__credWallet = CredWallet(*credWalletArgs, **credWalletKeys)
78
79
80    #_________________________________________________________________________
81    # CredWallet access
82    def __getCredWallet(self):
83        """Get Credential Wallet instance"""
84        return self.__credWallet
85   
86    credWallet = property(fget=__getCredWallet,
87                          doc="Read-only access to CredWallet instance")
88
89
90    #_________________________________________________________________________
91    # CredWallet access
92    def __getSessIDlist(self):
93        """Get Session ID list - last item is latest allocated for this
94        session"""
95        return self.__sessIDlist
96   
97    sessIDlist = property(fget=__getSessIDlist,
98                          doc="Read-only access to Session ID list")
99
100
101    #_________________________________________________________________________       
102    def __latestSessID(self):
103        """Get the session ID most recently allocated"""
104        return self.__sessIDlist[-1]
105   
106    # Publish as an attribute
107    latestSessID = property(fget=__latestSessID,
108                            doc="Latest Session ID allocated")
109
110
111    #_________________________________________________________________________
112    def addNewSessID(self):
113        """Add a new session ID to be associated with this UserSession
114        instance"""
115
116        # base 64 encode output from urandom - raw output from urandom is
117        # causes problems when passed over SOAP.  A consequence of this is
118        # that the string length of the session ID will almost certainly be
119        # longer than SessionMgr.__sessIDlen
120        sessID = base64.urlsafe_b64encode(os.urandom(SessionCookie.sessIDlen))
121        self.__sessIDlist.append(sessID)
122
123
124    #_________________________________________________________________________
125    def __getExpiryStr(self):
126        """Return session expiry date/time as would be formatted for a cookie
127        """
128
129        try:
130            # Proxy certificate's not after time determines the expiry
131            dtNotAfter = self.credWallet.proxyCert.notAfter
132
133            return dtNotAfter.strftime(self.__sessCookieExpiryFmt)
134        except Exception, e:
135            UserSessionError, "getExpiry: %s" % e
136
137
138    #_________________________________________________________________________
139    @staticmethod
140    def encodeSessionMgrURI(txt, encrKey=None):
141        """Encode Session Manager URI to allow inclusion in a web browser
142        session cookie
143       
144        The address is optionally encrypted and then base 64 encoded use a
145        URL safe encoding
146       
147        @type encrKey: string
148        @keyword encrKey: 16 char encryption key used to encrypt the URI.  If
149        omitted or set None, the URI is not encrypted but merely base 64
150        encoded"""
151       
152        if encrKey is not None:
153            # Text length must be a multiple of 16 for AES encryption
154            try:
155                mod = len(txt) % 16
156                nPad = mod and 16 - mod or 0
157                   
158                # Add padding
159                paddedURI = txt + ''.join(' '*nPad)
160            except Exception, e:
161                raise UserSessionError, "Padding text for encryption: %s" % e
162       
163            # Encrypt
164            try:
165                txt = AES.new(encrKey, AES.MODE_ECB).encrypt(paddedURI)
166           
167            except Exception, e:
168                raise UserSessionError, "Encrypting Session Manager URI: %s"%e
169
170        try:
171            return base64.urlsafe_b64encode(txt)
172       
173        except Exception, e:
174            raise UserSessionError, "Encoding Session Manager URI: %s"%e
175       
176   
177    #_________________________________________________________________________
178    @staticmethod                                   
179    def decodeSessionMgrURI(txt, encrKey=None):
180        """Decode the URI from cookie set by another Session Manager.  This
181        is required when reading a session cookie to find out which
182        Session Manager holds the client's session
183       
184        @type txt: string
185        @param txt: base 64 encoded encrypted text
186       
187        @type encrKey: string
188        @keyword encrKey: 16 char encryption key used to encrypt the URI.  If
189        omitted or set None, the URI is assumed to be unencrypted"""
190
191        try:
192            # Convert if unicode type - unicode causes TypeError with
193            # base64.urlsafe_b64decode
194            if isinstance(txt, unicode):
195                txt = str(txt)
196               
197            # Decode from base 64
198            b64DecodedEncrTxt = base64.urlsafe_b64decode(txt)
199           
200        except Exception, e:
201            raise SessionMgrError, "Decoding Session Manager URI: %s" % e           
202
203
204        if encrKey is not None:
205            try:
206                aes = AES.new(encrKey, AES.MODE_ECB)
207               
208                # Decrypt and strip trailing spaces
209                return aes.decrypt(b64DecodedEncrTxt).strip()
210           
211            except Exception, e:
212                raise SessionMgrError, "Decrypting Session Manager URI: %s"%e           
213        else:
214            return b64DecodedEncrTxt
215       
216
217    #_________________________________________________________________________
218    def createCookie(self, 
219                     sessMgrURI,
220                     encrKey, 
221                     sessID=None,
222                     cookieDomain=None,
223                     asString=True):
224        """Create cookies for session ID Session Manager WSDL address
225
226        @type sessMgrURI: string
227        @param sessMgrURI: address for Session Mananger
228       
229        @type encrKey: string
230        @param encrKey: encryption key used to encrypted above URIs
231       
232        @type sessID: string
233        @keyword sessID: if no session ID is provided, use the latest one to
234        be allocated.
235       
236        @type cookieDomain: string
237        @keyword cookieDomain: domain set for cookie, if non set, web server
238        domain name is used.  Nb. Generalised domains which don't set a
239        specific host can be a security risk.
240       
241        @type asString: bool
242        @keyword asString: Set to True to return the cookie as string text. 
243        If False, it is returned as a SessionCookie instance.
244       
245        @rtype: SessionCookie / string depending on asString keyword
246        @return: session cookie"""
247         
248        if sessID is None:
249            # Use latest session ID allocated if none was input
250            sessID = self.__sessIDlist[-1]
251           
252        elif not isinstance(sessID, basestring):
253            raise UserSessionError, "Input session ID is not a valid string"
254                               
255            if sessID not in self.__sessIDlist:
256                raise UserSessionError, "Input session ID not found in list"
257 
258 
259        encrSessMgrURI = self.encodeSessionMgrURI(sessMgrURI, encrKey)
260        dtExpiry = self.credWallet.proxyCert.notAfter
261       
262        # Call class method
263        cookieTags = SessionCookie.tags
264        cookieTagsKw = {}.fromkeys(cookieTags)
265        cookieTagsKw[cookieTags[0]] = sessID
266        cookieTagsKw[cookieTags[1]] = encrSessMgrURI
267       
268        sessCookie = SessionCookie(dtExpiry=dtExpiry,
269                                   cookieDomain=cookieDomain,
270                                   **cookieTagsKw)
271        if asString:
272            return str(sessCookie)
273        else:
274            return sessCookie
275   
276
277#_____________________________________________________________________________
278class SessionMgrError(Exception):   
279    """Exception handling for NDG Session Manager class."""
280
281# Find the missing elements in targ referenced in ref
282getMissingElem = lambda targ, ref: [e for e in targ if e not in ref]
283
284# Expand environment variables for ElementTree Element type.  Use in
285# readProperties.
286filtElemTxt = lambda elem: isinstance(elem.text, basestring) and \
287                os.path.expandvars(elem.text).strip() or elem.text
288
289
290#_____________________________________________________________________________
291class SessionMgr(dict):
292    """NDG authentication and session handling
293   
294    @type __validElem: dict
295    @cvar __validElem: list of the valid properties file element names and
296    sub-elements where appropriate
297   
298    @type __confDir: string
299    @cvar __confDir: configuration directory under $NDG_DIR - default location
300    for properties file
301   
302    @type __propFileName: string
303    @cvar __propFileName: default file name for properties file under
304    __confDir
305    """
306
307    # valid configuration property keywords
308    __validElem = \
309    {
310        'portNum':        None,
311        'useSSL':         None,
312        'sslCertFile':    None,
313        'sslKeyFile':     None,
314        'caCertFile':     None,
315        'certFile':       None,
316        'keyFile':        None,
317        'keyPwd':         None,
318        'clntCertFile':   None,
319        'sessMgrEncrKey': None, 
320        'sessMgrURI':     None,
321        'cookieDomain':   None, 
322        'myProxyProp':    None, 
323        'credReposProp':  ('modFilePath', 'modName', 'className', 'propFile'),
324        'simpleCACltProp':('uri', 'xmlSigKeyFile', 'xmlSigCertFile', 
325                           'xmlSigCertPwd')
326    }
327
328    __confDir = "conf"
329    __propFileName = "sessionMgrProperties.xml"
330     
331   
332    #_________________________________________________________________________
333    def __init__(self, propFilePath=None, credReposPPhrase=None, **prop):       
334        """Create a new session manager to manager NDG User Sessions
335       
336        propFilePath:        path to properties file
337        credReposPPhrase:    for credential repository if not set in
338                             properties file
339        **prop:              set any other properties corresponding to the
340                             tags in the properties file"""       
341
342        # Base class initialisation
343        dict.__init__(self)
344
345        # Key user sessions by session ID
346        self.__sessDict = {}
347
348        # Key user sessions by user DN
349        self.__dnDict = {}
350
351        # Credential Repository interface only set if properties file is set
352        # otherwise explict calls are necessary to set credReposProp via
353        # setProperties/readProperties and then loadCredReposInterface
354        self.__credRepos = None
355       
356        # MyProxy interface
357        try:
358            self.__myPx = MyProxyClient()
359           
360        except Exception, e:
361            raise SessionMgrError, "Creating MyProxy interface: %s" % e
362   
363        # Dictionary to hold properties     
364        self.__prop = {}
365       
366        # Set from input or use defaults based or environment variables
367        self.setPropFilePath(propFilePath)
368       
369        # Set properties from file
370        self.readProperties()
371
372        # Call here as we can safely expect that all Credential Repository
373        # parameters have been set above
374        self.loadCredReposInterface()
375
376        # Set any properties that were provided by keyword input
377        #
378        # Nb. If any are duplicated with tags in the properties file they
379        # will overwrite the latter
380        #
381        # loadCredReposInterface must be called explicitly if propFilePath
382        # wasn't set.  This is because if properties are passed by keyword
383        # alone there is no guarantee that those needed to load the interface
384        # will be present.  readProperties however, requires that all the
385        # required parameters are present in the properties file.
386        self.setProperties(**prop)
387       
388       
389    #_________________________________________________________________________
390    def loadCredReposInterface(self, credReposPPhrase=None, Force=False):
391        """
392        Pick up and instantiate Credential Repository interface class from
393        properties file settings/keywords set by setProperties/__init__
394       
395        @type credReposPPhrase: string
396        @keyword credReposPPhrase: password for CredentialRepository database
397        This is passed into the Credential Repository object but may not
398        be needed.  e.g. the custom class could pick up a password from
399        the properties file for it - ['credRepos']['propFilePath']
400       
401        @type Force: boolean
402        @keyword Force: flag to force reload of Credential Repository instance
403        """
404       
405        # Don't bother if object has already been created.  Use Force=True
406        # to override and force reload
407        if Force is False and self.__credRepos is not None:
408            return
409       
410        # Credentials repository - permanent store of user credentials
411        try:
412            try:
413                # Module file path may be None if the new module to be loaded
414                # can be found in the existing system path           
415                if self.__prop['credReposProp']['modFilePath'] is not None:
416                    # Temporarily extend system path ready for import
417                    sysPathBak = sys.path[:]
418
419                    if not os.path.exists(\
420                              self.__prop['credReposProp']['modFilePath']):
421                        raise Exception, "File path '%s' doesn't exist" % \
422                              self.__prop['credReposProp']['modFilePath']
423                             
424                    sys.path.append(\
425                                self.__prop['credReposProp']['modFilePath'])
426               
427                # Import module name specified in properties file
428                credReposMod = \
429                    __import__(self.__prop['credReposProp']['modName'],
430                               globals(),
431                               locals(),
432                               [self.__prop['credReposProp']['className']])
433   
434                credReposClass = eval(\
435                'credReposMod.' + self.__prop['credReposProp']['className'])
436            finally:
437                try:
438                    sys.path[:] = sysPathBak
439                except NameError:
440                    # sysPathBak may not have been defined
441                    pass
442               
443        except KeyError, e:
444            raise SessionMgrError, \
445        'Missing %s element for credential repository module import' % str(e)
446                       
447        except Exception, e:
448            raise SessionMgrError, \
449                        'Importing credential repository module: %s' % str(e)
450
451        # Check class inherits from CredWallet.CredRepos abstract base class
452        if not issubclass(credReposClass, CredRepos):
453            raise SessionMgrError, \
454                "Credential Repository class %s must be inherited from %s" % \
455                (credReposClass, CredRepos)
456
457
458        # Instantiate custom class
459        try:
460            self.__credRepos = credReposClass(\
461                      propFilePath=self.__prop['credReposProp']['propFile'],
462                      dbPPhrase=credReposPPhrase)
463           
464        except Exception, e:
465            raise SessionMgrError, \
466            "Error instantiating Credential Repository interface: " + str(e)
467     
468       
469    #_________________________________________________________________________       
470    def __delitem__(self, key):
471        "Session Manager keys cannot be removed"       
472        raise KeyError, 'Keys cannot be deleted from '+self.__class__.__name__
473
474
475    def __getitem__(self, key):
476        self.__class__.__name__ + """ behaves as data dictionary of Session
477        Manager properties
478        """
479        if key not in self.__prop:
480            raise KeyError, "Invalid key '%s'" % key
481       
482        return self.__prop[key]
483   
484   
485    def __setitem__(self, key, item):
486        self.__class__.__name__ + """ behaves as data dictionary of Session
487        Manager properties"""
488        self.setProperties(**{key: item})
489       
490
491    def clear(self):
492        raise KeyError, "Data cannot be cleared from "+self.__class__.__name__
493   
494    def keys(self):
495        return self.__prop.keys()
496
497    def items(self):
498        return self.__prop.items()
499
500    def values(self):
501        return self.__prop.values()
502
503    def has_key(self, key):
504        return self.__prop.has_key(key)
505
506    # 'in' operator
507    def __contains__(self, key):
508        return key in self.__prop
509
510
511    #_________________________________________________________________________
512    def setPropFilePath(self, val=None):
513        """Set properties file from input or based on environment variable
514        settings"""
515        if not val:
516            if 'NDGSEC_SM_PROPFILEPATH' in os.environ:
517                val = os.environ['NDGSEC_SM_PROPFILEPATH']
518               
519            elif 'NDG_DIR' in os.environ:
520                val = os.path.join(os.environ['NDG_DIR'], 
521                                   self.__class__.__confDir,
522                                   self.__class__.__propFileName)
523            else:
524                raise AttributeError, 'Unable to set default Session ' + \
525                    'Manager properties file path: neither ' + \
526                    '"NDGSEC_SM_PROPFILEPATH" or "NDG_DIR" environment ' + \
527                    'variables are set'
528               
529        if not isinstance(val, basestring):
530            raise AttributeError, "Input Properties file path " + \
531                                  "must be a valid string."
532     
533        self.__propFilePath = val
534       
535    # Also set up as a property
536    propFilePath = property(fset=setPropFilePath,
537                            doc="Set the path to the properties file")   
538           
539
540    #_________________________________________________________________________
541    def readProperties(self, propElem=None):
542        """Read Session Manager properties from an XML file or cElementTree
543        node
544       
545        @type propElem: Element
546        @keyword propElem: pass in existing ElementTree treeroot
547        """
548
549        if not propElem:
550            try:
551                tree = ElementTree.parse(self.__propFilePath)
552                propElem = tree.getroot()
553
554            except IOError, e:
555                raise SessionMgrError, \
556                                "Error parsing properties file \"%s\": %s" % \
557                                (e.filename, e.strerror)               
558            except Exception, e:
559                raise SessionMgrError, \
560                    "Error parsing properties file: \"%s\": %s" % \
561                    (propFilePath, e)
562
563        if propElem is None:
564            raise SessionMgrError, \
565                            "Parsing properties: root element is not defined"
566
567
568       
569        missingElem = []
570        invalidElem = []
571        try:
572            for elem in propElem:
573                if elem.tag == 'myProxyProp':
574                    self.__myPx.readProperties(propElem=elem)
575   
576                elif elem.tag == 'credReposProp':
577                    self.__prop['credReposProp'] = \
578                                dict([(e.tag, filtElemTxt(e)) for e in elem])
579                           
580                    # Check for missing elements
581                    missingElem.extend(getMissingElem(\
582                                           self.__validElem['credReposProp'],
583                                           self.__prop['credReposProp']))
584                   
585                elif elem.tag == 'simpleCACltProp':
586                    self.__prop['simpleCACltProp'] = \
587                                dict([(e.tag, filtElemTxt(e)) for e in elem])
588                           
589                    # Check for missing elements
590                    missingElem.extend(getMissingElem(\
591                                       self.__validElem['simpleCACltProp'],
592                                       self.__prop['simpleCACltProp']))
593                   
594                elif elem.tag in self.__validElem:
595                    # Strip white space but not in the case of password
596                    # field as password might contain leading or
597                    # trailing white space
598                    if elem.text is not None and elem.tag != 'keyPwd':                       
599                        if elem.text.isdigit():
600                            self.__prop[elem.tag] = int(elem.text)
601                        else:                           
602                            # Check for environment variables in file paths
603                            self.__prop[elem.tag] = filtElemTxt(elem)
604                    else:
605                        self.__prop[elem.tag] = elem.text
606                else:
607                    invalidElem.append(elem.tag)
608               
609        except Exception, e:
610            raise SessionMgrError, \
611                "Error parsing tag \"%s\" in properties file" % elem.tag
612
613        missingElem.extend(getMissingElem(self.__prop, self.__validElem))
614        errMsg = ''
615       
616        if invalidElem != []:
617            errMsg = 'Invalid elements: "%s"\n' % '", "'.join(invalidElem)
618
619        if missingElem != []:
620            errMsg += 'Missing elements: "%s"\n' % '", "'.join(missingElem)
621
622        if errMsg:
623            raise SessionMgrError, errMsg +  " for properties file"
624       
625
626    #_________________________________________________________________________
627    def setProperties(self, **prop):
628        """Update existing properties from an input dictionary
629        Check input keys are valid names"""
630       
631        for key in prop.keys():
632            if key not in self.__validElem:
633                raise SessionMgrError, "Property name \"%s\" is invalid" % key
634
635
636        for key, value in prop.items():
637                       
638            if key == 'myProxyProp':
639                self.__myPx.setProperties(prop[key])
640   
641            elif key == 'credReposProp':
642                self.__prop['credReposProp'] = prop[key].copy()
643
644            elif key in self.__validElem:
645                # Only update other keys if they are not None or ""
646                if value:
647                    self.__prop[key] = value               
648            else:
649                raise SessionMgrError, \
650                "Key \"%s\" is not a valid Session Manager property" % key
651
652
653    #_________________________________________________________________________
654    def addUser(self, username, passphrase=None):       
655        """Register a new user with an NDG data centre
656       
657        addUser([caConfigFilePath, ]|[, caPassPhrase]
658                |[, userName=u, pPhrase=p])
659
660        returns XML formatted response message
661       
662        caConfigFilePath|caPassPhrase:  pass phrase for SimpleCA's
663                                        certificate.  Set via file or direct
664                                        string input respectively.  Set here
665                                        to override setting [if any] made at
666                                        object creation.
667       
668                                        Passphrase is only required if
669                                        SimpleCA is instantiated on the local
670                                        machine.  If SimpleCA WS is called no
671                                        passphrase is required.
672                                       
673        **kw:                      use as alternative to
674                                        reqXMLtxt keyword - pass in
675                                        username and pass-phrase for new user
676                                        unencrypted as keywords username
677                                        and pPhrase respectively."""
678             
679        # Ask CA to sign certificate
680       
681        # Add new user certificate to MyProxy Repository
682        self.__myPx.store(username, 
683          certFile,
684          keyFile,
685          ownerCertFile=None,
686          ownerKeyFile=None,
687          ownerPassphrase=None,
688          lifetime=None,
689          force=True)
690
691        return userDN           
692   
693   
694    #_________________________________________________________________________       
695    def connect(self, getCookie=False, createServerSess=True, **kw):       
696        """Create a new user session or connect to an existing one:
697
698        connect([getCookie=True/False][createServerSess=True/False, ]
699                [, username=u, passphrase=p]|[, proxyCert=px]|[, sessID=id])
700
701        @param getCookie: If True, allocate a user session with a wallet in
702        the session manager and return a cookie containing the new session ID
703        allocated.  If set False, return a proxy certificate only.  The client
704        is then responsible for Credential Wallet management.
705       
706        @param createServerSess: If set to True, the SessionMgr will create
707        and manage a session for the user.  Nb. this flag is ignored and set
708        to True if getCookie is set.  For command line case, where getCookie
709        is False, it's possible to choose to have a client or server side
710        session using this keyword.
711       
712        @param **kw: username and pass-phrase OR proxy certificate OR session
713        ID keywords
714       
715        @rtype: tuple
716        @return proxy certificate, proxy private key, user certificate and
717        session cookie respectively.  Cookie will be None if getCookie is set
718        to False. Proxy cert. will be None if 'proxyCert' was set as an input.
719        """
720       
721        # Initial proxy cert and cookie to be returned
722        proxyCert, sessCookie = None, None
723       
724        if 'sessID' in kw:           
725            # Connect to an existing session identified by a session ID and
726            # return equivalent proxy cert
727            userSess = self.__connect2UserSession(sessID=kw['sessID'])
728            proxyCert = userSess.credWallet.proxyCertTxt
729       
730        elif 'proxyCert' in kw:
731            # Connect to an existing session identified by a proxy
732            # certificate
733            userSess = self.__connect2UserSession(proxyCert=kw['proxyCert'])
734           
735            # Make a new session cookie
736            sessCookie = userSess.createCookie(\
737                                   self.__prop['sessMgrURI'],
738                                   self.__prop['sessMgrEncrKey'],
739                                   cookieDomain=self.__prop['cookieDomain'])
740        else:
741            # Create a fresh session
742            try:           
743                # Get a proxy certificate to represent users ID for the new
744                # session
745                proxyCert, proxyPriKey, userCert = \
746                        self.__myPx.logon(kw['username'], kw['passphrase'])   
747            except Exception, e:
748                raise SessionMgrError, "Delegating from MyProxy: %s" % e
749
750            if getCookie or createServerSess:
751                # Session Manager creates and manages user's session
752                userSess = self.__createUserSession(proxyCert, 
753                                                    proxyPriKey, 
754                                                    userCert)
755                               
756            if getCookie:               
757                # Web browser client - Return session cookie
758                sessCookie = userSess.createCookie(\
759                                    self.__prop['sessMgrURI'],
760                                    self.__prop['sessMgrEncrKey'],
761                                    cookieDomain=self.__prop['cookieDomain'])
762               
763        # Return proxy details and cookie
764        return proxyCert, proxyPriKey, userCert, sessCookie       
765       
766       
767    #_________________________________________________________________________       
768    def __createUserSession(self, *proxy):
769        """Create a new user session from input user credentials       
770        and return
771       
772        @type proxy: tuple
773        @param proxy: tuple containing proxy certificate, private key
774        and issuing certificate."""
775       
776        # Check for an existing session for the same user
777        try:
778            userDN = str(X509CertParse(proxy[0]).dn)
779           
780        except Exception, e:
781            raise SessionMgrError, \
782            "Parsing input proxy certificate DN for session create: %s" % \
783                                                                    str(e)
784
785        if userDN in self.__dnDict:
786            # Update existing session with proxy cert and add a new
787            # session ID to access it - a single session can be accessed
788            # via multiple session IDs e.g. a user may wish to access the
789            # same session from the their desktop PC and their laptop.
790            # Different session IDs are allocated in each case.
791            userSess = self.__dnDict[userDN]
792            userSess.addNewSessID()
793           
794        else:
795            # Create a new user session using the new proxy certificate
796            # and session ID
797            #
798            # Nb. Client pub/pri key info to allow message level
799            # encryption for responses from Attribute Authority WS
800            try:   
801                userSess=UserSession(caCertFilePath=self.__prop['caCertFile'],
802                                     credRepos=self.__credRepos, 
803                                     *proxy)     
804            except Exception, e:
805                raise SessionMgrError, "Creating User Session: %s" % e
806
807            # Also allow access by user DN
808            self.__dnDict[userDN] = userSess
809
810                           
811        newSessID = userSess.latestSessID
812       
813        # Check for unique session ID
814        if newSessID in self.__sessDict:
815            raise SessionMgrError, \
816                "New Session ID is already in use:\n\n %s" % newSessID
817
818        # Add new session to list                 
819        self.__sessDict[newSessID] = userSess
820               
821        # Return new session
822        return userSess
823
824
825    #_________________________________________________________________________       
826    def __connect2UserSession(self, **idKw):
827        """Connect to an existing session by providing a valid session ID or
828        proxy certificate
829
830        __connect2UserSession([proxyCert]|[sessID])
831       
832        @param proxyCert: proxy certificate string corresponding to an
833        existing session to connect to.
834       
835        @param sessID: similiarly, a web browser session ID linking to an
836        an existing session."""
837       
838           
839        # Look for a session corresponding to this ID
840        if 'sessID' in idKw:
841            try:
842                # Check matched session has not expired
843                userSess = self.__sessDict[idKw['sessID']]
844               
845            except KeyError:
846                # User session not found with given ID
847                raise SessionMgrError, \
848                        "No user session found matching input session ID"
849                       
850            try:
851                userSess.credWallet.isValid(raiseExcep=True)
852                return userSess
853                       
854            except Exception, e:
855                raise SessionMgrError, \
856                        "Matching session ID to existing user session: %s" % e
857               
858       
859        elif 'userCert' in idKw:
860            try:
861                userDN = str(X509CertParse(idKw['userCert']).dn)
862               
863            except Exception, e:
864                raise SessionMgrError, \
865                "Parsing input user certificate DN for session connect: %s" %e
866            try:
867                userSess = self.__dnDict[userDN]
868                       
869            except KeyError:
870                # User session not found with given proxy cert
871                raise SessionMgrError, \
872                    "No user session found matching input proxy certificate"
873                   
874            try:
875                # Check matched session has not expired
876                userSess.credWallet.isValid(raiseExcep=True)
877                return userSess
878                                       
879            except Exception, e:
880                raise SessionMgrError, \
881                "Matching proxy certificate to existing user session: %s" % e
882        else:
883            raise SessionMgrError,\
884                                '"sessID" or "proxyCert" keywords must be set'
885
886
887    #_________________________________________________________________________       
888    def deleteUserSession(self, sessID=None, proxyCert=None):
889        """Delete an existing session by providing a valid session ID or
890        proxy certificate - use for user logout
891
892        __deleteUserSession([proxyCert]|[sessID])
893       
894        @param proxyCert: proxy certificate corresponding to an existing
895        session to connect to.
896        @param sessID: similiarly, a web browser session ID linking to an
897        an existing session."""
898       
899           
900        # Look for a session corresponding to the session ID/proxy cert.
901        if sessID:
902            try:
903                userSess = self.__sessDict[sessID]
904               
905            except KeyError:
906                raise SessionMgrError, \
907                    "Deleting user session - no matching session ID exists"
908
909            # Get associated user Distinguished Name
910            userDN = userSess.credWallet.proxyCert.dn
911           
912        elif proxyCert:
913            try:
914                userDN = str(X509CertParse(idKw['proxyCert']).dn)
915               
916            except Exception, e:
917                raise SessionMgrError, \
918                "Parsing input proxy certificate DN for session connect: %s"%\
919                                                                        str(e)
920            try:
921                userSess = self.__dnDict[userDN]
922                       
923            except KeyError:
924                # User session not found with given proxy cert
925                raise SessionMgrError, \
926                    "No user session found matching input proxy certificate"
927        else:
928            # User session not found with given ID
929            raise SessionMgrError, \
930                                '"sessID" or "proxyCert" keywords must be set'
931 
932        # Delete associated sessions
933        try:
934            # Each session may have a number of session IDs allocated to
935            # it
936            for userSessID in userSess.sessIDlist:
937                del self.__sessDict[userSessID]
938
939            del self.__dnDict[userDN]
940           
941        except Exception, e:
942            raise SessionMgrError, "Deleting user session: %s" % e       
943
944
945    #_________________________________________________________________________
946    def getAttCert(self,
947                   userCert=None,
948                   sessID=None,
949                   encrSessMgrURI=None,
950                   **credWalletKw):
951        """For a given user, request Attribute Certificate from an Attribute
952        Authority given by service URI.  If sucessful, an attribute
953        certificate is added to the user session credential wallet and also
954        returned from this method
955
956        @param **kw: keyword to CredWallet.getAttCert
957        """
958       
959        # Web browser client input will include the encrypted address of the
960        # Session Manager where the user's session is held.
961        if encrSessMgrURI:
962           
963            # Decrypt the URI for where the user's session resides
964            userSessMgrURI = UserSession.decodeSessionMgrURI(encrSessMgrURI,
965                                               self.__prop['sessMgrEncrKey'])
966                                               
967            # Check the address against the address of THIS Session Manager 
968            if userSessMgrURI != self.__prop['sessMgrURI']:
969               
970                # Session is held on a remote Session  Manager
971                userSessMgrResp = self.__redirectAttCertReq(userSessMgrURI,
972                                                            sessID=sessID,
973                                                            userCert=userCert,
974                                                            **credWalletKw)
975
976                # Reset response by making a new AuthorisationResp object
977                # The response from the remote Session Manager will still
978                # contain the encrypted XML sent by it.  This should be
979                # discarded
980                return userSessMgrResp
981
982           
983        # User's session resides with THIS Session Manager / no encrypted
984        # URI address passed in (as in command line use case for security) ...
985
986           
987        # Retrieve session corresponding to user's session ID using relevant
988        # input credential
989        userSess = self.__connect2UserSession(sessID=sessID,userCert=userCert)
990
991
992        # User's Credential Wallet carries out attribute request to the
993        # Attribute Authority
994        try:
995            attCert = userSess.credWallet.getAttCert(**credWalletKw)
996            return attCert, None, []
997           
998        except CredWalletAttributeRequestDenied, e:
999            # Exception object containa a list of attribute certificates
1000            # which could be used to re-try to get authorisation via a mapped
1001            # certificate
1002            return None, str(e), e.extAttCertList
1003
1004
1005    #_________________________________________________________________________
1006    def __redirectAttCertReq(self, userSessMgrURI, **kw):
1007        """Handle case where User session resides on another Session Manager -
1008        forward the request
1009       
1010        @type userSessMgrURI: string
1011        @param userSessMgrURI: address of remote session manager where user
1012        session is held
1013       
1014        @type **kw: dict
1015        @param **kw: same keywords which apply to getAttCert call"""
1016       
1017        # Instantiate WS proxy for remote session manager
1018        try:
1019            sessMgrClnt = SessionMgrClient(uri=userSessMgrURI,
1020                                 signingCertFilePath=self.__prop['certFile'],
1021                                 signingPriKeyFilePath=self.__prop['keyFile'],
1022                                 signingPriKeyPwd=self.__prop['keyPwd'])           
1023        except Exception, e:
1024            raise SessionMgrError, \
1025                "Re-directing attribute certificate request to \"%s\": %s" % \
1026                (userSessMgrURI, str(e))
1027
1028           
1029        # Call remote session manager's authorisation request method
1030        # and return result to caller
1031        try:
1032            # Call remote SessionMgr where users session lies
1033            resp = sessMgrClnt.getAttCert(**kw)       
1034            return resp
1035       
1036        except Exception, e:
1037            raise SessionMgrError, \
1038        "Forwarding Authorisation request for Session Manager \"%s\": %s" %\
1039                (userSessMgrURI, e)
1040
1041
1042    #_________________________________________________________________________
1043    def auditCredRepos(self):
1044        """Remove expired Attribute Certificates from the Credential
1045        Repository"""
1046        self.__credRepos.auditCredentials()
Note: See TracBrowser for help on using the repository browser.