1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/WebStack/Resources/Login.py Sun May 30 17:44:17 2004 +0000
1.3 @@ -0,0 +1,171 @@
1.4 +#!/usr/bin/env python
1.5 +
1.6 +"Login resources which redirect clients back to an application after a successful login."
1.7 +
1.8 +import WebStack.Generic
1.9 +from WebStack.Helpers.Auth import get_token
1.10 +
1.11 +class LoginResource:
1.12 +
1.13 + "A resource providing a login screen."
1.14 +
1.15 + def __init__(self, authenticator, use_redirect=1):
1.16 +
1.17 + """
1.18 + Initialise the resource with an 'authenticator'.
1.19 +
1.20 + If the optional 'use_redirect' flag is set to 0, a confirmation screen is given
1.21 + instead of redirecting the user back to the original application.
1.22 + """
1.23 +
1.24 + self.authenticator = authenticator
1.25 + self.use_redirect = use_redirect
1.26 +
1.27 + def respond(self, trans):
1.28 +
1.29 + "Respond using the transaction 'trans'."
1.30 +
1.31 + fields_path = trans.get_fields_from_path()
1.32 + fields_body = trans.get_fields_from_body()
1.33 +
1.34 + # NOTE: Handle missing redirects better.
1.35 +
1.36 + if fields_body.has_key("redirect"):
1.37 + redirects = fields_body["redirect"]
1.38 + redirect = redirects[0]
1.39 + elif fields_path.has_key("redirect"):
1.40 + redirects = fields_path["redirect"]
1.41 + redirect = redirects[0]
1.42 + else:
1.43 + redirect = ""
1.44 +
1.45 + # Check for a submitted login form.
1.46 +
1.47 + if fields_body.has_key("login"):
1.48 + if self.authenticator.authenticate(trans):
1.49 + self._redirect(trans, redirect)
1.50 + return
1.51 +
1.52 + # Otherwise, show the login form.
1.53 +
1.54 + self._show_login(trans, redirect)
1.55 +
1.56 + def _redirect(self, trans, redirect):
1.57 +
1.58 + "Redirect the client using 'trans' and the given 'redirect' URL."
1.59 +
1.60 + if self.use_redirect:
1.61 + trans.set_header_value("Location", redirect)
1.62 + trans.set_response_code(307)
1.63 +
1.64 + # Show the success page anyway.
1.65 +
1.66 + self._show_success(trans, redirect)
1.67 +
1.68 + def _show_login(self, trans, redirect):
1.69 +
1.70 + """
1.71 + Writes a login screen using the transaction 'trans', including details of the
1.72 + 'redirect' URL which the client was attempting to access.
1.73 + """
1.74 +
1.75 + trans.set_content_type(WebStack.Generic.ContentType("text/html"))
1.76 + out = trans.get_response_stream()
1.77 + out.write("""
1.78 +<html>
1.79 + <head>
1.80 + <title>Login Example</title>
1.81 + </head>
1.82 + <body>
1.83 + <h1>Login</h1>
1.84 + <form method="POST">
1.85 + <p>Username: <input name="username" type="text" size="12"/></p>
1.86 + <p>Password: <input name="password" type="text" size="12"/></p>
1.87 + <p><input name="login" type="submit" value="Login"/></p>
1.88 + <input name="redirect" type="hidden" value="%s"/>
1.89 + </form>
1.90 + </body>
1.91 +</html>
1.92 +""" % redirect)
1.93 +
1.94 + def _show_success(self, trans, redirect):
1.95 +
1.96 + # When authentication fails or is yet to take place, show the login
1.97 + # screen.
1.98 +
1.99 + trans.set_content_type(WebStack.Generic.ContentType("text/html"))
1.100 + out = trans.get_response_stream()
1.101 + out.write("""
1.102 +<html>
1.103 + <head>
1.104 + <title>Login Example</title>
1.105 + </head>
1.106 + <body>
1.107 + <h1>Login Successful</h1>
1.108 + <p>Please proceed <a href="%s">to the application</a>.</p>
1.109 + </body>
1.110 +</html>
1.111 +""" % redirect)
1.112 +
1.113 + def _decode(self, url):
1.114 +
1.115 + "Decode the given 'url' for redirection purposes."
1.116 +
1.117 + return url.replace("%3f", "?").replace("%26", "&")
1.118 +
1.119 +class LoginAuthenticator:
1.120 +
1.121 + def __init__(self, secret_key, credentials, cookie_name=None):
1.122 +
1.123 + """
1.124 + Initialise the authenticator with a 'secret_key', the authenticator's registry of
1.125 + 'credentials' and an optional 'cookie_name'.
1.126 +
1.127 + The 'credentials' must be an object which supports tests of the form
1.128 + '(username, password) in credentials'.
1.129 + """
1.130 +
1.131 + self.secret_key = secret_key
1.132 + self.credentials = credentials
1.133 + self.cookie_name = cookie_name or "LoginAuthenticator"
1.134 +
1.135 + def authenticate(self, trans):
1.136 +
1.137 + """
1.138 + Authenticate the sender of the transaction 'trans', returning 1 (true) if they are
1.139 + recognised, 0 (false) otherwise.
1.140 + """
1.141 +
1.142 + # Process any supplied parameters.
1.143 +
1.144 + fields = trans.get_fields_from_body()
1.145 +
1.146 + if fields.has_key("username") and fields.has_key("password"):
1.147 + usernames, passwords = fields["username"], fields["password"]
1.148 +
1.149 + # Insist on only one username and password.
1.150 +
1.151 + if len(usernames) == 1 and len(passwords) == 1:
1.152 + username, password = usernames[0], passwords[0]
1.153 +
1.154 + # Check against the class's credentials.
1.155 +
1.156 + if (username, password) in self.credentials:
1.157 +
1.158 + # Make a special cookie token.
1.159 +
1.160 + self.set_token(trans, username)
1.161 + return 1
1.162 +
1.163 + return 0
1.164 +
1.165 + def set_token(self, trans, username):
1.166 +
1.167 + "Set an authentication token in 'trans' with the given 'username'."
1.168 +
1.169 + trans.set_cookie_value(
1.170 + self.cookie_name,
1.171 + get_token(username, self.secret_key)
1.172 + )
1.173 +
1.174 +# vim: tabstop=4 expandtab shiftwidth=4