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

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

ows_server/ows_server/controllers/login.py: fix to exception handling for invalid SSL cert in LoginController?.doRedirect - added InvalidCertDN type

Line 
1import sys,cgi
2from urlparse import urlsplit
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 __call__(self, environ, start_response):
23        """Update c.requestURL and c.b64encRequestURL using g.server setting
24        to avoid exposing the absolute URL hidden behind the proxy"""
25               
26        # Insert any code to be run per request here. The Routes match
27        # is under environ['pylons.routes_dict'] should you want to check
28        # the action or route vars here
29
30        log.debug("LoginController.__call__ calling BaseController.__call__ ...")
31        response = super(LoginController, self).__call__(environ, start_response)
32
33       
34        # Construct URL picking up setting of server name from config to
35        # avoid exposing absolute URL hidden behind mod_proxy see #857           
36        c.requestURL = g.server + urllib.quote(environ.get('PATH_INFO', ''))
37
38        query = '&'.join(["%s=%s" % item for item in request.params.items()])
39        if query:
40            c.requestURL += '?' + query
41
42        # Base 64 encode to enable passing around in 'r' argument of query
43        # string for use with login/logout
44        c.b64encRequestURL = urlsafe_b64encode(c.requestURL)
45       
46        return response
47   
48   
49    def __before__(self, action): 
50        """For each action, get 'r' return to URL argument from current URL
51        query string.  c.returnTo is used in some of the .kid files"""
52        c.returnTo = request.params.get('r', '')
53        log.debug("Decoded c.returnTo = %s" % \
54                                      base64.urlsafe_b64decode(c.returnTo))
55   
56   
57    def index(self):
58        ''' Ok, you really want to login here '''
59        log.debug("LoginController.index ...")   
60
61        if 'ndgSec' in session: 
62            # Session is set in this domain - copy its content
63            # and return it across http GET to caller
64            return self.__doRedirect()
65        else:
66            return render_response('login')
67
68
69    def getCredentials(self):
70        """Authenticate user and cache user credentials in
71        Session Manager following user login"""
72        log.debug("LoginController.getCredentials ...")   
73
74        try:   
75            smClnt = SessionMgrClient(uri=g.securityCfg.smURI,
76                    sslCACertFilePathList=g.securityCfg.sslCACertFilePathList,
77                    sslPeerCertCN=g.securityCfg.sslPeerCertCN,
78                    signingCertFilePath=g.securityCfg.wssCertFilePath,
79                    signingPriKeyFilePath=g.securityCfg.wssPriKeyFilePath,
80                    signingPriKeyPwd=g.securityCfg.wssPriKeyPwd,
81                    caCertFilePathList=g.securityCfg.wssCACertFilePathList,
82                    tracefile=g.securityCfg.tracefile)
83                               
84            username = request.params['username']
85            passphrase = request.params['passphrase']                     
86                               
87        except Exception,e:
88            c.xml='Error establishing security context [%s]'%cgi.escape(str(e))
89            return Response(render('content'), code=400)
90       
91        # Connect to Session Manager
92        log.debug('Calling Session Manager "%s" connect for user "%s" ...' % \
93                  (g.securityCfg.smURI, username))
94        try:
95            sessID = smClnt.connect(username, passphrase=passphrase)[-1]
96        except Exception, e:
97            c.xml = \
98    "Error logging in.  Please check your username/pass-phrase and try again."
99            log.error("Session Manager connect returned: %s" % e)
100            return Response(render('login'), code=401)
101       
102        # Cache user attributes in Session Manager
103        log.debug("Calling Session Manager getAttCert for user ")
104        try:
105            # Make request for attribute certificate
106            attCert = smClnt.getAttCert(sessID=sessID, 
107                                        attAuthorityURI=g.securityCfg.aaURI)
108        except SessionExpired, e:
109            log.info("Session expired getting Attribute Certificate: %s" % e)
110            c.xml = "Session has expired, please re-login"
111            return Response(render('login'), code=401)
112           
113        except AttributeRequestDenied, e:
114            log.error("Login: attribute Certificate request denied: %s" % e)
115            c.xml = "No authorisation roles are available for your " + \
116                    "account.  Please check with your site administrator."
117            return Response(render('login'), code=401)
118           
119        except Exception, e:
120            log.error("Login: attribute Certificate request: %s" % e)
121            c.xml = "An internal error occured.  Please report this to " + \
122                    "your site administrator."
123            return Response(render('login'), code=400)
124
125        log.debug('Completing login...')
126       
127        # Make session
128        #
129        # Security credentials - proxyCert, userCert, ProxyPriKey and sessID
130        # could be held in the session but how secure is
131       
132        # Make a security cookie here ...
133        setSecuritySession(h=g.securityCfg.smURI,
134                           u=username,
135                           roles=attCert.roles,
136                           sid=sessID)
137        session['panelView']='History'
138        session.save()
139
140        log.info("user %s logged in with roles %s" % (session['ndgSec']['u'],
141                                                  session['ndgSec']['roles']))
142        return self.__doRedirect()
143           
144           
145    def wayf(self):
146        ''' NDG equivalent to Shibboleth WAYF '''
147        log.debug("LoginController.wayf ...")   
148
149        # May be better as a 'g' global set-up at start-up?
150        #
151        # tracefile could be removed for production use
152        aaClnt = AttAuthorityClient(uri=g.securityCfg.aaURI,
153                    signingCertFilePath=g.securityCfg.wssCertFilePath,
154                    signingPriKeyFilePath=g.securityCfg.wssPriKeyFilePath,
155                    signingPriKeyPwd=g.securityCfg.wssPriKeyPwd,
156                    caCertFilePathList=g.securityCfg.wssCACertFilePathList,
157                    tracefile=g.securityCfg.tracefile)
158
159        # Get list of login uris for trusted sites including THIS one
160        log.debug("Calling Attribute Authority getTrustedHostInfo and " + \
161                  "getHostInfo for wayf")
162
163        hosts = aaClnt.getAllHostsInfo()   
164        c.providers=dict([(k, v['loginURI']) for k, v in hosts.items()])
165       
166        if 'panelView' in session: del session['panelView']
167        session.save()
168       
169        return render_response('wayf')
170       
171    def __doRedirect(self):
172        """Pass security creds back to requestor so that they can make
173        a cookie.  If the requestor is in the same domain as the login then
174        this is not necessary."""
175       
176        # and now go back to whence we had come
177        if c.returnTo!='':
178            # is there a keyword on redirect_to that can make this https? See:
179            # http://pylonshq.com/project/pylonshq/browser/Pylons/trunk/pylons/decorators/secure.py#L69
180
181            # Only add token if return URI is in a different domain
182            thisHostname = request.host.split(':')[0]
183           
184            # Decode return to address
185            cc = base64.urlsafe_b64decode(c.returnTo)
186            log.debug('Login redirect to [%s]' % cc)
187
188            returnToHostname = urlsplit(cc)[1]
189#            returnToHostname = 'localhost'
190#            if thisHostname not in returnToHostname:
191            if True:
192                # Returning to a different domain - copy the security session
193                # details into the URL query string
194                if '?' in cc:
195                    cc+='&%s' % LoginServiceQuery()
196                else:
197                    cc+='?%s' % LoginServiceQuery()
198           
199            # Check return-to address by examining peer cert
200            log.debug("Checking return-to URL for valid SSL peer cert. ...")
201           
202            # Look-up list of Cert DNs for trusted requestors
203            aaClnt = AttAuthorityClient(uri=g.securityCfg.aaURI,
204                    signingCertFilePath=g.securityCfg.wssCertFilePath,
205                    signingPriKeyFilePath=g.securityCfg.wssPriKeyFilePath,
206                    signingPriKeyPwd=g.securityCfg.wssPriKeyPwd,
207                    caCertFilePathList=g.securityCfg.wssCACertFilePathList,
208                    tracefile=g.securityCfg.tracefile)
209           
210            HostInfo = aaClnt.getAllHostsInfo()
211            requestServerDN = [val['loginRequestServerDN'] \
212                               for val in HostInfo.values()]
213            log.debug("Expecting DN for SSL peer one of: %s"%requestServerDN)
214            hostCheck=HostCheck(acceptedDNs=requestServerDN,
215                    caCertFilePathList=g.securityCfg.sslCACertFilePathList)           
216            testConnection = HTTPSConnection(returnToHostname, 
217                                             None, 
218                                             postConnectionCheck=hostCheck)
219
220            log.debug('Testing connection to "%s"' % returnToHostname)
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.""" % returnToHostname
229                    return Response(render('login'), code=400)
230            finally:   
231                testConnection.close()
232
233            log.debug("SSL peer cert. is OK - redirecting to [%s] ..." % cc)
234            h.redirect_to(cc)
235        else:
236            c.xml='<p> Logged in </p>'
237            return render_response('content')
Note: See TracBrowser for help on using the repository browser.