See Release Notes
Long Term Support Release
Differences Between: [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 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 /** 18 * @package core_backup 19 * @category phpunit 20 * @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com} 21 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 22 */ 23 24 defined('MOODLE_INTERNAL') || die(); 25 26 // Include all the needed stuff 27 global $CFG; 28 require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php'); 29 30 /** 31 * Restore dbops tests (all). 32 */ 33 class restore_dbops_testcase extends advanced_testcase { 34 35 /** 36 * Verify the xxx_ids_cached (in-memory backup_ids cache) stuff works as expected. 37 * 38 * Note that those private implementations are tested here by using the public 39 * backup_ids API and later performing low-level tests. 40 */ 41 public function test_backup_ids_cached() { 42 global $DB; 43 $dbman = $DB->get_manager(); // We are going to use database_manager services. 44 45 $this->resetAfterTest(true); // Playing with temp tables, better reset once finished. 46 47 // Some variables and objects for testing. 48 $restoreid = 'testrestoreid'; 49 50 $mapping = new stdClass(); 51 $mapping->itemname = 'user'; 52 $mapping->itemid = 1; 53 $mapping->newitemid = 2; 54 $mapping->parentitemid = 3; 55 $mapping->info = 'info'; 56 57 // Create the backup_ids temp tables used by restore. 58 restore_controller_dbops::create_restore_temp_tables($restoreid); 59 60 // Send one mapping using the public api with defaults. 61 restore_dbops::set_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid); 62 // Get that mapping and verify everything is returned as expected. 63 $result = restore_dbops::get_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid); 64 $this->assertSame($mapping->itemname, $result->itemname); 65 $this->assertSame($mapping->itemid, $result->itemid); 66 $this->assertSame(0, $result->newitemid); 67 $this->assertSame(null, $result->parentitemid); 68 $this->assertSame(null, $result->info); 69 70 // Drop the backup_xxx_temp temptables manually, so memory cache won't be invalidated. 71 $dbman->drop_table(new xmldb_table('backup_ids_temp')); 72 $dbman->drop_table(new xmldb_table('backup_files_temp')); 73 74 // Verify the mapping continues returning the same info, 75 // now from cache (the table does not exist). 76 $result = restore_dbops::get_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid); 77 $this->assertSame($mapping->itemname, $result->itemname); 78 $this->assertSame($mapping->itemid, $result->itemid); 79 $this->assertSame(0, $result->newitemid); 80 $this->assertSame(null, $result->parentitemid); 81 $this->assertSame(null, $result->info); 82 83 // Recreate the temp table, just to drop it using the restore API in 84 // order to check that, then, the cache becomes invalid for the same request. 85 restore_controller_dbops::create_restore_temp_tables($restoreid); 86 restore_controller_dbops::drop_restore_temp_tables($restoreid); 87 88 // No cached info anymore, so the mapping request will arrive to 89 // DB leading to error (temp table does not exist). 90 try { 91 $result = restore_dbops::get_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid); 92 $this->fail('Expecting an exception, none occurred'); 93 } catch (Exception $e) { 94 $this->assertTrue($e instanceof dml_exception); 95 $this->assertSame('Table "backup_ids_temp" does not exist', $e->getMessage()); 96 } 97 98 // Create the backup_ids temp tables once more. 99 restore_controller_dbops::create_restore_temp_tables($restoreid); 100 101 // Send one mapping using the public api with complete values. 102 restore_dbops::set_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid, 103 $mapping->newitemid, $mapping->parentitemid, $mapping->info); 104 // Get that mapping and verify everything is returned as expected. 105 $result = restore_dbops::get_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid); 106 $this->assertSame($mapping->itemname, $result->itemname); 107 $this->assertSame($mapping->itemid, $result->itemid); 108 $this->assertSame($mapping->newitemid, $result->newitemid); 109 $this->assertSame($mapping->parentitemid, $result->parentitemid); 110 $this->assertSame($mapping->info, $result->info); 111 112 // Finally, drop the temp tables properly and get the DB error again (memory caches empty). 113 restore_controller_dbops::drop_restore_temp_tables($restoreid); 114 try { 115 $result = restore_dbops::get_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid); 116 $this->fail('Expecting an exception, none occurred'); 117 } catch (Exception $e) { 118 $this->assertTrue($e instanceof dml_exception); 119 $this->assertSame('Table "backup_ids_temp" does not exist', $e->getMessage()); 120 } 121 } 122 123 /** 124 * Data provider for {@link test_precheck_user()} 125 */ 126 public function precheck_user_provider() { 127 128 $emailmultiplier = [ 129 'shortmail' => 'normalusername@example.com', 130 'longmail' => str_repeat('a', 100) // It's not validated, hence any string is ok. 131 ]; 132 133 $providercases = []; 134 135 foreach ($emailmultiplier as $emailk => $email) { 136 // Get the related cases. 137 $cases = $this->precheck_user_cases($email); 138 // Rename them (keys). 139 foreach ($cases as $key => $case) { 140 $providercases[$key . ' - ' . $emailk] = $case; 141 } 142 } 143 144 return $providercases; 145 } 146 147 /** 148 * Get all the cases implemented in {@link restore_dbops::precheck_users()} 149 * 150 * @param string $email 151 */ 152 private function precheck_user_cases($email) { 153 global $CFG; 154 155 $baseuserarr = [ 156 'username' => 'normalusername', 157 'email' => $email, 158 'mnethostid' => $CFG->mnet_localhost_id, 159 'firstaccess' => 123456789, 160 'deleted' => 0, 161 'forceemailcleanup' => false, // Hack to force the DB record to have empty mail. 162 'forceduplicateadminallowed' => false]; // Hack to enable import_general_duplicate_admin_allowed. 163 164 return [ 165 // Cases with samesite = true. 166 'samesite match existing (1A)' => [ 167 'dbuser' => $baseuserarr, 168 'backupuser' => $baseuserarr, 169 'samesite' => true, 170 'outcome' => 'match' 171 ], 172 'samesite match existing anon (1B)' => [ 173 'dbuser' => array_merge($baseuserarr, [ 174 'username' => 'anon01']), 175 'backupuser' => array_merge($baseuserarr, [ 176 'id' => -1, 'username' => 'anon01', 'firstname' => 'anonfirstname01', 177 'lastname' => 'anonlastname01', 'email' => 'anon01@doesntexist.invalid']), 178 'samesite' => true, 179 'outcome' => 'match' 180 ], 181 'samesite match existing deleted in db, alive in backup, by db username (1C)' => [ 182 'dbuser' => array_merge($baseuserarr, [ 183 'deleted' => 1]), 184 'backupuser' => array_merge($baseuserarr, [ 185 'username' => 'this_wont_match']), 186 'samesite' => true, 187 'outcome' => 'match' 188 ], 189 'samesite match existing deleted in db, alive in backup, by db email (1C)' => [ 190 'dbuser' => array_merge($baseuserarr, [ 191 'deleted' => 1]), 192 'backupuser' => array_merge($baseuserarr, [ 193 'email' => 'this_wont_match']), 194 'samesite' => true, 195 'outcome' => 'match' 196 ], 197 'samesite match existing alive in db, deleted in backup (1D)' => [ 198 'dbuser' => $baseuserarr, 199 'backupuser' => array_merge($baseuserarr, [ 200 'deleted' => 1]), 201 'samesite' => true, 202 'outcome' => 'match' 203 ], 204 'samesite conflict (1E)' => [ 205 'dbuser' => $baseuserarr, 206 'backupuser' => array_merge($baseuserarr, ['id' => -1]), 207 'samesite' => true, 208 'outcome' => false 209 ], 210 'samesite create user (1F)' => [ 211 'dbuser' => $baseuserarr, 212 'backupuser' => array_merge($baseuserarr, [ 213 'username' => 'newusername']), 214 'samesite' => false, 215 'outcome' => true 216 ], 217 218 // Cases with samesite = false. 219 'no samesite match existing, by db email (2A1)' => [ 220 'dbuser' => $baseuserarr, 221 'backupuser' => array_merge($baseuserarr, [ 222 'firstaccess' => 0]), 223 'samesite' => false, 224 'outcome' => 'match' 225 ], 226 'no samesite match existing, by db firstaccess (2A1)' => [ 227 'dbuser' => $baseuserarr, 228 'backupuser' => array_merge($baseuserarr, [ 229 'email' => 'this_wont_match@example.con']), 230 'samesite' => false, 231 'outcome' => 'match' 232 ], 233 'no samesite match existing anon (2A1 too)' => [ 234 'dbuser' => array_merge($baseuserarr, [ 235 'username' => 'anon01']), 236 'backupuser' => array_merge($baseuserarr, [ 237 'id' => -1, 'username' => 'anon01', 'firstname' => 'anonfirstname01', 238 'lastname' => 'anonlastname01', 'email' => 'anon01@doesntexist.invalid']), 239 'samesite' => false, 240 'outcome' => 'match' 241 ], 242 'no samesite match dupe admin (2A2)' => [ 243 'dbuser' => array_merge($baseuserarr, [ 244 'username' => 'admin_old_site_id', 245 'forceduplicateadminallowed' => true]), 246 'backupuser' => array_merge($baseuserarr, [ 247 'username' => 'admin']), 248 'samesite' => false, 249 'outcome' => 'match' 250 ], 251 'no samesite match existing deleted in db, alive in backup, by db username (2B1)' => [ 252 'dbuser' => array_merge($baseuserarr, [ 253 'deleted' => 1]), 254 'backupuser' => array_merge($baseuserarr, [ 255 'firstaccess' => 0]), 256 'samesite' => false, 257 'outcome' => 'match' 258 ], 259 'no samesite match existing deleted in db, alive in backup, by db firstaccess (2B1)' => [ 260 'dbuser' => array_merge($baseuserarr, [ 261 'deleted' => 1]), 262 'backupuser' => array_merge($baseuserarr, [ 263 'mail' => 'this_wont_match']), 264 'samesite' => false, 265 'outcome' => 'match' 266 ], 267 'no samesite match existing deleted in db, alive in backup (2B2)' => [ 268 'dbuser' => array_merge($baseuserarr, [ 269 'deleted' => 1, 270 'forceemailcleanup' => true]), 271 'backupuser' => $baseuserarr, 272 'samesite' => false, 273 'outcome' => 'match' 274 ], 275 'no samesite match existing alive in db, deleted in backup (2C)' => [ 276 'dbuser' => $baseuserarr, 277 'backupuser' => array_merge($baseuserarr, [ 278 'deleted' => 1]), 279 'samesite' => false, 280 'outcome' => 'match' 281 ], 282 'no samesite conflict (2D)' => [ 283 'dbuser' => $baseuserarr, 284 'backupuser' => array_merge($baseuserarr, [ 285 'email' => 'anotheruser@example.com', 'firstaccess' => 0]), 286 'samesite' => false, 287 'outcome' => false 288 ], 289 'no samesite create user (2E)' => [ 290 'dbuser' => $baseuserarr, 291 'backupuser' => array_merge($baseuserarr, [ 292 'username' => 'newusername']), 293 'samesite' => false, 294 'outcome' => true 295 ], 296 297 ]; 298 } 299 300 /** 301 * Test restore precheck_user method 302 * 303 * @dataProvider precheck_user_provider 304 * @covers \restore_dbops::precheck_user() 305 * 306 * @param array $dbuser 307 * @param array $backupuser 308 * @param bool $samesite 309 * @param mixed $outcome 310 **/ 311 public function test_precheck_user($dbuser, $backupuser, $samesite, $outcome) { 312 global $DB; 313 314 $this->resetAfterTest(); 315 316 $dbuser = (object)$dbuser; 317 $backupuser = (object)$backupuser; 318 319 $siteid = null; 320 321 // If the backup user must be deleted, simulate it (by temp inserting to DB, deleting and fetching it back). 322 if ($backupuser->deleted) { 323 $backupuser->id = $DB->insert_record('user', array_merge((array)$backupuser, ['deleted' => 0])); 324 delete_user($backupuser); 325 $backupuser = $DB->get_record('user', ['id' => $backupuser->id]); 326 $DB->delete_records('user', ['id' => $backupuser->id]); 327 unset($backupuser->id); 328 } 329 330 // Create the db user, normally. 331 $dbuser->id = $DB->insert_record('user', array_merge((array)$dbuser, ['deleted' => 0])); 332 $backupuser->id = $backupuser->id ?? $dbuser->id; 333 334 // We may want to enable the import_general_duplicate_admin_allowed setting and look for old admin records. 335 if ($dbuser->forceduplicateadminallowed) { 336 set_config('import_general_duplicate_admin_allowed', true, 'backup'); 337 $siteid = 'old_site_id'; 338 } 339 340 // If the DB user must be deleted, do it and fetch it back. 341 if ($dbuser->deleted) { 342 delete_user($dbuser); 343 // We may want to clean the mail field (old behavior, not containing the current md5(username). 344 if ($dbuser->forceemailcleanup) { 345 $DB->set_field('user', 'email', '', ['id' => $dbuser->id]); 346 } 347 } 348 349 // Get the dbuser record, because we may have changed it above. 350 $dbuser = $DB->get_record('user', ['id' => $dbuser->id]); 351 352 $method = (new ReflectionClass('restore_dbops'))->getMethod('precheck_user'); 353 $method->setAccessible(true); 354 $result = $method->invoke(null, $backupuser, $samesite, $siteid); 355 356 if (is_bool($result)) { 357 $this->assertSame($outcome, $result); 358 } else { 359 $outcome = $dbuser; // Outcome is not bool, matching found, so it must be the dbuser, 360 // Just check ids, it means the expected match has been found in database. 361 $this->assertSame($outcome->id, $result->id); 362 } 363 } 364 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body