Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

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