1 | #!/usr/bin/env python |
---|
2 | |
---|
3 | """NDG Security CGI services |
---|
4 | |
---|
5 | NERC Data Grid Project |
---|
6 | |
---|
7 | P J Kershaw 23/05/06 |
---|
8 | |
---|
9 | Copyright (C) 2006 CCLRC & NERC |
---|
10 | |
---|
11 | This software may be distributed under the terms of the Q Public License, |
---|
12 | version 1.0 or later. |
---|
13 | """ |
---|
14 | |
---|
15 | from NDG.SecurityClient import * |
---|
16 | from Cookie import SimpleCookie |
---|
17 | |
---|
18 | import sys |
---|
19 | import cgi |
---|
20 | import os |
---|
21 | |
---|
22 | |
---|
23 | class 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 | |
---|
445 | if __name__ == "__main__": |
---|
446 | securityCGI = SecurityCGI() |
---|
447 | securityCGI() |
---|