[ Index ]

PHP Cross Reference of Joomla 1.5.26 DE

title

Body

[close]

/libraries/openid/Auth/OpenID/ -> Server.php (source)

   1  <?php
   2  
   3  /**
   4   * OpenID server protocol and logic.
   5   * 
   6   * Overview
   7   *
   8   * An OpenID server must perform three tasks:
   9   *
  10   *  1. Examine the incoming request to determine its nature and validity.
  11   *  2. Make a decision about how to respond to this request.
  12   *  3. Format the response according to the protocol.
  13   * 
  14   * The first and last of these tasks may performed by the {@link
  15   * Auth_OpenID_Server::decodeRequest()} and {@link
  16   * Auth_OpenID_Server::encodeResponse} methods.  Who gets to do the
  17   * intermediate task -- deciding how to respond to the request -- will
  18   * depend on what type of request it is.
  19   *
  20   * If it's a request to authenticate a user (a 'checkid_setup' or
  21   * 'checkid_immediate' request), you need to decide if you will assert
  22   * that this user may claim the identity in question.  Exactly how you
  23   * do that is a matter of application policy, but it generally
  24   * involves making sure the user has an account with your system and
  25   * is logged in, checking to see if that identity is hers to claim,
  26   * and verifying with the user that she does consent to releasing that
  27   * information to the party making the request.
  28   *
  29   * Examine the properties of the {@link Auth_OpenID_CheckIDRequest}
  30   * object, and if and when you've come to a decision, form a response
  31   * by calling {@link Auth_OpenID_CheckIDRequest::answer()}.
  32   *
  33   * Other types of requests relate to establishing associations between
  34   * client and server and verifing the authenticity of previous
  35   * communications.  {@link Auth_OpenID_Server} contains all the logic
  36   * and data necessary to respond to such requests; just pass it to
  37   * {@link Auth_OpenID_Server::handleRequest()}.
  38   *
  39   * OpenID Extensions
  40   * 
  41   * Do you want to provide other information for your users in addition
  42   * to authentication?  Version 1.2 of the OpenID protocol allows
  43   * consumers to add extensions to their requests.  For example, with
  44   * sites using the Simple Registration
  45   * Extension
  46   * (http://www.openidenabled.com/openid/simple-registration-extension/),
  47   * a user can agree to have their nickname and e-mail address sent to
  48   * a site when they sign up.
  49   *
  50   * Since extensions do not change the way OpenID authentication works,
  51   * code to handle extension requests may be completely separate from
  52   * the {@link Auth_OpenID_Request} class here.  But you'll likely want
  53   * data sent back by your extension to be signed.  {@link
  54   * Auth_OpenID_ServerResponse} provides methods with which you can add
  55   * data to it which can be signed with the other data in the OpenID
  56   * signature.
  57   *
  58   * For example:
  59   *
  60   * <pre>  // when request is a checkid_* request
  61   *  $response = $request->answer(true);
  62   *  // this will a signed 'openid.sreg.timezone' parameter to the response
  63   *  response.addField('sreg', 'timezone', 'America/Los_Angeles')</pre>
  64   *
  65   * Stores
  66   *
  67   * The OpenID server needs to maintain state between requests in order
  68   * to function.  Its mechanism for doing this is called a store.  The
  69   * store interface is defined in Interface.php.  Additionally, several
  70   * concrete store implementations are provided, so that most sites
  71   * won't need to implement a custom store.  For a store backed by flat
  72   * files on disk, see {@link Auth_OpenID_FileStore}.  For stores based
  73   * on MySQL, SQLite, or PostgreSQL, see the {@link
  74   * Auth_OpenID_SQLStore} subclasses.
  75   *
  76   * Upgrading
  77   *
  78   * The keys by which a server looks up associations in its store have
  79   * changed in version 1.2 of this library.  If your store has entries
  80   * created from version 1.0 code, you should empty it.
  81   *
  82   * PHP versions 4 and 5
  83   *
  84   * LICENSE: See the COPYING file included in this distribution.
  85   *
  86   * @package OpenID
  87   * @author JanRain, Inc. <openid@janrain.com>
  88   * @copyright 2005-2008 Janrain, Inc.
  89   * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
  90   */
  91  
  92  // Do not allow direct access
  93  defined( '_JEXEC' ) or die( 'Restricted access' );
  94  
  95  /**
  96   * Required imports
  97   */
  98  require_once "Auth/OpenID.php";
  99  require_once "Auth/OpenID/Association.php";
 100  require_once "Auth/OpenID/CryptUtil.php";
 101  require_once "Auth/OpenID/BigMath.php";
 102  require_once "Auth/OpenID/DiffieHellman.php";
 103  require_once "Auth/OpenID/KVForm.php";
 104  require_once "Auth/OpenID/TrustRoot.php";
 105  require_once "Auth/OpenID/ServerRequest.php";
 106  require_once "Auth/OpenID/Message.php";
 107  require_once "Auth/OpenID/Nonce.php";
 108  
 109  define('AUTH_OPENID_HTTP_OK', 200);
 110  define('AUTH_OPENID_HTTP_REDIRECT', 302);
 111  define('AUTH_OPENID_HTTP_ERROR', 400);
 112  
 113  /**
 114   * @access private
 115   */
 116  global $_Auth_OpenID_Request_Modes;
 117  $_Auth_OpenID_Request_Modes = array('checkid_setup',
 118                                      'checkid_immediate');
 119  
 120  /**
 121   * @access private
 122   */
 123  define('Auth_OpenID_ENCODE_KVFORM', 'kfvorm');
 124  
 125  /**
 126   * @access private
 127   */
 128  define('Auth_OpenID_ENCODE_URL', 'URL/redirect');
 129  
 130  /**
 131   * @access private
 132   */
 133  define('Auth_OpenID_ENCODE_HTML_FORM', 'HTML form');
 134  
 135  /**
 136   * @access private
 137   */
 138  function Auth_OpenID_isError($obj, $cls = 'Auth_OpenID_ServerError')
 139  {
 140      return is_a($obj, $cls);
 141  }
 142  
 143  /**
 144   * An error class which gets instantiated and returned whenever an
 145   * OpenID protocol error occurs.  Be prepared to use this in place of
 146   * an ordinary server response.
 147   *
 148   * @package OpenID
 149   */
 150  class Auth_OpenID_ServerError {
 151      /**
 152       * @access private
 153       */
 154      function Auth_OpenID_ServerError($message = null, $text = null,
 155                                       $reference = null, $contact = null)
 156      {
 157          $this->message = $message;
 158          $this->text = $text;
 159          $this->contact = $contact;
 160          $this->reference = $reference;
 161      }
 162  
 163      function getReturnTo()
 164      {
 165          if ($this->message &&
 166              $this->message->hasKey(Auth_OpenID_OPENID_NS, 'return_to')) {
 167              return $this->message->getArg(Auth_OpenID_OPENID_NS,
 168                                            'return_to');
 169          } else {
 170              return null;
 171          }
 172      }
 173  
 174      /**
 175       * Returns the return_to URL for the request which caused this
 176       * error.
 177       */
 178      function hasReturnTo()
 179      {
 180          return $this->getReturnTo() !== null;
 181      }
 182  
 183      /**
 184       * Encodes this error's response as a URL suitable for
 185       * redirection.  If the response has no return_to, another
 186       * Auth_OpenID_ServerError is returned.
 187       */
 188      function encodeToURL()
 189      {
 190          if (!$this->message) {
 191              return null;
 192          }
 193  
 194          $msg = $this->toMessage();
 195          return $msg->toURL($this->getReturnTo());
 196      }
 197  
 198      /**
 199       * Encodes the response to key-value form.  This is a
 200       * machine-readable format used to respond to messages which came
 201       * directly from the consumer and not through the user-agent.  See
 202       * the OpenID specification.
 203       */
 204      function encodeToKVForm()
 205      {
 206          return Auth_OpenID_KVForm::fromArray(
 207                                        array('mode' => 'error',
 208                                              'error' => $this->toString()));
 209      }
 210  
 211      function toFormMarkup($form_tag_attrs=null)
 212      {
 213          $msg = $this->toMessage();
 214          return $msg->toFormMarkup($this->getReturnTo(), $form_tag_attrs);
 215      }
 216  
 217      function toHTML($form_tag_attrs=null)
 218      {
 219          return Auth_OpenID::autoSubmitHTML(
 220                        $this->toFormMarkup($form_tag_attrs));
 221      }
 222  
 223      function toMessage()
 224      {
 225          // Generate a Message object for sending to the relying party,
 226          // after encoding.
 227          $namespace = $this->message->getOpenIDNamespace();
 228          $reply = new Auth_OpenID_Message($namespace);
 229          $reply->setArg(Auth_OpenID_OPENID_NS, 'mode', 'error');
 230          $reply->setArg(Auth_OpenID_OPENID_NS, 'error', $this->toString());
 231  
 232          if ($this->contact !== null) {
 233              $reply->setArg(Auth_OpenID_OPENID_NS, 'contact', $this->contact);
 234          }
 235  
 236          if ($this->reference !== null) {
 237              $reply->setArg(Auth_OpenID_OPENID_NS, 'reference',
 238                             $this->reference);
 239          }
 240  
 241          return $reply;
 242      }
 243  
 244      /**
 245       * Returns one of Auth_OpenID_ENCODE_URL,
 246       * Auth_OpenID_ENCODE_KVFORM, or null, depending on the type of
 247       * encoding expected for this error's payload.
 248       */
 249      function whichEncoding()
 250      {
 251          global $_Auth_OpenID_Request_Modes;
 252  
 253          if ($this->hasReturnTo()) {
 254              if ($this->message->isOpenID2() &&
 255                  (strlen($this->encodeToURL()) >
 256                     Auth_OpenID_OPENID1_URL_LIMIT)) {
 257                  return Auth_OpenID_ENCODE_HTML_FORM;
 258              } else {
 259                  return Auth_OpenID_ENCODE_URL;
 260              }
 261          }
 262  
 263          if (!$this->message) {
 264              return null;
 265          }
 266  
 267          $mode = $this->message->getArg(Auth_OpenID_OPENID_NS,
 268                                         'mode');
 269  
 270          if ($mode) {
 271              if (!in_array($mode, $_Auth_OpenID_Request_Modes)) {
 272                  return Auth_OpenID_ENCODE_KVFORM;
 273              }
 274          }
 275          return null;
 276      }
 277  
 278      /**
 279       * Returns this error message.
 280       */
 281      function toString()
 282      {
 283          if ($this->text) {
 284              return $this->text;
 285          } else {
 286              return get_class($this) . " error";
 287          }
 288      }
 289  }
 290  
 291  /**
 292   * Error returned by the server code when a return_to is absent from a
 293   * request.
 294   *
 295   * @package OpenID
 296   */
 297  class Auth_OpenID_NoReturnToError extends Auth_OpenID_ServerError {
 298      function Auth_OpenID_NoReturnToError($message = null,
 299                                           $text = "No return_to URL available")
 300      {
 301          parent::Auth_OpenID_ServerError($message, $text);
 302      }
 303  
 304      function toString()
 305      {
 306          return "No return_to available";
 307      }
 308  }
 309  
 310  /**
 311   * An error indicating that the return_to URL is malformed.
 312   *
 313   * @package OpenID
 314   */
 315  class Auth_OpenID_MalformedReturnURL extends Auth_OpenID_ServerError {
 316      function Auth_OpenID_MalformedReturnURL($message, $return_to)
 317      {
 318          $this->return_to = $return_to;
 319          parent::Auth_OpenID_ServerError($message, "malformed return_to URL");
 320      }
 321  }
 322  
 323  /**
 324   * This error is returned when the trust_root value is malformed.
 325   *
 326   * @package OpenID
 327   */
 328  class Auth_OpenID_MalformedTrustRoot extends Auth_OpenID_ServerError {
 329      function Auth_OpenID_MalformedTrustRoot($message = null,
 330                                              $text = "Malformed trust root")
 331      {
 332          parent::Auth_OpenID_ServerError($message, $text);
 333      }
 334  
 335      function toString()
 336      {
 337          return "Malformed trust root";
 338      }
 339  }
 340  
 341  /**
 342   * The base class for all server request classes.
 343   *
 344   * @package OpenID
 345   */
 346  class Auth_OpenID_Request {
 347      var $mode = null;
 348  }
 349  
 350  /**
 351   * A request to verify the validity of a previous response.
 352   *
 353   * @package OpenID
 354   */
 355  class Auth_OpenID_CheckAuthRequest extends Auth_OpenID_Request {
 356      var $mode = "check_authentication";
 357      var $invalidate_handle = null;
 358  
 359      function Auth_OpenID_CheckAuthRequest($assoc_handle, $signed,
 360                                            $invalidate_handle = null)
 361      {
 362          $this->assoc_handle = $assoc_handle;
 363          $this->signed = $signed;
 364          if ($invalidate_handle !== null) {
 365              $this->invalidate_handle = $invalidate_handle;
 366          }
 367          $this->namespace = Auth_OpenID_OPENID2_NS;
 368          $this->message = null;
 369      }
 370  
 371      function fromMessage($message, $server=null)
 372      {
 373          $required_keys = array('assoc_handle', 'sig', 'signed');
 374  
 375          foreach ($required_keys as $k) {
 376              if (!$message->getArg(Auth_OpenID_OPENID_NS, $k)) {
 377                  return new Auth_OpenID_ServerError($message,
 378                      sprintf("%s request missing required parameter %s from \
 379                              query", "check_authentication", $k));
 380              }
 381          }
 382  
 383          $assoc_handle = $message->getArg(Auth_OpenID_OPENID_NS, 'assoc_handle');
 384          $sig = $message->getArg(Auth_OpenID_OPENID_NS, 'sig');
 385  
 386          $signed_list = $message->getArg(Auth_OpenID_OPENID_NS, 'signed');
 387          $signed_list = explode(",", $signed_list);
 388  
 389          $signed = $message;
 390          if ($signed->hasKey(Auth_OpenID_OPENID_NS, 'mode')) {
 391              $signed->setArg(Auth_OpenID_OPENID_NS, 'mode', 'id_res');
 392          }
 393  
 394          $result = new Auth_OpenID_CheckAuthRequest($assoc_handle, $signed);
 395          $result->message = $message;
 396          $result->sig = $sig;
 397          $result->invalidate_handle = $message->getArg(Auth_OpenID_OPENID_NS,
 398                                                        'invalidate_handle');
 399          return $result;
 400      }
 401  
 402      function answer(&$signatory)
 403      {
 404          $is_valid = $signatory->verify($this->assoc_handle, $this->signed);
 405  
 406          // Now invalidate that assoc_handle so it this checkAuth
 407          // message cannot be replayed.
 408          $signatory->invalidate($this->assoc_handle, true);
 409          $response = new Auth_OpenID_ServerResponse($this);
 410  
 411          $response->fields->setArg(Auth_OpenID_OPENID_NS,
 412                                    'is_valid',
 413                                    ($is_valid ? "true" : "false"));
 414  
 415          if ($this->invalidate_handle) {
 416              $assoc = $signatory->getAssociation($this->invalidate_handle,
 417                                                  false);
 418              if (!$assoc) {
 419                  $response->fields->setArg(Auth_OpenID_OPENID_NS,
 420                                            'invalidate_handle',
 421                                            $this->invalidate_handle);
 422              }
 423          }
 424          return $response;
 425      }
 426  }
 427  
 428  /**
 429   * A class implementing plaintext server sessions.
 430   *
 431   * @package OpenID
 432   */
 433  class Auth_OpenID_PlainTextServerSession {
 434      /**
 435       * An object that knows how to handle association requests with no
 436       * session type.
 437       */
 438      var $session_type = 'no-encryption';
 439      var $needs_math = false;
 440      var $allowed_assoc_types = array('HMAC-SHA1', 'HMAC-SHA256');
 441  
 442      function fromMessage($unused_request)
 443      {
 444          return new Auth_OpenID_PlainTextServerSession();
 445      }
 446  
 447      function answer($secret)
 448      {
 449          return array('mac_key' => base64_encode($secret));
 450      }
 451  }
 452  
 453  /**
 454   * A class implementing DH-SHA1 server sessions.
 455   *
 456   * @package OpenID
 457   */
 458  class Auth_OpenID_DiffieHellmanSHA1ServerSession {
 459      /**
 460       * An object that knows how to handle association requests with
 461       * the Diffie-Hellman session type.
 462       */
 463  
 464      var $session_type = 'DH-SHA1';
 465      var $needs_math = true;
 466      var $allowed_assoc_types = array('HMAC-SHA1');
 467      var $hash_func = 'Auth_OpenID_SHA1';
 468  
 469      function Auth_OpenID_DiffieHellmanSHA1ServerSession($dh, $consumer_pubkey)
 470      {
 471          $this->dh = $dh;
 472          $this->consumer_pubkey = $consumer_pubkey;
 473      }
 474  
 475      function getDH($message)
 476      {
 477          $dh_modulus = $message->getArg(Auth_OpenID_OPENID_NS, 'dh_modulus');
 478          $dh_gen = $message->getArg(Auth_OpenID_OPENID_NS, 'dh_gen');
 479  
 480          if ((($dh_modulus === null) && ($dh_gen !== null)) ||
 481              (($dh_gen === null) && ($dh_modulus !== null))) {
 482  
 483              if ($dh_modulus === null) {
 484                  $missing = 'modulus';
 485              } else {
 486                  $missing = 'generator';
 487              }
 488  
 489              return new Auth_OpenID_ServerError($message,
 490                                  'If non-default modulus or generator is '.
 491                                  'supplied, both must be supplied.  Missing '.
 492                                  $missing);
 493          }
 494  
 495          $lib =& Auth_OpenID_getMathLib();
 496  
 497          if ($dh_modulus || $dh_gen) {
 498              $dh_modulus = $lib->base64ToLong($dh_modulus);
 499              $dh_gen = $lib->base64ToLong($dh_gen);
 500              if ($lib->cmp($dh_modulus, 0) == 0 ||
 501                  $lib->cmp($dh_gen, 0) == 0) {
 502                  return new Auth_OpenID_ServerError(
 503                    $message, "Failed to parse dh_mod or dh_gen");
 504              }
 505              $dh = new Auth_OpenID_DiffieHellman($dh_modulus, $dh_gen);
 506          } else {
 507              $dh = new Auth_OpenID_DiffieHellman();
 508          }
 509  
 510          $consumer_pubkey = $message->getArg(Auth_OpenID_OPENID_NS,
 511                                              'dh_consumer_public');
 512          if ($consumer_pubkey === null) {
 513              return new Auth_OpenID_ServerError($message,
 514                                    'Public key for DH-SHA1 session '.
 515                                    'not found in query');
 516          }
 517  
 518          $consumer_pubkey =
 519              $lib->base64ToLong($consumer_pubkey);
 520  
 521          if ($consumer_pubkey === false) {
 522              return new Auth_OpenID_ServerError($message,
 523                                         "dh_consumer_public is not base64");
 524          }
 525  
 526          return array($dh, $consumer_pubkey);
 527      }
 528  
 529      function fromMessage($message)
 530      {
 531          $result = Auth_OpenID_DiffieHellmanSHA1ServerSession::getDH($message);
 532  
 533          if (is_a($result, 'Auth_OpenID_ServerError')) {
 534              return $result;
 535          } else {
 536              list($dh, $consumer_pubkey) = $result;
 537              return new Auth_OpenID_DiffieHellmanSHA1ServerSession($dh,
 538                                                      $consumer_pubkey);
 539          }
 540      }
 541  
 542      function answer($secret)
 543      {
 544          $lib =& Auth_OpenID_getMathLib();
 545          $mac_key = $this->dh->xorSecret($this->consumer_pubkey, $secret,
 546                                          $this->hash_func);
 547          return array(
 548             'dh_server_public' =>
 549                  $lib->longToBase64($this->dh->public),
 550             'enc_mac_key' => base64_encode($mac_key));
 551      }
 552  }
 553  
 554  /**
 555   * A class implementing DH-SHA256 server sessions.
 556   *
 557   * @package OpenID
 558   */
 559  class Auth_OpenID_DiffieHellmanSHA256ServerSession
 560        extends Auth_OpenID_DiffieHellmanSHA1ServerSession {
 561  
 562      var $session_type = 'DH-SHA256';
 563      var $hash_func = 'Auth_OpenID_SHA256';
 564      var $allowed_assoc_types = array('HMAC-SHA256');
 565  
 566      function fromMessage($message)
 567      {
 568          $result = Auth_OpenID_DiffieHellmanSHA1ServerSession::getDH($message);
 569  
 570          if (is_a($result, 'Auth_OpenID_ServerError')) {
 571              return $result;
 572          } else {
 573              list($dh, $consumer_pubkey) = $result;
 574              return new Auth_OpenID_DiffieHellmanSHA256ServerSession($dh,
 575                                                        $consumer_pubkey);
 576          }
 577      }
 578  }
 579  
 580  /**
 581   * A request to associate with the server.
 582   *
 583   * @package OpenID
 584   */
 585  class Auth_OpenID_AssociateRequest extends Auth_OpenID_Request {
 586      var $mode = "associate";
 587  
 588      function getSessionClasses()
 589      {
 590          return array(
 591            'no-encryption' => 'Auth_OpenID_PlainTextServerSession',
 592            'DH-SHA1' => 'Auth_OpenID_DiffieHellmanSHA1ServerSession',
 593            'DH-SHA256' => 'Auth_OpenID_DiffieHellmanSHA256ServerSession');
 594      }
 595  
 596      function Auth_OpenID_AssociateRequest(&$session, $assoc_type)
 597      {
 598          $this->session =& $session;
 599          $this->namespace = Auth_OpenID_OPENID2_NS;
 600          $this->assoc_type = $assoc_type;
 601      }
 602  
 603      function fromMessage($message, $server=null)
 604      {
 605          if ($message->isOpenID1()) {
 606              $session_type = $message->getArg(Auth_OpenID_OPENID_NS,
 607                                               'session_type');
 608  
 609              if ($session_type == 'no-encryption') {
 610                  // oidutil.log('Received OpenID 1 request with a no-encryption '
 611                  //             'assocaition session type. Continuing anyway.')
 612              } else if (!$session_type) {
 613                  $session_type = 'no-encryption';
 614              }
 615          } else {
 616              $session_type = $message->getArg(Auth_OpenID_OPENID_NS,
 617                                               'session_type');
 618              if ($session_type === null) {
 619                  return new Auth_OpenID_ServerError($message,
 620                    "session_type missing from request");
 621              }
 622          }
 623  
 624          $session_class = Auth_OpenID::arrayGet(
 625             Auth_OpenID_AssociateRequest::getSessionClasses(),
 626             $session_type);
 627  
 628          if ($session_class === null) {
 629              return new Auth_OpenID_ServerError($message,
 630                                                 "Unknown session type " .
 631                                                 $session_type);
 632          }
 633  
 634          $session = call_user_func(array($session_class, 'fromMessage'),
 635                                    $message);
 636          if (is_a($session, 'Auth_OpenID_ServerError')) {
 637              return $session;
 638          }
 639  
 640          $assoc_type = $message->getArg(Auth_OpenID_OPENID_NS,
 641                                         'assoc_type', 'HMAC-SHA1');
 642  
 643          if (!in_array($assoc_type, $session->allowed_assoc_types)) {
 644              $fmt = "Session type %s does not support association type %s";
 645              return new Auth_OpenID_ServerError($message,
 646                sprintf($fmt, $session_type, $assoc_type));
 647          }
 648  
 649          $obj = new Auth_OpenID_AssociateRequest($session, $assoc_type);
 650          $obj->message = $message;
 651          $obj->namespace = $message->getOpenIDNamespace();
 652          return $obj;
 653      }
 654  
 655      function answer($assoc)
 656      {
 657          $response = new Auth_OpenID_ServerResponse($this);
 658          $response->fields->updateArgs(Auth_OpenID_OPENID_NS,
 659             array(
 660                   'expires_in' => sprintf('%d', $assoc->getExpiresIn()),
 661                   'assoc_type' => $this->assoc_type,
 662                   'assoc_handle' => $assoc->handle));
 663  
 664          $response->fields->updateArgs(Auth_OpenID_OPENID_NS,
 665             $this->session->answer($assoc->secret));
 666  
 667          if (! ($this->session->session_type == 'no-encryption' 
 668                 && $this->message->isOpenID1())) {
 669              $response->fields->setArg(Auth_OpenID_OPENID_NS,
 670                                        'session_type',
 671                                        $this->session->session_type);
 672          }
 673  
 674          return $response;
 675      }
 676  
 677      function answerUnsupported($text_message,
 678                                 $preferred_association_type=null,
 679                                 $preferred_session_type=null)
 680      {
 681          if ($this->message->isOpenID1()) {
 682              return new Auth_OpenID_ServerError($this->message);
 683          }
 684  
 685          $response = new Auth_OpenID_ServerResponse($this);
 686          $response->fields->setArg(Auth_OpenID_OPENID_NS,
 687                                    'error_code', 'unsupported-type');
 688          $response->fields->setArg(Auth_OpenID_OPENID_NS,
 689                                    'error', $text_message);
 690  
 691          if ($preferred_association_type) {
 692              $response->fields->setArg(Auth_OpenID_OPENID_NS,
 693                                        'assoc_type',
 694                                        $preferred_association_type);
 695          }
 696  
 697          if ($preferred_session_type) {
 698              $response->fields->setArg(Auth_OpenID_OPENID_NS,
 699                                        'session_type',
 700                                        $preferred_session_type);
 701          }
 702  
 703          return $response;
 704      }
 705  }
 706  
 707  /**
 708   * A request to confirm the identity of a user.
 709   *
 710   * @package OpenID
 711   */
 712  class Auth_OpenID_CheckIDRequest extends Auth_OpenID_Request {
 713      /**
 714       * Return-to verification callback.  Default is
 715       * Auth_OpenID_verifyReturnTo from TrustRoot.php.
 716       */
 717      var $verifyReturnTo = 'Auth_OpenID_verifyReturnTo';
 718  
 719      /**
 720       * The mode of this request.
 721       */
 722      var $mode = "checkid_setup"; // or "checkid_immediate"
 723  
 724      /**
 725       * Whether this request is for immediate mode.
 726       */
 727      var $immediate = false;
 728  
 729      /**
 730       * The trust_root value for this request.
 731       */
 732      var $trust_root = null;
 733  
 734      /**
 735       * The OpenID namespace for this request.
 736       * deprecated since version 2.0.2
 737       */
 738      var $namespace;
 739      
 740      function make(&$message, $identity, $return_to, $trust_root = null,
 741                    $immediate = false, $assoc_handle = null, $server = null)
 742      {
 743          if ($server === null) {
 744              return new Auth_OpenID_ServerError($message,
 745                                                 "server must not be null");
 746          }
 747  
 748          if ($return_to &&
 749              !Auth_OpenID_TrustRoot::_parse($return_to)) {
 750              return new Auth_OpenID_MalformedReturnURL($message, $return_to);
 751          }
 752  
 753          $r = new Auth_OpenID_CheckIDRequest($identity, $return_to,
 754                                              $trust_root, $immediate,
 755                                              $assoc_handle, $server);
 756  
 757          $r->namespace = $message->getOpenIDNamespace();
 758          $r->message =& $message;
 759  
 760          if (!$r->trustRootValid()) {
 761              return new Auth_OpenID_UntrustedReturnURL($message,
 762                                                        $return_to,
 763                                                        $trust_root);
 764          } else {
 765              return $r;
 766          }
 767      }
 768  
 769      function Auth_OpenID_CheckIDRequest($identity, $return_to,
 770                                          $trust_root = null, $immediate = false,
 771                                          $assoc_handle = null, $server = null,
 772                                          $claimed_id = null)
 773      {
 774          $this->namespace = Auth_OpenID_OPENID2_NS;
 775          $this->assoc_handle = $assoc_handle;
 776          $this->identity = $identity;
 777          if ($claimed_id === null) {
 778              $this->claimed_id = $identity;
 779          } else {
 780              $this->claimed_id = $claimed_id;
 781          }
 782          $this->return_to = $return_to;
 783          $this->trust_root = $trust_root;
 784          $this->server =& $server;
 785  
 786          if ($immediate) {
 787              $this->immediate = true;
 788              $this->mode = "checkid_immediate";
 789          } else {
 790              $this->immediate = false;
 791              $this->mode = "checkid_setup";
 792          }
 793      }
 794  
 795      function equals($other)
 796      {
 797          return (
 798                  (is_a($other, 'Auth_OpenID_CheckIDRequest')) &&
 799                  ($this->namespace == $other->namespace) &&
 800                  ($this->assoc_handle == $other->assoc_handle) &&
 801                  ($this->identity == $other->identity) &&
 802                  ($this->claimed_id == $other->claimed_id) &&
 803                  ($this->return_to == $other->return_to) &&
 804                  ($this->trust_root == $other->trust_root));
 805      }
 806  
 807      /*
 808       * Does the relying party publish the return_to URL for this
 809       * response under the realm? It is up to the provider to set a
 810       * policy for what kinds of realms should be allowed. This
 811       * return_to URL verification reduces vulnerability to data-theft
 812       * attacks based on open proxies, corss-site-scripting, or open
 813       * redirectors.
 814       *
 815       * This check should only be performed after making sure that the
 816       * return_to URL matches the realm.
 817       *
 818       * @return true if the realm publishes a document with the
 819       * return_to URL listed, false if not or if discovery fails
 820       */
 821      function returnToVerified()
 822      {
 823          return call_user_func_array($this->verifyReturnTo,
 824                                      array($this->trust_root, $this->return_to));
 825      }
 826  
 827      function fromMessage(&$message, $server)
 828      {
 829          $mode = $message->getArg(Auth_OpenID_OPENID_NS, 'mode');
 830          $immediate = null;
 831  
 832          if ($mode == "checkid_immediate") {
 833              $immediate = true;
 834              $mode = "checkid_immediate";
 835          } else {
 836              $immediate = false;
 837              $mode = "checkid_setup";
 838          }
 839  
 840          $return_to = $message->getArg(Auth_OpenID_OPENID_NS,
 841                                        'return_to');
 842  
 843          if (($message->isOpenID1()) &&
 844              (!$return_to)) {
 845              $fmt = "Missing required field 'return_to' from checkid request";
 846              return new Auth_OpenID_ServerError($message, $fmt);
 847          }
 848  
 849          $identity = $message->getArg(Auth_OpenID_OPENID_NS,
 850                                       'identity');
 851          $claimed_id = $message->getArg(Auth_OpenID_OPENID_NS, 'claimed_id');
 852          if ($message->isOpenID1()) {
 853              if ($identity === null) {
 854                  $s = "OpenID 1 message did not contain openid.identity";
 855                  return new Auth_OpenID_ServerError($message, $s);
 856              }
 857          } else {
 858              if ($identity && !$claimed_id) {
 859                  $s = "OpenID 2.0 message contained openid.identity but not " .
 860                    "claimed_id";
 861                  return new Auth_OpenID_ServerError($message, $s);
 862              } else if ($claimed_id && !$identity) {
 863                  $s = "OpenID 2.0 message contained openid.claimed_id " .
 864                    "but not identity";
 865                  return new Auth_OpenID_ServerError($message, $s);
 866              }
 867          }
 868  
 869          // There's a case for making self.trust_root be a TrustRoot
 870          // here.  But if TrustRoot isn't currently part of the
 871          // "public" API, I'm not sure it's worth doing.
 872          if ($message->isOpenID1()) {
 873              $trust_root_param = 'trust_root';
 874          } else {
 875              $trust_root_param = 'realm';
 876          }
 877          $trust_root = $message->getArg(Auth_OpenID_OPENID_NS, 
 878                                         $trust_root_param);
 879          if (! $trust_root) {
 880              $trust_root = $return_to;
 881          }
 882  
 883          if (! $message->isOpenID1() && 
 884              ($return_to === null) &&
 885              ($trust_root === null)) {
 886              return new Auth_OpenID_ServerError($message,
 887                "openid.realm required when openid.return_to absent");
 888          }
 889  
 890          $assoc_handle = $message->getArg(Auth_OpenID_OPENID_NS,
 891                                           'assoc_handle');
 892  
 893          $obj = Auth_OpenID_CheckIDRequest::make($message,
 894                                                  $identity,
 895                                                  $return_to,
 896                                                  $trust_root,
 897                                                  $immediate,
 898                                                  $assoc_handle,
 899                                                  $server);
 900  
 901          if (is_a($obj, 'Auth_OpenID_ServerError')) {
 902              return $obj;
 903          }
 904  
 905          $obj->claimed_id = $claimed_id;
 906  
 907          return $obj;
 908      }
 909  
 910      function idSelect()
 911      {
 912          // Is the identifier to be selected by the IDP?
 913          // So IDPs don't have to import the constant
 914          return $this->identity == Auth_OpenID_IDENTIFIER_SELECT;
 915      }
 916  
 917      function trustRootValid()
 918      {
 919          if (!$this->trust_root) {
 920              return true;
 921          }
 922  
 923          $tr = Auth_OpenID_TrustRoot::_parse($this->trust_root);
 924          if ($tr === false) {
 925              return new Auth_OpenID_MalformedTrustRoot($this->message,
 926                                                        $this->trust_root);
 927          }
 928  
 929          if ($this->return_to !== null) {
 930              return Auth_OpenID_TrustRoot::match($this->trust_root,
 931                                                  $this->return_to);
 932          } else {
 933              return true;
 934          }
 935      }
 936  
 937      /**
 938       * Respond to this request.  Return either an
 939       * {@link Auth_OpenID_ServerResponse} or
 940       * {@link Auth_OpenID_ServerError}.
 941       *
 942       * @param bool $allow Allow this user to claim this identity, and
 943       * allow the consumer to have this information?
 944       *
 945       * @param string $server_url DEPRECATED.  Passing $op_endpoint to
 946       * the {@link Auth_OpenID_Server} constructor makes this optional.
 947       *
 948       * When an OpenID 1.x immediate mode request does not succeed, it
 949       * gets back a URL where the request may be carried out in a
 950       * not-so-immediate fashion.  Pass my URL in here (the fully
 951       * qualified address of this server's endpoint, i.e.
 952       * http://example.com/server), and I will use it as a base for the
 953       * URL for a new request.
 954       *
 955       * Optional for requests where {@link $immediate} is false or
 956       * $allow is true.
 957       *
 958       * @param string $identity The OP-local identifier to answer with.
 959       * Only for use when the relying party requested identifier
 960       * selection.
 961       *
 962       * @param string $claimed_id The claimed identifier to answer
 963       * with, for use with identifier selection in the case where the
 964       * claimed identifier and the OP-local identifier differ,
 965       * i.e. when the claimed_id uses delegation.
 966       *
 967       * If $identity is provided but this is not, $claimed_id will
 968       * default to the value of $identity.  When answering requests
 969       * that did not ask for identifier selection, the response
 970       * $claimed_id will default to that of the request.
 971       *
 972       * This parameter is new in OpenID 2.0.
 973       *
 974       * @return mixed
 975       */
 976      function answer($allow, $server_url = null, $identity = null,
 977                      $claimed_id = null)
 978      {
 979          if (!$this->return_to) {
 980              return new Auth_OpenID_NoReturnToError();
 981          }
 982  
 983          if (!$server_url) {
 984              if ((!$this->message->isOpenID1()) &&
 985                  (!$this->server->op_endpoint)) {
 986                  return new Auth_OpenID_ServerError(null,
 987                    "server should be constructed with op_endpoint to " .
 988                    "respond to OpenID 2.0 messages.");
 989              }
 990  
 991              $server_url = $this->server->op_endpoint;
 992          }
 993  
 994          if ($allow) {
 995              $mode = 'id_res';
 996          } else if ($this->message->isOpenID1()) {
 997              if ($this->immediate) {
 998                  $mode = 'id_res';
 999              } else {
1000                  $mode = 'cancel';
1001              }
1002          } else {
1003              if ($this->immediate) {
1004                  $mode = 'setup_needed';
1005              } else {
1006                  $mode = 'cancel';
1007              }
1008          }
1009  
1010          if (!$this->trustRootValid()) {
1011              return new Auth_OpenID_UntrustedReturnURL(null,
1012                                                        $this->return_to,
1013                                                        $this->trust_root);
1014          }
1015  
1016          $response = new Auth_OpenID_ServerResponse($this);
1017  
1018          if ($claimed_id &&
1019              ($this->message->isOpenID1())) {
1020              return new Auth_OpenID_ServerError(null,
1021                "claimed_id is new in OpenID 2.0 and not " .
1022                "available for ".$this->namespace);
1023          }
1024  
1025          if ($identity && !$claimed_id) {
1026              $claimed_id = $identity;
1027          }
1028  
1029          if ($allow) {
1030  
1031              if ($this->identity == Auth_OpenID_IDENTIFIER_SELECT) {
1032                  if (!$identity) {
1033                      return new Auth_OpenID_ServerError(null,
1034                        "This request uses IdP-driven identifier selection.  " .
1035                        "You must supply an identifier in the response.");
1036                  }
1037  
1038                  $response_identity = $identity;
1039                  $response_claimed_id = $claimed_id;
1040  
1041              } else if ($this->identity) {
1042                  if ($identity &&
1043                      ($this->identity != $identity)) {
1044                      $fmt = "Request was for %s, cannot reply with identity %s";
1045                      return new Auth_OpenID_ServerError(null,
1046                        sprintf($fmt, $this->identity, $identity));
1047                  }
1048  
1049                  $response_identity = $this->identity;
1050                  $response_claimed_id = $this->claimed_id;
1051              } else {
1052                  if ($identity) {
1053                      return new Auth_OpenID_ServerError(null,
1054                        "This request specified no identity and " .
1055                        "you supplied ".$identity);
1056                  }
1057  
1058                  $response_identity = null;
1059              }
1060  
1061              if (($this->message->isOpenID1()) &&
1062                  ($response_identity === null)) {
1063                  return new Auth_OpenID_ServerError(null,
1064                    "Request was an OpenID 1 request, so response must " .
1065                    "include an identifier.");
1066              }
1067  
1068              $response->fields->updateArgs(Auth_OpenID_OPENID_NS,
1069                     array('mode' => $mode,
1070                           'return_to' => $this->return_to,
1071                           'response_nonce' => Auth_OpenID_mkNonce()));
1072  
1073              if (!$this->message->isOpenID1()) {
1074                  $response->fields->setArg(Auth_OpenID_OPENID_NS,
1075                                            'op_endpoint', $server_url);
1076              }
1077  
1078              if ($response_identity !== null) {
1079                  $response->fields->setArg(
1080                                            Auth_OpenID_OPENID_NS,
1081                                            'identity',
1082                                            $response_identity);
1083                  if ($this->message->isOpenID2()) {
1084                      $response->fields->setArg(
1085                                                Auth_OpenID_OPENID_NS,
1086                                                'claimed_id',
1087                                                $response_claimed_id);
1088                  }
1089              }
1090  
1091          } else {
1092              $response->fields->setArg(Auth_OpenID_OPENID_NS,
1093                                        'mode', $mode);
1094  
1095              if ($this->immediate) {
1096                  if (($this->message->isOpenID1()) &&
1097                      (!$server_url)) {
1098                      return new Auth_OpenID_ServerError(null,
1099                                   'setup_url is required for $allow=false \
1100                                    in OpenID 1.x immediate mode.');
1101                  }
1102  
1103                  $setup_request =& new Auth_OpenID_CheckIDRequest(
1104                                                  $this->identity,
1105                                                  $this->return_to,
1106                                                  $this->trust_root,
1107                                                  false,
1108                                                  $this->assoc_handle,
1109                                                  $this->server,
1110                                                  $this->claimed_id);
1111                  $setup_request->message = $this->message;
1112  
1113                  $setup_url = $setup_request->encodeToURL($server_url);
1114  
1115                  if ($setup_url === null) {
1116                      return new Auth_OpenID_NoReturnToError();
1117                  }
1118  
1119                  $response->fields->setArg(Auth_OpenID_OPENID_NS,
1120                                            'user_setup_url',
1121                                            $setup_url);
1122              }
1123          }
1124  
1125          return $response;
1126      }
1127  
1128      function encodeToURL($server_url)
1129      {
1130          if (!$this->return_to) {
1131              return new Auth_OpenID_NoReturnToError();
1132          }
1133  
1134          // Imported from the alternate reality where these classes are
1135          // used in both the client and server code, so Requests are
1136          // Encodable too.  That's right, code imported from alternate
1137          // realities all for the love of you, id_res/user_setup_url.
1138  
1139          $q = array('mode' => $this->mode,
1140                     'identity' => $this->identity,
1141                     'claimed_id' => $this->claimed_id,
1142                     'return_to' => $this->return_to);
1143  
1144          if ($this->trust_root) {
1145              if ($this->message->isOpenID1()) {
1146                  $q['trust_root'] = $this->trust_root;
1147              } else {
1148                  $q['realm'] = $this->trust_root;
1149              }
1150          }
1151  
1152          if ($this->assoc_handle) {
1153              $q['assoc_handle'] = $this->assoc_handle;
1154          }
1155  
1156          $response = new Auth_OpenID_Message(
1157              $this->message->getOpenIDNamespace());
1158          $response->updateArgs(Auth_OpenID_OPENID_NS, $q);
1159          return $response->toURL($server_url);
1160      }
1161  
1162      function getCancelURL()
1163      {
1164          if (!$this->return_to) {
1165              return new Auth_OpenID_NoReturnToError();
1166          }
1167  
1168          if ($this->immediate) {
1169              return new Auth_OpenID_ServerError(null,
1170                                                 "Cancel is not an appropriate \
1171                                                 response to immediate mode \
1172                                                 requests.");
1173          }
1174  
1175          $response = new Auth_OpenID_Message(
1176              $this->message->getOpenIDNamespace());
1177          $response->setArg(Auth_OpenID_OPENID_NS, 'mode', 'cancel');
1178          return $response->toURL($this->return_to);
1179      }
1180  }
1181  
1182  /**
1183   * This class encapsulates the response to an OpenID server request.
1184   *
1185   * @package OpenID
1186   */
1187  class Auth_OpenID_ServerResponse {
1188  
1189      function Auth_OpenID_ServerResponse(&$request)
1190      {
1191          $this->request =& $request;
1192          $this->fields = new Auth_OpenID_Message($this->request->namespace);
1193      }
1194  
1195      function whichEncoding()
1196      {
1197        global $_Auth_OpenID_Request_Modes;
1198  
1199          if (in_array($this->request->mode, $_Auth_OpenID_Request_Modes)) {
1200              if ($this->fields->isOpenID2() &&
1201                  (strlen($this->encodeToURL()) >
1202                     Auth_OpenID_OPENID1_URL_LIMIT)) {
1203                  return Auth_OpenID_ENCODE_HTML_FORM;
1204              } else {
1205                  return Auth_OpenID_ENCODE_URL;
1206              }
1207          } else {
1208              return Auth_OpenID_ENCODE_KVFORM;
1209          }
1210      }
1211  
1212      /*
1213       * Returns the form markup for this response.
1214       *
1215       * @return str
1216       */
1217      function toFormMarkup($form_tag_attrs=null)
1218      {
1219          return $this->fields->toFormMarkup($this->request->return_to,
1220                                             $form_tag_attrs);
1221      }
1222  
1223      /*
1224       * Returns an HTML document containing the form markup for this
1225       * response that autosubmits with javascript.
1226       */
1227      function toHTML()
1228      {
1229          return Auth_OpenID::autoSubmitHTML($this->toFormMarkup());
1230      }
1231  
1232      /*
1233       * Returns True if this response's encoding is ENCODE_HTML_FORM.
1234       * Convenience method for server authors.
1235       *
1236       * @return bool
1237       */
1238      function renderAsForm()
1239      {
1240          return $this->whichEncoding() == Auth_OpenID_ENCODE_HTML_FORM;
1241      }
1242  
1243  
1244      function encodeToURL()
1245      {
1246          return $this->fields->toURL($this->request->return_to);
1247      }
1248  
1249      function addExtension($extension_response)
1250      {
1251          $extension_response->toMessage($this->fields);
1252      }
1253  
1254      function needsSigning()
1255      {
1256          return $this->fields->getArg(Auth_OpenID_OPENID_NS,
1257                                       'mode') == 'id_res';
1258      }
1259  
1260      function encodeToKVForm()
1261      {
1262          return $this->fields->toKVForm();
1263      }
1264  }
1265  
1266  /**
1267   * A web-capable response object which you can use to generate a
1268   * user-agent response.
1269   *
1270   * @package OpenID
1271   */
1272  class Auth_OpenID_WebResponse {
1273      var $code = AUTH_OPENID_HTTP_OK;
1274      var $body = "";
1275  
1276      function Auth_OpenID_WebResponse($code = null, $headers = null,
1277                                       $body = null)
1278      {
1279          if ($code) {
1280              $this->code = $code;
1281          }
1282  
1283          if ($headers !== null) {
1284              $this->headers = $headers;
1285          } else {
1286              $this->headers = array();
1287          }
1288  
1289          if ($body !== null) {
1290              $this->body = $body;
1291          }
1292      }
1293  }
1294  
1295  /**
1296   * Responsible for the signature of query data and the verification of
1297   * OpenID signature values.
1298   *
1299   * @package OpenID
1300   */
1301  class Auth_OpenID_Signatory {
1302  
1303      // = 14 * 24 * 60 * 60; # 14 days, in seconds
1304      var $SECRET_LIFETIME = 1209600;
1305  
1306      // keys have a bogus server URL in them because the filestore
1307      // really does expect that key to be a URL.  This seems a little
1308      // silly for the server store, since I expect there to be only one
1309      // server URL.
1310      var $normal_key = 'http://localhost/|normal';
1311      var $dumb_key = 'http://localhost/|dumb';
1312  
1313      /**
1314       * Create a new signatory using a given store.
1315       */
1316      function Auth_OpenID_Signatory(&$store)
1317      {
1318          // assert store is not None
1319          $this->store =& $store;
1320      }
1321  
1322      /**
1323       * Verify, using a given association handle, a signature with
1324       * signed key-value pairs from an HTTP request.
1325       */
1326      function verify($assoc_handle, $message)
1327      {
1328          $assoc = $this->getAssociation($assoc_handle, true);
1329          if (!$assoc) {
1330              // oidutil.log("failed to get assoc with handle %r to verify sig %r"
1331              //             % (assoc_handle, sig))
1332              return false;
1333          }
1334  
1335          return $assoc->checkMessageSignature($message);
1336      }
1337  
1338      /**
1339       * Given a response, sign the fields in the response's 'signed'
1340       * list, and insert the signature into the response.
1341       */
1342      function sign($response)
1343      {
1344          $signed_response = $response;
1345          $assoc_handle = $response->request->assoc_handle;
1346  
1347          if ($assoc_handle) {
1348              // normal mode
1349              $assoc = $this->getAssociation($assoc_handle, false, false);
1350              if (!$assoc || ($assoc->getExpiresIn() <= 0)) {
1351                  // fall back to dumb mode
1352                  $signed_response->fields->setArg(Auth_OpenID_OPENID_NS,
1353                               'invalidate_handle', $assoc_handle);
1354                  $assoc_type = ($assoc ? $assoc->assoc_type : 'HMAC-SHA1');
1355  
1356                  if ($assoc && ($assoc->getExpiresIn() <= 0)) {
1357                      $this->invalidate($assoc_handle, false);
1358                  }
1359  
1360                  $assoc = $this->createAssociation(true, $assoc_type);
1361              }
1362          } else {
1363              // dumb mode.
1364              $assoc = $this->createAssociation(true);
1365          }
1366  
1367          $signed_response->fields = $assoc->signMessage(
1368                                        $signed_response->fields);
1369          return $signed_response;
1370      }
1371  
1372      /**
1373       * Make a new association.
1374       */
1375      function createAssociation($dumb = true, $assoc_type = 'HMAC-SHA1')
1376      {
1377          $secret = Auth_OpenID_CryptUtil::getBytes(
1378                      Auth_OpenID_getSecretSize($assoc_type));
1379  
1380          $uniq = base64_encode(Auth_OpenID_CryptUtil::getBytes(4));
1381          $handle = sprintf('{%s}{%x}{%s}', $assoc_type, intval(time()), $uniq);
1382  
1383          $assoc = Auth_OpenID_Association::fromExpiresIn(
1384                        $this->SECRET_LIFETIME, $handle, $secret, $assoc_type);
1385  
1386          if ($dumb) {
1387              $key = $this->dumb_key;
1388          } else {
1389              $key = $this->normal_key;
1390          }
1391  
1392          $this->store->storeAssociation($key, $assoc);
1393          return $assoc;
1394      }
1395  
1396      /**
1397       * Given an association handle, get the association from the
1398       * store, or return a ServerError or null if something goes wrong.
1399       */
1400      function getAssociation($assoc_handle, $dumb, $check_expiration=true)
1401      {
1402          if ($assoc_handle === null) {
1403              return new Auth_OpenID_ServerError(null,
1404                                       "assoc_handle must not be null");
1405          }
1406  
1407          if ($dumb) {
1408              $key = $this->dumb_key;
1409          } else {
1410              $key = $this->normal_key;
1411          }
1412  
1413          $assoc = $this->store->getAssociation($key, $assoc_handle);
1414  
1415          if (($assoc !== null) && ($assoc->getExpiresIn() <= 0)) {
1416              if ($check_expiration) {
1417                  $this->store->removeAssociation($key, $assoc_handle);
1418                  $assoc = null;
1419              }
1420          }
1421  
1422          return $assoc;
1423      }
1424  
1425      /**
1426       * Invalidate a given association handle.
1427       */
1428      function invalidate($assoc_handle, $dumb)
1429      {
1430          if ($dumb) {
1431              $key = $this->dumb_key;
1432          } else {
1433              $key = $this->normal_key;
1434          }
1435          $this->store->removeAssociation($key, $assoc_handle);
1436      }
1437  }
1438  
1439  /**
1440   * Encode an {@link Auth_OpenID_ServerResponse} to an
1441   * {@link Auth_OpenID_WebResponse}.
1442   *
1443   * @package OpenID
1444   */
1445  class Auth_OpenID_Encoder {
1446  
1447      var $responseFactory = 'Auth_OpenID_WebResponse';
1448  
1449      /**
1450       * Encode an {@link Auth_OpenID_ServerResponse} and return an
1451       * {@link Auth_OpenID_WebResponse}.
1452       */
1453      function encode(&$response)
1454      {
1455          $cls = $this->responseFactory;
1456  
1457          $encode_as = $response->whichEncoding();
1458          if ($encode_as == Auth_OpenID_ENCODE_KVFORM) {
1459              $wr = new $cls(null, null, $response->encodeToKVForm());
1460              if (is_a($response, 'Auth_OpenID_ServerError')) {
1461                  $wr->code = AUTH_OPENID_HTTP_ERROR;
1462              }
1463          } else if ($encode_as == Auth_OpenID_ENCODE_URL) {
1464              $location = $response->encodeToURL();
1465              $wr = new $cls(AUTH_OPENID_HTTP_REDIRECT,
1466                             array('location' => $location));
1467          } else if ($encode_as == Auth_OpenID_ENCODE_HTML_FORM) {
1468            $wr = new $cls(AUTH_OPENID_HTTP_OK, array(),
1469                           $response->toFormMarkup());
1470          } else {
1471              return new Auth_OpenID_EncodingError($response);
1472          }
1473          return $wr;
1474      }
1475  }
1476  
1477  /**
1478   * An encoder which also takes care of signing fields when required.
1479   *
1480   * @package OpenID
1481   */
1482  class Auth_OpenID_SigningEncoder extends Auth_OpenID_Encoder {
1483  
1484      function Auth_OpenID_SigningEncoder(&$signatory)
1485      {
1486          $this->signatory =& $signatory;
1487      }
1488  
1489      /**
1490       * Sign an {@link Auth_OpenID_ServerResponse} and return an
1491       * {@link Auth_OpenID_WebResponse}.
1492       */
1493      function encode(&$response)
1494      {
1495          // the isinstance is a bit of a kludge... it means there isn't
1496          // really an adapter to make the interfaces quite match.
1497          if (!is_a($response, 'Auth_OpenID_ServerError') &&
1498              $response->needsSigning()) {
1499  
1500              if (!$this->signatory) {
1501                  return new Auth_OpenID_ServerError(null,
1502                                         "Must have a store to sign request");
1503              }
1504  
1505              if ($response->fields->hasKey(Auth_OpenID_OPENID_NS, 'sig')) {
1506                  return new Auth_OpenID_AlreadySigned($response);
1507              }
1508              $response = $this->signatory->sign($response);
1509          }
1510  
1511          return parent::encode($response);
1512      }
1513  }
1514  
1515  /**
1516   * Decode an incoming query into an Auth_OpenID_Request.
1517   *
1518   * @package OpenID
1519   */
1520  class Auth_OpenID_Decoder {
1521  
1522      function Auth_OpenID_Decoder(&$server)
1523      {
1524          $this->server =& $server;
1525  
1526          $this->handlers = array(
1527              'checkid_setup' => 'Auth_OpenID_CheckIDRequest',
1528              'checkid_immediate' => 'Auth_OpenID_CheckIDRequest',
1529              'check_authentication' => 'Auth_OpenID_CheckAuthRequest',
1530              'associate' => 'Auth_OpenID_AssociateRequest'
1531              );
1532      }
1533  
1534      /**
1535       * Given an HTTP query in an array (key-value pairs), decode it
1536       * into an Auth_OpenID_Request object.
1537       */
1538      function decode($query)
1539      {
1540          if (!$query) {
1541              return null;
1542          }
1543  
1544          $message = Auth_OpenID_Message::fromPostArgs($query);
1545  
1546          if ($message === null) {
1547              /*
1548               * It's useful to have a Message attached to a
1549               * ProtocolError, so we override the bad ns value to build
1550               * a Message out of it.  Kinda kludgy, since it's made of
1551               * lies, but the parts that aren't lies are more useful
1552               * than a 'None'.
1553               */
1554              $old_ns = $query['openid.ns'];
1555  
1556              $query['openid.ns'] = Auth_OpenID_OPENID2_NS;
1557              $message = Auth_OpenID_Message::fromPostArgs($query);
1558              return new Auth_OpenID_ServerError(
1559                    $message,
1560                    sprintf("Invalid OpenID namespace URI: %s", $old_ns));
1561          }
1562  
1563          $mode = $message->getArg(Auth_OpenID_OPENID_NS, 'mode');
1564          if (!$mode) {
1565              return new Auth_OpenID_ServerError($message,
1566                                                 "No mode value in message");
1567          }
1568  
1569          if (Auth_OpenID::isFailure($mode)) {
1570              return new Auth_OpenID_ServerError($message,
1571                                                 $mode->message);
1572          }
1573  
1574          $handlerCls = Auth_OpenID::arrayGet($this->handlers, $mode,
1575                                              $this->defaultDecoder($message));
1576  
1577          if (!is_a($handlerCls, 'Auth_OpenID_ServerError')) {
1578              return call_user_func_array(array($handlerCls, 'fromMessage'),
1579                                          array($message, $this->server));
1580          } else {
1581              return $handlerCls;
1582          }
1583      }
1584  
1585      function defaultDecoder($message)
1586      {
1587          $mode = $message->getArg(Auth_OpenID_OPENID_NS, 'mode');
1588  
1589          if (Auth_OpenID::isFailure($mode)) {
1590              return new Auth_OpenID_ServerError($message,
1591                                                 $mode->message);
1592          }
1593  
1594          return new Auth_OpenID_ServerError($message,
1595                         sprintf("Unrecognized OpenID mode %s", $mode));
1596      }
1597  }
1598  
1599  /**
1600   * An error that indicates an encoding problem occurred.
1601   *
1602   * @package OpenID
1603   */
1604  class Auth_OpenID_EncodingError {
1605      function Auth_OpenID_EncodingError(&$response)
1606      {
1607          $this->response =& $response;
1608      }
1609  }
1610  
1611  /**
1612   * An error that indicates that a response was already signed.
1613   *
1614   * @package OpenID
1615   */
1616  class Auth_OpenID_AlreadySigned extends Auth_OpenID_EncodingError {
1617      // This response is already signed.
1618  }
1619  
1620  /**
1621   * An error that indicates that the given return_to is not under the
1622   * given trust_root.
1623   *
1624   * @package OpenID
1625   */
1626  class Auth_OpenID_UntrustedReturnURL extends Auth_OpenID_ServerError {
1627      function Auth_OpenID_UntrustedReturnURL($message, $return_to,
1628                                              $trust_root)
1629      {
1630          parent::Auth_OpenID_ServerError($message, "Untrusted return_to URL");
1631          $this->return_to = $return_to;
1632          $this->trust_root = $trust_root;
1633      }
1634  
1635      function toString()
1636      {
1637          return sprintf("return_to %s not under trust_root %s",
1638                         $this->return_to, $this->trust_root);
1639      }
1640  }
1641  
1642  /**
1643   * I handle requests for an OpenID server.
1644   *
1645   * Some types of requests (those which are not checkid requests) may
1646   * be handed to my {@link handleRequest} method, and I will take care
1647   * of it and return a response.
1648   *
1649   * For your convenience, I also provide an interface to {@link
1650   * Auth_OpenID_Decoder::decode()} and {@link
1651   * Auth_OpenID_SigningEncoder::encode()} through my methods {@link
1652   * decodeRequest} and {@link encodeResponse}.
1653   *
1654   * All my state is encapsulated in an {@link Auth_OpenID_OpenIDStore}.
1655   *
1656   * Example:
1657   *
1658   * <pre> $oserver = new Auth_OpenID_Server(Auth_OpenID_FileStore($data_path),
1659   *                                   "http://example.com/op");
1660   * $request = $oserver->decodeRequest();
1661   * if (in_array($request->mode, array('checkid_immediate',
1662   *                                    'checkid_setup'))) {
1663   *     if ($app->isAuthorized($request->identity, $request->trust_root)) {
1664   *         $response = $request->answer(true);
1665   *     } else if ($request->immediate) {
1666   *         $response = $request->answer(false);
1667   *     } else {
1668   *         $app->showDecidePage($request);
1669   *         return;
1670   *     }
1671   * } else {
1672   *     $response = $oserver->handleRequest($request);
1673   * }
1674   *
1675   * $webresponse = $oserver->encode($response);</pre>
1676   *
1677   * @package OpenID
1678   */
1679  class Auth_OpenID_Server {
1680      function Auth_OpenID_Server(&$store, $op_endpoint=null)
1681      {
1682          $this->store =& $store;
1683          $this->signatory =& new Auth_OpenID_Signatory($this->store);
1684          $this->encoder =& new Auth_OpenID_SigningEncoder($this->signatory);
1685          $this->decoder =& new Auth_OpenID_Decoder($this);
1686          $this->op_endpoint = $op_endpoint;
1687          $this->negotiator =& Auth_OpenID_getDefaultNegotiator();
1688      }
1689  
1690      /**
1691       * Handle a request.  Given an {@link Auth_OpenID_Request} object,
1692       * call the appropriate {@link Auth_OpenID_Server} method to
1693       * process the request and generate a response.
1694       *
1695       * @param Auth_OpenID_Request $request An {@link Auth_OpenID_Request}
1696       * returned by {@link Auth_OpenID_Server::decodeRequest()}.
1697       *
1698       * @return Auth_OpenID_ServerResponse $response A response object
1699       * capable of generating a user-agent reply.
1700       */
1701      function handleRequest($request)
1702      {
1703          if (method_exists($this, "openid_" . $request->mode)) {
1704              $handler = array($this, "openid_" . $request->mode);
1705              return call_user_func($handler, $request);
1706          }
1707          return null;
1708      }
1709  
1710      /**
1711       * The callback for 'check_authentication' messages.
1712       */
1713      function openid_check_authentication(&$request)
1714      {
1715          return $request->answer($this->signatory);
1716      }
1717  
1718      /**
1719       * The callback for 'associate' messages.
1720       */
1721      function openid_associate(&$request)
1722      {
1723          $assoc_type = $request->assoc_type;
1724          $session_type = $request->session->session_type;
1725          if ($this->negotiator->isAllowed($assoc_type, $session_type)) {
1726              $assoc = $this->signatory->createAssociation(false,
1727                                                           $assoc_type);
1728              return $request->answer($assoc);
1729          } else {
1730              $message = sprintf('Association type %s is not supported with '.
1731                                 'session type %s', $assoc_type, $session_type);
1732              list($preferred_assoc_type, $preferred_session_type) =
1733                  $this->negotiator->getAllowedType();
1734              return $request->answerUnsupported($message,
1735                                                 $preferred_assoc_type,
1736                                                 $preferred_session_type);
1737          }
1738      }
1739  
1740      /**
1741       * Encodes as response in the appropriate format suitable for
1742       * sending to the user agent.
1743       */
1744      function encodeResponse(&$response)
1745      {
1746          return $this->encoder->encode($response);
1747      }
1748  
1749      /**
1750       * Decodes a query args array into the appropriate
1751       * {@link Auth_OpenID_Request} object.
1752       */
1753      function decodeRequest($query=null)
1754      {
1755          if ($query === null) {
1756              $query = Auth_OpenID::getQuery();
1757          }
1758  
1759          return $this->decoder->decode($query);
1760      }
1761  }
1762  
1763  ?>


Generated: Wed Mar 28 15:54:07 2012 Cross-referenced by PHPXref 0.7.1