summaryrefslogtreecommitdiff
path: root/kolab.org/www/drupal-7.26/modules/openid
diff options
context:
space:
mode:
Diffstat (limited to 'kolab.org/www/drupal-7.26/modules/openid')
-rw-r--r--kolab.org/www/drupal-7.26/modules/openid/login-bg.pngbin0 -> 205 bytes
-rw-r--r--kolab.org/www/drupal-7.26/modules/openid/openid-rtl.css18
-rw-r--r--kolab.org/www/drupal-7.26/modules/openid/openid.api.php116
-rw-r--r--kolab.org/www/drupal-7.26/modules/openid/openid.css46
-rw-r--r--kolab.org/www/drupal-7.26/modules/openid/openid.inc894
-rw-r--r--kolab.org/www/drupal-7.26/modules/openid/openid.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/openid/openid.install230
-rw-r--r--kolab.org/www/drupal-7.26/modules/openid/openid.js49
-rw-r--r--kolab.org/www/drupal-7.26/modules/openid/openid.module1034
-rw-r--r--kolab.org/www/drupal-7.26/modules/openid/openid.pages.inc116
-rw-r--r--kolab.org/www/drupal-7.26/modules/openid/openid.test802
-rw-r--r--kolab.org/www/drupal-7.26/modules/openid/tests/openid_test.info13
-rw-r--r--kolab.org/www/drupal-7.26/modules/openid/tests/openid_test.install17
-rw-r--r--kolab.org/www/drupal-7.26/modules/openid/tests/openid_test.module367
14 files changed, 3714 insertions, 0 deletions
diff --git a/kolab.org/www/drupal-7.26/modules/openid/login-bg.png b/kolab.org/www/drupal-7.26/modules/openid/login-bg.png
new file mode 100644
index 0000000..532614f
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/openid/login-bg.png
Binary files differ
diff --git a/kolab.org/www/drupal-7.26/modules/openid/openid-rtl.css b/kolab.org/www/drupal-7.26/modules/openid/openid-rtl.css
new file mode 100644
index 0000000..861f6d7
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/openid/openid-rtl.css
@@ -0,0 +1,18 @@
+
+#edit-openid-identifier {
+ background-position: right 50%;
+ padding-left: 0;
+ padding-right: 20px;
+}
+#user-login .openid-links {
+ padding-right: 0;
+}
+html.js #user-login-form li.openid-link,
+html.js #user-login li.openid-link {
+ margin-right: 0;
+}
+#user-login-form li.openid-link a,
+#user-login li.openid-link a {
+ background-position: right top;
+ padding: 0 1.5em 0 0;
+}
diff --git a/kolab.org/www/drupal-7.26/modules/openid/openid.api.php b/kolab.org/www/drupal-7.26/modules/openid/openid.api.php
new file mode 100644
index 0000000..5e3d15d
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/openid/openid.api.php
@@ -0,0 +1,116 @@
+<?php
+
+/**
+ * @file
+ * Hooks provided by the OpenID module.
+ */
+
+/**
+ * @addtogroup hooks
+ * @{
+ */
+
+/**
+ * Allow modules to modify the OpenID request parameters.
+ *
+ * @param $op
+ * The operation to be performed.
+ * Possible values:
+ * - request: Modify parameters before they are sent to the OpenID provider.
+ * @param $request
+ * An associative array of parameter defaults to which to modify or append.
+ * @return
+ * An associative array of parameters to be merged with the default list.
+ *
+ */
+function hook_openid($op, $request) {
+ if ($op == 'request') {
+ $request['openid.identity'] = 'http://myname.myopenid.com/';
+ }
+ return $request;
+}
+
+/**
+ * Allow modules to act upon a successful OpenID login.
+ *
+ * @param $response
+ * Response values from the OpenID Provider.
+ * @param $account
+ * The Drupal user account that logged in
+ *
+ */
+function hook_openid_response($response, $account) {
+ if (isset($response['openid.ns.ax'])) {
+ _mymodule_store_ax_fields($response, $account);
+ }
+}
+
+/**
+ * Allow modules to declare OpenID discovery methods.
+ *
+ * The discovery function callbacks will be called in turn with an unique
+ * parameter, the claimed identifier. They have to return an associative array
+ * with array of services and claimed identifier in the same form as returned by
+ * openid_discover(). The resulting array must contain following keys:
+ * - 'services' (required) an array of discovered services (including OpenID
+ * version, endpoint URI, etc).
+ * - 'claimed_id' (optional) new claimed identifer, found by following HTTP
+ * redirects during the services discovery.
+ *
+ * The first discovery method that succeed (return at least one services) will
+ * stop the discovery process.
+ *
+ * @return
+ * An associative array which keys are the name of the discovery methods and
+ * values are function callbacks.
+ *
+ * @see hook_openid_discovery_method_info_alter()
+ */
+function hook_openid_discovery_method_info() {
+ return array(
+ 'new_discovery_idea' => '_my_discovery_method',
+ );
+}
+
+/**
+ * Allow modules to alter discovery methods.
+ */
+function hook_openid_discovery_method_info_alter(&$methods) {
+ // Remove XRI discovery scheme.
+ unset($methods['xri']);
+}
+
+/**
+ * Allow modules to declare OpenID normalization methods.
+ *
+ * The discovery function callbacks will be called in turn with an unique
+ * parameter, the identifier to normalize. They have to return a normalized
+ * identifier, or NULL if the identifier is not in a form they can handle.
+ *
+ * The first normalization method that succeed (return a value that is not NULL)
+ * will stop the normalization process.
+ *
+ * @return
+ * An array with a set of function callbacks, that will be called in turn
+ * when normalizing an OpenID identifier. The normalization functions have
+ * to return a normalized identifier, or NULL if the identifier is not in
+ * a form they can handle.
+ * @see hook_openid_normalization_method_info_alter()
+ */
+function hook_openid_normalization_method_info() {
+ return array(
+ 'new_normalization_idea' => '_my_normalization_method',
+ );
+}
+
+/**
+ * Allow modules to alter normalization methods.
+ */
+function hook_openid_normalization_method_info_alter(&$methods) {
+ // Remove Google IDP normalization.
+ unset($methods['google_idp']);
+}
+
+/**
+ * @} End of "addtogroup hooks".
+ */
diff --git a/kolab.org/www/drupal-7.26/modules/openid/openid.css b/kolab.org/www/drupal-7.26/modules/openid/openid.css
new file mode 100644
index 0000000..48b170f
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/openid/openid.css
@@ -0,0 +1,46 @@
+
+#edit-openid-identifier {
+ background-image: url("login-bg.png");
+ background-position: left 50%; /* LTR */
+ background-repeat: no-repeat;
+ padding-left: 20px; /* LTR */
+}
+div.form-item-openid-identifier {
+ display: block;
+}
+html.js #user-login-form div.form-item-openid-identifier,
+html.js #user-login div.form-item-openid-identifier {
+ display: none;
+}
+#user-login-form ul {
+ margin-top: 0;
+}
+#user-login ul {
+ margin: 0 0 5px;
+}
+#user-login ul li {
+ margin: 0;
+}
+#user-login-form .openid-links {
+ padding-bottom: 0;
+}
+#user-login .openid-links {
+ padding-left: 0; /* LTR */
+}
+#user-login-form .openid-links li,
+#user-login .openid-links li {
+ display: none;
+ list-style: none;
+}
+html.js #user-login-form li.openid-link,
+html.js #user-login li.openid-link {
+ display: block;
+ margin-left: 0; /* LTR */
+}
+#user-login-form li.openid-link a,
+#user-login li.openid-link a {
+ background-image: url("login-bg.png");
+ background-position: left top; /* LTR */
+ background-repeat: no-repeat;
+ padding: 0 0 0 1.5em; /* LTR */
+}
diff --git a/kolab.org/www/drupal-7.26/modules/openid/openid.inc b/kolab.org/www/drupal-7.26/modules/openid/openid.inc
new file mode 100644
index 0000000..d7ef663
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/openid/openid.inc
@@ -0,0 +1,894 @@
+<?php
+
+/**
+ * @file
+ * OpenID utility functions.
+ */
+
+/**
+ * Diffie-Hellman Key Exchange Default Value.
+ *
+ * This is used to establish an association between the Relying Party and the
+ * OpenID Provider.
+ *
+ * See RFC 2631: http://www.ietf.org/rfc/rfc2631.txt
+ */
+define('OPENID_DH_DEFAULT_MOD', '155172898181473697471232257763715539915724801' .
+ '966915404479707795314057629378541917580651227423698188993727816152646631' .
+ '438561595825688188889951272158842675419950341258706556549803580104870537' .
+ '681476726513255747040765857479291291572334510643245094715007229621094194' .
+ '349783925984760375594985848253359305585439638443');
+
+/**
+ * Diffie-Hellman generator; used for Diffie-Hellman key exchange computations.
+ */
+define('OPENID_DH_DEFAULT_GEN', '2');
+
+/**
+ * SHA-1 hash block size; used for Diffie-Hellman key exchange computations.
+ */
+define('OPENID_SHA1_BLOCKSIZE', 64);
+
+/**
+ * Random number generator; used for Diffie-Hellman key exchange computations.
+ */
+define('OPENID_RAND_SOURCE', '/dev/urandom');
+
+/**
+ * OpenID Authentication 2.0 namespace URL.
+ */
+define('OPENID_NS_2_0', 'http://specs.openid.net/auth/2.0');
+
+/**
+ * OpenID Authentication 1.1 namespace URL; used for backwards-compatibility.
+ */
+define('OPENID_NS_1_1', 'http://openid.net/signon/1.1');
+
+/**
+ * OpenID Authentication 1.0 namespace URL; used for backwards-compatibility.
+ */
+define('OPENID_NS_1_0', 'http://openid.net/signon/1.0');
+
+/**
+ * OpenID namespace used in Yadis documents.
+ */
+define('OPENID_NS_OPENID', 'http://openid.net/xmlns/1.0');
+
+/**
+ * OpenID Simple Registration extension.
+ */
+define('OPENID_NS_SREG', 'http://openid.net/extensions/sreg/1.1');
+
+/**
+ * OpenID Attribute Exchange extension.
+ */
+define('OPENID_NS_AX', 'http://openid.net/srv/ax/1.0');
+
+/**
+ * Extensible Resource Descriptor documents.
+ */
+define('OPENID_NS_XRD', 'xri://$xrd*($v*2.0)');
+
+/**
+ * Performs an HTTP 302 redirect (for the 1.x protocol).
+ */
+function openid_redirect_http($url, $message) {
+ $query = array();
+ foreach ($message as $key => $val) {
+ $query[] = $key . '=' . urlencode($val);
+ }
+
+ $sep = (strpos($url, '?') === FALSE) ? '?' : '&';
+ header('Location: ' . $url . $sep . implode('&', $query), TRUE, 302);
+
+ drupal_exit();
+}
+
+/**
+ * Creates a js auto-submit redirect for (for the 2.x protocol)
+ */
+function openid_redirect($url, $message) {
+ global $language;
+
+ $output = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' . "\n";
+ $output .= '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="' . $language->language . '" lang="' . $language->language . '">' . "\n";
+ $output .= "<head>\n";
+ $output .= "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n";
+ $output .= "<title>" . t('OpenID redirect') . "</title>\n";
+ $output .= "</head>\n";
+ $output .= "<body>\n";
+ $elements = drupal_get_form('openid_redirect_form', $url, $message);
+ $output .= drupal_render($elements);
+ $output .= '<script type="text/javascript">document.getElementById("openid-redirect-form").submit();</script>' . "\n";
+ $output .= "</body>\n";
+ $output .= "</html>\n";
+ print $output;
+
+ drupal_exit();
+}
+
+function openid_redirect_form($form, &$form_state, $url, $message) {
+ $form['#action'] = $url;
+ $form['#method'] = "post";
+ foreach ($message as $key => $value) {
+ $form[$key] = array(
+ '#type' => 'hidden',
+ '#name' => $key,
+ '#value' => $value,
+ );
+ }
+ $form['actions'] = array('#type' => 'actions');
+ $form['actions']['submit'] = array(
+ '#type' => 'submit',
+ '#prefix' => '<noscript><div>',
+ '#suffix' => '</div></noscript>',
+ '#value' => t('Send'),
+ );
+
+ return $form;
+}
+
+/**
+ * Parse an XRDS document.
+ *
+ * @param $raw_xml
+ * A string containing the XRDS document.
+ * @return
+ * An array of service entries.
+ */
+function _openid_xrds_parse($raw_xml) {
+ $services = array();
+
+ // For PHP version >= 5.2.11, we can use this function to protect against
+ // malicious doctype declarations and other unexpected entity loading.
+ // However, we will not rely on it, and reject any XML with a DOCTYPE.
+ $disable_entity_loader = function_exists('libxml_disable_entity_loader');
+ if ($disable_entity_loader) {
+ $load_entities = libxml_disable_entity_loader(TRUE);
+ }
+
+ // Load the XML into a DOM document.
+ $dom = new DOMDocument();
+ @$dom->loadXML($raw_xml);
+
+ // Since DOCTYPE declarations from an untrusted source could be malicious, we
+ // stop parsing here and treat the XML as invalid since XRDS documents do not
+ // require, and are not expected to have, a DOCTYPE.
+ if (isset($dom->doctype)) {
+ return array();
+ }
+
+ // Parse the DOM document for the information we need.
+ if ($xml = simplexml_import_dom($dom)) {
+ foreach ($xml->children(OPENID_NS_XRD)->XRD as $xrd) {
+ foreach ($xrd->children(OPENID_NS_XRD)->Service as $service_element) {
+ $service = array(
+ 'priority' => $service_element->attributes()->priority ? (int)$service_element->attributes()->priority : PHP_INT_MAX,
+ 'types' => array(),
+ 'uri' => (string)$service_element->children(OPENID_NS_XRD)->URI,
+ 'service' => $service_element,
+ 'xrd' => $xrd,
+ );
+ foreach ($service_element->Type as $type) {
+ $service['types'][] = (string)$type;
+ }
+ if ($service_element->children(OPENID_NS_XRD)->LocalID) {
+ $service['identity'] = (string)$service_element->children(OPENID_NS_XRD)->LocalID;
+ }
+ elseif ($service_element->children(OPENID_NS_OPENID)->Delegate) {
+ $service['identity'] = (string)$service_element->children(OPENID_NS_OPENID)->Delegate;
+ }
+ else {
+ $service['identity'] = FALSE;
+ }
+ $services[] = $service;
+ }
+ }
+ }
+
+ // Return the LIBXML options to the previous state before returning.
+ if ($disable_entity_loader) {
+ libxml_disable_entity_loader($load_entities);
+ }
+
+ return $services;
+}
+
+/**
+ * Select a service element.
+ *
+ * The procedure is described in OpenID Authentication 2.0, section 7.3.2.
+ *
+ * A new entry is added to the returned array with the key 'version' and the
+ * value 1 or 2 specifying the protocol version used by the service.
+ *
+ * @param $services
+ * An array of service arrays as returned by openid_discovery().
+ * @return
+ * The selected service array, or NULL if no valid services were found.
+ */
+function _openid_select_service(array $services) {
+ // Extensible Resource Identifier (XRI) Resolution Version 2.0, section 4.3.3:
+ // Find the service with the highest priority (lowest integer value). If there
+ // is a tie, select a random one, not just the first in the XML document.
+ shuffle($services);
+ $selected_service = NULL;
+ $selected_type_priority = FALSE;
+
+ // Search for an OP Identifier Element.
+ foreach ($services as $service) {
+ if (!empty($service['uri'])) {
+ $type_priority = FALSE;
+ if (in_array('http://specs.openid.net/auth/2.0/server', $service['types'])) {
+ $service['version'] = 2;
+ $type_priority = 1;
+ }
+ elseif (in_array('http://specs.openid.net/auth/2.0/signon', $service['types'])) {
+ $service['version'] = 2;
+ $type_priority = 2;
+ }
+ elseif (in_array(OPENID_NS_1_0, $service['types']) || in_array(OPENID_NS_1_1, $service['types'])) {
+ $service['version'] = 1;
+ $type_priority = 3;
+ }
+
+ if ($type_priority
+ && (!$selected_service
+ || $type_priority < $selected_type_priority
+ || ($type_priority == $selected_type_priority && $service['priority'] < $selected_service['priority']))) {
+ $selected_service = $service;
+ $selected_type_priority = $type_priority;
+ }
+ }
+ }
+
+ if ($selected_service) {
+ // Unset SimpleXMLElement instances that cannot be saved in $_SESSION.
+ unset($selected_service['xrd']);
+ unset($selected_service['service']);
+ }
+
+ return $selected_service;
+}
+
+/**
+ * Determine if the given identifier is an XRI ID.
+ */
+function _openid_is_xri($identifier) {
+ // Strip the xri:// scheme from the identifier if present.
+ if (stripos($identifier, 'xri://') === 0) {
+ $identifier = substr($identifier, 6);
+ }
+
+ // Test whether the identifier starts with an XRI global context symbol or (.
+ $firstchar = substr($identifier, 0, 1);
+ if (strpos("=@+$!(", $firstchar) !== FALSE) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * Normalize the given identifier.
+ *
+ * The procedure is described in OpenID Authentication 2.0, section 7.2.
+ */
+function openid_normalize($identifier) {
+ $methods = module_invoke_all('openid_normalization_method_info');
+ drupal_alter('openid_normalization_method_info', $methods);
+
+ // Execute each method in turn, stopping after the first method accepted
+ // the identifier.
+ foreach ($methods as $method) {
+ $result = $method($identifier);
+ if ($result !== NULL) {
+ $identifier = $result;
+ break;
+ }
+ }
+
+ return $identifier;
+}
+
+/**
+ * OpenID normalization method: normalize XRI identifiers.
+ */
+function _openid_xri_normalize($identifier) {
+ if (_openid_is_xri($identifier)) {
+ if (stristr($identifier, 'xri://') !== FALSE) {
+ $identifier = substr($identifier, 6);
+ }
+ return $identifier;
+ }
+}
+
+/**
+ * OpenID normalization method: normalize URL identifiers.
+ */
+function _openid_url_normalize($url) {
+ $normalized_url = $url;
+
+ if (stristr($url, '://') === FALSE) {
+ $normalized_url = 'http://' . $url;
+ }
+
+ // Strip the fragment and fragment delimiter if present.
+ $normalized_url = strtok($normalized_url, '#');
+
+ if (substr_count($normalized_url, '/') < 3) {
+ $normalized_url .= '/';
+ }
+
+ return $normalized_url;
+}
+
+/**
+ * Create a serialized message packet as per spec: $key:$value\n .
+ */
+function _openid_create_message($data) {
+ $serialized = '';
+
+ foreach ($data as $key => $value) {
+ if ((strpos($key, ':') !== FALSE) || (strpos($key, "\n") !== FALSE) || (strpos($value, "\n") !== FALSE)) {
+ return NULL;
+ }
+ $serialized .= "$key:$value\n";
+ }
+ return $serialized;
+}
+
+/**
+ * Encode a message from _openid_create_message for HTTP Post
+ */
+function _openid_encode_message($message) {
+ $encoded_message = '';
+
+ $items = explode("\n", $message);
+ foreach ($items as $item) {
+ $parts = explode(':', $item, 2);
+
+ if (count($parts) == 2) {
+ if ($encoded_message != '') {
+ $encoded_message .= '&';
+ }
+ $encoded_message .= rawurlencode(trim($parts[0])) . '=' . rawurlencode(trim($parts[1]));
+ }
+ }
+
+ return $encoded_message;
+}
+
+/**
+ * Convert a direct communication message
+ * into an associative array.
+ */
+function _openid_parse_message($message) {
+ $parsed_message = array();
+
+ $items = explode("\n", $message);
+ foreach ($items as $item) {
+ $parts = explode(':', $item, 2);
+
+ if (count($parts) == 2) {
+ $parsed_message[$parts[0]] = $parts[1];
+ }
+ }
+
+ return $parsed_message;
+}
+
+/**
+ * Return a nonce value - formatted per OpenID spec.
+ *
+ * NOTE: This nonce is not cryptographically secure and only suitable for use
+ * by the test framework.
+ */
+function _openid_nonce() {
+ // YYYY-MM-DDThh:mm:ssZ, plus some optional extra unique characters.
+ return gmdate('Y-m-d\TH:i:s\Z') .
+ chr(mt_rand(0, 25) + 65) .
+ chr(mt_rand(0, 25) + 65) .
+ chr(mt_rand(0, 25) + 65) .
+ chr(mt_rand(0, 25) + 65);
+}
+
+/**
+ * Pull the href attribute out of an html link element.
+ */
+function _openid_link_href($rel, $html) {
+ $rel = preg_quote($rel);
+ preg_match('|<link\s+rel=["\'](.*)' . $rel . '(.*)["\'](.*)/?>|iUs', $html, $matches);
+ if (isset($matches[3])) {
+ preg_match('|href=["\']([^"]+)["\']|iU', $matches[3], $href);
+ return trim($href[1]);
+ }
+ return FALSE;
+}
+
+/**
+ * Pull the http-equiv attribute out of an html meta element
+ */
+function _openid_meta_httpequiv($equiv, $html) {
+ preg_match('|<meta\s+http-equiv=["\']' . $equiv . '["\'](.*)/?>|iUs', $html, $matches);
+ if (isset($matches[1])) {
+ preg_match('|content=["\']([^"]+)["\']|iUs', $matches[1], $content);
+ if (isset($content[1])) {
+ return $content[1];
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * Sign certain keys in a message
+ * @param $association - object loaded from openid_association or openid_server_association table
+ * - important fields are ->assoc_type and ->mac_key
+ * @param $message_array - array of entire message about to be sent
+ * @param $keys_to_sign - keys in the message to include in signature (without
+ * 'openid.' appended)
+ */
+function _openid_signature($association, $message_array, $keys_to_sign) {
+ $signature = '';
+ $sign_data = array();
+
+ foreach ($keys_to_sign as $key) {
+ if (isset($message_array['openid.' . $key])) {
+ $sign_data[$key] = $message_array['openid.' . $key];
+ }
+ }
+
+ $message = _openid_create_message($sign_data);
+ $secret = base64_decode($association->mac_key);
+ $signature = _openid_hmac($secret, $message);
+
+ return base64_encode($signature);
+}
+
+function _openid_hmac($key, $text) {
+ if (strlen($key) > OPENID_SHA1_BLOCKSIZE) {
+ $key = sha1($key, TRUE);
+ }
+
+ $key = str_pad($key, OPENID_SHA1_BLOCKSIZE, chr(0x00));
+ $ipad = str_repeat(chr(0x36), OPENID_SHA1_BLOCKSIZE);
+ $opad = str_repeat(chr(0x5c), OPENID_SHA1_BLOCKSIZE);
+ $hash1 = sha1(($key ^ $ipad) . $text, TRUE);
+ $hmac = sha1(($key ^ $opad) . $hash1, TRUE);
+
+ return $hmac;
+}
+
+function _openid_dh_base64_to_long($str) {
+ $b64 = base64_decode($str);
+
+ return _openid_dh_binary_to_long($b64);
+}
+
+function _openid_dh_long_to_base64($str) {
+ return base64_encode(_openid_dh_long_to_binary($str));
+}
+
+function _openid_dh_binary_to_long($str) {
+ $bytes = array_merge(unpack('C*', $str));
+
+ $n = 0;
+ foreach ($bytes as $byte) {
+ $n = _openid_math_mul($n, pow(2, 8));
+ $n = _openid_math_add($n, $byte);
+ }
+
+ return $n;
+}
+
+function _openid_dh_long_to_binary($long) {
+ $cmp = _openid_math_cmp($long, 0);
+ if ($cmp < 0) {
+ return FALSE;
+ }
+
+ if ($cmp == 0) {
+ return "\x00";
+ }
+
+ $bytes = array();
+
+ while (_openid_math_cmp($long, 0) > 0) {
+ array_unshift($bytes, _openid_math_mod($long, 256));
+ $long = _openid_math_div($long, pow(2, 8));
+ }
+
+ if ($bytes && ($bytes[0] > 127)) {
+ array_unshift($bytes, 0);
+ }
+
+ $string = '';
+ foreach ($bytes as $byte) {
+ $string .= pack('C', $byte);
+ }
+
+ return $string;
+}
+
+function _openid_dh_xorsecret($shared, $secret) {
+ $dh_shared_str = _openid_dh_long_to_binary($shared);
+ $sha1_dh_shared = sha1($dh_shared_str, TRUE);
+ $xsecret = "";
+ for ($i = 0; $i < strlen($secret); $i++) {
+ $xsecret .= chr(ord($secret[$i]) ^ ord($sha1_dh_shared[$i]));
+ }
+
+ return $xsecret;
+}
+
+function _openid_dh_rand($stop) {
+ $duplicate_cache = &drupal_static(__FUNCTION__, array());
+
+ // Used as the key for the duplicate cache
+ $rbytes = _openid_dh_long_to_binary($stop);
+
+ if (isset($duplicate_cache[$rbytes])) {
+ list($duplicate, $nbytes) = $duplicate_cache[$rbytes];
+ }
+ else {
+ if ($rbytes[0] == "\x00") {
+ $nbytes = strlen($rbytes) - 1;
+ }
+ else {
+ $nbytes = strlen($rbytes);
+ }
+
+ $mxrand = _openid_math_pow(256, $nbytes);
+
+ // If we get a number less than this, then it is in the
+ // duplicated range.
+ $duplicate = _openid_math_mod($mxrand, $stop);
+
+ if (count($duplicate_cache) > 10) {
+ $duplicate_cache = array();
+ }
+
+ $duplicate_cache[$rbytes] = array($duplicate, $nbytes);
+ }
+
+ do {
+ $bytes = "\x00" . drupal_random_bytes($nbytes);
+ $n = _openid_dh_binary_to_long($bytes);
+ // Keep looping if this value is in the low duplicated range.
+ } while (_openid_math_cmp($n, $duplicate) < 0);
+
+ return _openid_math_mod($n, $stop);
+}
+
+function _openid_get_bytes($num_bytes) {
+ return drupal_random_bytes($num_bytes);
+}
+
+function _openid_response($str = NULL) {
+ $data = array();
+
+ if (isset($_SERVER['REQUEST_METHOD'])) {
+ $data = _openid_get_params($_SERVER['QUERY_STRING']);
+
+ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+ $str = file_get_contents('php://input');
+
+ $post = array();
+ if ($str !== FALSE) {
+ $post = _openid_get_params($str);
+ }
+
+ $data = array_merge($data, $post);
+ }
+ }
+
+ return $data;
+}
+
+function _openid_get_params($str) {
+ $chunks = explode("&", $str);
+
+ $data = array();
+ foreach ($chunks as $chunk) {
+ $parts = explode("=", $chunk, 2);
+
+ if (count($parts) == 2) {
+ list($k, $v) = $parts;
+ $data[$k] = urldecode($v);
+ }
+ }
+ return $data;
+}
+
+/**
+ * Extract all the parameters belonging to an extension in a response message.
+ *
+ * OpenID 2.0 defines a simple extension mechanism, based on a namespace prefix.
+ *
+ * Each request or response can define a prefix using:
+ * @code
+ * openid.ns.[prefix] = [extension_namespace]
+ * openid.[prefix].[key1] = [value1]
+ * openid.[prefix].[key2] = [value2]
+ * ...
+ * @endcode
+ *
+ * This function extracts all the keys belonging to an extension namespace in a
+ * response, optionally using a fallback prefix if none is provided in the response.
+ *
+ * Note that you cannot assume that a given extension namespace will use the same
+ * prefix on the response and the request: each party may use a different prefix
+ * to refer to the same namespace.
+ *
+ * @param $response
+ * The response array.
+ * @param $extension_namespace
+ * The namespace of the extension.
+ * @param $fallback_prefix
+ * An optional prefix that will be used in case no prefix is found for the
+ * target extension namespace.
+ * @param $only_signed
+ * Return only keys that are included in the message signature in openid.sig.
+ * Unsigned fields may have been modified or added by other parties than the
+ * OpenID Provider.
+ *
+ * @return
+ * An associative array containing all the parameters in the response message
+ * that belong to the extension. The keys are stripped from their namespace
+ * prefix.
+ *
+ * @see http://openid.net/specs/openid-authentication-2_0.html#extensions
+ */
+function openid_extract_namespace($response, $extension_namespace, $fallback_prefix = NULL, $only_signed = FALSE) {
+ $signed_keys = explode(',', $response['openid.signed']);
+
+ // Find the namespace prefix.
+ $prefix = $fallback_prefix;
+ foreach ($response as $key => $value) {
+ if ($value == $extension_namespace && preg_match('/^openid\.ns\.([^.]+)$/', $key, $matches)) {
+ $prefix = $matches[1];
+ if ($only_signed && !in_array('ns.' . $matches[1], $signed_keys)) {
+ // The namespace was defined but was not signed as required. In this
+ // case we do not fall back to $fallback_prefix.
+ $prefix = NULL;
+ }
+ break;
+ }
+ }
+
+ // Now extract the namespace keys from the response.
+ $output = array();
+ if (!isset($prefix)) {
+ return $output;
+ }
+ foreach ($response as $key => $value) {
+ if (preg_match('/^openid\.' . $prefix . '\.(.+)$/', $key, $matches)) {
+ $local_key = $matches[1];
+ if (!$only_signed || in_array($prefix . '.' . $local_key, $signed_keys)) {
+ $output[$local_key] = $value;
+ }
+ }
+ }
+
+ return $output;
+}
+
+/**
+ * Extracts values from an OpenID AX Response.
+ *
+ * The values can be returned in two forms:
+ * - only openid.ax.value.<alias> (for single-valued answers)
+ * - both openid.ax.count.<alias> and openid.ax.value.<alias>.<count> (for both
+ * single and multiple-valued answers)
+ *
+ * @param $values
+ * An array as returned by openid_extract_namespace(..., OPENID_NS_AX).
+ * @param $uris
+ * An array of identifier URIs.
+ * @return
+ * An array of values.
+ * @see http://openid.net/specs/openid-attribute-exchange-1_0.html#fetch_response
+ */
+function openid_extract_ax_values($values, $uris) {
+ $output = array();
+ foreach ($values as $key => $value) {
+ if (in_array($value, $uris) && preg_match('/^type\.([^.]+)$/', $key, $matches)) {
+ $alias = $matches[1];
+ if (isset($values['count.' . $alias])) {
+ for ($i = 1; $i <= $values['count.' . $alias]; $i++) {
+ $output[] = $values['value.' . $alias . '.' . $i];
+ }
+ }
+ elseif (isset($values['value.' . $alias])) {
+ $output[] = $values['value.' . $alias];
+ }
+ break;
+ }
+ }
+ return $output;
+}
+
+/**
+ * Determine the available math library GMP vs. BCMath, favouring GMP for performance.
+ */
+function _openid_get_math_library() {
+ // Not drupal_static(), because a function is not going to disappear and
+ // change the output of this under any circumstances.
+ static $library;
+
+ if (empty($library)) {
+ if (function_exists('gmp_add')) {
+ $library = 'gmp';
+ }
+ elseif (function_exists('bcadd')) {
+ $library = 'bcmath';
+ }
+ }
+
+ return $library;
+}
+
+/**
+ * Calls the add function from the available math library for OpenID.
+ */
+function _openid_math_add($x, $y) {
+ $library = _openid_get_math_library();
+ switch ($library) {
+ case 'gmp':
+ return gmp_strval(gmp_add($x, $y));
+ case 'bcmath':
+ return bcadd($x, $y);
+ }
+}
+
+/**
+ * Calls the mul function from the available math library for OpenID.
+ */
+function _openid_math_mul($x, $y) {
+ $library = _openid_get_math_library();
+ switch ($library) {
+ case 'gmp':
+ return gmp_mul($x, $y);
+ case 'bcmath':
+ return bcmul($x, $y);
+ }
+}
+
+/**
+ * Calls the div function from the available math library for OpenID.
+ */
+function _openid_math_div($x, $y) {
+ $library = _openid_get_math_library();
+ switch ($library) {
+ case 'gmp':
+ return gmp_div($x, $y);
+ case 'bcmath':
+ return bcdiv($x, $y);
+ }
+}
+
+/**
+ * Calls the cmp function from the available math library for OpenID.
+ */
+function _openid_math_cmp($x, $y) {
+ $library = _openid_get_math_library();
+ switch ($library) {
+ case 'gmp':
+ return gmp_cmp($x, $y);
+ case 'bcmath':
+ return bccomp($x, $y);
+ }
+}
+
+/**
+ * Calls the mod function from the available math library for OpenID.
+ */
+function _openid_math_mod($x, $y) {
+ $library = _openid_get_math_library();
+ switch ($library) {
+ case 'gmp':
+ return gmp_mod($x, $y);
+ case 'bcmath':
+ return bcmod($x, $y);
+ }
+}
+
+/**
+ * Calls the pow function from the available math library for OpenID.
+ */
+function _openid_math_pow($x, $y) {
+ $library = _openid_get_math_library();
+ switch ($library) {
+ case 'gmp':
+ return gmp_pow($x, $y);
+ case 'bcmath':
+ return bcpow($x, $y);
+ }
+}
+
+/**
+ * Calls the mul function from the available math library for OpenID.
+ */
+function _openid_math_powmod($x, $y, $z) {
+ $library = _openid_get_math_library();
+ switch ($library) {
+ case 'gmp':
+ return gmp_powm($x, $y, $z);
+ case 'bcmath':
+ return bcpowmod($x, $y, $z);
+ }
+}
+
+/**
+ * Provides transition for accounts with possibly invalid OpenID identifiers in authmap.
+ *
+ * This function provides a less safe but more unobtrusive procedure for users
+ * who cannot login with their OpenID identifiers. OpenID identifiers in the
+ * authmap could be incomplete due to invalid OpenID implementation in previous
+ * versions of Drupal (e.g. fragment part of the identifier could be missing).
+ * For more information see http://drupal.org/node/1120290.
+ *
+ * @param string $identity
+ * The user's claimed OpenID identifier.
+ *
+ * @return
+ * A fully-loaded user object if the user is found or FALSE if not found.
+ */
+function _openid_invalid_openid_transition($identity, $response) {
+ $account = FALSE;
+ $fallback_account = NULL;
+ $fallback_identity = $identity;
+
+ // Try to strip the fragment if it is present.
+ if (strpos($fallback_identity, '#') !== FALSE) {
+ $fallback_identity = preg_replace('/#.*/', '', $fallback_identity);
+ $fallback_account = user_external_load($fallback_identity);
+ }
+
+ // Try to replace HTTPS with HTTP. OpenID providers often redirect
+ // from http to https, but Drupal didn't follow the redirect.
+ if (!$fallback_account && strpos($fallback_identity, 'https://') !== FALSE) {
+ $fallback_identity = str_replace('https://', 'http://', $fallback_identity);
+ $fallback_account = user_external_load($fallback_identity);
+ }
+
+ // Try to use original identifier.
+ if (!$fallback_account && isset($_SESSION['openid']['user_login_values']['openid_identifier'])) {
+ $fallback_identity = openid_normalize($_SESSION['openid']['user_login_values']['openid_identifier']);
+ $fallback_account = user_external_load($fallback_identity);
+ }
+
+ if ($fallback_account) {
+ // Try to extract e-mail address from Simple Registration (SREG) or
+ // Attribute Exchanges (AX) keys.
+ $email = '';
+ $sreg_values = openid_extract_namespace($response, OPENID_NS_SREG, 'sreg', TRUE);
+ $ax_values = openid_extract_namespace($response, OPENID_NS_AX, 'ax', TRUE);
+ if (!empty($sreg_values['email']) && valid_email_address($sreg_values['email'])) {
+ $email = $sreg_values['email'];
+ }
+ elseif ($ax_mail_values = openid_extract_ax_values($ax_values, array('http://axschema.org/contact/email', 'http://schema.openid.net/contact/email'))) {
+ $email = current($ax_mail_values);
+ }
+
+ // If this e-mail address is the same as the e-mail address found in user
+ // account, login the user and update the claimed identifier.
+ if ($email && ($email == $fallback_account->mail || $email == $fallback_account->init)) {
+ $query = db_insert('authmap')
+ ->fields(array(
+ 'authname' => $identity,
+ 'uid' => $fallback_account->uid,
+ 'module' => 'openid',
+ ))
+ ->execute();
+ drupal_set_message(t('New OpenID identifier %identity was added as a replacement for invalid identifier %invalid_identity. To finish the invalid OpenID transition process, please go to your <a href="@openid_url">OpenID identities page</a> and remove the old identifier %invalid_identity.', array('%invalid_identity' => $fallback_identity, '%identity' => $identity, '@openid_url' => 'user/' . $fallback_account->uid . '/openid')));
+ // Set the account to the found one.
+ $account = $fallback_account;
+ }
+ else {
+ drupal_set_message(t('There is already an existing account associated with the OpenID identifier that you have provided. However, due to a bug in the previous version of the authentication system, we can\'t be sure that this account belongs to you. If you are new on this site, please continue registering the new user account. If you already have a registered account on this site associated with the provided OpenID identifier, please try to <a href="@url_password">reset the password</a> or contact the site administrator.', array('@url_password' => 'user/password')), 'warning');
+ }
+ }
+
+ return $account;
+}
diff --git a/kolab.org/www/drupal-7.26/modules/openid/openid.info b/kolab.org/www/drupal-7.26/modules/openid/openid.info
new file mode 100644
index 0000000..b0ed8c0
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/openid/openid.info
@@ -0,0 +1,12 @@
+name = OpenID
+description = "Allows users to log into your site using OpenID."
+version = VERSION
+package = Core
+core = 7.x
+files[] = openid.test
+
+; Information added by Drupal.org packaging script on 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/openid/openid.install b/kolab.org/www/drupal-7.26/modules/openid/openid.install
new file mode 100644
index 0000000..e382d86
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/openid/openid.install
@@ -0,0 +1,230 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the openid module.
+ */
+
+/**
+ * Implements hook_schema().
+ */
+function openid_schema() {
+ $schema['openid_association'] = array(
+ 'description' => 'Stores temporary shared key association information for OpenID authentication.',
+ 'fields' => array(
+ 'idp_endpoint_uri' => array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'description' => 'Primary Key: URI of the OpenID Provider endpoint.',
+ ),
+ 'assoc_handle' => array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'description' => 'Used to refer to this association in subsequent messages.',
+ ),
+ 'assoc_type' => array(
+ 'type' => 'varchar',
+ 'length' => 32,
+ 'description' => 'The signature algorithm used: one of HMAC-SHA1 or HMAC-SHA256.',
+ ),
+ 'session_type' => array(
+ 'type' => 'varchar',
+ 'length' => 32,
+ 'description' => 'Valid association session types: "no-encryption", "DH-SHA1", and "DH-SHA256".',
+ ),
+ 'mac_key' => array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'description' => 'The MAC key (shared secret) for this association.',
+ ),
+ 'created' => array(
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'UNIX timestamp for when the association was created.',
+ ),
+ 'expires_in' => array(
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'The lifetime, in seconds, of this association.',
+ ),
+ ),
+ 'primary key' => array('idp_endpoint_uri'),
+ 'unique keys' => array(
+ 'assoc_handle' => array('assoc_handle'),
+ ),
+ );
+
+ $schema['openid_nonce'] = array(
+ 'description' => 'Stores received openid.response_nonce per OpenID endpoint URL to prevent replay attacks.',
+ 'fields' => array(
+ 'idp_endpoint_uri' => array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'description' => 'URI of the OpenID Provider endpoint.',
+ ),
+ 'nonce' => array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'description' => 'The value of openid.response_nonce.',
+ ),
+ 'expires' => array(
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'A Unix timestamp indicating when the entry should expire.',
+ ),
+ ),
+ 'indexes' => array(
+ 'nonce' => array('nonce'),
+ 'expires' => array('expires'),
+ ),
+ );
+
+ return $schema;
+}
+
+/**
+ * Implements hook_requirements().
+ */
+function openid_requirements($phase) {
+ $requirements = array();
+
+ if ($phase == 'runtime') {
+ // Check for the PHP BC Math library.
+ if (!function_exists('bcadd') && !function_exists('gmp_add')) {
+ $requirements['openid_math'] = array(
+ 'value' => t('Not installed'),
+ 'severity' => REQUIREMENT_ERROR,
+ 'description' => t('OpenID suggests the use of either the <a href="@gmp">GMP Math</a> (recommended for performance) or <a href="@bc">BC Math</a> libraries to enable OpenID associations.', array('@gmp' => 'http://php.net/manual/en/book.gmp.php', '@bc' => 'http://www.php.net/manual/en/book.bc.php')),
+ );
+ }
+ elseif (!function_exists('gmp_add')) {
+ $requirements['openid_math'] = array(
+ 'value' => t('Not optimized'),
+ 'severity' => REQUIREMENT_WARNING,
+ 'description' => t('OpenID suggests the use of the GMP Math library for PHP for optimal performance. Check the <a href="@url">GMP Math Library documentation</a> for installation instructions.', array('@url' => 'http://www.php.net/manual/en/book.gmp.php')),
+ );
+ }
+ else {
+ $requirements['openid_math'] = array(
+ 'value' => t('Installed'),
+ 'severity' => REQUIREMENT_OK,
+ );
+ }
+ $requirements['openid_math']['title'] = t('OpenID Math library');
+ }
+
+ return $requirements;
+}
+
+/**
+ * @addtogroup updates-6.x-to-7.x
+ * @{
+ */
+
+/**
+ * Add a table to store nonces.
+ */
+function openid_update_6000() {
+ $schema['openid_nonce'] = array(
+ 'description' => 'Stores received openid.response_nonce per OpenID endpoint URL to prevent replay attacks.',
+ 'fields' => array(
+ 'idp_endpoint_uri' => array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'description' => 'URI of the OpenID Provider endpoint.',
+ ),
+ 'nonce' => array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'description' => 'The value of openid.response_nonce'
+ ),
+ 'expires' => array(
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'A Unix timestamp indicating when the entry should expire.',
+ ),
+ ),
+ 'indexes' => array(
+ 'nonce' => array('nonce'),
+ 'expires' => array('expires'),
+ ),
+ );
+
+ db_create_table('openid_nonce', $schema['openid_nonce']);
+}
+
+/**
+ * @} End of "addtogroup updates-6.x-to-7.x".
+ */
+
+/**
+ * @addtogroup updates-7.x-extra
+ * @{
+ */
+
+/**
+ * Bind associations to their providers.
+ */
+function openid_update_7000() {
+ db_drop_table('openid_association');
+
+ $schema = array(
+ 'description' => 'Stores temporary shared key association information for OpenID authentication.',
+ 'fields' => array(
+ 'idp_endpoint_uri' => array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'description' => 'Primary Key: URI of the OpenID Provider endpoint.',
+ ),
+ 'assoc_handle' => array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'description' => 'Used to refer to this association in subsequent messages.',
+ ),
+ 'assoc_type' => array(
+ 'type' => 'varchar',
+ 'length' => 32,
+ 'description' => 'The signature algorithm used: one of HMAC-SHA1 or HMAC-SHA256.',
+ ),
+ 'session_type' => array(
+ 'type' => 'varchar',
+ 'length' => 32,
+ 'description' => 'Valid association session types: "no-encryption", "DH-SHA1", and "DH-SHA256".',
+ ),
+ 'mac_key' => array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'description' => 'The MAC key (shared secret) for this association.',
+ ),
+ 'created' => array(
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'UNIX timestamp for when the association was created.',
+ ),
+ 'expires_in' => array(
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'The lifetime, in seconds, of this association.',
+ ),
+ ),
+ 'primary key' => array('idp_endpoint_uri'),
+ 'unique keys' => array(
+ 'assoc_handle' => array('assoc_handle'),
+ ),
+ );
+ db_create_table('openid_association', $schema);
+}
+
+/**
+ * @} End of "addtogroup updates-7.x-extra".
+ */
diff --git a/kolab.org/www/drupal-7.26/modules/openid/openid.js b/kolab.org/www/drupal-7.26/modules/openid/openid.js
new file mode 100644
index 0000000..4a09e5a
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/openid/openid.js
@@ -0,0 +1,49 @@
+(function ($) {
+
+Drupal.behaviors.openid = {
+ attach: function (context) {
+ var loginElements = $('.form-item-name, .form-item-pass, li.openid-link');
+ var openidElements = $('.form-item-openid-identifier, li.user-link');
+ var cookie = $.cookie('Drupal.visitor.openid_identifier');
+
+ // This behavior attaches by ID, so is only valid once on a page.
+ if (!$('#edit-openid-identifier.openid-processed').length) {
+ if (cookie) {
+ $('#edit-openid-identifier').val(cookie);
+ }
+ if ($('#edit-openid-identifier').val() || location.hash == '#openid-login') {
+ $('#edit-openid-identifier').addClass('openid-processed');
+ loginElements.hide();
+ // Use .css('display', 'block') instead of .show() to be Konqueror friendly.
+ openidElements.css('display', 'block');
+ }
+ }
+
+ $('li.openid-link:not(.openid-processed)', context)
+ .addClass('openid-processed')
+ .click(function () {
+ loginElements.hide();
+ openidElements.css('display', 'block');
+ // Remove possible error message.
+ $('#edit-name, #edit-pass').removeClass('error');
+ $('div.messages.error').hide();
+ // Set focus on OpenID Identifier field.
+ $('#edit-openid-identifier')[0].focus();
+ return false;
+ });
+ $('li.user-link:not(.openid-processed)', context)
+ .addClass('openid-processed')
+ .click(function () {
+ openidElements.hide();
+ loginElements.css('display', 'block');
+ // Clear OpenID Identifier field and remove possible error message.
+ $('#edit-openid-identifier').val('').removeClass('error');
+ $('div.messages.error').css('display', 'block');
+ // Set focus on username field.
+ $('#edit-name')[0].focus();
+ return false;
+ });
+ }
+};
+
+})(jQuery);
diff --git a/kolab.org/www/drupal-7.26/modules/openid/openid.module b/kolab.org/www/drupal-7.26/modules/openid/openid.module
new file mode 100644
index 0000000..a28f452
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/openid/openid.module
@@ -0,0 +1,1034 @@
+<?php
+
+/**
+ * @file
+ * Implement OpenID Relying Party support for Drupal
+ */
+
+/**
+ * Implements hook_menu().
+ */
+function openid_menu() {
+ $items['openid/authenticate'] = array(
+ 'title' => 'OpenID Login',
+ 'page callback' => 'openid_authentication_page',
+ 'access callback' => 'user_is_anonymous',
+ 'type' => MENU_CALLBACK,
+ 'file' => 'openid.pages.inc',
+ );
+ $items['user/%user/openid'] = array(
+ 'title' => 'OpenID identities',
+ 'page callback' => 'openid_user_identities',
+ 'page arguments' => array(1),
+ 'access callback' => 'user_edit_access',
+ 'access arguments' => array(1),
+ 'type' => MENU_LOCAL_TASK,
+ 'file' => 'openid.pages.inc',
+ );
+ $items['user/%user/openid/delete'] = array(
+ 'title' => 'Delete OpenID',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('openid_user_delete_form', 1),
+ 'access callback' => 'user_edit_access',
+ 'access arguments' => array(1),
+ 'file' => 'openid.pages.inc',
+ );
+ return $items;
+}
+
+/**
+ * Implements hook_menu_site_status_alter().
+ */
+function openid_menu_site_status_alter(&$menu_site_status, $path) {
+ // Allow access to openid/authenticate even if site is in offline mode.
+ if ($menu_site_status == MENU_SITE_OFFLINE && user_is_anonymous() && $path == 'openid/authenticate') {
+ $menu_site_status = MENU_SITE_ONLINE;
+ }
+}
+
+/**
+ * Implements hook_admin_paths().
+ */
+function openid_admin_paths() {
+ $paths = array(
+ 'user/*/openid' => TRUE,
+ 'user/*/openid/delete' => TRUE,
+ );
+ return $paths;
+}
+
+/**
+ * Implements hook_help().
+ */
+function openid_help($path, $arg) {
+ switch ($path) {
+ case 'user/%/openid':
+ $output = '<p>' . t('This site supports <a href="@openid-net">OpenID</a>, a secure way to log in to many websites using a single username and password. OpenID can reduce the necessity of managing many usernames and passwords for many websites.', array('@openid-net' => 'http://openid.net')) . '</p>';
+ $output .= '<p>' . t('To use OpenID you must first establish an identity on a public or private OpenID server. If you do not have an OpenID and would like one, look into one of the <a href="@openid-providers">free public providers</a>. You can find out more about OpenID at <a href="@openid-net">this website</a>.', array('@openid-providers' => 'http://openid.net/get/', '@openid-net' => 'http://openid.net')) . '</p>';
+ $output .= '<p>' . t('If you already have an OpenID, enter the URL to your OpenID server below (e.g. myusername.openidprovider.com). Next time you log in, you will be able to use this URL instead of a regular username and password. You can have multiple OpenID servers if you like; just keep adding them here.') . '</p>';
+ return $output;
+ case 'admin/help#openid':
+ $output = '';
+ $output .= '<h3>' . t('About') . '</h3>';
+ $output .= '<p>' . t('The OpenID module allows users to log in using the OpenID single sign on service. <a href="@openid-net">OpenID</a> is a secure method for logging into many websites with a single username and password. It does not require special software, and it does not share passwords with any site to which it is associated, including the site being logged into. The main benefit to users is that they can have a single password that they can use on many websites. This means they can easily update their single password from a centralized location, rather than having to change dozens of passwords individually. For more information, see the online handbook entry for <a href="@handbook">OpenID module</a>.', array('@openid-net' => 'http://openid.net', '@handbook' => 'http://drupal.org/documentation/modules/openid')) . '</p>';
+ $output .= '<h3>' . t('Uses') . '</h3>';
+ $output .= '<dl>';
+ $output .= '<dt>' . t('Logging in with OpenID') . '</dt>';
+ $output .= '<dd>' . t("To log in using OpenID, a user must already have an OpenID account. Users can then create site accounts using their OpenID, assign one or more OpenIDs to an existing account, and log in using an OpenID. This lowers the barrier to registration, which helps increase the user base, and offers convenience and security to the users. Because OpenID cannot guarantee a user is legitimate, email verification is still necessary. When logging in, users are presented with the option of entering their OpenID URL, which will look like <em>myusername.openidprovider.com</em>. The site then communicates with the OpenID server, asking it to verify the identity of the user. If the user is logged into their OpenID server, the server communicates back to your site, verifying the user. If they are not logged in, the OpenID server will ask the user for their password. At no point does the site being logged into record the user's OpenID password.") . '</dd>';
+ $output .= '</dl>';
+ return $output;
+ }
+}
+
+/**
+ * Implements hook_user_insert().
+ */
+function openid_user_insert(&$edit, $account, $category) {
+ if (!empty($edit['openid_claimed_id'])) {
+ // The user has registered after trying to log in via OpenID.
+ if (variable_get('user_email_verification', TRUE)) {
+ drupal_set_message(t('Once you have verified your e-mail address, you may log in via OpenID.'));
+ }
+ user_set_authmaps($account, array('authname_openid' => $edit['openid_claimed_id']));
+ unset($_SESSION['openid']);
+ unset($edit['openid_claimed_id']);
+ }
+}
+
+/**
+ * Implements hook_user_login().
+ *
+ * Save openid_identifier to visitor cookie.
+ */
+function openid_user_login(&$edit, $account) {
+ if (isset($_SESSION['openid'])) {
+ // The user has logged in via OpenID.
+ user_cookie_save(array_intersect_key($_SESSION['openid']['user_login_values'], array_flip(array('openid_identifier'))));
+ unset($_SESSION['openid']);
+ }
+}
+
+/**
+ * Implements hook_user_logout().
+ *
+ * Delete any openid_identifier in visitor cookie.
+ */
+function openid_user_logout($account) {
+ if (isset($_COOKIE['Drupal_visitor_openid_identifier'])) {
+ user_cookie_delete('openid_identifier');
+ }
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ */
+function openid_form_user_login_block_alter(&$form, &$form_state) {
+ _openid_user_login_form_alter($form, $form_state);
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ */
+function openid_form_user_login_alter(&$form, &$form_state) {
+ _openid_user_login_form_alter($form, $form_state);
+}
+
+function _openid_user_login_form_alter(&$form, &$form_state) {
+ $form['#attached']['css'][] = drupal_get_path('module', 'openid') . '/openid.css';
+ $form['#attached']['js'][] = drupal_get_path('module', 'openid') . '/openid.js';
+ $form['#attached']['library'][] = array('system', 'jquery.cookie');
+ if (!empty($form_state['input']['openid_identifier'])) {
+ $form['name']['#required'] = FALSE;
+ $form['pass']['#required'] = FALSE;
+ unset($form['#submit']);
+ $form['#validate'] = array('openid_login_validate');
+ }
+
+ $items = array();
+ $items[] = array(
+ 'data' => l(t('Log in using OpenID'), '#openid-login', array('external' => TRUE)),
+ 'class' => array('openid-link'),
+ );
+ $items[] = array(
+ 'data' => l(t('Cancel OpenID login'), '#', array('external' => TRUE)),
+ 'class' => array('user-link'),
+ );
+
+ $form['openid_links'] = array(
+ '#theme' => 'item_list',
+ '#items' => $items,
+ '#attributes' => array('class' => array('openid-links')),
+ '#weight' => 1,
+ );
+
+ $form['links']['#weight'] = 2;
+
+ $form['openid_identifier'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Log in using OpenID'),
+ '#size' => $form['name']['#size'],
+ '#maxlength' => 255,
+ '#weight' => -1,
+ '#description' => l(t('What is OpenID?'), 'http://openid.net/', array('external' => TRUE)),
+ );
+ $form['openid.return_to'] = array('#type' => 'hidden', '#value' => url('openid/authenticate', array('absolute' => TRUE, 'query' => user_login_destination())));
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ *
+ * Prefills the login form with values acquired via OpenID.
+ */
+function openid_form_user_register_form_alter(&$form, &$form_state) {
+ if (isset($_SESSION['openid']['response'])) {
+ module_load_include('inc', 'openid');
+
+ $response = $_SESSION['openid']['response'];
+
+ // Extract Simple Registration keys from the response. We only include
+ // signed keys as required by OpenID Simple Registration Extension 1.0,
+ // section 4.
+ $sreg_values = openid_extract_namespace($response, OPENID_NS_SREG, 'sreg', TRUE);
+ // Extract Attribute Exchanges keys from the response. We only include
+ // signed keys. This is not required by the specification, but it is
+ // recommended by Google, see
+ // http://googlecode.blogspot.com/2011/05/security-advisory-to-websites-using.html
+ $ax_values = openid_extract_namespace($response, OPENID_NS_AX, 'ax', TRUE);
+
+ if (!empty($sreg_values['nickname'])) {
+ // Use the nickname returned by Simple Registration if available.
+ $form['account']['name']['#default_value'] = $sreg_values['nickname'];
+ }
+ elseif ($ax_name_values = openid_extract_ax_values($ax_values, array('http://axschema.org/namePerson/friendly', 'http://schema.openid.net/namePerson/friendly'))) {
+ // Else, use the first nickname returned by AX if available.
+ $form['account']['name']['#default_value'] = current($ax_name_values);
+ }
+ else {
+ $form['account']['name']['#default_value'] = '';
+ }
+
+ if (!empty($sreg_values['email'])) {
+ // Use the email returned by Simple Registration if available.
+ $form['account']['mail']['#default_value'] = $sreg_values['email'];
+ }
+ elseif ($ax_mail_values = openid_extract_ax_values($ax_values, array('http://axschema.org/contact/email', 'http://schema.openid.net/contact/email'))) {
+ // Else, use the first nickname returned by AX if available.
+ $form['account']['mail']['#default_value'] = current($ax_mail_values);
+ }
+
+ // If user_email_verification is off, hide the password field and just fill
+ // with random password to avoid confusion.
+ if (!variable_get('user_email_verification', TRUE)) {
+ $form['account']['pass']['#type'] = 'hidden';
+ $form['account']['pass']['#value'] = user_password();
+ }
+
+ $form['openid_claimed_id'] = array(
+ '#type' => 'value',
+ '#default_value' => $response['openid.claimed_id'],
+ );
+ $form['openid_display'] = array(
+ '#type' => 'item',
+ '#title' => t('Your OpenID'),
+ '#description' => t('This OpenID will be attached to your account after registration.'),
+ '#markup' => check_plain($response['openid.claimed_id']),
+ );
+ }
+}
+
+/**
+ * Login form _validate hook
+ */
+function openid_login_validate($form, &$form_state) {
+ $return_to = $form_state['values']['openid.return_to'];
+ if (empty($return_to)) {
+ $return_to = url('', array('absolute' => TRUE));
+ }
+
+ openid_begin($form_state['values']['openid_identifier'], $return_to, $form_state['values']);
+}
+
+/**
+ * The initial step of OpenID authentication responsible for the following:
+ * - Perform discovery on the claimed OpenID.
+ * - If possible, create an association with the Provider's endpoint.
+ * - Create the authentication request.
+ * - Perform the appropriate redirect.
+ *
+ * @param $claimed_id The OpenID to authenticate
+ * @param $return_to The endpoint to return to from the OpenID Provider
+ */
+function openid_begin($claimed_id, $return_to = '', $form_values = array()) {
+ module_load_include('inc', 'openid');
+
+ $service = NULL;
+ $claimed_id = openid_normalize($claimed_id);
+ $discovery = openid_discovery($claimed_id);
+
+ if (!empty($discovery['services'])) {
+ $service = _openid_select_service($discovery['services']);
+ }
+
+ // Quit if the discovery result was empty or if we can't select any service.
+ if (!$discovery || !$service) {
+ form_set_error('openid_identifier', t('Sorry, that is not a valid OpenID. Ensure you have spelled your ID correctly.'));
+ return;
+ }
+
+ // Set claimed id from discovery.
+ if (!empty($discovery['claimed_id'])) {
+ $claimed_id = $discovery['claimed_id'];
+ }
+
+ // Store discovered information in the users' session so we don't have to rediscover.
+ $_SESSION['openid']['service'] = $service;
+ // Store the claimed id
+ $_SESSION['openid']['claimed_id'] = $claimed_id;
+ // Store the login form values so we can pass them to
+ // user_exteral_login later.
+ $_SESSION['openid']['user_login_values'] = $form_values;
+
+ // If a supported math library is present, then create an association.
+ $assoc_handle = '';
+ if (_openid_get_math_library()) {
+ $assoc_handle = openid_association($service['uri']);
+ }
+
+ if (in_array('http://specs.openid.net/auth/2.0/server', $service['types'])) {
+ // User entered an OP Identifier.
+ $claimed_id = $identity = 'http://specs.openid.net/auth/2.0/identifier_select';
+ }
+ else {
+ // Use Claimed ID and/or OP-Local Identifier from service description, if
+ // available.
+ if (!empty($service['claimed_id'])) {
+ $claimed_id = $service['claimed_id'];
+ }
+ $identity = !empty($service['identity']) ? $service['identity'] : $claimed_id;
+ }
+ $request = openid_authentication_request($claimed_id, $identity, $return_to, $assoc_handle, $service);
+
+ if ($service['version'] == 2) {
+ openid_redirect($service['uri'], $request);
+ }
+ else {
+ openid_redirect_http($service['uri'], $request);
+ }
+}
+
+/**
+ * Completes OpenID authentication by validating returned data from the OpenID
+ * Provider.
+ *
+ * @param $response Array of returned values from the OpenID Provider.
+ *
+ * @return $response Response values for further processing with
+ * $response['status'] set to one of 'success', 'failed' or 'cancel'.
+ */
+function openid_complete($response = array()) {
+ module_load_include('inc', 'openid');
+
+ if (count($response) == 0) {
+ $response = _openid_response();
+ }
+
+ // Default to failed response
+ $response['status'] = 'failed';
+ if (isset($_SESSION['openid']['service']['uri']) && isset($_SESSION['openid']['claimed_id'])) {
+ $service = $_SESSION['openid']['service'];
+ $claimed_id = $_SESSION['openid']['claimed_id'];
+ unset($_SESSION['openid']['service']);
+ unset($_SESSION['openid']['claimed_id']);
+ if (isset($response['openid.mode'])) {
+ if ($response['openid.mode'] == 'cancel') {
+ $response['status'] = 'cancel';
+ }
+ else {
+ if (openid_verify_assertion($service, $response)) {
+ // OpenID Authentication, section 7.3.2.3 and Appendix A.5:
+ // The CanonicalID specified in the XRDS document must be used as the
+ // account key. We rely on the XRI proxy resolver to verify that the
+ // provider is authorized to respond on behalf of the specified
+ // identifer (required per Extensible Resource Identifier (XRI)
+ // (XRI) Resolution Version 2.0, section 14.3):
+ if (!empty($service['claimed_id'])) {
+ $response['openid.claimed_id'] = $service['claimed_id'];
+ }
+ elseif ($service['version'] == 2) {
+ // Returned Claimed Identifier could contain unique fragment
+ // identifier to allow identifier recycling so we need to preserve
+ // it in the response.
+ $response_claimed_id = openid_normalize($response['openid.claimed_id']);
+
+ // OpenID Authentication, section 11.2:
+ // If the returned Claimed Identifier is different from the one sent
+ // to the OpenID Provider, we need to do discovery on the returned
+ // identififer to make sure that the provider is authorized to
+ // respond on behalf of this.
+ if ($response_claimed_id != $claimed_id) {
+ $discovery = openid_discovery($response['openid.claimed_id']);
+ if ($discovery && !empty($discovery['services'])) {
+ $uris = array();
+ foreach ($discovery['services'] as $discovered_service) {
+ if (in_array('http://specs.openid.net/auth/2.0/server', $discovered_service['types']) || in_array('http://specs.openid.net/auth/2.0/signon', $discovered_service['types'])) {
+ $uris[] = $discovered_service['uri'];
+ }
+ }
+ }
+ if (!in_array($service['uri'], $uris)) {
+ return $response;
+ }
+ }
+ }
+ else {
+ $response['openid.claimed_id'] = $claimed_id;
+ }
+ $response['status'] = 'success';
+ }
+ }
+ }
+ }
+ return $response;
+}
+
+/**
+ * Perform discovery on a claimed ID to determine the OpenID provider endpoint.
+ *
+ * Discovery methods are provided by the hook_openid_discovery_method_info and
+ * could be further altered using the hook_openid_discovery_method_info_alter.
+ *
+ * @param $claimed_id
+ * The OpenID URL to perform discovery on.
+ *
+ * @return
+ * The resulting discovery array from the first successful discovery method,
+ * which must contain following keys:
+ * - 'services' (required) an array of discovered services (including OpenID
+ * version, endpoint URI, etc).
+ * - 'claimed_id' (optional) new claimed identifer, found by following HTTP
+ * redirects during the services discovery.
+ * If all the discovery method fails or if no appropriate discovery method is
+ * found, FALSE is returned.
+ */
+function openid_discovery($claimed_id) {
+ module_load_include('inc', 'openid');
+
+ $methods = module_invoke_all('openid_discovery_method_info');
+ drupal_alter('openid_discovery_method_info', $methods);
+
+ // Execute each method in turn and return first successful discovery.
+ foreach ($methods as $method) {
+ $discovery = $method($claimed_id);
+ if (!empty($discovery)) {
+ return $discovery;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * Implements hook_openid_discovery_method_info().
+ *
+ * Define standard discovery methods.
+ */
+function openid_openid_discovery_method_info() {
+ // The discovery process will stop as soon as one discovery method succeed.
+ // We first attempt to discover XRI-based identifiers, then standard XRDS
+ // identifiers via Yadis and HTML-based discovery, conforming to the OpenID 2.0
+ // specification.
+ return array(
+ 'xri' => '_openid_xri_discovery',
+ 'xrds' => '_openid_xrds_discovery',
+ );
+}
+
+/**
+ * OpenID discovery method: perform an XRI discovery.
+ *
+ * @see http://openid.net/specs/openid-authentication-2_0.html#discovery
+ * @see hook_openid_discovery_method_info()
+ * @see openid_discovery()
+ *
+ * @return
+ * An array of discovered services and claimed identifier or NULL. See
+ * openid_discovery() for more specific information.
+ */
+function _openid_xri_discovery($claimed_id) {
+ if (_openid_is_xri($claimed_id)) {
+ // Resolve XRI using a proxy resolver (Extensible Resource Identifier (XRI)
+ // Resolution Version 2.0, section 11.2 and 14.3).
+ $xrds_url = variable_get('xri_proxy_resolver', 'http://xri.net/') . rawurlencode($claimed_id) . '?_xrd_r=application/xrds+xml';
+ $discovery = _openid_xrds_discovery($xrds_url);
+ if (!empty($discovery['services']) && is_array($discovery['services'])) {
+ foreach ($discovery['services'] as $i => &$service) {
+ $status = $service['xrd']->children(OPENID_NS_XRD)->Status;
+ if ($status && $status->attributes()->cid == 'verified') {
+ $service['claimed_id'] = openid_normalize((string)$service['xrd']->children(OPENID_NS_XRD)->CanonicalID);
+ }
+ else {
+ // Ignore service if the Canonical ID could not be verified.
+ unset($discovery['services'][$i]);
+ }
+ }
+ if (!empty($discovery['services'])) {
+ return $discovery;
+ }
+ }
+ }
+}
+
+/**
+ * OpenID discovery method: perform a XRDS discovery.
+ *
+ * @see http://openid.net/specs/openid-authentication-2_0.html#discovery
+ * @see hook_openid_discovery_method_info()
+ * @see openid_discovery()
+ *
+ * @return
+ * An array of discovered services and claimed identifier or NULL. See
+ * openid_discovery() for more specific information.
+ */
+function _openid_xrds_discovery($claimed_id) {
+ $services = array();
+
+ $xrds_url = $claimed_id;
+ $scheme = @parse_url($xrds_url, PHP_URL_SCHEME);
+ if ($scheme == 'http' || $scheme == 'https') {
+ // For regular URLs, try Yadis resolution first, then HTML-based discovery
+ $headers = array('Accept' => 'application/xrds+xml');
+ $result = drupal_http_request($xrds_url, array('headers' => $headers));
+
+ // Check for HTTP error and make sure, that we reach the target. If the
+ // maximum allowed redirects are exhausted, final destination URL isn't
+ // reached, but drupal_http_request() doesn't return any error.
+ // @todo Remove the check for 200 HTTP result code after the following issue
+ // will be fixed: http://drupal.org/node/1096890.
+ if (!isset($result->error) && $result->code == 200) {
+
+ // Replace the user-entered claimed_id if we received a redirect.
+ if (!empty($result->redirect_url)) {
+ $claimed_id = openid_normalize($result->redirect_url);
+ }
+
+ if (isset($result->headers['content-type']) && preg_match("/application\/xrds\+xml/", $result->headers['content-type'])) {
+ // Parse XML document to find URL
+ $services = _openid_xrds_parse($result->data);
+ }
+ else {
+ $xrds_url = NULL;
+ if (isset($result->headers['x-xrds-location'])) {
+ $xrds_url = $result->headers['x-xrds-location'];
+ }
+ else {
+ // Look for meta http-equiv link in HTML head
+ $xrds_url = _openid_meta_httpequiv('X-XRDS-Location', $result->data);
+ }
+ if (!empty($xrds_url)) {
+ $headers = array('Accept' => 'application/xrds+xml');
+ $xrds_result = drupal_http_request($xrds_url, array('headers' => $headers));
+ if (!isset($xrds_result->error)) {
+ $services = _openid_xrds_parse($xrds_result->data);
+ }
+ }
+ }
+
+ // Check for HTML delegation
+ if (count($services) == 0) {
+ // Look for 2.0 links
+ $uri = _openid_link_href('openid2.provider', $result->data);
+ $identity = _openid_link_href('openid2.local_id', $result->data);
+ $type = 'http://specs.openid.net/auth/2.0/signon';
+
+ // 1.x links
+ if (empty($uri)) {
+ $uri = _openid_link_href('openid.server', $result->data);
+ $identity = _openid_link_href('openid.delegate', $result->data);
+ $type = 'http://openid.net/signon/1.1';
+ }
+ if (!empty($uri)) {
+ $services[] = array(
+ 'uri' => $uri,
+ 'identity' => $identity,
+ 'types' => array($type),
+ );
+ }
+ }
+ }
+ }
+
+ if (!empty($services)) {
+ return array(
+ 'services' => $services,
+ 'claimed_id' => $claimed_id,
+ );
+ }
+}
+
+/**
+ * Implements hook_openid_normalization_method_info().
+ *
+ * Define standard normalization methods.
+ */
+function openid_openid_normalization_method_info() {
+ // OpenID Authentication 2.0, section 7.2:
+ // If the User-supplied Identifier looks like an XRI, treat it as such;
+ // otherwise treat it as an HTTP URL.
+ return array(
+ 'xri' => '_openid_xri_normalize',
+ 'url' => '_openid_url_normalize',
+ );
+}
+
+/**
+ * Attempt to create a shared secret with the OpenID Provider.
+ *
+ * @param $op_endpoint URL of the OpenID Provider endpoint.
+ *
+ * @return $assoc_handle The association handle.
+ */
+function openid_association($op_endpoint) {
+ module_load_include('inc', 'openid');
+
+ // Remove Old Associations:
+ db_delete('openid_association')
+ ->where('created + expires_in < :request_time', array(':request_time' => REQUEST_TIME))
+ ->execute();
+
+ // Check to see if we have an association for this IdP already
+ $assoc_handle = db_query("SELECT assoc_handle FROM {openid_association} WHERE idp_endpoint_uri = :endpoint", array(':endpoint' => $op_endpoint))->fetchField();
+ if (empty($assoc_handle)) {
+ $mod = OPENID_DH_DEFAULT_MOD;
+ $gen = OPENID_DH_DEFAULT_GEN;
+ $r = _openid_dh_rand($mod);
+ $private = _openid_math_add($r, 1);
+ $public = _openid_math_powmod($gen, $private, $mod);
+
+ // If there is no existing association, then request one
+ $assoc_request = openid_association_request($public);
+ $assoc_message = _openid_encode_message(_openid_create_message($assoc_request));
+ $assoc_options = array(
+ 'headers' => array('Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8'),
+ 'method' => 'POST',
+ 'data' => $assoc_message,
+ );
+ $assoc_result = drupal_http_request($op_endpoint, $assoc_options);
+ if (isset($assoc_result->error)) {
+ return FALSE;
+ }
+
+ $assoc_response = _openid_parse_message($assoc_result->data);
+ if (isset($assoc_response['mode']) && $assoc_response['mode'] == 'error') {
+ return FALSE;
+ }
+
+ if ($assoc_response['session_type'] == 'DH-SHA1') {
+ $spub = _openid_dh_base64_to_long($assoc_response['dh_server_public']);
+ $enc_mac_key = base64_decode($assoc_response['enc_mac_key']);
+ $shared = _openid_math_powmod($spub, $private, $mod);
+ $assoc_response['mac_key'] = base64_encode(_openid_dh_xorsecret($shared, $enc_mac_key));
+ }
+ db_insert('openid_association')
+ ->fields(array(
+ 'idp_endpoint_uri' => $op_endpoint,
+ 'session_type' => $assoc_response['session_type'],
+ 'assoc_handle' => $assoc_response['assoc_handle'],
+ 'assoc_type' => $assoc_response['assoc_type'],
+ 'expires_in' => $assoc_response['expires_in'],
+ 'mac_key' => $assoc_response['mac_key'],
+ 'created' => REQUEST_TIME,
+ ))
+ ->execute();
+ $assoc_handle = $assoc_response['assoc_handle'];
+ }
+ return $assoc_handle;
+}
+
+/**
+ * Authenticate a user or attempt registration.
+ *
+ * @param $response Response values from the OpenID Provider.
+ */
+function openid_authentication($response) {
+ $identity = $response['openid.claimed_id'];
+ $account = user_external_load($identity);
+
+ // Tries to load user account if user_external_load fails due to possibly
+ // incompletely stored OpenID identifier in the authmap.
+ if (!isset($account->uid) && variable_get('openid_less_obtrusive_transition', FALSE)) {
+ module_load_include('inc', 'openid');
+ $account = _openid_invalid_openid_transition($identity, $response);
+ }
+
+ if (isset($account->uid)) {
+ if (!variable_get('user_email_verification', TRUE) || $account->login) {
+ // Check if user is blocked.
+ $state['values']['name'] = $account->name;
+ user_login_name_validate(array(), $state);
+ if (!form_get_errors()) {
+ // Load global $user and perform final login tasks.
+ $form_state['uid'] = $account->uid;
+ user_login_submit(array(), $form_state);
+ // Let other modules act on OpenID login
+ module_invoke_all('openid_response', $response, $account);
+ }
+ }
+ else {
+ drupal_set_message(t('You must validate your email address for this account before logging in via OpenID.'));
+ }
+ }
+ elseif (variable_get('user_register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL)) {
+ // Register new user.
+
+ // Save response for use in openid_form_user_register_form_alter().
+ $_SESSION['openid']['response'] = $response;
+
+ $form_state['values'] = array();
+ $form_state['values']['op'] = t('Create new account');
+ drupal_form_submit('user_register_form', $form_state);
+
+ if (!empty($form_state['user'])) {
+ module_invoke_all('openid_response', $response, $form_state['user']);
+ drupal_goto();
+ }
+
+ $messages = drupal_get_messages('error');
+ if (empty($form_state['values']['name']) || empty($form_state['values']['mail'])) {
+ // If the OpenID provider did not provide both a user name and an email
+ // address, ask the user to complete the registration manually instead of
+ // showing the error messages about the missing values generated by FAPI.
+ drupal_set_message(t('Complete the registration by filling out the form below. If you already have an account, you can <a href="@login">log in</a> now and add your OpenID under "My account".', array('@login' => url('user/login'))), 'warning');
+ }
+ else {
+ drupal_set_message(t('Account registration using the information provided by your OpenID provider failed due to the reasons listed below. Complete the registration by filling out the form below. If you already have an account, you can <a href="@login">log in</a> now and add your OpenID under "My account".', array('@login' => url('user/login'))), 'warning');
+ // Append form validation errors below the above warning.
+ foreach ($messages['error'] as $message) {
+ drupal_set_message($message, 'error');
+ }
+ }
+
+ // We were unable to register a valid new user. Redirect to the normal
+ // registration page and prefill with the values we received.
+ $destination = drupal_get_destination();
+ unset($_GET['destination']);
+ drupal_goto('user/register', array('query' => $destination));
+ }
+ else {
+ drupal_set_message(t('Only site administrators can create new user accounts.'), 'error');
+ }
+ drupal_goto();
+}
+
+function openid_association_request($public) {
+ module_load_include('inc', 'openid');
+
+ $request = array(
+ 'openid.ns' => OPENID_NS_2_0,
+ 'openid.mode' => 'associate',
+ 'openid.session_type' => 'DH-SHA1',
+ 'openid.assoc_type' => 'HMAC-SHA1'
+ );
+
+ if ($request['openid.session_type'] == 'DH-SHA1' || $request['openid.session_type'] == 'DH-SHA256') {
+ $cpub = _openid_dh_long_to_base64($public);
+ $request['openid.dh_consumer_public'] = $cpub;
+ }
+
+ return $request;
+}
+
+function openid_authentication_request($claimed_id, $identity, $return_to = '', $assoc_handle = '', $service) {
+ global $base_url;
+
+ module_load_include('inc', 'openid');
+
+ $request = array(
+ 'openid.mode' => 'checkid_setup',
+ 'openid.identity' => $identity,
+ 'openid.assoc_handle' => $assoc_handle,
+ 'openid.return_to' => $return_to,
+ );
+
+ if ($service['version'] == 2) {
+ $request['openid.ns'] = OPENID_NS_2_0;
+ $request['openid.claimed_id'] = $claimed_id;
+ $request['openid.realm'] = $base_url .'/';
+ }
+ else {
+ $request['openid.trust_root'] = $base_url .'/';
+ }
+
+ // Always request Simple Registration. The specification doesn't mandate
+ // that the Endpoint advertise OPENID_NS_SREG in the service description.
+ $request['openid.ns.sreg'] = OPENID_NS_SREG;
+ $request['openid.sreg.required'] = 'nickname,email';
+
+ // Request Attribute Exchange, if available.
+ // We only request the minimum attributes we need here, contributed modules
+ // can alter the request to add more attribute, and map them to profile fields.
+ if (in_array(OPENID_NS_AX, $service['types'])) {
+ $request['openid.ns.ax'] = OPENID_NS_AX;
+ $request['openid.ax.mode'] = 'fetch_request';
+ $request['openid.ax.required'] = 'mail_ao,name_ao,mail_son,name_son';
+
+ // Implementors disagree on which URIs to use, even for simple
+ // attributes like name and email (*sigh*). We ask for both axschema.org
+ // attributes (which are supposed to be newer), and schema.openid.net ones
+ // (which are supposed to be legacy).
+
+ // Attributes as defined by axschema.org.
+ $request['openid.ax.type.mail_ao'] = 'http://axschema.org/contact/email';
+ $request['openid.ax.type.name_ao'] = 'http://axschema.org/namePerson/friendly';
+
+ // Attributes as defined by schema.openid.net.
+ $request['openid.ax.type.mail_son'] = 'http://schema.openid.net/contact/email';
+ $request['openid.ax.type.name_son'] = 'http://schema.openid.net/namePerson/friendly';
+ }
+
+ $request = array_merge($request, module_invoke_all('openid', 'request', $request));
+
+ // module_invoke_all() uses array_merge_recursive() which might return nested
+ // arrays if two or more modules alter a given parameter, resulting in an
+ // invalid request format. To ensure this doesn't happen, we flatten the returned
+ // value by taking the last entry in the array if an array is returned.
+ $flattened_request = array();
+ foreach ($request as $key => $value) {
+ if (is_array($value)) {
+ $flattened_request[$key] = end($value);
+ }
+ else {
+ $flattened_request[$key] = $value;
+ }
+ }
+
+ return $flattened_request;
+}
+
+/**
+ * Attempt to verify the response received from the OpenID Provider.
+ *
+ * @param $service
+ * Array describing the OpenID provider.
+ * @param $response
+ * Array of response values from the provider.
+ *
+ * @return boolean
+ * @see http://openid.net/specs/openid-authentication-2_0.html#rfc.section.11.4
+ */
+function openid_verify_assertion($service, $response) {
+ module_load_include('inc', 'openid');
+
+ // http://openid.net/specs/openid-authentication-2_0.html#rfc.section.11.3
+ // Check the Nonce to protect against replay attacks.
+ if (!openid_verify_assertion_nonce($service, $response)) {
+ return FALSE;
+ }
+
+ // http://openid.net/specs/openid-authentication-2_0.html#rfc.section.11.1
+ // Verifying the return URL.
+ if (!openid_verify_assertion_return_url($service, $response)) {
+ return FALSE;
+ }
+
+ // http://openid.net/specs/openid-authentication-2_0.html#rfc.section.11.4
+ // Verify the signatures.
+ $valid = FALSE;
+ $association = FALSE;
+
+ // If the OP returned a openid.invalidate_handle, we have to proceed with
+ // direct verification: ignore the openid.assoc_handle, even if present.
+ // See http://openid.net/specs/openid-authentication-2_0.html#rfc.section.11.4.1
+ if (!empty($response['openid.assoc_handle']) && empty($response['openid.invalidate_handle'])) {
+ $association = db_query("SELECT * FROM {openid_association} WHERE idp_endpoint_uri = :endpoint AND assoc_handle = :assoc_handle", array(':endpoint' => $service['uri'], ':assoc_handle' => $response['openid.assoc_handle']))->fetchObject();
+ }
+
+ if ($association && isset($association->session_type)) {
+ // http://openid.net/specs/openid-authentication-2_0.html#rfc.section.11.4.2
+ // Verification using an association.
+ $valid = openid_verify_assertion_signature($service, $association, $response);
+ }
+ else {
+ // http://openid.net/specs/openid-authentication-2_0.html#rfc.section.11.4.2
+ // Direct verification.
+ // The verification requests contain all the fields from the response,
+ // except openid.mode.
+ $request = $response;
+ $request['openid.mode'] = 'check_authentication';
+ $message = _openid_create_message($request);
+ $options = array(
+ 'headers' => array('Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8'),
+ 'method' => 'POST',
+ 'data' => _openid_encode_message($message),
+ );
+ $result = drupal_http_request($service['uri'], $options);
+ if (!isset($result->error)) {
+ $response = _openid_parse_message($result->data);
+
+ if (strtolower(trim($response['is_valid'])) == 'true') {
+ $valid = TRUE;
+ if (!empty($response['invalidate_handle'])) {
+ // This association handle has expired on the OP side, remove it from the
+ // database to avoid reusing it again on a subsequent authentication request.
+ // See http://openid.net/specs/openid-authentication-2_0.html#rfc.section.11.4.2.2
+ db_delete('openid_association')
+ ->condition('idp_endpoint_uri', $service['uri'])
+ ->condition('assoc_handle', $response['invalidate_handle'])
+ ->execute();
+ }
+ }
+ else {
+ $valid = FALSE;
+ }
+ }
+ }
+ return $valid;
+}
+
+
+/**
+ * Verify the signature of the response received from the OpenID provider.
+ *
+ * @param $service
+ * Array describing the OpenID provider.
+ * @param $association
+ * Information on the association with the OpenID provider.
+ * @param $response
+ * Array of response values from the provider.
+ *
+ * @return
+ * TRUE if the signature is valid and covers all fields required to be signed.
+ * @see http://openid.net/specs/openid-authentication-2_0.html#rfc.section.11.4
+ */
+function openid_verify_assertion_signature($service, $association, $response) {
+ if ($service['version'] == 2) {
+ // OpenID Authentication 2.0, section 10.1:
+ // These keys must always be signed.
+ $mandatory_keys = array('op_endpoint', 'return_to', 'response_nonce', 'assoc_handle');
+ if (isset($response['openid.claimed_id'])) {
+ // If present, these two keys must also be signed. According to the spec,
+ // they are either both present or both absent.
+ $mandatory_keys[] = 'claimed_id';
+ $mandatory_keys[] = 'identity';
+ }
+ }
+ else {
+ // OpenID Authentication 1.1. section 4.3.3.
+ $mandatory_keys = array('identity', 'return_to');
+ }
+
+ $keys_to_sign = explode(',', $response['openid.signed']);
+
+ if (count(array_diff($mandatory_keys, $keys_to_sign)) > 0) {
+ return FALSE;
+ }
+
+ return _openid_signature($association, $response, $keys_to_sign) === $response['openid.sig'];
+}
+
+/**
+ * Verify that the nonce has not been used in earlier assertions from the same OpenID provider.
+ *
+ * @param $service
+ * Array describing the OpenID provider.
+ * @param $response
+ * Array of response values from the provider.
+ *
+ * @return
+ * TRUE if the nonce has not expired and has not been used earlier.
+ */
+function openid_verify_assertion_nonce($service, $response) {
+ if ($service['version'] != 2) {
+ return TRUE;
+ }
+
+ if (preg_match('/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z/', $response['openid.response_nonce'], $matches)) {
+ list(, $year, $month, $day, $hour, $minutes, $seconds) = $matches;
+ $nonce_timestamp = gmmktime($hour, $minutes, $seconds, $month, $day, $year);
+ }
+ else {
+ watchdog('openid', 'Nonce from @endpoint rejected because it is not correctly formatted, nonce: @nonce.', array('@endpoint' => $service['uri'], '@nonce' => $response['openid.response_nonce']), WATCHDOG_WARNING);
+ return FALSE;
+ }
+
+ // A nonce with a timestamp to far in the past or future will already have
+ // been removed and cannot be checked for single use anymore.
+ $time = time();
+ $expiry = 900;
+ if ($nonce_timestamp <= $time - $expiry || $nonce_timestamp >= $time + $expiry) {
+ watchdog('openid', 'Nonce received from @endpoint is out of range (time difference: @intervals). Check possible clock skew.', array('@endpoint' => $service['uri'], '@interval' => $time - $nonce_timestamp), WATCHDOG_WARNING);
+ return FALSE;
+ }
+
+ // Record that this nonce was used.
+ db_insert('openid_nonce')
+ ->fields(array(
+ 'idp_endpoint_uri' => $service['uri'],
+ 'nonce' => $response['openid.response_nonce'],
+ 'expires' => $nonce_timestamp + $expiry,
+ ))
+ ->execute();
+
+ // Count the number of times this nonce was used.
+ $count_used = db_query("SELECT COUNT(*) FROM {openid_nonce} WHERE nonce = :nonce AND idp_endpoint_uri = :idp_endpoint_uri", array(
+ ':nonce' => $response['openid.response_nonce'],
+ ':idp_endpoint_uri' => $service['uri'],
+ ))->fetchField();
+
+ if ($count_used == 1) {
+ return TRUE;
+ }
+ else {
+ watchdog('openid', 'Nonce replay attempt blocked from @ip, nonce: @nonce.', array('@ip' => ip_address(), '@nonce' => $response['openid.response_nonce']), WATCHDOG_CRITICAL);
+ return FALSE;
+ }
+}
+
+
+/**
+ * Verify that openid.return_to matches the current URL.
+ *
+ * See OpenID Authentication 2.0, section 11.1. While OpenID Authentication
+ * 1.1, section 4.3 does not mandate return_to verification, the received
+ * return_to should still match these constraints.
+ *
+ * @param $service
+ * Array describing the OpenID provider.
+ * @param $response
+ * Array of response values from the provider.
+ *
+ * @return
+ * TRUE if return_to is valid, FALSE otherwise.
+ */
+function openid_verify_assertion_return_url($service, $response) {
+ global $base_url;
+
+ $return_to_parts = parse_url($response['openid.return_to']);
+
+ $base_url_parts = parse_url($base_url);
+ $current_parts = parse_url($base_url_parts['scheme'] .'://'. $base_url_parts['host'] . request_uri());
+
+ if ($return_to_parts['scheme'] != $current_parts['scheme'] || $return_to_parts['host'] != $current_parts['host'] || $return_to_parts['path'] != $current_parts['path']) {
+ return FALSE;
+ }
+ // Verify that all query parameters in the openid.return_to URL have
+ // the same value in the current URL. In addition, the current URL
+ // contains a number of other parameters added by the OpenID Provider.
+ parse_str(isset($return_to_parts['query']) ? $return_to_parts['query'] : '', $return_to_query_parameters);
+ foreach ($return_to_query_parameters as $name => $value) {
+ if (!isset($_GET[$name]) || $_GET[$name] != $value) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/**
+ * Remove expired nonces from the database.
+ *
+ * Implements hook_cron().
+ */
+function openid_cron() {
+ db_delete('openid_nonce')
+ ->condition('expires', REQUEST_TIME, '<')
+ ->execute();
+}
diff --git a/kolab.org/www/drupal-7.26/modules/openid/openid.pages.inc b/kolab.org/www/drupal-7.26/modules/openid/openid.pages.inc
new file mode 100644
index 0000000..8a52f20
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/openid/openid.pages.inc
@@ -0,0 +1,116 @@
+<?php
+
+/**
+ * @file
+ * User page callbacks for the openid module.
+ */
+
+/**
+ * Menu callback; Process an OpenID authentication.
+ */
+function openid_authentication_page() {
+ $result = openid_complete();
+ switch ($result['status']) {
+ case 'success':
+ return openid_authentication($result);
+ case 'failed':
+ drupal_set_message(t('OpenID login failed.'), 'error');
+ break;
+ case 'cancel':
+ drupal_set_message(t('OpenID login cancelled.'));
+ break;
+ }
+ drupal_goto();
+}
+
+/**
+ * Menu callback; Manage OpenID identities for the specified user.
+ */
+function openid_user_identities($account) {
+ drupal_set_title(format_username($account));
+ drupal_add_css(drupal_get_path('module', 'openid') . '/openid.css');
+
+ // Check to see if we got a response
+ $result = openid_complete();
+ if ($result['status'] == 'success') {
+ $identity = $result['openid.claimed_id'];
+ $query = db_insert('authmap')
+ ->fields(array(
+ 'uid' => $account->uid,
+ 'authname' => $identity,
+ 'module' => 'openid',
+ ))
+ ->execute();
+ drupal_set_message(t('Successfully added %identity', array('%identity' => $identity)));
+ }
+
+ $header = array(t('OpenID'), t('Operations'));
+ $rows = array();
+
+ $result = db_query("SELECT * FROM {authmap} WHERE module='openid' AND uid=:uid", array(':uid' => $account->uid));
+ foreach ($result as $identity) {
+ $rows[] = array(check_plain($identity->authname), l(t('Delete'), 'user/' . $account->uid . '/openid/delete/' . $identity->aid));
+ }
+
+ $build['openid_table'] = array(
+ '#theme' => 'table',
+ '#header' => $header,
+ '#rows' => $rows,
+ '#empty' => t('No OpenID identities available for this account.'),
+ );
+ $build['openid_user_add'] = drupal_get_form('openid_user_add');
+ return $build;
+}
+
+/**
+ * Form builder; Add an OpenID identity.
+ *
+ * @ingroup forms
+ * @see openid_user_add_validate()
+ */
+function openid_user_add() {
+ $form['openid_identifier'] = array(
+ '#type' => 'textfield',
+ '#title' => t('OpenID'),
+ );
+ $form['actions'] = array('#type' => 'actions');
+ $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Add an OpenID'));
+ return $form;
+}
+
+function openid_user_add_validate($form, &$form_state) {
+ // Check for existing entries.
+ $claimed_id = openid_normalize($form_state['values']['openid_identifier']);
+ if (db_query("SELECT authname FROM {authmap} WHERE authname = :authname", (array(':authname' => $claimed_id)))->fetchField()) {
+ form_set_error('openid_identifier', t('That OpenID is already in use on this site.'));
+ }
+}
+
+function openid_user_add_submit($form, &$form_state) {
+ $return_to = url('user/' . arg(1) . '/openid', array('absolute' => TRUE));
+ openid_begin($form_state['values']['openid_identifier'], $return_to);
+}
+
+/**
+ * Menu callback; Delete the specified OpenID identity from the system.
+ */
+function openid_user_delete_form($form, $form_state, $account, $aid = 0) {
+ $authname = db_query("SELECT authname FROM {authmap} WHERE uid = :uid AND aid = :aid AND module = 'openid'", array(
+ ':uid' => $account->uid,
+ ':aid' => $aid,
+ ))
+ ->fetchField();
+ return confirm_form(array(), t('Are you sure you want to delete the OpenID %authname for %user?', array('%authname' => $authname, '%user' => $account->name)), 'user/' . $account->uid . '/openid');
+}
+
+function openid_user_delete_form_submit($form, &$form_state) {
+ $query = db_delete('authmap')
+ ->condition('uid', $form_state['build_info']['args'][0]->uid)
+ ->condition('aid', $form_state['build_info']['args'][1])
+ ->condition('module', 'openid')
+ ->execute();
+ if ($query) {
+ drupal_set_message(t('OpenID deleted.'));
+ }
+ $form_state['redirect'] = 'user/' . $form_state['build_info']['args'][0]->uid . '/openid';
+}
diff --git a/kolab.org/www/drupal-7.26/modules/openid/openid.test b/kolab.org/www/drupal-7.26/modules/openid/openid.test
new file mode 100644
index 0000000..41af3f8
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/openid/openid.test
@@ -0,0 +1,802 @@
+<?php
+
+/**
+ * @file
+ * Tests for openid.module.
+ */
+
+/**
+ * Base class for OpenID tests.
+ */
+abstract class OpenIDWebTestCase extends DrupalWebTestCase {
+
+ /**
+ * Initiates the login procedure using the specified User-supplied Identity.
+ */
+ function submitLoginForm($identity) {
+ // Fill out and submit the login form.
+ $edit = array('openid_identifier' => $identity);
+ $this->drupalPost('', $edit, t('Log in'));
+
+ // Check we are on the OpenID redirect form.
+ $this->assertTitle(t('OpenID redirect'), 'OpenID redirect page was displayed.');
+
+ // Submit form to the OpenID Provider Endpoint.
+ $this->drupalPost(NULL, array(), t('Send'));
+ }
+
+ /**
+ * Parses the last sent e-mail and returns the one-time login link URL.
+ */
+ function getPasswordResetURLFromMail() {
+ $mails = $this->drupalGetMails();
+ $mail = end($mails);
+ preg_match('@.+user/reset/.+@', $mail['body'], $matches);
+ return $matches[0];
+ }
+}
+
+/**
+ * Test discovery and login using OpenID
+ */
+class OpenIDFunctionalTestCase extends OpenIDWebTestCase {
+ protected $web_user;
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'OpenID discovery and login',
+ 'description' => "Adds an identity to a user's profile and uses it to log in.",
+ 'group' => 'OpenID'
+ );
+ }
+
+ function setUp() {
+ parent::setUp('openid', 'openid_test');
+
+ // User doesn't need special permissions; only the ability to log in.
+ $this->web_user = $this->drupalCreateUser(array());
+ }
+
+ /**
+ * Test discovery of OpenID Provider Endpoint via Yadis and HTML.
+ */
+ function testDiscovery() {
+ $this->drupalLogin($this->web_user);
+
+ // The User-supplied Identifier entered by the user may indicate the URL of
+ // the OpenID Provider Endpoint in various ways, as described in OpenID
+ // Authentication 2.0 and Yadis Specification 1.0.
+ // Note that all of the tested identifiers refer to the same endpoint, so
+ // only the first will trigger an associate request in openid_association()
+ // (association is only done the first time Drupal encounters a given
+ // endpoint).
+
+
+ // Yadis discovery (see Yadis Specification 1.0, section 6.2.5):
+ // If the User-supplied Identifier is a URL, it may be a direct or indirect
+ // reference to an XRDS document (a Yadis Resource Descriptor) that contains
+ // the URL of the OpenID Provider Endpoint.
+
+ // Identifier is the URL of an XRDS document.
+ // On HTTP test environments, the URL scheme is stripped in order to test
+ // that the supplied identifier is normalized in openid_begin().
+ $identity = url('openid-test/yadis/xrds', array('absolute' => TRUE));
+ $this->addIdentity(preg_replace('@^http://@', '', $identity), 2, 'http://example.com/xrds', $identity);
+
+ $identity = url('openid-test/yadis/xrds/delegate', array('absolute' => TRUE));
+ $this->addIdentity(preg_replace('@^http://@', '', $identity), 2, 'http://example.com/xrds-delegate', $identity);
+
+ // Identifier is the URL of an XRDS document containing an OP Identifier
+ // Element. The Relying Party sends the special value
+ // "http://specs.openid.net/auth/2.0/identifier_select" as Claimed
+ // Identifier. The OpenID Provider responds with the actual identifier
+ // including the fragment.
+ $identity = url('openid-test/yadis/xrds/dummy-user', array('absolute' => TRUE, 'fragment' => $this->randomName()));
+ // Tell openid_test.module to respond with this identifier. If the fragment
+ // part is present in the identifier, it should be retained.
+ variable_set('openid_test_response', array('openid.claimed_id' => $identity));
+ $this->addIdentity(url('openid-test/yadis/xrds/server', array('absolute' => TRUE)), 2, 'http://specs.openid.net/auth/2.0/identifier_select', $identity);
+ variable_set('openid_test_response', array());
+
+ // Identifier is the URL of an HTML page that is sent with an HTTP header
+ // that contains the URL of an XRDS document.
+ $this->addIdentity(url('openid-test/yadis/x-xrds-location', array('absolute' => TRUE)), 2);
+
+ // Identifier is the URL of an HTML page containing a <meta http-equiv=...>
+ // element that contains the URL of an XRDS document.
+ $this->addIdentity(url('openid-test/yadis/http-equiv', array('absolute' => TRUE)), 2);
+
+ // Identifier is an XRI. Resolve using our own dummy proxy resolver.
+ variable_set('xri_proxy_resolver', url('openid-test/yadis/xrds/xri', array('absolute' => TRUE)) . '/');
+ $this->addIdentity('@example*résumé;%25', 2, 'http://example.com/xrds', 'http://example.com/user');
+
+ // Make sure that unverified CanonicalID are not trusted.
+ variable_set('openid_test_canonical_id_status', 'bad value');
+ $this->addIdentity('@example*résumé;%25', 2, FALSE, FALSE);
+
+ // HTML-based discovery:
+ // If the User-supplied Identifier is a URL of an HTML page, the page may
+ // contain a <link rel=...> element containing the URL of the OpenID
+ // Provider Endpoint. OpenID 1 and 2 describe slightly different formats.
+
+ // OpenID Authentication 1.1, section 3.1:
+ $this->addIdentity(url('openid-test/html/openid1', array('absolute' => TRUE)), 1, 'http://example.com/html-openid1');
+
+ // OpenID Authentication 2.0, section 7.3.3:
+ $this->addIdentity(url('openid-test/html/openid2', array('absolute' => TRUE)), 2, 'http://example.com/html-openid2');
+
+ // OpenID Authentication 2.0, section 7.2.4:
+ // URL Identifiers MUST then be further normalized by both (1) following
+ // redirects when retrieving their content and finally (2) applying the
+ // rules in Section 6 of RFC3986 to the final destination URL. This final
+ // URL MUST be noted by the Relying Party as the Claimed Identifier and be
+ // used when requesting authentication.
+
+ // Single redirect.
+ $identity = $expected_claimed_id = url('openid-test/redirected/yadis/xrds/1', array('absolute' => TRUE));
+ $this->addRedirectedIdentity($identity, 2, 'http://example.com/xrds', $expected_claimed_id, 0);
+
+ // Exact 3 redirects (default value for the 'max_redirects' option in
+ // drupal_http_request()).
+ $identity = $expected_claimed_id = url('openid-test/redirected/yadis/xrds/2', array('absolute' => TRUE));
+ $this->addRedirectedIdentity($identity, 2, 'http://example.com/xrds', $expected_claimed_id, 2);
+
+ // Fails because there are more than 3 redirects (default value for the
+ // 'max_redirects' option in drupal_http_request()).
+ $identity = url('openid-test/redirected/yadis/xrds/3', array('absolute' => TRUE));
+ $expected_claimed_id = FALSE;
+ $this->addRedirectedIdentity($identity, 2, 'http://example.com/xrds', $expected_claimed_id, 3);
+ }
+
+ /**
+ * Test login using OpenID.
+ */
+ function testLogin() {
+ $this->drupalLogin($this->web_user);
+
+ // Use a User-supplied Identity that is the URL of an XRDS document.
+ $identity = url('openid-test/yadis/xrds', array('absolute' => TRUE));
+ $this->addIdentity($identity);
+
+ $this->drupalLogout();
+
+ // Test logging in via the login block on the front page.
+ $this->submitLoginForm($identity);
+ $this->assertLink(t('Log out'), 0, 'User was logged in.');
+
+ $this->drupalLogout();
+
+ // Test logging in via the user/login page.
+ $edit = array('openid_identifier' => $identity);
+ $this->drupalPost('user/login', $edit, t('Log in'));
+
+ // Check we are on the OpenID redirect form.
+ $this->assertTitle(t('OpenID redirect'), 'OpenID redirect page was displayed.');
+
+ // Submit form to the OpenID Provider Endpoint.
+ $this->drupalPost(NULL, array(), t('Send'));
+
+ $this->assertLink(t('Log out'), 0, 'User was logged in.');
+
+ // Verify user was redirected away from user/login to an accessible page.
+ $this->assertResponse(200);
+
+ $this->drupalLogout();
+ // Use a User-supplied Identity that is the URL of an XRDS document.
+ // Tell the test module to add a doctype. This should fail.
+ $identity = url('openid-test/yadis/xrds', array('absolute' => TRUE, 'query' => array('doctype' => 1)));
+ // Test logging in via the login block on the front page.
+ $edit = array('openid_identifier' => $identity);
+ $this->drupalPost('', $edit, t('Log in'));
+ $this->assertRaw(t('Sorry, that is not a valid OpenID. Ensure you have spelled your ID correctly.'), 'XML with DOCTYPE was rejected.');
+ }
+
+ /**
+ * Test login using OpenID during maintenance mode.
+ */
+ function testLoginMaintenanceMode() {
+ $this->web_user = $this->drupalCreateUser(array('access site in maintenance mode'));
+ $this->drupalLogin($this->web_user);
+
+ // Use a User-supplied Identity that is the URL of an XRDS document.
+ $identity = url('openid-test/yadis/xrds', array('absolute' => TRUE));
+ $this->addIdentity($identity);
+ $this->drupalLogout();
+
+ // Enable maintenance mode.
+ variable_set('maintenance_mode', 1);
+
+ // Test logging in via the user/login page while the site is offline.
+ $edit = array('openid_identifier' => $identity);
+ $this->drupalPost('user/login', $edit, t('Log in'));
+
+ // Check we are on the OpenID redirect form.
+ $this->assertTitle(t('OpenID redirect'), 'OpenID redirect page was displayed.');
+
+ // Submit form to the OpenID Provider Endpoint.
+ $this->drupalPost(NULL, array(), t('Send'));
+
+ $this->assertLink(t('Log out'), 0, 'User was logged in.');
+
+ // Verify user was redirected away from user/login to an accessible page.
+ $this->assertText(t('Operating in maintenance mode.'));
+ $this->assertResponse(200);
+ }
+
+ /**
+ * Test deleting an OpenID identity from a user's profile.
+ */
+ function testDelete() {
+ $this->drupalLogin($this->web_user);
+
+ // Add identity to user's profile.
+ $identity = url('openid-test/yadis/xrds', array('absolute' => TRUE));
+ $this->addIdentity($identity);
+ $this->assertText($identity, 'Identity appears in list.');
+
+ // Delete the newly added identity.
+ $this->clickLink(t('Delete'));
+ $this->drupalPost(NULL, array(), t('Confirm'));
+
+ $this->assertText(t('OpenID deleted.'), 'Identity deleted');
+ $this->assertNoText($identity, 'Identity no longer appears in list.');
+ }
+
+ /**
+ * Test that a blocked user cannot log in.
+ */
+ function testBlockedUserLogin() {
+ // Use a User-supplied Identity that is the URL of an XRDS document.
+ $identity = url('openid-test/yadis/xrds', array('absolute' => TRUE));
+
+ // Log in and add an OpenID Identity to the account.
+ $this->drupalLogin($this->web_user);
+ $this->addIdentity($identity);
+ $this->drupalLogout();
+
+ // Log in as an admin user and block the account.
+ $admin_user = $this->drupalCreateUser(array('administer users'));
+ $this->drupalLogin($admin_user);
+ $this->drupalGet('admin/people');
+ $edit = array(
+ 'operation' => 'block',
+ 'accounts[' . $this->web_user->uid . ']' => TRUE,
+ );
+ $this->drupalPost('admin/people', $edit, t('Update'));
+ $this->assertRaw('The update has been performed.', 'Account was blocked.');
+ $this->drupalLogout();
+
+ $this->submitLoginForm($identity);
+ $this->assertRaw(t('The username %name has not been activated or is blocked.', array('%name' => $this->web_user->name)), 'User login was blocked.');
+ }
+
+ /**
+ * Add OpenID identity to user's profile.
+ *
+ * @param $identity
+ * The User-supplied Identifier.
+ * @param $version
+ * The protocol version used by the service.
+ * @param $local_id
+ * The expected OP-Local Identifier found during discovery.
+ * @param $claimed_id
+ * The expected Claimed Identifier returned by the OpenID Provider, or FALSE
+ * if the discovery is expected to fail.
+ */
+ function addIdentity($identity, $version = 2, $local_id = 'http://example.com/xrds', $claimed_id = NULL) {
+ // Tell openid_test.module to only accept this OP-Local Identifier.
+ variable_set('openid_test_identity', $local_id);
+
+ $edit = array('openid_identifier' => $identity);
+ $this->drupalPost('user/' . $this->web_user->uid . '/openid', $edit, t('Add an OpenID'));
+
+ if ($claimed_id === FALSE) {
+ $this->assertRaw(t('Sorry, that is not a valid OpenID. Ensure you have spelled your ID correctly.'), 'Invalid identity was rejected.');
+ return;
+ }
+
+ // OpenID 1 used a HTTP redirect, OpenID 2 uses a HTML form that is submitted automatically using JavaScript.
+ if ($version == 2) {
+ // Check we are on the OpenID redirect form.
+ $this->assertTitle(t('OpenID redirect'), 'OpenID redirect page was displayed.');
+
+ // Submit form to the OpenID Provider Endpoint.
+ $this->drupalPost(NULL, array(), t('Send'));
+ }
+
+ if (!isset($claimed_id)) {
+ $claimed_id = $identity;
+ }
+ $this->assertRaw(t('Successfully added %identity', array('%identity' => $claimed_id)), format_string('Identity %identity was added.', array('%identity' => $identity)));
+ }
+
+ /**
+ * Add OpenID identity, changed by the following redirects, to user's profile.
+ *
+ * According to OpenID Authentication 2.0, section 7.2.4, URL Identifiers MUST
+ * be further normalized by following redirects when retrieving their content
+ * and this final URL MUST be noted by the Relying Party as the Claimed
+ * Identifier and be used when requesting authentication.
+ *
+ * @param $identity
+ * The User-supplied Identifier.
+ * @param $version
+ * The protocol version used by the service.
+ * @param $local_id
+ * The expected OP-Local Identifier found during discovery.
+ * @param $claimed_id
+ * The expected Claimed Identifier returned by the OpenID Provider, or FALSE
+ * if the discovery is expected to fail.
+ * @param $redirects
+ * The number of redirects.
+ */
+ function addRedirectedIdentity($identity, $version = 2, $local_id = 'http://example.com/xrds', $claimed_id = NULL, $redirects = 0) {
+ // Set the final destination URL which is the same as the Claimed
+ // Identifier, we insert the same identifier also to the provider response,
+ // but provider could further change the Claimed ID actually (e.g. it could
+ // add unique fragment).
+ variable_set('openid_test_redirect_url', $identity);
+ variable_set('openid_test_response', array('openid.claimed_id' => $identity));
+
+ $this->addIdentity(url('openid-test/redirect/' . $redirects, array('absolute' => TRUE)), $version, $local_id, $claimed_id);
+
+ // Clean up.
+ variable_del('openid_test_redirect_url');
+ variable_del('openid_test_response');
+ }
+
+ /**
+ * Tests that openid.signed is verified.
+ */
+ function testSignatureValidation() {
+ // Use a User-supplied Identity that is the URL of an XRDS document.
+ $identity = url('openid-test/yadis/xrds', array('absolute' => TRUE));
+
+ // Respond with an invalid signature.
+ variable_set('openid_test_response', array('openid.sig' => 'this-is-an-invalid-signature'));
+ $this->submitLoginForm($identity);
+ $this->assertRaw('OpenID login failed.');
+
+ // Do not sign the mandatory field openid.assoc_handle.
+ variable_set('openid_test_response', array('openid.signed' => 'op_endpoint,claimed_id,identity,return_to,response_nonce'));
+ $this->submitLoginForm($identity);
+ $this->assertRaw('OpenID login failed.');
+
+ // Sign all mandatory fields and a custom field.
+ $keys_to_sign = array('op_endpoint', 'claimed_id', 'identity', 'return_to', 'response_nonce', 'assoc_handle', 'foo');
+ $association = new stdClass();
+ $association->mac_key = variable_get('mac_key');
+ $response = array(
+ 'openid.op_endpoint' => url('openid-test/endpoint', array('absolute' => TRUE)),
+ 'openid.claimed_id' => $identity,
+ 'openid.identity' => $identity,
+ 'openid.return_to' => url('openid/authenticate', array('absolute' => TRUE)),
+ 'openid.response_nonce' => _openid_nonce(),
+ 'openid.assoc_handle' => 'openid-test',
+ 'openid.foo' => 123,
+ 'openid.signed' => implode(',', $keys_to_sign),
+ );
+ $response['openid.sig'] = _openid_signature($association, $response, $keys_to_sign);
+ variable_set('openid_test_response', $response);
+ $this->submitLoginForm($identity);
+ $this->assertNoRaw('OpenID login failed.');
+ $this->assertFieldByName('name', '', 'No username was supplied by provider.');
+ $this->assertFieldByName('mail', '', 'No e-mail address was supplied by provider.');
+
+ // Check that unsigned SREG fields are ignored.
+ $response = array(
+ 'openid.signed' => 'op_endpoint,claimed_id,identity,return_to,response_nonce,assoc_handle,sreg.nickname',
+ 'openid.sreg.nickname' => 'john',
+ 'openid.sreg.email' => 'john@example.com',
+ );
+ variable_set('openid_test_response', $response);
+ $this->submitLoginForm($identity);
+ $this->assertNoRaw('OpenID login failed.');
+ $this->assertFieldByName('name', 'john', 'Username was supplied by provider.');
+ $this->assertFieldByName('mail', '', 'E-mail address supplied by provider was ignored.');
+ }
+}
+
+/**
+ * Test account registration using Simple Registration and Attribute Exchange.
+ */
+class OpenIDRegistrationTestCase extends OpenIDWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'OpenID account registration',
+ 'description' => 'Creates a user account using auto-registration.',
+ 'group' => 'OpenID'
+ );
+ }
+
+ function setUp() {
+ parent::setUp('openid', 'openid_test');
+ variable_set('user_register', USER_REGISTER_VISITORS);
+ }
+
+ /**
+ * Test OpenID auto-registration with e-mail verification enabled.
+ */
+ function testRegisterUserWithEmailVerification() {
+ variable_set('user_email_verification', TRUE);
+
+ // Tell openid_test.module to respond with these SREG fields.
+ variable_set('openid_test_response', array('openid.sreg.nickname' => 'john', 'openid.sreg.email' => 'john@example.com'));
+
+ // Use a User-supplied Identity that is the URL of an XRDS document.
+ $identity = url('openid-test/yadis/xrds', array('absolute' => TRUE));
+ $this->submitLoginForm($identity);
+ $this->assertRaw(t('Once you have verified your e-mail address, you may log in via OpenID.'), 'User was asked to verify e-mail address.');
+ $this->assertRaw(t('A welcome message with further instructions has been sent to your e-mail address.'), 'A welcome message was sent to the user.');
+ $reset_url = $this->getPasswordResetURLFromMail();
+
+ $user = user_load_by_name('john');
+ $this->assertTrue($user, 'User was registered with right username.');
+ $this->assertEqual($user->mail, 'john@example.com', 'User was registered with right email address.');
+ $this->assertFalse($user->data, 'No additional user info was saved.');
+
+ $this->submitLoginForm($identity);
+ $this->assertRaw(t('You must validate your email address for this account before logging in via OpenID.'));
+
+ // Follow the one-time login that was sent in the welcome e-mail.
+ $this->drupalGet($reset_url);
+ $this->drupalPost(NULL, array(), t('Log in'));
+
+ $this->drupalLogout();
+
+ // Verify that the account was activated.
+ $this->submitLoginForm($identity);
+ $this->assertLink(t('Log out'), 0, 'User was logged in.');
+ }
+
+ /**
+ * Test OpenID auto-registration with e-mail verification disabled.
+ */
+ function testRegisterUserWithoutEmailVerification() {
+ variable_set('user_email_verification', FALSE);
+
+ // Tell openid_test.module to respond with these SREG fields.
+ variable_set('openid_test_response', array('openid.sreg.nickname' => 'john', 'openid.sreg.email' => 'john@example.com'));
+
+ // Use a User-supplied Identity that is the URL of an XRDS document.
+ $identity = url('openid-test/yadis/xrds', array('absolute' => TRUE));
+ $this->submitLoginForm($identity);
+ $this->assertLink(t('Log out'), 0, 'User was logged in.');
+
+ $user = user_load_by_name('john');
+ $this->assertTrue($user, 'User was registered with right username.');
+ $this->assertEqual($user->mail, 'john@example.com', 'User was registered with right email address.');
+ $this->assertFalse($user->data, 'No additional user info was saved.');
+
+ $this->drupalLogout();
+
+ $this->submitLoginForm($identity);
+ $this->assertLink(t('Log out'), 0, 'User was logged in.');
+ }
+
+ /**
+ * Test OpenID auto-registration with a provider that supplies invalid SREG
+ * information (a username that is already taken, and no e-mail address).
+ */
+ function testRegisterUserWithInvalidSreg() {
+ // Tell openid_test.module to respond with these SREG fields.
+ $web_user = $this->drupalCreateUser(array());
+ variable_set('openid_test_response', array('openid.sreg.nickname' => $web_user->name, 'openid.sreg.email' => 'mail@invalid#'));
+
+ // Use a User-supplied Identity that is the URL of an XRDS document.
+ $identity = url('openid-test/yadis/xrds', array('absolute' => TRUE));
+ $this->submitLoginForm($identity);
+
+ $this->assertRaw(t('Account registration using the information provided by your OpenID provider failed due to the reasons listed below. Complete the registration by filling out the form below. If you already have an account, you can <a href="@login">log in</a> now and add your OpenID under "My account".', array('@login' => url('user/login'))), 'User was asked to complete the registration process manually.');
+ $this->assertRaw(t('The name %name is already taken.', array('%name' => $web_user->name)), 'Form validation error for username was displayed.');
+ $this->assertRaw(t('The e-mail address %mail is not valid.', array('%mail' => 'mail@invalid#')), 'Form validation error for e-mail address was displayed.');
+
+ // Enter username and e-mail address manually.
+ $edit = array('name' => 'john', 'mail' => 'john@example.com');
+ $this->drupalPost(NULL, $edit, t('Create new account'));
+ $this->assertRaw(t('Once you have verified your e-mail address, you may log in via OpenID.'), 'User was asked to verify e-mail address.');
+ $reset_url = $this->getPasswordResetURLFromMail();
+
+ $user = user_load_by_name('john');
+ $this->assertTrue($user, 'User was registered with right username.');
+ $this->assertFalse($user->data, 'No additional user info was saved.');
+
+ // Follow the one-time login that was sent in the welcome e-mail.
+ $this->drupalGet($reset_url);
+ $this->drupalPost(NULL, array(), t('Log in'));
+
+ // The user is taken to user/%uid/edit.
+ $this->assertFieldByName('mail', 'john@example.com', 'User was registered with right e-mail address.');
+
+ $this->clickLink(t('OpenID identities'));
+ $this->assertRaw($identity, 'OpenID identity was registered.');
+ }
+
+ /**
+ * Test OpenID auto-registration with a provider that does not supply SREG
+ * information (i.e. no username or e-mail address).
+ */
+ function testRegisterUserWithoutSreg() {
+ // Load the front page to get the user login block.
+ $this->drupalGet('');
+
+ // Use a User-supplied Identity that is the URL of an XRDS document.
+ $identity = url('openid-test/yadis/xrds', array('absolute' => TRUE));
+ $this->submitLoginForm($identity);
+ $this->assertRaw(t('Complete the registration by filling out the form below. If you already have an account, you can <a href="@login">log in</a> now and add your OpenID under "My account".', array('@login' => url('user/login'))), 'User was asked to complete the registration process manually.');
+ $this->assertNoRaw(t('You must enter a username.'), 'Form validation error for username was not displayed.');
+ $this->assertNoRaw(t('You must enter an e-mail address.'), 'Form validation error for e-mail address was not displayed.');
+
+ // Enter username and e-mail address manually.
+ $edit = array('name' => 'john', 'mail' => 'john@example.com');
+ $this->drupalPost(NULL, $edit, t('Create new account'));
+ $this->assertRaw(t('Once you have verified your e-mail address, you may log in via OpenID.'), 'User was asked to verify e-mail address.');
+ $reset_url = $this->getPasswordResetURLFromMail();
+
+ $user = user_load_by_name('john');
+ $this->assertTrue($user, 'User was registered with right username.');
+ $this->assertFalse($user->data, 'No additional user info was saved.');
+
+ // Follow the one-time login that was sent in the welcome e-mail.
+ $this->drupalGet($reset_url);
+ $this->drupalPost(NULL, array(), t('Log in'));
+
+ // The user is taken to user/%uid/edit.
+ $this->assertFieldByName('mail', 'john@example.com', 'User was registered with right e-mail address.');
+
+ $this->clickLink(t('OpenID identities'));
+ $this->assertRaw($identity, 'OpenID identity was registered.');
+ }
+
+ /**
+ * Test OpenID auto-registration with a provider that supplies AX information,
+ * but no SREG.
+ */
+ function testRegisterUserWithAXButNoSREG() {
+ variable_set('user_email_verification', FALSE);
+
+ // Tell openid_test.module to respond with these AX fields.
+ variable_set('openid_test_response', array(
+ 'openid.ns.ext123' => 'http://openid.net/srv/ax/1.0',
+ 'openid.ext123.type.mail456' => 'http://axschema.org/contact/email',
+ 'openid.ext123.value.mail456' => 'john@example.com',
+ 'openid.ext123.type.name789' => 'http://schema.openid.net/namePerson/friendly',
+ 'openid.ext123.count.name789' => '1',
+ 'openid.ext123.value.name789.1' => 'john',
+ ));
+
+ // Use a User-supplied Identity that is the URL of an XRDS document.
+ $identity = url('openid-test/yadis/xrds', array('absolute' => TRUE));
+ $this->submitLoginForm($identity);
+ $this->assertLink(t('Log out'), 0, 'User was logged in.');
+
+ $user = user_load_by_name('john');
+ $this->assertTrue($user, 'User was registered with right username.');
+ $this->assertEqual($user->mail, 'john@example.com', 'User was registered with right email address.');
+ }
+}
+
+/**
+ * Test account registration using Simple Registration and Attribute Exchange.
+ */
+class OpenIDInvalidIdentifierTransitionTestCase extends OpenIDFunctionalTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'OpenID account update',
+ 'description' => 'Tries to correct OpenID identifiers attached to accounts if their identifiers were stripped.',
+ 'group' => 'OpenID',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('openid', 'openid_test');
+ variable_set('user_register', USER_REGISTER_VISITORS);
+ variable_set('openid_less_obtrusive_transition', TRUE);
+ }
+
+ /**
+ * Test OpenID transition with e-mail mismatch.
+ */
+ function testStrippedFragmentAccountEmailMismatch() {
+ $this->drupalLogin($this->web_user);
+
+ // Use a User-supplied Identity that is the URL of an XRDS document.
+ $identity = url('openid-test/yadis/xrds', array('absolute' => TRUE, 'fragment' => $this->randomName()));
+ $identity_stripped = preg_replace('/#.*/', '', $identity);
+
+ // Add invalid identifier to the authmap (identifier has stripped fragment).
+ $this->addIdentity($identity_stripped);
+ $this->drupalLogout();
+
+ // Test logging in via the login form, provider will respond with full
+ // identifier (including fragment) but with different email, so we can't
+ // provide auto-update.
+ variable_set('openid_test_response', array(
+ 'openid.claimed_id' => $identity,
+ 'openid.sreg.nickname' => $this->web_user->name,
+ 'openid.sreg.email' => 'invalid-' . $this->web_user->mail));
+
+ $edit = array('openid_identifier' => $identity_stripped);
+ $this->submitLoginForm($identity_stripped);
+
+ // Verify user was redirected away from user login to an accessible page.
+ $this->assertResponse(200);
+
+ // Verify the message.
+ $this->assertRaw(t('There is already an existing account associated with the OpenID identifier that you have provided.'), 'Message that OpenID identifier must be updated manually was displayed.');
+ }
+
+ /**
+ * Test OpenID auto transition with e-mail.
+ */
+ function testStrippedFragmentAccountAutoUpdateSreg() {
+ $this->drupalLogin($this->web_user);
+
+ // Use a User-supplied Identity that is the URL of an XRDS document.
+ $identity = url('openid-test/yadis/xrds', array('absolute' => TRUE, 'fragment' => $this->randomName()));
+ $identity_stripped = preg_replace('/#.*/', '', $identity);
+
+ // Add invalid identifier to the authmap (identifier has stripped fragment).
+ $this->addIdentity($identity_stripped);
+ $this->drupalLogout();
+
+ // Test logging in via the login form, provider will respond with full
+ // identifier (including fragment) but with different email, so we can't
+ // provide auto-update.
+ variable_set('openid_test_response', array(
+ 'openid.claimed_id' => $identity,
+ 'openid.sreg.nickname' => $this->web_user->name,
+ 'openid.sreg.email' => $this->web_user->mail));
+
+ $this->submitLoginForm($identity_stripped);
+
+ // Verify user was redirected away from user login to an accessible page.
+ $this->assertResponse(200);
+
+ // Verify the message.
+ $this->assertRaw(t('New OpenID identifier %identity was added as a replacement for invalid identifier %invalid_identity.', array('%invalid_identity' => $identity_stripped, '%identity' => $identity)), 'Message that OpenID identifier was added automatically was displayed.');
+ }
+}
+
+/**
+ * Test internal helper functions.
+ */
+class OpenIDTestCase extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'OpenID helper functions',
+ 'description' => 'Test OpenID helper functions.',
+ 'group' => 'OpenID'
+ );
+ }
+
+ function setUp() {
+ parent::setUp('openid');
+ module_load_include('inc', 'openid');
+ }
+
+ /**
+ * Test _openid_dh_XXX_to_XXX() functions.
+ */
+ function testConversion() {
+ $this->assertEqual(_openid_dh_long_to_base64('12345678901234567890123456789012345678901234567890'), 'CHJ/Y2mq+DyhUCZ0evjH8ZbOPwrS', '_openid_dh_long_to_base64() returned expected result.');
+ $this->assertEqual(_openid_dh_base64_to_long('BsH/g8Nrpn2dtBSdu/sr1y8hxwyx'), '09876543210987654321098765432109876543210987654321', '_openid_dh_base64_to_long() returned expected result.');
+
+ $this->assertEqual(_openid_dh_long_to_binary('12345678901234567890123456789012345678901234567890'), "\x08r\x7fci\xaa\xf8<\xa1P&tz\xf8\xc7\xf1\x96\xce?\x0a\xd2", '_openid_dh_long_to_binary() returned expected result.');
+ $this->assertEqual(_openid_dh_binary_to_long("\x06\xc1\xff\x83\xc3k\xa6}\x9d\xb4\x14\x9d\xbb\xfb+\xd7/!\xc7\x0c\xb1"), '09876543210987654321098765432109876543210987654321', '_openid_dh_binary_to_long() returned expected result.');
+ }
+
+ /**
+ * Test _openid_dh_xorsecret().
+ */
+ function testOpenidDhXorsecret() {
+ $this->assertEqual(_openid_dh_xorsecret('123456790123456790123456790', "abc123ABC\x00\xFF"), "\xa4'\x06\xbe\xf1.\x00y\xff\xc2\xc1", '_openid_dh_xorsecret() returned expected result.');
+ }
+
+ /**
+ * Test _openid_signature().
+ */
+ function testOpenidSignature() {
+ // Test that signature is calculated according to OpenID Authentication 2.0,
+ // section 6.1. In the following array, only the two first entries should be
+ // included in the calculation, because the substring following the period
+ // is mentioned in the third argument for _openid_signature(). The last
+ // entry should not be included, because it does not start with "openid.".
+ $response = array(
+ 'openid.foo' => 'abc1',
+ 'openid.bar' => 'abc2',
+ 'openid.baz' => 'abc3',
+ 'foobar.foo' => 'abc4',
+ );
+ $association = new stdClass();
+ $association->mac_key = "1234567890abcdefghij\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9";
+ $this->assertEqual(_openid_signature($association, $response, array('foo', 'bar')), 'QnKZQzSFstT+GNiJDFOptdcZjrc=', 'Expected signature calculated.');
+ }
+
+ /**
+ * Test _openid_is_xri().
+ */
+ function testOpenidXRITest() {
+ // Test that the XRI test is according to OpenID Authentication 2.0,
+ // section 7.2. If the user-supplied string starts with xri:// it should be
+ // stripped and the resulting string should be treated as an XRI when it
+ // starts with "=", "@", "+", "$", "!" or "(".
+ $this->assertTrue(_openid_is_xri('xri://=foo'), '_openid_is_xri() returned expected result for an xri identifier with xri scheme.');
+ $this->assertTrue(_openid_is_xri('xri://@foo'), '_openid_is_xri() returned expected result for an xri identifier with xri scheme.');
+ $this->assertTrue(_openid_is_xri('xri://+foo'), '_openid_is_xri() returned expected result for an xri identifier with xri scheme.');
+ $this->assertTrue(_openid_is_xri('xri://$foo'), '_openid_is_xri() returned expected result for an xri identifier with xri scheme.');
+ $this->assertTrue(_openid_is_xri('xri://!foo'), '_openid_is_xri() returned expected result for an xri identifier with xri scheme..');
+ $this->assertTrue(_openid_is_xri('xri://(foo'), '_openid_is_xri() returned expected result for an xri identifier with xri scheme..');
+
+ $this->assertTrue(_openid_is_xri('=foo'), '_openid_is_xri() returned expected result for an xri identifier.');
+ $this->assertTrue(_openid_is_xri('@foo'), '_openid_is_xri() returned expected result for an xri identifier.');
+ $this->assertTrue(_openid_is_xri('+foo'), '_openid_is_xri() returned expected result for an xri identifier.');
+ $this->assertTrue(_openid_is_xri('$foo'), '_openid_is_xri() returned expected result for an xri identifier.');
+ $this->assertTrue(_openid_is_xri('!foo'), '_openid_is_xri() returned expected result for an xri identifier.');
+ $this->assertTrue(_openid_is_xri('(foo'), '_openid_is_xri() returned expected result for an xri identifier.');
+
+ $this->assertFalse(_openid_is_xri('foo'), '_openid_is_xri() returned expected result for an http URL.');
+ $this->assertFalse(_openid_is_xri('xri://foo'), '_openid_is_xri() returned expected result for an http URL.');
+ $this->assertFalse(_openid_is_xri('http://foo/'), '_openid_is_xri() returned expected result for an http URL.');
+ $this->assertFalse(_openid_is_xri('http://example.com/'), '_openid_is_xri() returned expected result for an http URL.');
+ $this->assertFalse(_openid_is_xri('user@example.com/'), '_openid_is_xri() returned expected result for an http URL.');
+ $this->assertFalse(_openid_is_xri('http://user@example.com/'), '_openid_is_xri() returned expected result for an http URL.');
+ }
+
+ /**
+ * Test openid_normalize().
+ */
+ function testOpenidNormalize() {
+ // Test that the normalization is according to OpenID Authentication 2.0,
+ // section 7.2 and 11.5.2.
+
+ $this->assertEqual(openid_normalize('$foo'), '$foo', 'openid_normalize() correctly normalized an XRI.');
+ $this->assertEqual(openid_normalize('xri://$foo'), '$foo', 'openid_normalize() correctly normalized an XRI with an xri:// scheme.');
+
+ $this->assertEqual(openid_normalize('example.com/'), 'http://example.com/', 'openid_normalize() correctly normalized a URL with a missing scheme.');
+ $this->assertEqual(openid_normalize('example.com'), 'http://example.com/', 'openid_normalize() correctly normalized a URL with a missing scheme and empty path.');
+ $this->assertEqual(openid_normalize('http://example.com'), 'http://example.com/', 'openid_normalize() correctly normalized a URL with an empty path.');
+
+ $this->assertEqual(openid_normalize('http://example.com/path'), 'http://example.com/path', 'openid_normalize() correctly normalized a URL with a path.');
+
+ $this->assertEqual(openid_normalize('http://example.com/path#fragment'), 'http://example.com/path', 'openid_normalize() correctly normalized a URL with a fragment.');
+ }
+
+ /**
+ * Test openid_extract_namespace().
+ */
+ function testOpenidExtractNamespace() {
+ $response = array(
+ 'openid.sreg.nickname' => 'john',
+ 'openid.ns.ext1' => OPENID_NS_SREG,
+ 'openid.ext1.nickname' => 'george',
+ 'openid.ext1.email' => 'george@example.com',
+ 'openid.ns.ext2' => 'http://example.com/ns/ext2',
+ 'openid.ext2.foo' => '123',
+ 'openid.ext2.bar' => '456',
+ 'openid.signed' => 'sreg.nickname,ns.ext1,ext1.email,ext2.foo',
+ );
+
+ $values = openid_extract_namespace($response, 'http://example.com/ns/dummy', NULL, FALSE);
+ $this->assertEqual($values, array(), 'Nothing found for unused namespace.');
+
+ $values = openid_extract_namespace($response, 'http://example.com/ns/dummy', 'sreg', FALSE);
+ $this->assertEqual($values, array('nickname' => 'john'), 'Value found for fallback prefix.');
+
+ $values = openid_extract_namespace($response, OPENID_NS_SREG, 'sreg', FALSE);
+ $this->assertEqual($values, array('nickname' => 'george', 'email' => 'george@example.com'), 'Namespace takes precedence over fallback prefix.');
+
+ // ext1.email is signed, but ext1.nickname is not.
+ $values = openid_extract_namespace($response, OPENID_NS_SREG, 'sreg', TRUE);
+ $this->assertEqual($values, array('email' => 'george@example.com'), 'Unsigned namespaced fields ignored.');
+
+ $values = openid_extract_namespace($response, 'http://example.com/ns/ext2', 'sreg', FALSE);
+ $this->assertEqual($values, array('foo' => '123', 'bar' => '456'), 'Unsigned fields found.');
+
+ // ext2.foo and ext2.bar are ignored, because ns.ext2 is not signed. The
+ // fallback prefix is not used, because the namespace is specified.
+ $values = openid_extract_namespace($response, 'http://example.com/ns/ext2', 'sreg', TRUE);
+ $this->assertEqual($values, array(), 'Unsigned fields ignored.');
+ }
+}
diff --git a/kolab.org/www/drupal-7.26/modules/openid/tests/openid_test.info b/kolab.org/www/drupal-7.26/modules/openid/tests/openid_test.info
new file mode 100644
index 0000000..f00e97a
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/openid/tests/openid_test.info
@@ -0,0 +1,13 @@
+name = OpenID dummy provider
+description = "OpenID provider used for testing."
+package = Testing
+version = VERSION
+core = 7.x
+dependencies[] = openid
+hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/openid/tests/openid_test.install b/kolab.org/www/drupal-7.26/modules/openid/tests/openid_test.install
new file mode 100644
index 0000000..d30e2dc
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/openid/tests/openid_test.install
@@ -0,0 +1,17 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the openid_test module.
+ */
+
+/**
+ * Implements hook_install().
+ */
+function openid_test_install() {
+ module_load_include('inc', 'openid');
+ // Generate a MAC key (Message Authentication Code) used for signing messages.
+ // The variable is base64-encoded, because variables cannot contain non-UTF-8
+ // data.
+ variable_set('openid_test_mac_key', drupal_random_key(20));
+}
diff --git a/kolab.org/www/drupal-7.26/modules/openid/tests/openid_test.module b/kolab.org/www/drupal-7.26/modules/openid/tests/openid_test.module
new file mode 100644
index 0000000..bcf9f42
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/openid/tests/openid_test.module
@@ -0,0 +1,367 @@
+<?php
+
+/**
+ * @file
+ * Dummy OpenID Provider used with SimpleTest.
+ *
+ * The provider simply responds positively to all authentication requests. In
+ * addition to a Provider Endpoint (a URL used for Drupal to communicate with
+ * the provider using the OpenID Authentication protocol) the module provides
+ * URLs used by the various discovery mechanisms.
+ *
+ * When a user enters an OpenID identity, the Relying Party (in the testing
+ * scenario, this is the OpenID module) looks up the URL of the Provider
+ * Endpoint using one of several discovery mechanisms. The Relying Party then
+ * redirects the user to Provider Endpoint. The provider verifies the user's
+ * identity and redirects the user back to the Relying Party accompanied by a
+ * signed message confirming the identity. Before redirecting to a provider for
+ * the first time, the Relying Party fetches a secret MAC key from the provider
+ * by doing a direct "associate" HTTP request to the Provider Endpoint. This
+ * key is used for verifying the signed messages from the provider.
+ */
+
+/**
+ * Implements hook_menu().
+ */
+function openid_test_menu() {
+ $items['openid-test/yadis/xrds'] = array(
+ 'title' => 'XRDS service document',
+ 'page callback' => 'openid_test_yadis_xrds',
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ );
+ $items['openid-test/yadis/x-xrds-location'] = array(
+ 'title' => 'Yadis discovery using X-XRDS-Location header',
+ 'page callback' => 'openid_test_yadis_x_xrds_location',
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ );
+ $items['openid-test/yadis/http-equiv'] = array(
+ 'title' => 'Yadis discovery using <meta http-equiv="X-XRDS-Location" ...>',
+ 'page callback' => 'openid_test_yadis_http_equiv',
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ );
+ $items['openid-test/html/openid1'] = array(
+ 'title' => 'HTML-based discovery using <link rel="openid.server" ...>',
+ 'page callback' => 'openid_test_html_openid1',
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ );
+ $items['openid-test/html/openid2'] = array(
+ 'title' => 'HTML-based discovery using <link rel="openid2.provider" ...>',
+ 'page callback' => 'openid_test_html_openid2',
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ );
+ $items['openid-test/endpoint'] = array(
+ 'title' => 'OpenID Provider Endpoint',
+ 'page callback' => 'openid_test_endpoint',
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ );
+ $items['openid-test/redirect'] = array(
+ 'title' => 'OpenID Provider Redirection Point',
+ 'page callback' => 'openid_test_redirect',
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ );
+ $items['openid-test/redirected/%/%'] = array(
+ 'title' => 'OpenID Provider Final URL',
+ 'page callback' => 'openid_test_redirected_method',
+ 'page arguments' => array(2, 3),
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ );
+ return $items;
+}
+
+/**
+ * Implements hook_menu_site_status_alter().
+ */
+function openid_test_menu_site_status_alter(&$menu_site_status, $path) {
+ // Allow access to openid endpoint and identity even in offline mode.
+ if ($menu_site_status == MENU_SITE_OFFLINE && user_is_anonymous() && in_array($path, array('openid-test/yadis/xrds', 'openid-test/endpoint'))) {
+ $menu_site_status = MENU_SITE_ONLINE;
+ }
+}
+
+/**
+ * Menu callback; XRDS document that references the OP Endpoint URL.
+ */
+function openid_test_yadis_xrds() {
+ if ($_SERVER['HTTP_ACCEPT'] == 'application/xrds+xml') {
+ // Only respond to XRI requests for one specific XRI. The is used to verify
+ // that the XRI has been properly encoded. The "+" sign in the _xrd_r query
+ // parameter is decoded to a space by PHP.
+ if (arg(3) == 'xri') {
+ if (variable_get('clean_url', 0)) {
+ if (arg(4) != '@example*résumé;%25' || $_GET['_xrd_r'] != 'application/xrds xml') {
+ drupal_not_found();
+ }
+ }
+ else {
+ // Drupal cannot properly emulate an XRI proxy resolver using unclean
+ // URLs, so the arguments gets messed up.
+ if (arg(4) . '/' . arg(5) != '@example*résumé;%25?_xrd_r=application/xrds xml') {
+ drupal_not_found();
+ }
+ }
+ }
+ drupal_add_http_header('Content-Type', 'application/xrds+xml');
+ print '<?xml version="1.0" encoding="UTF-8"?>';
+ if (!empty($_GET['doctype'])) {
+ print "\n<!DOCTYPE dct [ <!ELEMENT blue (#PCDATA)> ]>\n";
+ }
+ print '
+ <xrds:XRDS xmlns:xrds="xri://$xrds" xmlns="xri://$xrd*($v*2.0)" xmlns:openid="http://openid.net/xmlns/1.0">
+ <XRD>
+ <Status cid="' . check_plain(variable_get('openid_test_canonical_id_status', 'verified')) . '"/>
+ <ProviderID>xri://@</ProviderID>
+ <CanonicalID>http://example.com/user</CanonicalID>
+ <Service>
+ <Type>http://example.com/this-is-ignored</Type>
+ </Service>
+ <Service priority="5">
+ <Type>http://openid.net/signon/1.0</Type>
+ <URI>http://example.com/this-is-only-openid-1.0</URI>
+ </Service>
+ <Service priority="10">
+ <Type>http://specs.openid.net/auth/2.0/signon</Type>
+ <Type>http://openid.net/srv/ax/1.0</Type>
+ <URI>' . url('openid-test/endpoint', array('absolute' => TRUE)) . '</URI>
+ <LocalID>http://example.com/xrds</LocalID>
+ </Service>
+ <Service priority="15">
+ <Type>http://specs.openid.net/auth/2.0/signon</Type>
+ <URI>http://example.com/this-has-too-low-priority</URI>
+ </Service>
+ <Service>
+ <Type>http://specs.openid.net/auth/2.0/signon</Type>
+ <URI>http://example.com/this-has-too-low-priority</URI>
+ </Service>
+ ';
+ if (arg(3) == 'server') {
+ print '
+ <Service>
+ <Type>http://specs.openid.net/auth/2.0/server</Type>
+ <URI>http://example.com/this-has-too-low-priority</URI>
+ </Service>
+ <Service priority="20">
+ <Type>http://specs.openid.net/auth/2.0/server</Type>
+ <URI>' . url('openid-test/endpoint', array('absolute' => TRUE)) . '</URI>
+ </Service>';
+ }
+ elseif (arg(3) == 'delegate') {
+ print '
+ <Service priority="0">
+ <Type>http://specs.openid.net/auth/2.0/signon</Type>
+ <Type>http://openid.net/srv/ax/1.0</Type>
+ <URI>' . url('openid-test/endpoint', array('absolute' => TRUE)) . '</URI>
+ <openid:Delegate>http://example.com/xrds-delegate</openid:Delegate>
+ </Service>';
+ }
+ print '
+ </XRD>
+ </xrds:XRDS>';
+ }
+ else {
+ return t('This is a regular HTML page. If the client sends an Accept: application/xrds+xml header when requesting this URL, an XRDS document is returned.');
+ }
+}
+
+/**
+ * Menu callback; regular HTML page with an X-XRDS-Location HTTP header.
+ */
+function openid_test_yadis_x_xrds_location() {
+ drupal_add_http_header('X-XRDS-Location', url('openid-test/yadis/xrds', array('absolute' => TRUE)));
+ return t('This page includes an X-RDS-Location HTTP header containing the URL of an XRDS document.');
+}
+
+/**
+ * Menu callback; regular HTML page with <meta> element.
+ */
+function openid_test_yadis_http_equiv() {
+ $element = array(
+ '#tag' => 'meta',
+ '#attributes' => array(
+ 'http-equiv' => 'X-XRDS-Location',
+ 'content' => url('openid-test/yadis/xrds', array('absolute' => TRUE)),
+ ),
+ );
+ drupal_add_html_head($element, 'openid_test_yadis_http_equiv');
+ return t('This page includes a &lt;meta equiv=...&gt; element containing the URL of an XRDS document.');
+}
+
+/**
+ * Menu callback; regular HTML page with OpenID 1.0 <link> element.
+ */
+function openid_test_html_openid1() {
+ drupal_add_html_head_link(array('rel' => 'openid.server', 'href' => url('openid-test/endpoint', array('absolute' => TRUE))));
+ drupal_add_html_head_link(array('rel' => 'openid.delegate', 'href' => 'http://example.com/html-openid1'));
+ return t('This page includes a &lt;link rel=...&gt; element containing the URL of an OpenID Provider Endpoint.');
+}
+
+/**
+ * Menu callback; regular HTML page with OpenID 2.0 <link> element.
+ */
+function openid_test_html_openid2() {
+ drupal_add_html_head_link(array('rel' => 'openid2.provider', 'href' => url('openid-test/endpoint', array('absolute' => TRUE))));
+ drupal_add_html_head_link(array('rel' => 'openid2.local_id', 'href' => 'http://example.com/html-openid2'));
+ return t('This page includes a &lt;link rel=...&gt; element containing the URL of an OpenID Provider Endpoint.');
+}
+
+/**
+ * Menu callback; OpenID Provider Endpoint.
+ *
+ * It accepts "associate" requests directly from the Relying Party, and
+ * "checkid_setup" requests made by the user's browser based on HTTP redirects
+ * (in OpenID 1) or HTML forms (in OpenID 2) generated by the Relying Party.
+ */
+function openid_test_endpoint() {
+ switch ($_REQUEST['openid_mode']) {
+ case 'associate':
+ _openid_test_endpoint_associate();
+ break;
+ case 'checkid_setup':
+ _openid_test_endpoint_authenticate();
+ break;
+ }
+}
+
+/**
+ * Menu callback; redirect during Normalization/Discovery.
+ */
+function openid_test_redirect($count = 0) {
+ if ($count == 0) {
+ $url = variable_get('openid_test_redirect_url', '');
+ }
+ else {
+ $url = url('openid-test/redirect/' . --$count, array('absolute' => TRUE));
+ }
+ $http_response_code = variable_get('openid_test_redirect_http_reponse_code', 301);
+ header('Location: ' . $url, TRUE, $http_response_code);
+ exit();
+}
+
+/**
+ * Menu callback; respond with appropriate callback.
+ */
+function openid_test_redirected_method($method1, $method2) {
+ return call_user_func('openid_test_' . $method1 . '_' . $method2);
+}
+
+/**
+ * OpenID endpoint; handle "associate" requests (see OpenID Authentication 2.0,
+ * section 8).
+ *
+ * The purpose of association is to send the secret MAC key to the Relying Party
+ * using Diffie-Hellman key exchange. The MAC key is used in subsequent
+ * "authenticate" requests. The "associate" request is made by the Relying Party
+ * (in the testing scenario, this is the OpenID module that communicates with
+ * the endpoint using drupal_http_request()).
+ */
+function _openid_test_endpoint_associate() {
+ module_load_include('inc', 'openid');
+
+ // Use default parameters for Diffie-Helmann key exchange.
+ $mod = OPENID_DH_DEFAULT_MOD;
+ $gen = OPENID_DH_DEFAULT_GEN;
+
+ // Generate private Diffie-Helmann key.
+ $r = _openid_dh_rand($mod);
+ $private = _openid_math_add($r, 1);
+
+ // Calculate public Diffie-Helmann key.
+ $public = _openid_math_powmod($gen, $private, $mod);
+
+ // Calculate shared secret based on Relying Party's public key.
+ $cpub = _openid_dh_base64_to_long($_REQUEST['openid_dh_consumer_public']);
+ $shared = _openid_math_powmod($cpub, $private, $mod);
+
+ // Encrypt the MAC key using the shared secret.
+ $enc_mac_key = base64_encode(_openid_dh_xorsecret($shared, base64_decode(variable_get('mac_key'))));
+
+ // Generate response including our public key and the MAC key. Using our
+ // public key and its own private key, the Relying Party can calculate the
+ // shared secret, and with this it can decrypt the encrypted MAC key.
+ $response = array(
+ 'ns' => 'http://specs.openid.net/auth/2.0',
+ 'assoc_handle' => 'openid-test',
+ 'session_type' => $_REQUEST['openid_session_type'],
+ 'assoc_type' => $_REQUEST['openid_assoc_type'],
+ 'expires_in' => '3600',
+ 'dh_server_public' => _openid_dh_long_to_base64($public),
+ 'enc_mac_key' => $enc_mac_key,
+ );
+
+ // Respond to Relying Party in the special Key-Value Form Encoding (see OpenID
+ // Authentication 1.0, section 4.1.1).
+ drupal_add_http_header('Content-Type', 'text/plain');
+ print _openid_create_message($response);
+}
+
+/**
+ * OpenID endpoint; handle "authenticate" requests.
+ *
+ * All requests result in a successful response. The request is a GET or POST
+ * made by the user's browser based on an HTML form or HTTP redirect generated
+ * by the Relying Party. The user is redirected back to the Relying Party using
+ * a URL containing a signed message in the query string confirming the user's
+ * identity.
+ */
+function _openid_test_endpoint_authenticate() {
+ module_load_include('inc', 'openid');
+
+ $expected_identity = variable_get('openid_test_identity');
+ if ($expected_identity && $_REQUEST['openid_identity'] != $expected_identity) {
+ $response = variable_get('openid_test_response', array()) + array(
+ 'openid.ns' => OPENID_NS_2_0,
+ 'openid.mode' => 'error',
+ 'openid.error' => 'Unexpted identity',
+ );
+ drupal_add_http_header('Content-Type', 'text/plain');
+ header('Location: ' . url($_REQUEST['openid_return_to'], array('query' => $response, 'external' => TRUE)));
+ return;
+ }
+
+ // Generate unique identifier for this authentication.
+ $nonce = _openid_nonce();
+
+ // Generate response containing the user's identity.
+ $response = variable_get('openid_test_response', array()) + array(
+ 'openid.ns' => OPENID_NS_2_0,
+ 'openid.mode' => 'id_res',
+ 'openid.op_endpoint' => url('openid-test/endpoint', array('absolute' => TRUE)),
+ 'openid.claimed_id' => !empty($_REQUEST['openid_claimed_id']) ? $_REQUEST['openid_claimed_id'] : '',
+ 'openid.identity' => $_REQUEST['openid_identity'],
+ 'openid.return_to' => $_REQUEST['openid_return_to'],
+ 'openid.response_nonce' => $nonce,
+ 'openid.assoc_handle' => 'openid-test',
+ );
+
+ if (isset($response['openid.signed'])) {
+ $keys_to_sign = explode(',', $response['openid.signed']);
+ }
+ else {
+ // Unless openid.signed is explicitly defined, all keys are signed.
+ $keys_to_sign = array();
+ foreach ($response as $key => $value) {
+ // Strip off the "openid." prefix.
+ $keys_to_sign[] = substr($key, 7);
+ }
+ $response['openid.signed'] = implode(',', $keys_to_sign);
+ }
+
+ // Sign the message using the MAC key that was exchanged during association.
+ $association = new stdClass();
+ $association->mac_key = variable_get('mac_key');
+ if (!isset($response['openid.sig'])) {
+ $response['openid.sig'] = _openid_signature($association, $response, $keys_to_sign);
+ }
+
+ // Put the signed message into the query string of a URL supplied by the
+ // Relying Party, and redirect the user.
+ drupal_add_http_header('Content-Type', 'text/plain');
+ header('Location: ' . url($_REQUEST['openid_return_to'], array('query' => $response, 'external' => TRUE)));
+}