$value) { $this->set($key, $value); } } } /** * Returns true if $thing is an Auth_OpenID_Mapping object; false * if not. * * @param Auth_OpenID_Mapping $thing * @return bool */ static function isA($thing) { return (is_object($thing) && strtolower(get_class($thing)) == 'auth_openid_mapping'); } /** * Returns an array of the keys in the mapping. */ function keys() { return $this->keys; } /** * Returns an array of values in the mapping. */ function values() { return $this->values; } /** * Returns an array of (key, value) pairs in the mapping. */ function items() { $temp = []; for ($i = 0; $i < count($this->keys); $i++) { $temp[] = [ $this->keys[$i], $this->values[$i] ]; } return $temp; } /** * Returns the "length" of the mapping, or the number of keys. */ function len() { return count($this->keys); } /** * Sets a key-value pair in the mapping. If the key already * exists, its value is replaced with the new value. * * @param string|array $key * @param mixed $value */ function set($key, $value) { $index = array_search($key, $this->keys); if ($index !== false) { $this->values[$index] = $value; } else { $this->keys[] = $key; $this->values[] = $value; } } /** * Gets a specified value from the mapping, associated with the * specified key. If the key does not exist in the mapping, * $default is returned instead. * * @param string|array $key * @param mixed $default * @return mixed|null */ function get($key, $default = null) { $index = array_search($key, $this->keys); if ($index !== false) { return $this->values[$index]; } else { return $default; } } /** * @access private */ function _reflow() { // PHP is broken yet again. Sort the arrays to remove the // hole in the numeric indexes that make up the array. $old_keys = $this->keys; $old_values = $this->values; $this->keys = []; $this->values = []; foreach ($old_keys as $k) { $this->keys[] = $k; } foreach ($old_values as $v) { $this->values[] = $v; } } /** * Deletes a key-value pair from the mapping with the specified * key. * * @param string|array $key * @return bool */ function del($key) { $index = array_search($key, $this->keys); if ($index !== false) { unset($this->keys[$index]); unset($this->values[$index]); $this->_reflow(); return true; } return false; } /** * Returns true if the specified value has a key in the mapping; * false if not. * * @param string|array $key * @return bool */ function contains($key) { return array_search($key, $this->keys) !== false; } } /** * Maintains a bijective map between namespace uris and aliases. * * @package OpenID */ class Auth_OpenID_NamespaceMap { /** * @var Auth_OpenID_Mapping */ private $alias_to_namespace; /** * @var Auth_OpenID_Mapping */ private $namespace_to_alias; /** * @var array */ private $implicit_namespaces = []; function __construct() { $this->alias_to_namespace = new Auth_OpenID_Mapping(); $this->namespace_to_alias = new Auth_OpenID_Mapping(); $this->implicit_namespaces = []; } function getAlias($namespace_uri) { return $this->namespace_to_alias->get($namespace_uri); } function getNamespaceURI($alias) { return $this->alias_to_namespace->get($alias); } function iterNamespaceURIs() { // Return an iterator over the namespace URIs return $this->namespace_to_alias->keys(); } function iterAliases() { // Return an iterator over the aliases""" return $this->alias_to_namespace->keys(); } function iteritems() { return $this->namespace_to_alias->items(); } function isImplicit($namespace_uri) { return in_array($namespace_uri, $this->implicit_namespaces); } function addAlias($namespace_uri, $desired_alias, $implicit=false) { // Add an alias from this namespace URI to the desired alias global $Auth_OpenID_OPENID_PROTOCOL_FIELDS; // Check that desired_alias is not an openid protocol field as // per the spec. if (in_array($desired_alias, $Auth_OpenID_OPENID_PROTOCOL_FIELDS)) { Auth_OpenID::log("\"%s\" is not an allowed namespace alias", $desired_alias); return null; } // Check that desired_alias does not contain a period as per // the spec. if (strpos($desired_alias, '.') !== false) { Auth_OpenID::log('"%s" must not contain a dot', $desired_alias); return null; } // Check that there is not a namespace already defined for the // desired alias $current_namespace_uri = $this->alias_to_namespace->get($desired_alias); if (($current_namespace_uri !== null) && ($current_namespace_uri != $namespace_uri)) { Auth_OpenID::log('Cannot map "%s" because previous mapping exists', $namespace_uri); return null; } // Check that there is not already a (different) alias for // this namespace URI $alias = $this->namespace_to_alias->get($namespace_uri); if (($alias !== null) && ($alias != $desired_alias)) { Auth_OpenID::log('Cannot map %s to alias %s. ' . 'It is already mapped to alias %s', $namespace_uri, $desired_alias, $alias); return null; } assert((Auth_OpenID_NULL_NAMESPACE === $desired_alias) || is_string($desired_alias)); $this->alias_to_namespace->set($desired_alias, $namespace_uri); $this->namespace_to_alias->set($namespace_uri, $desired_alias); if ($implicit) { array_push($this->implicit_namespaces, $namespace_uri); } return $desired_alias; } function add($namespace_uri) { // Add this namespace URI to the mapping, without caring what // alias it ends up with // See if this namespace is already mapped to an alias $alias = $this->namespace_to_alias->get($namespace_uri); if ($alias !== null) { return $alias; } // Fall back to generating a numerical alias $i = 0; while (1) { $alias = 'ext' . strval($i); if ($this->addAlias($namespace_uri, $alias) === null) { $i += 1; } else { return $alias; } } // Should NEVER be reached! return null; } function contains($namespace_uri) { return $this->isDefined($namespace_uri); } function isDefined($namespace_uri) { return $this->namespace_to_alias->contains($namespace_uri); } } /** * In the implementation of this object, null represents the global * namespace as well as a namespace with no key. * * @package OpenID */ class Auth_OpenID_Message { private $allowed_openid_namespaces = [ Auth_OpenID_OPENID1_NS, Auth_OpenID_THE_OTHER_OPENID1_NS, Auth_OpenID_OPENID2_NS ]; /** * @var Auth_OpenID_Mapping */ private $args; /** * @var Auth_OpenID_NamespaceMap */ public $namespaces; /** * @var null|string */ private $_openid_ns_uri = null; function __construct($openid_namespace = null) { $this->args = new Auth_OpenID_Mapping(); $this->namespaces = new Auth_OpenID_NamespaceMap(); if ($openid_namespace !== null) { $implicit = Auth_OpenID_isOpenID1($openid_namespace); $this->setOpenIDNamespace($openid_namespace, $implicit); } } function isOpenID1() { return Auth_OpenID_isOpenID1($this->getOpenIDNamespace()); } function isOpenID2() { return $this->getOpenIDNamespace() == Auth_OpenID_OPENID2_NS; } /** * @param array $args * @return Auth_OpenID_Message|null */ static function fromPostArgs($args) { // Construct a Message containing a set of POST arguments $obj = new Auth_OpenID_Message(); // Partition into "openid." args and bare args $openid_args = []; foreach ($args as $key => $value) { if (is_array($value)) { return null; } $parts = explode('.', $key, 2); $rest = ''; if (count($parts) == 2) { list($prefix, $rest) = $parts; } else { $prefix = null; } if ($prefix != 'openid') { $obj->args->set([Auth_OpenID_BARE_NS, $key], $value); } else { $openid_args[$rest] = $value; } } if ($obj->_fromOpenIDArgs($openid_args)) { return $obj; } else { return null; } } static function fromOpenIDArgs($openid_args) { // Takes an array. // Construct a Message from a parsed KVForm message $obj = new Auth_OpenID_Message(); if ($obj->_fromOpenIDArgs($openid_args)) { return $obj; } else { return null; } } /** * @access private * @param Auth_OpenID_Mapping|array $openid_args * @return bool */ function _fromOpenIDArgs($openid_args) { // Takes an Auth_OpenID_Mapping instance OR an array. if (!Auth_OpenID_Mapping::isA($openid_args)) { $openid_args = new Auth_OpenID_Mapping($openid_args); } $ns_args = []; // Resolve namespaces foreach ($openid_args->items() as $pair) { list($rest, $value) = $pair; $parts = explode('.', $rest, 2); if (count($parts) == 2) { list($ns_alias, $ns_key) = $parts; } else { $ns_alias = Auth_OpenID_NULL_NAMESPACE; $ns_key = $rest; } if ($ns_alias == 'ns') { if ($this->namespaces->addAlias($value, $ns_key) === null) { return false; } } else if (($ns_alias == Auth_OpenID_NULL_NAMESPACE) && ($ns_key == 'ns')) { // null namespace if ($this->setOpenIDNamespace($value, false) === false) { return false; } } else { $ns_args[] = [$ns_alias, $ns_key, $value]; } } if (!$this->getOpenIDNamespace()) { if ($this->setOpenIDNamespace(Auth_OpenID_OPENID1_NS, true) === false) { return false; } } // Actually put the pairs into the appropriate namespaces foreach ($ns_args as $triple) { list($ns_alias, $ns_key, $value) = $triple; $ns_uri = $this->namespaces->getNamespaceURI($ns_alias); if ($ns_uri === null) { $ns_uri = $this->_getDefaultNamespace($ns_alias); if ($ns_uri === null) { $ns_uri = Auth_OpenID_OPENID_NS; $ns_key = sprintf('%s.%s', $ns_alias, $ns_key); } else { $this->namespaces->addAlias($ns_uri, $ns_alias, true); } } $this->setArg($ns_uri, $ns_key, $value); } return true; } function _getDefaultNamespace($mystery_alias) { global $Auth_OpenID_registered_aliases; if ($this->isOpenID1()) { return @$Auth_OpenID_registered_aliases[$mystery_alias]; } return null; } function setOpenIDNamespace($openid_ns_uri, $implicit) { if (!in_array($openid_ns_uri, $this->allowed_openid_namespaces)) { Auth_OpenID::log('Invalid null namespace: "%s"', $openid_ns_uri); return false; } $succeeded = $this->namespaces->addAlias($openid_ns_uri, Auth_OpenID_NULL_NAMESPACE, $implicit); if ($succeeded === false) { return false; } $this->_openid_ns_uri = $openid_ns_uri; return true; } function getOpenIDNamespace() { return $this->_openid_ns_uri; } static function fromKVForm($kvform_string) { // Create a Message from a KVForm string return Auth_OpenID_Message::fromOpenIDArgs( Auth_OpenID_KVForm::toArray($kvform_string)); } /** * @return Auth_OpenID_Message */ function copy() { return $this; } function toPostArgs() { // Return all arguments with openid. in front of namespaced // arguments. $args = []; // Add namespace definitions to the output foreach ($this->namespaces->iteritems() as $pair) { list($ns_uri, $alias) = $pair; if ($this->namespaces->isImplicit($ns_uri)) { continue; } if ($alias == Auth_OpenID_NULL_NAMESPACE) { $ns_key = 'openid.ns'; } else { $ns_key = 'openid.ns.' . $alias; } $args[$ns_key] = $ns_uri; } foreach ($this->args->items() as $pair) { list($ns_parts, $value) = $pair; list($ns_uri, $ns_key) = $ns_parts; $key = $this->getKey($ns_uri, $ns_key); $args[$key] = $value; } return $args; } function toArgs() { // Return all namespaced arguments, failing if any // non-namespaced arguments exist. $post_args = $this->toPostArgs(); $kvargs = []; foreach ($post_args as $k => $v) { if (strpos($k, 'openid.') !== 0) { // raise ValueError( // 'This message can only be encoded as a POST, because it ' // 'contains arguments that are not prefixed with "openid."') return null; } else { $kvargs[substr($k, 7)] = $v; } } return $kvargs; } /** * @param string $action_url * @param null|array $form_tag_attrs * @param string $submit_text * @return string */ function toFormMarkup($action_url, $form_tag_attrs = null, $submit_text = "Continue") { $form = "
$attr) { $form .= sprintf(" %s=\"%s\"", $name, htmlspecialchars($attr)); } } $form .= ">\n"; foreach ($this->toPostArgs() as $name => $value) { $form .= sprintf( "\n", htmlspecialchars($name), htmlspecialchars($value)); } $form .= sprintf("\n", htmlspecialchars($submit_text)); $form .= "
\n"; return $form; } function toURL($base_url) { // Generate a GET URL with the parameters in this message // attached as query parameters. return Auth_OpenID::appendArgs($base_url, $this->toPostArgs()); } function toKVForm() { // Generate a KVForm string that contains the parameters in // this message. This will fail if the message contains // arguments outside of the 'openid.' prefix. return Auth_OpenID_KVForm::fromArray($this->toArgs()); } function toURLEncoded() { // Generate an x-www-urlencoded string $args = []; foreach ($this->toPostArgs() as $k => $v) { $args[] = [$k, $v]; } sort($args); return Auth_OpenID::httpBuildQuery($args); } /** * @access private * @param string $namespace * @return Auth_OpenID_FailureResponse|null|string */ function _fixNS($namespace) { // Convert an input value into the internally used values of // this object if ($namespace == Auth_OpenID_OPENID_NS) { if ($this->_openid_ns_uri === null) { return new Auth_OpenID_FailureResponse(null, 'OpenID namespace not set'); } else { $namespace = $this->_openid_ns_uri; } } if (($namespace != Auth_OpenID_BARE_NS) && (!is_string($namespace))) { //TypeError $err_msg = sprintf("Namespace must be Auth_OpenID_BARE_NS, ". "Auth_OpenID_OPENID_NS or a string. got %s", print_r($namespace, true)); return new Auth_OpenID_FailureResponse(null, $err_msg); } if (($namespace != Auth_OpenID_BARE_NS) && (strpos($namespace, ':') === false)) { // fmt = 'OpenID 2.0 namespace identifiers SHOULD be URIs. Got %r' // warnings.warn(fmt % (namespace,), DeprecationWarning) if ($namespace == 'sreg') { // fmt = 'Using %r instead of "sreg" as namespace' // warnings.warn(fmt % (SREG_URI,), DeprecationWarning,) return Auth_OpenID_SREG_URI; } } return $namespace; } function hasKey($namespace, $ns_key) { $namespace = $this->_fixNS($namespace); if (Auth_OpenID::isFailure($namespace)) { // XXX log me return false; } else { return $this->args->contains([$namespace, $ns_key]); } } function getKey($namespace, $ns_key) { // Get the key for a particular namespaced argument $namespace = $this->_fixNS($namespace); if (Auth_OpenID::isFailure($namespace)) { return $namespace; } if ($namespace == Auth_OpenID_BARE_NS) { return $ns_key; } $ns_alias = $this->namespaces->getAlias($namespace); // No alias is defined, so no key can exist if ($ns_alias === null) { return null; } if ($ns_alias == Auth_OpenID_NULL_NAMESPACE) { $tail = $ns_key; } else { $tail = sprintf('%s.%s', $ns_alias, $ns_key); } return 'openid.' . $tail; } /** * @param string $namespace * @param string $key * @param mixed $default * @return Auth_OpenID_FailureResponse|mixed|null|string */ function getArg($namespace, $key, $default = null) { // Get a value for a namespaced key. $namespace = $this->_fixNS($namespace); if (Auth_OpenID::isFailure($namespace)) { return $namespace; } else { if ((!$this->args->contains([$namespace, $key])) && ($default == Auth_OpenID_NO_DEFAULT)) { $err_msg = sprintf("Namespace %s missing required field %s", $namespace, $key); return new Auth_OpenID_FailureResponse(null, $err_msg); } else { return $this->args->get([$namespace, $key], $default); } } } function getArgs($namespace) { // Get the arguments that are defined for this namespace URI $namespace = $this->_fixNS($namespace); if (Auth_OpenID::isFailure($namespace)) { return $namespace; } else { $stuff = []; foreach ($this->args->items() as $pair) { list($key, $value) = $pair; list($pair_ns, $ns_key) = $key; if ($pair_ns == $namespace) { $stuff[$ns_key] = $value; } } return $stuff; } } function updateArgs($namespace, $updates) { // Set multiple key/value pairs in one call $namespace = $this->_fixNS($namespace); if (Auth_OpenID::isFailure($namespace)) { return $namespace; } else { foreach ($updates as $k => $v) { $this->setArg($namespace, $k, $v); } return true; } } function setArg($namespace, $key, $value) { // Set a single argument in this namespace $namespace = $this->_fixNS($namespace); if (Auth_OpenID::isFailure($namespace)) { return $namespace; } else { $this->args->set([$namespace, $key], $value); if ($namespace !== Auth_OpenID_BARE_NS) { $this->namespaces->add($namespace); } return true; } } function delArg($namespace, $key) { $namespace = $this->_fixNS($namespace); if (Auth_OpenID::isFailure($namespace)) { return $namespace; } else { return $this->args->del([$namespace, $key]); } } function getAliasedArg($aliased_key, $default = null) { if ($aliased_key == 'ns') { // Return the namespace URI for the OpenID namespace return $this->getOpenIDNamespace(); } $parts = explode('.', $aliased_key, 2); $key = null; if (count($parts) != 2) { $ns = null; } else { list($alias, $key) = $parts; if ($alias == 'ns') { // Return the namespace URI for a namespace alias // parameter. return $this->namespaces->getNamespaceURI($key); } else { $ns = $this->namespaces->getNamespaceURI($alias); } } if ($ns === null) { $key = $aliased_key; $ns = $this->getOpenIDNamespace(); } return $this->getArg($ns, $key, $default); } }