ILIAS  eassessment Revision 61809
 All Data Structures Namespaces Files Functions Variables Groups Pages
AX.php
Go to the documentation of this file.
1 <?php
2 
13 require_once "Auth/OpenID/Extension.php";
14 require_once "Auth/OpenID/Message.php";
15 require_once "Auth/OpenID/TrustRoot.php";
16 
17 define('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.
22 define('Auth_OpenID_AX_UNLIMITED_VALUES', 'unlimited');
23 
24 // Minimum supported alias length in characters. Here for
25 // completeness.
26 define('Auth_OpenID_AX_MINIMUM_SUPPORTED_ALIAS_LENGTH', 32);
27 
33 class Auth_OpenID_AX {
41  static function isError($thing)
42  {
43  return is_a($thing, 'Auth_OpenID_AX_Error');
44  }
45 }
46 
51 function Auth_OpenID_AX_checkAlias($alias)
52 {
53  if (strpos($alias, ',') !== false) {
54  return new Auth_OpenID_AX_Error(sprintf(
55  "Alias %s must not contain comma", $alias));
56  }
57  if (strpos($alias, '.') !== false) {
58  return new Auth_OpenID_AX_Error(sprintf(
59  "Alias %s must not contain period", $alias));
60  }
61 
62  return true;
63 }
64 
72  function Auth_OpenID_AX_Error($message=null)
73  {
74  $this->message = $message;
75  }
76 }
77 
89  var $ns_alias = 'ax';
90 
95  var $mode = null;
96 
98 
106  function _checkMode($ax_args)
107  {
108  $mode = Auth_OpenID::arrayGet($ax_args, 'mode');
109  if ($mode != $this->mode) {
110  return new Auth_OpenID_AX_Error(
111  sprintf(
112  "Expected mode '%s'; got '%s'",
113  $this->mode, $mode));
114  }
115 
116  return true;
117  }
118 
126  function _newArgs()
127  {
128  return array('mode' => $this->mode);
129  }
130 }
131 
154  function Auth_OpenID_AX_AttrInfo($type_uri, $count, $required,
155  $alias)
156  {
162  $this->required = $required;
163 
168  $this->count = $count;
169 
177  $this->type_uri = $type_uri;
178 
187  $this->alias = $alias;
188  }
189 
194  static function make($type_uri, $count=1, $required=false,
195  $alias=null)
196  {
197  if ($alias !== null) {
198  $result = Auth_OpenID_AX_checkAlias($alias);
199 
200  if (Auth_OpenID_AX::isError($result)) {
201  return $result;
202  }
203  }
204 
205  return new Auth_OpenID_AX_AttrInfo($type_uri, $count, $required,
206  $alias);
207  }
208 
216  function wantsUnlimitedValues()
217  {
218  return $this->count === Auth_OpenID_AX_UNLIMITED_VALUES;
219  }
220 }
221 
238 function Auth_OpenID_AX_toTypeURIs($namespace_map, $alias_list_s)
239 {
240  $uris = array();
241 
242  if ($alias_list_s) {
243  foreach (explode(',', $alias_list_s) as $alias) {
244  $type_uri = $namespace_map->getNamespaceURI($alias);
245  if ($type_uri === null) {
246  // raise KeyError(
247  // 'No type is defined for attribute name %r' % (alias,))
248  return new Auth_OpenID_AX_Error(
249  sprintf('No type is defined for attribute name %s',
250  $alias)
251  );
252  } else {
253  $uris[] = $type_uri;
254  }
255  }
256  }
257 
258  return $uris;
259 }
260 
270  var $mode = 'fetch_request';
272  function Auth_OpenID_AX_FetchRequest($update_url=null)
273  {
278  $this->requested_attributes = array();
279 
285  $this->update_url = $update_url;
286  }
287 
295  function add($attribute)
296  {
297  if ($this->contains($attribute->type_uri)) {
298  return new Auth_OpenID_AX_Error(
299  sprintf("The attribute %s has already been requested",
300  $attribute->type_uri));
301  }
302 
303  $this->requested_attributes[$attribute->type_uri] = $attribute;
304 
305  return true;
306  }
307 
313  function getExtensionArgs()
314  {
316 
317  $required = array();
318  $if_available = array();
319 
320  $ax_args = $this->_newArgs();
321 
322  foreach ($this->requested_attributes as $type_uri => $attribute) {
323  if ($attribute->alias === null) {
324  $alias = $aliases->add($type_uri);
325  } else {
326  $alias = $aliases->addAlias($type_uri, $attribute->alias);
327 
328  if ($alias === null) {
329  return new Auth_OpenID_AX_Error(
330  sprintf("Could not add alias %s for URI %s",
331  $attribute->alias, $type_uri
332  ));
333  }
334  }
335 
336  if ($attribute->required) {
337  $required[] = $alias;
338  } else {
339  $if_available[] = $alias;
340  }
341 
342  if ($attribute->count != 1) {
343  $ax_args['count.' . $alias] = strval($attribute->count);
344  }
345 
346  $ax_args['type.' . $alias] = $type_uri;
347  }
348 
349  if ($required) {
350  $ax_args['required'] = implode(',', $required);
351  }
352 
353  if ($if_available) {
354  $ax_args['if_available'] = implode(',', $if_available);
355  }
356 
357  return $ax_args;
358  }
359 
367  function getRequiredAttrs()
368  {
369  $required = array();
370  foreach ($this->requested_attributes as $type_uri => $attribute) {
371  if ($attribute->required) {
372  $required[] = $type_uri;
373  }
374  }
375 
376  return $required;
377  }
378 
389  static function fromOpenIDRequest($request)
390  {
391  $m = $request->message;
392  $obj = new Auth_OpenID_AX_FetchRequest();
393  $ax_args = $m->getArgs($obj->ns_uri);
394 
395  $result = $obj->parseExtensionArgs($ax_args);
396 
397  if (Auth_OpenID_AX::isError($result)) {
398  return $result;
399  }
400 
401  if ($obj->update_url) {
402  // Update URL must match the openid.realm of the
403  // underlying OpenID 2 message.
404  $realm = $m->getArg(Auth_OpenID_OPENID_NS, 'realm',
405  $m->getArg(
407  'return_to'));
408 
409  if (!$realm) {
410  $obj = new Auth_OpenID_AX_Error(
411  sprintf("Cannot validate update_url %s " .
412  "against absent realm", $obj->update_url));
413  } else if (!Auth_OpenID_TrustRoot::match($realm,
414  $obj->update_url)) {
415  $obj = new Auth_OpenID_AX_Error(
416  sprintf("Update URL %s failed validation against realm %s",
417  $obj->update_url, $realm));
418  }
419  }
420 
421  return $obj;
422  }
423 
432  function parseExtensionArgs($ax_args)
433  {
434  $result = $this->_checkMode($ax_args);
435  if (Auth_OpenID_AX::isError($result)) {
436  return $result;
437  }
438 
440 
441  foreach ($ax_args as $key => $value) {
442  if (strpos($key, 'type.') === 0) {
443  $alias = substr($key, 5);
444  $type_uri = $value;
445 
446  $alias = $aliases->addAlias($type_uri, $alias);
447 
448  if ($alias === null) {
449  return new Auth_OpenID_AX_Error(
450  sprintf("Could not add alias %s for URI %s",
451  $alias, $type_uri)
452  );
453  }
454 
455  $count_s = Auth_OpenID::arrayGet($ax_args, 'count.' . $alias);
456  if ($count_s) {
457  $count = Auth_OpenID::intval($count_s);
458  if (($count === false) &&
459  ($count_s === Auth_OpenID_AX_UNLIMITED_VALUES)) {
460  $count = $count_s;
461  }
462  } else {
463  $count = 1;
464  }
465 
466  if ($count === false) {
467  return new Auth_OpenID_AX_Error(
468  sprintf("Integer value expected for %s, got %s",
469  'count.' . $alias, $count_s));
470  }
471 
472  $attrinfo = Auth_OpenID_AX_AttrInfo::make($type_uri, $count,
473  false, $alias);
474 
475  if (Auth_OpenID_AX::isError($attrinfo)) {
476  return $attrinfo;
477  }
478 
479  $this->add($attrinfo);
480  }
481  }
482 
484  Auth_OpenID::arrayGet($ax_args, 'required'));
485 
486  foreach ($required as $type_uri) {
487  $attrib = $this->requested_attributes[$type_uri];
488  $attrib->required = true;
489  }
490 
491  $if_available = Auth_OpenID_AX_toTypeURIs($aliases,
492  Auth_OpenID::arrayGet($ax_args, 'if_available'));
493 
494  $all_type_uris = array_merge($required, $if_available);
495 
496  foreach ($aliases->iterNamespaceURIs() as $type_uri) {
497  if (!in_array($type_uri, $all_type_uris)) {
498  return new Auth_OpenID_AX_Error(
499  sprintf('Type URI %s was in the request but not ' .
500  'present in "required" or "if_available"',
501  $type_uri));
502 
503  }
504  }
505 
506  $this->update_url = Auth_OpenID::arrayGet($ax_args, 'update_url');
507 
508  return true;
509  }
510 
515  function iterAttrs()
516  {
517  return array_values($this->requested_attributes);
518  }
520  function iterTypes()
521  {
522  return array_keys($this->requested_attributes);
523  }
524 
528  function contains($type_uri)
529  {
530  return in_array($type_uri, $this->iterTypes());
531  }
532 }
533 
544  {
545  $this->data = array();
546  }
547 
559  function addValue($type_uri, $value)
560  {
561  if (!array_key_exists($type_uri, $this->data)) {
562  $this->data[$type_uri] = array();
563  }
564 
565  $values =& $this->data[$type_uri];
566  $values[] = $value;
567  }
568 
576  function setValues($type_uri, &$values)
577  {
578  $this->data[$type_uri] =& $values;
579  }
580 
590  function _getExtensionKVArgs($aliases)
591  {
592  if ($aliases === null) {
594  }
595 
596  $ax_args = array();
597 
598  foreach ($this->data as $type_uri => $values) {
599  $alias = $aliases->add($type_uri);
600 
601  $ax_args['type.' . $alias] = $type_uri;
602  $ax_args['count.' . $alias] = strval(count($values));
603 
604  foreach ($values as $i => $value) {
605  $key = sprintf('value.%s.%d', $alias, $i + 1);
606  $ax_args[$key] = $value;
607  }
608  }
609 
610  return $ax_args;
611  }
612 
621  function parseExtensionArgs($ax_args)
622  {
623  $result = $this->_checkMode($ax_args);
624  if (Auth_OpenID_AX::isError($result)) {
625  return $result;
626  }
627 
629 
630  foreach ($ax_args as $key => $value) {
631  if (strpos($key, 'type.') === 0) {
632  $type_uri = $value;
633  $alias = substr($key, 5);
634 
635  $result = Auth_OpenID_AX_checkAlias($alias);
636 
637  if (Auth_OpenID_AX::isError($result)) {
638  return $result;
639  }
640 
641  $alias = $aliases->addAlias($type_uri, $alias);
642 
643  if ($alias === null) {
644  return new Auth_OpenID_AX_Error(
645  sprintf("Could not add alias %s for URI %s",
646  $alias, $type_uri)
647  );
648  }
649  }
650  }
651 
652  foreach ($aliases->iteritems() as $pair) {
653  list($type_uri, $alias) = $pair;
654 
655  if (array_key_exists('count.' . $alias, $ax_args) && ($ax_args['count.' . $alias] !== Auth_OpenID_AX_UNLIMITED_VALUES)) {
656 
657  $count_key = 'count.' . $alias;
658  $count_s = $ax_args[$count_key];
659 
660  $count = Auth_OpenID::intval($count_s);
661 
662  if ($count === false) {
663  return new Auth_OpenID_AX_Error(
664  sprintf("Integer value expected for %s, got %s",
665  'count. %s' . $alias, $count_s,
667  );
668  }
669 
670  $values = array();
671  for ($i = 1; $i < $count + 1; $i++) {
672  $value_key = sprintf('value.%s.%d', $alias, $i);
673 
674  if (!array_key_exists($value_key, $ax_args)) {
675  return new Auth_OpenID_AX_Error(
676  sprintf(
677  "No value found for key %s",
678  $value_key));
679  }
680 
681  $value = $ax_args[$value_key];
682  $values[] = $value;
683  }
684  } else {
685  $key = 'value.' . $alias;
686 
687  if (!array_key_exists($key, $ax_args)) {
688  return new Auth_OpenID_AX_Error(
689  sprintf(
690  "No value found for key %s",
691  $key));
692  }
693 
694  $value = $ax_args['value.' . $alias];
695 
696  if ($value == '') {
697  $values = array();
698  } else {
699  $values = array($value);
700  }
701  }
702 
703  $this->data[$type_uri] = $values;
704  }
705 
706  return true;
707  }
708 
722  function getSingle($type_uri, $default=null)
723  {
724  $values = Auth_OpenID::arrayGet($this->data, $type_uri);
725  if (!$values) {
726  return $default;
727  } else if (count($values) == 1) {
728  return $values[0];
729  } else {
730  return new Auth_OpenID_AX_Error(
731  sprintf('More than one value present for %s',
732  $type_uri)
733  );
734  }
735  }
736 
753  function get($type_uri)
754  {
755  if (array_key_exists($type_uri, $this->data)) {
756  return $this->data[$type_uri];
757  } else {
758  return new Auth_OpenID_AX_Error(
759  sprintf("Type URI %s not found in response",
760  $type_uri)
761  );
762  }
763  }
764 
775  function count($type_uri)
776  {
777  if (array_key_exists($type_uri, $this->data)) {
778  return count($this->get($type_uri));
779  } else {
780  return new Auth_OpenID_AX_Error(
781  sprintf("Type URI %s not found in response",
782  $type_uri)
783  );
784  }
785  }
786 }
787 
794  var $mode = 'fetch_response';
796  function Auth_OpenID_AX_FetchResponse($update_url=null)
797  {
799  $this->update_url = $update_url;
800  }
801 
810  function getExtensionArgs($request=null)
811  {
813 
814  $zero_value_types = array();
815 
816  if ($request !== null) {
817  // Validate the data in the context of the request (the
818  // same attributes should be present in each, and the
819  // counts in the response must be no more than the counts
820  // in the request)
821 
822  foreach ($this->data as $type_uri => $unused) {
823  if (!$request->contains($type_uri)) {
824  return new Auth_OpenID_AX_Error(
825  sprintf("Response attribute not present in request: %s",
826  $type_uri)
827  );
828  }
829  }
830 
831  foreach ($request->iterAttrs() as $attr_info) {
832  // Copy the aliases from the request so that reading
833  // the response in light of the request is easier
834  if ($attr_info->alias === null) {
835  $aliases->add($attr_info->type_uri);
836  } else {
837  $alias = $aliases->addAlias($attr_info->type_uri,
838  $attr_info->alias);
839 
840  if ($alias === null) {
841  return new Auth_OpenID_AX_Error(
842  sprintf("Could not add alias %s for URI %s",
843  $attr_info->alias, $attr_info->type_uri)
844  );
845  }
846  }
847 
848  if (array_key_exists($attr_info->type_uri, $this->data)) {
849  $values = $this->data[$attr_info->type_uri];
850  } else {
851  $values = array();
852  $zero_value_types[] = $attr_info;
853  }
854 
855  if (($attr_info->count != Auth_OpenID_AX_UNLIMITED_VALUES) &&
856  ($attr_info->count < count($values))) {
857  return new Auth_OpenID_AX_Error(
858  sprintf("More than the number of requested values " .
859  "were specified for %s",
860  $attr_info->type_uri)
861  );
862  }
863  }
864  }
865 
866  $kv_args = $this->_getExtensionKVArgs($aliases);
867 
868  // Add the KV args into the response with the args that are
869  // unique to the fetch_response
870  $ax_args = $this->_newArgs();
871 
872  // For each requested attribute, put its type/alias and count
873  // into the response even if no data were returned.
874  foreach ($zero_value_types as $attr_info) {
875  $alias = $aliases->getAlias($attr_info->type_uri);
876  $kv_args['type.' . $alias] = $attr_info->type_uri;
877  $kv_args['count.' . $alias] = '0';
878  }
879 
880  $update_url = null;
881  if ($request) {
882  $update_url = $request->update_url;
883  } else {
884  $update_url = $this->update_url;
885  }
886 
887  if ($update_url) {
888  $ax_args['update_url'] = $update_url;
889  }
890 
891  Auth_OpenID::update($ax_args, $kv_args);
892 
893  return $ax_args;
894  }
895 
900  function parseExtensionArgs($ax_args)
901  {
902  $result = parent::parseExtensionArgs($ax_args);
903 
904  if (Auth_OpenID_AX::isError($result)) {
905  return $result;
906  }
907 
908  $this->update_url = Auth_OpenID::arrayGet($ax_args, 'update_url');
909 
910  return true;
911  }
912 
925  static function fromSuccessResponse($success_response, $signed=true)
926  {
927  $obj = new Auth_OpenID_AX_FetchResponse();
928  if ($signed) {
929  $ax_args = $success_response->getSignedNS($obj->ns_uri);
930  } else {
931  $ax_args = $success_response->message->getArgs($obj->ns_uri);
932  }
933  if ($ax_args === null || Auth_OpenID::isFailure($ax_args) ||
934  sizeof($ax_args) == 0) {
935  return null;
936  }
937 
938  $result = $obj->parseExtensionArgs($ax_args);
939  if (Auth_OpenID_AX::isError($result)) {
940  #XXX log me
941  return null;
942  }
943  return $obj;
944  }
945 }
946 
953  var $mode = 'store_request';
954 
959  function getExtensionArgs($aliases=null)
960  {
961  $ax_args = $this->_newArgs();
962  $kv_args = $this->_getExtensionKVArgs($aliases);
963  Auth_OpenID::update($ax_args, $kv_args);
964  return $ax_args;
965  }
966 }
967 
976  var $SUCCESS_MODE = 'store_response_success';
977  var $FAILURE_MODE = 'store_response_failure';
978 
983  function make($succeeded=true, $error_message=null)
984  {
985  if (($succeeded) && ($error_message !== null)) {
986  return new Auth_OpenID_AX_Error('An error message may only be '.
987  'included in a failing fetch response');
988  }
989 
990  return new Auth_OpenID_AX_StoreResponse($succeeded, $error_message);
991  }
993  function Auth_OpenID_AX_StoreResponse($succeeded=true, $error_message=null)
994  {
995  if ($succeeded) {
996  $this->mode = $this->SUCCESS_MODE;
997  } else {
998  $this->mode = $this->FAILURE_MODE;
999  }
1000 
1001  $this->error_message = $error_message;
1002  }
1003 
1007  function succeeded()
1008  {
1009  return $this->mode == $this->SUCCESS_MODE;
1010  }
1012  function getExtensionArgs()
1013  {
1014  $ax_args = $this->_newArgs();
1015  if ((!$this->succeeded()) && $this->error_message) {
1016  $ax_args['error'] = $this->error_message;
1017  }
1018 
1019  return $ax_args;
1020  }
1021 }
1022