source: TI12-security/trunk/python/NDG/SecurityCGI.py @ 986

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/NDG/SecurityCGI.py@986
Revision 986, 14.1 KB checked in by pjkersha, 13 years ago (diff)

Tests/xDomainCredsTransfer.py: test script to demonstrate passing of cookie credential info
between domains

NDG/SecurityCGI.py: above test script converted into a class (incomplete). This will form a
generic interface for required CGI functionality for sites deploying NDG security.

  • Property svn:executable set to *
Line 
1#!/usr/bin/env python
2
3"""NDG Security CGI services
4
5NERC Data Grid Project
6
7P J Kershaw 23/05/06
8
9Copyright (C) 2006 CCLRC & NERC
10
11This software may be distributed under the terms of the Q Public License,
12version 1.0 or later.
13"""
14
15from NDG.SecurityClient import *
16from Cookie import SimpleCookie
17
18import sys
19import cgi
20import os
21
22
23class SecurityCGI(cgi.FieldStorage):
24    """CGI interface class for NDG Security"""
25
26    #_________________________________________________________________________
27    def __init__(self,
28                 scriptName=None,
29                 sessMgrWSDLuri=None,
30                 returnURI=None, 
31                 trustedHosts=None, 
32                 **kwargs):
33        """scriptName:        name of script to call in forms - defaults to
34                              this file.  Modify if you inherit from this
35                              class.
36        sessMgrWSDLuri:    URI For Session Manager WSDL used for user
37                              authentication
38        returnURI:            the address to redirect back to following a
39                              redirect 
40                              to the user's home site to obtain their
41                              credentials
42        trustedHosts:         dictionary of URIs for trusted hosts indexed by
43                              hostname"""
44       
45        if scriptName:
46            self.scriptName = scriptName
47        else:
48            self.scriptName = __file__
49           
50        self.sessMgrWSDLuri = sessMgrWSDLuri
51        self.returnURI = returnURI
52        self.trustedHosts = trustedHosts
53        self.__authorisationMethod = None
54       
55        cgi.FieldStorage.__init__(self, **kwargs)
56
57 
58    #_________________________________________________________________________
59    def processFields(self, **kwargs):
60        """Call appropriate actions according to the fields set"""
61       
62        if 'requestURI' in self:
63            # Request credentials from user's home site
64            self.requestCreds(**kwargs)
65   
66        elif 'NDG-ID1' in self and 'NDG-ID2' in self:
67            # Receive credentials back from home site
68            self.receiveCredsResponse(self['NDG-ID1'].value, 
69                                      self['NDG-ID2'].value)
70   
71        elif 'setCookie' in self and 'returnURI' in self:
72            # User has logged on at home site and a cookie is now to be set -
73            # next step is processCredsRequest() below
74            self.setCookie(returnURI=self['returnURI'].value)
75   
76        elif 'returnURI' in self:
77            # Home site receives request from remote site for credentials and
78            # returns them
79            self.processCredsRequest(**kwargs)
80        else:
81            self.showHomeSiteSelect(**kwargs)
82   
83   
84    #_________________________________________________________________________
85    # Use instance name as an alias to processFields method
86    __call__ = processFields
87   
88
89    #_________________________________________________________________________
90    def requestCreds(self,
91                     requestURI=None,
92                     pageTitle='',
93                     headTags='',
94                     delayTime=0,
95                     redirectMsg=''):
96        """Request credentials from a user's home site
97       
98        requestURI:   site to request credentials from - default is
99                      'requestURI' CGI form value
100        pageTitle:    Give the redirect page a title
101        headTags:     Optionally add additional tags in <head/> section
102        delayTime:    time in seconds before carrying out redirect - redirect
103                      page will be displayed in this interval
104        redirectMsg:  Message to put on redirect page.  Can be plain text or
105                      formatted HTML"""
106       
107        if requestURI is None:
108            requestURI = self['requestURI'].value
109           
110        print """Content-type: text/html
111   
112<html>
113<head>
114<title>%s</title>
115<meta http-equiv="REFRESH" content="%d; url=%s?returnURI=%s">
116%s
117</head>
118<body>
119%s
120</body>
121</html>""" % \
122    (pageTitle, delayTime, requestURI, self.returnURI, headTags, redirectMsg)
123   
124   
125    #_________________________________________________________________________
126    def receiveCredsResponse(self, sessID, sessMgrURI):
127        """Remote site receives returned credentials and creates a new cookie
128        for its domain"""
129        self.setCookie(sessID, sessMgrURI)
130   
131   
132    #_________________________________________________________________________
133    def processCredsRequest(self, **returnCredsKwArgs):
134        """Receive request from remote site for credentials.  Process and
135        return via a redirect"""
136               
137        # Check for cookie in environment
138        if 'HTTP_COOKIE' in os.environ:
139            # Cookie is set - check for NDG cookie
140   
141            # Get session ID from existing cookie
142            cookie = SimpleCookie(os.environ['HTTP_COOKIE'])
143            if "NDG-ID1" not in cookie:
144                raise Exception, 'Expecting "NDG-ID1" ID for session cookie'
145   
146            if "NDG-ID2" not in cookie:
147                raise Exception, 'Expecting "NDG-ID2" ID for session cookie'
148   
149            self.returnCreds(cookie["NDG-ID1"].value,
150                             cookie["NDG-ID2"].value,
151                             **returnCredsKwArgs)
152        else:
153            # No cookie present - display login.  Submit must redirect back to
154            # sender
155            print """Content-type: text/html
156   
157    """
158            showLogin(self.returnURI,
159                      setCookie=True,
160                      pageTitle="Login",
161                      htmlTag=True,
162                      bodyTag=True)
163
164   
165    #_________________________________________________________________________
166    def returnCreds(self, 
167                    sessID,
168                    sessMgrURI,
169                    pageTitle='',
170                    delayTime=0,
171                    redirectMsg=''):
172        """User's home site returns credentials to requestor via a HTTP
173        redirect
174       
175        sessID:       NDG Session ID from cookie
176        sessMgrURI:   NDG Session Manager WSDL URI derived from cookie
177        pageTitle:    Give the redirect page a title
178        headTags:     Optionally add additional tags in <head/> section
179        delayTime:    time in seconds before carrying out redirect - redirect
180                      page will be displayed in this interval
181        redirectMsg:  Message to put on redirect page.  Can be plain text or
182                      formatted HTML"""
183   
184        print """Content-type: text/html
185   
186<html>
187<head>
188<title>%s</title>
189<meta http-equiv="REFRESH" content="%d; url=%s?NDG-ID1=%s&NDG-ID2=%s">
190%s
191</head>
192<body>
193%s
194</body>
195</html>""" % \
196(pageTitle, delayTime, self.returnURI, sessID,sessMgrURI,headTags,redirectMsg)
197   
198   
199    #_________________________________________________________________________
200    def setCookie(self, sessID=None, sessMgrURI=None, returnURI=None):
201        """Make NDG cookie"""
202   
203        cookie = SimpleCookie()
204       
205        # TODO: Replace with call to SessionMgr WS
206        if not sessID: sessID = base64.b64encode(os.urandom(128))
207        if not sessMgrURI: sessMgrURI = base64.b64encode(os.urandom(32))
208   
209        cookie['NDG-ID1'] = sessID
210        cookie['NDG-ID1']['expires'] = "Tue, 13-12-2006 12:00:00 GMT"
211        cookie['NDG-ID2'] = sessMgrURI
212        cookie['NDG-ID2']['expires'] = "Tue, 13-12-2006 12:00:00 GMT"
213   
214        if returnURI:
215            returnURIfield = """<meta http-equiv=\"REFRESH\"
216            content=\"0;url=./xDomainCredsTransfer.py?returnURI=%s\">""" % \
217                                                                    returnURI
218        else:
219            returnURIfield = ''
220   
221        print "Content-type: text/html"
222        print cookie.output() + os.linesep + os.linesep
223        print """<html>
224    <head>
225    <title>Set Cookie</title>
226    %s
227    </head>
228   
229    <body>
230        <h1>Cookie set!</h1>
231    </body>
232    </html>""" % returnURIfield
233   
234   
235    #_________________________________________________________________________
236    def showLogin(self,
237                  returnURI=None,
238                  setCookie=False,
239                  htmlTag=False,
240                  pageTitle=None,
241                  hdrTxt='',
242                  bodyTag=False,
243                  bAuthorise=False):
244        """Display initial NDG login form"""
245   
246        if htmlTag: print "<html>"
247   
248        if pageTitle:
249            print """<head>
250        <title>%s</title>
251        %s
252    </head>""" % (pageTitle, hdrTxt)
253   
254   
255        if bodyTag: print "<body>"
256   
257        if returnURI:
258            returnURIfield = \
259                "<input type=hidden name=returnURI value=\"%s\">" % returnURI
260        else:
261            returnURIfield = ''
262   
263   
264        if setCookie:
265            setCookieField = "<input type=hidden name=setCookie value=\"1\">"
266        else:
267            setCookieField = ''
268   
269   
270        if bAuthorise:
271            authoriseArg = "<input type=hidden name=authorise value=\"1\">"
272        else:
273            authoriseArg = ""
274   
275   
276        # Set authorisation method default
277        authorisationMethodChk = {  "allowMapping":              '',
278                                    "allowMappingWithPrompt" :   '',
279                                    "noMapping":                 ''}
280   
281        if self.__authorisationMethod is None:
282            # Default to safest option for user
283            authorisationMethodChk["allowMappingWithPrompt"] = ' checked'
284        else:
285            authorisationMethodChk[self.__authorisationMethod] = ' checked'
286   
287        print \
288    """<script language="javascript">
289    <!--
290        function toggleLayer(layerId)
291        {
292            if (document.getElementById)
293            {
294                // Standard
295                var style = document.getElementById(layerId).style;
296            }
297            else if (document.all)
298            {
299                // Old msie versions
300                var style = document.all[whichLayer].style;
301            }
302            else if (document.layers)
303            {
304                // nn4
305                var style = document.layers[whichLayer].style;
306            }
307            style.visibility = style.visibility == "visible" ? "hidden":"visible";
308        }
309    //-->
310    </script>
311    <h3>NERC Data Grid Site Login (Test)<BR clear=all></h3>
312    <hr>
313   
314    <form action="%s" method="POST">
315   
316    <table bgcolor=#ADD8E6 cellspacing=0 border=0 cellpadding=5>
317    <tbody>
318    <tr><td>User Name:</td> <td><input type=text name=userName value="">
319    </td></tr>
320    <tr>
321        <td>Password:</td>
322        <td><input type=password name=passPhrase></td>
323    </tr>
324    <tr>
325        <td colspan="2" align="right">
326            <a href="javascript:toggleLayer('advSettings');">Advanced Settings</a>
327            <input type=submit value="Login">
328        </td>
329    </tr>
330    %s
331    %s"""  % (self.scriptName, returnURIfield, setCookieField)
332   
333        print \
334    """</tbody></table>
335    <br>
336    <div id="advSettings" style="position: relative; visibility: hidden;">
337        <h4>Role Mapping for access to other trusted sites</h4>
338        <p>Your account has roles or <i>privileges</i> which determine what data you have access to.  If you access data at another NDG trusted site, these roles can be mapped to local roles at that site to help you gain access:
339        </p>
340        <table bgcolor=#ADD8E6 cellspacing=0 border=0 cellpadding=5>
341        <tbody>
342        <tr>
343        <td>
344            <input type="radio" name="authorisationMethod" value="allowMapping"%s>
345        </td>
346            <td>
347                Allow my roles to be mapped to local roles at other NDG trusted sites.
348            </td>
349        </tr>
350        <tr>
351            <td>
352                <input type="radio" name="authorisationMethod" value="allowMappingWithPrompt"%s>
353            </td>
354        <td>
355            Allow my roles to be mapped, but prompt me so that I may choose which roles to map before gaining access.
356        </td>
357        <tr>
358        <td>
359            <input type="radio" name="authorisationMethod" value="noMapping"%s>
360        </td>
361        <td>
362            Don't allow mapping of my roles.
363        </td>
364        </tr>
365        </tbody>
366        </table>
367    </div>
368    </form>
369    """ % (authorisationMethodChk['allowMapping'], \
370           authorisationMethodChk['allowMappingWithPrompt'], \
371           authorisationMethodChk['noMapping'])
372   
373        if bodyTag: print "</body>"
374        if htmlTag: print "</html>"
375   
376        # end of showLogin()
377   
378   
379    def showHomeSiteSelect(self,
380                           trustedHosts=None,
381                           scriptName=None,
382                           contentTypeHdr=False,
383                           htmlTag=False,
384                           hdrTag=False,
385                           hdrTxt='',
386                           bodyTag=False,
387                           pageTitle=""):
388
389        if trustedHosts:
390            self.trustedHosts = trustedHosts
391       
392        if scriptName:
393            self.scriptName = scriptName
394           
395               
396        if contentTypeHdr:
397            print "Content-type: text/html\n\n"
398   
399        if htmlTag:
400            print "<html>\n"
401
402        if hdrTag:
403            print """<head>
404           
405    <title>%s</title>
406    %s
407</head>""" % (pageTitle, hdrTxt)
408   
409   
410        if bodyTag:
411            print "<body>\n"
412
413            print """
414    <form action="%s" method="POST">
415    <table bgcolor=#ADD8E6 cellspacing=0 border=0 cellpadding=5>
416    <tbody>
417    <tr>
418      <td>
419        <select name="requestURI">       
420          <option value="">Select your home site...""" % self.scriptName
421         
422            for hostname, uri in trustedHosts.items():
423                print "<option value=\"%s\">%s" % (uri, hostname)
424               
425            print \
426"""        </select>
427      </td>
428      <td align="right">
429        <input type=submit value="Go">
430      </td>
431    </tr>
432    </tbody>
433    </table>
434    </form>"""
435
436        if bodyTag:
437            print "</body>\n"
438
439        if htmlTag:
440            print "</html>\n"
441   
442        # end of showHomeSiteSelect()
443       
444       
445if __name__ == "__main__":
446    securityCGI = SecurityCGI()
447    securityCGI()
Note: See TracBrowser for help on using the repository browser.