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