00001 <?php
00002
00012 require_once 'Auth/OpenID.php';
00013 require_once 'Auth/OpenID/KVForm.php';
00014 require_once 'Auth/Yadis/XML.php';
00015 require_once 'Auth/OpenID/Consumer.php';
00016
00017
00018 define('Auth_OpenID_IDENTIFIER_SELECT',
00019 "http://specs.openid.net/auth/2.0/identifier_select");
00020
00021
00022
00023 define('Auth_OpenID_SREG_URI', 'http://openid.net/sreg/1.0');
00024
00025
00026 define('Auth_OpenID_OPENID1_NS', 'http://openid.net/signon/1.0');
00027 define('Auth_OpenID_THE_OTHER_OPENID1_NS', 'http://openid.net/signon/1.1');
00028
00029 function Auth_OpenID_isOpenID1($ns)
00030 {
00031 return ($ns == Auth_OpenID_THE_OTHER_OPENID1_NS) ||
00032 ($ns == Auth_OpenID_OPENID1_NS);
00033 }
00034
00035
00036 define('Auth_OpenID_OPENID2_NS', 'http://specs.openid.net/auth/2.0');
00037
00038
00039
00040 define('Auth_OpenID_NULL_NAMESPACE', 'Null namespace');
00041
00042
00043 define('Auth_OpenID_OPENID_NS', 'OpenID namespace');
00044
00045
00046
00047 define('Auth_OpenID_BARE_NS', 'Bare namespace');
00048
00049
00050
00051 define('Auth_OpenID_NO_DEFAULT', 'NO DEFAULT ALLOWED');
00052
00053
00054
00055 define('Auth_OpenID_OPENID1_URL_LIMIT', 2047);
00056
00057
00058 global $Auth_OpenID_OPENID_PROTOCOL_FIELDS;
00059 $Auth_OpenID_OPENID_PROTOCOL_FIELDS = array(
00060 'ns', 'mode', 'error', 'return_to', 'contact', 'reference',
00061 'signed', 'assoc_type', 'session_type', 'dh_modulus', 'dh_gen',
00062 'dh_consumer_public', 'claimed_id', 'identity', 'realm',
00063 'invalidate_handle', 'op_endpoint', 'response_nonce', 'sig',
00064 'assoc_handle', 'trust_root', 'openid');
00065
00066
00067
00068 global $Auth_OpenID_registered_aliases;
00069 $Auth_OpenID_registered_aliases = array();
00070
00078 function Auth_OpenID_registerNamespaceAlias($namespace_uri, $alias)
00079 {
00080 global $Auth_OpenID_registered_aliases;
00081
00082 if (Auth_OpenID::arrayGet($Auth_OpenID_registered_aliases,
00083 $alias) == $namespace_uri) {
00084 return true;
00085 }
00086
00087 if (in_array($namespace_uri,
00088 array_values($Auth_OpenID_registered_aliases))) {
00089 return false;
00090 }
00091
00092 if (in_array($alias, array_keys($Auth_OpenID_registered_aliases))) {
00093 return false;
00094 }
00095
00096 $Auth_OpenID_registered_aliases[$alias] = $namespace_uri;
00097 return true;
00098 }
00099
00105 function Auth_OpenID_removeNamespaceAlias($namespace_uri, $alias)
00106 {
00107 global $Auth_OpenID_registered_aliases;
00108
00109 if (Auth_OpenID::arrayGet($Auth_OpenID_registered_aliases,
00110 $alias) === $namespace_uri) {
00111 unset($Auth_OpenID_registered_aliases[$alias]);
00112 return true;
00113 }
00114
00115 return false;
00116 }
00117
00125 class Auth_OpenID_Mapping {
00130 function Auth_OpenID_Mapping($classic_array = null)
00131 {
00132 $this->keys = array();
00133 $this->values = array();
00134
00135 if (is_array($classic_array)) {
00136 foreach ($classic_array as $key => $value) {
00137 $this->set($key, $value);
00138 }
00139 }
00140 }
00141
00146 function isA($thing)
00147 {
00148 return (is_object($thing) &&
00149 strtolower(get_class($thing)) == 'auth_openid_mapping');
00150 }
00151
00155 function keys()
00156 {
00157 return $this->keys;
00158 }
00159
00163 function values()
00164 {
00165 return $this->values;
00166 }
00167
00171 function items()
00172 {
00173 $temp = array();
00174
00175 for ($i = 0; $i < count($this->keys); $i++) {
00176 $temp[] = array($this->keys[$i],
00177 $this->values[$i]);
00178 }
00179 return $temp;
00180 }
00181
00185 function len()
00186 {
00187 return count($this->keys);
00188 }
00189
00194 function set($key, $value)
00195 {
00196 $index = array_search($key, $this->keys);
00197
00198 if ($index !== false) {
00199 $this->values[$index] = $value;
00200 } else {
00201 $this->keys[] = $key;
00202 $this->values[] = $value;
00203 }
00204 }
00205
00211 function get($key, $default = null)
00212 {
00213 $index = array_search($key, $this->keys);
00214
00215 if ($index !== false) {
00216 return $this->values[$index];
00217 } else {
00218 return $default;
00219 }
00220 }
00221
00225 function _reflow()
00226 {
00227
00228
00229 $old_keys = $this->keys;
00230 $old_values = $this->values;
00231
00232 $this->keys = array();
00233 $this->values = array();
00234
00235 foreach ($old_keys as $k) {
00236 $this->keys[] = $k;
00237 }
00238
00239 foreach ($old_values as $v) {
00240 $this->values[] = $v;
00241 }
00242 }
00243
00248 function del($key)
00249 {
00250 $index = array_search($key, $this->keys);
00251
00252 if ($index !== false) {
00253 unset($this->keys[$index]);
00254 unset($this->values[$index]);
00255 $this->_reflow();
00256 return true;
00257 }
00258 return false;
00259 }
00260
00265 function contains($value)
00266 {
00267 return (array_search($value, $this->keys) !== false);
00268 }
00269 }
00270
00276 class Auth_OpenID_NamespaceMap {
00277 function Auth_OpenID_NamespaceMap()
00278 {
00279 $this->alias_to_namespace = new Auth_OpenID_Mapping();
00280 $this->namespace_to_alias = new Auth_OpenID_Mapping();
00281 $this->implicit_namespaces = array();
00282 }
00283
00284 function getAlias($namespace_uri)
00285 {
00286 return $this->namespace_to_alias->get($namespace_uri);
00287 }
00288
00289 function getNamespaceURI($alias)
00290 {
00291 return $this->alias_to_namespace->get($alias);
00292 }
00293
00294 function iterNamespaceURIs()
00295 {
00296
00297 return $this->namespace_to_alias->keys();
00298 }
00299
00300 function iterAliases()
00301 {
00302
00303 return $this->alias_to_namespace->keys();
00304 }
00305
00306 function iteritems()
00307 {
00308 return $this->namespace_to_alias->items();
00309 }
00310
00311 function isImplicit($namespace_uri)
00312 {
00313 return in_array($namespace_uri, $this->implicit_namespaces);
00314 }
00315
00316 function addAlias($namespace_uri, $desired_alias, $implicit=false)
00317 {
00318
00319 global $Auth_OpenID_OPENID_PROTOCOL_FIELDS;
00320
00321
00322
00323 if (in_array($desired_alias, $Auth_OpenID_OPENID_PROTOCOL_FIELDS)) {
00324 Auth_OpenID::log("\"%s\" is not an allowed namespace alias",
00325 $desired_alias);
00326 return null;
00327 }
00328
00329
00330
00331 if (strpos($desired_alias, '.') !== false) {
00332 Auth_OpenID::log('"%s" must not contain a dot', $desired_alias);
00333 return null;
00334 }
00335
00336
00337
00338 $current_namespace_uri =
00339 $this->alias_to_namespace->get($desired_alias);
00340
00341 if (($current_namespace_uri !== null) &&
00342 ($current_namespace_uri != $namespace_uri)) {
00343 Auth_OpenID::log('Cannot map "%s" because previous mapping exists',
00344 $namespace_uri);
00345 return null;
00346 }
00347
00348
00349
00350 $alias = $this->namespace_to_alias->get($namespace_uri);
00351
00352 if (($alias !== null) && ($alias != $desired_alias)) {
00353 Auth_OpenID::log('Cannot map %s to alias %s. ' .
00354 'It is already mapped to alias %s',
00355 $namespace_uri, $desired_alias, $alias);
00356 return null;
00357 }
00358
00359 assert((Auth_OpenID_NULL_NAMESPACE === $desired_alias) ||
00360 is_string($desired_alias));
00361
00362 $this->alias_to_namespace->set($desired_alias, $namespace_uri);
00363 $this->namespace_to_alias->set($namespace_uri, $desired_alias);
00364 if ($implicit) {
00365 array_push($this->implicit_namespaces, $namespace_uri);
00366 }
00367
00368 return $desired_alias;
00369 }
00370
00371 function add($namespace_uri)
00372 {
00373
00374
00375
00376
00377 $alias = $this->namespace_to_alias->get($namespace_uri);
00378
00379 if ($alias !== null) {
00380 return $alias;
00381 }
00382
00383
00384 $i = 0;
00385 while (1) {
00386 $alias = 'ext' . strval($i);
00387 if ($this->addAlias($namespace_uri, $alias) === null) {
00388 $i += 1;
00389 } else {
00390 return $alias;
00391 }
00392 }
00393
00394
00395 return null;
00396 }
00397
00398 function contains($namespace_uri)
00399 {
00400 return $this->isDefined($namespace_uri);
00401 }
00402
00403 function isDefined($namespace_uri)
00404 {
00405 return $this->namespace_to_alias->contains($namespace_uri);
00406 }
00407 }
00408
00415 class Auth_OpenID_Message {
00416
00417 function Auth_OpenID_Message($openid_namespace = null)
00418 {
00419
00420 $this->allowed_openid_namespaces = array(
00421 Auth_OpenID_OPENID1_NS,
00422 Auth_OpenID_THE_OTHER_OPENID1_NS,
00423 Auth_OpenID_OPENID2_NS);
00424
00425 $this->args = new Auth_OpenID_Mapping();
00426 $this->namespaces = new Auth_OpenID_NamespaceMap();
00427 if ($openid_namespace === null) {
00428 $this->_openid_ns_uri = null;
00429 } else {
00430 $implicit = Auth_OpenID_isOpenID1($openid_namespace);
00431 $this->setOpenIDNamespace($openid_namespace, $implicit);
00432 }
00433 }
00434
00435 function isOpenID1()
00436 {
00437 return Auth_OpenID_isOpenID1($this->getOpenIDNamespace());
00438 }
00439
00440 function isOpenID2()
00441 {
00442 return $this->getOpenIDNamespace() == Auth_OpenID_OPENID2_NS;
00443 }
00444
00445 function fromPostArgs($args)
00446 {
00447
00448 $obj = new Auth_OpenID_Message();
00449
00450
00451 $openid_args = array();
00452 foreach ($args as $key => $value) {
00453
00454 if (is_array($value)) {
00455 return null;
00456 }
00457
00458 $parts = explode('.', $key, 2);
00459
00460 if (count($parts) == 2) {
00461 list($prefix, $rest) = $parts;
00462 } else {
00463 $prefix = null;
00464 }
00465
00466 if ($prefix != 'openid') {
00467 $obj->args->set(array(Auth_OpenID_BARE_NS, $key), $value);
00468 } else {
00469 $openid_args[$rest] = $value;
00470 }
00471 }
00472
00473 if ($obj->_fromOpenIDArgs($openid_args)) {
00474 return $obj;
00475 } else {
00476 return null;
00477 }
00478 }
00479
00480 function fromOpenIDArgs($openid_args)
00481 {
00482
00483
00484
00485 $obj = new Auth_OpenID_Message();
00486 if ($obj->_fromOpenIDArgs($openid_args)) {
00487 return $obj;
00488 } else {
00489 return null;
00490 }
00491 }
00492
00496 function _fromOpenIDArgs($openid_args)
00497 {
00498 global $Auth_OpenID_registered_aliases;
00499
00500
00501
00502 if (!Auth_OpenID_Mapping::isA($openid_args)) {
00503 $openid_args = new Auth_OpenID_Mapping($openid_args);
00504 }
00505
00506 $ns_args = array();
00507
00508
00509 foreach ($openid_args->items() as $pair) {
00510 list($rest, $value) = $pair;
00511
00512 $parts = explode('.', $rest, 2);
00513
00514 if (count($parts) == 2) {
00515 list($ns_alias, $ns_key) = $parts;
00516 } else {
00517 $ns_alias = Auth_OpenID_NULL_NAMESPACE;
00518 $ns_key = $rest;
00519 }
00520
00521 if ($ns_alias == 'ns') {
00522 if ($this->namespaces->addAlias($value, $ns_key) === null) {
00523 return false;
00524 }
00525 } else if (($ns_alias == Auth_OpenID_NULL_NAMESPACE) &&
00526 ($ns_key == 'ns')) {
00527
00528 if ($this->setOpenIDNamespace($value, false) === false) {
00529 return false;
00530 }
00531 } else {
00532 $ns_args[] = array($ns_alias, $ns_key, $value);
00533 }
00534 }
00535
00536 if (!$this->getOpenIDNamespace()) {
00537 if ($this->setOpenIDNamespace(Auth_OpenID_OPENID1_NS, true) ===
00538 false) {
00539 return false;
00540 }
00541 }
00542
00543
00544 foreach ($ns_args as $triple) {
00545 list($ns_alias, $ns_key, $value) = $triple;
00546 $ns_uri = $this->namespaces->getNamespaceURI($ns_alias);
00547 if ($ns_uri === null) {
00548 $ns_uri = $this->_getDefaultNamespace($ns_alias);
00549 if ($ns_uri === null) {
00550
00551 $ns_uri = Auth_OpenID_OPENID_NS;
00552 $ns_key = sprintf('%s.%s', $ns_alias, $ns_key);
00553 } else {
00554 $this->namespaces->addAlias($ns_uri, $ns_alias, true);
00555 }
00556 }
00557
00558 $this->setArg($ns_uri, $ns_key, $value);
00559 }
00560
00561 return true;
00562 }
00563
00564 function _getDefaultNamespace($mystery_alias)
00565 {
00566 global $Auth_OpenID_registered_aliases;
00567 if ($this->isOpenID1()) {
00568 return @$Auth_OpenID_registered_aliases[$mystery_alias];
00569 }
00570 return null;
00571 }
00572
00573 function setOpenIDNamespace($openid_ns_uri, $implicit)
00574 {
00575 if (!in_array($openid_ns_uri, $this->allowed_openid_namespaces)) {
00576 Auth_OpenID::log('Invalid null namespace: "%s"', $openid_ns_uri);
00577 return false;
00578 }
00579
00580 $succeeded = $this->namespaces->addAlias($openid_ns_uri,
00581 Auth_OpenID_NULL_NAMESPACE,
00582 $implicit);
00583 if ($succeeded === false) {
00584 return false;
00585 }
00586
00587 $this->_openid_ns_uri = $openid_ns_uri;
00588
00589 return true;
00590 }
00591
00592 function getOpenIDNamespace()
00593 {
00594 return $this->_openid_ns_uri;
00595 }
00596
00597 function fromKVForm($kvform_string)
00598 {
00599
00600 return Auth_OpenID_Message::fromOpenIDArgs(
00601 Auth_OpenID_KVForm::toArray($kvform_string));
00602 }
00603
00604 function copy()
00605 {
00606 return $this;
00607 }
00608
00609 function toPostArgs()
00610 {
00611
00612
00613
00614 $args = array();
00615
00616
00617 foreach ($this->namespaces->iteritems() as $pair) {
00618 list($ns_uri, $alias) = $pair;
00619 if ($this->namespaces->isImplicit($ns_uri)) {
00620 continue;
00621 }
00622 if ($alias == Auth_OpenID_NULL_NAMESPACE) {
00623 $ns_key = 'openid.ns';
00624 } else {
00625 $ns_key = 'openid.ns.' . $alias;
00626 }
00627 $args[$ns_key] = $ns_uri;
00628 }
00629
00630 foreach ($this->args->items() as $pair) {
00631 list($ns_parts, $value) = $pair;
00632 list($ns_uri, $ns_key) = $ns_parts;
00633 $key = $this->getKey($ns_uri, $ns_key);
00634 $args[$key] = $value;
00635 }
00636
00637 return $args;
00638 }
00639
00640 function toArgs()
00641 {
00642
00643
00644 $post_args = $this->toPostArgs();
00645 $kvargs = array();
00646 foreach ($post_args as $k => $v) {
00647 if (strpos($k, 'openid.') !== 0) {
00648
00649
00650
00651 return null;
00652 } else {
00653 $kvargs[substr($k, 7)] = $v;
00654 }
00655 }
00656
00657 return $kvargs;
00658 }
00659
00660 function toFormMarkup($action_url, $form_tag_attrs = null,
00661 $submit_text = "Continue")
00662 {
00663 $form = "<form accept-charset=\"UTF-8\" ".
00664 "enctype=\"application/x-www-form-urlencoded\"";
00665
00666 if (!$form_tag_attrs) {
00667 $form_tag_attrs = array();
00668 }
00669
00670 $form_tag_attrs['action'] = $action_url;
00671 $form_tag_attrs['method'] = 'post';
00672
00673 unset($form_tag_attrs['enctype']);
00674 unset($form_tag_attrs['accept-charset']);
00675
00676 if ($form_tag_attrs) {
00677 foreach ($form_tag_attrs as $name => $attr) {
00678 $form .= sprintf(" %s=\"%s\"", $name, $attr);
00679 }
00680 }
00681
00682 $form .= ">\n";
00683
00684 foreach ($this->toPostArgs() as $name => $value) {
00685 $form .= sprintf(
00686 "<input type=\"hidden\" name=\"%s\" value=\"%s\" />\n",
00687 $name, $value);
00688 }
00689
00690 $form .= sprintf("<input type=\"submit\" value=\"%s\" />\n",
00691 $submit_text);
00692
00693 $form .= "</form>\n";
00694
00695 return $form;
00696 }
00697
00698 function toURL($base_url)
00699 {
00700
00701
00702 return Auth_OpenID::appendArgs($base_url, $this->toPostArgs());
00703 }
00704
00705 function toKVForm()
00706 {
00707
00708
00709
00710 return Auth_OpenID_KVForm::fromArray($this->toArgs());
00711 }
00712
00713 function toURLEncoded()
00714 {
00715
00716 $args = array();
00717
00718 foreach ($this->toPostArgs() as $k => $v) {
00719 $args[] = array($k, $v);
00720 }
00721
00722 sort($args);
00723 return Auth_OpenID::httpBuildQuery($args);
00724 }
00725
00729 function _fixNS($namespace)
00730 {
00731
00732
00733
00734 if ($namespace == Auth_OpenID_OPENID_NS) {
00735 if ($this->_openid_ns_uri === null) {
00736 return new Auth_OpenID_FailureResponse(null,
00737 'OpenID namespace not set');
00738 } else {
00739 $namespace = $this->_openid_ns_uri;
00740 }
00741 }
00742
00743 if (($namespace != Auth_OpenID_BARE_NS) &&
00744 (!is_string($namespace))) {
00745
00746 $err_msg = sprintf("Namespace must be Auth_OpenID_BARE_NS, ".
00747 "Auth_OpenID_OPENID_NS or a string. got %s",
00748 print_r($namespace, true));
00749 return new Auth_OpenID_FailureResponse(null, $err_msg);
00750 }
00751
00752 if (($namespace != Auth_OpenID_BARE_NS) &&
00753 (strpos($namespace, ':') === false)) {
00754
00755
00756
00757 if ($namespace == 'sreg') {
00758
00759
00760 return Auth_OpenID_SREG_URI;
00761 }
00762 }
00763
00764 return $namespace;
00765 }
00766
00767 function hasKey($namespace, $ns_key)
00768 {
00769 $namespace = $this->_fixNS($namespace);
00770 if (Auth_OpenID::isFailure($namespace)) {
00771
00772 return false;
00773 } else {
00774 return $this->args->contains(array($namespace, $ns_key));
00775 }
00776 }
00777
00778 function getKey($namespace, $ns_key)
00779 {
00780
00781 $namespace = $this->_fixNS($namespace);
00782 if (Auth_OpenID::isFailure($namespace)) {
00783 return $namespace;
00784 }
00785 if ($namespace == Auth_OpenID_BARE_NS) {
00786 return $ns_key;
00787 }
00788
00789 $ns_alias = $this->namespaces->getAlias($namespace);
00790
00791
00792 if ($ns_alias === null) {
00793 return null;
00794 }
00795
00796 if ($ns_alias == Auth_OpenID_NULL_NAMESPACE) {
00797 $tail = $ns_key;
00798 } else {
00799 $tail = sprintf('%s.%s', $ns_alias, $ns_key);
00800 }
00801
00802 return 'openid.' . $tail;
00803 }
00804
00805 function getArg($namespace, $key, $default = null)
00806 {
00807
00808 $namespace = $this->_fixNS($namespace);
00809
00810 if (Auth_OpenID::isFailure($namespace)) {
00811 return $namespace;
00812 } else {
00813 if ((!$this->args->contains(array($namespace, $key))) &&
00814 ($default == Auth_OpenID_NO_DEFAULT)) {
00815 $err_msg = sprintf("Namespace %s missing required field %s",
00816 $namespace, $key);
00817 return new Auth_OpenID_FailureResponse(null, $err_msg);
00818 } else {
00819 return $this->args->get(array($namespace, $key), $default);
00820 }
00821 }
00822 }
00823
00824 function getArgs($namespace)
00825 {
00826
00827
00828 $namespace = $this->_fixNS($namespace);
00829 if (Auth_OpenID::isFailure($namespace)) {
00830 return $namespace;
00831 } else {
00832 $stuff = array();
00833 foreach ($this->args->items() as $pair) {
00834 list($key, $value) = $pair;
00835 list($pair_ns, $ns_key) = $key;
00836 if ($pair_ns == $namespace) {
00837 $stuff[$ns_key] = $value;
00838 }
00839 }
00840
00841 return $stuff;
00842 }
00843 }
00844
00845 function updateArgs($namespace, $updates)
00846 {
00847
00848
00849 $namespace = $this->_fixNS($namespace);
00850
00851 if (Auth_OpenID::isFailure($namespace)) {
00852 return $namespace;
00853 } else {
00854 foreach ($updates as $k => $v) {
00855 $this->setArg($namespace, $k, $v);
00856 }
00857 return true;
00858 }
00859 }
00860
00861 function setArg($namespace, $key, $value)
00862 {
00863
00864 $namespace = $this->_fixNS($namespace);
00865
00866 if (Auth_OpenID::isFailure($namespace)) {
00867 return $namespace;
00868 } else {
00869 $this->args->set(array($namespace, $key), $value);
00870 if ($namespace !== Auth_OpenID_BARE_NS) {
00871 $this->namespaces->add($namespace);
00872 }
00873 return true;
00874 }
00875 }
00876
00877 function delArg($namespace, $key)
00878 {
00879 $namespace = $this->_fixNS($namespace);
00880
00881 if (Auth_OpenID::isFailure($namespace)) {
00882 return $namespace;
00883 } else {
00884 return $this->args->del(array($namespace, $key));
00885 }
00886 }
00887
00888 function getAliasedArg($aliased_key, $default = null)
00889 {
00890 $parts = explode('.', $aliased_key, 2);
00891
00892 if (count($parts) != 2) {
00893 $ns = null;
00894 } else {
00895 list($alias, $key) = $parts;
00896
00897 if ($alias == 'ns') {
00898
00899
00900 return $this->namespaces->getNamespaceURI($key);
00901 } else {
00902 $ns = $this->namespaces->getNamespaceURI($alias);
00903 }
00904 }
00905
00906 if ($ns === null) {
00907 $key = $aliased_key;
00908 $ns = $this->getOpenIDNamespace();
00909 }
00910
00911 return $this->getArg($ns, $key, $default);
00912 }
00913 }
00914
00915 ?>