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

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

Include organisation name in login info on NDG page bottom panel.

ows_server/ows_server/controllers/login.py: add attCert.issuerName into org key of security session info
ows_server/ows_server/lib/security_util.py: add 'org' to security session keys
ows_server/ows_server/templates/ndgPage.kid: change logged in message to include org.

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 security session details
118        setSecuritySession(h=g.securityCfg.smURI,
119                           u=username,
120                           org=attCert.issuerName,
121                           roles=attCert.roles,
122                           sid=sessID)
123        session['panelView']='History'
124        session.save()
125
126        log.info("user %s logged in with roles %s" % (session['ndgSec']['u'],
127                                                  session['ndgSec']['roles']))
128        return self.__doRedirect()
129           
130           
131    def wayf(self):
132        ''' NDG equivalent to Shibboleth WAYF '''
133        log.debug("LoginController.wayf ...")   
134
135        # May be better as a 'g' global set-up at start-up?
136        #
137        # tracefile could be removed for production use
138        aaClnt = AttAuthorityClient(uri=g.securityCfg.aaURI,
139                    signingCertFilePath=g.securityCfg.wssCertFilePath,
140                    signingPriKeyFilePath=g.securityCfg.wssPriKeyFilePath,
141                    signingPriKeyPwd=g.securityCfg.wssPriKeyPwd,
142                    caCertFilePathList=g.securityCfg.wssCACertFilePathList,
143                    tracefile=g.securityCfg.tracefile)
144
145        # Get list of login uris for trusted sites including THIS one
146        log.debug("Calling Attribute Authority getTrustedHostInfo and " + \
147                  "getHostInfo for wayf")
148
149        hosts = aaClnt.getAllHostsInfo()   
150        c.providers=dict([(k, v['loginURI']) for k, v in hosts.items()])
151       
152        if 'panelView' in session: del session['panelView']
153        session.save()
154       
155        return render_response('wayf')
156       
157    def __doRedirect(self):
158        """Pass security creds back to requestor so that they can make
159        a cookie.  If the requestor is in the same domain as the login then
160        this is not necessary."""
161       
162        # and now go back to whence we had come
163        if c.returnTo!='':
164            # is there a keyword on redirect_to that can make this https? See:
165            # http://pylonshq.com/project/pylonshq/browser/Pylons/trunk/pylons/decorators/secure.py#L69
166
167            # Only add token if return URI is in a different domain
168            thisHostname = request.host.split(':')[0]
169           
170            # Decode return to address
171            cc = base64.urlsafe_b64decode(c.returnTo)
172            log.debug('Login redirect to [%s]' % cc)
173
174            returnToHostname = urlsplit(cc)[1]
175#            returnToHostname = 'localhost'
176#            if thisHostname not in returnToHostname:
177            if True:
178                # Returning to a different domain - copy the security session
179                # details into the URL query string
180                if '?' in cc:
181                    cc+='&%s' % LoginServiceQuery()
182                else:
183                    cc+='?%s' % LoginServiceQuery()
184           
185            # Check return-to address by examining peer cert
186            log.debug("Checking return-to URL for valid SSL peer cert. ...")
187           
188            # Look-up list of Cert DNs for trusted requestors
189            aaClnt = AttAuthorityClient(uri=g.securityCfg.aaURI,
190                    signingCertFilePath=g.securityCfg.wssCertFilePath,
191                    signingPriKeyFilePath=g.securityCfg.wssPriKeyFilePath,
192                    signingPriKeyPwd=g.securityCfg.wssPriKeyPwd,
193                    caCertFilePathList=g.securityCfg.wssCACertFilePathList,
194                    tracefile=g.securityCfg.tracefile)
195           
196            HostInfo = aaClnt.getAllHostsInfo()
197            requestServerDN = [val['loginRequestServerDN'] \
198                               for val in HostInfo.values()]
199            log.debug("Expecting DN for SSL peer one of: %s"%requestServerDN)
200            hostCheck=HostCheck(acceptedDNs=requestServerDN,
201                    caCertFilePathList=g.securityCfg.sslCACertFilePathList)           
202            testConnection = HTTPSConnection(returnToHostname, 
203                                             None, 
204                                             postConnectionCheck=hostCheck)
205
206            log.debug('Testing connection to "%s"' % returnToHostname)
207            try:
208                try:
209                    testConnection.connect()
210                except (InvalidCertSignature, InvalidCertDN), e:
211                    log.error("Login: requestor SSL certificate: %s" % e)
212                    c.xml = """Request to redirect back to %s with your
213credentials refused: there is a problem with the SSL certificate of this site.
214  Please report this to your site administrator.""" % returnToHostname
215                    return Response(render('login'), code=400)
216            finally:   
217                testConnection.close()
218
219            log.debug("SSL peer cert. is OK - redirecting to [%s] ..." % cc)
220            h.redirect_to(cc)
221        else:
222            c.xml='<p> Logged in </p>'
223            return render_response('content')
Note: See TracBrowser for help on using the repository browser.