source: TI05-delivery/ows_framework/trunk/ows_server/ows_server/controllers/login.py @ 3536

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI05-delivery/ows_framework/trunk/ows_server/ows_server/controllers/login.py@3536
Revision 3536, 12.2 KB checked in by cbyrom, 12 years ago (diff)

Upgrade the server code to make consistent with newer pylons codebase - v0.9.6.1.
This mainly involves the replacement of the Response object, and associated methods,
with the inbuild, default response object. Typical changes include:

render_response -> render - with required settings on the response object
made before the render call against the response object

Response(...) -> response.write() - for content + response.headers.. = .. for headers info

  • also included the replacement of depricated functions, as highlighted by

the server logging

Line 
1import sys,cgi
2from urlparse import urlsplit, urlunsplit
3import base64
4
5from ows_server.lib.base import *
6from ows_server.lib.security_util import setSecuritySession, SecuritySession,\
7                                         LoginServiceQuery
8from ows_common.exception_report import OwsError
9from paste.request import parse_querystring
10import logging
11log = logging.getLogger(__name__)
12
13from ndg.security.common.AttAuthority import AttAuthorityClient
14from ndg.security.common.SessionMgr import SessionMgrClient, SessionExpired, \
15    AttributeRequestDenied
16from ndg.security.common.m2CryptoSSLUtility import HTTPSConnection, \
17    HostCheck, InvalidCertSignature, InvalidCertDN
18
19
20class LoginController(BaseController):
21    ''' Provides the pylons controller for local login '''
22   
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', '')
27       
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')
149           
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
261credentials 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 TracBrowser for help on using the repository browser.