1.1 --- a/WebStack/Resources/OpenIDLogin.py Sun Feb 03 19:58:01 2008 +0000
1.2 +++ b/WebStack/Resources/OpenIDLogin.py Sun Feb 03 20:00:03 2008 +0000
1.3 @@ -4,7 +4,7 @@
1.4 OpenID provider login resources which redirect clients back to the application
1.5 ("relying party").
1.6
1.7 -Copyright (C) 2004, 2005, 2006, 2007 Paul Boddie <paul@boddie.org.uk>
1.8 +Copyright (C) 2004, 2005, 2006, 2007, 2008 Paul Boddie <paul@boddie.org.uk>
1.9
1.10 This library is free software; you can redistribute it and/or
1.11 modify it under the terms of the GNU Lesser General Public
1.12 @@ -27,6 +27,7 @@
1.13 import time
1.14 import random
1.15 import cgi # for escape
1.16 +import urlparse # for urlsplit
1.17
1.18 class OpenIDLoginUtils:
1.19
1.20 @@ -35,15 +36,28 @@
1.21 openid_ns = "http://specs.openid.net/auth/2.0"
1.22 signed_names = ["op_endpoint", "return_to", "response_nonce", "assoc_handle", "claimed_id", "identity"]
1.23
1.24 - def __init__(self, associations=None, use_redirect=1):
1.25 + def __init__(self, app_url, authenticator, associations=None, use_redirect=1, urlencoding=None):
1.26 +
1.27 + """
1.28 + Initialise the resource with the application URL 'app_url' and an
1.29 + 'authenticator'.
1.30 +
1.31 + The optional 'associations' is a mapping from association handles to
1.32 + secret keys.
1.33 +
1.34 + If the optional 'use_redirect' flag is set to a false value (which is
1.35 + not the default), a confirmation screen is given instead of immediately
1.36 + redirecting the user back to the original application.
1.37 +
1.38 + The optional 'urlencoding' parameter allows a special encoding to be
1.39 + used in producing the redirection path.
1.40 + """
1.41 +
1.42 + self.app_url = app_url
1.43 + self.authenticator = authenticator
1.44 self.associations = associations or {}
1.45 self.use_redirect = use_redirect
1.46 -
1.47 - def urlencode(self, trans, value):
1.48 - if not trans.default_charset:
1.49 - return trans.encode_path(value, self.urlencoding)
1.50 - else:
1.51 - return trans.encode_path(value)
1.52 + self.urlencoding = urlencoding
1.53
1.54 def get_openid_fields(self, trans, claimed_id, local_id, username, return_to, endpoint):
1.55
1.56 @@ -81,13 +95,13 @@
1.57
1.58 # Build an URL for returning to the application.
1.59
1.60 - url = "%s?" % fields["openid.return_to"][0]
1.61 + url = trans.encode_url_without_query(fields["openid.return_to"][0]) + "?"
1.62
1.63 first = 1
1.64 for name, value in fields.items():
1.65 if not first:
1.66 url += "&"
1.67 - url += "%s=%s" % (name, self.urlencode(trans, value[0]))
1.68 + url += "%s=%s" % (name, trans.encode_path(value[0]))
1.69 first = 0
1.70
1.71 return url
1.72 @@ -147,26 +161,6 @@
1.73
1.74 self.show_verification(trans, valid)
1.75
1.76 - def check_login(self, trans, fields):
1.77 -
1.78 - "Check the login details supplied in 'trans' and 'fields'."
1.79 -
1.80 - return_to = fields.get("openid.return_to", [""])[0]
1.81 - claimed_id = fields.get("openid.claimed_id", [""])[0]
1.82 - local_id = fields.get("openid.identity", [""])[0]
1.83 -
1.84 - # Check a combination of local identifier and username together with
1.85 - # the password.
1.86 -
1.87 - username = fields.get("username", [""])[0]
1.88 - password = fields.get("password", [""])[0]
1.89 -
1.90 - # NOTE: Permit flexibility in the credentials.
1.91 -
1.92 - if self.authenticator.authenticate(trans, (local_id, username), password):
1.93 - endpoint = self.app_url + trans.get_path_without_query(self.urlencoding)
1.94 - self.redirect_to_application(trans, claimed_id, local_id, username, return_to, endpoint)
1.95 -
1.96 class OpenIDLoginResource(OpenIDLoginUtils):
1.97
1.98 "A resource providing a login screen."
1.99 @@ -195,10 +189,7 @@
1.100 a subclass, or override the 'show_login' and 'show_success' methods.
1.101 """
1.102
1.103 - OpenIDLoginUtils.__init__(self, associations, use_redirect)
1.104 - self.app_url = app_url
1.105 - self.authenticator = authenticator
1.106 - self.urlencoding = urlencoding
1.107 + OpenIDLoginUtils.__init__(self, app_url, authenticator, associations, use_redirect, urlencoding)
1.108 self.encoding = encoding
1.109
1.110 def respond(self, trans):
1.111 @@ -224,6 +215,26 @@
1.112
1.113 self.show_login(trans, fields)
1.114
1.115 + def check_login(self, trans, fields):
1.116 +
1.117 + "Check the login details supplied in 'trans' and 'fields'."
1.118 +
1.119 + return_to = fields.get("openid.return_to", [""])[0]
1.120 + claimed_id = fields.get("openid.claimed_id", [""])[0]
1.121 + local_id = fields.get("openid.identity", [""])[0]
1.122 +
1.123 + # Check a combination of local identifier and username together with
1.124 + # the password.
1.125 +
1.126 + username = fields.get("username", [""])[0]
1.127 + password = fields.get("password", [""])[0]
1.128 +
1.129 + # NOTE: Permit flexibility in the credentials.
1.130 +
1.131 + if self.authenticator.authenticate(trans, (local_id, username), password):
1.132 + endpoint = self.app_url + trans.get_path_without_query(self.urlencoding)
1.133 + self.redirect_to_application(trans, claimed_id, local_id, username, return_to, endpoint)
1.134 +
1.135 def show_login(self, trans, fields):
1.136
1.137 """
1.138 @@ -234,7 +245,7 @@
1.139 claimed_id = fields.get("openid.claimed_id", [""])[0]
1.140 local_id = fields.get("openid.identity", [""])[0]
1.141
1.142 - trans.set_content_type(WebStack.Generic.ContentType("text/html", self.encoding))
1.143 + trans.set_content_type(WebStack.Generic.ContentType("text/html", self.encoding or trans.default_charset))
1.144 out = trans.get_response_stream()
1.145 out.write(self.login_page % tuple(map(cgi.escape, (return_to, claimed_id, local_id))))
1.146
1.147 @@ -245,7 +256,7 @@
1.148 dictionary of 'fields' providing details of the transaction.
1.149 """
1.150
1.151 - trans.set_content_type(WebStack.Generic.ContentType("text/html", self.encoding))
1.152 + trans.set_content_type(WebStack.Generic.ContentType("text/html", self.encoding or trans.default_charset))
1.153 out = trans.get_response_stream()
1.154 l = []
1.155 for name, values in fields.items():