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