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

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

ows_server/ows_server/controllers/login.py:

  • removed call - BaseController?.call supplies all the needed functionality
  • added to before - filter out getCredentials from return to address - getCredentials can contain cred args so should be removed. The code here is a 2nd line of defence - BaseController?.call removes getCredentials from request URLs so return to should nvere get assigned a getCredenials call.

ows_server/ows_server/controllers/logout.py:

  • same mod to before call as login.py listed above.

ows_server/ows_server/lib/security_util.py:

  • constructURL function - not used but a starting point for attempting to encapsulate security related code and remove it from BaseController?.call

ows_server/ows_server/lib/base.py:

  • if c.requestURL contains getCredentials filter it out and redirect to /discovery instead. getCredentials recives username/password from login form. Without this check these fields can be exposed as URL query args in a c.returnTo address(!)
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, LoginServiceQuery
7from ows_common.exception_report import OwsError
8from paste.request import parse_querystring
9import logging
10log = logging.getLogger(__name__)
11
12from ndg.security.common.AttAuthority import AttAuthorityClient
13from ndg.security.common.SessionMgr import SessionMgrClient, SessionExpired, \
14    AttributeRequestDenied
15from ndg.security.common.m2CryptoSSLUtility import HTTPSConnection, \
16    HostCheck, InvalidCertSignature, InvalidCertDN
17
18
19class LoginController(BaseController):
20    ''' Provides the pylons controller for local login '''
21   
22    def __before__(self, action): 
23        """For each action, get 'r' return to URL argument from current URL
24        query string.  c.returnTo is used in some of the .kid files"""
25        c.returnTo = request.params.get('r', '')
26       
27        # Check return to address - getCredentials should NOT be returned to
28        # with its query args intact
29        b64decReturnTo = base64.urlsafe_b64decode(c.returnTo)
30        scheme, netloc, pathInfo, query, frag = urlsplit(b64decReturnTo)
31        if 'getCredentials' in pathInfo:
32            # Swap to discovery and remove sensitive creds query args
33            #
34            # TODO: re-write to be more robust and modular.  Nb.
35            # BaseController.__call__ should filter out 'getCredentials'
36            # calls from c.requestURL so this code should never need to be
37            # executed.
38            filteredReturnTo = urlunsplit((scheme,netloc,'/discovery','',''))
39            c.returnTo = base64.urlsafe_b64encode(filteredReturnTo)
40       
41        # Check return to address - getCredentials should NOT be returned to
42        # with its query args intact
43        log.debug("LoginController.__before__: Decoded c.returnTo = %s" % \
44                                      base64.urlsafe_b64decode(c.returnTo))
45   
46   
47    def index(self):
48        ''' Ok, you really want to login here '''
49        log.debug("LoginController.index ...")   
50
51        if 'ndgSec' in session: 
52            # Session is set in this domain - copy its content
53            # and return it across http GET to caller
54            return self.__doRedirect()
55        else:
56            return render_response('login')
57
58
59    def getCredentials(self):
60        """Authenticate user and cache user credentials in
61        Session Manager following user login"""
62        log.debug("LoginController.getCredentials ...")   
63
64        try:   
65            smClnt = SessionMgrClient(uri=g.securityCfg.smURI,
66                    sslCACertFilePathList=g.securityCfg.sslCACertFilePathList,
67                    sslPeerCertCN=g.securityCfg.sslPeerCertCN,
68                    signingCertFilePath=g.securityCfg.wssCertFilePath,
69                    signingPriKeyFilePath=g.securityCfg.wssPriKeyFilePath,
70                    signingPriKeyPwd=g.securityCfg.wssPriKeyPwd,
71                    caCertFilePathList=g.securityCfg.wssCACertFilePathList,
72                    tracefile=g.securityCfg.tracefile)
73                               
74            username = request.params['username']
75            passphrase = request.params['passphrase']                     
76                               
77        except Exception,e:
78            c.xml='Error establishing security context [%s]'%cgi.escape(str(e))
79            return Response(render('content'), code=400)
80       
81        # Connect to Session Manager
82        log.debug('Calling Session Manager "%s" connect for user "%s" ...' % \
83                  (g.securityCfg.smURI, username))
84        try:
85            sessID = smClnt.connect(username, passphrase=passphrase)[-1]
86        except Exception, e:
87            c.xml = \
88    "Error logging in.  Please check your username/pass-phrase and try again."
89            log.error("Session Manager connect returned: %s" % e)
90            return Response(render('login'), code=401)
91       
92        # Cache user attributes in Session Manager
93        log.debug("Calling Session Manager getAttCert for user ")
94        try:
95            # Make request for attribute certificate
96            attCert = smClnt.getAttCert(sessID=sessID, 
97                                        attAuthorityURI=g.securityCfg.aaURI)
98        except SessionExpired, e:
99            log.info("Session expired getting Attribute Certificate: %s" % e)
100            c.xml = "Session has expired, please re-login"
101            return Response(render('login'), code=401)
102           
103        except AttributeRequestDenied, e:
104            log.error("Login: attribute Certificate request denied: %s" % e)
105            c.xml = "No authorisation roles are available for your " + \
106                    "account.  Please check with your site administrator."
107            return Response(render('login'), code=401)
108           
109        except Exception, e:
110            log.error("Login: attribute Certificate request: %s" % e)
111            c.xml = "An internal error occured.  Please report this to " + \
112                    "your site administrator."
113            return Response(render('login'), code=400)
114
115        log.debug('Completing login...')
116       
117        # Make session
118        #
119        # Security credentials - proxyCert, userCert, ProxyPriKey and sessID
120        # could be held in the session but how secure is
121       
122        # Make a security cookie here ...
123        setSecuritySession(h=g.securityCfg.smURI,
124                           u=username,
125                           roles=attCert.roles,
126                           sid=sessID)
127        session['panelView']='History'
128        session.save()
129
130        log.info("user %s logged in with roles %s" % (session['ndgSec']['u'],
131                                                  session['ndgSec']['roles']))
132        return self.__doRedirect()
133           
134           
135    def wayf(self):
136        ''' NDG equivalent to Shibboleth WAYF '''
137        log.debug("LoginController.wayf ...")   
138
139        # May be better as a 'g' global set-up at start-up?
140        #
141        # tracefile could be removed for production use
142        aaClnt = AttAuthorityClient(uri=g.securityCfg.aaURI,
143                    signingCertFilePath=g.securityCfg.wssCertFilePath,
144                    signingPriKeyFilePath=g.securityCfg.wssPriKeyFilePath,
145                    signingPriKeyPwd=g.securityCfg.wssPriKeyPwd,
146                    caCertFilePathList=g.securityCfg.wssCACertFilePathList,
147                    tracefile=g.securityCfg.tracefile)
148
149        # Get list of login uris for trusted sites including THIS one
150        log.debug("Calling Attribute Authority getTrustedHostInfo and " + \
151                  "getHostInfo for wayf")
152
153        hosts = aaClnt.getAllHostsInfo()   
154        c.providers=dict([(k, v['loginURI']) for k, v in hosts.items()])
155       
156        if 'panelView' in session: del session['panelView']
157        session.save()
158       
159        return render_response('wayf')
160       
161    def __doRedirect(self):
162        """Pass security creds back to requestor so that they can make
163        a cookie.  If the requestor is in the same domain as the login then
164        this is not necessary."""
165       
166        # and now go back to whence we had come
167        if c.returnTo!='':
168            # is there a keyword on redirect_to that can make this https? See:
169            # http://pylonshq.com/project/pylonshq/browser/Pylons/trunk/pylons/decorators/secure.py#L69
170
171            # Only add token if return URI is in a different domain
172            thisHostname = request.host.split(':')[0]
173           
174            # Decode return to address
175            cc = base64.urlsafe_b64decode(c.returnTo)
176            log.debug('Login redirect to [%s]' % cc)
177
178            returnToHostname = urlsplit(cc)[1]
179#            returnToHostname = 'localhost'
180#            if thisHostname not in returnToHostname:
181            if True:
182                # Returning to a different domain - copy the security session
183                # details into the URL query string
184                if '?' in cc:
185                    cc+='&%s' % LoginServiceQuery()
186                else:
187                    cc+='?%s' % LoginServiceQuery()
188           
189            # Check return-to address by examining peer cert
190            log.debug("Checking return-to URL for valid SSL peer cert. ...")
191           
192            # Look-up list of Cert DNs for trusted requestors
193            aaClnt = AttAuthorityClient(uri=g.securityCfg.aaURI,
194                    signingCertFilePath=g.securityCfg.wssCertFilePath,
195                    signingPriKeyFilePath=g.securityCfg.wssPriKeyFilePath,
196                    signingPriKeyPwd=g.securityCfg.wssPriKeyPwd,
197                    caCertFilePathList=g.securityCfg.wssCACertFilePathList,
198                    tracefile=g.securityCfg.tracefile)
199           
200            HostInfo = aaClnt.getAllHostsInfo()
201            requestServerDN = [val['loginRequestServerDN'] \
202                               for val in HostInfo.values()]
203            log.debug("Expecting DN for SSL peer one of: %s"%requestServerDN)
204            hostCheck=HostCheck(acceptedDNs=requestServerDN,
205                    caCertFilePathList=g.securityCfg.sslCACertFilePathList)           
206            testConnection = HTTPSConnection(returnToHostname, 
207                                             None, 
208                                             postConnectionCheck=hostCheck)
209
210            log.debug('Testing connection to "%s"' % returnToHostname)
211            try:
212                try:
213                    testConnection.connect()
214                except (InvalidCertSignature, InvalidCertDN), e:
215                    log.error("Login: requestor SSL certificate: %s" % e)
216                    c.xml = """Request to redirect back to %s with your
217credentials refused: there is a problem with the SSL certificate of this site.
218  Please report this to your site administrator.""" % returnToHostname
219                    return Response(render('login'), code=400)
220            finally:   
221                testConnection.close()
222
223            log.debug("SSL peer cert. is OK - redirecting to [%s] ..." % cc)
224            h.redirect_to(cc)
225        else:
226            c.xml='<p> Logged in </p>'
227            return render_response('content')
Note: See TracBrowser for help on using the repository browser.