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 401 and 402] [Versions 401 and 403]

   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 quizaccess_seb\external;
  18  
  19  defined('MOODLE_INTERNAL') || die();
  20  
  21  global $CFG;
  22  
  23  use external_api;
  24  use external_function_parameters;
  25  use external_single_structure;
  26  use external_value;
  27  use invalid_parameter_exception;
  28  use quiz;
  29  use quizaccess_seb\event\access_prevented;
  30  use quizaccess_seb\access_manager;
  31  
  32  require_once($CFG->dirroot . '/mod/quiz/accessmanager.php');
  33  require_once($CFG->dirroot . '/mod/quiz/attemptlib.php');
  34  require_once($CFG->libdir . '/externallib.php');
  35  
  36  /**
  37   * Validate browser exam key and config key.
  38   *
  39   * @package    quizaccess_seb
  40   * @author     Andrew Madden <andrewmadden@catalyst-au.net>
  41   * @copyright  2021 Catalyst IT
  42   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  43   */
  44  class validate_quiz_keys extends external_api {
  45  
  46      /**
  47       * External function parameters.
  48       *
  49       * @return external_function_parameters
  50       */
  51      public static function execute_parameters(): external_function_parameters {
  52          return new external_function_parameters([
  53             'cmid' => new external_value(PARAM_INT, 'Course module ID',
  54                  VALUE_REQUIRED, null, NULL_NOT_ALLOWED),
  55             'url' => new external_value(PARAM_URL, 'Page URL to check',
  56                  VALUE_REQUIRED, null, NULL_NOT_ALLOWED),
  57             'configkey' => new external_value(PARAM_ALPHANUMEXT, 'SEB config key',
  58                  VALUE_DEFAULT, null),
  59             'browserexamkey' => new external_value(PARAM_ALPHANUMEXT, 'SEB browser exam key',
  60                  VALUE_DEFAULT, null),
  61          ]);
  62      }
  63  
  64      /**
  65       * Validate a SEB config key or browser exam key.
  66       *
  67       * @param string $cmid Course module ID.
  68       * @param string $url URL of the page on which the SEB JS API generated the keys.
  69       * @param string|null $configkey A SEB config key hash. Includes URL in the hash.
  70       * @param string|null $browserexamkey A SEB browser exam key hash. Includes the URL in the hash.
  71       * @return array
  72       */
  73      public static function execute(string $cmid, string $url, ?string $configkey = null, ?string $browserexamkey = null): array {
  74          list(
  75                  'cmid' => $cmid,
  76                  'url' => $url,
  77                  'configkey' => $configkey,
  78                  'browserexamkey' => $browserexamkey
  79              ) = self::validate_parameters(self::execute_parameters(), [
  80              'cmid' => $cmid,
  81              'url' => $url,
  82              'configkey' => $configkey,
  83              'browserexamkey' => $browserexamkey,
  84          ]);
  85  
  86          self::validate_context(\context_module::instance($cmid));
  87  
  88          // At least one SEB key must be provided.
  89          if (empty($configkey) && empty($browserexamkey)) {
  90              throw new invalid_parameter_exception(get_string('error:ws:nokeyprovided', 'quizaccess_seb'));
  91          }
  92  
  93          // Check quiz exists corresponding to cmid.
  94          if (($quizid = self::get_quiz_id($cmid)) === 0) {
  95              throw new invalid_parameter_exception(get_string('error:ws:quiznotexists', 'quizaccess_seb', $cmid));
  96          }
  97  
  98          $result = ['configkey' => true, 'browserexamkey' => true];
  99  
 100          $accessmanager = new access_manager(quiz::create($quizid));
 101  
 102          // Check if there is a valid config key.
 103          if (!$accessmanager->validate_config_key($configkey, $url)) {
 104              access_prevented::create_strict($accessmanager, get_string('invalid_config_key', 'quizaccess_seb'),
 105                      $configkey, $browserexamkey)->trigger();
 106              $result['configkey'] = false;
 107          }
 108  
 109          // Check if there is a valid browser exam key.
 110          if (!$accessmanager->validate_browser_exam_key($browserexamkey, $url)) {
 111              access_prevented::create_strict($accessmanager, get_string('invalid_browser_key', 'quizaccess_seb'),
 112                      $configkey, $browserexamkey)->trigger();
 113              $result['browserexamkey'] = false;
 114          }
 115  
 116          if ($result['configkey'] && $result['browserexamkey']) {
 117              // Set the state of the access for this Moodle session.
 118              $accessmanager->set_session_access(true);
 119          }
 120  
 121          return $result;
 122      }
 123  
 124      /**
 125       * External function returns.
 126       *
 127       * @return external_single_structure
 128       */
 129      public static function execute_returns(): external_single_structure {
 130          return new external_single_structure([
 131              'configkey' => new external_value(PARAM_BOOL, 'Is a provided config key valid?',
 132                      VALUE_REQUIRED, 0, NULL_NOT_ALLOWED),
 133              'browserexamkey' => new external_value(PARAM_BOOL, 'Is a provided browser exam key valid?',
 134                  VALUE_REQUIRED, 0, NULL_NOT_ALLOWED)
 135          ]);
 136      }
 137  
 138      /**
 139       * Check if there is a valid quiz corresponding to a course module it.
 140       *
 141       * @param string $cmid Course module ID.
 142       * @return int Returns quiz id if cmid matches valid quiz, or 0 if there is no match.
 143       */
 144      private static function get_quiz_id(string $cmid): int {
 145          $quizid = 0;
 146  
 147          $coursemodule = get_coursemodule_from_id('quiz', $cmid);
 148          if (!empty($coursemodule)) {
 149              $quizid = $coursemodule->instance;
 150          }
 151  
 152          return $quizid;
 153      }
 154  }
 155