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

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

ows_server/ows_server/controllers/login.py: delete security details from session if session not found on Session Manager

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_response('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            return Response(render('content'), code=400)
74       
75        # Check session status
76        log.debug('Calling Session Manager "%s" getSessionStatus ' % \
77                  session['ndgSec']['h'] + 'for user "%s" with sid="%s" ...'%\
78                  (session['ndgSec']['u'], session['ndgSec']['sid']))
79        try:
80            bSessOK = smClnt.getSessionStatus(sessID=session['ndgSec']['sid'])
81        except Exception, e:
82            c.xml = "Error checking your session details.  Please re-login"
83            log.error("Session Manager getSessionStatus returned: %s" % e)
84            SecuritySession.delete()
85            return Response(render('login'), code=401)
86   
87        if bSessOK:
88            log.debug("Session found - redirect back to site requesting " + \
89                      "credentials ...")
90            # ... Return across http GET passing security parameters...
91            return self.__doRedirect()
92        else:
93            log.debug("Session wasn't found - removing security details " + \
94                      "from cookie and re-displaying login...")
95            SecuritySession.delete()
96            render_response('login')
97
98
99    def getCredentials(self):
100        """Authenticate user and cache user credentials in
101        Session Manager following user login"""
102        log.debug("LoginController.getCredentials ...")   
103
104        try:   
105            smClnt = SessionMgrClient(uri=g.securityCfg.smURI,
106                    sslCACertFilePathList=g.securityCfg.sslCACertFilePathList,
107                    sslPeerCertCN=g.securityCfg.sslPeerCertCN,
108                    signingCertFilePath=g.securityCfg.wssCertFilePath,
109                    signingPriKeyFilePath=g.securityCfg.wssPriKeyFilePath,
110                    signingPriKeyPwd=g.securityCfg.wssPriKeyPwd,
111                    caCertFilePathList=g.securityCfg.wssCACertFilePathList,
112                    tracefile=g.securityCfg.tracefile)
113                               
114            username = request.params['username']
115            passphrase = request.params['passphrase']                     
116                               
117        except Exception, e:
118            c.xml='Error establishing security context.  Please report ' + \
119                  'the error to your site administrator'
120            log.error("Login: initialising SessionMgrClient: %s" % e)
121            return Response(render('content'), code=400)
122       
123        # Connect to Session Manager
124        log.debug('Calling Session Manager "%s" connect for user "%s" ...' % \
125                  (g.securityCfg.smURI, username))
126        try:
127            sessID = smClnt.connect(username, passphrase=passphrase)[-1]
128        except Exception, e:
129            c.xml = "Error logging in.  Please check your username/" + \
130                    "pass-phrase and try again."
131            log.error("Session Manager connect returned: %s" % e)
132            return Response(render('login'), code=401)
133       
134        # Cache user attributes in Session Manager
135        log.debug("Calling Session Manager getAttCert for user ")
136        try:
137            # Make request for attribute certificate
138            attCert = smClnt.getAttCert(sessID=sessID, 
139                                        attAuthorityURI=g.securityCfg.aaURI)
140        except SessionExpired, e:
141            log.info("Session expired getting Attribute Certificate: %s" % e)
142            c.xml = "Session has expired, please re-login"
143            return Response(render('login'), code=401)
144           
145        except AttributeRequestDenied, e:
146            log.error("Login: attribute Certificate request denied: %s" % e)
147            c.xml = "No authorisation roles are available for your " + \
148                    "account.  Please check with your site administrator."
149            return Response(render('login'), code=401)
150           
151        except Exception, e:
152            log.error("Login: attribute Certificate request: %s" % e)
153            c.xml = "An internal error occured.  Please report this to " + \
154                    "your site administrator."
155            return Response(render('login'), code=400)
156
157        log.debug('Completing login...')
158       
159        # Make security session details
160        setSecuritySession(h=g.securityCfg.smURI,
161                           u=username,
162                           org=attCert.issuerName,
163                           roles=attCert.roles,
164                           sid=sessID)
165        session['panelView']='History'
166        session.save()
167
168        log.info("user %s logged in with roles %s" % (session['ndgSec']['u'],
169                                                  session['ndgSec']['roles']))
170        return self.__doRedirect()
171           
172           
173    def wayf(self):
174        ''' NDG equivalent to Shibboleth WAYF '''
175        log.debug("LoginController.wayf ...")   
176
177        # May be better as a 'g' global set-up at start-up?
178        #
179        # tracefile could be removed for production use
180        aaClnt = AttAuthorityClient(uri=g.securityCfg.aaURI,
181                    signingCertFilePath=g.securityCfg.wssCertFilePath,
182                    signingPriKeyFilePath=g.securityCfg.wssPriKeyFilePath,
183                    signingPriKeyPwd=g.securityCfg.wssPriKeyPwd,
184                    caCertFilePathList=g.securityCfg.wssCACertFilePathList,
185                    tracefile=g.securityCfg.tracefile)
186
187        # Get list of login uris for trusted sites including THIS one
188        log.debug("Calling Attribute Authority getTrustedHostInfo and " + \
189                  "getHostInfo for wayf")
190
191        hosts = aaClnt.getAllHostsInfo()   
192        c.providers=dict([(k, v['loginURI']) for k, v in hosts.items()])
193       
194        if 'panelView' in session: del session['panelView']
195        session.save()
196       
197        return render_response('wayf')
198       
199       
200    def __doRedirect(self):
201        """Pass security creds back to requestor so that they can make
202        a cookie.  If the requestor is in the same domain as the login then
203        this is not necessary."""
204       
205        # and now go back to whence we had come
206        if c.returnTo!='':
207            # is there a keyword on redirect_to that can make this https? See:
208            # http://pylonshq.com/project/pylonshq/browser/Pylons/trunk/pylons/decorators/secure.py#L69
209
210            # Only add token if return URI is in a different domain
211            thisHostname = request.host.split(':')[0]
212           
213            # Decode return to address
214            cc = base64.urlsafe_b64decode(c.returnTo)
215            log.debug('Login redirect to [%s]' % cc)
216
217            returnToHostname = urlsplit(cc)[1]
218#            returnToHostname = 'localhost'
219#            if thisHostname not in returnToHostname:
220            if True:
221                # Returning to a different domain - copy the security session
222                # details into the URL query string
223                if '?' in cc:
224                    cc+='&%s' % LoginServiceQuery()
225                else:
226                    cc+='?%s' % LoginServiceQuery()
227           
228            # Check return-to address by examining peer cert
229            log.debug("Checking return-to URL for valid SSL peer cert. ...")
230           
231            # Look-up list of Cert DNs for trusted requestors
232            aaClnt = AttAuthorityClient(uri=g.securityCfg.aaURI,
233                    signingCertFilePath=g.securityCfg.wssCertFilePath,
234                    signingPriKeyFilePath=g.securityCfg.wssPriKeyFilePath,
235                    signingPriKeyPwd=g.securityCfg.wssPriKeyPwd,
236                    caCertFilePathList=g.securityCfg.wssCACertFilePathList,
237                    tracefile=g.securityCfg.tracefile)
238           
239            HostInfo = aaClnt.getAllHostsInfo()
240            requestServerDN = [val['loginRequestServerDN'] \
241                               for val in HostInfo.values()]
242            log.debug("Expecting DN for SSL peer one of: %s"%requestServerDN)
243            hostCheck=HostCheck(acceptedDNs=requestServerDN,
244                    caCertFilePathList=g.securityCfg.sslCACertFilePathList)           
245            testConnection = HTTPSConnection(returnToHostname, 
246                                             None, 
247                                             postConnectionCheck=hostCheck)
248
249            log.debug('Testing connection to "%s"' % returnToHostname)
250            try:
251                try:
252                    testConnection.connect()
253                except (InvalidCertSignature, InvalidCertDN), e:
254                    log.error("Login: requestor SSL certificate: %s" % e)
255                    c.xml = """Request to redirect back to %s with your
256credentials refused: there is a problem with the SSL certificate of this site.
257  Please report this to your site administrator.""" % returnToHostname
258                    return Response(render('login'), code=400)
259            finally:   
260                testConnection.close()
261
262            log.debug("SSL peer cert. is OK - redirecting to [%s] ..." % cc)
263            h.redirect_to(cc)
264        else:
265            c.xml='<p> Logged in </p>'
266            return render_response('content')
Note: See TracBrowser for help on using the repository browser.