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