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

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