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 17defined('MOODLE_INTERNAL') || die(); 18 19// Include all the needed stuff. 20global $CFG; 21require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php'); 22require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php'); 23 24 25/** 26 * Unit tests for how backup and restore handles role-related things. 27 * 28 * @package core_backup 29 * @copyright 2021 The Open University 30 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 31 */ 32class roles_backup_restore_test extends advanced_testcase { 33 34 /** 35 * Create a course where the (non-editing) Teacher role is overridden 36 * to have 'moodle/user:loginas' and 'moodle/site:accessallgroups'. 37 * 38 * @return stdClass the new course. 39 */ 40 protected function create_course_with_role_overrides(): stdClass { 41 $generator = $this->getDataGenerator(); 42 $course = $generator->create_course(); 43 $teacher = $generator->create_user(); 44 45 $context = context_course::instance($course->id); 46 $generator->enrol_user($teacher->id, $course->id, 'teacher'); 47 48 $editingteacherrole = $this->get_role('teacher'); 49 role_change_permission($editingteacherrole->id, $context, 'moodle/user:loginas', CAP_ALLOW); 50 role_change_permission($editingteacherrole->id, $context, 'moodle/site:accessallgroups', CAP_ALLOW); 51 52 return $course; 53 } 54 55 /** 56 * Get the role id from a shortname. 57 * 58 * @param string $shortname the role shortname. 59 * @return stdClass the role from the DB. 60 */ 61 protected function get_role(string $shortname): stdClass { 62 global $DB; 63 return $DB->get_record('role', ['shortname' => $shortname]); 64 } 65 66 /** 67 * Get an array capability => CAP_... constant for all the orverrides set for a given role on a given context. 68 * 69 * @param string $shortname role shortname. 70 * @param context $context context. 71 * @return array the overrides set here. 72 */ 73 protected function get_overrides_for_role_on_context(string $shortname, context $context): array { 74 $overridedata = get_capabilities_from_role_on_context($this->get_role($shortname), $context); 75 $overrides = []; 76 foreach ($overridedata as $override) { 77 $overrides[$override->capability] = $override->permission; 78 } 79 return $overrides; 80 } 81 82 /** 83 * Makes a backup of the course. 84 * 85 * @param stdClass $course The course object. 86 * @return string Unique identifier for this backup. 87 */ 88 protected function backup_course(\stdClass $course): string { 89 global $CFG, $USER; 90 91 // Turn off file logging, otherwise it can't delete the file (Windows). 92 $CFG->backup_file_logger_level = backup::LOG_NONE; 93 94 // Do backup with default settings. MODE_IMPORT means it will just 95 // create the directory and not zip it. 96 $bc = new \backup_controller(backup::TYPE_1COURSE, $course->id, 97 backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_IMPORT, 98 $USER->id); 99 $backupid = $bc->get_backupid(); 100 $bc->execute_plan(); 101 $bc->destroy(); 102 103 return $backupid; 104 } 105 106 /** 107 * Restores a backup that has been made earlier. 108 * 109 * @param string $backupid The unique identifier of the backup. 110 * @param string $asroleshortname Which role in the new cousre the restorer should have. 111 * @return int The new course id. 112 */ 113 protected function restore_adding_to_course(string $backupid, string $asroleshortname): int { 114 global $CFG, $USER; 115 116 // Create course to restore into, and a user to do the restore. 117 $generator = $this->getDataGenerator(); 118 $course = $generator->create_course(); 119 $restorer = $generator->create_user(); 120 121 $generator->enrol_user($restorer->id, $course->id, $asroleshortname); 122 $this->setUser($restorer); 123 124 // Turn off file logging, otherwise it can't delete the file (Windows). 125 $CFG->backup_file_logger_level = backup::LOG_NONE; 126 127 // Do restore to new course with default settings. 128 $rc = new \restore_controller($backupid, $course->id, 129 backup::INTERACTIVE_NO, backup::MODE_GENERAL, $USER->id, 130 backup::TARGET_CURRENT_ADDING); 131 132 $precheck = $rc->execute_precheck(); 133 $this->assertTrue($precheck); 134 $rc->get_plan()->get_setting('role_assignments')->set_value(true); 135 $rc->execute_plan(); 136 $rc->destroy(); 137 138 return $course->id; 139 } 140 141 public function test_restore_role_overrides_as_manager(): void { 142 $this->resetAfterTest(); 143 $this->setAdminUser(); 144 145 // Create a course and back it up. 146 $course = $this->create_course_with_role_overrides(); 147 $backupid = $this->backup_course($course); 148 149 // When manager restores, both role overrides should be restored. 150 $newcourseid = $this->restore_adding_to_course($backupid, 'manager'); 151 152 // Verify. 153 $overrides = $this->get_overrides_for_role_on_context('teacher', 154 context_course::instance($newcourseid)); 155 $this->assertArrayHasKey('moodle/user:loginas', $overrides); 156 $this->assertEquals(CAP_ALLOW, $overrides['moodle/user:loginas']); 157 $this->assertArrayHasKey('moodle/site:accessallgroups', $overrides); 158 $this->assertEquals(CAP_ALLOW, $overrides['moodle/site:accessallgroups']); 159 } 160 161 public function test_restore_role_overrides_as_teacher(): void { 162 $this->resetAfterTest(); 163 $this->setAdminUser(); 164 165 // Create a course and back it up. 166 $course = $this->create_course_with_role_overrides(); 167 $backupid = $this->backup_course($course); 168 169 // When teacher restores, only the safe override should be restored. 170 $newcourseid = $this->restore_adding_to_course($backupid, 'editingteacher'); 171 172 // Verify. 173 $overrides = $this->get_overrides_for_role_on_context('teacher', 174 context_course::instance($newcourseid)); 175 $this->assertArrayNotHasKey('moodle/user:loginas', $overrides); 176 $this->assertArrayHasKey('moodle/site:accessallgroups', $overrides); 177 $this->assertEquals(CAP_ALLOW, $overrides['moodle/site:accessallgroups']); 178 } 179} 180