source: TI12-security/trunk/python/ndg.security.server/ndg/security/server/sso/sso/controllers/login.py @ 3914

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/ndg.security.server/ndg/security/server/sso/sso/controllers/login.py@3914
Revision 3914, 10.9 KB checked in by pjkersha, 12 years ago (diff)
  • New ndg.security.common.zsi_util.httpproxy.ProxyHTTPConnection class replaces urllib2client - easier to fit into existing ZSI client framework.
  • Further OpenID integration into Single Sign On Service. User now authenticates OK but patches needed to AuthKit? + need to handle return_to URL dynamically according to page visited before WAYF call.
Line 
1import logging
2
3from ndg.security.server.sso.sso.lib.base import *
4from ndg.security.common.pylons.security_util import setSecuritySession, \
5    SecuritySession, SSOServiceQuery
6from ndg.security.common.AttAuthority import AttAuthorityClient
7from ndg.security.common.SessionMgr import SessionMgrClient, SessionExpired, \
8    AttributeRequestDenied
9from ndg.security.common.m2CryptoSSLUtility import HTTPSConnection, \
10    HostCheck, InvalidCertSignature, InvalidCertDN
11
12from base64 import urlsafe_b64decode, urlsafe_b64decode
13
14log = logging.getLogger(__name__)
15
16class LoginController(BaseController):       
17       
18    def index(self):
19        '''Initialise Session Manager client context, check for an existing
20        user session.  If found, redirect back to SSO Client, if not found
21        present login'''
22        log.debug("LoginController.index ...")   
23       
24        # Convenience alias
25        cfg = g.ndg.security.server.ssoservice.cfg
26
27        # Check the return to URL
28        c.b64encReturnTo = str(request.params.get('r', ''))
29       
30        if 'ndgSec' not in session: 
31            log.debug('No security session details found - offering login...')
32            return render('ndg.security.kid', 'ndg.security.login')
33       
34        # Session is set in this domain - check it
35        try:   
36            smClnt = SessionMgrClient(uri=session['ndgSec']['h'],
37                                    tracefile=cfg.tracefile,
38                                    httpProxyHost=cfg.httpProxyHost,
39                                    ignoreHttpProxyEnv=cfg.ignoreHttpProxyEnv,
40                                    **cfg.wss)
41                               
42        except Exception, e:
43            c.xml='Error establishing security context.  Please report ' + \
44                  'the error to your site administrator'
45            log.error("Initialising SessionMgrClient for " + \
46                      "getSessionStatus call: %s" % e)
47            SecuritySession.delete()
48            response.status_code = 400
49            return render('ndg.security.kid', 'ndg.security.login')
50       
51        # Check session status
52        log.debug('Calling Session Manager "%s" getSessionStatus ' % \
53                  session['ndgSec']['h'] + 'for user "%s" with sid="%s" ...'%\
54                  (session['ndgSec']['u'], session['ndgSec']['sid']))
55        try:
56            bSessOK = smClnt.getSessionStatus(sessID=session['ndgSec']['sid'])
57        except Exception, e:
58            c.xml = "Error checking your session details.  Please re-login"
59            log.error("Session Manager getSessionStatus returned: %s" % e)
60            SecuritySession.delete()
61            response.status_code = 400
62            return render('ndg.security.kid', 'ndg.security.login')
63   
64        if bSessOK:
65            log.debug("Session found - redirect back to site requesting " + \
66                      "credentials ...")
67            # ... Return across http GET passing security parameters...
68            return self._redirect()
69        else:
70            log.debug("Session wasn't found - removing security details " + \
71                      "from cookie and re-displaying login...")
72            SecuritySession.delete()
73            return render('ndg.security.kid', 'ndg.security.login')
74
75
76    def getCredentials(self):
77        """Authenticate user and cache user credentials in
78        Session Manager following user login"""
79        log.debug("LoginController.getCredentials ...")   
80       
81        # Convenience alias
82        cfg = g.ndg.security.server.ssoservice.cfg
83
84        # Check the return to URL
85        c.b64encReturnTo = str(request.params.get('r', ''))
86
87        if 'username' not in request.params:
88            log.debug("No username set - rendering login...")
89            return render('ndg.security.kid', 'ndg.security.login')
90       
91        try:   
92            smClnt = SessionMgrClient(uri=cfg.smURI,
93                                     tracefile=cfg.tracefile,
94                                     httpProxyHost=cfg.httpProxyHost,
95                                     ignoreHttpProxyEnv=cfg.ignoreHttpProxyEnv,
96                                     **cfg.wss)
97                               
98            username = request.params['username']
99            passphrase = request.params['passphrase']                     
100                               
101        except Exception, e:
102            c.xml='Error establishing security context.  Please report ' + \
103                  'the error to your site administrator'
104            log.error("Login: initialising SessionMgrClient: %s" % e)
105            response.status_code = 400
106            return render('ndg.security.kid', 'ndg.security.login')
107       
108        # Connect to Session Manager
109        log.debug('Calling Session Manager "%s" connect for user "%s" ...' % \
110                  (cfg.smURI, username))
111        try:
112            sessID = smClnt.connect(username, passphrase=passphrase)[-1]
113        except Exception, e:
114            c.xml = "Error logging in.  Please check your username/" + \
115                    "pass-phrase and try again.  If the problem persists " + \
116                    "please contact your site administrator."
117            log.error("Session Manager connect returned: %s" % e)
118            response.status_code = 401
119            return render('ndg.security.kid', 'ndg.security.login')
120       
121        # Cache user attributes in Session Manager
122        log.debug("Calling Session Manager getAttCert for user ")
123        try:
124            # Make request for attribute certificate
125            attCert = smClnt.getAttCert(sessID=sessID, 
126                                        attAuthorityURI=cfg.aaURI)
127        except SessionExpired, e:
128            log.info("Session expired getting Attribute Certificate: %s" % e)
129            c.xml = "Session has expired, please re-login"
130            response.status_code = 401
131            return render('ndg.security.kid', 'ndg.security.login')
132           
133        except AttributeRequestDenied, e:
134            log.error("Login: attribute Certificate request denied: %s" % e)
135            c.xml = "No authorisation roles are available for your " + \
136                    "account.  Please check with your site administrator."
137            response.status_code = 401
138            return render('ndg.security.kid', 'ndg.security.login')
139           
140        except Exception, e:
141            log.error("Login: attribute Certificate request: %s" % e)
142            c.xml = "An internal error occured.  Please report this to " + \
143                    "your site administrator."
144            response.status_code = 400
145            return render('ndg.security.kid', 'ndg.security.login')
146
147        log.debug('Completing login...')
148       
149        # Make security session details
150        setSecuritySession(h=cfg.smURI,
151                           u=username,
152                           org=attCert.issuerName,
153                           roles=attCert.roles,
154                           sid=sessID)
155        session.save()
156
157        log.debug("session = %s" % session)
158        log.info("user %s logged in with roles %s" % (session['ndgSec']['u'],
159                                                  session['ndgSec']['roles']))
160        return self._redirect()
161       
162       
163    def _redirect(self):
164        """Pass security creds back to requestor so that they can make
165        a cookie.  If the requestor is in the same domain as the login then
166        this is not necessary."""
167        log.debug("LoginController._redirect...")
168       
169        # Convenience alias
170        cfg = g.ndg.security.server.ssoservice.cfg
171       
172        # This is set in index and getCredentials
173        if c.b64encReturnTo:
174       
175            # Only add token if return URI is in a different domain
176            thisHostname = request.host.split(':')[0]
177           
178            # Decode return to address
179            returnToURL = urlsafe_b64decode(c.b64encReturnTo)
180            log.debug('Login redirect to [%s]' % returnToURL)
181
182            hostnameWithPortNum = urlsplit(returnToURL)[1]
183           
184            # Require hostname with port number striped to test SSL connection
185            # (will default to 443)
186            returnToURLHostname = hostnameWithPortNum.split(':')[0]
187           
188#            if thisHostname not in returnToURLHostname:
189            if True: # Ensure return args in URL regardless
190                # Returning to a different domain - copy the security session
191                # details into the URL query string
192                if '?' in returnToURL:
193                    returnToURL += '&%s' % SSOServiceQuery()
194                else:
195                    returnToURL += '?%s' % SSOServiceQuery()
196           
197            # Check return-to address by examining peer cert
198            log.debug("Checking return-to URL for valid SSL peer cert. ...")
199
200           
201            # Look-up list of Cert DNs for trusted requestors
202            aaClnt = AttAuthorityClient(uri=cfg.aaURI,
203                                    tracefile=cfg.tracefile,
204                                    httpProxyHost=cfg.httpProxyHost,
205                                    ignoreHttpProxyEnv=cfg.ignoreHttpProxyEnv,
206                                    **cfg.wss)
207           
208            HostInfo = aaClnt.getAllHostsInfo()
209            requestServerDN = [val['loginRequestServerDN'] \
210                               for val in HostInfo.values()]
211            log.debug(\
212            "Attribute Authority [%s] expecting DN for SSL peer one of: %s" % \
213                (cfg.aaURI, requestServerDN))
214           
215            hostCheck = HostCheck(acceptedDNs=requestServerDN,
216                                  caCertFilePathList=cfg.sslCACertFilePathList)
217           
218            testConnection = HTTPSConnection(returnToURLHostname, 
219                                             None, 
220                                             postConnectionCheck=hostCheck)
221
222            log.debug('Testing connection to "%s"' % returnToURLHostname)
223            try:
224                try:
225                    testConnection.connect()
226                except (InvalidCertSignature, InvalidCertDN), e:
227                    log.error("Login: requestor SSL certificate: %s" % e)
228                    c.xml = """Request to redirect back to %s with your
229credentials refused: there is a problem with the SSL certificate of this site.
230  Please report this to your site administrator.""" % returnToURLHostname
231                    response.status_code = 400
232                    return render('ndg.security.kid', 'ndg.security.login')
233            finally:   
234                testConnection.close()
235
236            log.debug("SSL peer cert. is OK - redirecting to [%s] ..." % \
237                                                                returnToURL)
238            # redirect_to doesn't like unicode
239            h.redirect_to(str(returnToURL))
240        else:
241            log.debug(\
242        "LoginController._redirect: no redirect URL set - render login page")
243            c.xml='Logged in'
244            return render('ndg.security.kid', 'ndg.security.login')
Note: See TracBrowser for help on using the repository browser.