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

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@4584
Revision 4584, 11.3 KB checked in by pjkersha, 12 years ago (diff)

Started integration work for common WSGI/SOAP client based interfaces (ndg.security.server.wsgi.utils.sessionmanagerclient and ndg.security.server.wsgi.utils.attributeauthorityclient) with Pylons Single Sign On package (ndg.security.server.sso)

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