/php-openid-1.2.3/Auth/OpenID/Server.php
PHP | 1307 lines | 878 code | 117 blank | 312 comment | 63 complexity | f2d0544e39ec72aad5205a37aeddcc3a MD5 | raw file
Possible License(s): LGPL-2.1
- <?php
- /**
- * OpenID server protocol and logic.
- *
- * Overview
- *
- * An OpenID server must perform three tasks:
- *
- * 1. Examine the incoming request to determine its nature and validity.
- * 2. Make a decision about how to respond to this request.
- * 3. Format the response according to the protocol.
- *
- * The first and last of these tasks may performed by the
- * 'decodeRequest' and 'encodeResponse' methods of the
- * Auth_OpenID_Server object. Who gets to do the intermediate task --
- * deciding how to respond to the request -- will depend on what type
- * of request it is.
- *
- * If it's a request to authenticate a user (a 'checkid_setup' or
- * 'checkid_immediate' request), you need to decide if you will assert
- * that this user may claim the identity in question. Exactly how you
- * do that is a matter of application policy, but it generally
- * involves making sure the user has an account with your system and
- * is logged in, checking to see if that identity is hers to claim,
- * and verifying with the user that she does consent to releasing that
- * information to the party making the request.
- *
- * Examine the properties of the Auth_OpenID_CheckIDRequest object,
- * and if and when you've come to a decision, form a response by
- * calling Auth_OpenID_CheckIDRequest::answer.
- *
- * Other types of requests relate to establishing associations between
- * client and server and verifing the authenticity of previous
- * communications. Auth_OpenID_Server contains all the logic and data
- * necessary to respond to such requests; just pass it to
- * Auth_OpenID_Server::handleRequest.
- *
- * OpenID Extensions
- *
- * Do you want to provide other information for your users in addition
- * to authentication? Version 1.2 of the OpenID protocol allows
- * consumers to add extensions to their requests. For example, with
- * sites using the Simple Registration
- * Extension
- * (http://www.openidenabled.com/openid/simple-registration-extension/),
- * a user can agree to have their nickname and e-mail address sent to
- * a site when they sign up.
- *
- * Since extensions do not change the way OpenID authentication works,
- * code to handle extension requests may be completely separate from
- * the Auth_OpenID_Request class here. But you'll likely want data
- * sent back by your extension to be signed. Auth_OpenID_Response
- * provides methods with which you can add data to it which can be
- * signed with the other data in the OpenID signature.
- *
- * For example:
- *
- * // when request is a checkid_* request
- * response = request.answer(True)
- * // this will a signed 'openid.sreg.timezone' parameter to the response
- * response.addField('sreg', 'timezone', 'America/Los_Angeles')
- *
- * Stores
- *
- * The OpenID server needs to maintain state between requests in order
- * to function. Its mechanism for doing this is called a store. The
- * store interface is defined in Interface.php. Additionally, several
- * concrete store implementations are provided, so that most sites
- * won't need to implement a custom store. For a store backed by flat
- * files on disk, see Auth_OpenID_FileStore. For stores based on
- * MySQL, SQLite, or PostgreSQL, see the Auth_OpenID_SQLStore
- * subclasses.
- *
- * Upgrading
- *
- * The keys by which a server looks up associations in its store have
- * changed in version 1.2 of this library. If your store has entries
- * created from version 1.0 code, you should empty it.
- *
- * PHP versions 4 and 5
- *
- * LICENSE: See the COPYING file included in this distribution.
- *
- * @package OpenID
- * @author JanRain, Inc. <openid@janrain.com>
- * @copyright 2005 Janrain, Inc.
- * @license http://www.gnu.org/copyleft/lesser.html LGPL
- */
- /**
- * Required imports
- */
- require_once "Auth/OpenID.php";
- require_once "Auth/OpenID/Association.php";
- require_once "Auth/OpenID/CryptUtil.php";
- require_once "Auth/OpenID/BigMath.php";
- require_once "Auth/OpenID/DiffieHellman.php";
- require_once "Auth/OpenID/KVForm.php";
- require_once "Auth/OpenID/TrustRoot.php";
- require_once "Auth/OpenID/ServerRequest.php";
- define('AUTH_OPENID_HTTP_OK', 200);
- define('AUTH_OPENID_HTTP_REDIRECT', 302);
- define('AUTH_OPENID_HTTP_ERROR', 400);
- global $_Auth_OpenID_Request_Modes,
- $_Auth_OpenID_OpenID_Prefix,
- $_Auth_OpenID_Encode_Kvform,
- $_Auth_OpenID_Encode_Url;
- /**
- * @access private
- */
- $_Auth_OpenID_Request_Modes = array('checkid_setup',
- 'checkid_immediate');
- /**
- * @access private
- */
- $_Auth_OpenID_OpenID_Prefix = "openid.";
- /**
- * @access private
- */
- $_Auth_OpenID_Encode_Kvform = array('kfvorm');
- /**
- * @access private
- */
- $_Auth_OpenID_Encode_Url = array('URL/redirect');
- /**
- * @access private
- */
- function _isError($obj, $cls = 'Auth_OpenID_ServerError')
- {
- return is_a($obj, $cls);
- }
- /**
- * An error class which gets instantiated and returned whenever an
- * OpenID protocol error occurs. Be prepared to use this in place of
- * an ordinary server response.
- *
- * @package OpenID
- */
- class Auth_OpenID_ServerError {
- /**
- * @access private
- */
- function Auth_OpenID_ServerError($query = null, $message = null)
- {
- $this->message = $message;
- $this->query = $query;
- }
- /**
- * Returns the return_to URL for the request which caused this
- * error.
- */
- function hasReturnTo()
- {
- global $_Auth_OpenID_OpenID_Prefix;
- if ($this->query) {
- return array_key_exists($_Auth_OpenID_OpenID_Prefix .
- 'return_to', $this->query);
- } else {
- return false;
- }
- }
- /**
- * Encodes this error's response as a URL suitable for
- * redirection. If the response has no return_to, another
- * Auth_OpenID_ServerError is returned.
- */
- function encodeToURL()
- {
- global $_Auth_OpenID_OpenID_Prefix;
- $return_to = Auth_OpenID::arrayGet($this->query,
- $_Auth_OpenID_OpenID_Prefix .
- 'return_to');
- if (!$return_to) {
- return new Auth_OpenID_ServerError(null, "no return_to URL");
- }
- return Auth_OpenID::appendArgs($return_to,
- array('openid.mode' => 'error',
- 'openid.error' => $this->toString()));
- }
- /**
- * Encodes the response to key-value form. This is a
- * machine-readable format used to respond to messages which came
- * directly from the consumer and not through the user-agent. See
- * the OpenID specification.
- */
- function encodeToKVForm()
- {
- return Auth_OpenID_KVForm::fromArray(
- array('mode' => 'error',
- 'error' => $this->toString()));
- }
- /**
- * Returns one of $_Auth_OpenID_Encode_Url,
- * $_Auth_OpenID_Encode_Kvform, or null, depending on the type of
- * encoding expected for this error's payload.
- */
- function whichEncoding()
- {
- global $_Auth_OpenID_Encode_Url,
- $_Auth_OpenID_Encode_Kvform,
- $_Auth_OpenID_Request_Modes;
- if ($this->hasReturnTo()) {
- return $_Auth_OpenID_Encode_Url;
- }
- $mode = Auth_OpenID::arrayGet($this->query, 'openid.mode');
- if ($mode) {
- if (!in_array($mode, $_Auth_OpenID_Request_Modes)) {
- return $_Auth_OpenID_Encode_Kvform;
- }
- }
- return null;
- }
- /**
- * Returns this error message.
- */
- function toString()
- {
- if ($this->message) {
- return $this->message;
- } else {
- return get_class($this) . " error";
- }
- }
- }
- /**
- * An error indicating that the return_to URL is malformed.
- *
- * @package OpenID
- */
- class Auth_OpenID_MalformedReturnURL extends Auth_OpenID_ServerError {
- function Auth_OpenID_MalformedReturnURL($query, $return_to)
- {
- $this->return_to = $return_to;
- parent::Auth_OpenID_ServerError($query, "malformed return_to URL");
- }
- }
- /**
- * This error is returned when the trust_root value is malformed.
- *
- * @package OpenID
- */
- class Auth_OpenID_MalformedTrustRoot extends Auth_OpenID_ServerError {
- function toString()
- {
- return "Malformed trust root";
- }
- }
- /**
- * The base class for all server request classes.
- *
- * @access private
- * @package OpenID
- */
- class Auth_OpenID_Request {
- var $mode = null;
- }
- /**
- * A request to verify the validity of a previous response.
- *
- * @access private
- * @package OpenID
- */
- class Auth_OpenID_CheckAuthRequest extends Auth_OpenID_Request {
- var $mode = "check_authentication";
- var $invalidate_handle = null;
- function Auth_OpenID_CheckAuthRequest($assoc_handle, $sig, $signed,
- $invalidate_handle = null)
- {
- $this->assoc_handle = $assoc_handle;
- $this->sig = $sig;
- $this->signed = $signed;
- if ($invalidate_handle !== null) {
- $this->invalidate_handle = $invalidate_handle;
- }
- }
- function fromQuery($query)
- {
- global $_Auth_OpenID_OpenID_Prefix;
- $required_keys = array('assoc_handle', 'sig', 'signed');
- foreach ($required_keys as $k) {
- if (!array_key_exists($_Auth_OpenID_OpenID_Prefix . $k,
- $query)) {
- return new Auth_OpenID_ServerError($query,
- sprintf("%s request missing required parameter %s from \
- query", "check_authentication", $k));
- }
- }
- $assoc_handle = $query[$_Auth_OpenID_OpenID_Prefix . 'assoc_handle'];
- $sig = $query[$_Auth_OpenID_OpenID_Prefix . 'sig'];
- $signed_list = $query[$_Auth_OpenID_OpenID_Prefix . 'signed'];
- $signed_list = explode(",", $signed_list);
- $signed_pairs = array();
- foreach ($signed_list as $field) {
- if ($field == 'mode') {
- // XXX KLUDGE HAX WEB PROTOCoL BR0KENNN
- //
- // openid.mode is currently check_authentication
- // because that's the mode of this request. But the
- // signature was made on something with a different
- // openid.mode.
- $value = "id_res";
- } else {
- if (array_key_exists($_Auth_OpenID_OpenID_Prefix . $field,
- $query)) {
- $value = $query[$_Auth_OpenID_OpenID_Prefix . $field];
- } else {
- return new Auth_OpenID_ServerError($query,
- sprintf("Couldn't find signed field %r in query %s",
- $field, var_export($query, true)));
- }
- }
- $signed_pairs[] = array($field, $value);
- }
- $result = new Auth_OpenID_CheckAuthRequest($assoc_handle, $sig,
- $signed_pairs);
- $result->invalidate_handle = Auth_OpenID::arrayGet($query,
- $_Auth_OpenID_OpenID_Prefix . 'invalidate_handle');
- return $result;
- }
- function answer(&$signatory)
- {
- $is_valid = $signatory->verify($this->assoc_handle, $this->sig,
- $this->signed);
- // Now invalidate that assoc_handle so it this checkAuth
- // message cannot be replayed.
- $signatory->invalidate($this->assoc_handle, true);
- $response = new Auth_OpenID_ServerResponse($this);
- $response->fields['is_valid'] = $is_valid ? "true" : "false";
- if ($this->invalidate_handle) {
- $assoc = $signatory->getAssociation($this->invalidate_handle,
- false);
- if (!$assoc) {
- $response->fields['invalidate_handle'] =
- $this->invalidate_handle;
- }
- }
- return $response;
- }
- }
- class Auth_OpenID_PlainTextServerSession {
- /**
- * An object that knows how to handle association requests with no
- * session type.
- */
- var $session_type = 'plaintext';
- function fromQuery($unused_request)
- {
- return new Auth_OpenID_PlainTextServerSession();
- }
- function answer($secret)
- {
- return array('mac_key' => base64_encode($secret));
- }
- }
- class Auth_OpenID_DiffieHellmanServerSession {
- /**
- * An object that knows how to handle association requests with
- * the Diffie-Hellman session type.
- */
- var $session_type = 'DH-SHA1';
- function Auth_OpenID_DiffieHellmanServerSession($dh, $consumer_pubkey)
- {
- $this->dh = $dh;
- $this->consumer_pubkey = $consumer_pubkey;
- }
- function fromQuery($query)
- {
- $dh_modulus = Auth_OpenID::arrayGet($query, 'openid.dh_modulus');
- $dh_gen = Auth_OpenID::arrayGet($query, 'openid.dh_gen');
- if ((($dh_modulus === null) && ($dh_gen !== null)) ||
- (($dh_gen === null) && ($dh_modulus !== null))) {
- if ($dh_modulus === null) {
- $missing = 'modulus';
- } else {
- $missing = 'generator';
- }
- return new Auth_OpenID_ServerError(
- 'If non-default modulus or generator is '.
- 'supplied, both must be supplied. Missing '.
- $missing);
- }
- $lib =& Auth_OpenID_getMathLib();
- if ($dh_modulus || $dh_gen) {
- $dh_modulus = $lib->base64ToLong($dh_modulus);
- $dh_gen = $lib->base64ToLong($dh_gen);
- if ($lib->cmp($dh_modulus, 0) == 0 ||
- $lib->cmp($dh_gen, 0) == 0) {
- return new Auth_OpenID_ServerError(
- $query, "Failed to parse dh_mod or dh_gen");
- }
- $dh = new Auth_OpenID_DiffieHellman($dh_modulus, $dh_gen);
- } else {
- $dh = new Auth_OpenID_DiffieHellman();
- }
- $consumer_pubkey = Auth_OpenID::arrayGet($query,
- 'openid.dh_consumer_public');
- if ($consumer_pubkey === null) {
- return new Auth_OpenID_ServerError(
- 'Public key for DH-SHA1 session '.
- 'not found in query');
- }
- $consumer_pubkey =
- $lib->base64ToLong($consumer_pubkey);
- if ($consumer_pubkey === false) {
- return new Auth_OpenID_ServerError($query,
- "dh_consumer_public is not base64");
- }
- return new Auth_OpenID_DiffieHellmanServerSession($dh,
- $consumer_pubkey);
- }
- function answer($secret)
- {
- $lib =& Auth_OpenID_getMathLib();
- $mac_key = $this->dh->xorSecret($this->consumer_pubkey, $secret);
- return array(
- 'dh_server_public' =>
- $lib->longToBase64($this->dh->public),
- 'enc_mac_key' => base64_encode($mac_key));
- }
- }
- /**
- * A request to associate with the server.
- *
- * @access private
- * @package OpenID
- */
- class Auth_OpenID_AssociateRequest extends Auth_OpenID_Request {
- var $mode = "associate";
- var $assoc_type = 'HMAC-SHA1';
- function Auth_OpenID_AssociateRequest(&$session)
- {
- $this->session =& $session;
- }
- function fromQuery($query)
- {
- global $_Auth_OpenID_OpenID_Prefix;
- $session_classes = array(
- 'DH-SHA1' => 'Auth_OpenID_DiffieHellmanServerSession',
- null => 'Auth_OpenID_PlainTextServerSession');
- $session_type = null;
- if (array_key_exists($_Auth_OpenID_OpenID_Prefix . 'session_type',
- $query)) {
- $session_type = $query[$_Auth_OpenID_OpenID_Prefix .
- 'session_type'];
- }
- if (!array_key_exists($session_type, $session_classes)) {
- return new Auth_OpenID_ServerError($query,
- "Unknown session type $session_type");
- }
- $session_cls = $session_classes[$session_type];
- $session = call_user_func_array(array($session_cls, 'fromQuery'),
- array($query));
- if (($session === null) || (_isError($session))) {
- return new Auth_OpenID_ServerError($query,
- "Error parsing $session_type session");
- }
- return new Auth_OpenID_AssociateRequest($session);
- }
- function answer($assoc)
- {
- $ml =& Auth_OpenID_getMathLib();
- $response = new Auth_OpenID_ServerResponse($this);
- $response->fields = array('expires_in' => $assoc->getExpiresIn(),
- 'assoc_type' => 'HMAC-SHA1',
- 'assoc_handle' => $assoc->handle);
- $r = $this->session->answer($assoc->secret);
- foreach ($r as $k => $v) {
- $response->fields[$k] = $v;
- }
- if ($this->session->session_type != 'plaintext') {
- $response->fields['session_type'] = $this->session->session_type;
- }
- return $response;
- }
- }
- /**
- * A request to confirm the identity of a user.
- *
- * @access private
- * @package OpenID
- */
- class Auth_OpenID_CheckIDRequest extends Auth_OpenID_Request {
- var $mode = "checkid_setup"; // or "checkid_immediate"
- var $immediate = false;
- var $trust_root = null;
- function make($query, $identity, $return_to, $trust_root = null,
- $immediate = false, $assoc_handle = null)
- {
- if (!Auth_OpenID_TrustRoot::_parse($return_to)) {
- return new Auth_OpenID_MalformedReturnURL($query, $return_to);
- }
- $r = new Auth_OpenID_CheckIDRequest($identity, $return_to,
- $trust_root, $immediate,
- $assoc_handle);
- if (!$r->trustRootValid()) {
- return new Auth_OpenID_UntrustedReturnURL($return_to,
- $trust_root);
- } else {
- return $r;
- }
- }
- function Auth_OpenID_CheckIDRequest($identity, $return_to,
- $trust_root = null, $immediate = false,
- $assoc_handle = null)
- {
- $this->identity = $identity;
- $this->return_to = $return_to;
- $this->trust_root = $trust_root;
- $this->assoc_handle = $assoc_handle;
- if ($immediate) {
- $this->immediate = true;
- $this->mode = "checkid_immediate";
- } else {
- $this->immediate = false;
- $this->mode = "checkid_setup";
- }
- }
- function fromQuery($query)
- {
- global $_Auth_OpenID_OpenID_Prefix;
- $mode = $query[$_Auth_OpenID_OpenID_Prefix . 'mode'];
- $immediate = null;
- if ($mode == "checkid_immediate") {
- $immediate = true;
- $mode = "checkid_immediate";
- } else {
- $immediate = false;
- $mode = "checkid_setup";
- }
- $required = array('identity',
- 'return_to');
- $optional = array('trust_root',
- 'assoc_handle');
- $values = array();
- foreach ($required as $field) {
- if (array_key_exists($_Auth_OpenID_OpenID_Prefix . $field,
- $query)) {
- $value = $query[$_Auth_OpenID_OpenID_Prefix . $field];
- } else {
- return new Auth_OpenID_ServerError($query,
- sprintf("Missing required field %s from request",
- $field));
- }
- $values[$field] = $value;
- }
- foreach ($optional as $field) {
- $value = null;
- if (array_key_exists($_Auth_OpenID_OpenID_Prefix . $field,
- $query)) {
- $value = $query[$_Auth_OpenID_OpenID_Prefix. $field];
- }
- if ($value) {
- $values[$field] = $value;
- }
- }
- if (!Auth_OpenID_TrustRoot::_parse($values['return_to'])) {
- return new Auth_OpenID_MalformedReturnURL($query,
- $values['return_to']);
- }
- $obj = Auth_OpenID_CheckIDRequest::make($query,
- $values['identity'],
- $values['return_to'],
- Auth_OpenID::arrayGet($values,
- 'trust_root', null),
- $immediate);
- if (is_a($obj, 'Auth_OpenID_ServerError')) {
- return $obj;
- }
- if (Auth_OpenID::arrayGet($values, 'assoc_handle')) {
- $obj->assoc_handle = $values['assoc_handle'];
- }
- return $obj;
- }
- function trustRootValid()
- {
- if (!$this->trust_root) {
- return true;
- }
- $tr = Auth_OpenID_TrustRoot::_parse($this->trust_root);
- if ($tr === false) {
- return new Auth_OpenID_MalformedTrustRoot(null, $this->trust_root);
- }
- return Auth_OpenID_TrustRoot::match($this->trust_root,
- $this->return_to);
- }
- function answer($allow, $server_url = null)
- {
- if ($allow || $this->immediate) {
- $mode = 'id_res';
- } else {
- $mode = 'cancel';
- }
- $response = new Auth_OpenID_CheckIDResponse($this, $mode);
- if ($allow) {
- $response->fields['identity'] = $this->identity;
- $response->fields['return_to'] = $this->return_to;
- if (!$this->trustRootValid()) {
- return new Auth_OpenID_UntrustedReturnURL($this->return_to,
- $this->trust_root);
- }
- } else {
- $response->signed = array();
- if ($this->immediate) {
- if (!$server_url) {
- return new Auth_OpenID_ServerError(null,
- 'setup_url is required for $allow=false \
- in immediate mode.');
- }
- $setup_request =& new Auth_OpenID_CheckIDRequest(
- $this->identity,
- $this->return_to,
- $this->trust_root,
- false,
- $this->assoc_handle);
- $setup_url = $setup_request->encodeToURL($server_url);
- $response->fields['user_setup_url'] = $setup_url;
- }
- }
- return $response;
- }
- function encodeToURL($server_url)
- {
- global $_Auth_OpenID_OpenID_Prefix;
- // Imported from the alternate reality where these classes are
- // used in both the client and server code, so Requests are
- // Encodable too. That's right, code imported from alternate
- // realities all for the love of you, id_res/user_setup_url.
- $q = array('mode' => $this->mode,
- 'identity' => $this->identity,
- 'return_to' => $this->return_to);
- if ($this->trust_root) {
- $q['trust_root'] = $this->trust_root;
- }
- if ($this->assoc_handle) {
- $q['assoc_handle'] = $this->assoc_handle;
- }
- $_q = array();
- foreach ($q as $k => $v) {
- $_q[$_Auth_OpenID_OpenID_Prefix . $k] = $v;
- }
- return Auth_OpenID::appendArgs($server_url, $_q);
- }
- function getCancelURL()
- {
- global $_Auth_OpenID_OpenID_Prefix;
- if ($this->immediate) {
- return new Auth_OpenID_ServerError(null,
- "Cancel is not an appropriate \
- response to immediate mode \
- requests.");
- }
- return Auth_OpenID::appendArgs($this->return_to,
- array($_Auth_OpenID_OpenID_Prefix . 'mode' =>
- 'cancel'));
- }
- }
- /**
- * This class encapsulates the response to an OpenID server request.
- *
- * @access private
- * @package OpenID
- */
- class Auth_OpenID_ServerResponse {
- function Auth_OpenID_ServerResponse($request)
- {
- $this->request = $request;
- $this->fields = array();
- }
- function whichEncoding()
- {
- global $_Auth_OpenID_Encode_Kvform,
- $_Auth_OpenID_Request_Modes,
- $_Auth_OpenID_Encode_Url;
- if (in_array($this->request->mode, $_Auth_OpenID_Request_Modes)) {
- return $_Auth_OpenID_Encode_Url;
- } else {
- return $_Auth_OpenID_Encode_Kvform;
- }
- }
- function encodeToURL()
- {
- global $_Auth_OpenID_OpenID_Prefix;
- $fields = array();
- foreach ($this->fields as $k => $v) {
- $fields[$_Auth_OpenID_OpenID_Prefix . $k] = $v;
- }
- return Auth_OpenID::appendArgs($this->request->return_to, $fields);
- }
- function encodeToKVForm()
- {
- return Auth_OpenID_KVForm::fromArray($this->fields);
- }
- }
- /**
- * A response to a checkid request.
- *
- * @access private
- * @package OpenID
- */
- class Auth_OpenID_CheckIDResponse extends Auth_OpenID_ServerResponse {
- function Auth_OpenID_CheckIDResponse(&$request, $mode = 'id_res')
- {
- parent::Auth_OpenID_ServerResponse($request);
- $this->fields['mode'] = $mode;
- $this->signed = array();
- if ($mode == 'id_res') {
- array_push($this->signed, 'mode', 'identity', 'return_to');
- }
- }
- function addField($namespace, $key, $value, $signed = true)
- {
- if ($namespace) {
- $key = sprintf('%s.%s', $namespace, $key);
- }
- $this->fields[$key] = $value;
- if ($signed && !in_array($key, $this->signed)) {
- $this->signed[] = $key;
- }
- }
- function addFields($namespace, $fields, $signed = true)
- {
- foreach ($fields as $k => $v) {
- $this->addField($namespace, $k, $v, $signed);
- }
- }
- function update($namespace, $other)
- {
- $namespaced_fields = array();
- foreach ($other->fields as $k => $v) {
- $name = sprintf('%s.%s', $namespace, $k);
- $namespaced_fields[$name] = $v;
- }
- $this->fields = array_merge($this->fields, $namespaced_fields);
- $this->signed = array_merge($this->signed, $other->signed);
- }
- }
- /**
- * A web-capable response object which you can use to generate a
- * user-agent response.
- *
- * @package OpenID
- */
- class Auth_OpenID_WebResponse {
- var $code = AUTH_OPENID_HTTP_OK;
- var $body = "";
- function Auth_OpenID_WebResponse($code = null, $headers = null,
- $body = null)
- {
- if ($code) {
- $this->code = $code;
- }
- if ($headers !== null) {
- $this->headers = $headers;
- } else {
- $this->headers = array();
- }
- if ($body !== null) {
- $this->body = $body;
- }
- }
- }
- /**
- * Responsible for the signature of query data and the verification of
- * OpenID signature values.
- *
- * @package OpenID
- */
- class Auth_OpenID_Signatory {
- // = 14 * 24 * 60 * 60; # 14 days, in seconds
- var $SECRET_LIFETIME = 1209600;
- // keys have a bogus server URL in them because the filestore
- // really does expect that key to be a URL. This seems a little
- // silly for the server store, since I expect there to be only one
- // server URL.
- var $normal_key = 'http://localhost/|normal';
- var $dumb_key = 'http://localhost/|dumb';
- /**
- * Create a new signatory using a given store.
- */
- function Auth_OpenID_Signatory(&$store)
- {
- // assert store is not None
- $this->store =& $store;
- }
- /**
- * Verify, using a given association handle, a signature with
- * signed key-value pairs from an HTTP request.
- */
- function verify($assoc_handle, $sig, $signed_pairs)
- {
- $assoc = $this->getAssociation($assoc_handle, true);
- if (!$assoc) {
- // oidutil.log("failed to get assoc with handle %r to verify sig %r"
- // % (assoc_handle, sig))
- return false;
- }
- $expected_sig = base64_encode($assoc->sign($signed_pairs));
- return $sig == $expected_sig;
- }
- /**
- * Given a response, sign the fields in the response's 'signed'
- * list, and insert the signature into the response.
- */
- function sign($response)
- {
- $signed_response = $response;
- $assoc_handle = $response->request->assoc_handle;
- if ($assoc_handle) {
- // normal mode
- $assoc = $this->getAssociation($assoc_handle, false);
- if (!$assoc) {
- // fall back to dumb mode
- $signed_response->fields['invalidate_handle'] = $assoc_handle;
- $assoc = $this->createAssociation(true);
- }
- } else {
- // dumb mode.
- $assoc = $this->createAssociation(true);
- }
- $signed_response->fields['assoc_handle'] = $assoc->handle;
- $assoc->addSignature($signed_response->signed,
- $signed_response->fields, '');
- return $signed_response;
- }
- /**
- * Make a new association.
- */
- function createAssociation($dumb = true, $assoc_type = 'HMAC-SHA1')
- {
- $secret = Auth_OpenID_CryptUtil::getBytes(20);
- $uniq = base64_encode(Auth_OpenID_CryptUtil::getBytes(4));
- $handle = sprintf('{%s}{%x}{%s}', $assoc_type, intval(time()), $uniq);
- $assoc = Auth_OpenID_Association::fromExpiresIn(
- $this->SECRET_LIFETIME, $handle, $secret, $assoc_type);
- if ($dumb) {
- $key = $this->dumb_key;
- } else {
- $key = $this->normal_key;
- }
- $this->store->storeAssociation($key, $assoc);
- return $assoc;
- }
- /**
- * Given an association handle, get the association from the
- * store, or return a ServerError or null if something goes wrong.
- */
- function getAssociation($assoc_handle, $dumb)
- {
- if ($assoc_handle === null) {
- return new Auth_OpenID_ServerError(null,
- "assoc_handle must not be null");
- }
- if ($dumb) {
- $key = $this->dumb_key;
- } else {
- $key = $this->normal_key;
- }
- $assoc = $this->store->getAssociation($key, $assoc_handle);
- if (($assoc !== null) && ($assoc->getExpiresIn() <= 0)) {
- $this->store->removeAssociation($key, $assoc_handle);
- $assoc = null;
- }
- return $assoc;
- }
- /**
- * Invalidate a given association handle.
- */
- function invalidate($assoc_handle, $dumb)
- {
- if ($dumb) {
- $key = $this->dumb_key;
- } else {
- $key = $this->normal_key;
- }
- $this->store->removeAssociation($key, $assoc_handle);
- }
- }
- /**
- * Encode an Auth_OpenID_Response to an Auth_OpenID_WebResponse.
- *
- * @package OpenID
- */
- class Auth_OpenID_Encoder {
- var $responseFactory = 'Auth_OpenID_WebResponse';
- /**
- * Encode an Auth_OpenID_Response and return an
- * Auth_OpenID_WebResponse.
- */
- function encode(&$response)
- {
- global $_Auth_OpenID_Encode_Kvform,
- $_Auth_OpenID_Encode_Url;
- $cls = $this->responseFactory;
- $encode_as = $response->whichEncoding();
- if ($encode_as == $_Auth_OpenID_Encode_Kvform) {
- $wr = new $cls(null, null, $response->encodeToKVForm());
- if (is_a($response, 'Auth_OpenID_ServerError')) {
- $wr->code = AUTH_OPENID_HTTP_ERROR;
- }
- } else if ($encode_as == $_Auth_OpenID_Encode_Url) {
- $location = $response->encodeToURL();
- $wr = new $cls(AUTH_OPENID_HTTP_REDIRECT,
- array('location' => $location));
- } else {
- return new Auth_OpenID_EncodingError($response);
- }
- return $wr;
- }
- }
- /**
- * Returns true if the given response needs a signature.
- *
- * @access private
- */
- function needsSigning($response)
- {
- return (in_array($response->request->mode, array('checkid_setup',
- 'checkid_immediate')) &&
- $response->signed);
- }
- /**
- * An encoder which also takes care of signing fields when required.
- *
- * @package OpenID
- */
- class Auth_OpenID_SigningEncoder extends Auth_OpenID_Encoder {
- function Auth_OpenID_SigningEncoder(&$signatory)
- {
- $this->signatory =& $signatory;
- }
- /**
- * Sign an Auth_OpenID_Response and return an
- * Auth_OpenID_WebResponse.
- */
- function encode(&$response)
- {
- // the isinstance is a bit of a kludge... it means there isn't
- // really an adapter to make the interfaces quite match.
- if (!is_a($response, 'Auth_OpenID_ServerError') &&
- needsSigning($response)) {
- if (!$this->signatory) {
- return new Auth_OpenID_ServerError(null,
- "Must have a store to sign request");
- }
- if (array_key_exists('sig', $response->fields)) {
- return new Auth_OpenID_AlreadySigned($response);
- }
- $response = $this->signatory->sign($response);
- }
- return parent::encode($response);
- }
- }
- /**
- * Decode an incoming Auth_OpenID_WebResponse into an
- * Auth_OpenID_Request.
- *
- * @package OpenID
- */
- class Auth_OpenID_Decoder {
- function Auth_OpenID_Decoder()
- {
- global $_Auth_OpenID_OpenID_Prefix;
- $this->prefix = $_Auth_OpenID_OpenID_Prefix;
- $this->handlers = array(
- 'checkid_setup' => 'Auth_OpenID_CheckIDRequest',
- 'checkid_immediate' => 'Auth_OpenID_CheckIDRequest',
- 'check_authentication' => 'Auth_OpenID_CheckAuthRequest',
- 'associate' => 'Auth_OpenID_AssociateRequest'
- );
- }
- /**
- * Given an HTTP query in an array (key-value pairs), decode it
- * into an Auth_OpenID_Request object.
- */
- function decode($query)
- {
- if (!$query) {
- return null;
- }
- $myquery = array();
- foreach ($query as $k => $v) {
- if (strpos($k, $this->prefix) === 0) {
- $myquery[$k] = $v;
- }
- }
- if (!$myquery) {
- return null;
- }
- $mode = Auth_OpenID::arrayGet($myquery, $this->prefix . 'mode');
- if (!$mode) {
- return new Auth_OpenID_ServerError($query,
- sprintf("No %s mode found in query", $this->prefix));
- }
- $handlerCls = Auth_OpenID::arrayGet($this->handlers, $mode,
- $this->defaultDecoder($query));
- if (!is_a($handlerCls, 'Auth_OpenID_ServerError')) {
- return call_user_func_array(array($handlerCls, 'fromQuery'),
- array($query));
- } else {
- return $handlerCls;
- }
- }
- function defaultDecoder($query)
- {
- $mode = $query[$this->prefix . 'mode'];
- return new Auth_OpenID_ServerError($query,
- sprintf("No decoder for mode %s", $mode));
- }
- }
- /**
- * An error that indicates an encoding problem occurred.
- *
- * @package OpenID
- */
- class Auth_OpenID_EncodingError {
- function Auth_OpenID_EncodingError(&$response)
- {
- $this->response =& $response;
- }
- }
- /**
- * An error that indicates that a response was already signed.
- *
- * @package OpenID
- */
- class Auth_OpenID_AlreadySigned extends Auth_OpenID_EncodingError {
- // This response is already signed.
- }
- /**
- * An error that indicates that the given return_to is not under the
- * given trust_root.
- *
- * @package OpenID
- */
- class Auth_OpenID_UntrustedReturnURL extends Auth_OpenID_ServerError {
- function Auth_OpenID_UntrustedReturnURL($return_to, $trust_root)
- {
- global $_Auth_OpenID_OpenID_Prefix;
- $query = array(
- $_Auth_OpenID_OpenID_Prefix . 'return_to' => $return_to,
- $_Auth_OpenID_OpenID_Prefix . 'trust_root' => $trust_root);
- parent::Auth_OpenID_ServerError($query);
- }
- function toString()
- {
- global $_Auth_OpenID_OpenID_Prefix;
- $return_to = $this->query[$_Auth_OpenID_OpenID_Prefix . 'return_to'];
- $trust_root = $this->query[$_Auth_OpenID_OpenID_Prefix . 'trust_root'];
- return sprintf("return_to %s not under trust_root %s",
- $return_to, $trust_root);
- }
- }
- /**
- * An object that implements the OpenID protocol for a single URL.
- *
- * Use this object by calling getOpenIDResponse when you get any
- * request for the server URL.
- *
- * @package OpenID
- */
- class Auth_OpenID_Server {
- function Auth_OpenID_Server(&$store)
- {
- $this->store =& $store;
- $this->signatory =& new Auth_OpenID_Signatory($this->store);
- $this->encoder =& new Auth_OpenID_SigningEncoder($this->signatory);
- $this->decoder =& new Auth_OpenID_Decoder();
- }
- /**
- * Handle a request. Given an Auth_OpenID_Request object, call
- * the appropriate Auth_OpenID_Server method to process the
- * request and generate a response.
- *
- * @param Auth_OpenID_Request $request An Auth_OpenID_Request
- * returned by Auth_OpenID_Server::decodeRequest.
- *
- * @return Auth_OpenID_Response $response A response object
- * capable of generating a user-agent reply.
- */
- function handleRequest($request)
- {
- if (method_exists($this, "openid_" . $request->mode)) {
- $handler = array($this, "openid_" . $request->mode);
- return call_user_func($handler, $request);
- }
- return null;
- }
- /**
- * The callback for 'check_authentication' messages.
- *
- * @access private
- */
- function openid_check_authentication(&$request)
- {
- return $request->answer($this->signatory);
- }
- /**
- * The callback for 'associate' messages.
- *
- * @access private
- */
- function openid_associate(&$request)
- {
- $assoc = $this->signatory->createAssociation(false);
- return $request->answer($assoc);
- }
- /**
- * Encodes as response in the appropriate format suitable for
- * sending to the user agent.
- */
- function encodeResponse(&$response)
- {
- return $this->encoder->encode($response);
- }
- /**
- * Decodes a query args array into the appropriate
- * Auth_OpenID_Request object.
- */
- function decodeRequest(&$query)
- {
- return $this->decoder->decode($query);
- }
- }
- ?>