source: TI12-security/trunk/python/NDG/SecurityCGI.py @ 1337

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

www/cgi-bin/idp.py: Security CGI for NDG Identity Provider
www/cgi-bin/sp.py: Security CGI for NDG Service Provider requesting credentials
NDG/SecurityCGI.py: continued re-working to split into two the two distinct components in the interface:

  • Service Provider interface - requests credentials for a user
  • Identity Provider interface - accepts requests from a Service Provider interface for credentials and interacts

with the user to retrieve them as required.

  • Property svn:executable set to *
Line 
1#!/usr/bin/env python
2
3"""NDG Security CGI Interface between Service Providers and Identiy Providers
4
5NERC Data Grid Project
6
7P J Kershaw 23/05/06
8
9Copyright (C) 2006 CCLRC & NERC
10
11This software may be distributed under the terms of the Q Public License,
12version 1.0 or later.
13"""
14from Cookie import SimpleCookie
15
16import sys
17import cgi
18import os
19import base64
20
21# Set cookie expiry
22from datetime import datetime
23from datetime import timedelta
24
25from NDG.SecurityClient import *
26from NDG.Session import UserSession
27from NDG.Session import UserSessionError
28
29
30class SecurityCGIError(Exception):
31    """Exception handling for NDG Security CGI class."""
32    pass
33
34
35# Default style for HTML pages.  This can easily be overriden using keyword
36# settings or overriding methods in a derived class.
37__defStyle = """    <style type=\"text/css\">
38    <!--
39    .al {
40    text-align: justify
41    }
42    a{
43    text-decoration:none;
44    }
45    a:hover{
46    color:#0000FF;
47    }
48        body { font-family: Verdana, sans-serif; font-size: 11}
49        table { font-family: Verdana, sans-serif; font-size: 11}
50    -->
51</style>"""
52
53 
54class _SecurityCGI(cgi.FieldStorage):
55    """CGI Service Provider - Identity Provider interface base class for NDG
56    Security
57   
58    Service Provider   - serves NDG resources over http
59    Identity Provider  - holds NDG user accounts and supports authentication
60                         over http
61    """
62
63    acAllowMapping = "allowMapping"
64    acAllowMappingWithPrompt = "allowMappingWithPrompt"
65    acNoMapping = "noMapping"
66   
67    acMappingFlags = [acAllowMapping, acAllowMappingWithPrompt, acNoMapping]
68   
69    #_________________________________________________________________________
70    def __init__(self,
71                 clntPubKeyFilePath=None,
72                 clntPriKeyFilePath=None,
73                 clntPriKeyPwd=None,
74                 scriptName=__file__,
75                 wsDebug=False,
76                 **cgiFieldStorageKwArgs):
77        """clntPubKeyFilePath:    file path to client public key.  The CGI
78                                  script must have access to a public/private
79                                  key to enable encryption of return traffic
80                                  from NDG security WSs.  IF THIS KEYWORD IS
81                                  NOT SET, RETURN TRAFFIC IS UNENCRYPTED.
82        clntPriKeyFilePath:       file path to client private key.
83        clntPriKeyPwd:            password protecting the private key.  If no
84                                  password is set, omit this keyword.
85        wsDebug:                  print output from WS transactions to stderr
86                                  for debu purposes.
87        """
88       
89        self.clntPubKeyFilePath = clntPubKeyFilePath
90        self.clntPriKeyFilePath = clntPriKeyFilePath
91        self.clntPriKeyPwd = clntPriKeyPwd
92
93        self.scriptName = scriptName
94
95        self._wsDebug = False       
96        self.acMappingFlag = None
97        self.attCert = None
98               
99        # Read fields so that self becomes a dictionary of the fields
100        cgi.FieldStorage.__init__(self, **cgiFieldStorageKwArgs)
101
102 
103    #_________________________________________________________________________
104    def processFields(self, **kwargs):
105        """Call appropriate actions according to the fields set"""       
106        raise NotImplementedError, \
107            self.processFields.__doc__.replace('\n       ','')
108   
109   
110    #_________________________________________________________________________
111    # Use instance name as an alias to processFields method
112    __call__ = processFields
113
114
115    #_________________________________________________________________________
116    def __getACmappingFlag(self):
117        """Flag determines whether mapping is permitted when contacting an
118        Attribute Authority to get an Attribute Certificate"""
119        return self.__acMappingFlag
120
121
122    def __setACmappingFlag(self, acMappingFlag):
123        """Flag determines whether mapping is permitted when contacting an
124        Attribute Authority to get an Attribute Certificate"""
125       
126        if acMappingFlag not in self.acMappingFlags:
127            raise AttributeError, \
128                "\"%s\" is invalid for acMappingFlag" % \
129                self.acMappingFlags
130               
131        self.__acMappingFlag = acMappingFlag
132 
133       
134    def __delACmappingFlag(self):
135        """Prevent certificate mapping flag from being deleted."""
136        raise AttributeError, \
137                        "\"acMappingFlag\" attribute cannot be deleted"
138       
139   
140    acMappingFlag = property(fget=__getACmappingFlag,
141                            fset=__setACmappingFlag,
142                            fdel=__delACmappingFlag,
143                            doc="mapping flag for AttCert requests to an AA")
144   
145   
146    #_________________________________________________________________________
147    def _getAttCert(self, sessCookie=None, reqRole=None):
148        """Contact Attribute Authority to get Attribute Certificate for data
149        access
150
151        sessCookie:     NDG security session cookie
152        reqRole:        specify the required role to get authorisation.  Set
153                        this to optimise the process for getting the required
154                        AC from a trusted host in order to perform mapping"""
155
156        # Check for session cookie input
157        if not sessCookie:
158            # No cookie set as input argument check for environment variable
159            if 'HTTP_COOKIE' in os.environ:
160                sessCookie = SimpleCookie(os.environ['HTTP_COOKIE'])   
161            else:
162                raise SecurityCGIError, \
163                    "Attribute certificate request requires a security cookie"
164
165        # Check cookie is valid
166        try:
167            UserSession.isValidSecurityCookie(sessCookie, raiseExcep=True)
168           
169        except UserSessionError, e:
170            raise SecurityCGIError, 'Checking existing session cookie: %s' % e
171
172
173        # Configure flags for attribute certificate request.  This determines
174        # whether mapping of certificates from trusted hosts is allowed
175        if self.acMappingFlag == self.acAllowMapping:
176            bMapFromTrustedHosts = True
177            bRtnExtAttCertList = False
178
179        elif self.acMappingFlag == self.acAllowMappingWithPrompt:
180            bMapFromTrustedHosts = False
181            bRtnExtAttCertList = True
182        else:
183            bMapFromTrustedHosts = False
184            bRtnExtAttCertList = False
185
186
187        # Instantiate WS proxy and request authorisation
188        try:
189            if not self.smClnt:
190                self.smClnt = SessionClient(\
191                            smWSDLuri=self.smWSDLuri,
192                            smPubKeyFilePath=self.smPubKeyFilePath,
193                            clntPubKeyFilePath=self.clntPubKeyFilePath,
194                            clntPriKeyFilePath=self.clntPriKeyFilePath,
195                            traceFile=self._wsDebug and sys.stderr or None)
196
197            authzResp = self.smClnt.reqAuthorisation(sessCookie=sessCookie,
198                                    aaWSDLuri=self.aaWSDLuri,
199                                    aaPubKey=self.aaPubKey,
200                                    reqRole=reqRole,
201                                    mapFromTrustedHosts=bMapFromTrustedHosts,
202                                    rtnExtAttCertList=bRtnExtAttCertList,
203                                    clntPriKeyPwd=self.clntPriKeyPwd)
204        except Exception, e:
205            raise SecurityCGIError, "Attribute Certificate request: %s" % e
206
207
208        if authzResp['statCode'] == authzResp.AccessGranted:
209            self.onAttCertGranted(authzResp['attCert'])
210       
211        elif authzResp['statCode'] == authzResp.AccessDenied:
212            self.onAttCertDenied(authzResp['extAttCertList'], 
213                                 authzResp['errMsg'])
214           
215        elif authzResp['statCode'] == authzResp.AccessError:
216            raise SecurityCGIError, authzResp['errMsg']
217           
218   
219    #_________________________________________________________________________
220    def onAttCertGranted(self, attCert):
221        """Callback invoked by getAttCert - handle case where an Attribute
222        Authority has granted a new attribute certificate to the user.  Derive
223        from this class and override this method as required.
224        """
225        pass
226   
227   
228    #_________________________________________________________________________
229    def onAttCertDenied(self, extAttCertList, errMsg):
230        """Callback invoked by getAttCert - handle case where an Attribute
231        Authority has denied an attribute certificate to the user.  Derive
232        from this class and override this method as required.
233       
234        extAttCertList:    a list of attribute certificates from trusted
235                           hosts.  Any of these could be selected and
236                           presented back to the target AA in order to get
237                           a mapped certificate.  This list may be None if
238                           no ACs could be obtained or if the
239                           mapFromTrustedHosts flag in the call to the Session
240                           Manager WS reqAuthorisation method was set to
241                           False.
242                           
243        errMsg:            the error message returned from the call to the
244                           AA to get an AC."""
245       
246        if not extAttCertList:
247            self.showLogin(pageTitle="Access denied by Attribute Authority")
248            raise SecurityCGIError, errMsg
249       
250        else:
251            # Display list of attCerts to choose from
252            print \
253"""Content-type: text/html
254
255<html>
256<head>
257    <title>Select an Attribute Certificate</title>
258    <style type=\"text/css\">
259    <!--
260    .al {
261    text-align: justify
262    }
263    a{
264    text-decoration:none;
265    }
266    a:hover{
267    color:#0000FF;
268    }
269        body { font-family: Verdana, sans-serif; font-size: 11}
270        table { font-family: Verdana, sans-serif; font-size: 11}
271    -->
272    </style>
273</head>
274<body>
275    <form action="%s" method="POST">
276    <table bgcolor=#ADD8E6 cellspacing=0 border=0 cellpadding=5>
277    <tbody>"""
278   
279            for attCert in extAttCertList:
280                print \
281"""    <tr>
282        <td>%s</td>
283    </tr>""" % attCert['issuer']
284               
285                print \
286"""    </tbody>
287    </table>
288    </form>
289</body>
290</html>"""
291   
292        # end of onAttCertDenied()
293       
294
295#_____________________________________________________________________________
296class ServiceProviderSecurityCGI(_SecurityCGI):
297    """CGI interface for a Service Provider requesting user credentials
298   
299    It enables the user to select an Identity Provider login page from a list
300    of trusted hosts, re-directs the user browser to the login and then can
301    process credentials passed back from the IdP to enable a new NDG security
302    cookie to be set in the Service Provider target domain.
303    """
304   
305    #_________________________________________________________________________
306    def __init__(self,
307                 aaWSDLuri,
308                 aaPubKeyFilePath=None,
309                 smWSDLuri=None,
310                 smPubKeyFilePath=None,
311                 trustedHostInfo=None,
312                 cookieLifetimeHrs=8,
313                 **securityCGIKwArgs):
314        """aaWSDLuri:         URI for Attribute Authority WSDL used to get a
315                              list of login URI for trusted hosts
316        smWSDLuri:            URI For Session Manager WSDL used for querying
317                              user session wallet for Attribute Certificates.
318                              Only needed for _getAttCert calls
319        aaPubKeyFilePath:     file path to Attribute Authority public key.
320                              If not set, the client will make a WS call for
321                              it.
322        trustedHostInfo:      dictionary of URIs for trusted hosts indexed by
323                              hostname
324        cookieLifetimeHrs:    cookie lifetime in hours for new cookie set in
325                              Service Provider target domain.
326        wsDebug:              print output from WS transactions to stderr"""
327
328        self.aaWSDLuri = aaWSDLuri
329        self.aaClnt = None
330       
331        self.aaPubKeyFilePath = aaPubKeyFilePath
332
333        self.smWSDLuri = smWSDLuri
334        self.smClnt = None
335       
336        self.smPubKeyFilePath = smPubKeyFilePath
337
338
339        self.trustedHostInfo = trustedHostInfo
340        self.cookieLifetimeHrs = cookieLifetimeHrs
341
342        # Work out expiry time offset from the time this script is run
343        self.dtCookieExpiry = datetime.utcnow() + \
344                            timedelta(seconds=self.cookieLifetimeHrs*60*60)
345
346        self._wsDebug = False       
347        self.acMappingFlag = None
348        self.attCert = None
349               
350        super(self.__class__, self).__init__(**securityCGIKwArgs)
351
352 
353    #_________________________________________________________________________
354    def processFields(self, **kwargs):
355        """Call appropriate actions according to the fields set"""
356
357        if 'requestURI' in self:
358            # Request credentials from user's identity provider
359            self._requestCreds(**kwargs)
360
361        elif not [True for tag in UserSession.cookieTags if tag not in self]:
362            # Credentials tags were set -  set a new cookie at service
363            # provider site
364            encodedExpiry='expires' in self and self['expires'].value or None
365
366            self._receiveCredsResponse(encodedExpiry=encodedExpiry, **kwargs)
367   
368   
369    #_________________________________________________________________________
370    def showHomeSiteSelect(self,
371                           trustedHostInfo=None,
372                           scriptName=None,
373                           contentTypeHdr=False,
374                           htmlTag=False,
375                           hdrTag=False,
376                           hdrTxt=__defStyle,
377                           bodyTag=False,
378                           pageTitle=""):
379
380        if trustedHostInfo:
381            self.trustedHostInfo = trustedHostInfo
382
383        if not self.trustedHostInfo:
384            self.getTrustedHostInfo()
385           
386        if scriptName:
387            self.scriptName = scriptName
388           
389               
390        if contentTypeHdr:
391            print "Content-type: text/html\n\n"
392               
393        if htmlTag:
394            print "<html>\n"
395
396        if hdrTag:           
397            print """<head>
398           
399    <title>%s</title>
400    %s
401</head>""" % (pageTitle, hdrTxt)
402   
403   
404        if bodyTag:
405            print "<body>\n"
406
407        # Form containing droplist for the trusted hosts
408        print """
409    <form action="%s" method="POST">
410    <table bgcolor=#ADD8E6 cellspacing=0 border=0 cellpadding=5>
411    <tbody>
412    <tr>
413      <td>
414        <select name="requestURI">       
415          <option value="">Select your home site...""" % self.scriptName
416         
417        for hostname, info in self.trustedHostInfo.items():
418            print "<option value=\"%s\">%s" % (info['loginURI'], hostname)
419               
420        print \
421"""        </select>
422      </td>
423      <td align="right">
424        <input type=submit value="Go">
425      </td>
426    </tr>
427    </tbody>
428    </table>
429    </form>"""
430
431        if bodyTag:
432            print "</body>\n"
433
434        if htmlTag:
435            print "</html>\n"
436   
437        # end of showHomeSiteSelect()
438
439
440    #_________________________________________________________________________
441    def showCredsReceived(self, 
442                          sessCookie,
443                          pageTitle='Credentials Received',
444                          hdrTxt=__defStyle,
445                          bodyText='NDG Security session cookie set'):
446        """Called from _receiveCredsResponse() once a cookie has been created.
447        Makes a page to set the cookie and display to the user that they have
448        been authenticated.  Derived class should override this method as
449        required"""
450       
451        print """Content-type: text/html"
452%s
453
454<html>
455<head>
456<title>%s</title>
457%s
458</head>
459<body>
460    %s
461</body>
462</html>""" % (sessCookie.output(), pageTitle, hdrTxt, bodyTxt)
463
464
465    #_________________________________________________________________________
466    def getTrustedHostInfo(self):
467        """Call Attribute Authority to find out trusted hosts.  These can be
468        use to populate list for use to select Identity Provider for login"""
469       
470        try:
471            if not self.aaClnt:
472                self.aaClnt = AttAuthorityClient(aaWSDLuri=self.aaWSDLuri,
473                            aaPubKeyFilePath=self.aaPubKeyFilePath,
474                            clntPubKeyFilePath=self.clntPubKeyFilePath,
475                            clntPriKeyFilePath=self.clntPriKeyFilePath,
476                            traceFile=self._wsDebug and sys.stderr or None)
477           
478            self.trustedHostInfo = self.aaClnt.getTrustedHostInfo(
479                                           clntPriKeyPwd=self.clntPriKeyPwd)
480        except Exception, e:
481            raise SecurityCGIError, "Attribute Authority client: %s" % e
482
483   
484    #_________________________________________________________________________
485    def _createCookie(self, encodedExpiry=None):
486        """Convert credentials passed over URI from users Identity Provider
487        into a new cookie"""
488
489        if encodedExpiry:
490            # Expiry is taken from encoded value passed over URI
491            dtExpiry = None
492            expiryStr = base64.urlsafe_b64decode(encodedExpiry)
493        else:
494            # Expiry is set from life time in hours input in __init__
495            dtExpiry = self.dtCookieExpiry
496            expiryStr = None
497
498       
499        try:
500            tagsDict = dict([(tag, self[tag].value) \
501                             for tag in UserSession.cookieTags])
502        except KeyError, e:
503            raise SecurityCGIError, "Missing cookie tag: %s" % e
504       
505        sessCookie =  UserSession.createSecurityCookie(dtExpiry=dtExpiry,
506                                                       expiryStr=expiryStr,
507                                                       **tagsDict)
508       
509        return sessCookie
510   
511
512    #_________________________________________________________________________
513    def _requestCreds(self,
514                      requestURI=None,
515                      returnURI=None,
516                      pageTitle='',
517                      headTags='',
518                      delayTime=0,
519                      redirectMsg=''):
520        """Request credentials from a user's Identity Provider
521       
522        requestURI:   site to request credentials from - default is
523                      'requestURI' CGI form value
524        pageTitle:    Give the redirect page a title
525        headTags:     Optionally add additional tags in <head/> section
526        delayTime:    time in seconds before carrying out redirect - redirect
527                      page will be displayed in this interval
528        redirectMsg:  Message to put on redirect page.  Can be plain text or
529                      formatted HTML"""
530
531        if returnURI is None:
532            returnURI = self['returnURI'].value
533
534        if requestURI is None:
535            requestURI = self['requestURI'].value
536
537        print """Content-type: text/html
538
539<html>
540<head>
541<title>%s</title>
542<meta http-equiv="REFRESH" content="%d; url=%s?returnURI=%s">
543%s
544</head>
545<body>
546%s
547</body>
548</html>""" % \
549    (pageTitle, delayTime, requestURI, returnURI, headTags, redirectMsg)
550
551
552    #_________________________________________________________________________
553    def _receiveCredsResponse(self, encodedExpiry=None):
554        """Remote site receives returned credentials and creates a new cookie
555        for its domain"""
556        sessCookie = self._createCookie(encodedExpiry=encodedExpiry)
557        self.showCredsReceived(sessCookie)
558       
559
560#_____________________________________________________________________________
561class IdentityProviderSecurityCGI(_SecurityCGI):
562    """CGI for an NDG Identity provider.  The IdP serves user credentials
563    back to a requesting Service Provider.
564   
565    An NDG Service Provider redirects the user's browser to an IdP login
566    script using this class.  If the user is already logged in, then a
567    security cookie will be present and it's content returned to the SP by
568    http redirect.  If no cookie is present the user must login first but then
569    similarly, the content of the new cookie is returned to the SP.  The SP
570    can them set a new security cookie it's target domain."""
571   
572    # _processCredsRequest must check that the returnURI given uses HTTPS
573    __httpsSpecifier = "https:"
574   
575   
576    #_________________________________________________________________________
577    def __init__(self, 
578                 smWSDLuri,
579                 smPubKeyFilePath=None,
580                 userName=None,
581                 passPhrase=None,
582                 **securityCGIKwArgs):
583        """smWSDLuri:         URI For Session Manager WSDL used for user
584                              authentication
585        smPubKeyFilePath:     file path for Session Manager public key.  If
586                              not set it will be retrieved using a
587                              Session Manager WS call.
588        userName:             normally set from user input to form in
589                              showLogin()
590        passPhrase:           ditto"""
591
592        self.smWSDLuri = smWSDLuri
593        self.smClnt = None
594       
595        self.smPubKeyFilePath = smPubKeyFilePath
596       
597        self.userName = userName
598        self.passPhrase = passPhrase
599                               
600        super(self.__class__, self).__init__(**securityCGIKwArgs)
601
602   
603    #_________________________________________________________________________
604    def processFields(self, **kwargs):
605        """Call appropriate actions according to the fields set"""
606   
607        bAuthorise = "authorise" in self
608       
609        if 'returnURI' in self:
610            # Identity provider receives request from remote site for
611            # credentials and returns them
612            self._processCredsRequest(**kwargs)
613
614        elif 'authenticate' in self:
615            # User has entered login details - now authenticate using the
616            # Session Manager WS
617            sessCookie = self._authenticate()
618           
619            if bAuthorise:
620                # Authorisation and authentication arguments were set -
621                # Now call authorisation passing the session cookie
622                self._getAttCert(sessCookie)
623           
624            if 'returnURI' in self:
625                # The authentication process is as a result of a redirect
626                # request from another site - redirect back to the remote site
627                # returning the credentials contained in the NDG security
628                self._processCredsRequest(sessCookie=sessCookie,
629                                          setCookie=True, 
630                                          **kwargs)
631               
632        elif bAuthorise:
633            # Handle a get attribute certificate request
634            self._getAttCert()
635                   
636        else:
637            # Remote site presents possible sites for user to get their
638            # credentials from
639            self.showHomeSiteSelect(**kwargs)
640
641
642    #_________________________________________________________________________
643    def _processCredsRequest(self,
644                             returnURI=None,
645                             bAuthorise=False, 
646                             sessCookie=None, 
647                             **returnCredsResponseKwArgs):
648        """Receive request from a Service Provider for credentials.  Process
649        and return via a redirect"""
650
651        if returnURI is None:
652            returnURI = self['returnURI'].value
653       
654                                                     
655        # Check for cookie in environment
656        if sessCookie is None and 'HTTP_COOKIE' in os.environ:
657   
658            # Get session ID from existing cookie
659            sessCookie = SimpleCookie(os.environ['HTTP_COOKIE'])
660            bValidSessCookie = UserSession.isValidSecurityCookie(sessCookie)
661           
662        else:
663            bValidSessCookie = False
664       
665             
666        # Check that the returnURI is over https, if not credentials would be
667        # returned to the service provider in clear text and could be snooped
668        # The exception to this is where returnURI and IdP URI are in the
669        # same domain.  In this case credentials would not be passed between
670        # SP and IdP URIs anyway
671        if returnURI[0:6] != self.__httpsSpecifier and \
672           not bValidSessCookie or \
673           sessCookie[UserSession.cookieTags[0]]['domain'] not in returnURI:           
674            raise SecurityCGIError, "Specified returnURI must use HTTPS"
675
676
677        # Check for NDG cookie
678        if sessCookie and bValidSessCookie:
679               
680            # Return cookie to requestor
681            self._returnCredsResponse(sessCookie, 
682                                      returnURI, 
683                                      **returnCredsResponseKwArgs)
684
685        else:
686            # No cookie present - display login.  Submit must redirect back to
687            # this script with '?authenticate=1&returnURI=<...>'
688            self.showLogin(returnURI=returnURI,
689                           bAuthorise=bAuthorise, 
690                           pageTitle="NDG Login")
691
692   
693    #_________________________________________________________________________
694    def _returnCredsResponse(self,
695                             sessCookie, 
696                             returnURI=None,
697                             pageTitle='',
698                             hdrTxt='',
699                             delayTime=0,
700                             redirectMsg='',
701                             setCookie=False):
702        """User's Identity Provider returns credentials to requestor via a
703        HTTP redirect
704       
705        sessCookie:   NDG Session cookie
706        pageTitle:    Give the redirect page a title
707        headTags:     Optionally add additional tags in <head/> section
708        delayTime:    time in seconds before carrying out redirect - redirect
709                      page will be displayed in this interval
710        redirectMsg:  Message to put on redirect page.  Can be plain text or
711                      formatted HTML
712        setCookie:    Set to True to set the cookie in the IdP's target
713                      domain.  This could be used in the case where the user
714                      has just logged in but the session cookie has not been
715                      set yet."""
716
717        if returnURI is None:
718            returnURI = self['returnURI'].value
719                                         
720        if setCookie:
721            cookieTxt = sessCookie.output() + os.linesep
722        else:
723            cookieTxt = ''
724
725
726        # Check to see if the returnURI is in the same domain - if so there's
727        # no need to return any credentials in the redirect
728        cookieDomain = sessCookie[UserSession.cookieTags[0]]['domain']
729        if cookieDomain and cookieDomain in returnURI:
730            credArgs = ''
731           
732        else:
733            # returnURI is in a different domain - return the credentials
734            # Add credentials to URI but loop through so as to not have to
735            # refer to the tag names directly.  Tag names are abstracted
736            # behind the UserSession interface
737            sessCookieArgs = '&'.join(["%s=%s" % (tag, sessCookie[tag].value)\
738                                       for tag in UserSession.cookieTags])
739           
740            b64encExpiry = base64.urlsafe_b64encode(\
741                            sessCookie[UserSession.cookieTags[0]]['expires'])
742
743            # Nb. Allow for case where return URI already includes some args           
744            credArgs = "%s%s&expires=%s" % \
745            ('?' in returnURI and '&' or '?', sessCookieArgs, b64encExpiry)
746                 
747           
748            print """Content-type: text/html
749%s
750<html>
751<head>
752<title>%s</title>
753<meta http-equiv="REFRESH"
754content="%d; url=%s%s">
755%s
756</head>
757<body>
758%s
759</body>
760</html>""" % (cookieTxt, 
761              pageTitle, 
762              delayTime, 
763              returnURI, 
764              credArgs, 
765              hdrTxt,
766              redirectMsg)
767   
768   
769    #_________________________________________________________________________
770    def _authenticate(self, bAuthorise=False):
771        """Authenticate username and passphrase input from preceeding login
772        form
773
774        bAuthorise: set to True so that if an error occurs, login will be
775                    recalled followed by authorisation"""
776
777        self.userName = 'userName' in self and self['userName'].value or None
778        self.passPhrase = \
779                    'passPhrase' in self and self['passPhrase'].value or None
780                   
781        returnURI = 'returnURI' in self and self['returnURI'].value or None
782           
783           
784        if self.userName is None:
785            self.showLogin(returnURI=returnURI,
786                           bAuthorise=bAuthorise,
787                           pageTitle="Login - error no username set")
788            raise SecurityCGIError, "no username set for authentication"
789
790        if self.passPhrase is None:
791            self.showLogin(returnURI=returnURI,
792                           bAuthorise=bAuthorise,
793                           pageTitle="Login - error no pass-phrase set")
794            raise SecurityCGIError, "no pass-phrase set for authentication"
795
796
797        # Instantiate WS proxy and request connection
798        try:
799            if not self.smClnt:
800                self.smClnt = SessionClient(smWSDLuri=self.smWSDLuri,
801                               smPubKeyFilePath=self.smPubKeyFilePath,
802                               clntPubKeyFilePath=self.clntPubKeyFilePath,
803                               clntPriKeyFilePath=self.clntPriKeyFilePath,
804                               traceFile=self._wsDebug and sys.stderr or None)
805
806            sSessCookie = self.smClnt.connect(userName=self.userName,
807                                         pPhrase=self.passPhrase,
808                                         clntPriKeyPwd=self.clntPriKeyPwd)
809            sessCookie = SimpleCookie(sSessCookie)
810            return sessCookie
811
812        except Exception, e:
813            self.showLogin(returnURI=returnURI,
814                           bAuthorise=bAuthorise,
815                           pageTitle="Login - internal error")
816            raise SecurityCGIError, "Session client: %s" % e
817
818   
819    #_________________________________________________________________________
820    def showLogin(self,
821                  returnURI=None,
822                  contentTypeHdr=True,
823                  htmlTag=True,
824                  pageTitle='',
825                  hdrTxt=__defStyle,
826                  headTag=True,
827                  bodyTag=True,
828                  bAuthorise=False):
829        """Display initial NDG login form"""
830   
831        if contentTypeHdr: print "Content-type: text/html\n\n"
832       
833        if htmlTag: print "<html>"
834   
835        if headTag:
836            print """<head>
837        <title>%s</title>
838        %s
839    </head>""" % (pageTitle, hdrTxt)
840   
841   
842        if bodyTag: print "<body>"
843
844
845        if returnURI is None and 'returnURI' in self:
846            returnURI = self['returnURI'].value
847
848        if returnURI:
849            returnURIfield = \
850                "<input type=hidden name=returnURI value=\"%s\">" % returnURI
851        else:
852            returnURIfield = ''
853       
854   
855        if bAuthorise:
856            authoriseArg = "<input type=hidden name=authorise value=\"1\">"
857        else:
858            authoriseArg = ""
859   
860   
861        # Set authorisation method default
862        acMappingFlagChkBox = {}.fromkeys(self.acMappingFlag, '')
863   
864        if self.acMappingFlag is None:
865            # Default to safest option for user
866            acMappingFlagChkBox[self.acAllowMappingWithPrompt] = ' checked'
867        else:
868            acMappingFlagChkBox[self.acMappingFlag] = ' checked'
869   
870        print \
871    """<script language="javascript">
872    <!--
873        function toggleLayer(layerId)
874        {
875            if (document.getElementById)
876            {
877                // Standard
878                var style = document.getElementById(layerId).style;
879            }
880            else if (document.all)
881            {
882                // Old msie versions
883                var style = document.all[whichLayer].style;
884            }
885            else if (document.layers)
886            {
887                // nn4
888                var style = document.layers[whichLayer].style;
889            }
890            style.visibility = style.visibility == "visible" ?
891"hidden":"visible";        }
892    //-->
893    </script>
894    <h3>NERC Data Grid Site Login (Test)<BR clear=all></h3>
895    <hr>
896   
897    <form action="%s" method="POST">
898   
899    <table bgcolor=#ADD8E6 cellspacing=0 border=0 cellpadding=5>
900    <tbody>
901    <tr><td>User Name:</td> <td><input type=text name=userName value="">
902    </td></tr>
903    <tr>
904        <td>Password:</td>
905        <td><input type=password name=passPhrase></td>
906    </tr>
907    <tr>
908        <td colspan="2" align="right">
909            <a href="javascript:toggleLayer('advSettings');">Advanced
910Settings</a>            <input type=submit value="Login">
911        </td>
912    </tr>
913    <input type=hidden name=authenticate value="1">
914    %s"""  % (self.scriptName, returnURIfield)
915   
916        print \
917    """</tbody></table>
918    <br>
919    <div id="advSettings" style="position: relative; visibility: hidden;">
920        <h4>Role Mapping for access to other trusted sites</h4>
921        <p>Your account has roles or <i>privileges</i> which determine what
922        data you have access to.  If you access data at another NDG trusted
923        site, these roles can be mapped to local roles at that site to help
924        you gain access:
925        </p>   
926        <table bgcolor=#ADD8E6 cellspacing=0 border=0 cellpadding=5>
927        <tbody>
928        <tr>
929            <td>
930                <input type="radio" name="authorisationMethod" value="%s"%s>
931            </td>
932            <td>
933            Allow my roles to be mapped to local roles at other NDG trusted
934            sites.
935            </td>
936        </tr>
937        <tr>
938            <td>
939                <input type="radio" name="authorisationMethod" value="%s"%s>
940            </td>
941            <td>
942            Allow my roles to be mapped, but prompt me so that I may choose
943            which roles to map before gaining access.
944            </td>
945        <tr>
946            <td>
947                <input type="radio" name="authorisationMethod" value="%s"%s>
948            </td>
949            <td>
950                Don't allow mapping of my roles.
951            </td>
952        </tr>
953        </tbody>
954        </table>
955    </div>
956    </form>
957    """ % (self.acAllowMapping,
958           acMappingFlagChkBox[self.acAllowMapping], 
959           self.acAllowMappingWithPrompt,
960           acMappingFlagChkBox[self.acAllowMappingWithPrompt],
961           self.acNoMapping,
962           acMappingFlagChkBox[self.acNoMapping])
963   
964        if bodyTag: print "</body>"
965        if htmlTag: print "</html>"
966   
967        # end of showLogin()
Note: See TracBrowser for help on using the repository browser.