Consumer.php

Go to the documentation of this file.
00001 <?php
00002 
00163 require_once "Auth/OpenID.php";
00164 require_once "Auth/OpenID/Message.php";
00165 require_once "Auth/OpenID/HMAC.php";
00166 require_once "Auth/OpenID/Association.php";
00167 require_once "Auth/OpenID/CryptUtil.php";
00168 require_once "Auth/OpenID/DiffieHellman.php";
00169 require_once "Auth/OpenID/KVForm.php";
00170 require_once "Auth/OpenID/Nonce.php";
00171 require_once "Auth/OpenID/Discover.php";
00172 require_once "Auth/OpenID/URINorm.php";
00173 require_once "Auth/Yadis/Manager.php";
00174 require_once "Auth/Yadis/XRI.php";
00175 
00180 define('Auth_OpenID_SUCCESS', 'success');
00181 
00185 define('Auth_OpenID_CANCEL', 'cancel');
00186 
00191 define('Auth_OpenID_FAILURE', 'failure');
00192 
00199 define('Auth_OpenID_SETUP_NEEDED', 'setup needed');
00200 
00206 define('Auth_OpenID_PARSE_ERROR', 'parse error');
00207 
00215 class Auth_OpenID_Consumer {
00216 
00220     var $discoverMethod = 'Auth_OpenID_discover';
00221 
00225     var $session_key_prefix = "_openid_consumer_";
00226 
00230     var $_token_suffix = "last_token";
00231 
00261     function Auth_OpenID_Consumer(&$store, $session = null,
00262                                   $consumer_cls = null)
00263     {
00264         if ($session === null) {
00265             $session = new Auth_Yadis_PHPSession();
00266         }
00267 
00268         $this->session =& $session;
00269 
00270         if ($consumer_cls !== null) {
00271             $this->consumer =& new $consumer_cls($store);
00272         } else {
00273             $this->consumer =& new Auth_OpenID_GenericConsumer($store);
00274         }
00275 
00276         $this->_token_key = $this->session_key_prefix . $this->_token_suffix;
00277     }
00278 
00284     function getDiscoveryObject(&$session, $openid_url,
00285                                 $session_key_prefix)
00286     {
00287         return new Auth_Yadis_Discovery($session, $openid_url,
00288                                         $session_key_prefix);
00289     }
00290 
00313     function begin($user_url, $anonymous=false)
00314     {
00315         $openid_url = $user_url;
00316 
00317         $disco = $this->getDiscoveryObject($this->session,
00318                                            $openid_url,
00319                                            $this->session_key_prefix);
00320 
00321         // Set the 'stale' attribute of the manager.  If discovery
00322         // fails in a fatal way, the stale flag will cause the manager
00323         // to be cleaned up next time discovery is attempted.
00324 
00325         $m = $disco->getManager();
00326         $loader = new Auth_Yadis_ManagerLoader();
00327 
00328         if ($m) {
00329             if ($m->stale) {
00330                 $disco->destroyManager();
00331             } else {
00332                 $m->stale = true;
00333                 $disco->session->set($disco->session_key,
00334                                      serialize($loader->toSession($m)));
00335             }
00336         }
00337 
00338         $endpoint = $disco->getNextService($this->discoverMethod,
00339                                            $this->consumer->fetcher);
00340 
00341         // Reset the 'stale' attribute of the manager.
00342         $m =& $disco->getManager();
00343         if ($m) {
00344             $m->stale = false;
00345             $disco->session->set($disco->session_key,
00346                                  serialize($loader->toSession($m)));
00347         }
00348 
00349         if ($endpoint === null) {
00350             return null;
00351         } else {
00352             return $this->beginWithoutDiscovery($endpoint,
00353                                                 $anonymous);
00354         }
00355     }
00356 
00373     function &beginWithoutDiscovery($endpoint, $anonymous=false)
00374     {
00375         $loader = new Auth_OpenID_ServiceEndpointLoader();
00376         $auth_req = $this->consumer->begin($endpoint);
00377         $this->session->set($this->_token_key,
00378               $loader->toSession($auth_req->endpoint));
00379         if (!$auth_req->setAnonymous($anonymous)) {
00380             return new Auth_OpenID_FailureResponse(null,
00381               "OpenID 1 requests MUST include the identifier " .
00382               "in the request.");
00383         }
00384         return $auth_req;
00385     }
00386 
00410     function complete($current_url, $query=null)
00411     {
00412         if ($current_url && !is_string($current_url)) {
00413             // This is ugly, but we need to complain loudly when
00414             // someone uses the API incorrectly.
00415             trigger_error("current_url must be a string; see NEWS file " .
00416                           "for upgrading notes.",
00417                           E_USER_ERROR);
00418         }
00419 
00420         if ($query === null) {
00421             $query = Auth_OpenID::getQuery();
00422         }
00423 
00424         $loader = new Auth_OpenID_ServiceEndpointLoader();
00425         $endpoint_data = $this->session->get($this->_token_key);
00426         $endpoint =
00427             $loader->fromSession($endpoint_data);
00428 
00429         $message = Auth_OpenID_Message::fromPostArgs($query);
00430         $response = $this->consumer->complete($message, $endpoint, 
00431                                               $current_url);
00432         $this->session->del($this->_token_key);
00433 
00434         if (in_array($response->status, array(Auth_OpenID_SUCCESS,
00435                                               Auth_OpenID_CANCEL))) {
00436             if ($response->identity_url !== null) {
00437                 $disco = $this->getDiscoveryObject($this->session,
00438                                                    $response->identity_url,
00439                                                    $this->session_key_prefix);
00440                 $disco->cleanup(true);
00441             }
00442         }
00443 
00444         return $response;
00445     }
00446 }
00447 
00453 class Auth_OpenID_DiffieHellmanSHA1ConsumerSession {
00454     var $session_type = 'DH-SHA1';
00455     var $hash_func = 'Auth_OpenID_SHA1';
00456     var $secret_size = 20;
00457     var $allowed_assoc_types = array('HMAC-SHA1');
00458 
00459     function Auth_OpenID_DiffieHellmanSHA1ConsumerSession($dh = null)
00460     {
00461         if ($dh === null) {
00462             $dh = new Auth_OpenID_DiffieHellman();
00463         }
00464 
00465         $this->dh = $dh;
00466     }
00467 
00468     function getRequest()
00469     {
00470         $math =& Auth_OpenID_getMathLib();
00471 
00472         $cpub = $math->longToBase64($this->dh->public);
00473 
00474         $args = array('dh_consumer_public' => $cpub);
00475 
00476         if (!$this->dh->usingDefaultValues()) {
00477             $args = array_merge($args, array(
00478                 'dh_modulus' =>
00479                      $math->longToBase64($this->dh->mod),
00480                 'dh_gen' =>
00481                      $math->longToBase64($this->dh->gen)));
00482         }
00483 
00484         return $args;
00485     }
00486 
00487     function extractSecret($response)
00488     {
00489         if (!$response->hasKey(Auth_OpenID_OPENID_NS,
00490                                'dh_server_public')) {
00491             return null;
00492         }
00493 
00494         if (!$response->hasKey(Auth_OpenID_OPENID_NS,
00495                                'enc_mac_key')) {
00496             return null;
00497         }
00498 
00499         $math =& Auth_OpenID_getMathLib();
00500 
00501         $spub = $math->base64ToLong($response->getArg(Auth_OpenID_OPENID_NS,
00502                                                       'dh_server_public'));
00503         $enc_mac_key = base64_decode($response->getArg(Auth_OpenID_OPENID_NS,
00504                                                        'enc_mac_key'));
00505 
00506         return $this->dh->xorSecret($spub, $enc_mac_key, $this->hash_func);
00507     }
00508 }
00509 
00515 class Auth_OpenID_DiffieHellmanSHA256ConsumerSession extends
00516       Auth_OpenID_DiffieHellmanSHA1ConsumerSession {
00517     var $session_type = 'DH-SHA256';
00518     var $hash_func = 'Auth_OpenID_SHA256';
00519     var $secret_size = 32;
00520     var $allowed_assoc_types = array('HMAC-SHA256');
00521 }
00522 
00528 class Auth_OpenID_PlainTextConsumerSession {
00529     var $session_type = 'no-encryption';
00530     var $allowed_assoc_types =  array('HMAC-SHA1', 'HMAC-SHA256');
00531 
00532     function getRequest()
00533     {
00534         return array();
00535     }
00536 
00537     function extractSecret($response)
00538     {
00539         if (!$response->hasKey(Auth_OpenID_OPENID_NS, 'mac_key')) {
00540             return null;
00541         }
00542 
00543         return base64_decode($response->getArg(Auth_OpenID_OPENID_NS,
00544                                                'mac_key'));
00545     }
00546 }
00547 
00551 function Auth_OpenID_getAvailableSessionTypes()
00552 {
00553     $types = array(
00554       'no-encryption' => 'Auth_OpenID_PlainTextConsumerSession',
00555       'DH-SHA1' => 'Auth_OpenID_DiffieHellmanSHA1ConsumerSession',
00556       'DH-SHA256' => 'Auth_OpenID_DiffieHellmanSHA256ConsumerSession');
00557 
00558     return $types;
00559 }
00560 
00568 class Auth_OpenID_GenericConsumer {
00572     var $discoverMethod = 'Auth_OpenID_discover';
00573 
00577     var $store;
00578 
00582     var $_use_assocs;
00583 
00587     var $openid1_nonce_query_arg_name = 'janrain_nonce';
00588 
00594     var $openid1_return_to_identifier_name = 'openid1_claimed_id';
00595 
00614     function Auth_OpenID_GenericConsumer(&$store)
00615     {
00616         $this->store =& $store;
00617         $this->negotiator =& Auth_OpenID_getDefaultNegotiator();
00618         $this->_use_assocs = ($this->store ? true : false);
00619 
00620         $this->fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
00621 
00622         $this->session_types = Auth_OpenID_getAvailableSessionTypes();
00623     }
00624 
00631     function begin($service_endpoint)
00632     {
00633         $assoc = $this->_getAssociation($service_endpoint);
00634         $r = new Auth_OpenID_AuthRequest($service_endpoint, $assoc);
00635         $r->return_to_args[$this->openid1_nonce_query_arg_name] =
00636             Auth_OpenID_mkNonce();
00637 
00638         if ($r->message->isOpenID1()) {
00639             $r->return_to_args[$this->openid1_return_to_identifier_name] =
00640                 $r->endpoint->claimed_id;
00641         }
00642 
00643         return $r;
00644     }
00645 
00653     function complete($message, $endpoint, $return_to)
00654     {
00655         $mode = $message->getArg(Auth_OpenID_OPENID_NS, 'mode',
00656                                  '<no mode set>');
00657 
00658         $mode_methods = array(
00659                               'cancel' => '_complete_cancel',
00660                               'error' => '_complete_error',
00661                               'setup_needed' => '_complete_setup_needed',
00662                               'id_res' => '_complete_id_res',
00663                               );
00664 
00665         $method = Auth_OpenID::arrayGet($mode_methods, $mode,
00666                                         '_completeInvalid');
00667 
00668         return call_user_func_array(array(&$this, $method),
00669                                     array($message, $endpoint, $return_to));
00670     }
00671 
00675     function _completeInvalid($message, &$endpoint, $unused)
00676     {
00677         $mode = $message->getArg(Auth_OpenID_OPENID_NS, 'mode',
00678                                  '<No mode set>');
00679 
00680         return new Auth_OpenID_FailureResponse($endpoint,
00681                     sprintf("Invalid openid.mode '%s'", $mode));
00682     }
00683 
00687     function _complete_cancel($message, &$endpoint, $unused)
00688     {
00689         return new Auth_OpenID_CancelResponse($endpoint);
00690     }
00691 
00695     function _complete_error($message, &$endpoint, $unused)
00696     {
00697         $error = $message->getArg(Auth_OpenID_OPENID_NS, 'error');
00698         $contact = $message->getArg(Auth_OpenID_OPENID_NS, 'contact');
00699         $reference = $message->getArg(Auth_OpenID_OPENID_NS, 'reference');
00700 
00701         return new Auth_OpenID_FailureResponse($endpoint, $error,
00702                                                $contact, $reference);
00703     }
00704 
00708     function _complete_setup_needed($message, &$endpoint, $unused)
00709     {
00710         if (!$message->isOpenID2()) {
00711             return $this->_completeInvalid($message, $endpoint);
00712         }
00713 
00714         $user_setup_url = $message->getArg(Auth_OpenID_OPENID2_NS,
00715                                            'user_setup_url');
00716         return new Auth_OpenID_SetupNeededResponse($endpoint, $user_setup_url);
00717     }
00718 
00722     function _complete_id_res($message, &$endpoint, $return_to)
00723     {
00724         $user_setup_url = $message->getArg(Auth_OpenID_OPENID1_NS,
00725                                            'user_setup_url');
00726 
00727         if ($this->_checkSetupNeeded($message)) {
00728             return new Auth_OpenID_SetupNeededResponse(
00729                 $endpoint, $user_setup_url);
00730         } else {
00731             return $this->_doIdRes($message, $endpoint, $return_to);
00732         }
00733     }
00734 
00738     function _checkSetupNeeded($message)
00739     {
00740         // In OpenID 1, we check to see if this is a cancel from
00741         // immediate mode by the presence of the user_setup_url
00742         // parameter.
00743         if ($message->isOpenID1()) {
00744             $user_setup_url = $message->getArg(Auth_OpenID_OPENID1_NS,
00745                                                'user_setup_url');
00746             if ($user_setup_url !== null) {
00747                 return true;
00748             }
00749         }
00750 
00751         return false;
00752     }
00753 
00757     function _doIdRes($message, $endpoint, $return_to)
00758     {
00759         // Checks for presence of appropriate fields (and checks
00760         // signed list fields)
00761         $result = $this->_idResCheckForFields($message);
00762 
00763         if (Auth_OpenID::isFailure($result)) {
00764             return $result;
00765         }
00766 
00767         if (!$this->_checkReturnTo($message, $return_to)) {
00768             return new Auth_OpenID_FailureResponse(null,
00769             sprintf("return_to does not match return URL. Expected %s, got %s",
00770                     $return_to,
00771                     $message->getArg(Auth_OpenID_OPENID_NS, 'return_to')));
00772         }
00773 
00774         // Verify discovery information:
00775         $result = $this->_verifyDiscoveryResults($message, $endpoint);
00776 
00777         if (Auth_OpenID::isFailure($result)) {
00778             return $result;
00779         }
00780 
00781         $endpoint = $result;
00782 
00783         $result = $this->_idResCheckSignature($message,
00784                                               $endpoint->server_url);
00785 
00786         if (Auth_OpenID::isFailure($result)) {
00787             return $result;
00788         }
00789 
00790         $result = $this->_idResCheckNonce($message, $endpoint);
00791 
00792         if (Auth_OpenID::isFailure($result)) {
00793             return $result;
00794         }
00795 
00796         $signed_list_str = $message->getArg(Auth_OpenID_OPENID_NS, 'signed',
00797                                             Auth_OpenID_NO_DEFAULT);
00798         if (Auth_OpenID::isFailure($signed_list_str)) {
00799             return $signed_list_str;
00800         }
00801         $signed_list = explode(',', $signed_list_str);
00802 
00803         $signed_fields = Auth_OpenID::addPrefix($signed_list, "openid.");
00804 
00805         return new Auth_OpenID_SuccessResponse($endpoint, $message,
00806                                                $signed_fields);
00807 
00808     }
00809 
00813     function _checkReturnTo($message, $return_to)
00814     {
00815         // Check an OpenID message and its openid.return_to value
00816         // against a return_to URL from an application.  Return True
00817         // on success, False on failure.
00818 
00819         // Check the openid.return_to args against args in the
00820         // original message.
00821         $result = Auth_OpenID_GenericConsumer::_verifyReturnToArgs(
00822                                            $message->toPostArgs());
00823         if (Auth_OpenID::isFailure($result)) {
00824             return false;
00825         }
00826 
00827         // Check the return_to base URL against the one in the
00828         // message.
00829         $msg_return_to = $message->getArg(Auth_OpenID_OPENID_NS,
00830                                           'return_to');
00831         if (Auth_OpenID::isFailure($return_to)) {
00832             // XXX log me
00833             return false;
00834         }
00835 
00836         $return_to_parts = parse_url(Auth_OpenID_urinorm($return_to));
00837         $msg_return_to_parts = parse_url(Auth_OpenID_urinorm($msg_return_to));
00838 
00839         // If port is absent from both, add it so it's equal in the
00840         // check below.
00841         if ((!array_key_exists('port', $return_to_parts)) &&
00842             (!array_key_exists('port', $msg_return_to_parts))) {
00843             $return_to_parts['port'] = null;
00844             $msg_return_to_parts['port'] = null;
00845         }
00846 
00847         // If path is absent from both, add it so it's equal in the
00848         // check below.
00849         if ((!array_key_exists('path', $return_to_parts)) &&
00850             (!array_key_exists('path', $msg_return_to_parts))) {
00851             $return_to_parts['path'] = null;
00852             $msg_return_to_parts['path'] = null;
00853         }
00854 
00855         // The URL scheme, authority, and path MUST be the same
00856         // between the two URLs.
00857         foreach (array('scheme', 'host', 'port', 'path') as $component) {
00858             // If the url component is absent in either URL, fail.
00859             // There should always be a scheme, host, port, and path.
00860             if (!array_key_exists($component, $return_to_parts)) {
00861                 return false;
00862             }
00863 
00864             if (!array_key_exists($component, $msg_return_to_parts)) {
00865                 return false;
00866             }
00867 
00868             if (Auth_OpenID::arrayGet($return_to_parts, $component) !==
00869                 Auth_OpenID::arrayGet($msg_return_to_parts, $component)) {
00870                 return false;
00871             }
00872         }
00873 
00874         return true;
00875     }
00876 
00880     function _verifyReturnToArgs($query)
00881     {
00882         // Verify that the arguments in the return_to URL are present in this
00883         // response.
00884 
00885         $message = Auth_OpenID_Message::fromPostArgs($query);
00886         $return_to = $message->getArg(Auth_OpenID_OPENID_NS, 'return_to');
00887 
00888         if (Auth_OpenID::isFailure($return_to)) {
00889             return $return_to;
00890         }
00891         // XXX: this should be checked by _idResCheckForFields
00892         if (!$return_to) {
00893             return new Auth_OpenID_FailureResponse(null,
00894                            "Response has no return_to");
00895         }
00896 
00897         $parsed_url = parse_url($return_to);
00898 
00899         $q = array();
00900         if (array_key_exists('query', $parsed_url)) {
00901             $rt_query = $parsed_url['query'];
00902             $q = Auth_OpenID::parse_str($rt_query);
00903         }
00904 
00905         foreach ($q as $rt_key => $rt_value) {
00906             if (!array_key_exists($rt_key, $query)) {
00907                 return new Auth_OpenID_FailureResponse(null,
00908                   sprintf("return_to parameter %s absent from query", $rt_key));
00909             } else {
00910                 $value = $query[$rt_key];
00911                 if ($rt_value != $value) {
00912                     return new Auth_OpenID_FailureResponse(null,
00913                       sprintf("parameter %s value %s does not match " .
00914                               "return_to value %s", $rt_key,
00915                               $value, $rt_value));
00916                 }
00917             }
00918         }
00919 
00920         // Make sure all non-OpenID arguments in the response are also
00921         // in the signed return_to.
00922         $bare_args = $message->getArgs(Auth_OpenID_BARE_NS);
00923         foreach ($bare_args as $key => $value) {
00924             if (Auth_OpenID::arrayGet($q, $key) != $value) {
00925                 return new Auth_OpenID_FailureResponse(null,
00926                   sprintf("Parameter %s = %s not in return_to URL",
00927                           $key, $value));
00928             }
00929         }
00930 
00931         return true;
00932     }
00933 
00937     function _idResCheckSignature($message, $server_url)
00938     {
00939         $assoc_handle = $message->getArg(Auth_OpenID_OPENID_NS,
00940                                          'assoc_handle');
00941         if (Auth_OpenID::isFailure($assoc_handle)) {
00942             return $assoc_handle;
00943         }
00944 
00945         $assoc = $this->store->getAssociation($server_url, $assoc_handle);
00946 
00947         if ($assoc) {
00948             if ($assoc->getExpiresIn() <= 0) {
00949                 // XXX: It might be a good idea sometimes to re-start
00950                 // the authentication with a new association. Doing it
00951                 // automatically opens the possibility for
00952                 // denial-of-service by a server that just returns
00953                 // expired associations (or really short-lived
00954                 // associations)
00955                 return new Auth_OpenID_FailureResponse(null,
00956                              'Association with ' . $server_url . ' expired');
00957             }
00958 
00959             if (!$assoc->checkMessageSignature($message)) {
00960                 return new Auth_OpenID_FailureResponse(null,
00961                                                        "Bad signature");
00962             }
00963         } else {
00964             // It's not an association we know about.  Stateless mode
00965             // is our only possible path for recovery.  XXX - async
00966             // framework will not want to block on this call to
00967             // _checkAuth.
00968             if (!$this->_checkAuth($message, $server_url)) {
00969                 return new Auth_OpenID_FailureResponse(null,
00970                              "Server denied check_authentication");
00971             }
00972         }
00973 
00974         return null;
00975     }
00976 
00980     function _verifyDiscoveryResults($message, $endpoint=null)
00981     {
00982         if ($message->getOpenIDNamespace() == Auth_OpenID_OPENID2_NS) {
00983             return $this->_verifyDiscoveryResultsOpenID2($message,
00984                                                          $endpoint);
00985         } else {
00986             return $this->_verifyDiscoveryResultsOpenID1($message,
00987                                                          $endpoint);
00988         }
00989     }
00990 
00994     function _verifyDiscoveryResultsOpenID1($message, $endpoint)
00995     {
00996         $claimed_id = $message->getArg(Auth_OpenID_BARE_NS,
00997                                 $this->openid1_return_to_identifier_name);
00998 
00999         if (($endpoint === null) && ($claimed_id === null)) {
01000             return new Auth_OpenID_FailureResponse($endpoint,
01001               'When using OpenID 1, the claimed ID must be supplied, ' .
01002               'either by passing it through as a return_to parameter ' .
01003               'or by using a session, and supplied to the GenericConsumer ' .
01004               'as the argument to complete()');
01005         } else if (($endpoint !== null) && ($claimed_id === null)) {
01006             $claimed_id = $endpoint->claimed_id;
01007         }
01008 
01009         $to_match = new Auth_OpenID_ServiceEndpoint();
01010         $to_match->type_uris = array(Auth_OpenID_TYPE_1_1);
01011         $to_match->local_id = $message->getArg(Auth_OpenID_OPENID1_NS,
01012                                                'identity');
01013 
01014         // Restore delegate information from the initiation phase
01015         $to_match->claimed_id = $claimed_id;
01016 
01017         if ($to_match->local_id === null) {
01018             return new Auth_OpenID_FailureResponse($endpoint,
01019                          "Missing required field openid.identity");
01020         }
01021 
01022         $to_match_1_0 = $to_match->copy();
01023         $to_match_1_0->type_uris = array(Auth_OpenID_TYPE_1_0);
01024 
01025         if ($endpoint !== null) {
01026             $result = $this->_verifyDiscoverySingle($endpoint, $to_match);
01027 
01028             if (is_a($result, 'Auth_OpenID_TypeURIMismatch')) {
01029                 $result = $this->_verifyDiscoverySingle($endpoint,
01030                                                         $to_match_1_0);
01031             }
01032 
01033             if (Auth_OpenID::isFailure($result)) {
01034                 // oidutil.log("Error attempting to use stored
01035                 //             discovery information: " + str(e))
01036                 //             oidutil.log("Attempting discovery to
01037                 //             verify endpoint")
01038             } else {
01039                 return $endpoint;
01040             }
01041         }
01042 
01043         // Endpoint is either bad (failed verification) or None
01044         return $this->_discoverAndVerify($to_match->claimed_id,
01045                                          array($to_match, $to_match_1_0));
01046     }
01047 
01051     function _verifyDiscoverySingle($endpoint, $to_match)
01052     {
01053         // Every type URI that's in the to_match endpoint has to be
01054         // present in the discovered endpoint.
01055         foreach ($to_match->type_uris as $type_uri) {
01056             if (!$endpoint->usesExtension($type_uri)) {
01057                 return new Auth_OpenID_TypeURIMismatch($endpoint,
01058                              "Required type ".$type_uri." not present");
01059             }
01060         }
01061 
01062         // Fragments do not influence discovery, so we can't compare a
01063         // claimed identifier with a fragment to discovered
01064         // information.
01065         list($defragged_claimed_id, $_) =
01066             Auth_OpenID::urldefrag($to_match->claimed_id);
01067 
01068         if ($defragged_claimed_id != $endpoint->claimed_id) {
01069             return new Auth_OpenID_FailureResponse($endpoint,
01070               sprintf('Claimed ID does not match (different subjects!), ' .
01071                       'Expected %s, got %s', $defragged_claimed_id,
01072                       $endpoint->claimed_id));
01073         }
01074 
01075         if ($to_match->getLocalID() != $endpoint->getLocalID()) {
01076             return new Auth_OpenID_FailureResponse($endpoint,
01077               sprintf('local_id mismatch. Expected %s, got %s',
01078                       $to_match->getLocalID(), $endpoint->getLocalID()));
01079         }
01080 
01081         // If the server URL is None, this must be an OpenID 1
01082         // response, because op_endpoint is a required parameter in
01083         // OpenID 2. In that case, we don't actually care what the
01084         // discovered server_url is, because signature checking or
01085         // check_auth should take care of that check for us.
01086         if ($to_match->server_url === null) {
01087             if ($to_match->preferredNamespace() != Auth_OpenID_OPENID1_NS) {
01088                 return new Auth_OpenID_FailureResponse($endpoint,
01089                              "Preferred namespace mismatch (bug)");
01090             }
01091         } else if ($to_match->server_url != $endpoint->server_url) {
01092             return new Auth_OpenID_FailureResponse($endpoint,
01093               sprintf('OP Endpoint mismatch. Expected %s, got %s',
01094                       $to_match->server_url, $endpoint->server_url));
01095         }
01096 
01097         return null;
01098     }
01099 
01103     function _verifyDiscoveryResultsOpenID2($message, $endpoint)
01104     {
01105         $to_match = new Auth_OpenID_ServiceEndpoint();
01106         $to_match->type_uris = array(Auth_OpenID_TYPE_2_0);
01107         $to_match->claimed_id = $message->getArg(Auth_OpenID_OPENID2_NS,
01108                                                  'claimed_id');
01109 
01110         $to_match->local_id = $message->getArg(Auth_OpenID_OPENID2_NS,
01111                                                 'identity');
01112 
01113         $to_match->server_url = $message->getArg(Auth_OpenID_OPENID2_NS,
01114                                                  'op_endpoint');
01115 
01116         if ($to_match->server_url === null) {
01117             return new Auth_OpenID_FailureResponse($endpoint,
01118                          "OP Endpoint URL missing");
01119         }
01120 
01121         // claimed_id and identifier must both be present or both be
01122         // absent
01123         if (($to_match->claimed_id === null) &&
01124             ($to_match->local_id !== null)) {
01125             return new Auth_OpenID_FailureResponse($endpoint,
01126               'openid.identity is present without openid.claimed_id');
01127         }
01128 
01129         if (($to_match->claimed_id !== null) &&
01130             ($to_match->local_id === null)) {
01131             return new Auth_OpenID_FailureResponse($endpoint,
01132               'openid.claimed_id is present without openid.identity');
01133         }
01134 
01135         if ($to_match->claimed_id === null) {
01136             // This is a response without identifiers, so there's
01137             // really no checking that we can do, so return an
01138             // endpoint that's for the specified `openid.op_endpoint'
01139             return Auth_OpenID_ServiceEndpoint::fromOPEndpointURL(
01140                                                 $to_match->server_url);
01141         }
01142 
01143         if (!$endpoint) {
01144             // The claimed ID doesn't match, so we have to do
01145             // discovery again. This covers not using sessions, OP
01146             // identifier endpoints and responses that didn't match
01147             // the original request.
01148             // oidutil.log('No pre-discovered information supplied.')
01149             return $this->_discoverAndVerify($to_match->claimed_id,
01150                                              array($to_match));
01151         } else {
01152 
01153             // The claimed ID matches, so we use the endpoint that we
01154             // discovered in initiation. This should be the most
01155             // common case.
01156             $result = $this->_verifyDiscoverySingle($endpoint, $to_match);
01157 
01158             if (Auth_OpenID::isFailure($result)) {
01159                 $endpoint = $this->_discoverAndVerify($to_match->claimed_id,
01160                                                       array($to_match));
01161                 if (Auth_OpenID::isFailure($endpoint)) {
01162                     return $endpoint;
01163                 }
01164             }
01165         }
01166 
01167         // The endpoint we return should have the claimed ID from the
01168         // message we just verified, fragment and all.
01169         if ($endpoint->claimed_id != $to_match->claimed_id) {
01170             $endpoint->claimed_id = $to_match->claimed_id;
01171         }
01172 
01173         return $endpoint;
01174     }
01175 
01179     function _discoverAndVerify($claimed_id, $to_match_endpoints)
01180     {
01181         // oidutil.log('Performing discovery on %s' % (claimed_id,))
01182         list($unused, $services) = call_user_func($this->discoverMethod,
01183                                                   $claimed_id,
01184                                                   $this->fetcher);
01185 
01186         if (!$services) {
01187             return new Auth_OpenID_FailureResponse(null,
01188               sprintf("No OpenID information found at %s",
01189                       $claimed_id));
01190         }
01191 
01192         return $this->_verifyDiscoveryServices($claimed_id, $services,
01193                                                $to_match_endpoints);
01194     }
01195 
01199     function _verifyDiscoveryServices($claimed_id, 
01200                                       &$services, &$to_match_endpoints)
01201     {
01202         // Search the services resulting from discovery to find one
01203         // that matches the information from the assertion
01204 
01205         foreach ($services as $endpoint) {
01206             foreach ($to_match_endpoints as $to_match_endpoint) {
01207                 $result = $this->_verifyDiscoverySingle($endpoint, 
01208                                                         $to_match_endpoint);
01209 
01210                 if (!Auth_OpenID::isFailure($result)) {
01211                     // It matches, so discover verification has
01212                     // succeeded. Return this endpoint.
01213                     return $endpoint;
01214                 }
01215             }
01216         }
01217 
01218         return new Auth_OpenID_FailureResponse(null,
01219           sprintf('No matching endpoint found after discovering %s',
01220                   $claimed_id));
01221     }
01222 
01234     function _idResGetNonceOpenID1($message, $endpoint)
01235     {
01236         return $message->getArg(Auth_OpenID_BARE_NS,
01237                                 $this->openid1_nonce_query_arg_name);
01238     }
01239 
01243     function _idResCheckNonce($message, $endpoint)
01244     {
01245         if ($message->isOpenID1()) {
01246             // This indicates that the nonce was generated by the consumer
01247             $nonce = $this->_idResGetNonceOpenID1($message, $endpoint);
01248             $server_url = '';
01249         } else {
01250             $nonce = $message->getArg(Auth_OpenID_OPENID2_NS,
01251                                       'response_nonce');
01252 
01253             $server_url = $endpoint->server_url;
01254         }
01255 
01256         if ($nonce === null) {
01257             return new Auth_OpenID_FailureResponse($endpoint,
01258                                      "Nonce missing from response");
01259         }
01260 
01261         $parts = Auth_OpenID_splitNonce($nonce);
01262 
01263         if ($parts === null) {
01264             return new Auth_OpenID_FailureResponse($endpoint,
01265                                      "Malformed nonce in response");
01266         }
01267 
01268         list($timestamp, $salt) = $parts;
01269 
01270         if (!$this->store->useNonce($server_url, $timestamp, $salt)) {
01271             return new Auth_OpenID_FailureResponse($endpoint,
01272                          "Nonce already used or out of range");
01273         }
01274 
01275         return null;
01276     }
01277 
01281     function _idResCheckForFields($message)
01282     {
01283         $basic_fields = array('return_to', 'assoc_handle', 'sig', 'signed');
01284         $basic_sig_fields = array('return_to', 'identity');
01285 
01286         $require_fields = array(
01287             Auth_OpenID_OPENID2_NS => array_merge($basic_fields,
01288                                                   array('op_endpoint')),
01289 
01290             Auth_OpenID_OPENID1_NS => array_merge($basic_fields,
01291                                                   array('identity'))
01292             );
01293 
01294         $require_sigs = array(
01295             Auth_OpenID_OPENID2_NS => array_merge($basic_sig_fields,
01296                                                   array('response_nonce',
01297                                                         'claimed_id',
01298                                                         'assoc_handle')),
01299             Auth_OpenID_OPENID1_NS => array_merge($basic_sig_fields,
01300                                                   array('nonce'))
01301             );
01302 
01303         foreach ($require_fields[$message->getOpenIDNamespace()] as $field) {
01304             if (!$message->hasKey(Auth_OpenID_OPENID_NS, $field)) {
01305                 return new Auth_OpenID_FailureResponse(null,
01306                              "Missing required field '".$field."'");
01307             }
01308         }
01309 
01310         $signed_list_str = $message->getArg(Auth_OpenID_OPENID_NS,
01311                                             'signed',
01312                                             Auth_OpenID_NO_DEFAULT);
01313         if (Auth_OpenID::isFailure($signed_list_str)) {
01314             return $signed_list_str;
01315         }
01316         $signed_list = explode(',', $signed_list_str);
01317 
01318         foreach ($require_sigs[$message->getOpenIDNamespace()] as $field) {
01319             // Field is present and not in signed list
01320             if ($message->hasKey(Auth_OpenID_OPENID_NS, $field) &&
01321                 (!in_array($field, $signed_list))) {
01322                 return new Auth_OpenID_FailureResponse(null,
01323                              "'".$field."' not signed");
01324             }
01325         }
01326 
01327         return null;
01328     }
01329 
01333     function _checkAuth($message, $server_url)
01334     {
01335         $request = $this->_createCheckAuthRequest($message);
01336         if ($request === null) {
01337             return false;
01338         }
01339 
01340         $resp_message = $this->_makeKVPost($request, $server_url);
01341         if (($resp_message === null) ||
01342             (is_a($resp_message, 'Auth_OpenID_ServerErrorContainer'))) {
01343             return false;
01344         }
01345 
01346         return $this->_processCheckAuthResponse($resp_message, $server_url);
01347     }
01348 
01352     function _createCheckAuthRequest($message)
01353     {
01354         $signed = $message->getArg(Auth_OpenID_OPENID_NS, 'signed');
01355         if ($signed) {
01356             foreach (explode(',', $signed) as $k) {
01357                 $value = $message->getAliasedArg($k);
01358                 if ($value === null) {
01359                     return null;
01360                 }
01361             }
01362         }
01363         $ca_message = $message->copy();
01364         $ca_message->setArg(Auth_OpenID_OPENID_NS, 'mode', 
01365                             'check_authentication');
01366         return $ca_message;
01367     }
01368 
01372     function _processCheckAuthResponse($response, $server_url)
01373     {
01374         $is_valid = $response->getArg(Auth_OpenID_OPENID_NS, 'is_valid',
01375                                       'false');
01376 
01377         $invalidate_handle = $response->getArg(Auth_OpenID_OPENID_NS,
01378                                                'invalidate_handle');
01379 
01380         if ($invalidate_handle !== null) {
01381             $this->store->removeAssociation($server_url,
01382                                             $invalidate_handle);
01383         }
01384 
01385         if ($is_valid == 'true') {
01386             return true;
01387         }
01388 
01389         return false;
01390     }
01391 
01399     function _httpResponseToMessage($response, $server_url)
01400     {
01401         // Should this function be named Message.fromHTTPResponse instead?
01402         $response_message = Auth_OpenID_Message::fromKVForm($response->body);
01403 
01404         if ($response->status == 400) {
01405             return Auth_OpenID_ServerErrorContainer::fromMessage(
01406                         $response_message);
01407         } else if ($response->status != 200 and $response->status != 206) {
01408             return null;
01409         }
01410 
01411         return $response_message;
01412     }
01413 
01417     function _makeKVPost($message, $server_url)
01418     {
01419         $body = $message->toURLEncoded();
01420         $resp = $this->fetcher->post($server_url, $body);
01421 
01422         if ($resp === null) {
01423             return null;
01424         }
01425 
01426         return $this->_httpResponseToMessage($resp, $server_url);
01427     }
01428 
01432     function _getAssociation($endpoint)
01433     {
01434         if (!$this->_use_assocs) {
01435             return null;
01436         }
01437 
01438         $assoc = $this->store->getAssociation($endpoint->server_url);
01439 
01440         if (($assoc === null) ||
01441             ($assoc->getExpiresIn() <= 0)) {
01442 
01443             $assoc = $this->_negotiateAssociation($endpoint);
01444 
01445             if ($assoc !== null) {
01446                 $this->store->storeAssociation($endpoint->server_url,
01447                                                $assoc);
01448             }
01449         }
01450 
01451         return $assoc;
01452     }
01453 
01463     function _extractSupportedAssociationType(&$server_error, &$endpoint,
01464                                               $assoc_type)
01465     {
01466         // Any error message whose code is not 'unsupported-type'
01467         // should be considered a total failure.
01468         if (($server_error->error_code != 'unsupported-type') ||
01469             ($server_error->message->isOpenID1())) {
01470             return null;
01471         }
01472 
01473         // The server didn't like the association/session type that we
01474         // sent, and it sent us back a message that might tell us how
01475         // to handle it.
01476 
01477         // Extract the session_type and assoc_type from the error
01478         // message
01479         $assoc_type = $server_error->message->getArg(Auth_OpenID_OPENID_NS,
01480                                                      'assoc_type');
01481 
01482         $session_type = $server_error->message->getArg(Auth_OpenID_OPENID_NS,
01483                                                        'session_type');
01484 
01485         if (($assoc_type === null) || ($session_type === null)) {
01486             return null;
01487         } else if (!$this->negotiator->isAllowed($assoc_type,
01488                                                  $session_type)) {
01489             return null;
01490         } else {
01491           return array($assoc_type, $session_type);
01492         }
01493     }
01494 
01498     function _negotiateAssociation($endpoint)
01499     {
01500         // Get our preferred session/association type from the negotiatior.
01501         list($assoc_type, $session_type) = $this->negotiator->getAllowedType();
01502 
01503         $assoc = $this->_requestAssociation(
01504                            $endpoint, $assoc_type, $session_type);
01505 
01506         if (Auth_OpenID::isFailure($assoc)) {
01507             return null;
01508         }
01509 
01510         if (is_a($assoc, 'Auth_OpenID_ServerErrorContainer')) {
01511             $why = $assoc;
01512 
01513             $supportedTypes = $this->_extractSupportedAssociationType(
01514                                      $why, $endpoint, $assoc_type);
01515 
01516             if ($supportedTypes !== null) {
01517                 list($assoc_type, $session_type) = $supportedTypes;
01518 
01519                 // Attempt to create an association from the assoc_type
01520                 // and session_type that the server told us it
01521                 // supported.
01522                 $assoc = $this->_requestAssociation(
01523                                    $endpoint, $assoc_type, $session_type);
01524 
01525                 if (is_a($assoc, 'Auth_OpenID_ServerErrorContainer')) {
01526                     // Do not keep trying, since it rejected the
01527                     // association type that it told us to use.
01528                     // oidutil.log('Server %s refused its suggested association
01529                     //             'type: session_type=%s, assoc_type=%s'
01530                     //             % (endpoint.server_url, session_type,
01531                     //                assoc_type))
01532                     return null;
01533                 } else {
01534                     return $assoc;
01535                 }
01536             } else {
01537                 return null;
01538             }
01539         } else {
01540             return $assoc;
01541         }
01542     }
01543 
01547     function _requestAssociation($endpoint, $assoc_type, $session_type)
01548     {
01549         list($assoc_session, $args) = $this->_createAssociateRequest(
01550                                       $endpoint, $assoc_type, $session_type);
01551 
01552         $response_message = $this->_makeKVPost($args, $endpoint->server_url);
01553 
01554         if ($response_message === null) {
01555             // oidutil.log('openid.associate request failed: %s' % (why[0],))
01556             return null;
01557         } else if (is_a($response_message,
01558                         'Auth_OpenID_ServerErrorContainer')) {
01559             return $response_message;
01560         }
01561 
01562         return $this->_extractAssociation($response_message, $assoc_session);
01563     }
01564 
01568     function _extractAssociation(&$assoc_response, &$assoc_session)
01569     {
01570         // Extract the common fields from the response, raising an
01571         // exception if they are not found
01572         $assoc_type = $assoc_response->getArg(
01573                          Auth_OpenID_OPENID_NS, 'assoc_type',
01574                          Auth_OpenID_NO_DEFAULT);
01575 
01576         if (Auth_OpenID::isFailure($assoc_type)) {
01577             return $assoc_type;
01578         }
01579 
01580         $assoc_handle = $assoc_response->getArg(
01581                            Auth_OpenID_OPENID_NS, 'assoc_handle',
01582                            Auth_OpenID_NO_DEFAULT);
01583 
01584         if (Auth_OpenID::isFailure($assoc_handle)) {
01585             return $assoc_handle;
01586         }
01587 
01588         // expires_in is a base-10 string. The Python parsing will
01589         // accept literals that have whitespace around them and will
01590         // accept negative values. Neither of these are really in-spec,
01591         // but we think it's OK to accept them.
01592         $expires_in_str = $assoc_response->getArg(
01593                              Auth_OpenID_OPENID_NS, 'expires_in',
01594                              Auth_OpenID_NO_DEFAULT);
01595 
01596         if (Auth_OpenID::isFailure($expires_in_str)) {
01597             return $expires_in_str;
01598         }
01599 
01600         $expires_in = Auth_OpenID::intval($expires_in_str);
01601         if ($expires_in === false) {
01602             
01603             $err = sprintf("Could not parse expires_in from association ".
01604                            "response %s", print_r($assoc_response, true));
01605             return new Auth_OpenID_FailureResponse(null, $err);
01606         }
01607 
01608         // OpenID 1 has funny association session behaviour.
01609         if ($assoc_response->isOpenID1()) {
01610             $session_type = $this->_getOpenID1SessionType($assoc_response);
01611         } else {
01612             $session_type = $assoc_response->getArg(
01613                                Auth_OpenID_OPENID2_NS, 'session_type',
01614                                Auth_OpenID_NO_DEFAULT);
01615 
01616             if (Auth_OpenID::isFailure($session_type)) {
01617                 return $session_type;
01618             }
01619         }
01620 
01621         // Session type mismatch
01622         if ($assoc_session->session_type != $session_type) {
01623             if ($assoc_response->isOpenID1() &&
01624                 ($session_type == 'no-encryption')) {
01625                 // In OpenID 1, any association request can result in
01626                 // a 'no-encryption' association response. Setting
01627                 // assoc_session to a new no-encryption session should
01628                 // make the rest of this function work properly for
01629                 // that case.
01630                 $assoc_session = new Auth_OpenID_PlainTextConsumerSession();
01631             } else {
01632                 // Any other mismatch, regardless of protocol version
01633                 // results in the failure of the association session
01634                 // altogether.
01635                 return null;
01636             }
01637         }
01638 
01639         // Make sure assoc_type is valid for session_type
01640         if (!in_array($assoc_type, $assoc_session->allowed_assoc_types)) {
01641             return null;
01642         }
01643 
01644         // Delegate to the association session to extract the secret
01645         // from the response, however is appropriate for that session
01646         // type.
01647         $secret = $assoc_session->extractSecret($assoc_response);
01648 
01649         if ($secret === null) {
01650             return null;
01651         }
01652 
01653         return Auth_OpenID_Association::fromExpiresIn(
01654                  $expires_in, $assoc_handle, $secret, $assoc_type);
01655     }
01656 
01660     function _createAssociateRequest($endpoint, $assoc_type, $session_type)
01661     {
01662         if (array_key_exists($session_type, $this->session_types)) {
01663             $session_type_class = $this->session_types[$session_type];
01664 
01665             if (is_callable($session_type_class)) {
01666                 $assoc_session = $session_type_class();
01667             } else {
01668                 $assoc_session = new $session_type_class();
01669             }
01670         } else {
01671             return null;
01672         }
01673 
01674         $args = array(
01675             'mode' => 'associate',
01676             'assoc_type' => $assoc_type);
01677 
01678         if (!$endpoint->compatibilityMode()) {
01679             $args['ns'] = Auth_OpenID_OPENID2_NS;
01680         }
01681 
01682         // Leave out the session type if we're in compatibility mode
01683         // *and* it's no-encryption.
01684         if ((!$endpoint->compatibilityMode()) ||
01685             ($assoc_session->session_type != 'no-encryption')) {
01686             $args['session_type'] = $assoc_session->session_type;
01687         }
01688 
01689         $args = array_merge($args, $assoc_session->getRequest());
01690         $message = Auth_OpenID_Message::fromOpenIDArgs($args);
01691         return array($assoc_session, $message);
01692     }
01693 
01707     function _getOpenID1SessionType($assoc_response)
01708     {
01709         // If it's an OpenID 1 message, allow session_type to default
01710         // to None (which signifies "no-encryption")
01711         $session_type = $assoc_response->getArg(Auth_OpenID_OPENID1_NS,
01712                                                 'session_type');
01713 
01714         // Handle the differences between no-encryption association
01715         // respones in OpenID 1 and 2:
01716 
01717         // no-encryption is not really a valid session type for OpenID
01718         // 1, but we'll accept it anyway, while issuing a warning.
01719         if ($session_type == 'no-encryption') {
01720             // oidutil.log('WARNING: OpenID server sent "no-encryption"'
01721             //             'for OpenID 1.X')
01722         } else if (($session_type == '') || ($session_type === null)) {
01723             // Missing or empty session type is the way to flag a
01724             // 'no-encryption' response. Change the session type to
01725             // 'no-encryption' so that it can be handled in the same
01726             // way as OpenID 2 'no-encryption' respones.
01727             $session_type = 'no-encryption';
01728         }
01729 
01730         return $session_type;
01731     }
01732 }
01733 
01740 class Auth_OpenID_AuthRequest {
01741 
01750     function Auth_OpenID_AuthRequest(&$endpoint, $assoc)
01751     {
01752         $this->assoc = $assoc;
01753         $this->endpoint =& $endpoint;
01754         $this->return_to_args = array();
01755         $this->message = new Auth_OpenID_Message(
01756             $endpoint->preferredNamespace());
01757         $this->_anonymous = false;
01758     }
01759 
01766     function addExtension(&$extension_request)
01767     {
01768         $extension_request->toMessage($this->message);
01769     }
01770 
01790     function addExtensionArg($namespace, $key, $value)
01791     {
01792         return $this->message->setArg($namespace, $key, $value);
01793     }
01794 
01804     function setAnonymous($is_anonymous)
01805     {
01806         if ($is_anonymous && $this->message->isOpenID1()) {
01807             return false;
01808         } else {
01809             $this->_anonymous = $is_anonymous;
01810             return true;
01811         }
01812     }
01813 
01834     function getMessage($realm, $return_to=null, $immediate=false)
01835     {
01836         if ($return_to) {
01837             $return_to = Auth_OpenID::appendArgs($return_to,
01838                                                  $this->return_to_args);
01839         } else if ($immediate) {
01840             // raise ValueError(
01841             //     '"return_to" is mandatory when
01842             //using "checkid_immediate"')
01843             return new Auth_OpenID_FailureResponse(null,
01844               "'return_to' is mandatory when using checkid_immediate");
01845         } else if ($this->message->isOpenID1()) {
01846             // raise ValueError('"return_to" is
01847             // mandatory for OpenID 1 requests')
01848             return new Auth_OpenID_FailureResponse(null,
01849               "'return_to' is mandatory for OpenID 1 requests");
01850         } else if ($this->return_to_args) {
01851             // raise ValueError('extra "return_to" arguments
01852             // were specified, but no return_to was specified')
01853             return new Auth_OpenID_FailureResponse(null,
01854               "extra 'return_to' arguments where specified, " .
01855               "but no return_to was specified");
01856         }
01857 
01858         if ($immediate) {
01859             $mode = 'checkid_immediate';
01860         } else {
01861             $mode = 'checkid_setup';
01862         }
01863 
01864         $message = $this->message->copy();
01865         if ($message->isOpenID1()) {
01866             $realm_key = 'trust_root';
01867         } else {
01868             $realm_key = 'realm';
01869         }
01870 
01871         $message->updateArgs(Auth_OpenID_OPENID_NS,
01872                              array(
01873                                    $realm_key => $realm,
01874                                    'mode' => $mode,
01875                                    'return_to' => $return_to));
01876 
01877         if (!$this->_anonymous) {
01878             if ($this->endpoint->isOPIdentifier()) {
01879                 // This will never happen when we're in compatibility
01880                 // mode, as long as isOPIdentifier() returns False
01881                 // whenever preferredNamespace() returns OPENID1_NS.
01882                 $claimed_id = $request_identity =
01883                     Auth_OpenID_IDENTIFIER_SELECT;
01884             } else {
01885                 $request_identity = $this->endpoint->getLocalID();
01886                 $claimed_id = $this->endpoint->claimed_id;
01887             }
01888 
01889             // This is true for both OpenID 1 and 2
01890             $message->setArg(Auth_OpenID_OPENID_NS, 'identity',
01891                              $request_identity);
01892 
01893             if ($message->isOpenID2()) {
01894                 $message->setArg(Auth_OpenID_OPENID2_NS, 'claimed_id',
01895                                  $claimed_id);
01896             }
01897         }
01898 
01899         if ($this->assoc) {
01900             $message->setArg(Auth_OpenID_OPENID_NS, 'assoc_handle',
01901                              $this->assoc->handle);
01902         }
01903 
01904         return $message;
01905     }
01906 
01907     function redirectURL($realm, $return_to = null,
01908                          $immediate = false)
01909     {
01910         $message = $this->getMessage($realm, $return_to, $immediate);
01911 
01912         if (Auth_OpenID::isFailure($message)) {
01913             return $message;
01914         }
01915 
01916         return $message->toURL($this->endpoint->server_url);
01917     }
01918 
01927     function formMarkup($realm, $return_to=null, $immediate=false,
01928                         $form_tag_attrs=null)
01929     {
01930         $message = $this->getMessage($realm, $return_to, $immediate);
01931 
01932         if (Auth_OpenID::isFailure($message)) {
01933             return $message;
01934         }
01935 
01936         return $message->toFormMarkup($this->endpoint->server_url,
01937                                       $form_tag_attrs);
01938     }
01939 
01946     function htmlMarkup($realm, $return_to=null, $immediate=false,
01947                         $form_tag_attrs=null)
01948     {
01949         $form = $this->formMarkup($realm, $return_to, $immediate, 
01950                                   $form_tag_attrs);
01951 
01952         if (Auth_OpenID::isFailure($form)) {
01953             return $form;
01954         }
01955         return Auth_OpenID::autoSubmitHTML($form);
01956     }
01957 
01958     function shouldSendRedirect()
01959     {
01960         return $this->endpoint->compatibilityMode();
01961     }
01962 }
01963 
01969 class Auth_OpenID_ConsumerResponse {
01970     var $status = null;
01971 
01972     function setEndpoint($endpoint)
01973     {
01974         $this->endpoint = $endpoint;
01975         if ($endpoint === null) {
01976             $this->identity_url = null;
01977         } else {
01978             $this->identity_url = $endpoint->claimed_id;
01979         }
01980     }
01981 
01999     function getDisplayIdentifier()
02000     {
02001         if ($this->endpoint !== null) {
02002             return $this->endpoint->getDisplayIdentifier();
02003         }
02004         return null;
02005     }
02006 }
02007 
02023 class Auth_OpenID_SuccessResponse extends Auth_OpenID_ConsumerResponse {
02024     var $status = Auth_OpenID_SUCCESS;
02025 
02029     function Auth_OpenID_SuccessResponse($endpoint, $message, $signed_args=null)
02030     {
02031         $this->endpoint = $endpoint;
02032         $this->identity_url = $endpoint->claimed_id;
02033         $this->signed_args = $signed_args;
02034         $this->message = $message;
02035 
02036         if ($this->signed_args === null) {
02037             $this->signed_args = array();
02038         }
02039     }
02040 
02047     function extensionResponse($namespace_uri, $require_signed)
02048     {
02049         if ($require_signed) {
02050             return $this->getSignedNS($namespace_uri);
02051         } else {
02052             return $this->message->getArgs($namespace_uri);
02053         }
02054     }
02055 
02056     function isOpenID1()
02057     {
02058         return $this->message->isOpenID1();
02059     }
02060 
02061     function isSigned($ns_uri, $ns_key)
02062     {
02063         // Return whether a particular key is signed, regardless of
02064         // its namespace alias
02065         return in_array($this->message->getKey($ns_uri, $ns_key),
02066                         $this->signed_args);
02067     }
02068 
02069     function getSigned($ns_uri, $ns_key, $default = null)
02070     {
02071         // Return the specified signed field if available, otherwise
02072         // return default
02073         if ($this->isSigned($ns_uri, $ns_key)) {
02074             return $this->message->getArg($ns_uri, $ns_key, $default);
02075         } else {
02076             return $default;
02077         }
02078     }
02079 
02080     function getSignedNS($ns_uri)
02081     {
02082         $args = array();
02083 
02084         $msg_args = $this->message->getArgs($ns_uri);
02085         if (Auth_OpenID::isFailure($msg_args)) {
02086             return null;
02087         }
02088 
02089         foreach ($msg_args as $key => $value) {
02090             if (!$this->isSigned($ns_uri, $key)) {
02091                 return null;
02092             }
02093         }
02094 
02095         return $msg_args;
02096     }
02097 
02108     function getReturnTo()
02109     {
02110         return $this->getSigned(Auth_OpenID_OPENID_NS, 'return_to');
02111     }
02112 }
02113 
02129 class Auth_OpenID_FailureResponse extends Auth_OpenID_ConsumerResponse {
02130     var $status = Auth_OpenID_FAILURE;
02131 
02132     function Auth_OpenID_FailureResponse($endpoint, $message = null,
02133                                          $contact = null, $reference = null)
02134     {
02135         $this->setEndpoint($endpoint);
02136         $this->message = $message;
02137         $this->contact = $contact;
02138         $this->reference = $reference;
02139     }
02140 }
02141 
02147 class Auth_OpenID_TypeURIMismatch extends Auth_OpenID_FailureResponse {
02148 }
02149 
02156 class Auth_OpenID_ServerErrorContainer {
02157     function Auth_OpenID_ServerErrorContainer($error_text,
02158                                               $error_code,
02159                                               $message)
02160     {
02161         $this->error_text = $error_text;
02162         $this->error_code = $error_code;
02163         $this->message = $message;
02164     }
02165 
02169     function fromMessage($message)
02170     {
02171         $error_text = $message->getArg(
02172            Auth_OpenID_OPENID_NS, 'error', '<no error message supplied>');
02173         $error_code = $message->getArg(Auth_OpenID_OPENID_NS, 'error_code');
02174         return new Auth_OpenID_ServerErrorContainer($error_text,
02175                                                     $error_code,
02176                                                     $message);
02177     }
02178 }
02179 
02192 class Auth_OpenID_CancelResponse extends Auth_OpenID_ConsumerResponse {
02193     var $status = Auth_OpenID_CANCEL;
02194 
02195     function Auth_OpenID_CancelResponse($endpoint)
02196     {
02197         $this->setEndpoint($endpoint);
02198     }
02199 }
02200 
02218 class Auth_OpenID_SetupNeededResponse extends Auth_OpenID_ConsumerResponse {
02219     var $status = Auth_OpenID_SETUP_NEEDED;
02220 
02221     function Auth_OpenID_SetupNeededResponse($endpoint,
02222                                              $setup_url = null)
02223     {
02224         $this->setEndpoint($endpoint);
02225         $this->setup_url = $setup_url;
02226     }
02227 }
02228 
02229 ?>

Generated on Thu Feb 19 15:02:21 2009 for OXID eShop CE by  doxygen 1.5.5