Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 and 403]

   1  <?php
   2  
   3  namespace IMSGlobal\LTI\OAuth;
   4  
   5  /**
   6   * Class to represent an %OAuth Request
   7   *
   8   * @copyright  Andy Smith
   9   * @version 2008-08-04
  10   * @license https://opensource.org/licenses/MIT The MIT License
  11   */
  12  #[\AllowDynamicProperties]
  13  class OAuthRequest {
  14  
  15      protected $parameters;
  16      protected $http_method;
  17      protected $http_url;
  18      // for debug purposes
  19      public $base_string;
  20      public static $version = '1.0';
  21  
  22      function __construct($http_method, $http_url, $parameters = null) {
  23  
  24          $parameters = ($parameters) ? $parameters : array();
  25          $parameters = array_merge( OAuthUtil::parse_parameters(parse_url($http_url, PHP_URL_QUERY)), $parameters);
  26          $this->parameters = $parameters;
  27          $this->http_method = $http_method;
  28          $this->http_url = $http_url;
  29  
  30      }
  31  
  32  
  33      /**
  34       * attempt to build up a request from what was passed to the server
  35       */
  36      public static function from_request($http_method = null, $http_url = null, $parameters = null) {
  37  
  38        global $FULLME;
  39        $http_url = $http_url ?: $FULLME;
  40        $http_method = ($http_method) ? $http_method : $_SERVER['REQUEST_METHOD'];
  41  
  42        // We weren't handed any parameters, so let's find the ones relevant to
  43        // this request.
  44        // If you run XML-RPC or similar you should use this to provide your own
  45        // parsed parameter-list
  46        if (!$parameters) {
  47            // Find request headers
  48            $request_headers = OAuthUtil::get_headers();
  49  
  50            // Parse the query-string to find GET parameters
  51            if (isset($_SERVER['QUERY_STRING'])) {
  52                $parameters = OAuthUtil::parse_parameters($_SERVER['QUERY_STRING']);
  53            } else {
  54                $parameters = array();
  55            }
  56  
  57            // We have a Authorization-header with OAuth data. Parse the header and add those.
  58            if (isset($request_headers['Authorization']) && substr($request_headers['Authorization'], 0, 6) == 'OAuth ') {
  59                $header_parameters = OAuthUtil::split_header($request_headers['Authorization']);
  60                $parameters = array_merge($parameters, $header_parameters);
  61            }
  62  
  63            // If there are parameters in $_POST, these are likely what will be used. Therefore, they should be considered
  64            // the final value in the case of any duplicates from sources parsed above.
  65            $parameters = array_merge($parameters, $_POST);
  66        }
  67  
  68        return new OAuthRequest($http_method, $http_url, $parameters);
  69      }
  70  
  71      /**
  72       * pretty much a helper function to set up the request
  73       */
  74      public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters = null) {
  75  
  76          $parameters = ($parameters) ?  $parameters : array();
  77          $defaults = array('oauth_version' => OAuthRequest::$version,
  78                            'oauth_nonce' => OAuthRequest::generate_nonce(),
  79                            'oauth_timestamp' => OAuthRequest::generate_timestamp(),
  80                            'oauth_consumer_key' => $consumer->key);
  81          if ($token)
  82              $defaults['oauth_token'] = $token->key;
  83  
  84          $parameters = array_merge($defaults, $parameters);
  85  
  86          return new OAuthRequest($http_method, $http_url, $parameters);
  87  
  88      }
  89  
  90      public function set_parameter($name, $value, $allow_duplicates = true) {
  91  
  92        if ($allow_duplicates && isset($this->parameters[$name])) {
  93            // We have already added parameter(s) with this name, so add to the list
  94            if (is_scalar($this->parameters[$name])) {
  95                // This is the first duplicate, so transform scalar (string)
  96                // into an array so we can add the duplicates
  97                $this->parameters[$name] = array($this->parameters[$name]);
  98            }
  99  
 100            $this->parameters[$name][] = $value;
 101        } else {
 102            $this->parameters[$name] = $value;
 103        }
 104      }
 105  
 106      public function get_parameter($name) {
 107          return isset($this->parameters[$name]) ? $this->parameters[$name] : null;
 108      }
 109  
 110      public function get_parameters() {
 111          return $this->parameters;
 112      }
 113  
 114      public function unset_parameter($name) {
 115          unset($this->parameters[$name]);
 116      }
 117  
 118      /**
 119       * The request parameters, sorted and concatenated into a normalized string.
 120       * @return string
 121       */
 122      public function get_signable_parameters() {
 123  
 124          // Grab all parameters
 125          $params = $this->parameters;
 126  
 127          // Remove oauth_signature if present
 128          // Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.")
 129          if (isset($params['oauth_signature'])) {
 130              unset($params['oauth_signature']);
 131          }
 132  
 133          return OAuthUtil::build_http_query($params);
 134  
 135      }
 136  
 137      /**
 138       * Returns the base string of this request
 139       *
 140       * The base string defined as the method, the url
 141       * and the parameters (normalized), each urlencoded
 142       * and the concated with &.
 143       */
 144      public function get_signature_base_string() {
 145          $parts = array(
 146              $this->get_normalized_http_method(),
 147              $this->get_normalized_http_url(),
 148              $this->get_signable_parameters()
 149          );
 150  
 151          $parts = OAuthUtil::urlencode_rfc3986($parts);
 152  
 153          return implode('&', $parts);
 154  
 155      }
 156  
 157      /**
 158       * just uppercases the http method
 159       */
 160      public function get_normalized_http_method() {
 161          return strtoupper($this->http_method);
 162      }
 163  
 164      /**
 165       * parses the url and rebuilds it to be
 166       * scheme://host/path
 167       */
 168      public function get_normalized_http_url() {
 169  
 170          $parts = parse_url($this->http_url);
 171  
 172          $scheme = (isset($parts['scheme'])) ? $parts['scheme'] : 'http';
 173          $port = (isset($parts['port'])) ? $parts['port'] : (($scheme == 'https') ? '443' : '80');
 174          $host = (isset($parts['host'])) ? strtolower($parts['host']) : '';
 175          $path = (isset($parts['path'])) ? $parts['path'] : '';
 176  
 177          if (($scheme == 'https' && $port != '443')
 178              || ($scheme == 'http' && $port != '80')) {
 179              $host = "$host:$port";
 180          }
 181  
 182          return "$scheme://$host$path";
 183  
 184      }
 185  
 186      /**
 187       * builds a url usable for a GET request
 188       */
 189      public function to_url() {
 190  
 191          $post_data = $this->to_postdata();
 192          $out = $this->get_normalized_http_url();
 193          if ($post_data) {
 194              $out .= '?'.$post_data;
 195          }
 196  
 197          return $out;
 198  
 199      }
 200  
 201      /**
 202       * builds the data one would send in a POST request
 203       */
 204      public function to_postdata() {
 205          return OAuthUtil::build_http_query($this->parameters);
 206      }
 207  
 208      /**
 209       * builds the Authorization: header
 210       */
 211      public function to_header($realm = null) {
 212  
 213          $first = true;
 214          if($realm) {
 215              $out = 'Authorization: OAuth realm="' . OAuthUtil::urlencode_rfc3986($realm) . '"';
 216              $first = false;
 217          } else
 218              $out = 'Authorization: OAuth';
 219  
 220          $total = array();
 221          foreach ($this->parameters as $k => $v) {
 222              if (substr($k, 0, 5) != "oauth") continue;
 223              if (is_array($v)) {
 224                throw new OAuthException('Arrays not supported in headers');
 225              }
 226              $out .= ($first) ? ' ' : ',';
 227              $out .= OAuthUtil::urlencode_rfc3986($k) .
 228                      '="' .
 229                      OAuthUtil::urlencode_rfc3986($v) .
 230                      '"';
 231              $first = false;
 232          }
 233  
 234          return $out;
 235  
 236      }
 237  
 238      public function __toString() {
 239          return $this->to_url();
 240      }
 241  
 242  
 243      public function sign_request($signature_method, $consumer, $token) {
 244  
 245          $this->set_parameter(
 246            "oauth_signature_method",
 247            $signature_method->get_name(),
 248            false
 249          );
 250          $signature = $this->build_signature($signature_method, $consumer, $token);
 251          $this->set_parameter("oauth_signature", $signature, false);
 252  
 253      }
 254  
 255      public function build_signature($signature_method, $consumer, $token) {
 256          $signature = $signature_method->build_signature($this, $consumer, $token);
 257          return $signature;
 258      }
 259  
 260      /**
 261       * util function: current timestamp
 262       */
 263      private static function generate_timestamp() {
 264          return time();
 265      }
 266  
 267      /**
 268       * util function: current nonce
 269       */
 270      private static function generate_nonce() {
 271          $mt = microtime();
 272          $rand = mt_rand();
 273  
 274          return md5($mt . $rand); // md5s look nicer than numbers
 275      }
 276  
 277  }