# HG changeset patch # User Paul Boddie # Date 1226712786 -3600 # Node ID cb7d1387e1ef30e4699e6819abc803927de5023d # Parent c74377ab32be7ae652b87fcf831ecf5d023e033f Added Last-Modified header support for static resources. Added useful selector resources. Changed LoginRedirectorResource initialisation. Added missing SimpleWithLogin application. diff -r c74377ab32be -r cb7d1387e1ef WebStack/Resources/LoginRedirect.py --- a/WebStack/Resources/LoginRedirect.py Sun Aug 24 20:51:10 2008 +0200 +++ b/WebStack/Resources/LoginRedirect.py Sat Nov 15 02:33:06 2008 +0100 @@ -26,12 +26,42 @@ class LoginRedirectResource: - "A resource redirecting to a login URL." + """ + A resource redirecting to a login URL. A number of class attributes can be + set or overridden by instance attributes: + + * anonymous_parameter_name - if set to a value other than None, clients + providing a parameter of that name in the + URL will not be authenticated, but then + such clients will get a predefined user + identity associated with them, configurable + using 'anonymous_username' + + * logout_parameter_name - if set to a value other than None, clients + providing a parameter of that name in the URL + will become logged out; after logging out, + clients are redirected to a location which can + be configured by 'logout_url' - def __init__(self, resource, authenticator, login_url=None, app_url=None, - anonymous_parameter_name=None, anonymous_username="anonymous", - logout_parameter_name=None, logout_url="/", use_logout_redirect=1, - urlencoding=None, path_encoding=None): + * use_logout_redirect - if set to 0, a confirmation screen is given + instead of redirecting the user to 'logout_url' + + * path_encoding' (previously 'urlencoding') allows a special encoding to + be used in producing the redirection path + + To change the page used by this resource, either redefine the + 'logout_page' attribute in instances of this class or a subclass, or + override the 'show_logout' method. + """ + + anonymous_parameter_name = None + anonymous_username = "anonymous" + logout_parameter_name = None + logout_url = "/" + use_logout_redirect = 1 + path_encoding = None + + def __init__(self, resource, authenticator, login_url=None, app_url=None): """ Initialise the resource with a 'resource' for the application being @@ -45,39 +75,12 @@ The 'app_url' should be the "bare" reference using a protocol, host and port, not including any path information. - - If the optional 'anonymous_parameter_name' is set, clients providing a - parameter of that name in the URL will not be authenticated, but then - such clients will get a predefined user identity associated with them, - configurable using the optional 'anonymous_username'. - - If the optional 'logout_parameter_name' is set, clients providing a - parameter of that name in the URL will become logged out. After logging - out, clients are redirected to a location which can be configured by the - optional 'logout_url'. - - If the optional 'use_logout_redirect' flag is set to 0, a confirmation - screen is given instead of redirecting the user to the 'logout_url'. - - The optional 'path_encoding' parameter (previously 'urlencoding', which - is still supported) allows a special encoding to be used in producing - the redirection path. - - To change the page used by this resource, either redefine the - 'logout_page' attribute in instances of this class or a subclass, or - override the 'show_logout' method. """ self.login_url = login_url self.app_url = app_url self.resource = resource self.authenticator = authenticator - self.anonymous_parameter_name = anonymous_parameter_name - self.anonymous_username = anonymous_username - self.logout_parameter_name = logout_parameter_name - self.logout_url = logout_url - self.use_logout_redirect = use_logout_redirect - self.path_encoding = path_encoding or urlencoding def respond(self, trans): @@ -179,6 +182,28 @@ """ +class SiteLoginRedirectResource(LoginRedirectResource): + + "Login redirection within a site." + + site_attribute_name = "root" + + def _get_url(self, trans, site_relative_url): + + "Return the URL, using 'trans', for the given 'site_relative_url'." + + return trans.get_attributes()[self.site_attribute_name] + site_relative_url + + def get_login_url(self, trans): + + """ + Return the login URL, using 'trans' if necessary, in order to + provide a complete URL to redirect an authenticated user to their + originally requested page. + """ + + return self._get_url(trans, LoginRedirectResource.get_login_url(self, trans)) + class LoginRedirectAuthenticator(Verifier): """ diff -r c74377ab32be -r cb7d1387e1ef WebStack/Resources/Selectors.py --- a/WebStack/Resources/Selectors.py Sun Aug 24 20:51:10 2008 +0200 +++ b/WebStack/Resources/Selectors.py Sat Nov 15 02:33:06 2008 +0100 @@ -4,7 +4,7 @@ Resources which "select" other resources, sometimes causing desirable side-effects. -Copyright (C) 2007 Paul Boddie +Copyright (C) 2007, 2008 Paul Boddie This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -138,4 +138,62 @@ finally: self.store.rollback() +class ConditionalAuthSelector: + + """ + Test for the presence of authenticated user information, dispatching to the + authenticating resource where no such information can be found, dispatching + beyond the authenticating resource otherwise. + """ + + def __init__(self, resource): + + """ + Initialise the selector with the given authenticating 'resource' which + would normally conduct an authentication process unconditionally. This + 'resource' must itself have an attribute called 'resource' in order to + support dispatch beyond the authentication mechanisms where + authentication has already taken place. + """ + + self.resource = resource + + def respond(self, trans): + + """ + Respond to the transaction 'trans', either dispatching to this object's + resource for authentication, or if authentication has already taken + place and a user has been set, dispatching to the target of the resource + - the resource wrapped by the authenticating resource. + """ + + if trans.get_user() is None: + self.resource.respond(trans) + else: + self.resource.resource.respond(trans) + +class AuthInfoSelector: + + """ + A selector which ensures that any information set by authenticators is made + available. + """ + + def __init__(self, resource, authenticator): + + "Initialise the selector with the given 'resource' and 'authenticator'." + + self.resource = resource + self.authenticator = authenticator + + def respond(self, trans): + + """ + Respond to 'trans' by performing authentication and forwarding the + request. + """ + + self.authenticator.authenticate(trans) + self.resource.respond(trans) + # vim: tabstop=4 expandtab shiftwidth=4 diff -r c74377ab32be -r cb7d1387e1ef WebStack/Resources/Static.py --- a/WebStack/Resources/Static.py Sun Aug 24 20:51:10 2008 +0200 +++ b/WebStack/Resources/Static.py Sat Nov 15 02:33:06 2008 +0100 @@ -3,7 +3,7 @@ """ Resources for serving static content. -Copyright (C) 2004, 2005, 2006 Paul Boddie +Copyright (C) 2004, 2005, 2006, 2007, 2008 Paul Boddie This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -22,6 +22,7 @@ from WebStack.Generic import ContentType, EndOfResponse import os +import email.utils class DirectoryResource: @@ -117,7 +118,11 @@ # Write the file to the client. - f = open(os.path.join(self.directory, filename), "rb") + pathname = os.path.join(self.directory, filename) + mtime = email.utils.formatdate(os.path.getmtime(pathname), usegmt=1) + trans.set_header_value("Last-Modified", mtime) + + f = open(pathname, "rb") out.write(f.read()) f.close() @@ -143,7 +148,10 @@ self.content_type = content_type def respond(self, trans): + mtime = email.utils.formatdate(os.path.getmtime(self.filename), usegmt=1) + trans.set_content_type(self.content_type) + trans.set_header_value("Last-Modified", mtime) f = open(self.filename, "rb") trans.get_response_stream().write(f.read()) f.close() diff -r c74377ab32be -r cb7d1387e1ef examples/Common/SimpleWithLogin/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/Common/SimpleWithLogin/__init__.py Sat Nov 15 02:33:06 2008 +0100 @@ -0,0 +1,41 @@ +#!/usr/bin/env python + +from WebStack.Generic import ContentType +from WebStack.Resources.LoginRedirect import LoginRedirectResource, LoginRedirectAuthenticator +from WebStack.Resources.Login import LoginResource, LoginAuthenticator +from WebStack.Resources.ResourceMap import MapResource +from Simple import SimpleResource + +def get_site_map(app_url, login_url, secret_key): + + """ + Resource a resource for the site having the given 'app_url' and 'login_url', + using the given 'secret_key' for authentication tokens. + """ + + simple = LoginRedirectResource( + login_url=login_url, + app_url=app_url, + resource=SimpleResource(), + authenticator=LoginRedirectAuthenticator(secret_key=secret_key) + ) + + simple.anonymous_parameter_name = "anonymous" + simple.logout_parameter_name = "logout" + + return MapResource({ + "simple" : simple, + "login" : + LoginResource( + LoginAuthenticator( + secret_key=secret_key, + credentials=( + ("badger", "abc"), + ("vole", "xyz"), + ) + ) + ), + "" : simple + }) + +# vim: tabstop=4 expandtab shiftwidth=4