Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.
   1  <?php
   2  
   3  // This file is part of Moodle - http://moodle.org/
   4  //
   5  // Moodle is free software: you can redistribute it and/or modify
   6  // it under the terms of the GNU General Public License as published by
   7  // the Free Software Foundation, either version 3 of the License, or
   8  // (at your option) any later version.
   9  //
  10  // Moodle is distributed in the hope that it will be useful,
  11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13  // GNU General Public License for more details.
  14  //
  15  // You should have received a copy of the GNU General Public License
  16  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  17  
  18  /**
  19   * @package    moodlecore
  20   * @subpackage backup-factories
  21   * @copyright  2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  /**
  26   * Non instantiable helper class providing different restore checks
  27   *
  28   * This class contains various static methods available in order to easily
  29   * perform a bunch of restore architecture tests
  30   *
  31   * TODO: Finish phpdocs
  32   */
  33  abstract class restore_check {
  34  
  35      public static function check_courseid($courseid) {
  36          global $DB;
  37          // id must exist in course table
  38          if (! $DB->record_exists('course', array('id' => $courseid))) {
  39              throw new restore_controller_exception('restore_check_course_not_exists', $courseid);
  40          }
  41          return true;
  42      }
  43  
  44      public static function check_user($userid) {
  45          global $DB;
  46          // userid must exist in user table
  47          if (! $DB->record_exists('user', array('id' => $userid))) {
  48              throw new restore_controller_exception('restore_check_user_not_exists', $userid);
  49          }
  50          return true;
  51      }
  52  
  53      public static function check_security($restore_controller, $apply) {
  54          global $DB;
  55  
  56          if (! $restore_controller instanceof restore_controller) {
  57              throw new restore_controller_exception('restore_check_security_requires_restore_controller');
  58          }
  59          $restore_controller->log('checking plan security', backup::LOG_INFO);
  60  
  61          // Some handy vars
  62          $type     = $restore_controller->get_type();
  63          $mode     = $restore_controller->get_mode();
  64          $courseid = $restore_controller->get_courseid();
  65          $coursectx= context_course::instance($courseid);
  66          $userid   = $restore_controller->get_userid();
  67  
  68          // Note: all the checks along the function MUST be performed for $userid, that
  69          // is the user who "requested" the course restore, not current $USER at all!!
  70  
  71          // First of all, decide which caps/contexts are we going to check
  72          // for common backups (general, automated...) based exclusively
  73          // in the type (course, section, activity). And store them into
  74          // one capability => context array structure
  75          $typecapstocheck = array();
  76          switch ($type) {
  77              case backup::TYPE_1COURSE :
  78                  $typecapstocheck['moodle/restore:restorecourse'] = $coursectx;
  79                  break;
  80              case backup::TYPE_1SECTION :
  81                  $typecapstocheck['moodle/restore:restoresection'] = $coursectx;
  82                  break;
  83              case backup::TYPE_1ACTIVITY :
  84                  $typecapstocheck['moodle/restore:restoreactivity'] = $coursectx;
  85                  break;
  86              default :
  87                  throw new restore_controller_exception('restore_unknown_restore_type', $type);
  88          }
  89  
  90          // Now, if restore mode is hub or import, check userid has permissions for those modes
  91          // other modes will perform common checks only (restorexxxx capabilities in $typecapstocheck)
  92          switch ($mode) {
  93              case backup::MODE_IMPORT:
  94                  if (!has_capability('moodle/restore:restoretargetimport', $coursectx, $userid)) {
  95                      $a = new stdclass();
  96                      $a->userid = $userid;
  97                      $a->courseid = $courseid;
  98                      $a->capability = 'moodle/restore:restoretargetimport';
  99                      throw new restore_controller_exception('restore_user_missing_capability', $a);
 100                  }
 101                  break;
 102              // Common backup (general, automated...), let's check all the $typecapstocheck
 103              // capability => context pairs
 104              default:
 105                  foreach ($typecapstocheck as $capability => $context) {
 106                      if (!has_capability($capability, $context, $userid)) {
 107                          $a = new stdclass();
 108                          $a->userid = $userid;
 109                          $a->courseid = $courseid;
 110                          $a->capability = $capability;
 111                          throw new restore_controller_exception('restore_user_missing_capability', $a);
 112                      }
 113                  }
 114          }
 115  
 116          // Now, enforce 'moodle/restore:userinfo' to 'users' setting, applying changes if allowed,
 117          // else throwing exception
 118          $userssetting = $restore_controller->get_plan()->get_setting('users');
 119          $prevvalue    = $userssetting->get_value();
 120          $prevstatus   = $userssetting->get_status();
 121          $hasusercap   = has_capability('moodle/restore:userinfo', $coursectx, $userid);
 122  
 123          // If setting is enabled but user lacks permission
 124          if (!$hasusercap && $prevvalue) { // If user has not the capability and setting is enabled
 125              // Now analyse if we are allowed to apply changes or must stop with exception
 126              if (!$apply) { // Cannot apply changes, throw exception
 127                  $a = new stdclass();
 128                  $a->setting = 'users';
 129                  $a->value = $prevvalue;
 130                  $a->capability = 'moodle/restore:userinfo';
 131                  throw new restore_controller_exception('restore_setting_value_wrong_for_capability', $a);
 132  
 133              } else { // Can apply changes
 134                  $userssetting->set_value(false);                              // Set the value to false
 135                  $userssetting->set_status(base_setting::LOCKED_BY_PERMISSION);// Set the status to locked by perm
 136              }
 137          }
 138  
 139          // Now, if mode is HUB or IMPORT, and still we are including users in restore, turn them off
 140          // Defaults processing should have handled this, but we need to be 100% sure
 141          if ($mode == backup::MODE_IMPORT || $mode == backup::MODE_HUB) {
 142              $userssetting = $restore_controller->get_plan()->get_setting('users');
 143              if ($userssetting->get_value()) {
 144                  $userssetting->set_value(false);                              // Set the value to false
 145                  $userssetting->set_status(base_setting::LOCKED_BY_PERMISSION);// Set the status to locked by perm
 146              }
 147          }
 148  
 149          // Check the user has the ability to configure the restore. If not then we need
 150          // to lock all settings by permission so that no changes can be made. This does
 151          // not apply to the import facility, where all the activities (picked on backup)
 152          // are restored automatically without restore UI
 153          if ($mode != backup::MODE_IMPORT) {
 154              $hasconfigcap = has_capability('moodle/restore:configure', $coursectx, $userid);
 155              if (!$hasconfigcap) {
 156                  $settings = $restore_controller->get_plan()->get_settings();
 157                  foreach ($settings as $setting) {
 158                      $setting->set_status(base_setting::LOCKED_BY_PERMISSION);
 159                  }
 160              }
 161          }
 162  
 163          if ($type == backup::TYPE_1COURSE) {
 164              // Ensure the user has the rolldates capability. If not we want to lock this
 165              // settings so that they cannot change it.
 166              $hasrolldatescap = has_capability('moodle/restore:rolldates', $coursectx, $userid);
 167              if (!$hasrolldatescap) {
 168                  $startdatesetting = $restore_controller->get_plan()->get_setting('course_startdate');
 169                  if ($startdatesetting) {
 170                      $startdatesetting->set_status(base_setting::NOT_LOCKED); // Permission lock overrides config lock.
 171                      $startdatesetting->set_value(false);
 172                      $startdatesetting->set_status(base_setting::LOCKED_BY_PERMISSION);
 173                  }
 174              }
 175  
 176              // Ensure the user has the changefullname capability. If not we want to lock
 177              // the setting so that they cannot change it.
 178              $haschangefullnamecap = has_capability('moodle/course:changefullname', $coursectx, $userid);
 179              if (!$haschangefullnamecap) {
 180                  $fullnamesetting = $restore_controller->get_plan()->get_setting('course_fullname');
 181                  $fullnamesetting->set_status(base_setting::NOT_LOCKED); // Permission lock overrides config lock.
 182                  $fullnamesetting->set_value(false);
 183                  $fullnamesetting->set_status(base_setting::LOCKED_BY_PERMISSION);
 184              }
 185  
 186              // Ensure the user has the changeshortname capability. If not we want to lock
 187              // the setting so that they cannot change it.
 188              $haschangeshortnamecap = has_capability('moodle/course:changeshortname', $coursectx, $userid);
 189              if (!$haschangeshortnamecap) {
 190                  $shortnamesetting = $restore_controller->get_plan()->get_setting('course_shortname');
 191                  $shortnamesetting->set_status(base_setting::NOT_LOCKED); // Permission lock overrides config lock.
 192                  $shortnamesetting->set_value(false);
 193                  $shortnamesetting->set_status(base_setting::LOCKED_BY_PERMISSION);
 194              }
 195  
 196              // Ensure the user has the update capability. If not we want to lock
 197              // the overwrite setting so that they cannot change it.
 198              $hasupdatecap = has_capability('moodle/course:update', $coursectx, $userid);
 199              if (!$hasupdatecap) {
 200                  $overwritesetting = $restore_controller->get_plan()->get_setting('overwrite_conf');
 201                  $overwritesetting->set_status(base_setting::NOT_LOCKED); // Permission lock overrides config lock.
 202                  $overwritesetting->set_value(false);
 203                  $overwritesetting->set_status(base_setting::LOCKED_BY_PERMISSION);
 204              }
 205  
 206              // Ensure the user has the capability to manage enrolment methods. If not we want to unset and lock
 207              // the setting so that they cannot change it.
 208              $hasmanageenrolcap = has_capability('moodle/course:enrolconfig', $coursectx, $userid);
 209              if (!$hasmanageenrolcap) {
 210                  if ($restore_controller->get_plan()->setting_exists('enrolments')) {
 211                      $enrolsetting = $restore_controller->get_plan()->get_setting('enrolments');
 212                      if ($enrolsetting->get_value() != backup::ENROL_NEVER) {
 213                          $enrolsetting->set_status(base_setting::NOT_LOCKED); // In case it was locked earlier.
 214                          $enrolsetting->set_value(backup::ENROL_NEVER);
 215                      }
 216                      $enrolsetting->set_status(base_setting::LOCKED_BY_PERMISSION);
 217                  }
 218              }
 219          }
 220  
 221          return true;
 222      }
 223  }