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