Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

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