From: <lph...@us...> - 2008-06-19 21:49:41
|
Revision: 13302 http://tikiwiki.svn.sourceforge.net/tikiwiki/?rev=13302&view=rev Author: lphuberdeau Date: 2008-06-19 14:49:50 -0700 (Thu, 19 Jun 2008) Log Message: ----------- [MOD] Updating OpenID library. Modified Paths: -------------- branches/1.10/lib/pear/Auth/OpenID/Association.php branches/1.10/lib/pear/Auth/OpenID/BigMath.php branches/1.10/lib/pear/Auth/OpenID/Consumer.php branches/1.10/lib/pear/Auth/OpenID/CryptUtil.php branches/1.10/lib/pear/Auth/OpenID/DatabaseConnection.php branches/1.10/lib/pear/Auth/OpenID/DiffieHellman.php branches/1.10/lib/pear/Auth/OpenID/Discover.php branches/1.10/lib/pear/Auth/OpenID/DumbStore.php branches/1.10/lib/pear/Auth/OpenID/Extension.php branches/1.10/lib/pear/Auth/OpenID/FileStore.php branches/1.10/lib/pear/Auth/OpenID/HMACSHA1.php branches/1.10/lib/pear/Auth/OpenID/Interface.php branches/1.10/lib/pear/Auth/OpenID/KVForm.php branches/1.10/lib/pear/Auth/OpenID/Message.php branches/1.10/lib/pear/Auth/OpenID/MySQLStore.php branches/1.10/lib/pear/Auth/OpenID/Nonce.php branches/1.10/lib/pear/Auth/OpenID/Parse.php branches/1.10/lib/pear/Auth/OpenID/PostgreSQLStore.php branches/1.10/lib/pear/Auth/OpenID/SQLStore.php branches/1.10/lib/pear/Auth/OpenID/SQLiteStore.php branches/1.10/lib/pear/Auth/OpenID/SReg.php branches/1.10/lib/pear/Auth/OpenID/Server.php branches/1.10/lib/pear/Auth/OpenID/ServerRequest.php branches/1.10/lib/pear/Auth/OpenID/TrustRoot.php branches/1.10/lib/pear/Auth/OpenID/URINorm.php branches/1.10/lib/pear/Auth/OpenID.php branches/1.10/lib/pear/Auth/Yadis/HTTPFetcher.php branches/1.10/lib/pear/Auth/Yadis/Manager.php branches/1.10/lib/pear/Auth/Yadis/Misc.php branches/1.10/lib/pear/Auth/Yadis/ParanoidHTTPFetcher.php branches/1.10/lib/pear/Auth/Yadis/ParseHTML.php branches/1.10/lib/pear/Auth/Yadis/PlainHTTPFetcher.php branches/1.10/lib/pear/Auth/Yadis/XRDS.php branches/1.10/lib/pear/Auth/Yadis/XRI.php branches/1.10/lib/pear/Auth/Yadis/Yadis.php branches/1.10/tiki-login_openid.php Added Paths: ----------- branches/1.10/lib/pear/Auth/OpenID/AX.php branches/1.10/lib/pear/Auth/OpenID/MemcachedStore.php branches/1.10/lib/pear/Auth/OpenID/PAPE.php Added: branches/1.10/lib/pear/Auth/OpenID/AX.php =================================================================== --- branches/1.10/lib/pear/Auth/OpenID/AX.php (rev 0) +++ branches/1.10/lib/pear/Auth/OpenID/AX.php 2008-06-19 21:49:50 UTC (rev 13302) @@ -0,0 +1,1023 @@ +<?php + +/** + * Implements the OpenID attribute exchange specification, version 1.0 + * as of svn revision 370 from openid.net svn. + * + * @package OpenID + */ + +/** + * Require utility classes and functions for the consumer. + */ +require_once "Auth/OpenID/Extension.php"; +require_once "Auth/OpenID/Message.php"; +require_once "Auth/OpenID/TrustRoot.php"; + +define('Auth_OpenID_AX_NS_URI', + 'http://openid.net/srv/ax/1.0'); + +// Use this as the 'count' value for an attribute in a FetchRequest to +// ask for as many values as the OP can provide. +define('Auth_OpenID_AX_UNLIMITED_VALUES', 'unlimited'); + +// Minimum supported alias length in characters. Here for +// completeness. +define('Auth_OpenID_AX_MINIMUM_SUPPORTED_ALIAS_LENGTH', 32); + +/** + * AX utility class. + * + * @package OpenID + */ +class Auth_OpenID_AX { + /** + * @param mixed $thing Any object which may be an + * Auth_OpenID_AX_Error object. + * + * @return bool true if $thing is an Auth_OpenID_AX_Error; false + * if not. + */ + function isError($thing) + { + return is_a($thing, 'Auth_OpenID_AX_Error'); + } +} + +/** + * Check an alias for invalid characters; raise AXError if any are + * found. Return None if the alias is valid. + */ +function Auth_OpenID_AX_checkAlias($alias) +{ + if (strpos($alias, ',') !== false) { + return new Auth_OpenID_AX_Error(sprintf( + "Alias %s must not contain comma", $alias)); + } + if (strpos($alias, '.') !== false) { + return new Auth_OpenID_AX_Error(sprintf( + "Alias %s must not contain period", $alias)); + } + + return true; +} + +/** + * Results from data that does not meet the attribute exchange 1.0 + * specification + * + * @package OpenID + */ +class Auth_OpenID_AX_Error { + function Auth_OpenID_AX_Error($message=null) + { + $this->message = $message; + } +} + +/** + * Abstract class containing common code for attribute exchange + * messages. + * + * @package OpenID + */ +class Auth_OpenID_AX_Message extends Auth_OpenID_Extension { + /** + * ns_alias: The preferred namespace alias for attribute exchange + * messages + */ + var $ns_alias = 'ax'; + + /** + * mode: The type of this attribute exchange message. This must be + * overridden in subclasses. + */ + var $mode = null; + + var $ns_uri = Auth_OpenID_AX_NS_URI; + + /** + * Return Auth_OpenID_AX_Error if the mode in the attribute + * exchange arguments does not match what is expected for this + * class; true otherwise. + * + * @access private + */ + function _checkMode($ax_args) + { + $mode = Auth_OpenID::arrayGet($ax_args, 'mode'); + if ($mode != $this->mode) { + return new Auth_OpenID_AX_Error( + sprintf( + "Expected mode '%s'; got '%s'", + $this->mode, $mode)); + } + + return true; + } + + /** + * Return a set of attribute exchange arguments containing the + * basic information that must be in every attribute exchange + * message. + * + * @access private + */ + function _newArgs() + { + return array('mode' => $this->mode); + } +} + +/** + * Represents a single attribute in an attribute exchange + * request. This should be added to an AXRequest object in order to + * request the attribute. + * + * @package OpenID + */ +class Auth_OpenID_AX_AttrInfo { + /** + * Construct an attribute information object. Do not call this + * directly; call make(...) instead. + * + * @param string $type_uri The type URI for this attribute. + * + * @param int $count The number of values of this type to request. + * + * @param bool $required Whether the attribute will be marked as + * required in the request. + * + * @param string $alias The name that should be given to this + * attribute in the request. + */ + function Auth_OpenID_AX_AttrInfo($type_uri, $count, $required, + $alias) + { + /** + * required: Whether the attribute will be marked as required + * when presented to the subject of the attribute exchange + * request. + */ + $this->required = $required; + + /** + * count: How many values of this type to request from the + * subject. Defaults to one. + */ + $this->count = $count; + + /** + * type_uri: The identifier that determines what the attribute + * represents and how it is serialized. For example, one type + * URI representing dates could represent a Unix timestamp in + * base 10 and another could represent a human-readable + * string. + */ + $this->type_uri = $type_uri; + + /** + * alias: The name that should be given to this attribute in + * the request. If it is not supplied, a generic name will be + * assigned. For example, if you want to call a Unix timestamp + * value 'tstamp', set its alias to that value. If two + * attributes in the same message request to use the same + * alias, the request will fail to be generated. + */ + $this->alias = $alias; + } + + /** + * Construct an attribute information object. For parameter + * details, see the constructor. + */ + function make($type_uri, $count=1, $required=false, + $alias=null) + { + if ($alias !== null) { + $result = Auth_OpenID_AX_checkAlias($alias); + + if (Auth_OpenID_AX::isError($result)) { + return $result; + } + } + + return new Auth_OpenID_AX_AttrInfo($type_uri, $count, $required, + $alias); + } + + /** + * When processing a request for this attribute, the OP should + * call this method to determine whether all available attribute + * values were requested. If self.count == UNLIMITED_VALUES, this + * returns True. Otherwise this returns False, in which case + * self.count is an integer. + */ + function wantsUnlimitedValues() + { + return $this->count === Auth_OpenID_AX_UNLIMITED_VALUES; + } +} + +/** + * Given a namespace mapping and a string containing a comma-separated + * list of namespace aliases, return a list of type URIs that + * correspond to those aliases. + * + * @param $namespace_map The mapping from namespace URI to alias + * @param $alias_list_s The string containing the comma-separated + * list of aliases. May also be None for convenience. + * + * @return $seq The list of namespace URIs that corresponds to the + * supplied list of aliases. If the string was zero-length or None, an + * empty list will be returned. + * + * return null If an alias is present in the list of aliases but + * is not present in the namespace map. + */ +function Auth_OpenID_AX_toTypeURIs(&$namespace_map, $alias_list_s) +{ + $uris = array(); + + if ($alias_list_s) { + foreach (explode(',', $alias_list_s) as $alias) { + $type_uri = $namespace_map->getNamespaceURI($alias); + if ($type_uri === null) { + // raise KeyError( + // 'No type is defined for attribute name %r' % (alias,)) + return new Auth_OpenID_AX_Error( + sprintf('No type is defined for attribute name %s', + $alias) + ); + } else { + $uris[] = $type_uri; + } + } + } + + return $uris; +} + +/** + * An attribute exchange 'fetch_request' message. This message is sent + * by a relying party when it wishes to obtain attributes about the + * subject of an OpenID authentication request. + * + * @package OpenID + */ +class Auth_OpenID_AX_FetchRequest extends Auth_OpenID_AX_Message { + + var $mode = 'fetch_request'; + + function Auth_OpenID_AX_FetchRequest($update_url=null) + { + /** + * requested_attributes: The attributes that have been + * requested thus far, indexed by the type URI. + */ + $this->requested_attributes = array(); + + /** + * update_url: A URL that will accept responses for this + * attribute exchange request, even in the absence of the user + * who made this request. + */ + $this->update_url = $update_url; + } + + /** + * Add an attribute to this attribute exchange request. + * + * @param attribute: The attribute that is being requested + * @return true on success, false when the requested attribute is + * already present in this fetch request. + */ + function add($attribute) + { + if ($this->contains($attribute->type_uri)) { + return new Auth_OpenID_AX_Error( + sprintf("The attribute %s has already been requested", + $attribute->type_uri)); + } + + $this->requested_attributes[$attribute->type_uri] = $attribute; + + return true; + } + + /** + * Get the serialized form of this attribute fetch request. + * + * @returns Auth_OpenID_AX_FetchRequest The fetch request message parameters + */ + function getExtensionArgs() + { + $aliases = new Auth_OpenID_NamespaceMap(); + + $required = array(); + $if_available = array(); + + $ax_args = $this->_newArgs(); + + foreach ($this->requested_attributes as $type_uri => $attribute) { + if ($attribute->alias === null) { + $alias = $aliases->add($type_uri); + } else { + $alias = $aliases->addAlias($type_uri, $attribute->alias); + + if ($alias === null) { + return new Auth_OpenID_AX_Error( + sprintf("Could not add alias %s for URI %s", + $attribute->alias, $type_uri + )); + } + } + + if ($attribute->required) { + $required[] = $alias; + } else { + $if_available[] = $alias; + } + + if ($attribute->count != 1) { + $ax_args['count.' . $alias] = strval($attribute->count); + } + + $ax_args['type.' . $alias] = $type_uri; + } + + if ($required) { + $ax_args['required'] = implode(',', $required); + } + + if ($if_available) { + $ax_args['if_available'] = implode(',', $if_available); + } + + return $ax_args; + } + + /** + * Get the type URIs for all attributes that have been marked as + * required. + * + * @return A list of the type URIs for attributes that have been + * marked as required. + */ + function getRequiredAttrs() + { + $required = array(); + foreach ($this->requested_attributes as $type_uri => $attribute) { + if ($attribute->required) { + $required[] = $type_uri; + } + } + + return $required; + } + + /** + * Extract a FetchRequest from an OpenID message + * + * @param request: The OpenID request containing the attribute + * fetch request + * + * @returns mixed An Auth_OpenID_AX_Error or the + * Auth_OpenID_AX_FetchRequest extracted from the request message if + * successful + */ + function &fromOpenIDRequest($request) + { + $m = $request->message; + $obj = new Auth_OpenID_AX_FetchRequest(); + $ax_args = $m->getArgs($obj->ns_uri); + + $result = $obj->parseExtensionArgs($ax_args); + + if (Auth_OpenID_AX::isError($result)) { + return $result; + } + + if ($obj->update_url) { + // Update URL must match the openid.realm of the + // underlying OpenID 2 message. + $realm = $m->getArg(Auth_OpenID_OPENID_NS, 'realm', + $m->getArg( + Auth_OpenID_OPENID_NS, + 'return_to')); + + if (!$realm) { + $obj = new Auth_OpenID_AX_Error( + sprintf("Cannot validate update_url %s " . + "against absent realm", $obj->update_url)); + } else if (!Auth_OpenID_TrustRoot::match($realm, + $obj->update_url)) { + $obj = new Auth_OpenID_AX_Error( + sprintf("Update URL %s failed validation against realm %s", + $obj->update_url, $realm)); + } + } + + return $obj; + } + + /** + * Given attribute exchange arguments, populate this FetchRequest. + * + * @return $result Auth_OpenID_AX_Error if the data to be parsed + * does not follow the attribute exchange specification. At least + * when 'if_available' or 'required' is not specified for a + * particular attribute type. Returns true otherwise. + */ + function parseExtensionArgs($ax_args) + { + $result = $this->_checkMode($ax_args); + if (Auth_OpenID_AX::isError($result)) { + return $result; + } + + $aliases = new Auth_OpenID_NamespaceMap(); + + foreach ($ax_args as $key => $value) { + if (strpos($key, 'type.') === 0) { + $alias = substr($key, 5); + $type_uri = $value; + + $alias = $aliases->addAlias($type_uri, $alias); + + if ($alias === null) { + return new Auth_OpenID_AX_Error( + sprintf("Could not add alias %s for URI %s", + $alias, $type_uri) + ); + } + + $count_s = Auth_OpenID::arrayGet($ax_args, 'count.' . $alias); + if ($count_s) { + $count = Auth_OpenID::intval($count_s); + if (($count === false) && + ($count_s === Auth_OpenID_AX_UNLIMITED_VALUES)) { + $count = $count_s; + } + } else { + $count = 1; + } + + if ($count === false) { + return new Auth_OpenID_AX_Error( + sprintf("Integer value expected for %s, got %s", + 'count.' . $alias, $count_s)); + } + + $attrinfo = Auth_OpenID_AX_AttrInfo::make($type_uri, $count, + false, $alias); + + if (Auth_OpenID_AX::isError($attrinfo)) { + return $attrinfo; + } + + $this->add($attrinfo); + } + } + + $required = Auth_OpenID_AX_toTypeURIs($aliases, + Auth_OpenID::arrayGet($ax_args, 'required')); + + foreach ($required as $type_uri) { + $attrib =& $this->requested_attributes[$type_uri]; + $attrib->required = true; + } + + $if_available = Auth_OpenID_AX_toTypeURIs($aliases, + Auth_OpenID::arrayGet($ax_args, 'if_available')); + + $all_type_uris = array_merge($required, $if_available); + + foreach ($aliases->iterNamespaceURIs() as $type_uri) { + if (!in_array($type_uri, $all_type_uris)) { + return new Auth_OpenID_AX_Error( + sprintf('Type URI %s was in the request but not ' . + 'present in "required" or "if_available"', + $type_uri)); + + } + } + + $this->update_url = Auth_OpenID::arrayGet($ax_args, 'update_url'); + + return true; + } + + /** + * Iterate over the AttrInfo objects that are contained in this + * fetch_request. + */ + function iterAttrs() + { + return array_values($this->requested_attributes); + } + + function iterTypes() + { + return array_keys($this->requested_attributes); + } + + /** + * Is the given type URI present in this fetch_request? + */ + function contains($type_uri) + { + return in_array($type_uri, $this->iterTypes()); + } +} + +/** + * An abstract class that implements a message that has attribute keys + * and values. It contains the common code between fetch_response and + * store_request. + * + * @package OpenID + */ +class Auth_OpenID_AX_KeyValueMessage extends Auth_OpenID_AX_Message { + + function Auth_OpenID_AX_KeyValueMessage() + { + $this->data = array(); + } + + /** + * Add a single value for the given attribute type to the + * message. If there are already values specified for this type, + * this value will be sent in addition to the values already + * specified. + * + * @param type_uri: The URI for the attribute + * @param value: The value to add to the response to the relying + * party for this attribute + * @return null + */ + function addValue($type_uri, $value) + { + if (!array_key_exists($type_uri, $this->data)) { + $this->data[$type_uri] = array(); + } + + $values =& $this->data[$type_uri]; + $values[] = $value; + } + + /** + * Set the values for the given attribute type. This replaces any + * values that have already been set for this attribute. + * + * @param type_uri: The URI for the attribute + * @param values: A list of values to send for this attribute. + */ + function setValues($type_uri, &$values) + { + $this->data[$type_uri] =& $values; + } + + /** + * Get the extension arguments for the key/value pairs contained + * in this message. + * + * @param aliases: An alias mapping. Set to None if you don't care + * about the aliases for this request. + * + * @access private + */ + function _getExtensionKVArgs(&$aliases) + { + if ($aliases === null) { + $aliases = new Auth_OpenID_NamespaceMap(); + } + + $ax_args = array(); + + foreach ($this->data as $type_uri => $values) { + $alias = $aliases->add($type_uri); + + $ax_args['type.' . $alias] = $type_uri; + $ax_args['count.' . $alias] = strval(count($values)); + + foreach ($values as $i => $value) { + $key = sprintf('value.%s.%d', $alias, $i + 1); + $ax_args[$key] = $value; + } + } + + return $ax_args; + } + + /** + * Parse attribute exchange key/value arguments into this object. + * + * @param ax_args: The attribute exchange fetch_response + * arguments, with namespacing removed. + * + * @return Auth_OpenID_AX_Error or true + */ + function parseExtensionArgs($ax_args) + { + $result = $this->_checkMode($ax_args); + if (Auth_OpenID_AX::isError($result)) { + return $result; + } + + $aliases = new Auth_OpenID_NamespaceMap(); + + foreach ($ax_args as $key => $value) { + if (strpos($key, 'type.') === 0) { + $type_uri = $value; + $alias = substr($key, 5); + + $result = Auth_OpenID_AX_checkAlias($alias); + + if (Auth_OpenID_AX::isError($result)) { + return $result; + } + + $alias = $aliases->addAlias($type_uri, $alias); + + if ($alias === null) { + return new Auth_OpenID_AX_Error( + sprintf("Could not add alias %s for URI %s", + $alias, $type_uri) + ); + } + } + } + + foreach ($aliases->iteritems() as $pair) { + list($type_uri, $alias) = $pair; + + if (array_key_exists('count.' . $alias, $ax_args)) { + + $count_key = 'count.' . $alias; + $count_s = $ax_args[$count_key]; + + $count = Auth_OpenID::intval($count_s); + + if ($count === false) { + return new Auth_OpenID_AX_Error( + sprintf("Integer value expected for %s, got %s", + 'count. %s' . $alias, $count_s, + Auth_OpenID_AX_UNLIMITED_VALUES) + ); + } + + $values = array(); + for ($i = 1; $i < $count + 1; $i++) { + $value_key = sprintf('value.%s.%d', $alias, $i); + + if (!array_key_exists($value_key, $ax_args)) { + return new Auth_OpenID_AX_Error( + sprintf( + "No value found for key %s", + $value_key)); + } + + $value = $ax_args[$value_key]; + $values[] = $value; + } + } else { + $key = 'value.' . $alias; + + if (!array_key_exists($key, $ax_args)) { + return new Auth_OpenID_AX_Error( + sprintf( + "No value found for key %s", + $key)); + } + + $value = $ax_args['value.' . $alias]; + + if ($value == '') { + $values = array(); + } else { + $values = array($value); + } + } + + $this->data[$type_uri] = $values; + } + + return true; + } + + /** + * Get a single value for an attribute. If no value was sent for + * this attribute, use the supplied default. If there is more than + * one value for this attribute, this method will fail. + * + * @param type_uri: The URI for the attribute + * @param default: The value to return if the attribute was not + * sent in the fetch_response. + * + * @return $value Auth_OpenID_AX_Error on failure or the value of + * the attribute in the fetch_response message, or the default + * supplied + */ + function getSingle($type_uri, $default=null) + { + $values = Auth_OpenID::arrayGet($this->data, $type_uri); + if (!$values) { + return $default; + } else if (count($values) == 1) { + return $values[0]; + } else { + return new Auth_OpenID_AX_Error( + sprintf('More than one value present for %s', + $type_uri) + ); + } + } + + /** + * Get the list of values for this attribute in the + * fetch_response. + * + * XXX: what to do if the values are not present? default + * parameter? this is funny because it's always supposed to return + * a list, so the default may break that, though it's provided by + * the user's code, so it might be okay. If no default is + * supplied, should the return be None or []? + * + * @param type_uri: The URI of the attribute + * + * @return $values The list of values for this attribute in the + * response. May be an empty list. If the attribute was not sent + * in the response, returns Auth_OpenID_AX_Error. + */ + function get($type_uri) + { + if (array_key_exists($type_uri, $this->data)) { + return $this->data[$type_uri]; + } else { + return new Auth_OpenID_AX_Error( + sprintf("Type URI %s not found in response", + $type_uri) + ); + } + } + + /** + * Get the number of responses for a particular attribute in this + * fetch_response message. + * + * @param type_uri: The URI of the attribute + * + * @returns int The number of values sent for this attribute. If + * the attribute was not sent in the response, returns + * Auth_OpenID_AX_Error. + */ + function count($type_uri) + { + if (array_key_exists($type_uri, $this->data)) { + return count($this->get($type_uri)); + } else { + return new Auth_OpenID_AX_Error( + sprintf("Type URI %s not found in response", + $type_uri) + ); + } + } +} + +/** + * A fetch_response attribute exchange message. + * + * @package OpenID + */ +class Auth_OpenID_AX_FetchResponse extends Auth_OpenID_AX_KeyValueMessage { + var $mode = 'fetch_response'; + + function Auth_OpenID_AX_FetchResponse($update_url=null) + { + $this->Auth_OpenID_AX_KeyValueMessage(); + $this->update_url = $update_url; + } + + /** + * Serialize this object into arguments in the attribute exchange + * namespace + * + * @return $args The dictionary of unqualified attribute exchange + * arguments that represent this fetch_response, or + * Auth_OpenID_AX_Error on error. + */ + function getExtensionArgs($request=null) + { + $aliases = new Auth_OpenID_NamespaceMap(); + + $zero_value_types = array(); + + if ($request !== null) { + // Validate the data in the context of the request (the + // same attributes should be present in each, and the + // counts in the response must be no more than the counts + // in the request) + + foreach ($this->data as $type_uri => $unused) { + if (!$request->contains($type_uri)) { + return new Auth_OpenID_AX_Error( + sprintf("Response attribute not present in request: %s", + $type_uri) + ); + } + } + + foreach ($request->iterAttrs() as $attr_info) { + // Copy the aliases from the request so that reading + // the response in light of the request is easier + if ($attr_info->alias === null) { + $aliases->add($attr_info->type_uri); + } else { + $alias = $aliases->addAlias($attr_info->type_uri, + $attr_info->alias); + + if ($alias === null) { + return new Auth_OpenID_AX_Error( + sprintf("Could not add alias %s for URI %s", + $attr_info->alias, $attr_info->type_uri) + ); + } + } + + if (array_key_exists($attr_info->type_uri, $this->data)) { + $values = $this->data[$attr_info->type_uri]; + } else { + $values = array(); + $zero_value_types[] = $attr_info; + } + + if (($attr_info->count != Auth_OpenID_AX_UNLIMITED_VALUES) && + ($attr_info->count < count($values))) { + return new Auth_OpenID_AX_Error( + sprintf("More than the number of requested values " . + "were specified for %s", + $attr_info->type_uri) + ); + } + } + } + + $kv_args = $this->_getExtensionKVArgs($aliases); + + // Add the KV args into the response with the args that are + // unique to the fetch_response + $ax_args = $this->_newArgs(); + + // For each requested attribute, put its type/alias and count + // into the response even if no data were returned. + foreach ($zero_value_types as $attr_info) { + $alias = $aliases->getAlias($attr_info->type_uri); + $kv_args['type.' . $alias] = $attr_info->type_uri; + $kv_args['count.' . $alias] = '0'; + } + + $update_url = null; + if ($request) { + $update_url = $request->update_url; + } else { + $update_url = $this->update_url; + } + + if ($update_url) { + $ax_args['update_url'] = $update_url; + } + + Auth_OpenID::update(&$ax_args, $kv_args); + + return $ax_args; + } + + /** + * @return $result Auth_OpenID_AX_Error on failure or true on + * success. + */ + function parseExtensionArgs($ax_args) + { + $result = parent::parseExtensionArgs($ax_args); + + if (Auth_OpenID_AX::isError($result)) { + return $result; + } + + $this->update_url = Auth_OpenID::arrayGet($ax_args, 'update_url'); + + return true; + } + + /** + * Construct a FetchResponse object from an OpenID library + * SuccessResponse object. + * + * @param success_response: A successful id_res response object + * + * @param signed: Whether non-signed args should be processsed. If + * True (the default), only signed arguments will be processsed. + * + * @return $response A FetchResponse containing the data from the + * OpenID message + */ + function fromSuccessResponse($success_response, $signed=true) + { + $obj = new Auth_OpenID_AX_FetchResponse(); + if ($signed) { + $ax_args = $success_response->getSignedNS($obj->ns_uri); + } else { + $ax_args = $success_response->message->getArgs($obj->ns_uri); + } + if ($ax_args === null || Auth_OpenID::isFailure($ax_args) || + sizeof($ax_args) == 0) { + return null; + } + + $result = $obj->parseExtensionArgs($ax_args); + if (Auth_OpenID_AX::isError($result)) { + #XXX log me + return null; + } + return $obj; + } +} + +/** + * A store request attribute exchange message representation. + * + * @package OpenID + */ +class Auth_OpenID_AX_StoreRequest extends Auth_OpenID_AX_KeyValueMessage { + var $mode = 'store_request'; + + /** + * @param array $aliases The namespace aliases to use when making + * this store response. Leave as None to use defaults. + */ + function getExtensionArgs($aliases=null) + { + $ax_args = $this->_newArgs(); + $kv_args = $this->_getExtensionKVArgs($aliases); + Auth_OpenID::update(&$ax_args, $kv_args); + return $ax_args; + } +} + +/** + * An indication that the store request was processed along with this + * OpenID transaction. Use make(), NOT the constructor, to create + * response objects. + * + * @package OpenID + */ +class Auth_OpenID_AX_StoreResponse extends Auth_OpenID_AX_Message { + var $SUCCESS_MODE = 'store_response_success'; + var $FAILURE_MODE = 'store_response_failure'; + + /** + * Returns Auth_OpenID_AX_Error on error or an + * Auth_OpenID_AX_StoreResponse object on success. + */ + function &make($succeeded=true, $error_message=null) + { + if (($succeeded) && ($error_message !== null)) { + return new Auth_OpenID_AX_Error('An error message may only be '. + 'included in a failing fetch response'); + } + + return new Auth_OpenID_AX_StoreResponse($succeeded, $error_message); + } + + function Auth_OpenID_AX_StoreResponse($succeeded=true, $error_message=null) + { + if ($succeeded) { + $this->mode = $this->SUCCESS_MODE; + } else { + $this->mode = $this->FAILURE_MODE; + } + + $this->error_message = $error_message; + } + + /** + * Was this response a success response? + */ + function succeeded() + { + return $this->mode == $this->SUCCESS_MODE; + } + + function getExtensionArgs() + { + $ax_args = $this->_newArgs(); + if ((!$this->succeeded()) && $this->error_message) { + $ax_args['error'] = $this->error_message; + } + + return $ax_args; + } +} + +?> \ No newline at end of file Modified: branches/1.10/lib/pear/Auth/OpenID/Association.php =================================================================== --- branches/1.10/lib/pear/Auth/OpenID/Association.php 2008-06-19 20:03:30 UTC (rev 13301) +++ branches/1.10/lib/pear/Auth/OpenID/Association.php 2008-06-19 21:49:50 UTC (rev 13302) @@ -10,8 +10,8 @@ * * @package OpenID * @author JanRain, Inc. <op...@ja...> - * @copyright 2005 Janrain, Inc. - * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @copyright 2005-2008 Janrain, Inc. + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache */ /** @@ -64,6 +64,11 @@ 'assoc_type' ); + var $_macs = array( + 'HMAC-SHA1' => 'Auth_OpenID_HMACSHA1', + 'HMAC-SHA256' => 'Auth_OpenID_HMACSHA256' + ); + /** * This is an alternate constructor (factory method) used by the * OpenID consumer library to create associations. OpenID store @@ -82,9 +87,9 @@ * generated for this association. * * @param assoc_type This is the type of association this - * instance represents. The only valid value of this field at - * this time is 'HMAC-SHA1', but new types may be defined in the - * future. + * instance represents. The only valid values of this field at + * this time is 'HMAC-SHA1' and 'HMAC-SHA256', but new types may + * be defined in the future. * * @return association An {@link Auth_OpenID_Association} * instance. @@ -119,9 +124,9 @@ * association was issued. * * @param string $assoc_type This is the type of association this - * instance represents. The only valid value of this field at - * this time is 'HMAC-SHA1', but new types may be defined in the - * future. + * instance represents. The only valid values of this field at + * this time is 'HMAC-SHA1' and 'HMAC-SHA256', but new types may + * be defined in the future. */ function Auth_OpenID_Association( $handle, $secret, $issued, $lifetime, $assoc_type) @@ -258,7 +263,11 @@ function sign($pairs) { $kv = Auth_OpenID_KVForm::fromArray($pairs); - return Auth_OpenID_HMACSHA1($this->secret, $kv); + + /* Invalid association types should be caught at constructor */ + $callback = $this->_macs[$this->assoc_type]; + + return call_user_func_array($callback, array($this->secret, $kv)); } /** @@ -321,7 +330,7 @@ function _makePairs(&$message) { $signed = $message->getArg(Auth_OpenID_OPENID_NS, 'signed'); - if (!$signed) { + if (!$signed || Auth_OpenID::isFailure($signed)) { // raise ValueError('Message has no signed list: %s' % (message,)) return null; } @@ -360,7 +369,7 @@ $sig = $message->getArg(Auth_OpenID_OPENID_NS, 'sig'); - if (!$sig) { + if (!$sig || Auth_OpenID::isFailure($sig)) { return false; } @@ -423,7 +432,7 @@ { $order = array(); - if (!defined('Auth_OpenID_NO_MATH_SUPPORT')) { + if (!Auth_OpenID_noMathSupport()) { $order[] = array('HMAC-SHA1', 'DH-SHA1'); if (Auth_OpenID_HMACSHA256_SUPPORTED) { @@ -518,7 +527,8 @@ class Auth_OpenID_SessionNegotiator { function Auth_OpenID_SessionNegotiator($allowed_types) { - $this->allowed_types = $allowed_types; + $this->allowed_types = array(); + $this->setAllowedTypes($allowed_types); } /** Modified: branches/1.10/lib/pear/Auth/OpenID/BigMath.php =================================================================== --- branches/1.10/lib/pear/Auth/OpenID/BigMath.php 2008-06-19 20:03:30 UTC (rev 13301) +++ branches/1.10/lib/pear/Auth/OpenID/BigMath.php 2008-06-19 21:49:50 UTC (rev 13302) @@ -11,8 +11,8 @@ * @access private * @package OpenID * @author JanRain, Inc. <op...@ja...> - * @copyright 2005 Janrain, Inc. - * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @copyright 2005-2008 Janrain, Inc. + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache */ /** @@ -427,7 +427,7 @@ return $lib; } - if (defined('Auth_OpenID_NO_MATH_SUPPORT')) { + if (Auth_OpenID_noMathSupport()) { $null = null; return $null; } @@ -443,8 +443,10 @@ } $triedstr = implode(", ", $tried); - define('Auth_OpenID_NO_MATH_SUPPORT', true); - return null; + Auth_OpenID_setNoMathSupport(); + + $result = null; + return $result; } // Instantiate a new wrapper @@ -454,4 +456,16 @@ return $lib; } +function Auth_OpenID_setNoMathSupport() +{ + if (!defined('Auth_OpenID_NO_MATH_SUPPORT')) { + define('Auth_OpenID_NO_MATH_SUPPORT', true); + } +} + +function Auth_OpenID_noMathSupport() +{ + return defined('Auth_OpenID_NO_MATH_SUPPORT'); +} + ?> Modified: branches/1.10/lib/pear/Auth/OpenID/Consumer.php =================================================================== --- branches/1.10/lib/pear/Auth/OpenID/Consumer.php 2008-06-19 20:03:30 UTC (rev 13301) +++ branches/1.10/lib/pear/Auth/OpenID/Consumer.php 2008-06-19 21:49:50 UTC (rev 13302) @@ -113,8 +113,8 @@ * class using the store of choice (Auth_OpenID_FileStore or one of * the SQL-based stores). If the application has a custom * session-management implementation, an object implementing the - * {@link Auth_Yadis_Session} interface should be passed as the second - * parameter. Otherwise, the default uses $_SESSION. + * {@link Auth_Yadis_PHPSession} interface should be passed as the + * second parameter. Otherwise, the default uses $_SESSION. * * Next, the application should call the Auth_OpenID_Consumer object's * 'begin' method. This method takes the OpenID URL. The 'begin' @@ -153,8 +153,8 @@ * * @package OpenID * @author JanRain, Inc. <op...@ja...> - * @copyright 2005 Janrain, Inc. - * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @copyright 2005-2008 Janrain, Inc. + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache */ /** @@ -169,6 +169,7 @@ require_once "Auth/OpenID/KVForm.php"; require_once "Auth/OpenID/Nonce.php"; require_once "Auth/OpenID/Discover.php"; +require_once "Auth/OpenID/URINorm.php"; require_once "Auth/Yadis/Manager.php"; require_once "Auth/Yadis/XRI.php"; @@ -246,7 +247,7 @@ * Auth_OpenID_DumbStore} can be used. * * @param mixed $session An object which implements the interface - * of the {@link Auth_Yadis_Session} class. Particularly, this + * of the {@link Auth_Yadis_PHPSession} class. Particularly, this * object is expected to have these methods: get($key), set($key), * $value), and del($key). This defaults to a session object * which wraps PHP's native session machinery. You should only @@ -388,6 +389,13 @@ * request. It is called in step 4 of the flow described in the * consumer overview. * + * @param string $current_url The URL used to invoke the application. + * Extract the URL from your application's web + * request framework and specify it here to have it checked + * against the openid.current_url value in the response. If + * the current_url URL check fails, the status of the + * completion will be FAILURE. + * * @param array $query An array of the query parameters (key => * value pairs) for this HTTP request. Defaults to null. If * null, the GET or POST data are automatically gotten from the @@ -399,8 +407,16 @@ * indicated by the status attribute, which will be one of * SUCCESS, CANCEL, FAILURE, or SETUP_NEEDED. */ - function complete($query=null) + function complete($current_url, $query=null) { + if ($current_url && !is_string($current_url)) { + // This is ugly, but we need to complain loudly when + // someone uses the API incorrectly. + trigger_error("current_url must be a string; see NEWS file " . + "for upgrading notes.", + E_USER_ERROR); + } + if ($query === null) { $query = Auth_OpenID::getQuery(); } @@ -410,14 +426,10 @@ $endpoint = $loader->fromSession($endpoint_data); - if ($endpoint === null) { - $response = new Auth_OpenID_FailureResponse(null, - 'No session state found'); - } else { - $message = Auth_OpenID_Message::fromPostArgs($query); - $response = $this->consumer->complete($message, $endpoint); - $this->session->del($this->_token_key); - } + $message = Auth_OpenID_Message::fromPostArgs($query); + $response = $this->consumer->complete($message, $endpoint, + $current_url); + $this->session->del($this->_token_key); if (in_array($response->status, array(Auth_OpenID_SUCCESS, Auth_OpenID_CANCEL))) { @@ -425,7 +437,7 @@ $disco = $this->getDiscoveryObject($this->session, $response->identity_url, $this->session_key_prefix); - $disco->cleanup(); + $disco->cleanup(true); } } @@ -515,7 +527,7 @@ */ class Auth_OpenID_PlainTextConsumerSession { var $session_type = 'no-encryption'; - var $allowed_assoc_types = array('HMAC-SHA1'); + var $allowed_assoc_types = array('HMAC-SHA1', 'HMAC-SHA256'); function getRequest() { @@ -575,6 +587,13 @@ var $openid1_nonce_query_arg_name = 'janrain_nonce'; /** + * Another query parameter that gets added to the return_to for + * OpenID 1; if the user's session state is lost, use this claimed + * identifier to do discovery when verifying the response. + */ + var $openid1_return_to_identifier_name = 'openid1_claimed_id'; + + /** * This method initializes a new {@link Auth_OpenID_Consumer} * instance to access the library. * @@ -615,6 +634,12 @@ $r = new Auth_OpenID_AuthRequest($service_endpoint, $assoc); $r->return_to_args[$this->openid1_nonce_query_arg_name] = Auth_OpenID_mkNonce(); + + if ($r->message->isOpenID1()) { + $r->return_to_args[$this->openid1_return_to_identifier_name] = + $r->endpoint->claimed_id; + } + return $r; } @@ -625,41 +650,82 @@ * * @access private */ - function complete($message, $endpoint, $return_to = null) + function complete($message, $endpoint, $return_to) { $mode = $message->getArg(Auth_OpenID_OPENID_NS, 'mode', '<no mode set>'); - if ($return_to !== null) { - if (!$this->_checkReturnTo($message, $return_to)) { - return new Auth_OpenID_FailureResponse($endpoint, - "openid.return_to does not match return URL"); - } + $mode_methods = array( + 'cancel' => '_complete_cancel', + 'error' => '_complete_error', + 'setup_needed' => '_complete_setup_needed', + 'id_res' => '_complete_id_res', + ); + + $method = Auth_OpenID::arrayGet($mode_methods, $mode, + '_completeInvalid'); + + return call_user_func_array(array(&$this, $method), + array($message, $endpoint, $return_to)); + } + + /** + * @access private + */ + function _completeInvalid($message, &$endpoint, $unused) + { + $mode = $message->getArg(Auth_OpenID_OPENID_NS, 'mode', + '<No mode set>'); + + return new Auth_OpenID_FailureResponse($endpoint, + sprintf("Invalid openid.mode '%s'", $mode)); + } + + /** + * @access private + */ + function _complete_cancel($message, &$endpoint, $unused) + { + return new Auth_OpenID_CancelResponse($endpoint); + } + + /** + * @access private + */ + function _complete_error($message, &$endpoint, $unused) + { + $error = $message->getArg(Auth_OpenID_OPENID_NS, 'error'); + $contact = $message->getArg(Auth_OpenID_OPENID_NS, 'contact'); + $reference = $message->getArg(Auth_OpenID_OPENID_NS, 'reference'); + + return new Auth_OpenID_FailureResponse($endpoint, $error, + $contact, $reference); + } + + /** + * @access private + */ + function _complete_setup_needed($message, &$endpoint, $unused) + { + if (!$message->isOpenID2()) { + return $this->_completeInvalid($message, $endpoint); } - if ($mode == 'cancel') { - return new Auth_OpenID_CancelResponse($endpoint); - } else if ($mode == 'error') { - $error = $message->getArg(Auth_OpenID_OPENID_NS, 'error'); - $contact = $message->getArg(Auth_OpenID_OPENID_NS, 'contact'); - $reference = $message->getArg(Auth_OpenID_OPENID_NS, 'reference'); + return new Auth_OpenID_SetupNeededResponse($endpoint); + } - return new Auth_OpenID_FailureResponse($endpoint, $error, - $contact, $reference); - } else if ($message->isOpenID2() && ($mode == 'setup_needed')) { - return new Auth_OpenID_SetupNeededResponse($endpoint); + /** + * @access private + */ + function _complete_id_res($message, &$endpoint, $return_to) + { + $user_setup_url = $message->getArg(Auth_OpenID_OPENID1_NS, + 'user_setup_url'); - } else if ($mode == 'id_res') { - if ($this->_checkSetupNeeded($message)) { - return SetupNeededResponse($endpoint, - $result->user_setup_url); - } else { - return $this->_doIdRes($message, $endpoint); - } + if ($this->_checkSetupNeeded($message)) { + return SetupNeededResponse($endpoint, $user_setup_url); } else { - return new Auth_OpenID_FailureResponse($endpoint, - sprintf("Invalid openid.mode '%s'", - $mode)); + return $this->_doIdRes($message, $endpoint, $return_to); } } @@ -685,26 +751,23 @@ /** * @access private */ - function _doIdRes($message, $endpoint) + function _doIdRes($message, $endpoint, $return_to) { - $signed_list_str = $message->getArg(Auth_OpenID_OPENID_NS, - 'signed'); - - if ($signed_list_str === null) { - return new Auth_OpenID_FailureResponse($endpoint, - "Response missing signed list"); - } - - $signed_list = explode(',', $signed_list_str); - // Checks for presence of appropriate fields (and checks // signed list fields) - $result = $this->_idResCheckForFields($message, $signed_list); + $result = $this->_idResCheckForFields($message); if (Auth_OpenID::isFailure($result)) { return $result; } + if (!$this->_checkReturnTo($message, $return_to)) { + return new Auth_OpenID_FailureResponse(null, + sprintf("return_to does not match return URL. Expected %s, got %s", + $return_to, + $message->getArg(Auth_OpenID_OPENID_NS, 'return_to'))); + } + // Verify discovery information: $result = $this->_verifyDiscoveryResults($message, $endpoint); @@ -721,15 +784,19 @@ return $result; } - $response_identity = $message->getArg(Auth_OpenID_OPENID_NS, - 'identity'); - $result = $this->_idResCheckNonce($message, $endpoint); if (Auth_OpenID::isFailure($result)) { return $result; } + $signed_list_str = $message->getArg(Auth_OpenID_OPENID_NS, 'signed', + Auth_OpenID_NO_DEFAULT); + if (Auth_OpenID::isFailure($signed_list_str)) { + return $signed_list_str; + } + $signed_list = explode(',', $signed_list_str); + $signed_fields = Auth_OpenID::addPrefix($signed_list, "openid."); return new Auth_OpenID_SuccessResponse($endpoint, $message, @@ -758,9 +825,13 @@ // message. $msg_return_to = $message->getArg(Auth_OpenID_OPENID_NS, 'return_to'); + if (Auth_OpenID::isFailure($return_to)) { + // XXX log me + return false; + } - $return_to_parts = parse_url($return_to); - $msg_return_to_parts = parse_url($msg_return_to); + $return_to_parts = parse_url(Auth_OpenID_urinorm($return_to)); + $msg_return_to_parts = parse_url(Auth_OpenID_urinorm($msg_return_to)); // If port is absent from both, add it so it's equal in the // check below. @@ -811,10 +882,13 @@ $message = Auth_OpenID_Message::fromPostArgs($query); $return_to = $message->getArg(Auth_OpenID_OPENID_NS, 'return_to'); + if (Auth_OpenID::isFailure($return_to)) { + return $return_to; + } // XXX: this should be checked by _idResCheckForFields if (!$return_to) { return new Auth_OpenID_FailureResponse(null, - "no openid.return_to in query"); + "Response has no return_to"); } $parsed_url = parse_url($return_to); @@ -840,6 +914,17 @@ } } + // Make sure all non-OpenID arguments in the response are also + // in the signed return_to. + $bare_args = $message->getArgs(Auth_OpenID_BARE_NS); + foreach ($bare_args as $key => $value) { + if (Auth_OpenID::arrayGet($q, $key) != $value) { + return new Auth_OpenID_FailureResponse(null, + sprintf("Parameter %s = %s not in return_to URL", + $key, $value)); + } + } + return true; } @@ -850,6 +935,9 @@ { $assoc_handle = $message->getArg(Auth_OpenID_OPENID_NS, 'assoc_handle'); + if (Auth_OpenID::isFailure($assoc_handle)) { + return $assoc_handle; + } $assoc = $this->store->getAssociation($server_url, $assoc_handle); @@ -902,12 +990,17 @@ */ function _verifyDiscoveryResultsOpenID1($message, $endpoint) { - if ($endpoint === null) { + $claimed_id = $message->getArg(Auth_OpenID_BARE_NS, + $this->openid1_return_to_identifier_name); + + if (($endpoint === null) && ($claimed_id === null)) { return new Auth_OpenID_FailureResponse($endpoint, 'When using OpenID 1, the claimed ID must be supplied, ' . 'either by passing it through as a return_to parameter ' . 'or by using a session, and supplied to the GenericConsumer ' . 'as the argument to complete()'); + } else if (($endpoint !== null) && ($claimed_id === null)) { + $claimed_id = $endpoint->claimed_id; } $to_match = new Auth_OpenID_ServiceEndpoint(); @@ -916,7 +1009,7 @@ 'identity'); // Restore delegate information from the initiation phase - $to_match->claimed_id = $endpoint->claimed_id; + $to_match->claimed_id = $claimed_id; if ($to_match->local_id === null) { return new Auth_OpenID_FailureResponse($endpoint, @@ -926,17 +1019,27 @@ $to_match_1_0 = $to_match->copy(); $to_match_1_0->type_uris = array(Auth_OpenID_TYPE_1_0); - $result = $this->_verifyDiscoverySingle($endpoint, $to_match); + if ($endpoint !== null) { + $result = $this->_verifyDiscoverySingle($endpoint, $to_match); - if (is_a($result, 'Auth_OpenID_TypeURIMismatch')) { - $result = $this->_verifyDiscoverySingle($endpoint, $to_match_1_0); + if (is_a($result, 'Auth_OpenID_TypeURIMismatch')) { + $result = $this->_verifyDiscoverySingle($endpoint, + $to_match_1_0); + } + + if (Auth_OpenID::isFailure($result)) { + // oidutil.log("Error attempting to use stored + // discovery information: " + str(e)) + // oidutil.log("Attempting discovery to + // verify endpoint") + } else { + return $endpoint; + } } - if (Auth_OpenID::isFailure($result)) { - return $result; - } else { - return $endpoint; - } + // Endpoint is either bad (failed verification) or None + return $this->_discoverAndVerify($to_match->claimed_id, + array($to_match, $to_match_1_0)); } /** @@ -953,10 +1056,16 @@ } } - if ($to_match->claimed_id != $endpoint->claimed_id) { + // Fragments do not influence discovery, so we can't compare a + // claimed identifier with a fragment to discovered + // information. + list($defragged_claimed_id, $_) = + Auth_OpenID::urldefrag($to_match->claimed_id); + + if ($defragged_claimed_id != $endpoint->claimed_id) { return new Auth_OpenID_FailureResponse($endpoint, sprintf('Claimed ID does not match (different subjects!), ' . - 'Expected %s, got %s', $to_match->claimed_id, + 'Expected %s, got %s', $defragged_claimed_id, $endpoint->claimed_id)); } @@ -1012,111 +1121,117 @@ ($to_match->local_id !== null)) { return new Auth_OpenID_FailureResponse($endpoint, 'openid.identity is present without openid.claimed_id'); - } else if (($to_match->claimed_id !== null) && - ($to_match->local_id === null)) { + } + + if (($to_match->claimed_id !== null) && + ($to_match->local_id === null)) { return new Auth_OpenID_FailureResponse($endpoint, 'openid.claimed_id is present without openid.identity'); - } else if ($to_match->claimed_id === null) { + } + + if ($to_match->claimed_id === null) { // This is a response without identifiers, so there's // really no checking that we can do, so return an // endpoint that's for the specified `openid.op_endpoint' return Auth_OpenID_ServiceEndpoint::fromOPEndpointURL( $to_match->server_url); - } else if (!$endpoint) { + } + + if (!$endpoint) { // The claimed ID doesn't match, so we have to do // discovery again. This covers not using sessions, OP // identifier endpoints and responses that didn't match // the original request. // oidutil.log('No pre-discovered information supplied.') - return $this->_discoverAndVerify($to_match); - } else if ($to_match->claimed_id != $endpoint->claimed_id) { - // oidutil.log('Mismatched pre-discovered session data. ' - // 'Claimed ID in session=%s, in assertion=%s' % - // (endpoint.claimed_id, to_match.claimed_id)) - return $this->_discoverAndVerify($to_match); + return $this->_discoverAndVerify($to_match->claimed_id, + array($to_match)); } else { + // The claimed ID matches, so we use the endpoint that we // discovered in initiation. This should be the most // common case. $result = $this->_verifyDiscoverySingle($endpoint, $to_match); if (Auth_OpenID::isFailure($result)) { - return $result; + $endpoint = $this->_discoverAndVerify($to_match->claimed_id, + array($to_match)); + if (Auth_OpenID::isFailure($endpoint)) { + return $endpoint; + } } + } - return $endpoint; + // The endpoint we return should have the claimed ID from the + // message we just verified, fragment and all. + if ($endpoint->claimed_id != $to_match->claimed_id) { + $endpoint->claimed_id = $to_match->claimed_id; } - // Never reached. + return $endpoint; } /** * @access private */ - function _discoverAndVerify($to_match) + function _discoverAndVerify($claimed_id, $to_match_endpoints) { - // oidutil.log('Performing discovery on %s' % (to_match.claimed_id,)) + // oidutil.log('P... [truncated message content] |