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