Proyectos de Subversion Moodle

Rev

Rev 11 | | 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/>.
1441 ariadna 16
 
1 efrain 17
namespace core_user\privacy;
18
 
1441 ariadna 19
use core\tests\session\mock_handler;
20
use core_privacy\tests\provider_testcase;
21
use core_privacy\local\request\approved_userlist;
22
use core_privacy\local\request\transform;
23
use core_user\privacy\provider;
1 efrain 24
 
25
/**
26
 * Unit tests for core_user.
27
 *
1441 ariadna 28
 * @package core_user
1 efrain 29
 * @copyright  2018 Adrian Greeve <adrian@moodle.com>
30
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1441 ariadna 31
 * @covers \core_user\privacy\provider
1 efrain 32
 */
1441 ariadna 33
final class provider_test extends provider_testcase {
34
    public static function setUpBeforeClass(): void {
35
        global $CFG;
36
        parent::setUpBeforeClass();
1 efrain 37
 
1441 ariadna 38
        require_once($CFG->dirroot . "/user/lib.php");
39
    }
40
 
1 efrain 41
    /**
42
     * Check that context information is returned correctly.
43
     */
11 efrain 44
    public function test_get_contexts_for_userid(): void {
1 efrain 45
        $this->resetAfterTest();
46
        $user = $this->getDataGenerator()->create_user();
47
        // Create some other users as well.
48
        $user2 = $this->getDataGenerator()->create_user();
49
        $user3 = $this->getDataGenerator()->create_user();
50
 
51
        $context = \context_user::instance($user->id);
52
        $contextlist = provider::get_contexts_for_userid($user->id);
53
        $this->assertSame($context, $contextlist->current());
54
    }
55
 
56
    /**
57
     * Test that data is exported as expected for a user.
58
     */
11 efrain 59
    public function test_export_user_data(): void {
1 efrain 60
        $this->resetAfterTest();
61
        $user = $this->getDataGenerator()->create_user([
62
            'firstaccess' => 1535760000,
63
            'lastaccess' => 1541030400,
64
            'currentlogin' => 1541030400,
65
        ]);
66
        $course = $this->getDataGenerator()->create_course();
67
        $context = \context_user::instance($user->id);
68
 
69
        $this->create_data_for_user($user, $course);
70
 
71
        $approvedlist = new \core_privacy\local\request\approved_contextlist($user, 'core_user', [$context->id]);
72
 
73
        $writer = \core_privacy\local\request\writer::with_context($context);
74
        provider::export_user_data($approvedlist);
75
 
76
        // Make sure that the password history only returns a count.
77
        $history = $writer->get_data([get_string('privacy:passwordhistorypath', 'user')]);
78
        $objectcount = new \ArrayObject($history);
79
        // This object should only have one property.
80
        $this->assertCount(1, $objectcount);
81
        $this->assertEquals(1, $history->password_history_count);
82
 
83
        // Password resets should have two fields - timerequested and timererequested.
84
        $resetarray = (array) $writer->get_data([get_string('privacy:passwordresetpath', 'user')]);
85
        $detail = array_shift($resetarray);
86
        $this->assertTrue(array_key_exists('timerequested', $detail));
87
        $this->assertTrue(array_key_exists('timererequested', $detail));
88
 
89
        // Last access to course.
90
        $lastcourseaccess = (array) $writer->get_data([get_string('privacy:lastaccesspath', 'user')]);
91
        $entry = array_shift($lastcourseaccess);
92
        $this->assertEquals($course->fullname, $entry['course_name']);
93
        $this->assertTrue(array_key_exists('timeaccess', $entry));
94
 
95
        // User devices.
96
        $userdevices = (array) $writer->get_data([get_string('privacy:devicespath', 'user')]);
97
        $entry = array_shift($userdevices);
98
        $this->assertEquals('com.moodle.moodlemobile', $entry['appid']);
99
        // Make sure these fields are not exported.
100
        $this->assertFalse(array_key_exists('pushid', $entry));
101
        $this->assertFalse(array_key_exists('uuid', $entry));
102
 
103
        // Session data.
104
        $sessiondata = (array) $writer->get_data([get_string('privacy:sessionpath', 'user')]);
105
        $entry = array_shift($sessiondata);
106
        // Make sure that the sid is not exported.
107
        $this->assertFalse(array_key_exists('sid', $entry));
108
        // Check that some of the other fields are present.
109
        $this->assertTrue(array_key_exists('state', $entry));
110
        $this->assertTrue(array_key_exists('sessdata', $entry));
111
        $this->assertTrue(array_key_exists('timecreated', $entry));
112
 
113
        // Course requests
114
        $courserequestdata = (array) $writer->get_data([get_string('privacy:courserequestpath', 'user')]);
115
        $entry = array_shift($courserequestdata);
116
        // Make sure that the password is not exported.
117
        $this->assertFalse(property_exists($entry, 'password'));
118
        // Check that some of the other fields are present.
119
        $this->assertTrue(property_exists($entry, 'fullname'));
120
        $this->assertTrue(property_exists($entry, 'shortname'));
121
        $this->assertTrue(property_exists($entry, 'summary'));
122
 
123
         // User details.
124
        $userdata = (array) $writer->get_data([]);
125
        // Check that the password is not exported.
126
        $this->assertFalse(array_key_exists('password', $userdata));
127
        // Check that some critical fields exist.
128
        $this->assertTrue(array_key_exists('firstname', $userdata));
129
        $this->assertTrue(array_key_exists('lastname', $userdata));
130
        $this->assertTrue(array_key_exists('email', $userdata));
131
        // Check access times.
132
        $this->assertEquals(transform::datetime($user->firstaccess), $userdata['firstaccess']);
133
        $this->assertEquals(transform::datetime($user->lastaccess), $userdata['lastaccess']);
134
        $this->assertNull($userdata['lastlogin']);
135
        $this->assertEquals(transform::datetime($user->currentlogin), $userdata['currentlogin']);
136
    }
137
 
138
    /**
139
     * Test that user data is deleted for one user.
140
     */
11 efrain 141
    public function test_delete_data_for_all_users_in_context(): void {
1 efrain 142
        global $DB;
143
        $this->resetAfterTest();
144
        $user = $this->getDataGenerator()->create_user([
145
            'idnumber' => 'A0023',
146
            'emailstop' => 1,
147
            'phone1' => '555 3257',
148
            'institution' => 'test',
149
            'department' => 'Science',
150
            'city' => 'Perth',
151
            'country' => 'AU'
152
        ]);
153
        $user2 = $this->getDataGenerator()->create_user();
154
        $course = $this->getDataGenerator()->create_course();
155
 
156
        $this->create_data_for_user($user, $course);
157
        $this->create_data_for_user($user2, $course);
158
 
159
        provider::delete_data_for_all_users_in_context(\context_user::instance($user->id));
160
 
161
        // These tables should not have any user data for $user. Only for $user2.
162
        $records = $DB->get_records('user_password_history');
163
        $this->assertCount(1, $records);
164
        $data = array_shift($records);
165
        $this->assertNotEquals($user->id, $data->userid);
166
        $this->assertEquals($user2->id, $data->userid);
167
        $records = $DB->get_records('user_password_resets');
168
        $this->assertCount(1, $records);
169
        $data = array_shift($records);
170
        $this->assertNotEquals($user->id, $data->userid);
171
        $this->assertEquals($user2->id, $data->userid);
172
        $records = $DB->get_records('user_lastaccess');
173
        $this->assertCount(1, $records);
174
        $data = array_shift($records);
175
        $this->assertNotEquals($user->id, $data->userid);
176
        $this->assertEquals($user2->id, $data->userid);
177
        $records = $DB->get_records('user_devices');
178
        $this->assertCount(1, $records);
179
        $data = array_shift($records);
180
        $this->assertNotEquals($user->id, $data->userid);
181
        $this->assertEquals($user2->id, $data->userid);
182
 
183
        // Now check that there is still a record for the deleted user, but that non-critical information is removed.
184
        $record = $DB->get_record('user', ['id' => $user->id]);
185
        $this->assertEmpty($record->idnumber);
186
        $this->assertEmpty($record->emailstop);
187
        $this->assertEmpty($record->phone1);
188
        $this->assertEmpty($record->institution);
189
        $this->assertEmpty($record->department);
190
        $this->assertEmpty($record->city);
191
        $this->assertEmpty($record->country);
192
        $this->assertEmpty($record->timezone);
193
        $this->assertEmpty($record->timecreated);
194
        $this->assertEmpty($record->timemodified);
195
        $this->assertEmpty($record->firstnamephonetic);
196
        // Check for critical fields.
197
        // Deleted should now be 1.
198
        $this->assertEquals(1, $record->deleted);
199
        $this->assertEquals($user->id, $record->id);
200
        $this->assertEquals($user->username, $record->username);
201
        $this->assertEquals($user->password, $record->password);
202
        $this->assertEquals($user->firstname, $record->firstname);
203
        $this->assertEquals($user->lastname, $record->lastname);
204
        $this->assertEquals($user->email, $record->email);
205
    }
206
 
207
    /**
208
     * Test that user data is deleted for one user.
209
     */
11 efrain 210
    public function test_delete_data_for_user(): void {
1 efrain 211
        global $DB;
212
        $this->resetAfterTest();
213
        $user = $this->getDataGenerator()->create_user([
214
            'idnumber' => 'A0023',
215
            'emailstop' => 1,
216
            'phone1' => '555 3257',
217
            'institution' => 'test',
218
            'department' => 'Science',
219
            'city' => 'Perth',
220
            'country' => 'AU'
221
        ]);
222
        $user2 = $this->getDataGenerator()->create_user();
223
        $course = $this->getDataGenerator()->create_course();
224
 
225
        $this->create_data_for_user($user, $course);
226
        $this->create_data_for_user($user2, $course);
227
 
228
        // Provide multiple different context to check that only the correct user is deleted.
229
        $contexts = [
230
            \context_user::instance($user->id)->id,
231
            \context_user::instance($user2->id)->id,
232
            \context_system::instance()->id];
233
        $approvedlist = new \core_privacy\local\request\approved_contextlist($user, 'core_user', $contexts);
234
 
235
        provider::delete_data_for_user($approvedlist);
236
 
237
        // These tables should not have any user data for $user. Only for $user2.
238
        $records = $DB->get_records('user_password_history');
239
        $this->assertCount(1, $records);
240
        $data = array_shift($records);
241
        $this->assertNotEquals($user->id, $data->userid);
242
        $this->assertEquals($user2->id, $data->userid);
243
        $records = $DB->get_records('user_password_resets');
244
        $this->assertCount(1, $records);
245
        $data = array_shift($records);
246
        $this->assertNotEquals($user->id, $data->userid);
247
        $this->assertEquals($user2->id, $data->userid);
248
        $records = $DB->get_records('user_lastaccess');
249
        $this->assertCount(1, $records);
250
        $data = array_shift($records);
251
        $this->assertNotEquals($user->id, $data->userid);
252
        $this->assertEquals($user2->id, $data->userid);
253
        $records = $DB->get_records('user_devices');
254
        $this->assertCount(1, $records);
255
        $data = array_shift($records);
256
        $this->assertNotEquals($user->id, $data->userid);
257
        $this->assertEquals($user2->id, $data->userid);
258
 
259
        // Now check that there is still a record for the deleted user, but that non-critical information is removed.
260
        $record = $DB->get_record('user', ['id' => $user->id]);
261
        $this->assertEmpty($record->idnumber);
262
        $this->assertEmpty($record->emailstop);
263
        $this->assertEmpty($record->phone1);
264
        $this->assertEmpty($record->institution);
265
        $this->assertEmpty($record->department);
266
        $this->assertEmpty($record->city);
267
        $this->assertEmpty($record->country);
268
        $this->assertEmpty($record->timezone);
269
        $this->assertEmpty($record->timecreated);
270
        $this->assertEmpty($record->timemodified);
271
        $this->assertEmpty($record->firstnamephonetic);
272
        // Check for critical fields.
273
        // Deleted should now be 1.
274
        $this->assertEquals(1, $record->deleted);
275
        $this->assertEquals($user->id, $record->id);
276
        $this->assertEquals($user->username, $record->username);
277
        $this->assertEquals($user->password, $record->password);
278
        $this->assertEquals($user->firstname, $record->firstname);
279
        $this->assertEquals($user->lastname, $record->lastname);
280
        $this->assertEquals($user->email, $record->email);
281
    }
282
 
283
    /**
284
     * Test that only users with a user context are fetched.
285
     */
11 efrain 286
    public function test_get_users_in_context(): void {
1 efrain 287
        $this->resetAfterTest();
288
 
289
        $component = 'core_user';
290
        // Create a user.
291
        $user = $this->getDataGenerator()->create_user();
292
        $usercontext = \context_user::instance($user->id);
293
        $userlist = new \core_privacy\local\request\userlist($usercontext, $component);
294
 
295
        // The list of users for user context should return the user.
296
        provider::get_users_in_context($userlist);
297
        $this->assertCount(1, $userlist);
298
        $expected = [$user->id];
299
        $actual = $userlist->get_userids();
300
        $this->assertEquals($expected, $actual);
301
 
302
        // The list of users for system context should not return any users.
303
        $systemcontext = \context_system::instance();
304
        $userlist = new \core_privacy\local\request\userlist($systemcontext, $component);
305
        provider::get_users_in_context($userlist);
306
        $this->assertCount(0, $userlist);
307
    }
308
 
309
    /**
310
     * Test that data for users in approved userlist is deleted.
311
     */
11 efrain 312
    public function test_delete_data_for_users(): void {
1 efrain 313
        global $DB;
314
 
315
        $this->resetAfterTest();
316
 
317
        $component = 'core_user';
318
 
319
        // Create user1.
320
        $user1 = $this->getDataGenerator()->create_user([
321
            'idnumber' => 'A0023',
322
            'emailstop' => 1,
323
            'phone1' => '555 3257',
324
            'institution' => 'test',
325
            'department' => 'Science',
326
            'city' => 'Perth',
327
            'country' => 'AU'
328
        ]);
329
        $usercontext1 = \context_user::instance($user1->id);
330
        $userlist1 = new \core_privacy\local\request\userlist($usercontext1, $component);
331
 
332
        // Create user2.
333
        $user2 = $this->getDataGenerator()->create_user([
334
            'idnumber' => 'A0024',
335
            'emailstop' => 1,
336
            'phone1' => '555 3258',
337
            'institution' => 'test',
338
            'department' => 'Science',
339
            'city' => 'Perth',
340
            'country' => 'AU'
341
        ]);
342
        $usercontext2 = \context_user::instance($user2->id);
343
        $userlist2 = new \core_privacy\local\request\userlist($usercontext2, $component);
344
 
345
        // The list of users for usercontext1 should return user1.
346
        provider::get_users_in_context($userlist1);
347
        $this->assertCount(1, $userlist1);
348
        // The list of users for usercontext2 should return user2.
349
        provider::get_users_in_context($userlist2);
350
        $this->assertCount(1, $userlist2);
351
 
352
        // Add userlist1 to the approved user list.
353
        $approvedlist = new approved_userlist($usercontext1, $component, $userlist1->get_userids());
354
        // Delete using delete_data_for_users().
355
        provider::delete_data_for_users($approvedlist);
356
 
357
        // Now check that there is still a record for user1 (deleted user), but non-critical information is removed.
358
        $record = $DB->get_record('user', ['id' => $user1->id]);
359
        $this->assertEmpty($record->idnumber);
360
        $this->assertEmpty($record->emailstop);
361
        $this->assertEmpty($record->phone1);
362
        $this->assertEmpty($record->institution);
363
        $this->assertEmpty($record->department);
364
        $this->assertEmpty($record->city);
365
        $this->assertEmpty($record->country);
366
        $this->assertEmpty($record->timezone);
367
        $this->assertEmpty($record->timecreated);
368
        $this->assertEmpty($record->timemodified);
369
        $this->assertEmpty($record->firstnamephonetic);
370
        // Check for critical fields.
371
        // Deleted should now be 1.
372
        $this->assertEquals(1, $record->deleted);
373
        $this->assertEquals($user1->id, $record->id);
374
        $this->assertEquals($user1->username, $record->username);
375
        $this->assertEquals($user1->password, $record->password);
376
        $this->assertEquals($user1->firstname, $record->firstname);
377
        $this->assertEquals($user1->lastname, $record->lastname);
378
        $this->assertEquals($user1->email, $record->email);
379
 
380
        // Now check that the record and information for user2 is still present.
381
        $record = $DB->get_record('user', ['id' => $user2->id]);
382
        $this->assertNotEmpty($record->idnumber);
383
        $this->assertNotEmpty($record->emailstop);
384
        $this->assertNotEmpty($record->phone1);
385
        $this->assertNotEmpty($record->institution);
386
        $this->assertNotEmpty($record->department);
387
        $this->assertNotEmpty($record->city);
388
        $this->assertNotEmpty($record->country);
389
        $this->assertNotEmpty($record->timezone);
390
        $this->assertNotEmpty($record->timecreated);
391
        $this->assertNotEmpty($record->timemodified);
392
        $this->assertNotEmpty($record->firstnamephonetic);
393
        $this->assertEquals(0, $record->deleted);
394
        $this->assertEquals($user2->id, $record->id);
395
        $this->assertEquals($user2->username, $record->username);
396
        $this->assertEquals($user2->password, $record->password);
397
        $this->assertEquals($user2->firstname, $record->firstname);
398
        $this->assertEquals($user2->lastname, $record->lastname);
399
        $this->assertEquals($user2->email, $record->email);
400
    }
401
 
402
    /**
403
     * Create user data for a user.
404
     *
405
     * @param  \stdClass $user A user object.
406
     * @param  \stdClass $course A course.
407
     */
408
    protected function create_data_for_user($user, $course) {
409
        global $DB;
410
        $this->resetAfterTest();
411
        // Last course access.
412
        $lastaccess = (object) [
413
            'userid' => $user->id,
414
            'courseid' => $course->id,
415
            'timeaccess' => time() - DAYSECS
416
        ];
417
        $DB->insert_record('user_lastaccess', $lastaccess);
418
 
419
        // Password history.
420
        $history = (object) [
421
            'userid' => $user->id,
422
            'hash' => 'HID098djJUU',
423
            'timecreated' => time()
424
        ];
425
        $DB->insert_record('user_password_history', $history);
426
 
427
        // Password resets.
428
        $passwordreset = (object) [
429
            'userid' => $user->id,
430
            'timerequested' => time(),
431
            'timererequested' => time(),
432
            'token' => $this->generate_random_string()
433
        ];
434
        $DB->insert_record('user_password_resets', $passwordreset);
435
 
436
        // User mobile devices.
437
        $userdevices = (object) [
438
            'userid' => $user->id,
439
            'appid' => 'com.moodle.moodlemobile',
440
            'name' => 'occam',
441
            'model' => 'Nexus 4',
442
            'platform' => 'Android',
443
            'version' => '4.2.2',
444
            'pushid' => 'kishUhd',
445
            'uuid' => 'KIhud7s',
446
            'timecreated' => time(),
447
            'timemodified' => time()
448
        ];
449
        $DB->insert_record('user_devices', $userdevices);
450
 
451
        // Course request.
452
        $courserequest = (object) [
453
            'fullname' => 'Test Course',
454
            'shortname' => 'TC',
455
            'summary' => 'Summary of course',
456
            'summaryformat' => 1,
457
            'category' => 1,
458
            'reason' => 'Because it would be nice.',
459
            'requester' => $user->id,
460
            'password' => ''
461
        ];
462
        $DB->insert_record('course_request', $courserequest);
463
 
464
        // User session table data.
465
        $usersessions = (object) [
466
            'state' => 0,
467
            'sid' => $this->generate_random_string(), // Needs a unique id.
468
            'userid' => $user->id,
469
            'sessdata' => 'Nothing',
470
            'timecreated' => time(),
471
            'timemodified' => time(),
472
            'firstip' => '0.0.0.0',
473
            'lastip' => '0.0.0.0'
474
        ];
1441 ariadna 475
        $mockhandler = new mock_handler();
476
        $mockhandler->add_test_session($usersessions);
1 efrain 477
    }
478
 
479
    /**
480
     * Create a random string.
481
     *
482
     * @param  integer $length length of the string to generate.
483
     * @return string A random string.
484
     */
485
    protected function generate_random_string($length = 6) {
486
        $response = '';
487
        $source = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
488
 
489
        if ($length > 0) {
490
 
491
            $response = '';
492
            $source = str_split($source, 1);
493
 
494
            for ($i = 1; $i <= $length; $i++) {
495
                $num = mt_rand(1, count($source));
496
                $response .= $source[$num - 1];
497
            }
498
        }
499
 
500
        return $response;
501
    }
502
}