1: <?php
2:
3: 4: 5: 6: 7:
8:
9: 10: 11:
12: require_once 'Auth/OpenID.php';
13: require_once 'Auth/OpenID/KVForm.php';
14: require_once 'Auth/Yadis/XML.php';
15: require_once 'Auth/OpenID/Consumer.php';
16:
17:
18: define('Auth_OpenID_IDENTIFIER_SELECT',
19: "http://specs.openid.net/auth/2.0/identifier_select");
20:
21:
22:
23: define('Auth_OpenID_SREG_URI', 'http://openid.net/sreg/1.0');
24:
25:
26: define('Auth_OpenID_OPENID1_NS', 'http://openid.net/signon/1.0');
27: define('Auth_OpenID_THE_OTHER_OPENID1_NS', 'http://openid.net/signon/1.1');
28:
29: function Auth_OpenID_isOpenID1($ns)
30: {
31: return ($ns == Auth_OpenID_THE_OTHER_OPENID1_NS) ||
32: ($ns == Auth_OpenID_OPENID1_NS);
33: }
34:
35:
36: define('Auth_OpenID_OPENID2_NS', 'http://specs.openid.net/auth/2.0');
37:
38:
39:
40: define('Auth_OpenID_NULL_NAMESPACE', 'Null namespace');
41:
42:
43: define('Auth_OpenID_OPENID_NS', 'OpenID namespace');
44:
45:
46:
47: define('Auth_OpenID_BARE_NS', 'Bare namespace');
48:
49:
50:
51: define('Auth_OpenID_NO_DEFAULT', 'NO DEFAULT ALLOWED');
52:
53:
54:
55: define('Auth_OpenID_OPENID1_URL_LIMIT', 2047);
56:
57:
58: global $Auth_OpenID_OPENID_PROTOCOL_FIELDS;
59: $Auth_OpenID_OPENID_PROTOCOL_FIELDS = array(
60: 'ns', 'mode', 'error', 'return_to', 'contact', 'reference',
61: 'signed', 'assoc_type', 'session_type', 'dh_modulus', 'dh_gen',
62: 'dh_consumer_public', 'claimed_id', 'identity', 'realm',
63: 'invalidate_handle', 'op_endpoint', 'response_nonce', 'sig',
64: 'assoc_handle', 'trust_root', 'openid');
65:
66:
67:
68: global $Auth_OpenID_registered_aliases;
69: $Auth_OpenID_registered_aliases = array();
70:
71: 72: 73: 74: 75: 76: 77:
78: function Auth_OpenID_registerNamespaceAlias($namespace_uri, $alias)
79: {
80: global $Auth_OpenID_registered_aliases;
81:
82: if (Auth_OpenID::arrayGet($Auth_OpenID_registered_aliases,
83: $alias) == $namespace_uri) {
84: return true;
85: }
86:
87: if (in_array($namespace_uri,
88: array_values($Auth_OpenID_registered_aliases))) {
89: return false;
90: }
91:
92: if (in_array($alias, array_keys($Auth_OpenID_registered_aliases))) {
93: return false;
94: }
95:
96: $Auth_OpenID_registered_aliases[$alias] = $namespace_uri;
97: return true;
98: }
99:
100: 101: 102: 103: 104:
105: function Auth_OpenID_removeNamespaceAlias($namespace_uri, $alias)
106: {
107: global $Auth_OpenID_registered_aliases;
108:
109: if (Auth_OpenID::arrayGet($Auth_OpenID_registered_aliases,
110: $alias) === $namespace_uri) {
111: unset($Auth_OpenID_registered_aliases[$alias]);
112: return true;
113: }
114:
115: return false;
116: }
117:
118: 119: 120: 121: 122: 123: 124:
125: class Auth_OpenID_Mapping {
126: 127: 128: 129:
130: function Auth_OpenID_Mapping($classic_array = null)
131: {
132: $this->keys = array();
133: $this->values = array();
134:
135: if (is_array($classic_array)) {
136: foreach ($classic_array as $key => $value) {
137: $this->set($key, $value);
138: }
139: }
140: }
141:
142: 143: 144: 145:
146: static function isA($thing)
147: {
148: return (is_object($thing) &&
149: strtolower(get_class($thing)) == 'auth_openid_mapping');
150: }
151:
152: 153: 154:
155: function keys()
156: {
157: return $this->keys;
158: }
159:
160: 161: 162:
163: function values()
164: {
165: return $this->values;
166: }
167:
168: 169: 170:
171: function items()
172: {
173: $temp = array();
174:
175: for ($i = 0; $i < count($this->keys); $i++) {
176: $temp[] = array($this->keys[$i],
177: $this->values[$i]);
178: }
179: return $temp;
180: }
181:
182: 183: 184:
185: function len()
186: {
187: return count($this->keys);
188: }
189:
190: 191: 192: 193:
194: function set($key, $value)
195: {
196: $index = array_search($key, $this->keys);
197:
198: if ($index !== false) {
199: $this->values[$index] = $value;
200: } else {
201: $this->keys[] = $key;
202: $this->values[] = $value;
203: }
204: }
205:
206: 207: 208: 209: 210:
211: function get($key, $default = null)
212: {
213: $index = array_search($key, $this->keys);
214:
215: if ($index !== false) {
216: return $this->values[$index];
217: } else {
218: return $default;
219: }
220: }
221:
222: 223: 224:
225: function _reflow()
226: {
227:
228:
229: $old_keys = $this->keys;
230: $old_values = $this->values;
231:
232: $this->keys = array();
233: $this->values = array();
234:
235: foreach ($old_keys as $k) {
236: $this->keys[] = $k;
237: }
238:
239: foreach ($old_values as $v) {
240: $this->values[] = $v;
241: }
242: }
243:
244: 245: 246: 247:
248: function del($key)
249: {
250: $index = array_search($key, $this->keys);
251:
252: if ($index !== false) {
253: unset($this->keys[$index]);
254: unset($this->values[$index]);
255: $this->_reflow();
256: return true;
257: }
258: return false;
259: }
260:
261: 262: 263: 264:
265: function contains($value)
266: {
267: return (array_search($value, $this->keys) !== false);
268: }
269: }
270:
271: 272: 273: 274: 275:
276: class Auth_OpenID_NamespaceMap {
277: function Auth_OpenID_NamespaceMap()
278: {
279: $this->alias_to_namespace = new Auth_OpenID_Mapping();
280: $this->namespace_to_alias = new Auth_OpenID_Mapping();
281: $this->implicit_namespaces = array();
282: }
283:
284: function getAlias($namespace_uri)
285: {
286: return $this->namespace_to_alias->get($namespace_uri);
287: }
288:
289: function getNamespaceURI($alias)
290: {
291: return $this->alias_to_namespace->get($alias);
292: }
293:
294: function iterNamespaceURIs()
295: {
296:
297: return $this->namespace_to_alias->keys();
298: }
299:
300: function iterAliases()
301: {
302:
303: return $this->alias_to_namespace->keys();
304: }
305:
306: function iteritems()
307: {
308: return $this->namespace_to_alias->items();
309: }
310:
311: function isImplicit($namespace_uri)
312: {
313: return in_array($namespace_uri, $this->implicit_namespaces);
314: }
315:
316: function addAlias($namespace_uri, $desired_alias, $implicit=false)
317: {
318:
319: global $Auth_OpenID_OPENID_PROTOCOL_FIELDS;
320:
321:
322:
323: if (in_array($desired_alias, $Auth_OpenID_OPENID_PROTOCOL_FIELDS)) {
324: Auth_OpenID::log("\"%s\" is not an allowed namespace alias",
325: $desired_alias);
326: return null;
327: }
328:
329:
330:
331: if (strpos($desired_alias, '.') !== false) {
332: Auth_OpenID::log('"%s" must not contain a dot', $desired_alias);
333: return null;
334: }
335:
336:
337:
338: $current_namespace_uri =
339: $this->alias_to_namespace->get($desired_alias);
340:
341: if (($current_namespace_uri !== null) &&
342: ($current_namespace_uri != $namespace_uri)) {
343: Auth_OpenID::log('Cannot map "%s" because previous mapping exists',
344: $namespace_uri);
345: return null;
346: }
347:
348:
349:
350: $alias = $this->namespace_to_alias->get($namespace_uri);
351:
352: if (($alias !== null) && ($alias != $desired_alias)) {
353: Auth_OpenID::log('Cannot map %s to alias %s. ' .
354: 'It is already mapped to alias %s',
355: $namespace_uri, $desired_alias, $alias);
356: return null;
357: }
358:
359: assert((Auth_OpenID_NULL_NAMESPACE === $desired_alias) ||
360: is_string($desired_alias));
361:
362: $this->alias_to_namespace->set($desired_alias, $namespace_uri);
363: $this->namespace_to_alias->set($namespace_uri, $desired_alias);
364: if ($implicit) {
365: array_push($this->implicit_namespaces, $namespace_uri);
366: }
367:
368: return $desired_alias;
369: }
370:
371: function add($namespace_uri)
372: {
373:
374:
375:
376:
377: $alias = $this->namespace_to_alias->get($namespace_uri);
378:
379: if ($alias !== null) {
380: return $alias;
381: }
382:
383:
384: $i = 0;
385: while (1) {
386: $alias = 'ext' . strval($i);
387: if ($this->addAlias($namespace_uri, $alias) === null) {
388: $i += 1;
389: } else {
390: return $alias;
391: }
392: }
393:
394:
395: return null;
396: }
397:
398: function contains($namespace_uri)
399: {
400: return $this->isDefined($namespace_uri);
401: }
402:
403: function isDefined($namespace_uri)
404: {
405: return $this->namespace_to_alias->contains($namespace_uri);
406: }
407: }
408:
409: 410: 411: 412: 413: 414:
415: class Auth_OpenID_Message {
416:
417: function Auth_OpenID_Message($openid_namespace = null)
418: {
419:
420: $this->allowed_openid_namespaces = array(
421: Auth_OpenID_OPENID1_NS,
422: Auth_OpenID_THE_OTHER_OPENID1_NS,
423: Auth_OpenID_OPENID2_NS);
424:
425: $this->args = new Auth_OpenID_Mapping();
426: $this->namespaces = new Auth_OpenID_NamespaceMap();
427: if ($openid_namespace === null) {
428: $this->_openid_ns_uri = null;
429: } else {
430: $implicit = Auth_OpenID_isOpenID1($openid_namespace);
431: $this->setOpenIDNamespace($openid_namespace, $implicit);
432: }
433: }
434:
435: function isOpenID1()
436: {
437: return Auth_OpenID_isOpenID1($this->getOpenIDNamespace());
438: }
439:
440: function isOpenID2()
441: {
442: return $this->getOpenIDNamespace() == Auth_OpenID_OPENID2_NS;
443: }
444:
445: static function fromPostArgs($args)
446: {
447:
448: $obj = new Auth_OpenID_Message();
449:
450:
451: $openid_args = array();
452: foreach ($args as $key => $value) {
453:
454: if (is_array($value)) {
455: return null;
456: }
457:
458: $parts = explode('.', $key, 2);
459:
460: if (count($parts) == 2) {
461: list($prefix, $rest) = $parts;
462: } else {
463: $prefix = null;
464: }
465:
466: if ($prefix != 'openid') {
467: $obj->args->set(array(Auth_OpenID_BARE_NS, $key), $value);
468: } else {
469: $openid_args[$rest] = $value;
470: }
471: }
472:
473: if ($obj->_fromOpenIDArgs($openid_args)) {
474: return $obj;
475: } else {
476: return null;
477: }
478: }
479:
480: static function fromOpenIDArgs($openid_args)
481: {
482:
483:
484:
485: $obj = new Auth_OpenID_Message();
486: if ($obj->_fromOpenIDArgs($openid_args)) {
487: return $obj;
488: } else {
489: return null;
490: }
491: }
492:
493: 494: 495:
496: function _fromOpenIDArgs($openid_args)
497: {
498: global $Auth_OpenID_registered_aliases;
499:
500:
501:
502: if (!Auth_OpenID_Mapping::isA($openid_args)) {
503: $openid_args = new Auth_OpenID_Mapping($openid_args);
504: }
505:
506: $ns_args = array();
507:
508:
509: foreach ($openid_args->items() as $pair) {
510: list($rest, $value) = $pair;
511:
512: $parts = explode('.', $rest, 2);
513:
514: if (count($parts) == 2) {
515: list($ns_alias, $ns_key) = $parts;
516: } else {
517: $ns_alias = Auth_OpenID_NULL_NAMESPACE;
518: $ns_key = $rest;
519: }
520:
521: if ($ns_alias == 'ns') {
522: if ($this->namespaces->addAlias($value, $ns_key) === null) {
523: return false;
524: }
525: } else if (($ns_alias == Auth_OpenID_NULL_NAMESPACE) &&
526: ($ns_key == 'ns')) {
527:
528: if ($this->setOpenIDNamespace($value, false) === false) {
529: return false;
530: }
531: } else {
532: $ns_args[] = array($ns_alias, $ns_key, $value);
533: }
534: }
535:
536: if (!$this->getOpenIDNamespace()) {
537: if ($this->setOpenIDNamespace(Auth_OpenID_OPENID1_NS, true) ===
538: false) {
539: return false;
540: }
541: }
542:
543:
544: foreach ($ns_args as $triple) {
545: list($ns_alias, $ns_key, $value) = $triple;
546: $ns_uri = $this->namespaces->getNamespaceURI($ns_alias);
547: if ($ns_uri === null) {
548: $ns_uri = $this->_getDefaultNamespace($ns_alias);
549: if ($ns_uri === null) {
550:
551: $ns_uri = Auth_OpenID_OPENID_NS;
552: $ns_key = sprintf('%s.%s', $ns_alias, $ns_key);
553: } else {
554: $this->namespaces->addAlias($ns_uri, $ns_alias, true);
555: }
556: }
557:
558: $this->setArg($ns_uri, $ns_key, $value);
559: }
560:
561: return true;
562: }
563:
564: function _getDefaultNamespace($mystery_alias)
565: {
566: global $Auth_OpenID_registered_aliases;
567: if ($this->isOpenID1()) {
568: return @$Auth_OpenID_registered_aliases[$mystery_alias];
569: }
570: return null;
571: }
572:
573: function setOpenIDNamespace($openid_ns_uri, $implicit)
574: {
575: if (!in_array($openid_ns_uri, $this->allowed_openid_namespaces)) {
576: Auth_OpenID::log('Invalid null namespace: "%s"', $openid_ns_uri);
577: return false;
578: }
579:
580: $succeeded = $this->namespaces->addAlias($openid_ns_uri,
581: Auth_OpenID_NULL_NAMESPACE,
582: $implicit);
583: if ($succeeded === false) {
584: return false;
585: }
586:
587: $this->_openid_ns_uri = $openid_ns_uri;
588:
589: return true;
590: }
591:
592: function getOpenIDNamespace()
593: {
594: return $this->_openid_ns_uri;
595: }
596:
597: static function fromKVForm($kvform_string)
598: {
599:
600: return Auth_OpenID_Message::fromOpenIDArgs(
601: Auth_OpenID_KVForm::toArray($kvform_string));
602: }
603:
604: function copy()
605: {
606: return $this;
607: }
608:
609: function toPostArgs()
610: {
611:
612:
613:
614: $args = array();
615:
616:
617: foreach ($this->namespaces->iteritems() as $pair) {
618: list($ns_uri, $alias) = $pair;
619: if ($this->namespaces->isImplicit($ns_uri)) {
620: continue;
621: }
622: if ($alias == Auth_OpenID_NULL_NAMESPACE) {
623: $ns_key = 'openid.ns';
624: } else {
625: $ns_key = 'openid.ns.' . $alias;
626: }
627: $args[$ns_key] = $ns_uri;
628: }
629:
630: foreach ($this->args->items() as $pair) {
631: list($ns_parts, $value) = $pair;
632: list($ns_uri, $ns_key) = $ns_parts;
633: $key = $this->getKey($ns_uri, $ns_key);
634: $args[$key] = $value;
635: }
636:
637: return $args;
638: }
639:
640: function toArgs()
641: {
642:
643:
644: $post_args = $this->toPostArgs();
645: $kvargs = array();
646: foreach ($post_args as $k => $v) {
647: if (strpos($k, 'openid.') !== 0) {
648:
649:
650:
651: return null;
652: } else {
653: $kvargs[substr($k, 7)] = $v;
654: }
655: }
656:
657: return $kvargs;
658: }
659:
660: function toFormMarkup($action_url, $form_tag_attrs = null,
661: $submit_text = "Continue")
662: {
663: $form = "<form accept-charset=\"UTF-8\" ".
664: "enctype=\"application/x-www-form-urlencoded\"";
665:
666: if (!$form_tag_attrs) {
667: $form_tag_attrs = array();
668: }
669:
670: $form_tag_attrs['action'] = $action_url;
671: $form_tag_attrs['method'] = 'post';
672:
673: unset($form_tag_attrs['enctype']);
674: unset($form_tag_attrs['accept-charset']);
675:
676: if ($form_tag_attrs) {
677: foreach ($form_tag_attrs as $name => $attr) {
678: $form .= sprintf(" %s=\"%s\"", $name, $attr);
679: }
680: }
681:
682: $form .= ">\n";
683:
684: foreach ($this->toPostArgs() as $name => $value) {
685: $form .= sprintf(
686: "<input type=\"hidden\" name=\"%s\" value=\"%s\" />\n",
687: $name, urldecode($value));
688: }
689:
690: $form .= sprintf("<input type=\"submit\" value=\"%s\" />\n",
691: $submit_text);
692:
693: $form .= "</form>\n";
694:
695: return $form;
696: }
697:
698: function toURL($base_url)
699: {
700:
701:
702: return Auth_OpenID::appendArgs($base_url, $this->toPostArgs());
703: }
704:
705: function toKVForm()
706: {
707:
708:
709:
710: return Auth_OpenID_KVForm::fromArray($this->toArgs());
711: }
712:
713: function toURLEncoded()
714: {
715:
716: $args = array();
717:
718: foreach ($this->toPostArgs() as $k => $v) {
719: $args[] = array($k, $v);
720: }
721:
722: sort($args);
723: return Auth_OpenID::httpBuildQuery($args);
724: }
725:
726: 727: 728:
729: function _fixNS($namespace)
730: {
731:
732:
733:
734: if ($namespace == Auth_OpenID_OPENID_NS) {
735: if ($this->_openid_ns_uri === null) {
736: return new Auth_OpenID_FailureResponse(null,
737: 'OpenID namespace not set');
738: } else {
739: $namespace = $this->_openid_ns_uri;
740: }
741: }
742:
743: if (($namespace != Auth_OpenID_BARE_NS) &&
744: (!is_string($namespace))) {
745:
746: $err_msg = sprintf("Namespace must be Auth_OpenID_BARE_NS, ".
747: "Auth_OpenID_OPENID_NS or a string. got %s",
748: print_r($namespace, true));
749: return new Auth_OpenID_FailureResponse(null, $err_msg);
750: }
751:
752: if (($namespace != Auth_OpenID_BARE_NS) &&
753: (strpos($namespace, ':') === false)) {
754:
755:
756:
757: if ($namespace == 'sreg') {
758:
759:
760: return Auth_OpenID_SREG_URI;
761: }
762: }
763:
764: return $namespace;
765: }
766:
767: function hasKey($namespace, $ns_key)
768: {
769: $namespace = $this->_fixNS($namespace);
770: if (Auth_OpenID::isFailure($namespace)) {
771: return false;
772: } else {
773: return $this->args->contains(array($namespace, $ns_key));
774: }
775: }
776:
777: function getKey($namespace, $ns_key)
778: {
779:
780: $namespace = $this->_fixNS($namespace);
781: if (Auth_OpenID::isFailure($namespace)) {
782: return $namespace;
783: }
784: if ($namespace == Auth_OpenID_BARE_NS) {
785: return $ns_key;
786: }
787:
788: $ns_alias = $this->namespaces->getAlias($namespace);
789:
790:
791: if ($ns_alias === null) {
792: return null;
793: }
794:
795: if ($ns_alias == Auth_OpenID_NULL_NAMESPACE) {
796: $tail = $ns_key;
797: } else {
798: $tail = sprintf('%s.%s', $ns_alias, $ns_key);
799: }
800:
801: return 'openid.' . $tail;
802: }
803:
804: function getArg($namespace, $key, $default = null)
805: {
806:
807: $namespace = $this->_fixNS($namespace);
808:
809: if (Auth_OpenID::isFailure($namespace)) {
810: return $namespace;
811: } else {
812: if ((!$this->args->contains(array($namespace, $key))) &&
813: ($default == Auth_OpenID_NO_DEFAULT)) {
814: $err_msg = sprintf("Namespace %s missing required field %s",
815: $namespace, $key);
816: return new Auth_OpenID_FailureResponse(null, $err_msg);
817: } else {
818: return $this->args->get(array($namespace, $key), $default);
819: }
820: }
821: }
822:
823: function getArgs($namespace)
824: {
825:
826:
827: $namespace = $this->_fixNS($namespace);
828: if (Auth_OpenID::isFailure($namespace)) {
829: return $namespace;
830: } else {
831: $stuff = array();
832: foreach ($this->args->items() as $pair) {
833: list($key, $value) = $pair;
834: list($pair_ns, $ns_key) = $key;
835: if ($pair_ns == $namespace) {
836: $stuff[$ns_key] = $value;
837: }
838: }
839:
840: return $stuff;
841: }
842: }
843:
844: function updateArgs($namespace, $updates)
845: {
846:
847:
848: $namespace = $this->_fixNS($namespace);
849:
850: if (Auth_OpenID::isFailure($namespace)) {
851: return $namespace;
852: } else {
853: foreach ($updates as $k => $v) {
854: $this->setArg($namespace, $k, $v);
855: }
856: return true;
857: }
858: }
859:
860: function setArg($namespace, $key, $value)
861: {
862:
863: $namespace = $this->_fixNS($namespace);
864:
865: if (Auth_OpenID::isFailure($namespace)) {
866: return $namespace;
867: } else {
868: $this->args->set(array($namespace, $key), $value);
869: if ($namespace !== Auth_OpenID_BARE_NS) {
870: $this->namespaces->add($namespace);
871: }
872: return true;
873: }
874: }
875:
876: function delArg($namespace, $key)
877: {
878: $namespace = $this->_fixNS($namespace);
879:
880: if (Auth_OpenID::isFailure($namespace)) {
881: return $namespace;
882: } else {
883: return $this->args->del(array($namespace, $key));
884: }
885: }
886:
887: function getAliasedArg($aliased_key, $default = null)
888: {
889: if ($aliased_key == 'ns') {
890:
891: return $this->getOpenIDNamespace();
892: }
893:
894: $parts = explode('.', $aliased_key, 2);
895:
896: if (count($parts) != 2) {
897: $ns = null;
898: } else {
899: list($alias, $key) = $parts;
900:
901: if ($alias == 'ns') {
902:
903:
904: return $this->namespaces->getNamespaceURI($key);
905: } else {
906: $ns = $this->namespaces->getNamespaceURI($alias);
907: }
908: }
909:
910: if ($ns === null) {
911: $key = $aliased_key;
912: $ns = $this->getOpenIDNamespace();
913: }
914:
915: return $this->getArg($ns, $key, $default);
916: }
917: }
918:
919:
920: