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.
   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  namespace enrol_lti\local\ltiadvantage\lib;
  18  use enrol_lti\local\ltiadvantage\repository\application_registration_repository;
  19  use enrol_lti\local\ltiadvantage\repository\deployment_repository;
  20  use Packback\Lti1p3\Interfaces\IDatabase;
  21  use Packback\Lti1p3\LtiDeployment;
  22  use Packback\Lti1p3\LtiRegistration;
  23  
  24  /**
  25   * The issuer_database class, providing a read-only store of issuer details.
  26   *
  27   * @package    enrol_lti
  28   * @copyright  2021 Jake Dallimore <jrhdallimore@gmail.com>
  29   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  30   */
  31  class issuer_database implements IDatabase {
  32  
  33      /** @var application_registration_repository an application registration repository instance used for lookups.*/
  34      private $appregrepo;
  35  
  36      /** @var deployment_repository a deployment repository instance for lookups.*/
  37      private $deploymentrepo;
  38  
  39      /**
  40       * The issuer_database constructor.
  41       * @param application_registration_repository $appregrepo an application registration repository instance.
  42       * @param deployment_repository $deploymentrepo a deployment repository instance.
  43       */
  44      public function __construct(application_registration_repository $appregrepo,
  45              deployment_repository $deploymentrepo) {
  46  
  47          $this->appregrepo = $appregrepo;
  48          $this->deploymentrepo = $deploymentrepo;
  49      }
  50  
  51      /**
  52       * Find and return an LTI registration based on its unique {issuer, client_id} tuple.
  53       *
  54       * @param string $iss the issuer id.
  55       * @param string $clientId the client_id of the registration.
  56       * @return LtiRegistration|null The registration object, or null if not found.
  57       */
  58      public function findRegistrationByIssuer($iss, $clientId = null): ?LtiRegistration {
  59          if (is_null($clientId)) {
  60              throw new \coding_exception("The param 'clientid' is required. Calling code must either pass in 'client_id' ".
  61                  "(generated by the platform during registration) or 'id' (found in the initiate login URI created by the tool) ".
  62                  "to identify the client.");
  63          }
  64  
  65          global $CFG;
  66          require_once($CFG->libdir . '/moodlelib.php'); // For get_config() usage.
  67  
  68          // We can identify registrations two ways. Either:
  69          // 1. Using issuer + the platform-generated clientid. Most platforms will have sent client_id in the initiate login request
  70          // despite it being an optional param in the spec. They must include it as the aud value in the resource link request JWT.
  71          // 2. Using issuer + a tool-generated ID. This supports platforms which omit client_id during a login call. Using the ID
  72          // that is a part of the initiate login URI allows Moodle to locate the registration for that unique client, without the
  73          // platform-generated client_id.
  74          // Major platforms will likely include client_id in the login request, so favor that approach first, only falling back on
  75          // the local id approach where a registration cannot be found in the first instance.
  76          $reg = $this->appregrepo->find_by_platform($iss, $clientId);
  77          if (!$reg) {
  78              $reg = $this->appregrepo->find_by_platform_uniqueid($iss, $clientId);
  79          }
  80          if (!$reg) {
  81              return null;
  82          }
  83          $privatekey = get_config('enrol_lti', 'lti_13_privatekey');
  84          $kid = get_config('enrol_lti', 'lti_13_kid');
  85  
  86          return LtiRegistration::new()
  87              ->setAuthLoginUrl($reg->get_authenticationrequesturl()->out(false))
  88              ->setAuthTokenUrl($reg->get_accesstokenurl()->out(false))
  89              ->setClientId($reg->get_clientid())
  90              ->setKeySetUrl($reg->get_jwksurl()->out(false))
  91              ->setKid($kid)
  92              ->setIssuer($reg->get_platformid()->out(false))
  93              ->setToolPrivateKey($privatekey);
  94      }
  95  
  96      /**
  97       * Returns an LTI deployment based on the {issuer, client_id} tuple and a deployment id string.
  98       *
  99       * @param string $iss the issuer id.
 100       * @param string $deploymentId the deployment id.
 101       * @param string $clientId the client_id of the registration.
 102       * @return LtiDeployment|null The deployment object or null if not found.
 103       */
 104      public function findDeployment($iss, $deploymentId, $clientId = null): ?LtiDeployment {
 105          if (is_null($clientId)) {
 106              throw new \coding_exception("Both issuer and client id are required to identify platform registrations ".
 107                  "and must be included in the 'aud' claim of the message JWT.");
 108          }
 109  
 110          $appregistration = $this->appregrepo->find_by_platform($iss, $clientId);
 111          if (!$appregistration) {
 112              return null;
 113          }
 114          $deployment = $this->deploymentrepo->find_by_registration($appregistration->get_id(), $deploymentId);
 115          if (!$deployment) {
 116              return null;
 117          }
 118          return LtiDeployment::new()
 119              ->setDeploymentId($deployment->get_deploymentid());
 120      }
 121  }