ILIAS  Release_4_1_x_branch Revision 61804
 All Data Structures Namespaces Files Functions Variables Groups Pages
Message.php
Go to the documentation of this file.
1 <?php
2 
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'; // For Auth_OpenID_FailureResponse
16 
17 // This doesn't REALLY belong here, but where is better?
18 define('Auth_OpenID_IDENTIFIER_SELECT',
19  "http://specs.openid.net/auth/2.0/identifier_select");
20 
21 // URI for Simple Registration extension, the only commonly deployed
22 // OpenID 1.x extension, and so a special case
23 define('Auth_OpenID_SREG_URI', 'http://openid.net/sreg/1.0');
24 
25 // The OpenID 1.X namespace URI
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 // The OpenID 2.0 namespace URI
36 define('Auth_OpenID_OPENID2_NS', 'http://specs.openid.net/auth/2.0');
37 
38 // The namespace consisting of pairs with keys that are prefixed with
39 // "openid." but not in another namespace.
40 define('Auth_OpenID_NULL_NAMESPACE', 'Null namespace');
41 
42 // The null namespace, when it is an allowed OpenID namespace
43 define('Auth_OpenID_OPENID_NS', 'OpenID namespace');
44 
45 // The top-level namespace, excluding all pairs with keys that start
46 // with "openid."
47 define('Auth_OpenID_BARE_NS', 'Bare namespace');
48 
49 // Sentinel for Message implementation to indicate that getArg should
50 // return null instead of returning a default.
51 define('Auth_OpenID_NO_DEFAULT', 'NO DEFAULT ALLOWED');
52 
53 // Limit, in bytes, of identity provider and return_to URLs, including
54 // response payload. See OpenID 1.1 specification, Appendix D.
55 define('Auth_OpenID_OPENID1_URL_LIMIT', 2047);
56 
57 // All OpenID protocol fields. Used to check namespace aliases.
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 // Global namespace / alias registration map. See
67 // Auth_OpenID_registerNamespaceAlias.
70 
78 function Auth_OpenID_registerNamespaceAlias($namespace_uri, $alias)
79 {
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 
105 function Auth_OpenID_removeNamespaceAlias($namespace_uri, $alias)
106 {
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 
125 class Auth_OpenID_Mapping {
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 
146  static function isA($thing)
147  {
148  return (is_object($thing) &&
149  strtolower(get_class($thing)) == 'auth_openid_mapping');
150  }
151 
155  function keys()
156  {
157  return $this->keys;
158  }
159 
163  function values()
164  {
165  return $this->values;
166  }
167 
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 
185  function len()
186  {
187  return count($this->keys);
188  }
189 
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 
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 
225  function _reflow()
226  {
227  // PHP is broken yet again. Sort the arrays to remove the
228  // hole in the numeric indexes that make up the array.
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 
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 
265  function contains($value)
266  {
267  return (array_search($value, $this->keys) !== false);
268  }
269 }
270 
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  }
284  function getAlias($namespace_uri)
285  {
286  return $this->namespace_to_alias->get($namespace_uri);
287  }
289  function getNamespaceURI($alias)
290  {
291  return $this->alias_to_namespace->get($alias);
292  }
294  function iterNamespaceURIs()
295  {
296  // Return an iterator over the namespace URIs
297  return $this->namespace_to_alias->keys();
298  }
300  function iterAliases()
301  {
302  // Return an iterator over the aliases"""
303  return $this->alias_to_namespace->keys();
304  }
306  function iteritems()
307  {
308  return $this->namespace_to_alias->items();
309  }
311  function isImplicit($namespace_uri)
312  {
313  return in_array($namespace_uri, $this->implicit_namespaces);
314  }
316  function addAlias($namespace_uri, $desired_alias, $implicit=false)
317  {
318  // Add an alias from this namespace URI to the desired alias
320 
321  // Check that desired_alias is not an openid protocol field as
322  // per the spec.
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  // Check that desired_alias does not contain a period as per
330  // the spec.
331  if (strpos($desired_alias, '.') !== false) {
332  Auth_OpenID::log('"%s" must not contain a dot', $desired_alias);
333  return null;
334  }
335 
336  // Check that there is not a namespace already defined for the
337  // desired alias
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  // Check that there is not already a (different) alias for
349  // this namespace URI
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  }
371  function add($namespace_uri)
372  {
373  // Add this namespace URI to the mapping, without caring what
374  // alias it ends up with
375 
376  // See if this namespace is already mapped to an alias
377  $alias = $this->namespace_to_alias->get($namespace_uri);
378 
379  if ($alias !== null) {
380  return $alias;
381  }
382 
383  // Fall back to generating a numerical alias
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  // Should NEVER be reached!
395  return null;
396  }
398  function contains($namespace_uri)
399  {
400  return $this->isDefined($namespace_uri);
401  }
403  function isDefined($namespace_uri)
404  {
405  return $this->namespace_to_alias->contains($namespace_uri);
406  }
407 }
408 
415 class Auth_OpenID_Message {
417  function Auth_OpenID_Message($openid_namespace = null)
418  {
419  // Create an empty Message
420  $this->allowed_openid_namespaces = array(
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  }
435  function isOpenID1()
436  {
437  return Auth_OpenID_isOpenID1($this->getOpenIDNamespace());
438  }
440  function isOpenID2()
441  {
442  return $this->getOpenIDNamespace() == Auth_OpenID_OPENID2_NS;
443  }
445  static function fromPostArgs($args)
446  {
447  // Construct a Message containing a set of POST arguments
448  $obj = new Auth_OpenID_Message();
449 
450  // Partition into "openid." args and bare args
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  }
480  static function fromOpenIDArgs($openid_args)
481  {
482  // Takes an array.
483 
484  // Construct a Message from a parsed KVForm message
485  $obj = new Auth_OpenID_Message();
486  if ($obj->_fromOpenIDArgs($openid_args)) {
487  return $obj;
488  } else {
489  return null;
490  }
491  }
492 
496  function _fromOpenIDArgs($openid_args)
497  {
499 
500  // Takes an Auth_OpenID_Mapping instance OR an array.
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  // Resolve namespaces
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  // null namespace
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  // Actually put the pairs into the appropriate namespaces
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  }
564  function _getDefaultNamespace($mystery_alias)
565  {
567  if ($this->isOpenID1()) {
568  return @$Auth_OpenID_registered_aliases[$mystery_alias];
569  }
570  return null;
571  }
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,
582  $implicit);
583  if ($succeeded === false) {
584  return false;
585  }
586 
587  $this->_openid_ns_uri = $openid_ns_uri;
588 
589  return true;
590  }
592  function getOpenIDNamespace()
593  {
594  return $this->_openid_ns_uri;
595  }
597  static function fromKVForm($kvform_string)
598  {
599  // Create a Message from a KVForm string
601  Auth_OpenID_KVForm::toArray($kvform_string));
602  }
604  function copy()
605  {
606  return $this;
607  }
609  function toPostArgs()
610  {
611  // Return all arguments with openid. in front of namespaced
612  // arguments.
613 
614  $args = array();
615 
616  // Add namespace definitions to the output
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  }
640  function toArgs()
641  {
642  // Return all namespaced arguments, failing if any
643  // non-namespaced arguments exist.
644  $post_args = $this->toPostArgs();
645  $kvargs = array();
646  foreach ($post_args as $k => $v) {
647  if (strpos($k, 'openid.') !== 0) {
648  // raise ValueError(
649  // 'This message can only be encoded as a POST, because it '
650  // 'contains arguments that are not prefixed with "openid."')
651  return null;
652  } else {
653  $kvargs[substr($k, 7)] = $v;
654  }
655  }
656 
657  return $kvargs;
658  }
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, $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  }
698  function toURL($base_url)
699  {
700  // Generate a GET URL with the parameters in this message
701  // attached as query parameters.
702  return Auth_OpenID::appendArgs($base_url, $this->toPostArgs());
703  }
705  function toKVForm()
706  {
707  // Generate a KVForm string that contains the parameters in
708  // this message. This will fail if the message contains
709  // arguments outside of the 'openid.' prefix.
710  return Auth_OpenID_KVForm::fromArray($this->toArgs());
711  }
713  function toURLEncoded()
714  {
715  // Generate an x-www-urlencoded string
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 
729  function _fixNS($namespace)
730  {
731  // Convert an input value into the internally used values of
732  // this object
733 
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  //TypeError
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  // fmt = 'OpenID 2.0 namespace identifiers SHOULD be URIs. Got %r'
755  // warnings.warn(fmt % (namespace,), DeprecationWarning)
756 
757  if ($namespace == 'sreg') {
758  // fmt = 'Using %r instead of "sreg" as namespace'
759  // warnings.warn(fmt % (SREG_URI,), DeprecationWarning,)
760  return Auth_OpenID_SREG_URI;
761  }
762  }
763 
764  return $namespace;
765  }
767  function hasKey($namespace, $ns_key)
768  {
769  $namespace = $this->_fixNS($namespace);
771  // XXX log me
772  return false;
773  } else {
774  return $this->args->contains(array($namespace, $ns_key));
775  }
776  }
778  function getKey($namespace, $ns_key)
779  {
780  // Get the key for a particular namespaced argument
781  $namespace = $this->_fixNS($namespace);
783  return $namespace;
784  }
786  return $ns_key;
787  }
788 
789  $ns_alias = $this->namespaces->getAlias($namespace);
790 
791  // No alias is defined, so no key can exist
792  if ($ns_alias === null) {
793  return null;
794  }
795 
796  if ($ns_alias == Auth_OpenID_NULL_NAMESPACE) {
797  $tail = $ns_key;
798  } else {
799  $tail = sprintf('%s.%s', $ns_alias, $ns_key);
800  }
801 
802  return 'openid.' . $tail;
803  }
805  function getArg($namespace, $key, $default = null)
806  {
807  // Get a value for a namespaced key.
808  $namespace = $this->_fixNS($namespace);
809 
811  return $namespace;
812  } else {
813  if ((!$this->args->contains(array($namespace, $key))) &&
814  ($default == Auth_OpenID_NO_DEFAULT)) {
815  $err_msg = sprintf("Namespace %s missing required field %s",
816  $namespace, $key);
817  return new Auth_OpenID_FailureResponse(null, $err_msg);
818  } else {
819  return $this->args->get(array($namespace, $key), $default);
820  }
821  }
822  }
824  function getArgs($namespace)
825  {
826  // Get the arguments that are defined for this namespace URI
827 
828  $namespace = $this->_fixNS($namespace);
830  return $namespace;
831  } else {
832  $stuff = array();
833  foreach ($this->args->items() as $pair) {
834  list($key, $value) = $pair;
835  list($pair_ns, $ns_key) = $key;
836  if ($pair_ns == $namespace) {
837  $stuff[$ns_key] = $value;
838  }
839  }
840 
841  return $stuff;
842  }
843  }
845  function updateArgs($namespace, $updates)
846  {
847  // Set multiple key/value pairs in one call
848 
849  $namespace = $this->_fixNS($namespace);
850 
852  return $namespace;
853  } else {
854  foreach ($updates as $k => $v) {
855  $this->setArg($namespace, $k, $v);
856  }
857  return true;
858  }
859  }
861  function setArg($namespace, $key, $value)
862  {
863  // Set a single argument in this namespace
864  $namespace = $this->_fixNS($namespace);
865 
867  return $namespace;
868  } else {
869  $this->args->set(array($namespace, $key), $value);
871  $this->namespaces->add($namespace);
872  }
873  return true;
874  }
875  }
877  function delArg($namespace, $key)
878  {
879  $namespace = $this->_fixNS($namespace);
880 
882  return $namespace;
883  } else {
884  return $this->args->del(array($namespace, $key));
885  }
886  }
888  function getAliasedArg($aliased_key, $default = null)
889  {
890  if ($aliased_key == 'ns') {
891  // Return the namespace URI for the OpenID namespace
892  return $this->getOpenIDNamespace();
893  }
894 
895  $parts = explode('.', $aliased_key, 2);
896 
897  if (count($parts) != 2) {
898  $ns = null;
899  } else {
900  list($alias, $key) = $parts;
901 
902  if ($alias == 'ns') {
903  // Return the namespace URI for a namespace alias
904  // parameter.
905  return $this->namespaces->getNamespaceURI($key);
906  } else {
907  $ns = $this->namespaces->getNamespaceURI($alias);
908  }
909  }
910 
911  if ($ns === null) {
912  $key = $aliased_key;
913  $ns = $this->getOpenIDNamespace();
914  }
915 
916  return $this->getArg($ns, $key, $default);
917  }
918 }
919 
920