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

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

Initial Integration of Single Sign On Service with OpenID and Pylons AuthKit?:

  • WAYF now contains an OpenID textbox for sign in
  • No role integration carried out yet - OpenID has no better privileges than an anonymous user(!)
  • Integrated into Authkit - requires lots of config settings in pylons ini file
  • HTTP 401 error get redirected automatically to WAYF
  • Need to create an AuthKit? egg from SVN 151 checkout - will put on NDG dist
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                            ignoreHttpProxyEnv=self.cfg.ignoreHttpProxyEnv,
46                            **self.cfg.wss)
47                               
48        except Exception, e:
49            c.xml='Error establishing security context.  Please report ' + \
50                  'the error to your site administrator'
51            log.error("Initialising SessionMgrClient for " + \
52                      "getSessionStatus call: %s" % e)
53            SecuritySession.delete()
54            response.status_code = 400
55            return render('ndg.security.kid', 'ndg.security.login')
56       
57        # Check session status
58        log.debug('Calling Session Manager "%s" getSessionStatus ' % \
59                  session['ndgSec']['h'] + 'for user "%s" with sid="%s" ...'%\
60                  (session['ndgSec']['u'], session['ndgSec']['sid']))
61
62        try:
63            bSessOK = smClnt.getSessionStatus(sessID=session['ndgSec']['sid'])
64        except Exception, e:
65            c.xml = "Error checking your session details.  Please re-login"
66            log.error("Session Manager getSessionStatus: %s" % e)
67            SecuritySession.delete()
68            response.status_code = 400
69            return render('ndg.security.kid', 'ndg.security.login')
70   
71        if bSessOK:
72            log.debug("Session found - redirect back to site requesting " + \
73                      "credentials ...")
74            # ... Return across http GET passing security parameters...
75            return self._redirect()
76        else:
77            log.debug("Session wasn't found - removing security details " + \
78                      "from cookie and re-displaying login...")
79            SecuritySession.delete()
80            return render('ndg.security.kid', 'ndg.security.login')
81
82
83    def getCredentials(self):
84        """Authenticate user and cache user credentials in Session Manager
85        following user login"""
86        log.debug("LoginController.getCredentials ...")   
87
88        if 'username' not in request.params:
89            log.debug("No username set - rendering login...")
90            return render('ndg.security.kid', 'ndg.security.login')
91       
92        try:   
93            smClnt = SessionMgrClient(uri=self.cfg.smURI,
94                         tracefile=self.cfg.tracefile,
95                         httpProxyHost=self.cfg.httpProxyHost,
96                         ignoreHttpProxyEnv=self.cfg.ignoreHttpProxyEnv,
97                         **self.cfg.wss)
98                               
99            username = request.params['username']
100            passphrase = request.params['passphrase']                     
101                               
102        except Exception, e:
103            c.xml='Error establishing security context.  Please report ' + \
104                  'the error to your site administrator'
105            log.error("Login: initialising SessionMgrClient: %s" % e)
106            response.status_code = 400
107            return render('ndg.security.kid', 'ndg.security.login')
108       
109        # Connect to Session Manager
110        log.debug('Calling Session Manager "%s" connect for user "%s" ...' % \
111                  (self.cfg.smURI, username))
112        try:
113            sessID = smClnt.connect(username, passphrase=passphrase)[-1]
114        except Exception, e:
115            c.xml = "Error logging in.  Please check your username/" + \
116                    "pass-phrase and try again.  If the problem persists " + \
117                    "please contact your site administrator."
118            log.error("Session Manager connect returned: %s" % e)
119            response.status_code = 400
120            return render('ndg.security.kid', 'ndg.security.login')
121       
122        # Cache user attributes in Session Manager
123        log.debug("Calling Session Manager getAttCert for user ")
124        try:
125            # Make request for attribute certificate
126            attCert = smClnt.getAttCert(sessID=sessID, 
127                                        attAuthorityURI=self.cfg.aaURI)
128        except SessionExpired, e:
129            log.info("Session expired getting Attribute Certificate: %s" % e)
130            c.xml = "Session has expired, please re-login"
131            response.status_code = 400
132            return render('ndg.security.kid', 'ndg.security.login')
133           
134        except AttributeRequestDenied, e:
135            log.error("Login: attribute Certificate request denied: %s" % e)
136            c.xml = "No authorisation roles are available for your " + \
137                    "account.  Please check with your site administrator."
138            response.status_code = 400
139            return render('ndg.security.kid', 'ndg.security.login')
140           
141        except Exception, e:
142            log.error("Login: attribute Certificate request: %s" % e)
143            c.xml = "An internal error occured.  Please report this to " + \
144                    "your site administrator."
145            response.status_code = 400
146            return render('ndg.security.kid', 'ndg.security.login')
147
148        log.debug('Completing login...')
149       
150        # Make security session details
151        setSecuritySession(h=self.cfg.smURI,
152                           u=username,
153                           org=attCert.issuerName,
154                           roles=attCert.roles,
155                           sid=sessID)
156        session.save()
157
158        log.debug("session = %s" % session)
159        log.info("user %s logged in with roles %s" % (session['ndgSec']['u'],
160                                                  session['ndgSec']['roles']))
161        return self._redirect()
162       
163       
164    def _redirect(self):
165        """Pass security creds back to requestor so that they can make
166        a cookie.  If the requestor is in the same domain as the login then
167        this is not necessary."""
168        log.debug("LoginController._redirect...")
169       
170        # This is set in index and getCredentials
171        if self.state.b64encReturnToURL:
172       
173            # Only add token if return URI is in a different domain
174            thisHostname = request.host.split(':')[0]
175           
176            # Decode return to address
177            returnToURL = urlsafe_b64decode(self.state.b64encReturnToURL)
178            log.debug('Login redirect to [%s]' % returnToURL)
179
180            hostnameWithPortNum = urlsplit(returnToURL)[1]
181           
182            # Require hostname with port number striped to test SSL connection
183            # (will default to 443)
184            returnToURLHostname = hostnameWithPortNum.split(':')[0]
185           
186#            if thisHostname not in returnToURLHostname:
187            if True: # Ensure return args in URL regardless
188                # Returning to a different domain - copy the security session
189                # details into the URL query string
190                if '?' in returnToURL:
191                    returnToURL += '&%s' % SSOServiceQuery()
192                else:
193                    returnToURL += '?%s' % SSOServiceQuery()
194           
195            # Check return-to address by examining peer cert
196            log.debug("Checking return-to URL for valid SSL peer cert. ...")
197
198           
199            # Look-up list of Cert DNs for trusted requestors
200            aaClnt = AttAuthorityClient(uri=self.cfg.aaURI,
201                            tracefile=self.cfg.tracefile,
202                            httpProxyHost=self.cfg.httpProxyHost,
203                            ignoreHttpProxyEnv=self.cfg.ignoreHttpProxyEnv,
204                            **self.cfg.wss)
205           
206            HostInfo = aaClnt.getAllHostsInfo()
207            requestServerDN = [val['loginRequestServerDN'] \
208                               for val in HostInfo.values()]
209            log.debug(\
210            "Attribute Authority [%s] expecting DN for SSL peer one of: %s" % \
211                (self.cfg.aaURI, requestServerDN))
212           
213            hostCheck = HostCheck(acceptedDNs=requestServerDN,
214                        caCertFilePathList=self.cfg.sslCACertFilePathList)
215           
216            testConnection = HTTPSConnection(returnToURLHostname, 
217                                             None, 
218                                             postConnectionCheck=hostCheck)
219
220            log.debug('Testing connection to "%s"' % returnToURLHostname)
221            try:
222                try:
223                    testConnection.connect()
224                except (InvalidCertSignature, InvalidCertDN), e:
225                    log.error("Login: requestor SSL certificate: %s" % e)
226                    c.xml = """Request to redirect back to %s with your
227credentials refused: there is a problem with the SSL certificate of this site.
228  Please report this to your site administrator.""" % returnToURLHostname
229                    response.status_code = 400
230                    return render('ndg.security.kid', 'ndg.security.login')
231            finally:   
232                testConnection.close()
233
234            log.debug("SSL peer cert. is OK - redirecting to [%s] ..." % \
235                                                                returnToURL)
236            # redirect_to doesn't like unicode
237            h.redirect_to(str(returnToURL))
238        else:
239            log.debug(\
240        "LoginController._redirect: no redirect URL set - render login page")
241            c.xml='Logged in'
242            return render('ndg.security.kid', 'ndg.security.login')
Note: See TracBrowser for help on using the repository browser.