Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
     */
11 efrain 42
    public function test_backup_ids_cached(): void {
1 efrain 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
     **/
11 efrain 312
    public function test_precheck_user($dbuser, $backupuser, $samesite, $outcome): void {
1 efrain 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
        $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
}