Support Joomla!

Joomla! 1.5 Documentation

Packages

Package: OpenID

Developer Network License

The Joomla! Developer Network content is © copyright 2006 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution- NonCommercial- ShareAlike 2.5
Source code for file /openid/Auth/OpenID/Server.php

Documentation is available at Server.php

  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
  15.  * 'decodeRequest' and 'encodeResponse' methods of the
  16.  * Auth_OpenID_Server object.  Who gets to do the intermediate task --
  17.  * deciding how to respond to the request -- will depend on what type
  18.  * 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 Auth_OpenID_CheckIDRequest object,
  30.  * and if and when you've come to a decision, form a response by
  31.  * calling 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.  Auth_OpenID_Server contains all the logic and data
  36.  * necessary to respond to such requests; just pass it to
  37.  * 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 Auth_OpenID_Request class here.  But you'll likely want data
  53.  * sent back by your extension to be signed.  Auth_OpenID_Response
  54.  * provides methods with which you can add data to it which can be
  55.  * signed with the other data in the OpenID signature.
  56.  *
  57.  * For example:
  58.  *
  59.  *   //  when request is a checkid_* request
  60.  *   response = request.answer(True)
  61.  *   // this will a signed 'openid.sreg.timezone' parameter to the response
  62.  *   response.addField('sreg', 'timezone', 'America/Los_Angeles')
  63.  *
  64.  * Stores
  65.  *
  66.  * The OpenID server needs to maintain state between requests in order
  67.  * to function.  Its mechanism for doing this is called a store.  The
  68.  * store interface is defined in Interface.php.  Additionally, several
  69.  * concrete store implementations are provided, so that most sites
  70.  * won't need to implement a custom store.  For a store backed by flat
  71.  * files on disk, see Auth_OpenID_FileStore.  For stores based on
  72.  * MySQL, SQLite, or PostgreSQL, see the Auth_OpenID_SQLStore
  73.  * subclasses.
  74.  *
  75.  * Upgrading
  76.  *
  77.  * The keys by which a server looks up associations in its store have
  78.  * changed in version 1.2 of this library.  If your store has entries
  79.  * created from version 1.0 code, you should empty it.
  80.  *
  81.  * PHP versions 4 and 5
  82.  *
  83.  * LICENSE: See the COPYING file included in this distribution.
  84.  *
  85.  * @package OpenID
  86.  * @author JanRain, Inc. <[email protected]>
  87.  * @copyright 2005 Janrain, Inc.
  88.  * @license http://www.gnu.org/copyleft/lesser.html LGPL
  89.  */
  90.  
  91. /**
  92.  * Required imports
  93.  */
  94. require_once "Auth/OpenID.php";
  95. require_once "Auth/OpenID/Association.php";
  96. require_once "Auth/OpenID/CryptUtil.php";
  97. require_once "Auth/OpenID/BigMath.php";
  98. require_once "Auth/OpenID/DiffieHellman.php";
  99. require_once "Auth/OpenID/KVForm.php";
  100. require_once "Auth/OpenID/TrustRoot.php";
  101. require_once "Auth/OpenID/ServerRequest.php";
  102.  
  103. define('AUTH_OPENID_HTTP_OK'200);
  104. define('AUTH_OPENID_HTTP_REDIRECT'302);
  105. define('AUTH_OPENID_HTTP_ERROR'400);
  106.  
  107. global $_Auth_OpenID_Request_Modes,
  108.     $_Auth_OpenID_OpenID_Prefix,
  109.     $_Auth_OpenID_Encode_Kvform,
  110.     $_Auth_OpenID_Encode_Url;
  111.  
  112. /**
  113.  * @access private
  114.  */
  115. $_Auth_OpenID_Request_Modes array('checkid_setup',
  116.                                     'checkid_immediate');
  117.  
  118. /**
  119.  * @access private
  120.  */
  121. $_Auth_OpenID_OpenID_Prefix "openid.";
  122.  
  123. /**
  124.  * @access private
  125.  */
  126. $_Auth_OpenID_Encode_Kvform array('kfvorm');
  127.  
  128. /**
  129.  * @access private
  130.  */
  131. $_Auth_OpenID_Encode_Url array('URL/redirect');
  132.  
  133. /**
  134.  * @access private
  135.  */
  136. function _isError($obj$cls 'Auth_OpenID_ServerError')
  137. {
  138.     return is_a($obj$cls);
  139. }
  140.  
  141. /**
  142.  * An error class which gets instantiated and returned whenever an
  143.  * OpenID protocol error occurs.  Be prepared to use this in place of
  144.  * an ordinary server response.
  145.  *
  146.  * @package OpenID
  147.  */
  148.     /**
  149.      * @access private
  150.      */
  151.     function Auth_OpenID_ServerError($query null$message null)
  152.     {
  153.         $this->message $message;
  154.         $this->query $query;
  155.     }
  156.  
  157.     /**
  158.      * Returns the return_to URL for the request which caused this
  159.      * error.
  160.      */
  161.     function hasReturnTo()
  162.     {
  163.         global $_Auth_OpenID_OpenID_Prefix;
  164.         if ($this->query{
  165.             return array_key_exists($_Auth_OpenID_OpenID_Prefix .
  166.                                     'return_to'$this->query);
  167.         else {
  168.             return false;
  169.         }
  170.     }
  171.  
  172.     /**
  173.      * Encodes this error's response as a URL suitable for
  174.      * redirection.  If the response has no return_to, another
  175.      * Auth_OpenID_ServerError is returned.
  176.      */
  177.     function encodeToURL()
  178.     {
  179.         global $_Auth_OpenID_OpenID_Prefix;
  180.         $return_to Auth_OpenID::arrayGet($this->query,
  181.                                            $_Auth_OpenID_OpenID_Prefix .
  182.                                            'return_to');
  183.         if (!$return_to{
  184.             return new Auth_OpenID_ServerError(null"no return_to URL");
  185.         }
  186.  
  187.         return Auth_OpenID::appendArgs($return_to,
  188.                             array('openid.mode' => 'error',
  189.                                   'openid.error' => $this->toString()));
  190.     }
  191.  
  192.     /**
  193.      * Encodes the response to key-value form.  This is a
  194.      * machine-readable format used to respond to messages which came
  195.      * directly from the consumer and not through the user-agent.  See
  196.      * the OpenID specification.
  197.      */
  198.     function encodeToKVForm()
  199.     {
  200.         return Auth_OpenID_KVForm::fromArray(
  201.                                       array('mode' => 'error',
  202.                                             'error' => $this->toString()));
  203.     }
  204.  
  205.     /**
  206.      * Returns one of $_Auth_OpenID_Encode_Url,
  207.      * $_Auth_OpenID_Encode_Kvform, or null, depending on the type of
  208.      * encoding expected for this error's payload.
  209.      */
  210.     function whichEncoding()
  211.     {
  212.         global $_Auth_OpenID_Encode_Url,
  213.             $_Auth_OpenID_Encode_Kvform,
  214.             $_Auth_OpenID_Request_Modes;
  215.  
  216.         if ($this->hasReturnTo()) {
  217.             return $_Auth_OpenID_Encode_Url;
  218.         }
  219.  
  220.         $mode Auth_OpenID::arrayGet($this->query'openid.mode');
  221.  
  222.         if ($mode{
  223.             if (!in_array($mode$_Auth_OpenID_Request_Modes)) {
  224.                 return $_Auth_OpenID_Encode_Kvform;
  225.             }
  226.         }
  227.         return null;
  228.     }
  229.  
  230.     /**
  231.      * Returns this error message.
  232.      */
  233.     function toString()
  234.     {
  235.         if ($this->message{
  236.             return $this->message;
  237.         else {
  238.             return get_class($this" error";
  239.         }
  240.     }
  241. }
  242.  
  243. /**
  244.  * An error indicating that the return_to URL is malformed.
  245.  *
  246.  * @package OpenID
  247.  */
  248.     function Auth_OpenID_MalformedReturnURL($query$return_to)
  249.     {
  250.         $this->return_to $return_to;
  251.         parent::Auth_OpenID_ServerError($query"malformed return_to URL");
  252.     }
  253. }
  254.  
  255. /**
  256.  * This error is returned when the trust_root value is malformed.
  257.  *
  258.  * @package OpenID
  259.  */
  260.     function toString()
  261.     {
  262.         return "Malformed trust root";
  263.     }
  264. }
  265.  
  266. /**
  267.  * The base class for all server request classes.
  268.  *
  269.  * @access private
  270.  * @package OpenID
  271.  */
  272. class Auth_OpenID_Request {
  273.     var $mode null;
  274. }
  275.  
  276. /**
  277.  * A request to verify the validity of a previous response.
  278.  *
  279.  * @access private
  280.  * @package OpenID
  281.  */
  282. class Auth_OpenID_CheckAuthRequest extends Auth_OpenID_Request {
  283.     var $mode "check_authentication";
  284.     var $invalidate_handle null;
  285.  
  286.     function Auth_OpenID_CheckAuthRequest($assoc_handle$sig$signed,
  287.                                           $invalidate_handle null)
  288.     {
  289.         $this->assoc_handle $assoc_handle;
  290.         $this->sig $sig;
  291.         $this->signed $signed;
  292.         if ($invalidate_handle !== null{
  293.             $this->invalidate_handle $invalidate_handle;
  294.         }
  295.     }
  296.  
  297.     function fromQuery($query)
  298.     {
  299.         global $_Auth_OpenID_OpenID_Prefix;
  300.  
  301.         $required_keys array('assoc_handle''sig''signed');
  302.  
  303.         foreach ($required_keys as $k{
  304.             if (!array_key_exists($_Auth_OpenID_OpenID_Prefix $k,
  305.                                   $query)) {
  306.                 return new Auth_OpenID_ServerError($query,
  307.                     sprintf("%s request missing required parameter %s from \
  308.                             query""check_authentication"$k));
  309.             }
  310.         }
  311.  
  312.         $assoc_handle $query[$_Auth_OpenID_OpenID_Prefix 'assoc_handle'];
  313.         $sig $query[$_Auth_OpenID_OpenID_Prefix 'sig'];
  314.         $signed_list $query[$_Auth_OpenID_OpenID_Prefix 'signed'];
  315.  
  316.         $signed_list explode(","$signed_list);
  317.         $signed_pairs array();
  318.  
  319.         foreach ($signed_list as $field{
  320.             if ($field == 'mode'{
  321.                 // XXX KLUDGE HAX WEB PROTOCoL BR0KENNN
  322.                 //
  323.                 // openid.mode is currently check_authentication
  324.                 // because that's the mode of this request.  But the
  325.                 // signature was made on something with a different
  326.                 // openid.mode.
  327.                 $value "id_res";
  328.             else {
  329.                 if (array_key_exists($_Auth_OpenID_OpenID_Prefix $field,
  330.                                      $query)) {
  331.                     $value $query[$_Auth_OpenID_OpenID_Prefix $field];
  332.                 else {
  333.                     return new Auth_OpenID_ServerError($query,
  334.                           sprintf("Couldn't find signed field %r in query %s",
  335.                                   $fieldvar_export($querytrue)));
  336.                 }
  337.             }
  338.             $signed_pairs[array($field$value);
  339.         }
  340.  
  341.         $result new Auth_OpenID_CheckAuthRequest($assoc_handle$sig,
  342.                                                    $signed_pairs);
  343.         $result->invalidate_handle Auth_OpenID::arrayGet($query,
  344.                     $_Auth_OpenID_OpenID_Prefix 'invalidate_handle');
  345.         return $result;
  346.     }
  347.  
  348.     function answer(&$signatory)
  349.     {
  350.         $is_valid $signatory->verify($this->assoc_handle$this->sig,
  351.                                        $this->signed);
  352.  
  353.         // Now invalidate that assoc_handle so it this checkAuth
  354.         // message cannot be replayed.
  355.         $signatory->invalidate($this->assoc_handletrue);
  356.         $response new Auth_OpenID_ServerResponse($this);
  357.         $response->fields['is_valid'$is_valid "true" "false";
  358.  
  359.         if ($this->invalidate_handle{
  360.             $assoc $signatory->getAssociation($this->invalidate_handle,
  361.                                                 false);
  362.             if (!$assoc{
  363.                 $response->fields['invalidate_handle'=
  364.                     $this->invalidate_handle;
  365.             }
  366.         }
  367.         return $response;
  368.     }
  369. }
  370.  
  371.     /**
  372.      * An object that knows how to handle association requests with no
  373.      * session type.
  374.      */
  375.     var $session_type = 'plaintext';
  376.  
  377.     function fromQuery($unused_request)
  378.     {
  379.         return new Auth_OpenID_PlainTextServerSession();
  380.     }
  381.  
  382.     function answer($secret)
  383.     {
  384.         return array('mac_key' => base64_encode($secret));
  385.     }
  386. }
  387.  
  388.     /**
  389.      * An object that knows how to handle association requests with
  390.      * the Diffie-Hellman session type.
  391.      */
  392.  
  393.     var $session_type = 'DH-SHA1';
  394.  
  395.     function Auth_OpenID_DiffieHellmanServerSession($dh$consumer_pubkey)
  396.     {
  397.         $this->dh $dh;
  398.         $this->consumer_pubkey $consumer_pubkey;
  399.     }
  400.  
  401.     function fromQuery($query)
  402.     {
  403.         $dh_modulus Auth_OpenID::arrayGet($query'openid.dh_modulus');
  404.         $dh_gen Auth_OpenID::arrayGet($query'openid.dh_gen');
  405.  
  406.         if ((($dh_modulus === null&& ($dh_gen !== null)) ||
  407.             (($dh_gen === null&& ($dh_modulus !== null))) {
  408.  
  409.             if ($dh_modulus === null{
  410.                 $missing 'modulus';
  411.             else {
  412.                 $missing 'generator';
  413.             }
  414.  
  415.             return new Auth_OpenID_ServerError(
  416.                                 'If non-default modulus or generator is '.
  417.                                 'supplied, both must be supplied.  Missing '.
  418.                                 $missing);
  419.         }
  420.  
  421.         $lib =Auth_OpenID_getMathLib();
  422.  
  423.         if ($dh_modulus || $dh_gen{
  424.             $dh_modulus $lib->base64ToLong($dh_modulus);
  425.             $dh_gen $lib->base64ToLong($dh_gen);
  426.             if ($lib->cmp($dh_modulus0== ||
  427.                 $lib->cmp($dh_gen0== 0{
  428.                 return new Auth_OpenID_ServerError(
  429.                   $query"Failed to parse dh_mod or dh_gen");
  430.             }
  431.             $dh new Auth_OpenID_DiffieHellman($dh_modulus$dh_gen);
  432.         else {
  433.             $dh new Auth_OpenID_DiffieHellman();
  434.         }
  435.  
  436.         $consumer_pubkey Auth_OpenID::arrayGet($query,
  437.                                                  'openid.dh_consumer_public');
  438.         if ($consumer_pubkey === null{
  439.             return new Auth_OpenID_ServerError(
  440.                                   'Public key for DH-SHA1 session '.
  441.                                   'not found in query');
  442.         }
  443.  
  444.         $consumer_pubkey =
  445.             $lib->base64ToLong($consumer_pubkey);
  446.  
  447.         if ($consumer_pubkey === false{
  448.             return new Auth_OpenID_ServerError($query,
  449.                                        "dh_consumer_public is not base64");
  450.         }
  451.  
  452.         return new Auth_OpenID_DiffieHellmanServerSession($dh,
  453.                                                           $consumer_pubkey);
  454.     }
  455.  
  456.     function answer($secret)
  457.     {
  458.         $lib =Auth_OpenID_getMathLib();
  459.         $mac_key $this->dh->xorSecret($this->consumer_pubkey$secret);
  460.         return array(
  461.            'dh_server_public' =>
  462.                 $lib->longToBase64($this->dh->public),
  463.            'enc_mac_key' => base64_encode($mac_key));
  464.     }
  465. }
  466.  
  467. /**
  468.  * A request to associate with the server.
  469.  *
  470.  * @access private
  471.  * @package OpenID
  472.  */
  473. class Auth_OpenID_AssociateRequest extends Auth_OpenID_Request {
  474.     var $mode "associate";
  475.     var $assoc_type 'HMAC-SHA1';
  476.  
  477.     function Auth_OpenID_AssociateRequest(&$session)
  478.     {
  479.         $this->session =$session;
  480.     }
  481.  
  482.     function fromQuery($query)
  483.     {
  484.         global $_Auth_OpenID_OpenID_Prefix;
  485.  
  486.         $session_classes array(
  487.                  'DH-SHA1' => 'Auth_OpenID_DiffieHellmanServerSession',
  488.                  null => 'Auth_OpenID_PlainTextServerSession');
  489.  
  490.         $session_type null;
  491.  
  492.         if (array_key_exists($_Auth_OpenID_OpenID_Prefix 'session_type',
  493.                              $query)) {
  494.             $session_type $query[$_Auth_OpenID_OpenID_Prefix .
  495.                                    'session_type'];
  496.         }
  497.  
  498.         if (!array_key_exists($session_type$session_classes)) {
  499.             return new Auth_OpenID_ServerError($query,
  500.                                     "Unknown session type $session_type");
  501.         }
  502.  
  503.         $session_cls $session_classes[$session_type];
  504.         $session call_user_func_array(array($session_cls'fromQuery'),
  505.                                         array($query));
  506.  
  507.         if (($session === null|| (_isError($session))) {
  508.             return new Auth_OpenID_ServerError($query,
  509.                                      "Error parsing $session_type session");
  510.         }
  511.  
  512.         return new Auth_OpenID_AssociateRequest($session);
  513.     }
  514.  
  515.     function answer($assoc)
  516.     {
  517.         $ml =Auth_OpenID_getMathLib();
  518.         $response new Auth_OpenID_ServerResponse($this);
  519.  
  520.         $response->fields array('expires_in' => $assoc->getExpiresIn(),
  521.                                   'assoc_type' => 'HMAC-SHA1',
  522.                                   'assoc_handle' => $assoc->handle);
  523.  
  524.         $r $this->session->answer($assoc->secret);
  525.         foreach ($r as $k => $v{
  526.             $response->fields[$k$v;
  527.         }
  528.  
  529.         if ($this->session->session_type != 'plaintext'{
  530.             $response->fields['session_type'$this->session->session_type;
  531.         }
  532.  
  533.         return $response;
  534.     }
  535. }
  536.  
  537. /**
  538.  * A request to confirm the identity of a user.
  539.  *
  540.  * @access private
  541.  * @package OpenID
  542.  */
  543. class Auth_OpenID_CheckIDRequest extends Auth_OpenID_Request {
  544.     var $mode "checkid_setup"// or "checkid_immediate"
  545.         var $immediate false;
  546.     var $trust_root null;
  547.  
  548.     function make($query$identity$return_to$trust_root null,
  549.                   $immediate false$assoc_handle null)
  550.     {
  551.         if (!Auth_OpenID_TrustRoot::_parse($return_to)) {
  552.             return new Auth_OpenID_MalformedReturnURL($query$return_to);
  553.         }
  554.  
  555.         $r new Auth_OpenID_CheckIDRequest($identity$return_to,
  556.                                             $trust_root$immediate,
  557.                                             $assoc_handle);
  558.  
  559.         if (!$r->trustRootValid()) {
  560.             return new Auth_OpenID_UntrustedReturnURL($return_to,
  561.                                                       $trust_root);
  562.         else {
  563.             return $r;
  564.         }
  565.     }
  566.  
  567.     function Auth_OpenID_CheckIDRequest($identity$return_to,
  568.                                         $trust_root null$immediate false,
  569.                                         $assoc_handle null)
  570.     {
  571.         $this->identity $identity;
  572.         $this->return_to $return_to;
  573.         $this->trust_root $trust_root;
  574.         $this->assoc_handle $assoc_handle;
  575.  
  576.         if ($immediate{
  577.             $this->immediate true;
  578.             $this->mode "checkid_immediate";
  579.         else {
  580.             $this->immediate false;
  581.             $this->mode "checkid_setup";
  582.         }
  583.     }
  584.  
  585.     function fromQuery($query)
  586.     {
  587.         global $_Auth_OpenID_OpenID_Prefix;
  588.  
  589.         $mode $query[$_Auth_OpenID_OpenID_Prefix 'mode'];
  590.         $immediate null;
  591.  
  592.         if ($mode == "checkid_immediate"{
  593.             $immediate true;
  594.             $mode "checkid_immediate";
  595.         else {
  596.             $immediate false;
  597.             $mode "checkid_setup";
  598.         }
  599.  
  600.         $required array('identity',
  601.                           'return_to');
  602.  
  603.         $optional array('trust_root',
  604.                           'assoc_handle');
  605.  
  606.         $values array();
  607.  
  608.         foreach ($required as $field{
  609.             if (array_key_exists($_Auth_OpenID_OpenID_Prefix $field,
  610.                                  $query)) {
  611.                 $value $query[$_Auth_OpenID_OpenID_Prefix $field];
  612.             else {
  613.                 return new Auth_OpenID_ServerError($query,
  614.                                sprintf("Missing required field %s from request",
  615.                                        $field));
  616.             }
  617.             $values[$field$value;
  618.         }
  619.  
  620.         foreach ($optional as $field{
  621.             $value null;
  622.             if (array_key_exists($_Auth_OpenID_OpenID_Prefix $field,
  623.                                  $query)) {
  624.                 $value $query[$_Auth_OpenID_OpenID_Prefix$field];
  625.             }
  626.             if ($value{
  627.                 $values[$field$value;
  628.             }
  629.         }
  630.  
  631.         if (!Auth_OpenID_TrustRoot::_parse($values['return_to'])) {
  632.             return new Auth_OpenID_MalformedReturnURL($query,
  633.                                                       $values['return_to']);
  634.         }
  635.  
  636.         $obj Auth_OpenID_CheckIDRequest::make($query,
  637.                                    $values['identity'],
  638.                                    $values['return_to'],
  639.                                    Auth_OpenID::arrayGet($values,
  640.                                                          'trust_root'null),
  641.                                    $immediate);
  642.  
  643.         if (is_a($obj'Auth_OpenID_ServerError')) {
  644.             return $obj;
  645.         }
  646.  
  647.         if (Auth_OpenID::arrayGet($values'assoc_handle')) {
  648.             $obj->assoc_handle $values['assoc_handle'];
  649.         }
  650.  
  651.         return $obj;
  652.     }
  653.  
  654.     function trustRootValid()
  655.     {
  656.         if (!$this->trust_root{
  657.             return true;
  658.         }
  659.  
  660.         $tr Auth_OpenID_TrustRoot::_parse($this->trust_root);
  661.         if ($tr === false{
  662.             return new Auth_OpenID_MalformedTrustRoot(null$this->trust_root);
  663.         }
  664.  
  665.         return Auth_OpenID_TrustRoot::match($this->trust_root,
  666.                                             $this->return_to);
  667.     }
  668.  
  669.     function answer($allow$server_url null)
  670.     {
  671.         if ($allow || $this->immediate{
  672.             $mode 'id_res';
  673.         else {
  674.             $mode 'cancel';
  675.         }
  676.  
  677.         $response new Auth_OpenID_CheckIDResponse($this$mode);
  678.  
  679.         if ($allow{
  680.             $response->fields['identity'$this->identity;
  681.             $response->fields['return_to'$this->return_to;
  682.             if (!$this->trustRootValid()) {
  683.                 return new Auth_OpenID_UntrustedReturnURL($this->return_to,
  684.                                                           $this->trust_root);
  685.             }
  686.         else {
  687.             $response->signed array();
  688.             if ($this->immediate{
  689.                 if (!$server_url{
  690.                     return new Auth_OpenID_ServerError(null,
  691.                                  'setup_url is required for $allow=false \
  692.                                   in immediate mode.');
  693.                 }
  694.  
  695.                 $setup_request =new Auth_OpenID_CheckIDRequest(
  696.                                                 $this->identity,
  697.                                                 $this->return_to,
  698.                                                 $this->trust_root,
  699.                                                 false,
  700.                                                 $this->assoc_handle);
  701.  
  702.                 $setup_url $setup_request->encodeToURL($server_url);
  703.  
  704.                 $response->fields['user_setup_url'$setup_url;
  705.             }
  706.         }
  707.  
  708.         return $response;
  709.     }
  710.  
  711.     function encodeToURL($server_url)
  712.     {
  713.         global $_Auth_OpenID_OpenID_Prefix;
  714.  
  715.         // Imported from the alternate reality where these classes are
  716.         // used in both the client and server code, so Requests are
  717.         // Encodable too.  That's right, code imported from alternate
  718.         // realities all for the love of you, id_res/user_setup_url.
  719.  
  720.         $q array('mode' => $this->mode,
  721.                    'identity' => $this->identity,
  722.                    'return_to' => $this->return_to);
  723.  
  724.         if ($this->trust_root{
  725.             $q['trust_root'$this->trust_root;
  726.         }
  727.  
  728.         if ($this->assoc_handle{
  729.             $q['assoc_handle'$this->assoc_handle;
  730.         }
  731.  
  732.         $_q array();
  733.  
  734.         foreach ($q as $k => $v{
  735.             $_q[$_Auth_OpenID_OpenID_Prefix $k$v;
  736.         }
  737.  
  738.         return Auth_OpenID::appendArgs($server_url$_q);
  739.     }
  740.  
  741.     function getCancelURL()
  742.     {
  743.         global $_Auth_OpenID_OpenID_Prefix;
  744.  
  745.         if ($this->immediate{
  746.             return new Auth_OpenID_ServerError(null,
  747.                                                "Cancel is not an appropriate \
  748.                                                response to immediate mode \
  749.                                                requests.");
  750.         }
  751.  
  752.         return Auth_OpenID::appendArgs($this->return_to,
  753.                               array($_Auth_OpenID_OpenID_Prefix 'mode' =>
  754.                                     'cancel'));
  755.     }
  756. }
  757.  
  758. /**
  759.  * This class encapsulates the response to an OpenID server request.
  760.  *
  761.  * @access private
  762.  * @package OpenID
  763.  */
  764. class Auth_OpenID_ServerResponse {
  765.  
  766.     function Auth_OpenID_ServerResponse($request)
  767.     {
  768.         $this->request $request;
  769.         $this->fields array();
  770.     }
  771.  
  772.     function whichEncoding()
  773.     {
  774.         global $_Auth_OpenID_Encode_Kvform,
  775.             $_Auth_OpenID_Request_Modes,
  776.             $_Auth_OpenID_Encode_Url;
  777.  
  778.         if (in_array($this->request->mode$_Auth_OpenID_Request_Modes)) {
  779.             return $_Auth_OpenID_Encode_Url;
  780.         else {
  781.             return $_Auth_OpenID_Encode_Kvform;
  782.         }
  783.     }
  784.  
  785.     function encodeToURL()
  786.     {
  787.         global $_Auth_OpenID_OpenID_Prefix;
  788.  
  789.         $fields array();
  790.  
  791.         foreach ($this->fields as $k => $v{
  792.             $fields[$_Auth_OpenID_OpenID_Prefix $k$v;
  793.         }
  794.  
  795.         return Auth_OpenID::appendArgs($this->request->return_to$fields);
  796.     }
  797.  
  798.     function encodeToKVForm()
  799.     {
  800.         return Auth_OpenID_KVForm::fromArray($this->fields);
  801.     }
  802. }
  803.  
  804. /**
  805.  * A response to a checkid request.
  806.  *
  807.  * @access private
  808.  * @package OpenID
  809.  */
  810. class Auth_OpenID_CheckIDResponse extends Auth_OpenID_ServerResponse {
  811.  
  812.     function Auth_OpenID_CheckIDResponse(&$request$mode 'id_res')
  813.     {
  814.         parent::Auth_OpenID_ServerResponse($request);
  815.         $this->fields['mode'$mode;
  816.         $this->signed array();
  817.  
  818.         if ($mode == 'id_res'{
  819.             array_push($this->signed'mode''identity''return_to');
  820.         }
  821.     }
  822.  
  823.     function addField($namespace$key$value$signed true)
  824.     {
  825.         if ($namespace{
  826.             $key sprintf('%s.%s'$namespace$key);
  827.         }
  828.         $this->fields[$key$value;
  829.         if ($signed && !in_array($key$this->signed)) {
  830.             $this->signed[$key;
  831.         }
  832.     }
  833.  
  834.     function addFields($namespace$fields$signed true)
  835.     {
  836.         foreach ($fields as $k => $v{
  837.             $this->addField($namespace$k$v$signed);
  838.         }
  839.     }
  840.  
  841.     function update($namespace$other)
  842.     {
  843.         $namespaced_fields array();
  844.  
  845.         foreach ($other->fields as $k => $v{
  846.             $name sprintf('%s.%s'$namespace$k);
  847.  
  848.             $namespaced_fields[$name$v;
  849.         }
  850.  
  851.         $this->fields array_merge($this->fields$namespaced_fields);
  852.         $this->signed array_merge($this->signed$other->signed);
  853.     }
  854. }
  855.  
  856. /**
  857.  * A web-capable response object which you can use to generate a
  858.  * user-agent response.
  859.  *
  860.  * @package OpenID
  861.  */
  862.     var $code = AUTH_OPENID_HTTP_OK;
  863.     var $body = "";
  864.  
  865.     function Auth_OpenID_WebResponse($code null$headers null,
  866.                                      $body null)
  867.     {
  868.         if ($code{
  869.             $this->code = $code;
  870.         }
  871.  
  872.         if ($headers !== null{
  873.             $this->headers $headers;
  874.         else {
  875.             $this->headers array();
  876.         }
  877.  
  878.         if ($body !== null{
  879.             $this->body = $body;
  880.         }
  881.     }
  882. }
  883.  
  884. /**
  885.  * Responsible for the signature of query data and the verification of
  886.  * OpenID signature values.
  887.  *
  888.  * @package OpenID
  889.  */
  890.  
  891.     // = 14 * 24 * 60 * 60; # 14 days, in seconds
  892.         var $SECRET_LIFETIME = 1209600;
  893.  
  894.     // keys have a bogus server URL in them because the filestore
  895.     // really does expect that key to be a URL.  This seems a little
  896.     // silly for the server store, since I expect there to be only one
  897.     // server URL.
  898.         var $normal_key = 'http://localhost/|normal';
  899.     var $dumb_key = 'http://localhost/|dumb';
  900.  
  901.     /**
  902.      * Create a new signatory using a given store.
  903.      */
  904.     function Auth_OpenID_Signatory(&$store)
  905.     {
  906.         // assert store is not None
  907.         $this->store =$store;
  908.     }
  909.  
  910.     /**
  911.      * Verify, using a given association handle, a signature with
  912.      * signed key-value pairs from an HTTP request.
  913.      */
  914.     function verify($assoc_handle$sig$signed_pairs)
  915.     {
  916.         $assoc $this->getAssociation($assoc_handletrue);
  917.         if (!$assoc{
  918.             // oidutil.log("failed to get assoc with handle %r to verify sig %r"
  919.             //             % (assoc_handle, sig))
  920.             return false;
  921.         }
  922.  
  923.         $expected_sig base64_encode($assoc->sign($signed_pairs));
  924.  
  925.         return $sig == $expected_sig;
  926.     }
  927.  
  928.     /**
  929.      * Given a response, sign the fields in the response's 'signed'
  930.      * list, and insert the signature into the response.
  931.      */
  932.     function sign($response)
  933.     {
  934.         $signed_response $response;
  935.         $assoc_handle $response->request->assoc_handle;
  936.  
  937.         if ($assoc_handle{
  938.             // normal mode
  939.             $assoc $this->getAssociation($assoc_handlefalse);
  940.             if (!$assoc{
  941.                 // fall back to dumb mode
  942.                 $signed_response->fields['invalidate_handle'$assoc_handle;
  943.                 $assoc $this->createAssociation(true);
  944.             }
  945.         else {
  946.             // dumb mode.
  947.             $assoc $this->createAssociation(true);
  948.         }
  949.  
  950.         $signed_response->fields['assoc_handle'$assoc->handle;
  951.         $assoc->addSignature($signed_response->signed,
  952.                              $signed_response->fields'');
  953.         return $signed_response;
  954.     }
  955.  
  956.     /**
  957.      * Make a new association.
  958.      */
  959.     function createAssociation($dumb true$assoc_type 'HMAC-SHA1')
  960.     {
  961.         $secret Auth_OpenID_CryptUtil::getBytes(20);
  962.         $uniq base64_encode(Auth_OpenID_CryptUtil::getBytes(4));
  963.         $handle sprintf('{%s}{%x}{%s}'$assoc_typeintval(time())$uniq);
  964.  
  965.         $assoc Auth_OpenID_Association::fromExpiresIn(
  966.                       $this->SECRET_LIFETIME$handle$secret$assoc_type);
  967.  
  968.         if ($dumb{
  969.             $key $this->dumb_key;
  970.         else {
  971.             $key $this->normal_key;
  972.         }
  973.  
  974.         $this->store->storeAssociation($key$assoc);
  975.         return $assoc;
  976.     }
  977.  
  978.     /**
  979.      * Given an association handle, get the association from the
  980.      * store, or return a ServerError or null if something goes wrong.
  981.      */
  982.     function getAssociation($assoc_handle$dumb)
  983.     {
  984.         if ($assoc_handle === null{
  985.             return new Auth_OpenID_ServerError(null,
  986.                                      "assoc_handle must not be null");
  987.         }
  988.  
  989.         if ($dumb{
  990.             $key $this->dumb_key;
  991.         else {
  992.             $key $this->normal_key;
  993.         }
  994.  
  995.         $assoc $this->store->getAssociation($key$assoc_handle);
  996.  
  997.         if (($assoc !== null&& ($assoc->getExpiresIn(<= 0)) {
  998.             $this->store->removeAssociation($key$assoc_handle);
  999.             $assoc null;
  1000.         }
  1001.  
  1002.         return $assoc;
  1003.     }
  1004.  
  1005.     /**
  1006.      * Invalidate a given association handle.
  1007.      */
  1008.     function invalidate($assoc_handle$dumb)
  1009.     {
  1010.         if ($dumb{
  1011.             $key $this->dumb_key;
  1012.         else {
  1013.             $key $this->normal_key;
  1014.         }
  1015.         $this->store->removeAssociation($key$assoc_handle);
  1016.     }
  1017. }
  1018.  
  1019. /**
  1020.  * Encode an Auth_OpenID_Response to an Auth_OpenID_WebResponse.
  1021.  *
  1022.  * @package OpenID
  1023.  */
  1024.  
  1025.     var $responseFactory = 'Auth_OpenID_WebResponse';
  1026.  
  1027.     /**
  1028.      * Encode an Auth_OpenID_Response and return an
  1029.      * Auth_OpenID_WebResponse.
  1030.      */
  1031.     function encode(&$response)
  1032.     {
  1033.         global $_Auth_OpenID_Encode_Kvform,
  1034.             $_Auth_OpenID_Encode_Url;
  1035.  
  1036.         $cls $this->responseFactory;
  1037.  
  1038.         $encode_as $response->whichEncoding();
  1039.         if ($encode_as == $_Auth_OpenID_Encode_Kvform{
  1040.             $wr new $cls(nullnull$response->encodeToKVForm());
  1041.             if (is_a($response'Auth_OpenID_ServerError')) {
  1042.                 $wr->code AUTH_OPENID_HTTP_ERROR;
  1043.             }
  1044.         else if ($encode_as == $_Auth_OpenID_Encode_Url{
  1045.             $location $response->encodeToURL();
  1046.             $wr new $cls(AUTH_OPENID_HTTP_REDIRECT,
  1047.                            array('location' => $location));
  1048.         else {
  1049.             return new Auth_OpenID_EncodingError($response);
  1050.         }
  1051.         return $wr;
  1052.     }
  1053. }
  1054.  
  1055. /**
  1056.  * Returns true if the given response needs a signature.
  1057.  *
  1058.  * @access private
  1059.  */
  1060. function needsSigning($response)
  1061. {
  1062.     return (in_array($response->request->modearray('checkid_setup',
  1063.                                                      'checkid_immediate')) &&
  1064.             $response->signed);
  1065. }
  1066.  
  1067. /**
  1068.  * An encoder which also takes care of signing fields when required.
  1069.  *
  1070.  * @package OpenID
  1071.  */
  1072.  
  1073.     function Auth_OpenID_SigningEncoder(&$signatory)
  1074.     {
  1075.         $this->signatory =$signatory;
  1076.     }
  1077.  
  1078.     /**
  1079.      * Sign an Auth_OpenID_Response and return an
  1080.      * Auth_OpenID_WebResponse.
  1081.      */
  1082.     function encode(&$response)
  1083.     {
  1084.         // the isinstance is a bit of a kludge... it means there isn't
  1085.         // really an adapter to make the interfaces quite match.
  1086.         if (!is_a($response'Auth_OpenID_ServerError'&&
  1087.             needsSigning($response)) {
  1088.  
  1089.             if (!$this->signatory{
  1090.                 return new Auth_OpenID_ServerError(null,
  1091.                                        "Must have a store to sign request");
  1092.             }
  1093.             if (array_key_exists('sig'$response->fields)) {
  1094.                 return new Auth_OpenID_AlreadySigned($response);
  1095.             }
  1096.             $response $this->signatory->sign($response);
  1097.         }
  1098.         return parent::encode($response);
  1099.     }
  1100. }
  1101.  
  1102. /**
  1103.  * Decode an incoming Auth_OpenID_WebResponse into an
  1104.  * Auth_OpenID_Request.
  1105.  *
  1106.  * @package OpenID
  1107.  */
  1108.  
  1109.     function Auth_OpenID_Decoder()
  1110.     {
  1111.         global $_Auth_OpenID_OpenID_Prefix;
  1112.         $this->prefix $_Auth_OpenID_OpenID_Prefix;
  1113.  
  1114.         $this->handlers array(
  1115.             'checkid_setup' => 'Auth_OpenID_CheckIDRequest',
  1116.             'checkid_immediate' => 'Auth_OpenID_CheckIDRequest',
  1117.             'check_authentication' => 'Auth_OpenID_CheckAuthRequest',
  1118.             'associate' => 'Auth_OpenID_AssociateRequest'
  1119.             );
  1120.     }
  1121.  
  1122.     /**
  1123.      * Given an HTTP query in an array (key-value pairs), decode it
  1124.      * into an Auth_OpenID_Request object.
  1125.      */
  1126.     function decode($query)
  1127.     {
  1128.         if (!$query{
  1129.             return null;
  1130.         }
  1131.  
  1132.         $myquery array();
  1133.  
  1134.         foreach ($query as $k => $v{
  1135.             if (strpos($k$this->prefix=== 0{
  1136.                 $myquery[$k$v;
  1137.             }
  1138.         }
  1139.  
  1140.         if (!$myquery{
  1141.             return null;
  1142.         }
  1143.  
  1144.         $mode Auth_OpenID::arrayGet($myquery$this->prefix 'mode');
  1145.         if (!$mode{
  1146.             return new Auth_OpenID_ServerError($query,
  1147.                            sprintf("No %s mode found in query"$this->prefix));
  1148.         }
  1149.  
  1150.         $handlerCls Auth_OpenID::arrayGet($this->handlers$mode,
  1151.                                             $this->defaultDecoder($query));
  1152.  
  1153.         if (!is_a($handlerCls'Auth_OpenID_ServerError')) {
  1154.             return call_user_func_array(array($handlerCls'fromQuery'),
  1155.                                         array($query));
  1156.         else {
  1157.             return $handlerCls;
  1158.         }
  1159.     }
  1160.  
  1161.     function defaultDecoder($query)
  1162.     {
  1163.         $mode $query[$this->prefix 'mode'];
  1164.         return new Auth_OpenID_ServerError($query,
  1165.                        sprintf("No decoder for mode %s"$mode));
  1166.     }
  1167. }
  1168.  
  1169. /**
  1170.  * An error that indicates an encoding problem occurred.
  1171.  *
  1172.  * @package OpenID
  1173.  */
  1174.     function Auth_OpenID_EncodingError(&$response)
  1175.     {
  1176.         $this->response =$response;
  1177.     }
  1178. }
  1179.  
  1180. /**
  1181.  * An error that indicates that a response was already signed.
  1182.  *
  1183.  * @package OpenID
  1184.  */
  1185.     // This response is already signed.
  1186. }
  1187.  
  1188. /**
  1189.  * An error that indicates that the given return_to is not under the
  1190.  * given trust_root.
  1191.  *
  1192.  * @package OpenID
  1193.  */
  1194.     function Auth_OpenID_UntrustedReturnURL($return_to$trust_root)
  1195.     {
  1196.         global $_Auth_OpenID_OpenID_Prefix;
  1197.  
  1198.         $query array(
  1199.                $_Auth_OpenID_OpenID_Prefix 'return_to' => $return_to,
  1200.                $_Auth_OpenID_OpenID_Prefix 'trust_root' => $trust_root);
  1201.  
  1202.         parent::Auth_OpenID_ServerError($query);
  1203.     }
  1204.  
  1205.     function toString()
  1206.     {
  1207.         global $_Auth_OpenID_OpenID_Prefix;
  1208.  
  1209.         $return_to $this->query[$_Auth_OpenID_OpenID_Prefix 'return_to'];
  1210.         $trust_root $this->query[$_Auth_OpenID_OpenID_Prefix 'trust_root'];
  1211.  
  1212.         return sprintf("return_to %s not under trust_root %s",
  1213.                        $return_to$trust_root);
  1214.     }
  1215. }
  1216.  
  1217. /**
  1218.  * An object that implements the OpenID protocol for a single URL.
  1219.  *
  1220.  * Use this object by calling getOpenIDResponse when you get any
  1221.  * request for the server URL.
  1222.  *
  1223.  * @package OpenID
  1224.  */
  1225.     function Auth_OpenID_Server(&$store)
  1226.     {
  1227.         $this->store =$store;
  1228.         $this->signatory =new Auth_OpenID_Signatory($this->store);
  1229.         $this->encoder =new Auth_OpenID_SigningEncoder($this->signatory);
  1230.         $this->decoder =new Auth_OpenID_Decoder();
  1231.     }
  1232.  
  1233.     /**
  1234.      * Handle a request.  Given an Auth_OpenID_Request object, call
  1235.      * the appropriate Auth_OpenID_Server method to process the
  1236.      * request and generate a response.
  1237.      *
  1238.      * @param Auth_OpenID_Request $request An Auth_OpenID_Request
  1239.      *  returned by Auth_OpenID_Server::decodeRequest.
  1240.      *
  1241.      * @return Auth_OpenID_Response $response A response object
  1242.      *  capable of generating a user-agent reply.
  1243.      */
  1244.     function handleRequest($request)
  1245.     {
  1246.         if (method_exists($this"openid_" $request->mode)) {
  1247.             $handler array($this"openid_" $request->mode);
  1248.             return call_user_func($handler$request);
  1249.         }
  1250.         return null;
  1251.     }
  1252.  
  1253.     /**
  1254.      * The callback for 'check_authentication' messages.
  1255.      *
  1256.      * @access private
  1257.      */
  1258.     function openid_check_authentication(&$request)
  1259.     {
  1260.         return $request->answer($this->signatory);
  1261.     }
  1262.  
  1263.     /**
  1264.      * The callback for 'associate' messages.
  1265.      *
  1266.      * @access private
  1267.      */
  1268.     function openid_associate(&$request)
  1269.     {
  1270.         $assoc $this->signatory->createAssociation(false);
  1271.         return $request->answer($assoc);
  1272.     }
  1273.  
  1274.     /**
  1275.      * Encodes as response in the appropriate format suitable for
  1276.      * sending to the user agent.
  1277.      */
  1278.     function encodeResponse(&$response)
  1279.     {
  1280.         return $this->encoder->encode($response);
  1281.     }
  1282.  
  1283.     /**
  1284.      * Decodes a query args array into the appropriate
  1285.      * Auth_OpenID_Request object.
  1286.      */
  1287.     function decodeRequest(&$query)
  1288.     {
  1289.         return $this->decoder->decode($query);
  1290.     }
  1291. }
  1292.  
  1293. ?>

Documentation generated on Mon, 05 Mar 2007 21:21:12 +0000 by phpDocumentor 1.3.1