ILIAS  release_8 Revision v8.24
OAuth.php
Go to the documentation of this file.
1<?php
2
3declare(strict_types=1);
4
22
24
32
34// * OAuth PECL extension includes an OAuth Exception class, so we need to wrap
35// * the definition of this class in order to avoid a PHP error.
36// */
37if (!class_exists('OAuthException')) {
38 /*
39 * Generic exception class
40 */
41 class OAuthException extends Exception
42 {
43 // pass
44 }
45}
46
47if (!class_exists('OAuthConsumer')) {
48 class OAuthConsumer
49 {
51 public string $key;
52
54 public string $secret;
55
57 public ?string $callback_url;
58
59
65 public function __construct(string $key, string $secret, ?string $callback_url = null)
66 {
67 $this->key = $key;
68 $this->secret = $secret;
69 $this->callback_url = $callback_url;
70 }
71
72
76 public function __toString()
77 {
78 return "OAuthConsumer[key=$this->key,secret=$this->secret]";
79 }
80 }
81}
82if (!class_exists('OAuthToken')) {
83 class OAuthToken
84 {
85 // access tokens and request tokens
87 public string $key;
88
90 public string $secret;
91
93 public $callback = null;
94
99 public function __construct(string $key, string $secret)
100 {
101 $this->key = $key;
102 $this->secret = $secret;
103 }
104
109 public function to_string(): string
110 {
112 $key = OAuthUtil::urlencode_rfc3986($this->key);
114 $secret = OAuthUtil::urlencode_rfc3986($this->secret);
115 return "oauth_token=" . $key .
116 "&oauth_token_secret=" . $secret .
117 "&oauth_callback_confirmed=true";
118 }
119
123 public function __toString()
124 {
125 return $this->to_string();
126 }
127 }
128}
133if (!class_exists('OAuthSignatureMethod')) {
134 abstract class OAuthSignatureMethod
135 {
139 abstract public function get_name(): string;
140
148 abstract public function build_signature(OAuthRequest $request, OAuthConsumer $consumer, OAuthToken $token): string;
149
158 public function check_signature(OAuthRequest $request, OAuthConsumer $consumer, OAuthToken $token, string $signature): bool
159 {
160 $built = $this->build_signature($request, $consumer, $token);
161
162 // Check for zero length, although unlikely here
163 if (strlen($built) == 0 || strlen($signature) == 0) {
164 return false;
165 }
166
167 if (strlen($built) != strlen($signature)) {
168 return false;
169 }
170
171 // Avoid a timing leak with a (hopefully) time insensitive compare
172 $result = 0;
173 for ($i = 0; $i < strlen($signature); $i++) {
174 $result |= ord($built[$i]) ^ ord($signature[$i]);
175 }
176
177 return $result == 0;
178 }
179 }
180}
188//if (!class_exists('OAuthSignatureMethod_HMAC_SHA1')) {
190{
191 public function get_name(): string
192 {
193 return "HMAC-SHA1";
194 }
195
196
200 public function build_signature(OAuthRequest $request, OAuthConsumer $consumer, ?OAuthToken $token): string
201 {
202 $base_string = $request->get_signature_base_string();
203 $request->base_string = $base_string;
204
205 $key_parts = [
206 $consumer->secret,
207 ($token) ? $token->secret : ""
208 ];
209
211 $key_parts = OAuthUtil::urlencode_rfc3986($key_parts);
212 $key = implode('&', $key_parts);
213
214 return base64_encode(hash_hmac('sha1', $base_string, $key, true));
215 }
216}
217//}
223if (!class_exists('OAuthSignatureMethod_PLAINTEXT')) {
224 class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod
225 {
226 public function get_name(): string
227 {
228 return "PLAINTEXT";
229 }
230
243 public function build_signature(OAuthRequest $request, OAuthConsumer $consumer, ?OAuthToken $token): string
244 {
245 $key_parts = [
246 $consumer->secret,
247 ($token) ? $token->secret : ""
248 ];
249
251 $key_parts = OAuthUtil::urlencode_rfc3986($key_parts);
252 $key = implode('&', $key_parts);
253 $request->base_string = $key;
254
255 return $key;
256 }
257 }
258}
267if (!class_exists('OAuthSignatureMethod_RSA_SHA1')) {
268 abstract class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod
269 {
270 public function get_name(): string
271 {
272 return "RSA-SHA1";
273 }
274
275
286 abstract protected function fetch_public_cert(OAuthRequest &$request);
287
288
297 abstract protected function fetch_private_cert(OAuthRequest &$request);
298
299
300 public function build_signature(OAuthRequest $request, OAuthConsumer $consumer, OAuthToken $token): string
301 {
302 $base_string = $request->get_signature_base_string();
303 $request->base_string = $base_string;
304
305 // Fetch the private key cert based on the request
306 $cert = $this->fetch_private_cert($request);
307
308 // Pull the private key ID from the certificate
309 $privatekeyid = openssl_get_privatekey($cert);
310
311 // Sign using the key
312 openssl_sign($base_string, $signature, $privatekeyid);
313
314 if (PHP_MAJOR_VERSION < 8) {
315 // Release the key resource
316 openssl_free_key($privatekeyid);
317 }
318 return base64_encode($signature);
319 }
320
321
322 public function check_signature(OAuthRequest $request, OAuthConsumer $consumer, OAuthToken $token, string $signature): bool
323 {
324 $decoded_sig = base64_decode($signature);
325
326 $base_string = $request->get_signature_base_string();
327
328 // Fetch the public key cert based on the request
329 $cert = $this->fetch_public_cert($request);
330
331 // Pull the public key ID from the certificate
332 $publickeyid = openssl_get_publickey($cert);
333
334 // Check the computed signature against the one passed in the query
335 $ok = openssl_verify($base_string, $decoded_sig, $publickeyid);
336
337 if (PHP_MAJOR_VERSION < 8) {
338 // Release the key resource
339 openssl_free_key($publickeyid);
340 }
341
342 return $ok == 1;
343 }
344 }
345}
346if (!class_exists('OAuthRequest')) {
347 class OAuthRequest
348 {
350 protected array $parameters;
351
353 protected string $http_method;
354
356 protected string $http_url;
357
358 // for debug purposes
360 public ?string $base_string = null;
361
363 public static string $version = '1.0';
364
366 public static string $POST_INPUT = 'php://input';
367
368
375 public function __construct(string $http_method, string $http_url, ?array $parameters = null)
376 {
377 $parameters = ($parameters) ? $parameters : [];
378 $parameters = array_merge(OAuthUtil::parse_parameters((string) parse_url($http_url, PHP_URL_QUERY)), $parameters);
379 $this->parameters = $parameters;
380 $this->http_method = $http_method;
381 $this->http_url = $http_url;
382 }
383
391 public static function from_request(?string $http_method = null, ?string $http_url = null, ?array $parameters = null): OAuthRequest
392 {
393 $scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on")
394 ? 'http'
395 : 'https';
396
397 //SPECIAL FOR LTI BEGIN
400 $port = "";
401 if ($_SERVER['SERVER_PORT'] != "80" && $_SERVER['SERVER_PORT'] != "443" &&
402 strpos(':', $_SERVER['HTTP_HOST']) < 0) {
403 $port = ':' . $_SERVER['SERVER_PORT'] ;
404 }
405 $http_url = ($http_url) ? $http_url : $scheme .
406 '://' . $_SERVER['HTTP_HOST'] .
407 $port .
408 $_SERVER['REQUEST_URI'];
409 } else {
410 $http_url = ($http_url) ? $http_url : $scheme .
411 '://' . $_SERVER['SERVER_NAME'] .
412 ':' .
413 $_SERVER['SERVER_PORT'] .
414 $_SERVER['REQUEST_URI'];
415 }
416 //SPECIAL FOR LTI END
417
418 $http_method = ($http_method) ? $http_method : $_SERVER['REQUEST_METHOD'];
419
420 // We weren't handed any parameters, so let's find the ones relevant to
421 // this request.
422 // If you run XML-RPC or similar you should use this to provide your own
423 // parsed parameter-list
424 if (!$parameters) {
425 // Find request headers
426 $request_headers = OAuthUtil::get_headers();
427
428 // Parse the query-string to find GET parameters
429 $parameters = OAuthUtil::parse_parameters($_SERVER['QUERY_STRING']);
430
431 // It's a POST request of the proper content-type, so parse POST
432 // parameters and add those overriding any duplicates from GET
433 if ($http_method == "POST"
434 && isset($request_headers['Content-Type'])
435 && strstr($request_headers['Content-Type'], 'application/x-www-form-urlencoded')
436 ) {
437 $post_data = OAuthUtil::parse_parameters(
438 file_get_contents(self::$POST_INPUT)
439 );
440 $parameters = array_merge($parameters, $post_data);
441 }
442
443 // We have a Authorization-header with OAuth data. Parse the header
444 // and add those overriding any duplicates from GET or POST
445 if (isset($request_headers['Authorization'])
446 && substr($request_headers['Authorization'], 0, 6) == 'OAuth '
447 ) {
448 $header_parameters = OAuthUtil::split_header(
449 $request_headers['Authorization']
450 );
451 $parameters = array_merge($parameters, $header_parameters);
452 }
453 }
454
455 return new OAuthRequest($http_method, $http_url, $parameters);
456 }
457
467 public static function from_consumer_and_token(OAuthConsumer $consumer, ?OAuthToken $token, string $http_method, string $http_url, ?array $parameters = null): OAuthRequest
468 {
469 $parameters = ($parameters) ? $parameters : [];
470 $defaults = ["oauth_version" => OAuthRequest::$version,
471 "oauth_nonce" => OAuthRequest::generate_nonce(),
472 "oauth_timestamp" => OAuthRequest::generate_timestamp(),
473 "oauth_consumer_key" => $consumer->key];
474 if ($token) {
475 $defaults['oauth_token'] = $token->key;
476 }
477
478 $parameters = array_merge($defaults, $parameters);
479
480 return new OAuthRequest($http_method, $http_url, $parameters);
481 }
482
483
484 public function set_parameter(string $name, string $value, bool $allow_duplicates = true): void
485 {
486 if ($allow_duplicates && isset($this->parameters[$name])) {
487 // We have already added parameter(s) with this name, so add to the list
488 if (is_scalar($this->parameters[$name])) {
489 // This is the first duplicate, so transform scalar (string)
490 // into an array so we can add the duplicates
491 $this->parameters[$name] = [$this->parameters[$name]];
492 }
493
494 $this->parameters[$name][] = $value;
495 } else {
496 $this->parameters[$name] = $value;
497 }
498 }
499
500
504 public function get_parameter(string $name)
505 {
506 return isset($this->parameters[$name]) ? $this->parameters[$name] : null;
507 }
508
509
513 public function get_parameters(): array
514 {
515 return $this->parameters;
516 }
517
518
519 public function unset_parameter(string $name): void
520 {
521 unset($this->parameters[$name]);
522 }
523
524
528 public function get_signable_parameters(): string
529 {
530 // Grab all parameters
531 $params = $this->parameters;
532
533 // Remove oauth_signature if present
534 // Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.")
535 if (isset($params['oauth_signature'])) {
536 unset($params['oauth_signature']);
537 }
538
539 return OAuthUtil::build_http_query($params);
540 }
541
542
550 public function get_signature_base_string(): string
551 {
552 $parts = [
553 $this->get_normalized_http_method(),
554 $this->get_normalized_http_url(),
555 $this->get_signable_parameters()
556 ];
557
559 $parts = OAuthUtil::urlencode_rfc3986($parts);
560
561 return implode('&', $parts);
562 }
563
564
568 public function get_normalized_http_method(): string
569 {
570 return strtoupper($this->http_method);
571 }
572
573
578 public function get_normalized_http_url(): string
579 {
580 $parts = parse_url($this->http_url);
581
582 $scheme = (isset($parts['scheme'])) ? $parts['scheme'] : 'http';
583 $port = (isset($parts['port'])) ? $parts['port'] : (($scheme == 'https') ? '443' : '80');
584 $host = (isset($parts['host'])) ? strtolower($parts['host']) : '';
585 $path = (isset($parts['path'])) ? $parts['path'] : '';
586
587 if (($scheme == 'https' && $port != '443')
588 || ($scheme == 'http' && $port != '80')) {
589 $host = "$host:$port";
590 }
591 return "$scheme://$host$path";
592 }
593
594
598 public function to_url(): string
599 {
600 $post_data = $this->to_postdata();
601 $out = $this->get_normalized_http_url();
602 if ($post_data) {
603 $out .= '?' . $post_data;
604 }
605 return $out;
606 }
607
608
612 public function to_postdata(): string
613 {
614 return OAuthUtil::build_http_query($this->parameters);
615 }
616
623 public function to_header(?string $realm = null): string
624 {
625 $first = true;
626 if (!is_null($realm)) {
628 $realm = OAuthUtil::urlencode_rfc3986($realm);
629 $out = 'Authorization: OAuth realm="' . $realm . '"';
630 $first = false;
631 } else {
632 $out = 'Authorization: OAuth';
633 }
634
635 foreach ($this->parameters as $k => $v) {
636 if (substr($k, 0, 5) != "oauth") {
637 continue;
638 }
639 if (is_array($v)) {
640 throw new OAuthException('Arrays not supported in headers');
641 }
642 $out .= ($first) ? ' ' : ',';
644 $key = OAuthUtil::urlencode_rfc3986($k);
646 $value = OAuthUtil::urlencode_rfc3986($v);
647 $out .= $key . '="' . $value . '"';
648 $first = false;
649 }
650 return $out;
651 }
652
653
657 public function __toString()
658 {
659 return $this->to_url();
660 }
661
662
663 public function sign_request(OAuthSignatureMethod $signature_method, OAuthConsumer $consumer, ?OAuthToken $token): void
664 {
665 $this->set_parameter(
666 "oauth_signature_method",
667 $signature_method->get_name(),
668 false
669 );
670 $signature = $this->build_signature($signature_method, $consumer, $token);
671 $this->set_parameter("oauth_signature", $signature, false);
672 }
673
674
675 public function build_signature(OAuthSignatureMethod $signature_method, OAuthConsumer $consumer, ?OAuthToken $token): string
676 {
677 $signature = $signature_method->build_signature($this, $consumer, $token);
678 return $signature;
679 }
680
681
685 private static function generate_timestamp(): int
686 {
687 return time();
688 }
689
690
694 private static function generate_nonce(): string
695 {
696 $mt = microtime();
697 $rand = mt_rand();
698
699 return md5($mt . $rand); // md5s look nicer than numbers
700 }
701 }
702}
703if (!class_exists('OAuthServer')) {
704 class OAuthServer
705 {
707 protected int $timestamp_threshold = 300; // in seconds, five minutes
708
710 protected string $version = '1.0'; // hi blaine
711
713 protected array $signature_methods = [];
714
716 protected OAuthDataStore $data_store;
717
718
722 public function __construct(OAuthDataStore $data_store)
723 {
724 $this->data_store = $data_store;
725 }
726
727
728 public function add_signature_method(OAuthSignatureMethod $signature_method): void
729 {
730 $this->signature_methods[$signature_method->get_name()] =
731 $signature_method;
732 }
733
734
735 // high level functions
740 public function fetch_request_token(OAuthRequest &$request): OAuthToken
741 {
742 $this->getVersion($request);
743
744 $consumer = $this->getConsumer($request);
745
746 // no token required for the initial token request
747 $token = null;
748
749 $this->checkSignature($request, $consumer, $token);
750
751 // Rev A change
752 $callback = $request->get_parameter('oauth_callback');
753 $new_token = $this->data_store->new_request_token($consumer, $callback);
754
755 return $new_token;
756 }
757
758
763 public function fetch_access_token(OAuthRequest &$request): OAuthToken
764 {
765 $this->getVersion($request);
766
767 $consumer = $this->getConsumer($request);
768
769 // requires authorized request token
770 $token = $this->getToken($request, $consumer, "request");
771
772 $this->checkSignature($request, $consumer, $token);
773
774 // Rev A change
775 $verifier = $request->get_parameter('oauth_verifier');
776 $new_token = $this->data_store->new_access_token($token, $consumer, $verifier);
777
778 return $new_token;
779 }
780
781
787 public function verify_request(OAuthRequest &$request): array
788 {
789 $this->getVersion($request);
790 $consumer = $this->getConsumer($request);
791 $token = $this->getToken($request, $consumer, "access");
792 $this->checkSignature($request, $consumer, $token);
793 return [$consumer, $token];
794 }
795
796
797 // Internals from here
801 private function getVersion(OAuthRequest &$request): string
802 {
803 $version = $request->get_parameter("oauth_version");
804 if (!$version) {
805 // Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present.
806 // Chapter 7.0 ("Accessing Protected Ressources")
807 $version = '1.0';
808 }
809 if ($version !== $this->version) {
810 throw new OAuthException("OAuth version '$version' not supported");
811 }
812 return $version;
813 }
814
821 private function getSignatureMethod(OAuthRequest $request): object
822 {
823 $signature_method = $request->get_parameter("oauth_signature_method");
824
825 if (!$signature_method) {
826 //Special LTI BEGIN
829 $signature_method = "PLAINTEXT";
830 } else {
831 // According to chapter 7 ("Accessing Protected Ressources") the signature-method
832 // parameter is required, and we can't just fallback to PLAINTEXT
833 throw new OAuthException('No signature method parameter. This parameter is required');
834 }
835 //Special LTI END
836 }
837
838 if (!in_array($signature_method, array_keys($this->signature_methods))) {
839 throw new OAuthException(
840 "Signature method '$signature_method' not supported " .
841 "try one of the following: " .
842 implode(", ", array_keys($this->signature_methods))
843 );
844 }
845 return $this->signature_methods[$signature_method];
846 }
847
848
854 private function getConsumer(OAuthRequest $request): OAuthConsumer
855 {
856 $consumer_key = $request->get_parameter("oauth_consumer_key");
857
858 if (!$consumer_key) {
859 throw new OAuthException("Invalid consumer key");
860 }
861
862 $consumer = $this->data_store->lookup_consumer($consumer_key);
863 if (!$consumer) {
864 throw new OAuthException("Invalid consumer");
865 }
866
867 return $consumer;
868 }
869
870
876 private function getToken(OAuthRequest $request, OAuthConsumer $consumer, string $token_type = "access"): OAuthToken
877 {
878 $token_field = $request->get_parameter('oauth_token');
879
880 if (!empty($token_field)) {
881 $token = $this->data_store->lookup_token($consumer, $token_type, $token_field);
882 if (!$token) {
883 throw new OAuthException('Invalid ' . $token_type . ' token: ' . $token_field);
884 }
885 } else {
886 $token = new OAuthToken('', '');
887 }
888 return $token;
889 }
890
891
901 private function checkSignature(OAuthRequest $request, OAuthConsumer $consumer, OAuthToken $token = null): void
902 {
903 // this should probably be in a different method
904 $timestamp = (int) $request->get_parameter('oauth_timestamp');
905 $nonce = $request->get_parameter('oauth_nonce');
906
907 $this->checkTimestamp($timestamp);
908 $this->checkNonce($consumer, $token, $nonce, $timestamp);
909
910// $signature_method = 'OAuthSignatureMethod_' . $this->getSignatureMethod($request);
911 $signature_method = $this->getSignatureMethod($request); //check UK
913 $method = new $signature_method();
914
915 $signature = $request->get_parameter('oauth_signature');
916// $valid_sig = $method->checkSignature( //check UK
917 $valid_sig = $method->check_signature(
918 $request,
919 $consumer,
920 $token,
921 $signature
922 );
923
924 if (!$valid_sig) {
925 throw new OAuthException("Invalid signature");
926 }
927 }
928
929
935 private function checkTimestamp(?int $timestamp): void
936 {
937 if (!$timestamp) {
938 throw new OAuthException(
939 'Missing timestamp parameter. The parameter is required'
940 );
941 }
942
943 // verify that timestamp is recentish
944 $now = time();
945 if (abs($now - $timestamp) > $this->timestamp_threshold) {
946 throw new OAuthException(
947 "Expired timestamp, yours $timestamp, ours $now"
948 );
949 }
950 }
951
952
958 private function checkNonce(OAuthConsumer $consumer, ?OAuthToken $token, string $nonce, int $timestamp): void
959 {
960 if (!$nonce) {
961 throw new OAuthException(
962 'Missing nonce parameter. The parameter is required'
963 );
964 }
965
966 // verify that the nonce is uniqueish
967 $found = $this->data_store->lookup_nonce(
968 $consumer,
969 $token,
970 $nonce,
972 );
973 if ($found) {
974 throw new OAuthException("Nonce already used: $nonce");
975 }
976 }
977 }
978}
979if (!class_exists('OAuthDataStore')) {
980 abstract class OAuthDataStore
981 {
982 abstract public function lookup_consumer(string $consumer_key);
983
984
985 abstract public function lookup_token(OAuthConsumer $consumer, string $token_type, string $token);
986
987
988 abstract public function lookup_nonce(OAuthConsumer $consumer, ?OAuthToken $token, string $nonce, int $timestamp);
989
990
994 abstract public function new_request_token(OAuthConsumer $consumer, ?string $callback = null);
995
1001 abstract public function new_access_token(OAuthToken $token, OAuthConsumer $consumer, ?string $verifier = null);
1002 }
1003}
1004if (!class_exists('OAuthUtil')) {
1005 class OAuthUtil
1006 {
1011 public static function urlencode_rfc3986($input)
1012 {
1013 if (is_array($input)) {
1014 return array_map(['ILIAS\LTIOAuth\OAuthUtil', 'urlencode_rfc3986'], $input);
1015 } elseif (is_scalar($input)) {
1016 return str_replace(
1017 '+',
1018 ' ',
1019 str_replace('%7E', '~', rawurlencode(strval($input)))
1020 );
1021 } else {
1022 return '';
1023 }
1024 }
1025
1026
1032 public static function urldecode_rfc3986(string $string): string
1033 {
1034 return urldecode($string);
1035 }
1036
1037
1047 public static function split_header(string $header, bool $only_allow_oauth_parameters = true): array
1048 {
1049 $params = [];
1050 if (preg_match_all(
1051 '/(' . ($only_allow_oauth_parameters ? 'oauth_' : '') . '[a-z_-]*)=(:?"([^"]*)"|([^,]*))/',
1052 $header,
1053 $matches
1054 )) {
1055 foreach ($matches[1] as $i => $h) {
1056 $params[$h] = OAuthUtil::urldecode_rfc3986(empty($matches[3][$i]) ? $matches[4][$i] : $matches[3][$i]);
1057 }
1058 if (isset($params['realm'])) {
1059 unset($params['realm']);
1060 }
1061 }
1062 return $params;
1063 }
1064
1065
1071 public static function get_headers(): array
1072 {
1073 if (function_exists('apache_request_headers')) {
1074 // we need this to get the actual Authorization: header
1075 // because apache tends to tell us it doesn't exist
1076 $headers = getallheaders();
1077
1078 // sanitize the output of apache_request_headers because
1079 // we always want the keys to be Cased-Like-This and arh()
1080 // returns the headers in the same case as they are in the
1081 // request
1082 $out = [];
1083 foreach ($headers as $key => $value) {
1084 $key = str_replace(
1085 " ",
1086 "-",
1087 ucwords(strtolower(str_replace("-", " ", $key)))
1088 );
1089 $out[$key] = $value;
1090 }
1091 } else {
1092 // otherwise we don't have apache and are just going to have to hope
1093 // that $_SERVER actually contains what we need
1094 $out = [];
1095 if (isset($_SERVER['CONTENT_TYPE'])) {
1096 $out['Content-Type'] = $_SERVER['CONTENT_TYPE'];
1097 }
1098 if (isset($_ENV['CONTENT_TYPE'])) {
1099 $out['Content-Type'] = $_ENV['CONTENT_TYPE'];
1100 }
1101
1102 foreach ($_SERVER as $key => $value) {
1103 if (substr($key, 0, 5) == "HTTP_") {
1104 // this is chaos, basically it is just there to capitalize the first
1105 // letter of every word that is not an initial HTTP and strip HTTP
1106 // code from przemek
1107 $key = str_replace(
1108 " ",
1109 "-",
1110 ucwords(strtolower(str_replace("_", " ", substr($key, 5))))
1111 );
1112 $out[$key] = $value;
1113 }
1114 }
1115 // The "Authorization" header may get turned into "Auth".
1116 if (isset($out['Auth'])) {
1117 $out['Authorization'] = $out['Auth'];
1118 }
1119 }
1120 return $out;
1121 }
1122
1123
1131 public static function parse_parameters(?string $input): array
1132 {
1133 if (!isset($input) || $input == "") {
1134 return [];
1135 }
1136
1137 $pairs = explode('&', $input);
1138
1139 $parsed_parameters = [];
1140 foreach ($pairs as $pair) {
1141 $split = explode('=', $pair, 2);
1142 $parameter = OAuthUtil::urldecode_rfc3986($split[0]);
1143 $value = isset($split[1]) ? OAuthUtil::urldecode_rfc3986($split[1]) : '';
1144
1145 if (isset($parsed_parameters[$parameter])) {
1146 // We have already recieved parameter(s) with this name, so add to the list
1147 // of parameters with this name
1148
1149 if (is_scalar($parsed_parameters[$parameter])) {
1150 // This is the first duplicate, so transform scalar (string) into an array
1151 // so we can add the duplicates
1152 $parsed_parameters[$parameter] = [$parsed_parameters[$parameter]];
1153 }
1154
1155 $parsed_parameters[$parameter][] = $value;
1156 } else {
1157 $parsed_parameters[$parameter] = $value;
1158 }
1159 }
1160 return $parsed_parameters;
1161 }
1162
1163
1167 public static function build_http_query(array $params): string
1168 {
1169 if (!$params) {
1170 return '';
1171 }
1172
1173 // Urlencode both keys and values
1175 $keys = OAuthUtil::urlencode_rfc3986(array_keys($params));
1177 $values = OAuthUtil::urlencode_rfc3986(array_values($params));
1178 $params = array_combine($keys, $values);
1179
1180 // Parameters are sorted by name, using lexicographical byte value ordering.
1181 // Ref: Spec: 9.1.1 (1)
1182 uksort($params, 'strcmp');
1183
1184 $pairs = [];
1185 foreach ($params as $parameter => $value) {
1186 if (is_array($value)) {
1187 // If two or more parameters share the same name, they are sorted by their value
1188 // Ref: Spec: 9.1.1 (1)
1189 // June 12th, 2010 - changed to sort because of issue 164 by hidetaka
1190 sort($value, SORT_STRING);
1191 foreach ($value as $duplicate_value) {
1192 $pairs[] = $parameter . '=' . $duplicate_value;
1193 }
1194 } else {
1195 $pairs[] = $parameter . '=' . $value;
1196 }
1197 }
1198 // For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61)
1199 // Each name-value pair is separated by an '&' character (ASCII code 38)
1200 return implode('&', $pairs);
1201 }
1202 }
1203}
$version
Definition: plugin.php:24
$out
Definition: buildRTE.php:24
foreach($mandatory_scripts as $file) $timestamp
Definition: buildRTE.php:70
__construct()
Constructor setup ILIAS global object @access public.
Definition: class.ilias.php:62
if(! $DIC->user() ->getId()||!ilLTIConsumerAccess::hasCustomProviderCreationAccess()) $params
Definition: ltiregstart.php:33
$path
Definition: ltiservices.php:32
if($clientAssertionType !='urn:ietf:params:oauth:client-assertion-type:jwt-bearer'|| $grantType !='client_credentials') $parts
Definition: ltitoken.php:64
if($format !==null) $name
Definition: metadata.php:247
$i
Definition: metadata.php:41
$keys
Definition: metadata.php:204
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Definition: OAuth.php:21
$UseHttpHostInsteadOfServerName
Definition: OAuth.php:30
$DefaultSignatureMethodPlaintext
Definition: OAuth.php:31
string $key
Consumer key/client ID value.
Definition: System.php:193
string $secret
Shared secret.
Definition: System.php:43
$_SERVER['HTTP_HOST']
Definition: raiseError.php:10
$token
Definition: xapitoken.php:70