Differences Between: [Versions 311 and 403] [Versions 400 and 403] [Versions 401 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 * 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 */ 47 public function test_get_contexts_for_userid() { 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 */ 62 public function test_export_user_data() { 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 */ 144 public function test_delete_data_for_all_users_in_context() { 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 */ 213 public function test_delete_data_for_user() { 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 */ 289 public function test_get_users_in_context() { 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 */ 315 public function test_delete_data_for_users() { 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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body