Ignore:
Timestamp:
13/05/08 09:34:07 (13 years ago)
Author:
pjkersha
Message:

Security Single Sign On code separated out of ows_server code stack and put in ndg.security. ows_server can still run single sign on but in alternate modes:

  • Single Sign On Service run from within ows_server code stack - all SSO controllers, templates and globals are imported from ndg.security
  • ... or ows_server runs as a client to a Single Sign On service running in a separate paster instance. ows_server imports SSO client interface code from ndg.security

ows_server/development.ini:

  • added logging config as available with Pylons 0.9.6

ows_server/ndgDiscovery.config: [NDG_SECURITY] settings are now divided into sub sections:

  • NDG_SECURITY.ssoClient - for running a client to a Single Sign On service
  • NDG_SECURITY.ssoService - for running an integral SSO service
  • NDG_SECURITY.wssecurity - digital signature for web service interfaces
  • TODO: separate section for Gatekeeper


ows_server/ows_server/models/ndgSecurity.py: get rid of sslPeerCertDN setting to SM client - not needed

ows_server/ows_server/config/environment.py: include templates from ndg.security.server.sso

ows_server/ows_server/config/ndgMiddleware.py: call separate security SSO service/client middleware set-up

ows_server/ows_server/config/routing.py,
ows_server/ows_server/controllers/wayf.py: separate wayf controller

ows_server/ows_server/controllers/login.py: code moved to ndg.security.server.sso.sso.controllers.login ows_server login extends this class
ows_server/ows_server/controllers/logout.py: likewise for logout - inherit from ndg.security.server.sso equivalent

ows_server/ows_server/lib/security_util.py:

  • stripFromURI returns str type not unicode
  • SecurityConfig? class no longer needed - code transfered to ndg.security

ows_server/ows_server/lib/base.py: remove security handling code and instead inherit from ndg.security.client.ssoclient.ssoclient.base.BaseController?

ows_server/ows_server/public/layout/ndg2.css: fix to header image path

ows_server/ows_server/templates/ndgPage.kid: embed code to base 64 encode return to URL

File:
1 edited

Legend:

Unmodified
Added
Removed
  • TI05-delivery/ows_framework/trunk/ows_server/ows_server/controllers/login.py

    r3536 r3893  
    44 
    55from ows_server.lib.base import * 
    6 from ows_server.lib.security_util import setSecuritySession, SecuritySession,\ 
    7                                          LoginServiceQuery 
    8 from ows_common.exception_report import OwsError 
    9 from paste.request import parse_querystring 
     6 
    107import logging 
    118log = logging.getLogger(__name__) 
    129 
    13 from ndg.security.common.AttAuthority import AttAuthorityClient 
    14 from ndg.security.common.SessionMgr import SessionMgrClient, SessionExpired, \ 
    15     AttributeRequestDenied 
    16 from ndg.security.common.m2CryptoSSLUtility import HTTPSConnection, \ 
    17     HostCheck, InvalidCertSignature, InvalidCertDN 
    18  
    19  
    20 class LoginController(BaseController): 
    21     ''' Provides the pylons controller for local login ''' 
     10try: 
     11    from ndg.security.server.sso.sso.controllers.login \ 
     12        import LoginController as _LoginController 
     13         
     14    class LoginController(_LoginController): 
     15        '''Provides the pylons controller for Login.  This is a wrapper class. 
     16        - All functionality is provided from ndg.security.server.sso.sso 
     17        the NDG Security Single Sign On Service package''' 
     18             
     19except ImportError, e: 
     20    from warnings import warn 
     21    warn("Importing LoginController for Single Sign On Service: %s" % e,  
     22         RuntimeWarning) 
    2223     
    23     def __before__(self, action):  
    24         """For each action, get 'r' return to URL argument from current URL  
    25         query string.  c.returnTo is used in some of the .kid files""" 
    26         c.returnTo = request.params.get('r', '') 
     24    class LoginController(BaseController): 
     25        '''Raise a 404 error for case where Single Sign ON Service is disabled 
     26        '''     
    2727         
    28         # Check return to address - getCredentials should NOT be returned to 
    29         # with its query args intact 
    30         b64decReturnTo = base64.urlsafe_b64decode(c.returnTo) 
    31         scheme, netloc, pathInfo, query, frag = urlsplit(b64decReturnTo) 
    32         if 'getCredentials' in pathInfo: 
    33             # Swap to discovery and remove sensitive creds query args 
    34             # 
    35             # TODO: re-write to be more robust and modular.  Nb.  
    36             # BaseController.__call__ should filter out 'getCredentials' 
    37             # calls from c.requestURL so this code should never need to be  
    38             # executed. 
    39             filteredReturnTo = urlunsplit((scheme,netloc,'/discovery','','')) 
    40             c.returnTo = base64.urlsafe_b64encode(filteredReturnTo) 
    41          
    42         # Check return to address - getCredentials should NOT be returned to 
    43         # with its query args intact 
    44         log.debug("LoginController.__before__: Decoded c.returnTo = %s" % \ 
    45                                       base64.urlsafe_b64decode(c.returnTo)) 
    46      
    47      
    48     def index(self): 
    49         ''' Ok, you really want to login here ''' 
    50         log.debug("LoginController.index ...")    
    51  
    52         if 'ndgSec' not in session:  
    53             log.debug('No security session details found - offering login...') 
    54             return render('login') 
    55          
    56         # Session is set in this domain - check it  
    57         try:     
    58             smClnt = SessionMgrClient(uri=session['ndgSec']['h'], 
    59                     sslCACertFilePathList=g.securityCfg.sslCACertFilePathList, 
    60                     sslPeerCertCN=g.securityCfg.sslPeerCertCN, 
    61                     signingCertFilePath=g.securityCfg.wssCertFilePath, 
    62                     signingPriKeyFilePath=g.securityCfg.wssPriKeyFilePath, 
    63                     signingPriKeyPwd=g.securityCfg.wssPriKeyPwd, 
    64                     caCertFilePathList=g.securityCfg.wssCACertFilePathList, 
    65                     tracefile=g.securityCfg.tracefile) 
    66                                  
    67         except Exception, e: 
    68             c.xml='Error establishing security context.  Please report ' + \ 
    69                   'the error to your site administrator' 
    70             log.error("Initialising SessionMgrClient for " + \ 
    71                       "getSessionStatus call: %s" % e) 
    72             SecuritySession.delete() 
    73             response.status_code = 400 
    74             return render('content') 
    75          
    76         # Check session status 
    77         log.debug('Calling Session Manager "%s" getSessionStatus ' % \ 
    78                   session['ndgSec']['h'] + 'for user "%s" with sid="%s" ...'%\ 
    79                   (session['ndgSec']['u'], session['ndgSec']['sid'])) 
    80         try: 
    81             bSessOK = smClnt.getSessionStatus(sessID=session['ndgSec']['sid']) 
    82         except Exception, e: 
    83             c.xml = "Error checking your session details.  Please re-login" 
    84             log.error("Session Manager getSessionStatus returned: %s" % e) 
    85             SecuritySession.delete() 
    86             response.status_code = 400 
    87             return render('login') 
    88     
    89         if bSessOK: 
    90             log.debug("Session found - redirect back to site requesting " + \ 
    91                       "credentials ...") 
    92             # ... Return across http GET passing security parameters... 
    93             return self.__doRedirect() 
    94         else: 
    95             log.debug("Session wasn't found - removing security details " + \ 
    96                       "from cookie and re-displaying login...") 
    97             SecuritySession.delete() 
    98             return render('login') 
    99  
    100  
    101     def getCredentials(self): 
    102         """Authenticate user and cache user credentials in 
    103         Session Manager following user login""" 
    104         log.debug("LoginController.getCredentials ...")    
    105  
    106         try:     
    107             smClnt = SessionMgrClient(uri=g.securityCfg.smURI, 
    108                     sslCACertFilePathList=g.securityCfg.sslCACertFilePathList, 
    109                     sslPeerCertCN=g.securityCfg.sslPeerCertCN, 
    110                     signingCertFilePath=g.securityCfg.wssCertFilePath, 
    111                     signingPriKeyFilePath=g.securityCfg.wssPriKeyFilePath, 
    112                     signingPriKeyPwd=g.securityCfg.wssPriKeyPwd, 
    113                     caCertFilePathList=g.securityCfg.wssCACertFilePathList, 
    114                     tracefile=g.securityCfg.tracefile) 
    115                                  
    116             username = request.params['username'] 
    117             passphrase = request.params['passphrase']                      
    118                                  
    119         except Exception, e: 
    120             c.xml='Error establishing security context.  Please report ' + \ 
    121                   'the error to your site administrator' 
    122             log.error("Login: initialising SessionMgrClient: %s" % e) 
    123             response.status_code = 400 
    124             return render('content') 
    125          
    126         # Connect to Session Manager 
    127         log.debug('Calling Session Manager "%s" connect for user "%s" ...' % \ 
    128                   (g.securityCfg.smURI, username)) 
    129         try: 
    130             sessID = smClnt.connect(username, passphrase=passphrase)[-1] 
    131         except Exception, e: 
    132             c.xml = "Error logging in.  Please check your username/" + \ 
    133                     "pass-phrase and try again." 
    134             log.error("Session Manager connect returned: %s" % e) 
    135             response.status_code = 401 
    136             return render('login') 
    137          
    138         # Cache user attributes in Session Manager 
    139         log.debug("Calling Session Manager getAttCert for user ") 
    140         try: 
    141             # Make request for attribute certificate 
    142             attCert = smClnt.getAttCert(sessID=sessID,  
    143                                         attAuthorityURI=g.securityCfg.aaURI) 
    144         except SessionExpired, e: 
    145             log.info("Session expired getting Attribute Certificate: %s" % e) 
    146             c.xml = "Session has expired, please re-login" 
    147             response.status_code = 401 
    148             return render('login') 
     28        def index(self): 
     29            ''' Ok, you really want to login here ''' 
     30            log.info("Single Sign On Service is disabled setting 404 error...") 
     31            abort(404) 
    14932             
    150         except AttributeRequestDenied, e: 
    151             log.error("Login: attribute Certificate request denied: %s" % e) 
    152             c.xml = "No authorisation roles are available for your " + \ 
    153                     "account.  Please check with your site administrator." 
    154             response.status_code = 401 
    155             return render('login') 
    156              
    157         except Exception, e: 
    158             log.error("Login: attribute Certificate request: %s" % e) 
    159             c.xml = "An internal error occured.  Please report this to " + \ 
    160                     "your site administrator." 
    161             response.status_code = 400 
    162             return render('login') 
    163  
    164         log.debug('Completing login...') 
    165          
    166         # Make security session details 
    167         setSecuritySession(h=g.securityCfg.smURI, 
    168                            u=username, 
    169                            org=attCert.issuerName, 
    170                            roles=attCert.roles, 
    171                            sid=sessID) 
    172         session.save() 
    173  
    174         log.info("user %s logged in with roles %s" % (session['ndgSec']['u'], 
    175                                                   session['ndgSec']['roles'])) 
    176         return self.__doRedirect() 
    177              
    178              
    179     def wayf(self): 
    180         ''' NDG equivalent to Shibboleth WAYF ''' 
    181         log.debug("LoginController.wayf ...")    
    182  
    183         # May be better as a 'g' global set-up at start-up? 
    184         # 
    185         # tracefile could be removed for production use 
    186         aaClnt = AttAuthorityClient(uri=g.securityCfg.aaURI, 
    187                     signingCertFilePath=g.securityCfg.wssCertFilePath, 
    188                     signingPriKeyFilePath=g.securityCfg.wssPriKeyFilePath, 
    189                     signingPriKeyPwd=g.securityCfg.wssPriKeyPwd, 
    190                     caCertFilePathList=g.securityCfg.wssCACertFilePathList, 
    191                     tracefile=g.securityCfg.tracefile) 
    192  
    193         # Get list of login uris for trusted sites including THIS one 
    194         log.debug("Calling Attribute Authority getTrustedHostInfo and " + \ 
    195                   "getHostInfo for wayf") 
    196  
    197         hosts = aaClnt.getAllHostsInfo()     
    198         c.providers=dict([(k, v['loginURI']) for k, v in hosts.items()]) 
    199          
    200         session.save() 
    201          
    202         return render('wayf') 
    203          
    204          
    205     def __doRedirect(self): 
    206         """Pass security creds back to requestor so that they can make 
    207         a cookie.  If the requestor is in the same domain as the login then 
    208         this is not necessary.""" 
    209          
    210         # and now go back to whence we had come 
    211         if c.returnTo!='': 
    212             # is there a keyword on redirect_to that can make this https? See: 
    213             # http://pylonshq.com/project/pylonshq/browser/Pylons/trunk/pylons/decorators/secure.py#L69 
    214  
    215             # Only add token if return URI is in a different domain 
    216             thisHostname = request.host.split(':')[0] 
    217              
    218             # Decode return to address 
    219             cc = base64.urlsafe_b64decode(c.returnTo) 
    220             log.debug('Login redirect to [%s]' % cc) 
    221  
    222             returnToHostname = urlsplit(cc)[1] 
    223 #            returnToHostname = 'localhost' 
    224 #            if thisHostname not in returnToHostname: 
    225             if True: 
    226                 # Returning to a different domain - copy the security session 
    227                 # details into the URL query string 
    228                 if '?' in cc: 
    229                     cc+='&%s' % LoginServiceQuery() 
    230                 else: 
    231                     cc+='?%s' % LoginServiceQuery() 
    232              
    233             # Check return-to address by examining peer cert 
    234             log.debug("Checking return-to URL for valid SSL peer cert. ...") 
    235              
    236             # Look-up list of Cert DNs for trusted requestors 
    237             aaClnt = AttAuthorityClient(uri=g.securityCfg.aaURI, 
    238                     signingCertFilePath=g.securityCfg.wssCertFilePath, 
    239                     signingPriKeyFilePath=g.securityCfg.wssPriKeyFilePath, 
    240                     signingPriKeyPwd=g.securityCfg.wssPriKeyPwd, 
    241                     caCertFilePathList=g.securityCfg.wssCACertFilePathList, 
    242                     tracefile=g.securityCfg.tracefile) 
    243              
    244             HostInfo = aaClnt.getAllHostsInfo() 
    245             requestServerDN = [val['loginRequestServerDN'] \ 
    246                                for val in HostInfo.values()] 
    247             log.debug("Expecting DN for SSL peer one of: %s"%requestServerDN) 
    248             hostCheck=HostCheck(acceptedDNs=requestServerDN, 
    249                     caCertFilePathList=g.securityCfg.sslCACertFilePathList)             
    250             testConnection = HTTPSConnection(returnToHostname,  
    251                                              None,  
    252                                              postConnectionCheck=hostCheck) 
    253  
    254             log.debug('Testing connection to "%s"' % returnToHostname) 
    255             try: 
    256                 try: 
    257                     testConnection.connect() 
    258                 except (InvalidCertSignature, InvalidCertDN), e: 
    259                     log.error("Login: requestor SSL certificate: %s" % e) 
    260                     c.xml = """Request to redirect back to %s with your  
    261 credentials refused: there is a problem with the SSL certificate of this site. 
    262   Please report this to your site administrator.""" % returnToHostname 
    263                     response.status_code = 400 
    264                     return render('login') 
    265             finally:     
    266                 testConnection.close() 
    267  
    268             log.debug("SSL peer cert. is OK - redirecting to [%s] ..." % cc) 
    269             h.redirect_to(cc) 
    270         else: 
    271             c.xml='<p> Logged in </p>' 
    272             return render('content') 
Note: See TracChangeset for help on using the changeset viewer.