# HG changeset patch # User paulb # Date 1195431497 0 # Node ID e2e9c1b51687a750a8c1ef6a9773c4b061de9614 # Parent 6c98d0d4988702c25728b3034747169311f23322 [project @ 2007-11-19 00:18:17 by paulb] Fixed initialisation-related activities in XSLFormsResource. Updated login-related code to work with WebStack 1.3. Added an OpenID login resource. diff -r 6c98d0d49887 -r e2e9c1b51687 XSLForms/Resources/Login.py --- a/XSLForms/Resources/Login.py Mon Nov 19 00:16:57 2007 +0000 +++ b/XSLForms/Resources/Login.py Mon Nov 19 00:18:17 2007 +0000 @@ -25,7 +25,6 @@ import XSLForms.Resources.WebResources import WebStack.Resources.LoginRedirect # LoginRedirectResource -import WebStack.Resources.Login # get_target class LoginResource(XSLForms.Resources.WebResources.XSLFormsResource): @@ -104,17 +103,17 @@ password = logelem.getAttribute("password") if self.authenticator.authenticate(trans, username, password): - app, path, qs = WebStack.Resources.Login.get_target(trans) + app = parameters.get("app", [""])[0] # Either redirect or switch to the success template. if self.use_redirect: - trans.redirect(app + trans.encode_path(path) + qs) + trans.redirect(app) else: template_name = "success" doc = form.new_instance("success") successelem = doc.xpath(self.path_to_success_element)[0] - successelem.setAttribute("location", app + trans.encode_path(path) + qs) + successelem.setAttribute("location", app) else: error = doc.createElement("error") logelem.appendChild(error) diff -r 6c98d0d49887 -r e2e9c1b51687 XSLForms/Resources/OpenIDLogin.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/XSLForms/Resources/OpenIDLogin.py Mon Nov 19 00:18:17 2007 +0000 @@ -0,0 +1,219 @@ +#!/usr/bin/env python + +""" +OpenID Login resources for XSLForms applications. These resources use "root" +attributes on transaction objects, and therefore should be defined within the +appropriate resources in site maps. + +Copyright (C) 2006, 2007 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the Free +Software Foundation; either version 3 of the License, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. + +You should have received a copy of the GNU Lesser General Public License along +with this program. If not, see . +""" + +from WebStack.Generic import ContentType, EndOfResponse +from WebStack.Resources.OpenIDLogin import OpenIDLoginUtils +from XSLForms.Resources.WebResources import XSLFormsResource + +import WebStack.Resources.OpenIDRedirect # LoginRedirectResource + +class OpenIDLoginResource(XSLFormsResource, OpenIDLoginUtils): + + """ + A login screen resource which should be modified or subclassed to define the + following attributes: + + * resource_dir + * template_resources - including a "login" entry for the login screen and + a "success" entry for a screen indicating a + successful login (used when redirects are not in + use) + * document_resources - including a "translations" entry + + The latter attribute is optional. + + The login template must define a "login" action, and provide a document + structure where the login credentials can be found through this class's + 'path_to_login_element' attribute (which can be overridden or modified). + Such a structure would be as follows for the default configuration: + + + + The success template must provide a document structure where the location of + the application can be found through this class's 'path_to_success_element' + attribute (which can be overridden or modified). Such a structure would be + as follows for the default configuration: + + + """ + + path_to_login_element = "/login" + path_to_success_element = "/success" + + def __init__(self, app_url, authenticator, associations=None, use_redirect=1): + + """ + Initialise the resource with an 'app_url' and an 'authenticator'. + + The optional 'associations' is a mapping from association handles to + secret keys. + + If the optional 'use_redirect' flag is set to a false value (which is + not the default), a confirmation screen is given instead of immediately + redirecting the user back to the original application. + + To get the root of the application, this resource needs an attribute on + the transaction called "root". + """ + + OpenIDLoginUtils.__init__(self, associations, use_redirect) + self.app_url = app_url + self.authenticator = authenticator + + def select_activity(self, trans, form): + form.set_activity("login") + + def respond_to_input(self, trans, form): + parameters = form.get_parameters() + + # Test for login. + + if parameters.has_key("login"): + self.check_login(trans, form) + + # Check for an OpenID signature verification request. + + elif parameters.get("openid.mode", [None])[0] == "check_authentication": + self.check_authentication(trans, trans.get_fields()) + + # NOTE: Permit association requests here. + # Otherwise, show the login form. + + else: + self.show_login(trans, form) + + # Methods called by the OpenID logic. + + def check_login(self, trans, form): + doc = form.get_document() + parameters = form.get_parameters() + + logelem = doc.xpath(self.path_to_login_element)[0] + return_to = logelem.getAttribute("return_to") or parameters.get("openid.return_to", [""])[0] + claimed_id = logelem.getAttribute("claimed_id") or parameters.get("openid.claimed_id", [""])[0] + local_id = logelem.getAttribute("identity") or parameters.get("openid.identity", [""])[0] + + username = logelem.getAttribute("username") + password = logelem.getAttribute("password") + + # If successful, switch to the success template and redirect. + # NOTE: Permit flexibility in the credentials. + + if self.authenticator.authenticate(trans, (local_id, username), password): + endpoint = self.app_url + trans.get_path_without_query() + self.redirect_to_application(trans, form, claimed_id, local_id, username, return_to, endpoint) + else: + error = doc.createElement("error") + logelem.appendChild(error) + error.setAttribute("message", "Username or password not valid") + self.show_login(trans, form) + + def redirect_to_application(self, trans, form, claimed_id, local_id, username, return_to, endpoint): + + """ + Redirect the client using 'trans', 'claimed_id', 'local_id', 'username' + and the given 'return_to' and 'endpoint' details. + """ + + fields = self.get_openid_fields(trans, claimed_id, local_id, username, return_to, endpoint) + url = self.get_openid_url(trans, fields) + + # Show the success page anyway. + # Offer a POST-based form for redirection. + + self.show_success(trans, form, fields) + if self.use_redirect: + trans.redirect(url) + + def show_login(self, trans, form): + + """ + Writes a login screen using the transaction 'trans' and 'form', + including details of the 'return_to' URL which the client was attempting + to access, along with the 'claimed_id' and 'local_id'. + """ + + doc = form.get_document() + parameters = form.get_parameters() + + logelem = doc.xpath(self.path_to_login_element)[0] + return_to = logelem.getAttribute("return_to") or parameters.get("openid.return_to", [""])[0] + claimed_id = logelem.getAttribute("claimed_id") or parameters.get("openid.claimed_id", [""])[0] + local_id = logelem.getAttribute("identity") or parameters.get("openid.identity", [""])[0] + + logelem = doc.xpath(self.path_to_login_element)[0] + logelem.setAttribute("return_to", return_to) + logelem.setAttribute("claimed_id", claimed_id) + logelem.setAttribute("identity", local_id) + + def show_success(self, trans, form, fields): + + """ + Writes a success screen using the transaction 'trans' and 'form', using + a dictionary of 'fields' providing details of the transaction. + """ + + # Switch to the success activity. + + form.set_activity("success") + doc = form.new_instance("success") + successelem = doc.xpath(self.path_to_success_element)[0] + successelem.setAttribute("location", fields["openid.return_to"][0]) + + # Add OpenID fields. + + for name, values in fields.items(): + field = doc.createElement("field") + field.setAttribute("name", name) + field.setAttribute("value", values[0]) + successelem.appendChild(field) + + form.set_document(doc) + + # Output preparation. + + def create_output(self, trans, form): + attributes = trans.get_attributes() + + stylesheet_parameters = {} + references = {} + + # Set up translations. + + if self.document_resources.has_key("translations"): + translations_xml = self.prepare_document("translations") + + try: + language = trans.get_content_languages()[0] + except IndexError: + language = "en" + + stylesheet_parameters["locale"] = language + references["translations"] = translations_xml + + # Complete the response. + + stylesheet_parameters["root"] = attributes["root"] + XSLFormsResource.create_output(self, trans, form, stylesheet_parameters=stylesheet_parameters, references=references) + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 6c98d0d49887 -r e2e9c1b51687 XSLForms/Resources/WebResources.py --- a/XSLForms/Resources/WebResources.py Mon Nov 19 00:16:57 2007 +0000 +++ b/XSLForms/Resources/WebResources.py Mon Nov 19 00:18:17 2007 +0000 @@ -380,15 +380,18 @@ the current document. """ + activity = form.get_activity() + # Transform, adding enumerations/ranges. - init_xsl = self.prepare_initialiser(form.get_activity()) - form.set_document( - self.get_result( - [init_xsl], form.get_document(), stylesheet_parameters, - stylesheet_expressions, references + if self.init_resources.has_key(activity): + init_xsl = self.prepare_initialiser(activity) + form.set_document( + self.get_result( + [init_xsl], form.get_document(), stylesheet_parameters, + stylesheet_expressions, references + ) ) - ) def respond_to_document(self, trans, form):