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\repository;
  18  use enrol_lti\local\ltiadvantage\entity\application_registration;
  19  
  20  /**
  21   * Class application_registration_repository.
  22   *
  23   * @package    enrol_lti
  24   * @copyright 2021 Jake Dallimore <jrhdallimore@gmail.com>
  25   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  26   */
  27  class application_registration_repository {
  28  
  29      /** @var string $applicationregistrationtable the table containing application registrations. */
  30      private $applicationregistrationtable = 'enrol_lti_app_registration';
  31  
  32      /**
  33       * Create an application_registration instance from a record.
  34       *
  35       * @param \stdClass $record the record.
  36       * @return application_registration an application_registration instance.
  37       */
  38      private function application_registration_from_record(\stdClass $record): application_registration {
  39  
  40          if ($record->status == application_registration::REGISTRATION_STATUS_INCOMPLETE) {
  41              $appreg = application_registration::create_draft(
  42                  $record->name,
  43                  $record->uniqueid,
  44                  $record->id
  45              );
  46              if (!empty($record->platformid)) {
  47                  $appreg->set_platformid(new \moodle_url($record->platformid));
  48              }
  49              if (!empty($record->clientid)) {
  50                  $appreg->set_clientid($record->clientid);
  51              }
  52              if (!empty($record->authenticationrequesturl)) {
  53                  $appreg->set_authenticationrequesturl(new \moodle_url($record->authenticationrequesturl));
  54              }
  55              if (!empty($record->jwksurl)) {
  56                  $appreg->set_jwksurl(new \moodle_url($record->jwksurl));
  57              }
  58              if (!empty($record->accesstokenurl)) {
  59                  $appreg->set_accesstokenurl(new \moodle_url($record->accesstokenurl));
  60              }
  61          } else if ($record->status == application_registration::REGISTRATION_STATUS_COMPLETE) {
  62              $appreg = application_registration::create(
  63                  $record->name,
  64                  $record->uniqueid,
  65                  new \moodle_url($record->platformid),
  66                  $record->clientid,
  67                  new \moodle_url($record->authenticationrequesturl),
  68                  new \moodle_url($record->jwksurl),
  69                  new \moodle_url($record->accesstokenurl),
  70                  $record->id
  71              );
  72          }
  73          return $appreg;
  74      }
  75  
  76      /**
  77       * Get an array of application_registration instances from a set of records.
  78       *
  79       * @param \stdClass[] $records the array of records.
  80       * @return array|application_registration[] the array of object instances.
  81       */
  82      private function application_registrations_from_records(array $records): array {
  83          if (empty($records)) {
  84              return [];
  85          }
  86          return array_map(function($record) {
  87              return $this->application_registration_from_record($record);
  88          }, $records);
  89      }
  90  
  91      /**
  92       * Convert the application_registration object into a stdClass for use with the data store.
  93       *
  94       * @param application_registration $appregistration the app registration.
  95       * @return \stdClass the record.
  96       */
  97      private function record_from_application_registration(application_registration $appregistration): \stdClass {
  98          $appregistrationrecord = [
  99              'name' => $appregistration->get_name(),
 100              'uniqueid' => $appregistration->get_uniqueid(),
 101              'status' => $appregistration->is_complete() ? application_registration::REGISTRATION_STATUS_COMPLETE
 102                  : application_registration::REGISTRATION_STATUS_INCOMPLETE
 103          ];
 104  
 105          $platformid = $appregistration->get_platformid();
 106          $clientid = $appregistration->get_clientid();
 107          $authrequesturl = $appregistration->get_authenticationrequesturl();
 108          $jwksurl = $appregistration->get_jwksurl();
 109          $accesstokenurl = $appregistration->get_accesstokenurl();
 110  
 111          $appregistrationrecord['platformid'] = !is_null($platformid) ? $platformid->out(false) : null;
 112          $appregistrationrecord['clientid'] = $clientid;
 113          $appregistrationrecord['authenticationrequesturl'] = !is_null($authrequesturl) ? $authrequesturl->out(false) : null;
 114          $appregistrationrecord['jwksurl'] = !is_null($jwksurl) ? $jwksurl->out(false) : null;
 115          $appregistrationrecord['accesstokenurl'] = !is_null($accesstokenurl) ? $accesstokenurl->out(false) : null;
 116  
 117          if ($platformid && $clientid) {
 118              $indexhash = $this->get_unique_index_hash($appregistration->get_platformid()->out(false),
 119                  $appregistration->get_clientid());
 120              $appregistrationrecord['platformclienthash'] = $indexhash;
 121          }
 122  
 123          if ($platformid) {
 124              $indexhash = $this->get_unique_index_hash($appregistration->get_platformid()->out(false),
 125                  $appregistration->get_uniqueid());
 126              $appregistrationrecord['platformuniqueidhash'] = $indexhash;
 127          }
 128  
 129          if ($id = $appregistration->get_id()) {
 130              $appregistrationrecord['id'] = $id;
 131          }
 132  
 133          return (object) $appregistrationrecord;
 134      }
 135  
 136      /**
 137       * Gets a hash of the {platformid, clientid} tuple for use in indexing purposes.
 138       *
 139       * @param string $platformid the platformid of the registration.
 140       * @param string $clientid the clientid of the registration
 141       * @return string a SHA256 hash.
 142       */
 143      private function get_unique_index_hash(string $platformid, string $clientid): string {
 144          return hash('sha256', $platformid . ':' . $clientid);
 145      }
 146  
 147      /**
 148       * Find a registration by id.
 149       *
 150       * @param int $id the id of the application registration.
 151       * @return null|application_registration the registration object if found, otherwise null.
 152       */
 153      public function find(int $id): ?application_registration {
 154          global $DB;
 155          try {
 156              $record = $DB->get_record($this->applicationregistrationtable, ['id' => $id], '*', MUST_EXIST);
 157              return $this->application_registration_from_record($record);
 158          } catch (\dml_missing_record_exception $e) {
 159              return null;
 160          }
 161      }
 162  
 163      /**
 164       * Get all app registrations in the repository.
 165       *
 166       * @return application_registration[] the array of application registration instances.
 167       */
 168      public function find_all(): array {
 169          global $DB;
 170          return $this->application_registrations_from_records($DB->get_records($this->applicationregistrationtable));
 171      }
 172  
 173      /**
 174       * Find a registration by its unique {platformid, uniqueid} tuple.
 175       *
 176       * @param string $platformid the url of the platform (the issuer).
 177       * @param string $uniqueid the locally uniqueid of the tool registration.
 178       * @return application_registration|null application registration instance if found, else null.
 179       */
 180      public function find_by_platform_uniqueid(string $platformid, string $uniqueid): ?application_registration {
 181          global $DB;
 182          try {
 183              $indexhash = $this->get_unique_index_hash($platformid, $uniqueid);
 184              $record = $DB->get_record($this->applicationregistrationtable, ['platformuniqueidhash' => $indexhash], '*',
 185                  MUST_EXIST);
 186              return $this->application_registration_from_record($record);
 187          } catch (\dml_missing_record_exception $e) {
 188              return null;
 189          }
 190      }
 191  
 192      /**
 193       * Find a registration by its uniqueid.
 194       *
 195       * @param string $uniqueid the uniqueid identifying the registration.
 196       * @return application_registration|null application_registration instance if found, else null.
 197       */
 198      public function find_by_uniqueid(string $uniqueid): ?application_registration {
 199          global $DB;
 200          try {
 201              $record = $DB->get_record($this->applicationregistrationtable, ['uniqueid' => $uniqueid], '*', MUST_EXIST);
 202              return $this->application_registration_from_record($record);
 203          } catch (\dml_missing_record_exception $e) {
 204              return null;
 205          }
 206      }
 207  
 208      /**
 209       * Find a registration by its unique {platformid, clientid} tuple.
 210       *
 211       * @param string $platformid the url of the platform (the issuer).
 212       * @param string $clientid the client_id of the tool registration on the platform.
 213       * @return application_registration|null application registration instance if found, else null.
 214       */
 215      public function find_by_platform(string $platformid, string $clientid): ?application_registration {
 216          global $DB;
 217          try {
 218              $indexhash = $this->get_unique_index_hash($platformid, $clientid);
 219              $record = $DB->get_record($this->applicationregistrationtable, ['platformclienthash' => $indexhash], '*',
 220                  MUST_EXIST);
 221              return $this->application_registration_from_record($record);
 222          } catch (\dml_missing_record_exception $e) {
 223              return null;
 224          }
 225      }
 226  
 227      /**
 228       * Find an application_registration corresponding to the local id of a given tool deployment.
 229       *
 230       * @param int $deploymentid the local id of the tool deployment object.
 231       * @return application_registration|null the application_registration instance or null if not found.
 232       */
 233      public function find_by_deployment(int $deploymentid): ?application_registration {
 234          global $DB;
 235          try {
 236              $sql = "SELECT a.id, a.name, a.platformid, a.clientid, a.authenticationrequesturl, a.jwksurl,
 237                             a.accesstokenurl, a.uniqueid, a.status, a.timecreated, a.timemodified
 238                        FROM {enrol_lti_app_registration} a
 239                        JOIN {enrol_lti_deployment} d
 240                          ON (d.platformid = a.id)
 241                       WHERE d.id = :id";
 242              $record = $DB->get_record_sql($sql, ['id' => $deploymentid], MUST_EXIST);
 243              return $this->application_registration_from_record($record);
 244          } catch (\dml_missing_record_exception $e) {
 245              return null;
 246          }
 247      }
 248  
 249      /**
 250       * Save an application_registration instance to the store.
 251       *
 252       * @param application_registration $appregistration the application registration instance.
 253       * @return application_registration the saved application registration instance.
 254       */
 255      public function save(application_registration $appregistration): application_registration {
 256          global $DB;
 257          $id = $appregistration->get_id();
 258          $exists = $id ? $this->exists($id) : false;
 259  
 260          $record = $this->record_from_application_registration($appregistration);
 261          $timenow = time();
 262          if ($exists) {
 263              $record->timemodified = $timenow;
 264              $DB->update_record($this->applicationregistrationtable, $record);
 265          } else {
 266              $record->timecreated = $record->timemodified = $timenow;
 267              $appregid = $DB->insert_record($this->applicationregistrationtable, $record);
 268              $record->id = $appregid;
 269          }
 270  
 271          return $this->application_registration_from_record($record);
 272      }
 273  
 274      /**
 275       * Report whether an application_registration with id $id exists or not.
 276       *
 277       * @param int $appregid the id of the application_registration
 278       * @return bool true if the object exists, false otherwise.
 279       */
 280      public function exists(int $appregid): bool {
 281          global $DB;
 282          return $DB->record_exists($this->applicationregistrationtable, ['id' => $appregid]);
 283      }
 284  
 285      /**
 286       * Delete the application_registration identified by id.
 287       *
 288       * @param int $id the id of the object to delete.
 289       */
 290      public function delete(int $id): void {
 291          global $DB;
 292          $DB->delete_records($this->applicationregistrationtable, ['id' => $id]);
 293      }
 294  }