Changeset 1337 for TI12-security


Ignore:
Timestamp:
27/07/06 14:39:50 (13 years ago)
Author:
pjkersha
Message:

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.

Location:
TI12-security/trunk/python
Files:
2 added
4 edited

Legend:

Unmodified
Added
Removed
  • TI12-security/trunk/python/NDG/SecurityCGI.py

    r1331 r1337  
    11#!/usr/bin/env python 
    22 
    3 """NDG Security CGI services 
     3"""NDG Security CGI Interface between Service Providers and Identiy Providers 
    44 
    55NERC Data Grid Project 
     
    3232    pass 
    3333 
    34     
     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   
    3554class _SecurityCGI(cgi.FieldStorage): 
    36     """CGI interface base class for NDG Security 
    37      
    38     Terms used throughout:         
    39         Service Provider   - where user is accessing resources 
    40         Identity Provider  - where user's credentials are held or they login 
     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 
    4161    """ 
    4262 
     
    4969    #_________________________________________________________________________ 
    5070    def __init__(self, 
    51                  smWSDL, 
    52                  smPubKeyFilePath=None, 
    5371                 clntPubKeyFilePath=None, 
    5472                 clntPriKeyFilePath=None, 
     
    5775                 wsDebug=False, 
    5876                 **cgiFieldStorageKwArgs): 
    59         """smWSDL:            URI For Session Manager WSDL used for user 
    60                               authentication 
    61         wsDebug:              print output from WS transactions to stderr""" 
    62  
    63         self.smWSDL = smWSDL 
    64         self.smClnt = None 
    65          
    66         self.smPubKeyFilePath = smPubKeyFilePath 
     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        """ 
    6788         
    6889        self.clntPubKeyFilePath = clntPubKeyFilePath 
     
    7697        self.attCert = None 
    7798                 
     99        # Read fields so that self becomes a dictionary of the fields 
    78100        cgi.FieldStorage.__init__(self, **cgiFieldStorageKwArgs) 
    79101 
     
    167189            if not self.smClnt: 
    168190                self.smClnt = SessionClient(\ 
    169                             smWSDL=self.smWSDL, 
     191                            smWSDLuri=self.smWSDLuri, 
    170192                            smPubKeyFilePath=self.smPubKeyFilePath, 
    171193                            clntPubKeyFilePath=self.clntPubKeyFilePath, 
     
    174196 
    175197            authzResp = self.smClnt.reqAuthorisation(sessCookie=sessCookie, 
    176                                     aaWSDL=self.aaWSDL, 
     198                                    aaWSDLuri=self.aaWSDLuri, 
    177199                                    aaPubKey=self.aaPubKey, 
    178200                                    reqRole=reqRole, 
     
    273295#_____________________________________________________________________________ 
    274296class ServiceProviderSecurityCGI(_SecurityCGI): 
    275  
     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     
    276305    #_________________________________________________________________________ 
    277306    def __init__(self, 
    278                  smWSDL, 
    279                  aaWSDL, 
     307                 aaWSDLuri, 
     308                 aaPubKeyFilePath=None, 
     309                 smWSDLuri=None, 
    280310                 smPubKeyFilePath=None, 
    281                  aaPubKeyFilePath=None, 
    282311                 trustedHostInfo=None, 
    283312                 cookieLifetimeHrs=8, 
    284313                 **securityCGIKwArgs): 
    285         """smWSDL:            URI For Session Manager WSDL used for user 
    286                               authentication 
    287         aaWSDL:               URI for Attribute Authority WSDL used to get a 
     314        """aaWSDLuri:         URI for Attribute Authority WSDL used to get a 
    288315                              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. 
    289322        trustedHostInfo:      dictionary of URIs for trusted hosts indexed by 
    290323                              hostname 
    291         cookieLifetimeHrs:    cookie lifetime in hours 
     324        cookieLifetimeHrs:    cookie lifetime in hours for new cookie set in 
     325                              Service Provider target domain. 
    292326        wsDebug:              print output from WS transactions to stderr""" 
    293327 
    294         self.smWSDL = smWSDL 
     328        self.aaWSDLuri = aaWSDLuri 
     329        self.aaClnt = None 
     330         
     331        self.aaPubKeyFilePath = aaPubKeyFilePath 
     332 
     333        self.smWSDLuri = smWSDLuri 
    295334        self.smClnt = None 
    296335         
    297         self.aaWSDL = aaWSDL 
    298         self.aaClnt = None 
    299          
    300336        self.smPubKeyFilePath = smPubKeyFilePath 
    301         self.aaPubKeyFilePath = aaPubKeyFilePath 
     337 
    302338 
    303339        self.trustedHostInfo = trustedHostInfo 
     
    312348        self.attCert = None 
    313349                 
    314         super(self.__class__, self).__init__(self, **securityCGIKwArgs) 
     350        super(self.__class__, self).__init__(**securityCGIKwArgs) 
    315351 
    316352  
     
    328364            encodedExpiry='expires' in self and self['expires'].value or None 
    329365 
    330             self._receiveCredsResponse(encodedExpiry=encodedExpiry, 
    331                                        **kwargs) 
     366            self._receiveCredsResponse(encodedExpiry=encodedExpiry, **kwargs) 
    332367     
    333368     
     
    339374                           htmlTag=False, 
    340375                           hdrTag=False, 
    341                            hdrTxt='', 
     376                           hdrTxt=__defStyle, 
    342377                           bodyTag=False, 
    343378                           pageTitle=""): 
     
    360395 
    361396        if hdrTag:             
    362             if not hdrTxt: 
    363                 hdrTxt = """    <style type=\"text/css\"> 
    364 <!-- 
    365 .al { 
    366 text-align: justify 
    367 } 
    368 a{ 
    369 text-decoration:none; 
    370 } 
    371 a:hover{ 
    372 color:#0000FF; 
    373 } 
    374     body { font-family: Verdana, sans-serif; font-size: 11} 
    375     table { font-family: Verdana, sans-serif; font-size: 11} 
    376 --> 
    377 </style>""" 
    378  
    379397            print """<head> 
    380398             
     
    387405            print "<body>\n" 
    388406 
    389             print """ 
     407        # Form containing droplist for the trusted hosts 
     408        print """ 
    390409    <form action="%s" method="POST"> 
    391410    <table bgcolor=#ADD8E6 cellspacing=0 border=0 cellpadding=5> 
     
    396415          <option value="">Select your home site...""" % self.scriptName 
    397416           
    398             for hostname, info in self.trustedHostInfo.items(): 
    399                 print "<option value=\"%s\">%s" % (info['loginURI'], hostname) 
     417        for hostname, info in self.trustedHostInfo.items(): 
     418            print "<option value=\"%s\">%s" % (info['loginURI'], hostname) 
    400419                 
    401             print \ 
     420        print \ 
    402421"""        </select> 
    403422      </td> 
     
    420439 
    421440    #_________________________________________________________________________ 
    422     def showCredsReceived(self, sessCookie): 
     441    def showCredsReceived(self,  
     442                          sessCookie, 
     443                          pageTitle='Credentials Received', 
     444                          hdrTxt=__defStyle, 
     445                          bodyText='NDG Security session cookie set'): 
    423446        """Called from _receiveCredsResponse() once a cookie has been created. 
    424447        Makes a page to set the cookie and display to the user that they have 
    425448        been authenticated.  Derived class should override this method as 
    426449        required""" 
     450         
    427451        print """Content-type: text/html" 
    428452%s 
     
    430454<html> 
    431455<head> 
    432 <title>Credentials Received</title> 
     456<title>%s</title> 
     457%s 
    433458</head> 
    434459<body> 
    435     Credentials Received 
     460    %s 
    436461</body> 
    437 </html>""" % sessCookie.output() 
     462</html>""" % (sessCookie.output(), pageTitle, hdrTxt, bodyTxt) 
    438463 
    439464 
     
    445470        try: 
    446471            if not self.aaClnt: 
    447                 self.aaClnt = AttAuthorityClient(aaWSDL=self.aaWSDL, 
     472                self.aaClnt = AttAuthorityClient(aaWSDLuri=self.aaWSDLuri, 
    448473                            aaPubKeyFilePath=self.aaPubKeyFilePath, 
    449474                            clntPubKeyFilePath=self.clntPubKeyFilePath, 
     
    535560#_____________________________________________________________________________ 
    536561class IdentityProviderSecurityCGI(_SecurityCGI): 
    537  
    538     #_________________________________________________________________________ 
    539     def __init__(self, smWSDL, **securityCGIKwArgs): 
    540         """smWSDL:            URI For Session Manager WSDL used for user 
    541                               authentication""" 
    542  
    543         self.smWSDL = smWSDL 
     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 
    544593        self.smClnt = None 
     594         
     595        self.smPubKeyFilePath = smPubKeyFilePath 
    545596         
    546597        self.userName = userName 
    547598        self.passPhrase = passPhrase 
    548          
    549         self.smPubKeyFilePath = smPubKeyFilePath 
    550599                                 
    551         super(self.__class__, self).__init__(self, **securityCGIKwArgs) 
     600        super(self.__class__, self).__init__(**securityCGIKwArgs) 
    552601 
    553602     
     
    555604    def processFields(self, **kwargs): 
    556605        """Call appropriate actions according to the fields set""" 
    557  
    558      
     606    
    559607        bAuthorise = "authorise" in self 
    560608         
     
    598646                             sessCookie=None,  
    599647                             **returnCredsResponseKwArgs): 
    600         """Receive request from remote site for credentials.  Process and  
    601         return via a redirect""" 
     648        """Receive request from a Service Provider for credentials.  Process  
     649        and return via a redirect""" 
    602650 
    603651        if returnURI is None: 
    604652            returnURI = self['returnURI'].value 
    605                                                           
     653         
     654                                                      
    606655        # Check for cookie in environment 
    607656        if sessCookie is None and 'HTTP_COOKIE' in os.environ: 
     
    609658            # Get session ID from existing cookie 
    610659            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 
    611676 
    612677        # Check for NDG cookie 
    613         if sessCookie and UserSession.isValidSecurityCookie(sessCookie): 
     678        if sessCookie and bValidSessCookie: 
    614679                 
    615680            # Return cookie to requestor 
    616             self.returnCredsResponse(sessCookie,  
    617                                      returnURI,  
    618                                      **returnCredsResponseKwArgs) 
     681            self._returnCredsResponse(sessCookie,  
     682                                      returnURI,  
     683                                      **returnCredsResponseKwArgs) 
    619684 
    620685        else: 
     
    628693    #_________________________________________________________________________ 
    629694    def _returnCredsResponse(self, 
    630                             sessCookie,  
    631                             returnURI=None, 
    632                             pageTitle='', 
    633                             hdrTxt='', 
    634                             delayTime=0, 
    635                             redirectMsg='', 
    636                             setCookie=False): 
     695                             sessCookie,  
     696                             returnURI=None, 
     697                             pageTitle='', 
     698                             hdrTxt='', 
     699                             delayTime=0, 
     700                             redirectMsg='', 
     701                             setCookie=False): 
    637702        """User's Identity Provider returns credentials to requestor via a  
    638703        HTTP redirect 
     
    644709                      page will be displayed in this interval 
    645710        redirectMsg:  Message to put on redirect page.  Can be plain text or 
    646                       formatted HTML""" 
     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.""" 
    647716 
    648717        if returnURI is None: 
     
    654723            cookieTxt = '' 
    655724 
     725 
    656726        # Check to see if the returnURI is in the same domain - if so there's 
    657727        # no need to return any credentials in the redirect 
    658728        cookieDomain = sessCookie[UserSession.cookieTags[0]]['domain'] 
    659729        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             
    660748            print """Content-type: text/html 
    661749%s 
     
    664752<title>%s</title> 
    665753<meta http-equiv="REFRESH" 
    666 content="%d; url=%s"> 
     754content="%d; url=%s%s"> 
    667755%s 
    668756</head> 
     
    670758%s 
    671759</body> 
    672 </html>""" % ( cookieTxt, 
    673                pageTitle, 
    674                delayTime, 
    675                returnURI, 
    676                hdrTxt, 
    677                redirectMsg) 
    678              
    679         else: 
    680             # returnURI is in a different domain - return the credentials 
    681             # 
    682             # Allow for case where return URI already includes some args 
    683             if '?' in returnURI: 
    684                 argSeparator = '&' 
    685             else: 
    686                 argSeparator = '?' 
    687      
    688      
    689             print """Content-type: text/html 
    690 %s 
    691 <html> 
    692 <head> 
    693 <title>%s</title> 
    694 <meta http-equiv="REFRESH" 
    695 content="%d; url=%s%sNDG-ID1=%s&NDG-ID2=%s&expires=%s"> 
    696 %s 
    697 </head> 
    698 <body> 
    699 %s 
    700 </body> 
    701 </html>""" % ( cookieTxt, 
    702                pageTitle, 
    703                delayTime, 
    704                returnURI, 
    705                argSeparator, 
    706                sessCookie['NDG-ID1'].value, 
    707                sessCookie['NDG-ID2'].value, 
    708                base64.urlsafe_b64encode(sessCookie['NDG-ID1']['expires']), 
    709                hdrTxt, 
    710                redirectMsg) 
     760</html>""" % (cookieTxt,  
     761              pageTitle,  
     762              delayTime,  
     763              returnURI,  
     764              credArgs,  
     765              hdrTxt, 
     766              redirectMsg) 
    711767     
    712768     
     
    742798        try: 
    743799            if not self.smClnt: 
    744                 self.smClnt = SessionClient(smWSDL=self.smWSDL, 
     800                self.smClnt = SessionClient(smWSDLuri=self.smWSDLuri, 
    745801                               smPubKeyFilePath=self.smPubKeyFilePath, 
    746802                               clntPubKeyFilePath=self.clntPubKeyFilePath, 
     
    767823                  htmlTag=True, 
    768824                  pageTitle='', 
    769                   hdrTxt='', 
     825                  hdrTxt=__defStyle, 
    770826                  headTag=True, 
    771827                  bodyTag=True, 
     
    778834     
    779835        if headTag: 
    780             if not hdrTxt: 
    781                 hdrTxt = """    <style type=\"text/css\"> 
    782 <!-- 
    783 .al { 
    784 text-align: justify 
    785 } 
    786 a{ 
    787 text-decoration:none; 
    788 } 
    789 a:hover{ 
    790 color:#0000FF; 
    791 } 
    792     body { font-family: Verdana, sans-serif; font-size: 11} 
    793     table { font-family: Verdana, sans-serif; font-size: 11} 
    794 --> 
    795 </style>""" 
    796                      
    797836            print """<head> 
    798837        <title>%s</title> 
     
    898937        <tr> 
    899938            <td> 
    900                 <input type="radio" name="authorisationMethod" value="%s"%s>            </td> 
     939                <input type="radio" name="authorisationMethod" value="%s"%s> 
     940            </td> 
    901941            <td> 
    902942            Allow my roles to be mapped, but prompt me so that I may choose  
  • TI12-security/trunk/python/README

    r1308 r1337  
    1 NDG Security 0.72 Post-Alpha Release (version development 24/07/06) 
    2 ___________________________________________________________________ 
     1NDG Security Post-Alpha Development Version 
     2___________________________________________ 
    33 
    44To install: 
  • TI12-security/trunk/python/setup.py

    r1308 r1337  
    1818{ 
    1919    'name':           'NDG-Security', 
    20     'version':        '0.72', 
     20    'version':        'Dev-PostAlpha', 
    2121    'description':    'NERC DataGrid Security Utilities', 
    2222    'author':         'P J Kershaw', 
Note: See TracChangeset for help on using the changeset viewer.