Add OpenID auth for editing pages
[busui.git] / lib / openid-php / Auth / OpenID / SReg.php
blob:a/lib/openid-php/Auth/OpenID/SReg.php -> blob:b/lib/openid-php/Auth/OpenID/SReg.php
  <?php
   
  /**
  * Simple registration request and response parsing and object
  * representation.
  *
  * This module contains objects representing simple registration
  * requests and responses that can be used with both OpenID relying
  * parties and OpenID providers.
  *
  * 1. The relying party creates a request object and adds it to the
  * {@link Auth_OpenID_AuthRequest} object before making the
  * checkid request to the OpenID provider:
  *
  * $sreg_req = Auth_OpenID_SRegRequest::build(array('email'));
  * $auth_request->addExtension($sreg_req);
  *
  * 2. The OpenID provider extracts the simple registration request
  * from the OpenID request using {@link
  * Auth_OpenID_SRegRequest::fromOpenIDRequest}, gets the user's
  * approval and data, creates an {@link Auth_OpenID_SRegResponse}
  * object and adds it to the id_res response:
  *
  * $sreg_req = Auth_OpenID_SRegRequest::fromOpenIDRequest(
  * $checkid_request);
  * // [ get the user's approval and data, informing the user that
  * // the fields in sreg_response were requested ]
  * $sreg_resp = Auth_OpenID_SRegResponse::extractResponse(
  * $sreg_req, $user_data);
  * $sreg_resp->toMessage($openid_response->fields);
  *
  * 3. The relying party uses {@link
  * Auth_OpenID_SRegResponse::fromSuccessResponse} to extract the data
  * from the OpenID response:
  *
  * $sreg_resp = Auth_OpenID_SRegResponse::fromSuccessResponse(
  * $success_response);
  *
  * @package OpenID
  */
   
  /**
  * Import message and extension internals.
  */
  require_once 'Auth/OpenID/Message.php';
  require_once 'Auth/OpenID/Extension.php';
   
  // The data fields that are listed in the sreg spec
  global $Auth_OpenID_sreg_data_fields;
  $Auth_OpenID_sreg_data_fields = array(
  'fullname' => 'Full Name',
  'nickname' => 'Nickname',
  'dob' => 'Date of Birth',
  'email' => 'E-mail Address',
  'gender' => 'Gender',
  'postcode' => 'Postal Code',
  'country' => 'Country',
  'language' => 'Language',
  'timezone' => 'Time Zone');
   
  /**
  * Check to see that the given value is a valid simple registration
  * data field name. Return true if so, false if not.
  */
  function Auth_OpenID_checkFieldName($field_name)
  {
  global $Auth_OpenID_sreg_data_fields;
   
  if (!in_array($field_name, array_keys($Auth_OpenID_sreg_data_fields))) {
  return false;
  }
  return true;
  }
   
  // URI used in the wild for Yadis documents advertising simple
  // registration support
  define('Auth_OpenID_SREG_NS_URI_1_0', 'http://openid.net/sreg/1.0');
   
  // URI in the draft specification for simple registration 1.1
  // <http://openid.net/specs/openid-simple-registration-extension-1_1-01.html>
  define('Auth_OpenID_SREG_NS_URI_1_1', 'http://openid.net/extensions/sreg/1.1');
   
  // This attribute will always hold the preferred URI to use when
  // adding sreg support to an XRDS file or in an OpenID namespace
  // declaration.
  define('Auth_OpenID_SREG_NS_URI', Auth_OpenID_SREG_NS_URI_1_1);
   
  Auth_OpenID_registerNamespaceAlias(Auth_OpenID_SREG_NS_URI_1_1, 'sreg');
   
  /**
  * Does the given endpoint advertise support for simple
  * registration?
  *
  * $endpoint: The endpoint object as returned by OpenID discovery.
  * returns whether an sreg type was advertised by the endpoint
  */
  function Auth_OpenID_supportsSReg($endpoint)
  {
  return ($endpoint->usesExtension(Auth_OpenID_SREG_NS_URI_1_1) ||
  $endpoint->usesExtension(Auth_OpenID_SREG_NS_URI_1_0));
  }
   
  /**
  * A base class for classes dealing with Simple Registration protocol
  * messages.
  *
  * @package OpenID
  */
  class Auth_OpenID_SRegBase extends Auth_OpenID_Extension {
  /**
  * Extract the simple registration namespace URI from the given
  * OpenID message. Handles OpenID 1 and 2, as well as both sreg
  * namespace URIs found in the wild, as well as missing namespace
  * definitions (for OpenID 1)
  *
  * $message: The OpenID message from which to parse simple
  * registration fields. This may be a request or response message.
  *
  * Returns the sreg namespace URI for the supplied message. The
  * message may be modified to define a simple registration
  * namespace.
  *
  * @access private
  */
  static function _getSRegNS($message)
  {
  $alias = null;
  $found_ns_uri = null;
   
  // See if there exists an alias for one of the two defined
  // simple registration types.
  foreach (array(Auth_OpenID_SREG_NS_URI_1_1,
  Auth_OpenID_SREG_NS_URI_1_0) as $sreg_ns_uri) {
  $alias = $message->namespaces->getAlias($sreg_ns_uri);
  if ($alias !== null) {
  $found_ns_uri = $sreg_ns_uri;
  break;
  }
  }
   
  if ($alias === null) {
  // There is no alias for either of the types, so try to
  // add one. We default to using the modern value (1.1)
  $found_ns_uri = Auth_OpenID_SREG_NS_URI_1_1;
  if ($message->namespaces->addAlias(Auth_OpenID_SREG_NS_URI_1_1,
  'sreg') === null) {
  // An alias for the string 'sreg' already exists, but
  // it's defined for something other than simple
  // registration
  return null;
  }
  }
   
  return $found_ns_uri;
  }
  }
   
  /**
  * An object to hold the state of a simple registration request.
  *
  * required: A list of the required fields in this simple registration
  * request
  *
  * optional: A list of the optional fields in this simple registration
  * request
  *
  * @package OpenID
  */
  class Auth_OpenID_SRegRequest extends Auth_OpenID_SRegBase {
   
  var $ns_alias = 'sreg';
   
  /**
  * Initialize an empty simple registration request.
  */
  static function build($required=null, $optional=null,
  $policy_url=null,
  $sreg_ns_uri=Auth_OpenID_SREG_NS_URI,
  $cls='Auth_OpenID_SRegRequest')
  {
  $obj = new $cls();
   
  $obj->required = array();
  $obj->optional = array();
  $obj->policy_url = $policy_url;
  $obj->ns_uri = $sreg_ns_uri;
   
  if ($required) {
  if (!$obj->requestFields($required, true, true)) {
  return null;
  }
  }
   
  if ($optional) {
  if (!$obj->requestFields($optional, false, true)) {
  return null;
  }
  }
   
  return $obj;
  }
   
  /**
  * Create a simple registration request that contains the fields
  * that were requested in the OpenID request with the given
  * arguments
  *
  * $request: The OpenID authentication request from which to
  * extract an sreg request.
  *
  * $cls: name of class to use when creating sreg request object.
  * Used for testing.
  *
  * Returns the newly created simple registration request
  */
  static function fromOpenIDRequest($request, $cls='Auth_OpenID_SRegRequest')
  {
   
  $obj = call_user_func_array(array($cls, 'build'),
  array(null, null, null, Auth_OpenID_SREG_NS_URI, $cls));
   
  // Since we're going to mess with namespace URI mapping, don't
  // mutate the object that was passed in.
  $m = $request->message;
   
  $obj->ns_uri = $obj->_getSRegNS($m);
  $args = $m->getArgs($obj->ns_uri);
   
  if ($args === null || Auth_OpenID::isFailure($args)) {
  return null;
  }
   
  $obj->parseExtensionArgs($args);
   
  return $obj;
  }
   
  /**
  * Parse the unqualified simple registration request parameters
  * and add them to this object.
  *
  * This method is essentially the inverse of
  * getExtensionArgs. This method restores the serialized simple
  * registration request fields.
  *
  * If you are extracting arguments from a standard OpenID
  * checkid_* request, you probably want to use fromOpenIDRequest,
  * which will extract the sreg namespace and arguments from the
  * OpenID request. This method is intended for cases where the
  * OpenID server needs more control over how the arguments are
  * parsed than that method provides.
  *
  * $args == $message->getArgs($ns_uri);
  * $request->parseExtensionArgs($args);
  *
  * $args: The unqualified simple registration arguments
  *
  * strict: Whether requests with fields that are not defined in
  * the simple registration specification should be tolerated (and
  * ignored)
  */
  function parseExtensionArgs($args, $strict=false)
  {
  foreach (array('required', 'optional') as $list_name) {
  $required = ($list_name == 'required');
  $items = Auth_OpenID::arrayGet($args, $list_name);
  if ($items) {
  foreach (explode(',', $items) as $field_name) {
  if (!$this->requestField($field_name, $required, $strict)) {
  if ($strict) {
  return false;
  }
  }
  }
  }
  }
   
  $this->policy_url = Auth_OpenID::arrayGet($args, 'policy_url');
   
  return true;
  }
   
  /**
  * A list of all of the simple registration fields that were
  * requested, whether they were required or optional.
  */
  function allRequestedFields()
  {
  return array_merge($this->required, $this->optional);
  }
   
  /**
  * Have any simple registration fields been requested?
  */
  function wereFieldsRequested()
  {
  return count($this->allRequestedFields());
  }
   
  /**
  * Was this field in the request?
  */
  function contains($field_name)
  {
  return (in_array($field_name, $this->required) ||
  in_array($field_name, $this->optional));
  }
   
  /**
  * Request the specified field from the OpenID user
  *
  * $field_name: the unqualified simple registration field name
  *
  * required: whether the given field should be presented to the
  * user as being a required to successfully complete the request
  *
  * strict: whether to raise an exception when a field is added to
  * a request more than once
  */
  function requestField($field_name,
  $required=false, $strict=false)
  {
  if (!Auth_OpenID_checkFieldName($field_name)) {
  return false;
  }
   
  if ($strict) {
  if ($this->contains($field_name)) {
  return false;
  }
  } else {
  if (in_array($field_name, $this->required)) {
  return true;
  }
   
  if (in_array($field_name, $this->optional)) {
  if ($required) {
  unset($this->optional[array_search($field_name,
  $this->optional)]);
  } else {
  return true;
  }
  }
  }
   
  if ($required) {
  $this->required[] = $field_name;
  } else {
  $this->optional[] = $field_name;
  }
   
  return true;
  }
   
  /**
  * Add the given list of fields to the request
  *
  * field_names: The simple registration data fields to request
  *
  * required: Whether these values should be presented to the user
  * as required
  *
  * strict: whether to raise an exception when a field is added to
  * a request more than once
  */
  function requestFields($field_names, $required=false, $strict=false)
  {
  if (!is_array($field_names)) {
  return false;
  }
   
  foreach ($field_names as $field_name) {
  if (!$this->requestField($field_name, $required, $strict=$strict)) {
  return false;
  }
  }