1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/WebStack/Resources/LoginRedirect.py Sun May 30 17:21:51 2004 +0000
1.3 @@ -0,0 +1,175 @@
1.4 +#!/usr/bin/env python
1.5 +
1.6 +"Login redirection."
1.7 +
1.8 +from WebStack.Helpers.Auth import get_token
1.9 +import WebStack.Generic
1.10 +
1.11 +class LoginRedirectResource:
1.12 +
1.13 + "A resource redirecting to a login URL."
1.14 +
1.15 + def __init__(self, login_url, app_url, resource, authenticator, anonymous_parameter_name=None,
1.16 + anonymous_username="anonymous", logout_parameter_name=None, logout_url="/",
1.17 + use_logout_redirect=1):
1.18 +
1.19 + """
1.20 + Initialise the resource with a 'login_url', an 'app_url' where the 'resource' for
1.21 + the application being protected should be reachable, and an 'authenticator'.
1.22 +
1.23 + If the optional 'anonymous_parameter_name' is set, clients providing a parameter
1.24 + of that name in the URL will not be authenticated, but then such clients will get
1.25 + a predefined user identity associated with them, configurable using the optional
1.26 + 'anonymous_username'.
1.27 +
1.28 + If the optional 'logout_parameter_name' is set, clients providing a parameter of
1.29 + that name in the URL will become logged out. After logging out, clients are
1.30 + redirected to a location which can be configured by the optional 'logout_url'.
1.31 +
1.32 + If the optional 'use_logout_redirect' flag is set to 0, a confirmation screen is
1.33 + given instead of redirecting the user to the 'logout_url'.
1.34 + """
1.35 +
1.36 + self.login_url = login_url
1.37 + self.app_url = app_url
1.38 + self.resource = resource
1.39 + self.authenticator = authenticator
1.40 + self.anonymous_parameter_name = anonymous_parameter_name
1.41 + self.anonymous_username = anonymous_username
1.42 + self.logout_parameter_name = logout_parameter_name
1.43 + self.logout_url = logout_url
1.44 + self.use_logout_redirect = use_logout_redirect
1.45 +
1.46 + def respond(self, trans):
1.47 +
1.48 + "Respond using the given transaction 'trans'."
1.49 +
1.50 + fields_path = trans.get_fields_from_path()
1.51 +
1.52 + # Check for the logout parameter, if appropriate.
1.53 +
1.54 + if self.logout_parameter_name is not None and fields_path.has_key(self.logout_parameter_name):
1.55 +
1.56 + # Remove the special cookie token, then pass on the transaction.
1.57 +
1.58 + self.authenticator.unset_token(trans)
1.59 +
1.60 + # Redirect to the logout URL.
1.61 +
1.62 + if self.use_logout_redirect:
1.63 + trans.set_header_value("Location", self.logout_url)
1.64 + trans.set_response_code(307)
1.65 +
1.66 + # Show the logout confirmation anyway.
1.67 +
1.68 + self._show_logout(trans, self.logout_url)
1.69 +
1.70 + # Check the authentication details with the specified authenticator.
1.71 +
1.72 + elif self.authenticator.authenticate(trans):
1.73 +
1.74 + # If successful, pass on the transaction.
1.75 +
1.76 + self.resource.respond(trans)
1.77 +
1.78 + # Check for the anonymous parameter, if appropriate.
1.79 +
1.80 + elif self.anonymous_parameter_name is not None and fields_path.has_key(self.anonymous_parameter_name):
1.81 +
1.82 + # Make a special cookie token, then pass on the transaction.
1.83 +
1.84 + self.authenticator.set_token(trans, self.anonymous_username)
1.85 + self.resource.respond(trans)
1.86 +
1.87 + else:
1.88 +
1.89 + # Redirect to the login URL.
1.90 +
1.91 + trans.set_header_value("Location", "%s?redirect=%s%s" % (
1.92 + self.login_url, self.app_url, self._encode(trans.get_path()))
1.93 + )
1.94 + trans.set_response_code(307)
1.95 +
1.96 + def _encode(self, url):
1.97 +
1.98 + "Encode the given 'url' for redirection purposes."
1.99 +
1.100 + return url.replace("?", "%3f").replace("&", "%26")
1.101 +
1.102 + def _show_logout(self, trans, redirect):
1.103 +
1.104 + """
1.105 + Write a confirmation page to 'trans' containing the 'redirect' URL which the
1.106 + client should be sent to upon logout.
1.107 + """
1.108 +
1.109 + # When logout takes place, show the login screen.
1.110 +
1.111 + trans.set_content_type(WebStack.Generic.ContentType("text/html"))
1.112 + out = trans.get_response_stream()
1.113 + out.write("""
1.114 +<html>
1.115 + <head>
1.116 + <title>Logout</title>
1.117 + </head>
1.118 + <body>
1.119 + <h1>Logout Successful</h1>
1.120 + <p>Please proceed <a href="%s">to the application</a>.</p>
1.121 + </body>
1.122 +</html>
1.123 +""" % redirect)
1.124 +
1.125 +class LoginRedirectAuthenticator:
1.126 +
1.127 + """
1.128 + An authenticator which verifies the credentials provided in a special login cookie.
1.129 + """
1.130 +
1.131 + def __init__(self, secret_key, cookie_name=None):
1.132 +
1.133 + "Initialise the authenticator with a 'secret_key' and an optional 'cookie_name'."
1.134 +
1.135 + self.secret_key = secret_key
1.136 + self.cookie_name = cookie_name or "LoginAuthenticator"
1.137 +
1.138 + def authenticate(self, trans):
1.139 +
1.140 + "Authenticate the originator of 'trans', updating the object if successful."
1.141 +
1.142 + cookie = trans.get_cookie(self.cookie_name)
1.143 + if cookie is None or cookie.value is None:
1.144 + return 0
1.145 +
1.146 + # Test the token from the cookie against a recreated token using the
1.147 + # given information.
1.148 +
1.149 + username, code = cookie.value.split(":")
1.150 + if cookie.value == get_token(username, self.secret_key):
1.151 +
1.152 + # Update the transaction with the user details.
1.153 +
1.154 + trans.set_user(username)
1.155 + return 1
1.156 + else:
1.157 + return 0
1.158 +
1.159 + def set_token(self, trans, username):
1.160 +
1.161 + "Set an authentication token in 'trans' with the given 'username'."
1.162 +
1.163 + trans.set_cookie_value(
1.164 + self.cookie_name,
1.165 + get_token(username, self.secret_key)
1.166 + )
1.167 +
1.168 + # Update the transaction with the user details.
1.169 +
1.170 + trans.set_user(username)
1.171 +
1.172 + def unset_token(self, trans):
1.173 +
1.174 + "Unset the authentication token in 'trans'."
1.175 +
1.176 + trans.delete_cookie(self.cookie_name)
1.177 +
1.178 +# vim: tabstop=4 expandtab shiftwidth=4