1 #!/usr/bin/env python 2 3 "An example login screen." 4 5 import WebStack.Generic 6 from WebStack.Helpers.Auth import get_token 7 8 class LoginResource: 9 10 "A resource providing a login screen." 11 12 def __init__(self, authenticator, use_redirect=1): 13 14 """ 15 Initialise the resource with an 'authenticator'. If the optional 'use_redirect' 16 flag is set to 0, a confirmation screen is given instead of redirecting the user 17 back to the original application. 18 """ 19 20 self.authenticator = authenticator 21 self.use_redirect = use_redirect 22 23 def respond(self, trans): 24 25 fields = trans.get_fields_from_body() 26 redirect = "" 27 28 if fields.has_key("redirect"): 29 redirects = fields["redirect"] 30 redirect = redirects[0] 31 if self.authenticator.authenticate(trans): 32 if self.use_redirect: 33 trans.set_header_value("Location", redirect) 34 trans.set_response_code(307) 35 return 36 else: 37 self._show_success(trans, redirect) 38 return 39 else: 40 fields = trans.get_fields_from_path() 41 if fields.has_key("redirect"): 42 redirects = fields["redirect"] 43 redirect = redirects[0] 44 45 self._show_login(trans, redirect) 46 47 def _show_login(self, trans, redirect): 48 49 # When authentication fails or is yet to take place, show the login 50 # screen. 51 52 trans.set_content_type(WebStack.Generic.ContentType("text/html")) 53 out = trans.get_response_stream() 54 out.write(""" 55 <html> 56 <head> 57 <title>Login Example</title> 58 </head> 59 <body> 60 <h1>Login</h1> 61 <form method="POST"> 62 <p>Username: <input name="username" type="text" size="12"/></p> 63 <p>Password: <input name="password" type="text" size="12"/></p> 64 <p><input name="login" type="submit" value="Login"/></p> 65 <input name="redirect" type="hidden" value="%s"/> 66 </form> 67 </body> 68 </html> 69 """ % redirect) 70 71 def _show_success(self, trans, redirect): 72 73 # When authentication fails or is yet to take place, show the login 74 # screen. 75 76 trans.set_content_type(WebStack.Generic.ContentType("text/html")) 77 out = trans.get_response_stream() 78 out.write(""" 79 <html> 80 <head> 81 <title>Login Example</title> 82 </head> 83 <body> 84 <h1>Login Successful</h1> 85 <p>Please proceed <a href="%s">to the application</a>.</p> 86 </body> 87 </html> 88 """ % redirect) 89 90 def _decode(self, url): 91 92 "Decode the given 'url' for redirection purposes." 93 94 return url.replace("%3f", "?").replace("%26", "&") 95 96 class LoginAuthenticator: 97 98 def __init__(self, secret_key, credentials, cookie_name=None): 99 100 """ 101 Initialise the authenticator with a 'secret_key', the authenticator's registry of 102 'credentials' and an optional 'cookie_name'. 103 104 The 'credentials' must be an object which supports tests of the form 105 '(username, password) in credentials'. 106 """ 107 108 self.secret_key = secret_key 109 self.credentials = credentials 110 self.cookie_name = cookie_name or "LoginAuthenticator" 111 112 def authenticate(self, trans): 113 114 # Process any supplied parameters. 115 116 fields = trans.get_fields_from_body() 117 118 if fields.has_key("username") and fields.has_key("password"): 119 usernames, passwords = fields["username"], fields["password"] 120 121 # Insist on only one username and password. 122 123 if len(usernames) == 1 and len(passwords) == 1: 124 username, password = usernames[0], passwords[0] 125 126 # Check against the class's credentials. 127 128 if (username, password) in self.credentials: 129 130 # Make a special cookie token. 131 132 trans.set_cookie_value( 133 self.cookie_name, 134 get_token(username, self.secret_key) 135 ) 136 137 return 1 138 139 return 0 140 141 # vim: tabstop=4 expandtab shiftwidth=4