1<?php 2 3/** 4 * Implements the OpenID attribute exchange specification, version 1.0 5 * as of svn revision 370 from openid.net svn. 6 * 7 * @package OpenID 8 */ 9 10/** 11 * Require utility classes and functions for the consumer. 12 */ 13require_once "Auth/OpenID/Extension.php"; 14require_once "Auth/OpenID/Message.php"; 15require_once "Auth/OpenID/TrustRoot.php"; 16 17define('Auth_OpenID_AX_NS_URI', 18 'http://openid.net/srv/ax/1.0'); 19 20// Use this as the 'count' value for an attribute in a FetchRequest to 21// ask for as many values as the OP can provide. 22define('Auth_OpenID_AX_UNLIMITED_VALUES', 'unlimited'); 23 24// Minimum supported alias length in characters. Here for 25// completeness. 26define('Auth_OpenID_AX_MINIMUM_SUPPORTED_ALIAS_LENGTH', 32); 27 28/** 29 * AX utility class. 30 * 31 * @package OpenID 32 */ 33class Auth_OpenID_AX { 34 /** 35 * @param mixed $thing Any object which may be an 36 * Auth_OpenID_AX_Error object. 37 * 38 * @return bool true if $thing is an Auth_OpenID_AX_Error; false 39 * if not. 40 */ 41 static function isError($thing) 42 { 43 return is_a($thing, 'Auth_OpenID_AX_Error'); 44 } 45} 46 47/** 48 * Check an alias for invalid characters; raise AXError if any are 49 * found. Return None if the alias is valid. 50 * 51 * @param string $alias 52 * @return Auth_OpenID_AX_Error|bool 53 */ 54function Auth_OpenID_AX_checkAlias($alias) 55{ 56 if (strpos($alias, ',') !== false) { 57 return new Auth_OpenID_AX_Error(sprintf( 58 "Alias %s must not contain comma", $alias)); 59 } 60 if (strpos($alias, '.') !== false) { 61 return new Auth_OpenID_AX_Error(sprintf( 62 "Alias %s must not contain period", $alias)); 63 } 64 65 return true; 66} 67 68/** 69 * Results from data that does not meet the attribute exchange 1.0 70 * specification 71 * 72 * @package OpenID 73 */ 74class Auth_OpenID_AX_Error { 75 function __construct($message=null) 76 { 77 $this->message = $message; 78 } 79} 80 81/** 82 * Abstract class containing common code for attribute exchange 83 * messages. 84 * 85 * @package OpenID 86 */ 87class Auth_OpenID_AX_Message extends Auth_OpenID_Extension { 88 /** 89 * ns_alias: The preferred namespace alias for attribute exchange 90 * messages 91 */ 92 public $ns_alias = 'ax'; 93 94 /** 95 * mode: The type of this attribute exchange message. This must be 96 * overridden in subclasses. 97 */ 98 public $mode = null; 99 100 public $ns_uri = Auth_OpenID_AX_NS_URI; 101 102 /** 103 * Return Auth_OpenID_AX_Error if the mode in the attribute 104 * exchange arguments does not match what is expected for this 105 * class; true otherwise. 106 * 107 * @access private 108 * @param array $ax_args 109 * @return Auth_OpenID_AX_Error|bool 110 */ 111 function _checkMode($ax_args) 112 { 113 $mode = Auth_OpenID::arrayGet($ax_args, 'mode'); 114 if ($mode != $this->mode) { 115 return new Auth_OpenID_AX_Error( 116 sprintf( 117 "Expected mode '%s'; got '%s'", 118 $this->mode, $mode)); 119 } 120 121 return true; 122 } 123 124 /** 125 * Return a set of attribute exchange arguments containing the 126 * basic information that must be in every attribute exchange 127 * message. 128 * 129 * @access private 130 */ 131 function _newArgs() 132 { 133 return ['mode' => $this->mode]; 134 } 135} 136 137/** 138 * Represents a single attribute in an attribute exchange 139 * request. This should be added to an AXRequest object in order to 140 * request the attribute. 141 * 142 * @package OpenID 143 */ 144class Auth_OpenID_AX_AttrInfo { 145 /** 146 * Construct an attribute information object. Do not call this 147 * directly; call make(...) instead. 148 * 149 * @param string $type_uri The type URI for this attribute. 150 * 151 * @param int $count The number of values of this type to request. 152 * 153 * @param bool $required Whether the attribute will be marked as 154 * required in the request. 155 * 156 * @param string $alias The name that should be given to this 157 * attribute in the request. 158 */ 159 function __construct($type_uri, $count, $required, 160 $alias) 161 { 162 /** 163 * required: Whether the attribute will be marked as required 164 * when presented to the subject of the attribute exchange 165 * request. 166 */ 167 $this->required = $required; 168 169 /** 170 * count: How many values of this type to request from the 171 * subject. Defaults to one. 172 */ 173 $this->count = $count; 174 175 /** 176 * type_uri: The identifier that determines what the attribute 177 * represents and how it is serialized. For example, one type 178 * URI representing dates could represent a Unix timestamp in 179 * base 10 and another could represent a human-readable 180 * string. 181 */ 182 $this->type_uri = $type_uri; 183 184 /** 185 * alias: The name that should be given to this attribute in 186 * the request. If it is not supplied, a generic name will be 187 * assigned. For example, if you want to call a Unix timestamp 188 * value 'tstamp', set its alias to that value. If two 189 * attributes in the same message request to use the same 190 * alias, the request will fail to be generated. 191 */ 192 $this->alias = $alias; 193 } 194 195 /** 196 * Construct an attribute information object. For parameter 197 * details, see the constructor. 198 * 199 * @param string $type_uri 200 * @param int $count 201 * @param bool $required 202 * @param string|null $alias 203 * @return Auth_OpenID_AX_AttrInfo|Auth_OpenID_AX_Error|bool 204 */ 205 static function make($type_uri, $count=1, $required=false, 206 $alias=null) 207 { 208 if ($alias !== null) { 209 $result = Auth_OpenID_AX_checkAlias($alias); 210 211 if (Auth_OpenID_AX::isError($result)) { 212 return $result; 213 } 214 } 215 216 return new Auth_OpenID_AX_AttrInfo($type_uri, $count, $required, 217 $alias); 218 } 219 220 /** 221 * When processing a request for this attribute, the OP should 222 * call this method to determine whether all available attribute 223 * values were requested. If self.count == UNLIMITED_VALUES, this 224 * returns True. Otherwise this returns False, in which case 225 * self.count is an integer. 226 */ 227 function wantsUnlimitedValues() 228 { 229 return $this->count === Auth_OpenID_AX_UNLIMITED_VALUES; 230 } 231} 232 233/** 234 * Given a namespace mapping and a string containing a comma-separated 235 * list of namespace aliases, return a list of type URIs that 236 * correspond to those aliases. 237 * 238 * @param Auth_OpenID_NamespaceMap $namespace_map The mapping from namespace URI to alias 239 * @param string $alias_list_s The string containing the comma-separated 240 * list of aliases. May also be None for convenience. 241 * 242 * @return string[]|Auth_OpenID_AX_Error The list of namespace URIs that corresponds to the 243 * supplied list of aliases. If the string was zero-length or None, an 244 * empty list will be returned. 245 * 246 * return null If an alias is present in the list of aliases but 247 * is not present in the namespace map. 248 */ 249function Auth_OpenID_AX_toTypeURIs($namespace_map, $alias_list_s) 250{ 251 $uris = []; 252 253 if ($alias_list_s) { 254 foreach (explode(',', $alias_list_s) as $alias) { 255 $type_uri = $namespace_map->getNamespaceURI($alias); 256 if ($type_uri === null) { 257 // raise KeyError( 258 // 'No type is defined for attribute name %r' % (alias,)) 259 return new Auth_OpenID_AX_Error( 260 sprintf('No type is defined for attribute name %s', 261 $alias) 262 ); 263 } else { 264 $uris[] = $type_uri; 265 } 266 } 267 } 268 269 return $uris; 270} 271 272/** 273 * An attribute exchange 'fetch_request' message. This message is sent 274 * by a relying party when it wishes to obtain attributes about the 275 * subject of an OpenID authentication request. 276 * 277 * @package OpenID 278 */ 279class Auth_OpenID_AX_FetchRequest extends Auth_OpenID_AX_Message { 280 281 public $mode = 'fetch_request'; 282 283 /** 284 * update_url: A URL that will accept responses for this 285 * attribute exchange request, even in the absence of the user 286 * who made this request. 287 * 288 * @var string 289 */ 290 public $update_url = ''; 291 292 /** 293 * requested_attributes: The attributes that have been 294 * requested thus far, indexed by the type URI. 295 * 296 * @var array 297 */ 298 private $requested_attributes = []; 299 300 function __construct($update_url=null) 301 { 302 $this->update_url = $update_url; 303 } 304 305 /** 306 * Add an attribute to this attribute exchange request. 307 * 308 * @param attribute: The attribute that is being requested 309 * @return bool|Auth_OpenID_AX_Error 310 */ 311 function add($attribute) 312 { 313 if ($this->contains($attribute->type_uri)) { 314 return new Auth_OpenID_AX_Error( 315 sprintf("The attribute %s has already been requested", 316 $attribute->type_uri)); 317 } 318 319 $this->requested_attributes[$attribute->type_uri] = $attribute; 320 321 return true; 322 } 323 324 /** 325 * Get the serialized form of this attribute fetch request. 326 * 327 * @param Auth_OpenID_Request|null $request 328 * @return Auth_OpenID_AX_Error|Auth_OpenID_AX_FetchRequest The fetch request message parameters 329 */ 330 function getExtensionArgs($request = null) 331 { 332 $aliases = new Auth_OpenID_NamespaceMap(); 333 334 $required = []; 335 $if_available = []; 336 337 $ax_args = $this->_newArgs(); 338 339 foreach ($this->requested_attributes as $type_uri => $attribute) { 340 if ($attribute->alias === null) { 341 $alias = $aliases->add($type_uri); 342 } else { 343 $alias = $aliases->addAlias($type_uri, $attribute->alias); 344 345 if ($alias === null) { 346 return new Auth_OpenID_AX_Error( 347 sprintf("Could not add alias %s for URI %s", 348 $attribute->alias, $type_uri 349 )); 350 } 351 } 352 353 if ($attribute->required) { 354 $required[] = $alias; 355 } else { 356 $if_available[] = $alias; 357 } 358 359 if ($attribute->count != 1) { 360 $ax_args['count.' . $alias] = strval($attribute->count); 361 } 362 363 $ax_args['type.' . $alias] = $type_uri; 364 } 365 366 if ($required) { 367 $ax_args['required'] = implode(',', $required); 368 } 369 370 if ($if_available) { 371 $ax_args['if_available'] = implode(',', $if_available); 372 } 373 374 return $ax_args; 375 } 376 377 /** 378 * Get the type URIs for all attributes that have been marked as 379 * required. 380 * 381 * @return array A list of the type URIs for attributes that have been 382 * marked as required. 383 */ 384 function getRequiredAttrs() 385 { 386 $required = []; 387 foreach ($this->requested_attributes as $type_uri => $attribute) { 388 if ($attribute->required) { 389 $required[] = $type_uri; 390 } 391 } 392 393 return $required; 394 } 395 396 /** 397 * Extract a FetchRequest from an OpenID message 398 * 399 * @param Auth_OpenID_Request $request The OpenID request containing the attribute fetch request 400 * 401 * @return Auth_OpenID_AX_FetchRequest|Auth_OpenID_AX_Error 402 */ 403 static function fromOpenIDRequest($request) 404 { 405 $m = $request->message; 406 $obj = new Auth_OpenID_AX_FetchRequest(); 407 $ax_args = $m->getArgs($obj->ns_uri); 408 409 $result = $obj->parseExtensionArgs($ax_args); 410 411 if (Auth_OpenID_AX::isError($result)) { 412 return $result; 413 } 414 415 if ($obj->update_url) { 416 // Update URL must match the openid.realm of the 417 // underlying OpenID 2 message. 418 $realm = $m->getArg(Auth_OpenID_OPENID_NS, 'realm', 419 $m->getArg( 420 Auth_OpenID_OPENID_NS, 421 'return_to')); 422 423 if (!$realm) { 424 $obj = new Auth_OpenID_AX_Error( 425 sprintf("Cannot validate update_url %s " . 426 "against absent realm", $obj->update_url)); 427 } else if (!Auth_OpenID_TrustRoot::match($realm, 428 $obj->update_url)) { 429 $obj = new Auth_OpenID_AX_Error( 430 sprintf("Update URL %s failed validation against realm %s", 431 $obj->update_url, $realm)); 432 } 433 } 434 435 return $obj; 436 } 437 438 /** 439 * Given attribute exchange arguments, populate this FetchRequest. 440 * 441 * @param array $ax_args 442 * @return Auth_OpenID_AX_Error|bool if the data to be parsed 443 * does not follow the attribute exchange specification. At least 444 * when 'if_available' or 'required' is not specified for a 445 * particular attribute type. Returns true otherwise. 446 */ 447 function parseExtensionArgs($ax_args) 448 { 449 $result = $this->_checkMode($ax_args); 450 if (Auth_OpenID_AX::isError($result)) { 451 return $result; 452 } 453 454 $aliases = new Auth_OpenID_NamespaceMap(); 455 456 foreach ($ax_args as $key => $value) { 457 if (strpos($key, 'type.') === 0) { 458 $alias = substr($key, 5); 459 $type_uri = $value; 460 461 $alias = $aliases->addAlias($type_uri, $alias); 462 463 if ($alias === null) { 464 return new Auth_OpenID_AX_Error( 465 sprintf("Could not add alias %s for URI %s", 466 $alias, $type_uri) 467 ); 468 } 469 470 $count_s = Auth_OpenID::arrayGet($ax_args, 'count.' . $alias); 471 if ($count_s) { 472 $count = Auth_OpenID::intval($count_s); 473 if (($count === false) && 474 ($count_s === Auth_OpenID_AX_UNLIMITED_VALUES)) { 475 $count = $count_s; 476 } 477 } else { 478 $count = 1; 479 } 480 481 if ($count === false) { 482 return new Auth_OpenID_AX_Error( 483 sprintf("Integer value expected for %s, got %s", 484 'count.' . $alias, $count_s)); 485 } 486 487 $attrinfo = Auth_OpenID_AX_AttrInfo::make($type_uri, $count, 488 false, $alias); 489 490 if (Auth_OpenID_AX::isError($attrinfo)) { 491 return $attrinfo; 492 } 493 494 $this->add($attrinfo); 495 } 496 } 497 498 $required = Auth_OpenID_AX_toTypeURIs($aliases, 499 Auth_OpenID::arrayGet($ax_args, 'required')); 500 501 foreach ($required as $type_uri) { 502 $attrib = $this->requested_attributes[$type_uri]; 503 $attrib->required = true; 504 } 505 506 $if_available = Auth_OpenID_AX_toTypeURIs($aliases, 507 Auth_OpenID::arrayGet($ax_args, 'if_available')); 508 509 $all_type_uris = array_merge($required, $if_available); 510 511 foreach ($aliases->iterNamespaceURIs() as $type_uri) { 512 if (!in_array($type_uri, $all_type_uris)) { 513 return new Auth_OpenID_AX_Error( 514 sprintf('Type URI %s was in the request but not ' . 515 'present in "required" or "if_available"', 516 $type_uri)); 517 518 } 519 } 520 521 $this->update_url = Auth_OpenID::arrayGet($ax_args, 'update_url'); 522 523 return true; 524 } 525 526 /** 527 * Iterate over the AttrInfo objects that are contained in this 528 * fetch_request. 529 */ 530 function iterAttrs() 531 { 532 return array_values($this->requested_attributes); 533 } 534 535 function iterTypes() 536 { 537 return array_keys($this->requested_attributes); 538 } 539 540 /** 541 * Is the given type URI present in this fetch_request? 542 * 543 * @param string $type_uri 544 * @return bool 545 */ 546 function contains($type_uri) 547 { 548 return in_array($type_uri, $this->iterTypes()); 549 } 550} 551 552/** 553 * An abstract class that implements a message that has attribute keys 554 * and values. It contains the common code between fetch_response and 555 * store_request. 556 * 557 * @package OpenID 558 */ 559class Auth_OpenID_AX_KeyValueMessage extends Auth_OpenID_AX_Message { 560 561 /** @var array */ 562 protected $data = []; 563 564 /** 565 * Add a single value for the given attribute type to the 566 * message. If there are already values specified for this type, 567 * this value will be sent in addition to the values already 568 * specified. 569 * 570 * @param type_uri: The URI for the attribute 571 * @param value: The value to add to the response to the relying 572 * party for this attribute 573 */ 574 function addValue($type_uri, $value) 575 { 576 if (!array_key_exists($type_uri, $this->data)) { 577 $this->data[$type_uri] = []; 578 } 579 580 $values =& $this->data[$type_uri]; 581 $values[] = $value; 582 } 583 584 /** 585 * Set the values for the given attribute type. This replaces any 586 * values that have already been set for this attribute. 587 * 588 * @param type_uri: The URI for the attribute 589 * @param values: A list of values to send for this attribute. 590 */ 591 function setValues($type_uri, &$values) 592 { 593 $this->data[$type_uri] =& $values; 594 } 595 596 /** 597 * Get the extension arguments for the key/value pairs contained 598 * in this message. 599 * 600 * @param Auth_OpenID_NamespaceMap $aliases An alias mapping. Set to None if you don't care 601 * about the aliases for this request. 602 * 603 * @access private 604 * @return array 605 */ 606 function _getExtensionKpublicgs($aliases) 607 { 608 if ($aliases === null) { 609 $aliases = new Auth_OpenID_NamespaceMap(); 610 } 611 612 $ax_args = []; 613 614 foreach ($this->data as $type_uri => $values) { 615 $alias = $aliases->add($type_uri); 616 617 $ax_args['type.' . $alias] = $type_uri; 618 $ax_args['count.' . $alias] = strval(count($values)); 619 620 foreach ($values as $i => $value) { 621 $key = sprintf('value.%s.%d', $alias, $i + 1); 622 $ax_args[$key] = $value; 623 } 624 } 625 626 return $ax_args; 627 } 628 629 /** 630 * Parse attribute exchange key/value arguments into this object. 631 * 632 * @param array $ax_args The attribute exchange fetch_response 633 * arguments, with namespacing removed. 634 * 635 * @return Auth_OpenID_AX_Error|bool 636 */ 637 function parseExtensionArgs($ax_args) 638 { 639 $result = $this->_checkMode($ax_args); 640 if (Auth_OpenID_AX::isError($result)) { 641 return $result; 642 } 643 644 $aliases = new Auth_OpenID_NamespaceMap(); 645 646 foreach ($ax_args as $key => $value) { 647 if (strpos($key, 'type.') === 0) { 648 $type_uri = $value; 649 $alias = substr($key, 5); 650 651 $result = Auth_OpenID_AX_checkAlias($alias); 652 653 if (Auth_OpenID_AX::isError($result)) { 654 return $result; 655 } 656 657 $alias = $aliases->addAlias($type_uri, $alias); 658 659 if ($alias === null) { 660 return new Auth_OpenID_AX_Error( 661 sprintf("Could not add alias %s for URI %s", 662 $alias, $type_uri) 663 ); 664 } 665 } 666 } 667 668 foreach ($aliases->iteritems() as $pair) { 669 list($type_uri, $alias) = $pair; 670 671 if (array_key_exists('count.' . $alias, $ax_args) && ($ax_args['count.' . $alias] !== Auth_OpenID_AX_UNLIMITED_VALUES)) { 672 673 $count_key = 'count.' . $alias; 674 $count_s = $ax_args[$count_key]; 675 676 $count = Auth_OpenID::intval($count_s); 677 678 if ($count === false) { 679 return new Auth_OpenID_AX_Error( 680 sprintf("Integer value expected for %s, got %s", 681 'count. %s' . $alias, $count_s, 682 Auth_OpenID_AX_UNLIMITED_VALUES) 683 ); 684 } 685 686 $values = []; 687 for ($i = 1; $i < $count + 1; $i++) { 688 $value_key = sprintf('value.%s.%d', $alias, $i); 689 690 if (!array_key_exists($value_key, $ax_args)) { 691 return new Auth_OpenID_AX_Error( 692 sprintf( 693 "No value found for key %s", 694 $value_key)); 695 } 696 697 $value = $ax_args[$value_key]; 698 $values[] = $value; 699 } 700 } else { 701 $key = 'value.' . $alias; 702 703 if (!array_key_exists($key, $ax_args)) { 704 return new Auth_OpenID_AX_Error( 705 sprintf( 706 "No value found for key %s", 707 $key)); 708 } 709 710 $value = $ax_args['value.' . $alias]; 711 712 if ($value == '') { 713 $values = []; 714 } else { 715 $values = [$value]; 716 } 717 } 718 719 $this->data[$type_uri] = $values; 720 } 721 722 return true; 723 } 724 725 /** 726 * Get a single value for an attribute. If no value was sent for 727 * this attribute, use the supplied default. If there is more than 728 * one value for this attribute, this method will fail. 729 * 730 * @param string $type_uri The URI for the attribute 731 * @param mixed $default The value to return if the attribute was not 732 * sent in the fetch_response. 733 * 734 * @return Auth_OpenID_AX_Error|mixed 735 */ 736 function getSingle($type_uri, $default=null) 737 { 738 $values = Auth_OpenID::arrayGet($this->data, $type_uri); 739 if (!$values) { 740 return $default; 741 } else if (count($values) == 1) { 742 return $values[0]; 743 } else { 744 return new Auth_OpenID_AX_Error( 745 sprintf('More than one value present for %s', 746 $type_uri) 747 ); 748 } 749 } 750 751 /** 752 * Get the list of values for this attribute in the 753 * fetch_response. 754 * 755 * XXX: what to do if the values are not present? default 756 * parameter? this is funny because it's always supposed to return 757 * a list, so the default may break that, though it's provided by 758 * the user's code, so it might be okay. If no default is 759 * supplied, should the return be None or []? 760 * 761 * @param string $type_uri The URI of the attribute 762 * 763 * @return Auth_OpenID_AX_Error|array The list of values for this attribute in the 764 * response. May be an empty list. If the attribute was not sent 765 * in the response, returns Auth_OpenID_AX_Error. 766 */ 767 function get($type_uri) 768 { 769 if (array_key_exists($type_uri, $this->data)) { 770 return $this->data[$type_uri]; 771 } else { 772 return new Auth_OpenID_AX_Error( 773 sprintf("Type URI %s not found in response", 774 $type_uri) 775 ); 776 } 777 } 778 779 /** 780 * Get the number of responses for a particular attribute in this 781 * fetch_response message. 782 * 783 * @param string $type_uri The URI of the attribute 784 * 785 * @returns int|Auth_OpenID_AX_Error The number of values sent for this attribute. If 786 * the attribute was not sent in the response, returns 787 * Auth_OpenID_AX_Error. 788 */ 789 function count($type_uri) 790 { 791 if (array_key_exists($type_uri, $this->data)) { 792 return count($this->get($type_uri)); 793 } else { 794 return new Auth_OpenID_AX_Error( 795 sprintf("Type URI %s not found in response", 796 $type_uri) 797 ); 798 } 799 } 800} 801 802/** 803 * A fetch_response attribute exchange message. 804 * 805 * @package OpenID 806 */ 807class Auth_OpenID_AX_FetchResponse extends Auth_OpenID_AX_KeyValueMessage { 808 public $mode = 'fetch_response'; 809 810 /** @var string */ 811 private $update_url = ''; 812 813 function __construct($update_url=null) 814 { 815 $this->update_url = $update_url; 816 } 817 818 /** 819 * Serialize this object into arguments in the attribute exchange 820 * namespace 821 * 822 * @param Auth_OpenID_AX_FetchRequest|null $request 823 * @return Auth_OpenID_AX_Error|array|null $args The dictionary of unqualified attribute exchange 824 * arguments that represent this fetch_response, or 825 * Auth_OpenID_AX_Error on error. 826 */ 827 function getExtensionArgs($request=null) 828 { 829 $aliases = new Auth_OpenID_NamespaceMap(); 830 831 $zero_value_types = []; 832 833 if ($request !== null) { 834 // Validate the data in the context of the request (the 835 // same attributes should be present in each, and the 836 // counts in the response must be no more than the counts 837 // in the request) 838 839 foreach ($this->data as $type_uri => $unused) { 840 if (!$request->contains($type_uri)) { 841 return new Auth_OpenID_AX_Error( 842 sprintf("Response attribute not present in request: %s", 843 $type_uri) 844 ); 845 } 846 } 847 848 foreach ($request->iterAttrs() as $attr_info) { 849 // Copy the aliases from the request so that reading 850 // the response in light of the request is easier 851 if ($attr_info->alias === null) { 852 $aliases->add($attr_info->type_uri); 853 } else { 854 $alias = $aliases->addAlias($attr_info->type_uri, 855 $attr_info->alias); 856 857 if ($alias === null) { 858 return new Auth_OpenID_AX_Error( 859 sprintf("Could not add alias %s for URI %s", 860 $attr_info->alias, $attr_info->type_uri) 861 ); 862 } 863 } 864 865 if (array_key_exists($attr_info->type_uri, $this->data)) { 866 $values = $this->data[$attr_info->type_uri]; 867 } else { 868 $values = []; 869 $zero_value_types[] = $attr_info; 870 } 871 872 if (($attr_info->count != Auth_OpenID_AX_UNLIMITED_VALUES) && 873 ($attr_info->count < count($values))) { 874 return new Auth_OpenID_AX_Error( 875 sprintf("More than the number of requested values " . 876 "were specified for %s", 877 $attr_info->type_uri) 878 ); 879 } 880 } 881 } 882 883 $kv_args = $this->_getExtensionKpublicgs($aliases); 884 885 // Add the KV args into the response with the args that are 886 // unique to the fetch_response 887 $ax_args = $this->_newArgs(); 888 889 // For each requested attribute, put its type/alias and count 890 // into the response even if no data were returned. 891 foreach ($zero_value_types as $attr_info) { 892 $alias = $aliases->getAlias($attr_info->type_uri); 893 $kv_args['type.' . $alias] = $attr_info->type_uri; 894 $kv_args['count.' . $alias] = '0'; 895 } 896 897 $update_url = null; 898 if ($request) { 899 $update_url = $request->update_url; 900 } else { 901 $update_url = $this->update_url; 902 } 903 904 if ($update_url) { 905 $ax_args['update_url'] = $update_url; 906 } 907 908 Auth_OpenID::update($ax_args, $kv_args); 909 910 return $ax_args; 911 } 912 913 /** 914 * @param array $ax_args 915 * @return Auth_OpenID_AX_Error|bool Auth_OpenID_AX_Error on failure or true on 916 * success. 917 */ 918 function parseExtensionArgs($ax_args) 919 { 920 $result = parent::parseExtensionArgs($ax_args); 921 922 if (Auth_OpenID_AX::isError($result)) { 923 return $result; 924 } 925 926 $this->update_url = Auth_OpenID::arrayGet($ax_args, 'update_url'); 927 928 return true; 929 } 930 931 /** 932 * Construct a FetchResponse object from an OpenID library 933 * SuccessResponse object. 934 * 935 * @param Auth_OpenID_SuccessResponse $success_response A successful id_res response object 936 * 937 * @param bool $signed Whether non-signed args should be processsed. If 938 * True (the default), only signed arguments will be processsed. 939 * 940 * @return Auth_OpenID_AX_FetchResponse|null A FetchResponse containing the data from the 941 * OpenID message 942 */ 943 static function fromSuccessResponse($success_response, $signed=true) 944 { 945 $obj = new Auth_OpenID_AX_FetchResponse(); 946 if ($signed) { 947 $ax_args = $success_response->getSignedNS($obj->ns_uri); 948 } else { 949 $ax_args = $success_response->message->getArgs($obj->ns_uri); 950 } 951 if ($ax_args === null || Auth_OpenID::isFailure($ax_args) || 952 sizeof($ax_args) == 0) { 953 return null; 954 } 955 956 $result = $obj->parseExtensionArgs($ax_args); 957 if (Auth_OpenID_AX::isError($result)) { 958 #XXX log me 959 return null; 960 } 961 return $obj; 962 } 963} 964 965/** 966 * A store request attribute exchange message representation. 967 * 968 * @package OpenID 969 */ 970class Auth_OpenID_AX_StoreRequest extends Auth_OpenID_AX_KeyValueMessage { 971 public $mode = 'store_request'; 972 973 /** 974 * @param Auth_OpenID_NamespaceMap $aliases The namespace aliases to use when making 975 * this store response. Leave as None to use defaults. 976 * @return array|null 977 */ 978 function getExtensionArgs($aliases=null) 979 { 980 $ax_args = $this->_newArgs(); 981 $kv_args = $this->_getExtensionKpublicgs($aliases); 982 Auth_OpenID::update($ax_args, $kv_args); 983 return $ax_args; 984 } 985} 986 987/** 988 * An indication that the store request was processed along with this 989 * OpenID transaction. Use make(), NOT the constructor, to create 990 * response objects. 991 * 992 * @package OpenID 993 */ 994class Auth_OpenID_AX_StoreResponse extends Auth_OpenID_AX_Message { 995 public $SUCCESS_MODE = 'store_response_success'; 996 public $FAILURE_MODE = 'store_response_failure'; 997 998 /** 999 * @var string 1000 */ 1001 private $error_message = ''; 1002 1003 /** 1004 * @param bool $succeeded 1005 * @param string $error_message 1006 * @return Auth_OpenID_AX_Error|Auth_OpenID_AX_StoreResponse 1007 */ 1008 function make($succeeded=true, $error_message=null) 1009 { 1010 if (($succeeded) && ($error_message !== null)) { 1011 return new Auth_OpenID_AX_Error('An error message may only be '. 1012 'included in a failing fetch response'); 1013 } 1014 1015 return new Auth_OpenID_AX_StoreResponse($succeeded, $error_message); 1016 } 1017 1018 function __construct($succeeded=true, $error_message=null) 1019 { 1020 if ($succeeded) { 1021 $this->mode = $this->SUCCESS_MODE; 1022 } else { 1023 $this->mode = $this->FAILURE_MODE; 1024 } 1025 1026 $this->error_message = $error_message; 1027 } 1028 1029 /** 1030 * Was this response a success response? 1031 */ 1032 function succeeded() 1033 { 1034 return $this->mode == $this->SUCCESS_MODE; 1035 } 1036 1037 /** 1038 * Get the string arguments that should be added to an OpenID 1039 * message for this extension. 1040 * 1041 * @param Auth_OpenID_Request|null $request 1042 * @return null 1043 */ 1044 function getExtensionArgs($request = null) 1045 { 1046 $ax_args = $this->_newArgs(); 1047 if ((!$this->succeeded()) && $this->error_message) { 1048 $ax_args['error'] = $this->error_message; 1049 } 1050 1051 return $ax_args; 1052 } 1053} 1054 1055