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.
   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  //
  17  // This file is part of BasicLTI4Moodle
  18  //
  19  // BasicLTI4Moodle is an IMS BasicLTI (Basic Learning Tools for Interoperability)
  20  // consumer for Moodle 1.9 and Moodle 2.0. BasicLTI is a IMS Standard that allows web
  21  // based learning tools to be easily integrated in LMS as native ones. The IMS BasicLTI
  22  // specification is part of the IMS standard Common Cartridge 1.1 Sakai and other main LMS
  23  // are already supporting or going to support BasicLTI. This project Implements the consumer
  24  // for Moodle. Moodle is a Free Open source Learning Management System by Martin Dougiamas.
  25  // BasicLTI4Moodle is a project iniciated and leaded by Ludo(Marc Alier) and Jordi Piguillem
  26  // at the GESSI research group at UPC.
  27  // SimpleLTI consumer for Moodle is an implementation of the early specification of LTI
  28  // by Charles Severance (Dr Chuck) htp://dr-chuck.com , developed by Jordi Piguillem in a
  29  // Google Summer of Code 2008 project co-mentored by Charles Severance and Marc Alier.
  30  //
  31  // BasicLTI4Moodle is copyright 2009 by Marc Alier Forment, Jordi Piguillem and Nikolas Galanis
  32  // of the Universitat Politecnica de Catalunya http://www.upc.edu
  33  // Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu.
  34  
  35  namespace moodle\mod\lti; // Using a namespace as the basicLTI module imports classes with the same names.
  36  
  37  defined('MOODLE_INTERNAL') || die;
  38  
  39  require_once($CFG->dirroot . '/mod/lti/OAuth.php');
  40  require_once($CFG->dirroot . '/mod/lti/TrivialStore.php');
  41  
  42  /**
  43   *
  44   * @param int $typeid LTI type ID.
  45   * @param string[] $scopes  Array of scopes which give permission for the current request.
  46   *
  47   * @return string|int|boolean  The OAuth consumer key, the LTI type ID for the validated bearer token,
  48                                 true for requests not requiring a scope, otherwise false.
  49   */
  50  function get_oauth_key_from_headers($typeid = null, $scopes = null) {
  51      global $DB;
  52  
  53      $now = time();
  54  
  55      $requestheaders = OAuthUtil::get_headers();
  56  
  57      if (isset($requestheaders['Authorization'])) {
  58          if (substr($requestheaders['Authorization'], 0, 6) == "OAuth ") {
  59              $headerparameters = OAuthUtil::split_header($requestheaders['Authorization']);
  60  
  61              return format_string($headerparameters['oauth_consumer_key']);
  62          } else if (empty($scopes)) {
  63              return true;
  64          } else if (substr($requestheaders['Authorization'], 0, 7) == 'Bearer ') {
  65              $tokenvalue = trim(substr($requestheaders['Authorization'], 7));
  66              $conditions = array('token' => $tokenvalue);
  67              if (!empty($typeid)) {
  68                  $conditions['typeid'] = intval($typeid);
  69              }
  70              $token = $DB->get_record('lti_access_tokens', $conditions);
  71              if ($token) {
  72                  // Log token access.
  73                  $DB->set_field('lti_access_tokens', 'lastaccess', $now, array('id' => $token->id));
  74                  $permittedscopes = json_decode($token->scope);
  75                  if ((intval($token->validuntil) > $now) && !empty(array_intersect($scopes, $permittedscopes))) {
  76                      return intval($token->typeid);
  77                  }
  78              }
  79          }
  80      }
  81      return false;
  82  }
  83  
  84  function handle_oauth_body_post($oauthconsumerkey, $oauthconsumersecret, $body, $requestheaders = null) {
  85  
  86      if ($requestheaders == null) {
  87          $requestheaders = OAuthUtil::get_headers();
  88      }
  89  
  90      // Must reject application/x-www-form-urlencoded.
  91      if (isset($requestheaders['Content-type'])) {
  92          if ($requestheaders['Content-type'] == 'application/x-www-form-urlencoded' ) {
  93              throw new OAuthException("OAuth request body signing must not use application/x-www-form-urlencoded");
  94          }
  95      }
  96  
  97      if (isset($requestheaders['Authorization']) && (substr($requestheaders['Authorization'], 0, 6) == "OAuth ")) {
  98          $headerparameters = OAuthUtil::split_header($requestheaders['Authorization']);
  99          $oauthbodyhash = $headerparameters['oauth_body_hash'];
 100      }
 101  
 102      if ( ! isset($oauthbodyhash)  ) {
 103          throw new OAuthException("OAuth request body signing requires oauth_body_hash body");
 104      }
 105  
 106      // Verify the message signature.
 107      $store = new TrivialOAuthDataStore();
 108      $store->add_consumer($oauthconsumerkey, $oauthconsumersecret);
 109  
 110      $server = new OAuthServer($store);
 111  
 112      $method = new OAuthSignatureMethod_HMAC_SHA1();
 113      $server->add_signature_method($method);
 114      $request = OAuthRequest::from_request();
 115  
 116      try {
 117          $server->verify_request($request);
 118      } catch (\Exception $e) {
 119          $message = $e->getMessage();
 120          throw new OAuthException("OAuth signature failed: " . $message);
 121      }
 122  
 123      $postdata = $body;
 124  
 125      $hash = base64_encode(sha1($postdata, true));
 126  
 127      if ( $hash != $oauthbodyhash ) {
 128          throw new OAuthException("OAuth oauth_body_hash mismatch");
 129      }
 130  
 131      return $postdata;
 132  }