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