1 #!/usr/bin/env python 2 3 """ 4 Login resources for XSLForms applications. These resources use "root" attributes 5 on transaction objects, and therefore should be defined within the appropriate 6 resources in site maps. 7 8 Copyright (C) 2006, 2007 Paul Boddie <paul@boddie.org.uk> 9 10 This program is free software; you can redistribute it and/or modify it under 11 the terms of the GNU Lesser General Public License as published by the Free 12 Software Foundation; either version 3 of the License, or (at your option) any 13 later version. 14 15 This program is distributed in the hope that it will be useful, but WITHOUT 16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 17 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 18 details. 19 20 You should have received a copy of the GNU Lesser General Public License along 21 with this program. If not, see <http://www.gnu.org/licenses/>. 22 """ 23 24 from WebStack.Generic import ContentType, EndOfResponse 25 import XSLForms.Resources.WebResources 26 27 import WebStack.Resources.LoginRedirect # LoginRedirectResource 28 import WebStack.Resources.Login # get_target 29 30 class LoginResource(XSLForms.Resources.WebResources.XSLFormsResource): 31 32 """ 33 A login screen resource which should be modified or subclassed to define the 34 following attributes: 35 36 * resource_dir 37 * template_resources - including a "login" entry for the login screen and 38 a "success" entry for a screen indicating a 39 successful login (used when redirects are not in 40 use) 41 * document_resources - including a "translations" entry 42 43 The latter attribute is optional. 44 45 The login template must define a "login" action, and provide a document 46 structure where the login credentials can be found through this class's 47 'path_to_login_element' attribute (which can be overridden or modified). 48 Such a structure would be as follows for the default configuration: 49 50 <login username="..." password="..."/> 51 52 The success template must provide a document structure where the location of 53 the application can be found through this class's 'path_to_success_element' 54 attribute (which can be overridden or modified). Such a structure would be 55 as follows for the default configuration: 56 57 <success location="..."/> 58 """ 59 60 path_to_login_element = "/login" 61 path_to_success_element = "/success" 62 63 def __init__(self, authenticator, use_redirect=1): 64 65 """ 66 Initialise the resource with an 'authenticator'. If the optional 67 'use_redirect' parameter is specified and set to a false value (unlike 68 the default), 69 70 To get the root of the application, this resource needs an attribute on 71 the transaction called "root". 72 """ 73 74 self.authenticator = authenticator 75 self.use_redirect = use_redirect 76 77 def respond_to_form(self, trans, form): 78 79 """ 80 Respond to a request having the given transaction 'trans' and the given 81 'form' information. 82 """ 83 84 parameters = form.get_parameters() 85 documents = form.get_documents() 86 attributes = trans.get_attributes() 87 88 # Ensure the presence of a document. 89 90 if documents.has_key("login"): 91 doc = documents["login"] 92 else: 93 doc = form.new_instance("login") 94 95 template_name = "login" 96 97 # NOTE: Consider initialisation of both the login and success documents. 98 99 # Test for login. 100 101 if parameters.has_key("login"): 102 logelem = doc.xpath(self.path_to_login_element)[0] 103 username = logelem.getAttribute("username") 104 password = logelem.getAttribute("password") 105 106 if self.authenticator.authenticate(trans, username, password): 107 app, path, qs = WebStack.Resources.Login.get_target(trans) 108 109 # Either redirect or switch to the success template. 110 111 if self.use_redirect: 112 trans.redirect(app + trans.encode_path(path) + qs) 113 else: 114 template_name = "success" 115 doc = form.new_instance("success") 116 successelem = doc.xpath(self.path_to_success_element)[0] 117 successelem.setAttribute("location", app + trans.encode_path(path) + qs) 118 else: 119 error = doc.createElement("error") 120 logelem.appendChild(error) 121 error.setAttribute("message", "Username or password not valid") 122 123 # Start the response. 124 125 trans.set_content_type(ContentType("application/xhtml+xml")) 126 stylesheet_parameters = {} 127 references = {} 128 129 # Set up translations. 130 131 if self.document_resources.has_key("translations"): 132 translations_xml = self.prepare_document("translations") 133 134 try: 135 language = trans.get_content_languages()[0] 136 except IndexError: 137 language = "en" 138 139 stylesheet_parameters["locale"] = language 140 references["translations"] = translations_xml 141 142 # Complete the response. 143 144 trans_xsl = self.prepare_output(template_name) 145 stylesheet_parameters["root"] = attributes["root"] 146 self.send_output(trans, [trans_xsl], doc, stylesheet_parameters, references=references) 147 148 class LoginRedirectResource(WebStack.Resources.LoginRedirect.LoginRedirectResource): 149 150 "A redirect resource which uses dynamic knowledge about the URL space." 151 152 def __init__(self, host, path_to_login, *args, **kw): 153 154 """ 155 Initialise the resource with the 'host', 'path_to_login' (the path from 156 the root of the application to the login screen), and other 157 LoginRedirectResource details. 158 159 To get the root of the application, this resource needs an attribute on 160 the transaction called "root". 161 162 Examples of 'path_to_login' with "root" attribute and result: 163 164 "login", "/" -> "/login" 165 "login", "/app/" -> "/app/login" 166 "app/login", "/" -> "/app/login" 167 """ 168 169 self.host = host 170 self.path_to_login = path_to_login 171 WebStack.Resources.LoginRedirect.LoginRedirectResource.__init__(self, *args, **kw) 172 173 def get_app_url(self, trans): 174 return self.host 175 176 def get_login_url(self, trans): 177 return self.host + trans.get_attributes()["root"] + self.path_to_login 178 179 # vim: tabstop=4 expandtab shiftwidth=4