1 #!/usr/bin/env python 2 3 """ 4 OpenID initiation resources for XSLForms applications. These resources use 5 "root" attributes on transaction objects, and therefore should be defined within 6 the appropriate resources in site maps. 7 8 Copyright (C) 2006, 2007, 2008 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 from WebStack.Resources.OpenIDInitiation import OpenIDInitiationUtils 26 from XSLForms.Resources.WebResources import XSLFormsResource 27 28 class OpenIDInitiationResource(XSLFormsResource, OpenIDInitiationUtils): 29 30 """ 31 An initiation screen resource which should be modified or subclassed to 32 define the following attributes: 33 34 * resource_dir 35 * template_resources - including an "initiation" entry for the initiation 36 screen and a "success" entry for a screen 37 indicating successful redirection to a provider 38 (used when redirects are not in use) 39 * document_resources - including a "translations" entry 40 41 The latter attribute is optional. 42 43 The initiation template must define an "initiation" action, and provide a 44 document structure where the identity credentials can be found through this 45 class's 'path_to_identity_element' attribute (which can be overridden or 46 modified). Such a structure would be as follows for the default 47 configuration: 48 49 <initiation identity="..."/> 50 51 The success template must provide a document structure where the location of 52 the application can be found through this class's 'path_to_success_element' 53 attribute (which can be overridden or modified). Such a structure would be 54 as follows for the default configuration: 55 56 <success mode="..." return_to="..." claimed_id="..." identity="..."/> 57 """ 58 59 path_to_initiation_element = "/initiation" 60 path_to_success_element = "/success" 61 62 def __init__(self, openid_mode=None, use_redirect=1): 63 64 """ 65 Initialise the resource. 66 67 The optional 'openid_mode' parameter may be set to "checkid_immediate" 68 or "checkid_setup" (the default). 69 70 If the optional 'use_redirect' flag is set to a false value (which is 71 not the default), a confirmation screen is given instead of immediately 72 redirecting the user to the OpenID provider. 73 74 To get the root of the application, this resource needs an attribute on 75 the transaction called "root". 76 """ 77 78 OpenIDInitiationUtils.__init__(self, openid_mode, use_redirect) 79 80 def select_activity(self, trans, form): 81 form.set_activity("initiation") 82 83 def respond_to_input(self, trans, form): 84 parameters = form.get_parameters() 85 86 if parameters.has_key("app"): 87 app = parameters["app"] 88 else: 89 app = trans.get_fields_from_path().get("app", [""])[0] 90 91 if parameters.has_key("initiate"): 92 self.check_identity(trans, form, app) 93 # The above method does not return. 94 95 # Otherwise, show the initiation form. 96 97 self.show_initiation(trans, form, app) 98 99 # Methods called by the OpenID logic. 100 101 def check_identity(self, trans, form, app): 102 103 """ 104 Check the identity found through 'trans' and 'fields', using 'app' and 105 discovered information about the identity to redirect to the provider. 106 """ 107 108 doc = form.get_document() 109 parameters = form.get_parameters() 110 111 initelem = doc.xpath(self.path_to_initiation_element)[0] 112 identity = initelem.getAttribute("identity") or parameters.get("identity", [""])[0] 113 claimed_identifier, provider, local_identifier = self.get_provider_url(trans, identity) 114 115 if provider is not None: 116 self.redirect_to_provider(trans, form, app, claimed_identifier, provider, local_identifier) 117 118 def redirect_to_provider(self, trans, form, app, claimed_identifier, provider, local_identifier): 119 120 """ 121 Redirect the client using 'trans', 'form' and the given 'app', 122 'claimed_identifier', 'provider' and 'local_identifier' details. 123 124 See: 125 http://openid.net/specs/openid-authentication-2_0-12.html#rfc.section.5.2 126 http://openid.net/specs/openid-authentication-2_0-12.html#rfc.section.9 127 """ 128 129 url = self.get_redirect_url(trans, app, claimed_identifier, provider, local_identifier) 130 131 # Show the success page anyway. 132 # Offer a POST-based form for redirection. 133 134 self.show_success(trans, form, provider, app, claimed_identifier, local_identifier) 135 136 # Redirect to the OpenID provider URL. 137 138 if self.use_redirect: 139 trans.redirect(url) 140 else: 141 raise WebStack.Generic.EndOfResponse 142 143 def show_initiation(self, trans, form, app): 144 145 """ 146 Writes a initiation screen using the transaction 'trans' and 'form', 147 including details of the 'app' which the client was attempting to 148 access. 149 """ 150 151 doc = form.get_document() 152 parameters = form.get_parameters() 153 154 initelem = doc.xpath(self.path_to_initiation_element)[0] 155 identity = initelem.getAttribute("identity") or parameters.get("identity", [""])[0] 156 app = initelem.getAttribute("app") or parameters.get("app", [""])[0] 157 158 initelem = doc.xpath(self.path_to_initiation_element)[0] 159 initelem.setAttribute("identity", identity) 160 initelem.setAttribute("app", app) 161 162 def show_success(self, trans, form, provider, app, claimed_identifier, local_identifier): 163 164 """ 165 Writes a success screen using the transaction 'trans' and 'form', 166 including details of the OpenID 'provider', the 'app' URL, 167 'claimed_identifier' and 'local_identifier'. 168 """ 169 170 # Switch to the success activity. 171 172 form.set_activity("success") 173 doc = form.new_instance("success") 174 successelem = doc.xpath(self.path_to_success_element)[0] 175 successelem.setAttribute("provider", provider) 176 successelem.setAttribute("ns", self.openid_ns) 177 successelem.setAttribute("mode", self.openid_mode) 178 successelem.setAttribute("return_to", app) 179 successelem.setAttribute("claimed_id", claimed_identifier) 180 successelem.setAttribute("identity", local_identifier) 181 182 form.set_document(doc) 183 184 # Output preparation. 185 186 def create_output(self, trans, form): 187 attributes = trans.get_attributes() 188 189 stylesheet_parameters = {} 190 references = {} 191 192 # Set up translations. 193 194 if self.document_resources.has_key("translations"): 195 translations_xml = self.prepare_document("translations") 196 197 try: 198 language = trans.get_content_languages()[0] 199 except IndexError: 200 language = "en" 201 202 stylesheet_parameters["locale"] = language 203 references["translations"] = translations_xml 204 205 # Complete the response. 206 207 stylesheet_parameters["root"] = attributes["root"] 208 XSLFormsResource.create_output(self, trans, form, stylesheet_parameters=stylesheet_parameters, references=references) 209 210 # vim: tabstop=4 expandtab shiftwidth=4