See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 39 and 401] [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 namespace tool_dataprivacy; 18 19 use core\invalid_persistent_exception; 20 use core\task\manager; 21 use testing_data_generator; 22 use tool_dataprivacy\local\helper; 23 use tool_dataprivacy\task\process_data_request_task; 24 25 /** 26 * API tests. 27 * 28 * @package tool_dataprivacy 29 * @covers \tool_dataprivacy\api 30 * @copyright 2018 Jun Pataleta 31 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 32 */ 33 class api_test extends \advanced_testcase { 34 35 /** 36 * Ensure that the check_can_manage_data_registry function fails cap testing when a user without capabilities is 37 * tested with the default context. 38 */ 39 public function test_check_can_manage_data_registry_admin() { 40 $this->resetAfterTest(); 41 42 $this->setAdminUser(); 43 // Technically this actually returns void, but assertNull will suffice to avoid a pointless test. 44 $this->assertNull(api::check_can_manage_data_registry()); 45 } 46 47 /** 48 * Ensure that the check_can_manage_data_registry function fails cap testing when a user without capabilities is 49 * tested with the default context. 50 */ 51 public function test_check_can_manage_data_registry_without_cap_default() { 52 $this->resetAfterTest(); 53 54 $user = $this->getDataGenerator()->create_user(); 55 $this->setUser($user); 56 57 $this->expectException(\required_capability_exception::class); 58 api::check_can_manage_data_registry(); 59 } 60 61 /** 62 * Ensure that the check_can_manage_data_registry function fails cap testing when a user without capabilities is 63 * tested with the default context. 64 */ 65 public function test_check_can_manage_data_registry_without_cap_system() { 66 $this->resetAfterTest(); 67 68 $user = $this->getDataGenerator()->create_user(); 69 $this->setUser($user); 70 71 $this->expectException(\required_capability_exception::class); 72 api::check_can_manage_data_registry(\context_system::instance()->id); 73 } 74 75 /** 76 * Ensure that the check_can_manage_data_registry function fails cap testing when a user without capabilities is 77 * tested with the default context. 78 */ 79 public function test_check_can_manage_data_registry_without_cap_own_user() { 80 $this->resetAfterTest(); 81 82 $user = $this->getDataGenerator()->create_user(); 83 $this->setUser($user); 84 85 $this->expectException(\required_capability_exception::class); 86 api::check_can_manage_data_registry(\context_user::instance($user->id)->id); 87 } 88 89 /** 90 * Test for api::update_request_status(). 91 */ 92 public function test_update_request_status() { 93 $this->resetAfterTest(); 94 95 $generator = new testing_data_generator(); 96 $s1 = $generator->create_user(); 97 $this->setUser($s1); 98 99 // Create the sample data request. 100 $datarequest = api::create_data_request($s1->id, api::DATAREQUEST_TYPE_EXPORT); 101 102 $requestid = $datarequest->get('id'); 103 104 // Update with a comment. 105 $comment = 'This is an example of a comment'; 106 $result = api::update_request_status($requestid, api::DATAREQUEST_STATUS_AWAITING_APPROVAL, 0, $comment); 107 $this->assertTrue($result); 108 $datarequest = new data_request($requestid); 109 $this->assertStringEndsWith($comment, $datarequest->get('dpocomment')); 110 111 // Update with a comment which will be trimmed. 112 $result = api::update_request_status($requestid, api::DATAREQUEST_STATUS_AWAITING_APPROVAL, 0, ' '); 113 $this->assertTrue($result); 114 $datarequest = new data_request($requestid); 115 $this->assertStringEndsWith($comment, $datarequest->get('dpocomment')); 116 117 // Update with a comment. 118 $secondcomment = ' - More comments - '; 119 $result = api::update_request_status($requestid, api::DATAREQUEST_STATUS_AWAITING_APPROVAL, 0, $secondcomment); 120 $this->assertTrue($result); 121 $datarequest = new data_request($requestid); 122 $this->assertMatchesRegularExpression("/.*{$comment}.*{$secondcomment}/s", $datarequest->get('dpocomment')); 123 124 // Update with a valid status. 125 $result = api::update_request_status($requestid, api::DATAREQUEST_STATUS_DOWNLOAD_READY); 126 $this->assertTrue($result); 127 128 // Fetch the request record again. 129 $datarequest = new data_request($requestid); 130 $this->assertEquals(api::DATAREQUEST_STATUS_DOWNLOAD_READY, $datarequest->get('status')); 131 132 // Update with an invalid status. 133 $this->expectException(invalid_persistent_exception::class); 134 api::update_request_status($requestid, -1); 135 } 136 137 /** 138 * Test for api::get_site_dpos() when there are no users with the DPO role. 139 */ 140 public function test_get_site_dpos_no_dpos() { 141 $this->resetAfterTest(); 142 143 $admin = get_admin(); 144 145 $dpos = api::get_site_dpos(); 146 $this->assertCount(1, $dpos); 147 $dpo = reset($dpos); 148 $this->assertEquals($admin->id, $dpo->id); 149 } 150 151 /** 152 * Test for api::get_site_dpos() when there are no users with the DPO role. 153 */ 154 public function test_get_site_dpos() { 155 global $DB; 156 157 $this->resetAfterTest(); 158 159 $generator = new testing_data_generator(); 160 $u1 = $generator->create_user(); 161 $u2 = $generator->create_user(); 162 163 $context = \context_system::instance(); 164 165 // Give the manager role with the capability to manage data requests. 166 $managerroleid = $DB->get_field('role', 'id', array('shortname' => 'manager')); 167 assign_capability('tool/dataprivacy:managedatarequests', CAP_ALLOW, $managerroleid, $context->id, true); 168 // Assign u1 as a manager. 169 role_assign($managerroleid, $u1->id, $context->id); 170 171 // Give the editing teacher role with the capability to manage data requests. 172 $editingteacherroleid = $DB->get_field('role', 'id', array('shortname' => 'editingteacher')); 173 assign_capability('tool/dataprivacy:managedatarequests', CAP_ALLOW, $editingteacherroleid, $context->id, true); 174 // Assign u1 as an editing teacher as well. 175 role_assign($editingteacherroleid, $u1->id, $context->id); 176 // Assign u2 as an editing teacher. 177 role_assign($editingteacherroleid, $u2->id, $context->id); 178 179 // Only map the manager role to the DPO role. 180 set_config('dporoles', $managerroleid, 'tool_dataprivacy'); 181 182 $dpos = api::get_site_dpos(); 183 $this->assertCount(1, $dpos); 184 $dpo = reset($dpos); 185 $this->assertEquals($u1->id, $dpo->id); 186 } 187 188 /** 189 * Test for \tool_dataprivacy\api::get_assigned_privacy_officer_roles(). 190 */ 191 public function test_get_assigned_privacy_officer_roles() { 192 global $DB; 193 194 $this->resetAfterTest(); 195 196 // Erroneously set the manager roles as the PO, even if it doesn't have the managedatarequests capability yet. 197 $managerroleid = $DB->get_field('role', 'id', array('shortname' => 'manager')); 198 set_config('dporoles', $managerroleid, 'tool_dataprivacy'); 199 // Get the assigned PO roles when nothing has been set yet. 200 $roleids = api::get_assigned_privacy_officer_roles(); 201 // Confirm that the returned list is empty. 202 $this->assertEmpty($roleids); 203 204 $context = \context_system::instance(); 205 206 // Give the manager role with the capability to manage data requests. 207 assign_capability('tool/dataprivacy:managedatarequests', CAP_ALLOW, $managerroleid, $context->id, true); 208 209 // Give the editing teacher role with the capability to manage data requests. 210 $editingteacherroleid = $DB->get_field('role', 'id', array('shortname' => 'editingteacher')); 211 assign_capability('tool/dataprivacy:managedatarequests', CAP_ALLOW, $editingteacherroleid, $context->id, true); 212 213 // Get the non-editing teacher role ID. 214 $teacherroleid = $DB->get_field('role', 'id', array('shortname' => 'teacher')); 215 216 // Erroneously map the manager and the non-editing teacher roles to the PO role. 217 $badconfig = $managerroleid . ',' . $teacherroleid; 218 set_config('dporoles', $badconfig, 'tool_dataprivacy'); 219 220 // Get the assigned PO roles. 221 $roleids = api::get_assigned_privacy_officer_roles(); 222 223 // There should only be one PO role. 224 $this->assertCount(1, $roleids); 225 // Confirm it contains the manager role. 226 $this->assertContainsEquals($managerroleid, $roleids); 227 // And it does not contain the editing teacher role. 228 $this->assertNotContainsEquals($editingteacherroleid, $roleids); 229 } 230 231 /** 232 * Test for api::approve_data_request(). 233 */ 234 public function test_approve_data_request() { 235 global $DB; 236 237 $this->resetAfterTest(); 238 239 $generator = new testing_data_generator(); 240 $s1 = $generator->create_user(); 241 $u1 = $generator->create_user(); 242 243 $context = \context_system::instance(); 244 245 // Manager role. 246 $managerroleid = $DB->get_field('role', 'id', array('shortname' => 'manager')); 247 // Give the manager role with the capability to manage data requests. 248 assign_capability('tool/dataprivacy:managedatarequests', CAP_ALLOW, $managerroleid, $context->id, true); 249 // Assign u1 as a manager. 250 role_assign($managerroleid, $u1->id, $context->id); 251 252 // Map the manager role to the DPO role. 253 set_config('dporoles', $managerroleid, 'tool_dataprivacy'); 254 255 // Create the sample data request. 256 $this->setUser($s1); 257 $datarequest = api::create_data_request($s1->id, api::DATAREQUEST_TYPE_EXPORT); 258 $requestid = $datarequest->get('id'); 259 260 // Make this ready for approval. 261 api::update_request_status($requestid, api::DATAREQUEST_STATUS_AWAITING_APPROVAL); 262 263 $this->setUser($u1); 264 $result = api::approve_data_request($requestid); 265 $this->assertTrue($result); 266 $datarequest = new data_request($requestid); 267 $this->assertEquals($u1->id, $datarequest->get('dpo')); 268 $this->assertEquals(api::DATAREQUEST_STATUS_APPROVED, $datarequest->get('status')); 269 270 // Test adhoc task creation. 271 $adhoctasks = manager::get_adhoc_tasks(process_data_request_task::class); 272 $this->assertCount(1, $adhoctasks); 273 } 274 275 /** 276 * Test for api::approve_data_request() when called by a user who doesn't have the DPO role. 277 */ 278 public function test_approve_data_request_non_dpo_user() { 279 $this->resetAfterTest(); 280 281 $generator = new testing_data_generator(); 282 $student = $generator->create_user(); 283 $teacher = $generator->create_user(); 284 285 // Create the sample data request. 286 $this->setUser($student); 287 $datarequest = api::create_data_request($student->id, api::DATAREQUEST_TYPE_EXPORT); 288 289 $requestid = $datarequest->get('id'); 290 291 // Login as a user without DPO role. 292 $this->setUser($teacher); 293 $this->expectException(\required_capability_exception::class); 294 api::approve_data_request($requestid); 295 } 296 297 /** 298 * Test that deletion requests for the primary admin are rejected 299 */ 300 public function test_reject_data_deletion_request_primary_admin() { 301 $this->resetAfterTest(); 302 $this->setAdminUser(); 303 304 $datarequest = api::create_data_request(get_admin()->id, api::DATAREQUEST_TYPE_DELETE); 305 306 // Approve the request and execute the ad-hoc process task. 307 ob_start(); 308 api::approve_data_request($datarequest->get('id')); 309 $this->runAdhocTasks('\tool_dataprivacy\task\process_data_request_task'); 310 ob_end_clean(); 311 312 $request = api::get_request($datarequest->get('id')); 313 $this->assertEquals(api::DATAREQUEST_STATUS_REJECTED, $request->get('status')); 314 315 // Confirm they weren't deleted. 316 $user = \core_user::get_user($request->get('userid')); 317 \core_user::require_active_user($user); 318 } 319 320 /** 321 * Test for api::can_contact_dpo() 322 */ 323 public function test_can_contact_dpo() { 324 $this->resetAfterTest(); 325 326 // Default ('contactdataprotectionofficer' is disabled by default). 327 $this->assertFalse(api::can_contact_dpo()); 328 329 // Enable. 330 set_config('contactdataprotectionofficer', 1, 'tool_dataprivacy'); 331 $this->assertTrue(api::can_contact_dpo()); 332 333 // Disable again. 334 set_config('contactdataprotectionofficer', 0, 'tool_dataprivacy'); 335 $this->assertFalse(api::can_contact_dpo()); 336 } 337 338 /** 339 * Test for api::can_manage_data_requests() 340 */ 341 public function test_can_manage_data_requests() { 342 global $DB; 343 344 $this->resetAfterTest(); 345 346 // No configured site DPOs yet. 347 $admin = get_admin(); 348 $this->assertTrue(api::can_manage_data_requests($admin->id)); 349 350 $generator = new testing_data_generator(); 351 $dpo = $generator->create_user(); 352 $nondpocapable = $generator->create_user(); 353 $nondpoincapable = $generator->create_user(); 354 355 $context = \context_system::instance(); 356 357 // Manager role. 358 $managerroleid = $DB->get_field('role', 'id', array('shortname' => 'manager')); 359 // Give the manager role with the capability to manage data requests. 360 assign_capability('tool/dataprivacy:managedatarequests', CAP_ALLOW, $managerroleid, $context->id, true); 361 // Assign u1 as a manager. 362 role_assign($managerroleid, $dpo->id, $context->id); 363 364 // Editing teacher role. 365 $editingteacherroleid = $DB->get_field('role', 'id', array('shortname' => 'editingteacher')); 366 // Give the editing teacher role with the capability to manage data requests. 367 assign_capability('tool/dataprivacy:managedatarequests', CAP_ALLOW, $managerroleid, $context->id, true); 368 // Assign u2 as an editing teacher. 369 role_assign($editingteacherroleid, $nondpocapable->id, $context->id); 370 371 // Map only the manager role to the DPO role. 372 set_config('dporoles', $managerroleid, 'tool_dataprivacy'); 373 374 // User with capability and has DPO role. 375 $this->assertTrue(api::can_manage_data_requests($dpo->id)); 376 // User with capability but has no DPO role. 377 $this->assertFalse(api::can_manage_data_requests($nondpocapable->id)); 378 // User without the capability and has no DPO role. 379 $this->assertFalse(api::can_manage_data_requests($nondpoincapable->id)); 380 } 381 382 /** 383 * Test that a user who has no capability to make any data requests for children cannot create data requests for any 384 * other user. 385 */ 386 public function test_can_create_data_request_for_user_no() { 387 $this->resetAfterTest(); 388 389 $parent = $this->getDataGenerator()->create_user(); 390 $otheruser = $this->getDataGenerator()->create_user(); 391 392 $this->setUser($parent); 393 $this->assertFalse(api::can_create_data_request_for_user($otheruser->id)); 394 } 395 396 /** 397 * Test that a user who has the capability to make any data requests for one other user cannot create data requests 398 * for any other user. 399 */ 400 public function test_can_create_data_request_for_user_some() { 401 $this->resetAfterTest(); 402 403 $parent = $this->getDataGenerator()->create_user(); 404 $child = $this->getDataGenerator()->create_user(); 405 $otheruser = $this->getDataGenerator()->create_user(); 406 407 $systemcontext = \context_system::instance(); 408 $parentrole = $this->getDataGenerator()->create_role(); 409 assign_capability('tool/dataprivacy:makedatarequestsforchildren', CAP_ALLOW, $parentrole, $systemcontext); 410 role_assign($parentrole, $parent->id, \context_user::instance($child->id)); 411 412 $this->setUser($parent); 413 $this->assertFalse(api::can_create_data_request_for_user($otheruser->id)); 414 } 415 416 /** 417 * Test that a user who has the capability to make any data requests for one other user cannot create data requests 418 * for any other user. 419 */ 420 public function test_can_create_data_request_for_user_own_child() { 421 $this->resetAfterTest(); 422 423 $parent = $this->getDataGenerator()->create_user(); 424 $child = $this->getDataGenerator()->create_user(); 425 426 $systemcontext = \context_system::instance(); 427 $parentrole = $this->getDataGenerator()->create_role(); 428 assign_capability('tool/dataprivacy:makedatarequestsforchildren', CAP_ALLOW, $parentrole, $systemcontext); 429 role_assign($parentrole, $parent->id, \context_user::instance($child->id)); 430 431 $this->setUser($parent); 432 $this->assertTrue(api::can_create_data_request_for_user($child->id)); 433 } 434 435 /** 436 * Test that a user who has no capability to make any data requests for children cannot create data requests for any 437 * other user. 438 */ 439 public function test_require_can_create_data_request_for_user_no() { 440 $this->resetAfterTest(); 441 442 $parent = $this->getDataGenerator()->create_user(); 443 $otheruser = $this->getDataGenerator()->create_user(); 444 445 $this->setUser($parent); 446 $this->expectException('required_capability_exception'); 447 api::require_can_create_data_request_for_user($otheruser->id); 448 } 449 450 /** 451 * Test that a user who has the capability to make any data requests for one other user cannot create data requests 452 * for any other user. 453 */ 454 public function test_require_can_create_data_request_for_user_some() { 455 $this->resetAfterTest(); 456 457 $parent = $this->getDataGenerator()->create_user(); 458 $child = $this->getDataGenerator()->create_user(); 459 $otheruser = $this->getDataGenerator()->create_user(); 460 461 $systemcontext = \context_system::instance(); 462 $parentrole = $this->getDataGenerator()->create_role(); 463 assign_capability('tool/dataprivacy:makedatarequestsforchildren', CAP_ALLOW, $parentrole, $systemcontext); 464 role_assign($parentrole, $parent->id, \context_user::instance($child->id)); 465 466 $this->setUser($parent); 467 $this->expectException('required_capability_exception'); 468 api::require_can_create_data_request_for_user($otheruser->id); 469 } 470 471 /** 472 * Test that a user who has the capability to make any data requests for one other user cannot create data requests 473 * for any other user. 474 */ 475 public function test_require_can_create_data_request_for_user_own_child() { 476 $this->resetAfterTest(); 477 478 $parent = $this->getDataGenerator()->create_user(); 479 $child = $this->getDataGenerator()->create_user(); 480 481 $systemcontext = \context_system::instance(); 482 $parentrole = $this->getDataGenerator()->create_role(); 483 assign_capability('tool/dataprivacy:makedatarequestsforchildren', CAP_ALLOW, $parentrole, $systemcontext); 484 role_assign($parentrole, $parent->id, \context_user::instance($child->id)); 485 486 $this->setUser($parent); 487 $this->assertTrue(api::require_can_create_data_request_for_user($child->id)); 488 } 489 490 /** 491 * Test for api::can_download_data_request_for_user() 492 */ 493 public function test_can_download_data_request_for_user() { 494 $this->resetAfterTest(); 495 496 $generator = $this->getDataGenerator(); 497 498 // Three victims. 499 $victim1 = $generator->create_user(); 500 $victim2 = $generator->create_user(); 501 $victim3 = $generator->create_user(); 502 503 // Assign a user as victim 1's parent. 504 $systemcontext = \context_system::instance(); 505 $parentrole = $generator->create_role(); 506 assign_capability('tool/dataprivacy:makedatarequestsforchildren', CAP_ALLOW, $parentrole, $systemcontext); 507 $parent = $generator->create_user(); 508 role_assign($parentrole, $parent->id, \context_user::instance($victim1->id)); 509 510 // Assign another user as data access wonder woman. 511 $wonderrole = $generator->create_role(); 512 assign_capability('tool/dataprivacy:downloadallrequests', CAP_ALLOW, $wonderrole, $systemcontext); 513 $staff = $generator->create_user(); 514 role_assign($wonderrole, $staff->id, $systemcontext); 515 516 // Finally, victim 3 has been naughty; stop them accessing their own data. 517 $naughtyrole = $generator->create_role(); 518 assign_capability('tool/dataprivacy:downloadownrequest', CAP_PROHIBIT, $naughtyrole, $systemcontext); 519 role_assign($naughtyrole, $victim3->id, $systemcontext); 520 521 // Victims 1 and 2 can access their own data, regardless of who requested it. 522 $this->assertTrue(api::can_download_data_request_for_user($victim1->id, $victim1->id, $victim1->id)); 523 $this->assertTrue(api::can_download_data_request_for_user($victim2->id, $staff->id, $victim2->id)); 524 525 // Victim 3 cannot access his own data. 526 $this->assertFalse(api::can_download_data_request_for_user($victim3->id, $victim3->id, $victim3->id)); 527 528 // Victims 1 and 2 cannot access another victim's data. 529 $this->assertFalse(api::can_download_data_request_for_user($victim2->id, $victim1->id, $victim1->id)); 530 $this->assertFalse(api::can_download_data_request_for_user($victim1->id, $staff->id, $victim2->id)); 531 532 // Staff can access everyone's data. 533 $this->assertTrue(api::can_download_data_request_for_user($victim1->id, $victim1->id, $staff->id)); 534 $this->assertTrue(api::can_download_data_request_for_user($victim2->id, $staff->id, $staff->id)); 535 $this->assertTrue(api::can_download_data_request_for_user($victim3->id, $staff->id, $staff->id)); 536 537 // Parent can access victim 1's data only if they requested it. 538 $this->assertTrue(api::can_download_data_request_for_user($victim1->id, $parent->id, $parent->id)); 539 $this->assertFalse(api::can_download_data_request_for_user($victim1->id, $staff->id, $parent->id)); 540 $this->assertFalse(api::can_download_data_request_for_user($victim2->id, $parent->id, $parent->id)); 541 } 542 543 /** 544 * Data provider for data request creation tests. 545 * 546 * @return array 547 */ 548 public function data_request_creation_provider() { 549 return [ 550 'Export request by user, automatic approval off' => [ 551 false, api::DATAREQUEST_TYPE_EXPORT, 'automaticdataexportapproval', false, 0, 552 api::DATAREQUEST_STATUS_AWAITING_APPROVAL, 0 553 ], 554 'Export request by user, automatic approval on' => [ 555 false, api::DATAREQUEST_TYPE_EXPORT, 'automaticdataexportapproval', true, 0, 556 api::DATAREQUEST_STATUS_APPROVED, 1 557 ], 558 'Export request by PO, automatic approval off' => [ 559 true, api::DATAREQUEST_TYPE_EXPORT, 'automaticdataexportapproval', false, 0, 560 api::DATAREQUEST_STATUS_AWAITING_APPROVAL, 0 561 ], 562 'Export request by PO, automatic approval on' => [ 563 true, api::DATAREQUEST_TYPE_EXPORT, 'automaticdataexportapproval', true, 'dpo', 564 api::DATAREQUEST_STATUS_APPROVED, 1 565 ], 566 'Delete request by user, automatic approval off' => [ 567 false, api::DATAREQUEST_TYPE_DELETE, 'automaticdatadeletionapproval', false, 0, 568 api::DATAREQUEST_STATUS_AWAITING_APPROVAL, 0 569 ], 570 'Delete request by user, automatic approval on' => [ 571 false, api::DATAREQUEST_TYPE_DELETE, 'automaticdatadeletionapproval', true, 0, 572 api::DATAREQUEST_STATUS_APPROVED, 1 573 ], 574 'Delete request by PO, automatic approval off' => [ 575 true, api::DATAREQUEST_TYPE_DELETE, 'automaticdatadeletionapproval', false, 0, 576 api::DATAREQUEST_STATUS_AWAITING_APPROVAL, 0 577 ], 578 'Delete request by PO, automatic approval on' => [ 579 true, api::DATAREQUEST_TYPE_DELETE, 'automaticdatadeletionapproval', true, 'dpo', 580 api::DATAREQUEST_STATUS_APPROVED, 1 581 ], 582 ]; 583 } 584 585 /** 586 * Test for api::create_data_request() 587 * 588 * @dataProvider data_request_creation_provider 589 * @param bool $asprivacyofficer Whether the request is made as the Privacy Officer or the user itself. 590 * @param string $type The data request type. 591 * @param string $setting The automatic approval setting. 592 * @param bool $automaticapproval Whether automatic data request approval is turned on or not. 593 * @param int|string $expecteddpoval The expected value for the 'dpo' field. 'dpo' means we'd the expected value would be the 594 * user ID of the privacy officer which happens in the case where a PO requests on behalf of 595 * someone else and automatic data request approval is turned on. 596 * @param int $expectedstatus The expected status of the data request. 597 * @param int $expectedtaskcount The number of expected queued data requests tasks. 598 * @throws coding_exception 599 * @throws invalid_persistent_exception 600 */ 601 public function test_create_data_request($asprivacyofficer, $type, $setting, $automaticapproval, $expecteddpoval, 602 $expectedstatus, $expectedtaskcount) { 603 global $USER; 604 605 $this->resetAfterTest(); 606 607 $generator = new testing_data_generator(); 608 $user = $generator->create_user(); 609 $comment = 'sample comment'; 610 611 // Login. 612 if ($asprivacyofficer) { 613 $this->setAdminUser(); 614 } else { 615 $this->setUser($user->id); 616 } 617 618 // Set the automatic data request approval setting value. 619 set_config($setting, $automaticapproval, 'tool_dataprivacy'); 620 621 // If set to 'dpo' use the currently logged-in user's ID (which should be the admin user's ID). 622 if ($expecteddpoval === 'dpo') { 623 $expecteddpoval = $USER->id; 624 } 625 626 // Test data request creation. 627 $datarequest = api::create_data_request($user->id, $type, $comment); 628 $this->assertEquals($user->id, $datarequest->get('userid')); 629 $this->assertEquals($USER->id, $datarequest->get('requestedby')); 630 $this->assertEquals($expecteddpoval, $datarequest->get('dpo')); 631 $this->assertEquals($type, $datarequest->get('type')); 632 $this->assertEquals($expectedstatus, $datarequest->get('status')); 633 $this->assertEquals($comment, $datarequest->get('comments')); 634 $this->assertEquals($automaticapproval, $datarequest->get('systemapproved')); 635 636 // Test number of queued data request tasks. 637 $datarequesttasks = manager::get_adhoc_tasks(process_data_request_task::class); 638 $this->assertCount($expectedtaskcount, $datarequesttasks); 639 } 640 641 /** 642 * Test for api::create_data_request() made by a parent. 643 */ 644 public function test_create_data_request_by_parent() { 645 global $DB; 646 647 $this->resetAfterTest(); 648 649 $generator = new testing_data_generator(); 650 $user = $generator->create_user(); 651 $parent = $generator->create_user(); 652 $comment = 'sample comment'; 653 654 // Get the teacher role pretend it's the parent roles ;). 655 $systemcontext = \context_system::instance(); 656 $usercontext = \context_user::instance($user->id); 657 $parentroleid = $DB->get_field('role', 'id', array('shortname' => 'teacher')); 658 // Give the manager role with the capability to manage data requests. 659 assign_capability('tool/dataprivacy:makedatarequestsforchildren', CAP_ALLOW, $parentroleid, $systemcontext->id, true); 660 // Assign the parent to user. 661 role_assign($parentroleid, $parent->id, $usercontext->id); 662 663 // Login as the user's parent. 664 $this->setUser($parent); 665 666 // Test data request creation. 667 $datarequest = api::create_data_request($user->id, api::DATAREQUEST_TYPE_EXPORT, $comment); 668 $this->assertEquals($user->id, $datarequest->get('userid')); 669 $this->assertEquals($parent->id, $datarequest->get('requestedby')); 670 $this->assertEquals(0, $datarequest->get('dpo')); 671 $this->assertEquals(api::DATAREQUEST_TYPE_EXPORT, $datarequest->get('type')); 672 $this->assertEquals(api::DATAREQUEST_STATUS_AWAITING_APPROVAL, $datarequest->get('status')); 673 $this->assertEquals($comment, $datarequest->get('comments')); 674 } 675 676 /** 677 * Test for api::deny_data_request() 678 */ 679 public function test_deny_data_request() { 680 $this->resetAfterTest(); 681 682 $generator = new testing_data_generator(); 683 $user = $generator->create_user(); 684 $comment = 'sample comment'; 685 686 // Login as user. 687 $this->setUser($user->id); 688 689 // Test data request creation. 690 $datarequest = api::create_data_request($user->id, api::DATAREQUEST_TYPE_EXPORT, $comment); 691 692 // Login as the admin (default DPO when no one is set). 693 $this->setAdminUser(); 694 695 // Make this ready for approval. 696 api::update_request_status($datarequest->get('id'), api::DATAREQUEST_STATUS_AWAITING_APPROVAL); 697 698 // Deny the data request. 699 $result = api::deny_data_request($datarequest->get('id')); 700 $this->assertTrue($result); 701 } 702 703 /** 704 * Data provider for \tool_dataprivacy_api_testcase::test_get_data_requests(). 705 * 706 * @return array 707 */ 708 public function get_data_requests_provider() { 709 $completeonly = [api::DATAREQUEST_STATUS_COMPLETE, api::DATAREQUEST_STATUS_DOWNLOAD_READY, api::DATAREQUEST_STATUS_DELETED]; 710 $completeandcancelled = array_merge($completeonly, [api::DATAREQUEST_STATUS_CANCELLED]); 711 712 return [ 713 // Own data requests. 714 ['user', false, $completeonly], 715 // Non-DPO fetching all requets. 716 ['user', true, $completeonly], 717 // Admin fetching all completed and cancelled requests. 718 ['dpo', true, $completeandcancelled], 719 // Admin fetching all completed requests. 720 ['dpo', true, $completeonly], 721 // Guest fetching all requests. 722 ['guest', true, $completeonly], 723 ]; 724 } 725 726 /** 727 * Test for api::get_data_requests() 728 * 729 * @dataProvider get_data_requests_provider 730 * @param string $usertype The type of the user logging in. 731 * @param boolean $fetchall Whether to fetch all records. 732 * @param int[] $statuses Status filters. 733 */ 734 public function test_get_data_requests($usertype, $fetchall, $statuses) { 735 $this->resetAfterTest(); 736 737 $generator = new testing_data_generator(); 738 $user1 = $generator->create_user(); 739 $user2 = $generator->create_user(); 740 $user3 = $generator->create_user(); 741 $user4 = $generator->create_user(); 742 $user5 = $generator->create_user(); 743 $users = [$user1, $user2, $user3, $user4, $user5]; 744 745 switch ($usertype) { 746 case 'user': 747 $loggeduser = $user1; 748 break; 749 case 'dpo': 750 $loggeduser = get_admin(); 751 break; 752 case 'guest': 753 $loggeduser = guest_user(); 754 break; 755 } 756 757 $comment = 'Data %s request comment by user %d'; 758 $exportstring = helper::get_shortened_request_type_string(api::DATAREQUEST_TYPE_EXPORT); 759 $deletionstring = helper::get_shortened_request_type_string(api::DATAREQUEST_TYPE_DELETE); 760 // Make a data requests for the users. 761 foreach ($users as $user) { 762 $this->setUser($user); 763 api::create_data_request($user->id, api::DATAREQUEST_TYPE_EXPORT, sprintf($comment, $exportstring, $user->id)); 764 api::create_data_request($user->id, api::DATAREQUEST_TYPE_EXPORT, sprintf($comment, $deletionstring, $user->id)); 765 } 766 767 // Log in as the target user. 768 $this->setUser($loggeduser); 769 // Get records count based on the filters. 770 $userid = $loggeduser->id; 771 if ($fetchall) { 772 $userid = 0; 773 } 774 $count = api::get_data_requests_count($userid); 775 if (api::is_site_dpo($loggeduser->id)) { 776 // DPOs should see all the requests. 777 $this->assertEquals(count($users) * 2, $count); 778 } else { 779 if (empty($userid)) { 780 // There should be no data requests for this user available. 781 $this->assertEquals(0, $count); 782 } else { 783 // There should be only one (request with pending status). 784 $this->assertEquals(2, $count); 785 } 786 } 787 // Get data requests. 788 $requests = api::get_data_requests($userid); 789 // The number of requests should match the count. 790 $this->assertCount($count, $requests); 791 792 // Test filtering by status. 793 if ($count && !empty($statuses)) { 794 $filteredcount = api::get_data_requests_count($userid, $statuses); 795 // There should be none as they are all pending. 796 $this->assertEquals(0, $filteredcount); 797 $filteredrequests = api::get_data_requests($userid, $statuses); 798 $this->assertCount($filteredcount, $filteredrequests); 799 800 $statuscounts = []; 801 foreach ($statuses as $stat) { 802 $statuscounts[$stat] = 0; 803 } 804 $numstatus = count($statuses); 805 // Get all requests with status filter and update statuses, randomly. 806 foreach ($requests as $request) { 807 if (rand(0, 1)) { 808 continue; 809 } 810 811 if ($numstatus > 1) { 812 $index = rand(0, $numstatus - 1); 813 $status = $statuses[$index]; 814 } else { 815 $status = reset($statuses); 816 } 817 $statuscounts[$status]++; 818 api::update_request_status($request->get('id'), $status); 819 } 820 $total = array_sum($statuscounts); 821 $filteredcount = api::get_data_requests_count($userid, $statuses); 822 $this->assertEquals($total, $filteredcount); 823 $filteredrequests = api::get_data_requests($userid, $statuses); 824 $this->assertCount($filteredcount, $filteredrequests); 825 // Confirm the filtered requests match the status filter(s). 826 foreach ($filteredrequests as $request) { 827 $this->assertContainsEquals($request->get('status'), $statuses); 828 } 829 830 if ($numstatus > 1) { 831 // Fetch by individual status to check the numbers match. 832 foreach ($statuses as $status) { 833 $filteredcount = api::get_data_requests_count($userid, [$status]); 834 $this->assertEquals($statuscounts[$status], $filteredcount); 835 $filteredrequests = api::get_data_requests($userid, [$status]); 836 $this->assertCount($filteredcount, $filteredrequests); 837 } 838 } 839 } 840 } 841 842 /** 843 * Data provider for test_has_ongoing_request. 844 */ 845 public function status_provider() { 846 return [ 847 [api::DATAREQUEST_STATUS_AWAITING_APPROVAL, true], 848 [api::DATAREQUEST_STATUS_APPROVED, true], 849 [api::DATAREQUEST_STATUS_PROCESSING, true], 850 [api::DATAREQUEST_STATUS_COMPLETE, false], 851 [api::DATAREQUEST_STATUS_CANCELLED, false], 852 [api::DATAREQUEST_STATUS_REJECTED, false], 853 [api::DATAREQUEST_STATUS_DOWNLOAD_READY, false], 854 [api::DATAREQUEST_STATUS_EXPIRED, false], 855 [api::DATAREQUEST_STATUS_DELETED, false], 856 ]; 857 } 858 859 /** 860 * Test for api::has_ongoing_request() 861 * 862 * @dataProvider status_provider 863 * @param int $status The request status. 864 * @param bool $expected The expected result. 865 */ 866 public function test_has_ongoing_request($status, $expected) { 867 $this->resetAfterTest(); 868 869 $generator = new testing_data_generator(); 870 $user1 = $generator->create_user(); 871 872 // Make a data request as user 1. 873 $this->setUser($user1); 874 $request = api::create_data_request($user1->id, api::DATAREQUEST_TYPE_EXPORT); 875 // Set the status. 876 api::update_request_status($request->get('id'), $status); 877 878 // Check if this request is ongoing. 879 $result = api::has_ongoing_request($user1->id, api::DATAREQUEST_TYPE_EXPORT); 880 $this->assertEquals($expected, $result); 881 } 882 883 /** 884 * Test for api::is_active() 885 * 886 * @dataProvider status_provider 887 * @param int $status The request status 888 * @param bool $expected The expected result 889 */ 890 public function test_is_active($status, $expected) { 891 // Check if this request is ongoing. 892 $result = api::is_active($status); 893 $this->assertEquals($expected, $result); 894 } 895 896 /** 897 * Test for api::is_site_dpo() 898 */ 899 public function test_is_site_dpo() { 900 global $DB; 901 902 $this->resetAfterTest(); 903 904 // No configured site DPOs yet. 905 $admin = get_admin(); 906 $this->assertTrue(api::is_site_dpo($admin->id)); 907 908 $generator = new testing_data_generator(); 909 $dpo = $generator->create_user(); 910 $nondpo = $generator->create_user(); 911 912 $context = \context_system::instance(); 913 914 // Manager role. 915 $managerroleid = $DB->get_field('role', 'id', array('shortname' => 'manager')); 916 // Give the manager role with the capability to manage data requests. 917 assign_capability('tool/dataprivacy:managedatarequests', CAP_ALLOW, $managerroleid, $context->id, true); 918 // Assign u1 as a manager. 919 role_assign($managerroleid, $dpo->id, $context->id); 920 921 // Map only the manager role to the DPO role. 922 set_config('dporoles', $managerroleid, 'tool_dataprivacy'); 923 924 // User is a DPO. 925 $this->assertTrue(api::is_site_dpo($dpo->id)); 926 // User is not a DPO. 927 $this->assertFalse(api::is_site_dpo($nondpo->id)); 928 } 929 930 /** 931 * Data provider function for test_notify_dpo 932 * 933 * @return array 934 */ 935 public function notify_dpo_provider() { 936 return [ 937 [false, api::DATAREQUEST_TYPE_EXPORT, 'requesttypeexport', 'Export my user data'], 938 [false, api::DATAREQUEST_TYPE_DELETE, 'requesttypedelete', 'Delete my user data'], 939 [false, api::DATAREQUEST_TYPE_OTHERS, 'requesttypeothers', 'Nothing. Just wanna say hi'], 940 [true, api::DATAREQUEST_TYPE_EXPORT, 'requesttypeexport', 'Admin export data of another user'], 941 ]; 942 } 943 944 /** 945 * Test for api::notify_dpo() 946 * 947 * @dataProvider notify_dpo_provider 948 * @param bool $byadmin Whether the admin requests data on behalf of the user 949 * @param int $type The request type 950 * @param string $typestringid The request lang string identifier 951 * @param string $comments The requestor's message to the DPO. 952 */ 953 public function test_notify_dpo($byadmin, $type, $typestringid, $comments) { 954 $this->resetAfterTest(); 955 956 $generator = new testing_data_generator(); 957 $user1 = $generator->create_user(); 958 // Let's just use admin as DPO (It's the default if not set). 959 $dpo = get_admin(); 960 if ($byadmin) { 961 $this->setAdminUser(); 962 $requestedby = $dpo; 963 } else { 964 $this->setUser($user1); 965 $requestedby = $user1; 966 } 967 968 // Make a data request for user 1. 969 $request = api::create_data_request($user1->id, $type, $comments); 970 971 $sink = $this->redirectMessages(); 972 $messageid = api::notify_dpo($dpo, $request); 973 $this->assertNotFalse($messageid); 974 $messages = $sink->get_messages(); 975 $this->assertCount(1, $messages); 976 $message = reset($messages); 977 978 // Check some of the message properties. 979 $this->assertEquals($requestedby->id, $message->useridfrom); 980 $this->assertEquals($dpo->id, $message->useridto); 981 $typestring = get_string($typestringid, 'tool_dataprivacy'); 982 $subject = get_string('datarequestemailsubject', 'tool_dataprivacy', $typestring); 983 $this->assertEquals($subject, $message->subject); 984 $this->assertEquals('tool_dataprivacy', $message->component); 985 $this->assertEquals('contactdataprotectionofficer', $message->eventtype); 986 $this->assertStringContainsString(fullname($dpo), $message->fullmessage); 987 $this->assertStringContainsString(fullname($user1), $message->fullmessage); 988 } 989 990 /** 991 * Test data purposes CRUD actions. 992 * 993 * @return null 994 */ 995 public function test_purpose_crud() { 996 $this->resetAfterTest(); 997 998 $this->setAdminUser(); 999 1000 // Add. 1001 $purpose = api::create_purpose((object)[ 1002 'name' => 'bbb', 1003 'description' => '<b>yeah</b>', 1004 'descriptionformat' => 1, 1005 'retentionperiod' => 'PT1M', 1006 'lawfulbases' => 'gdpr_art_6_1_a,gdpr_art_6_1_c,gdpr_art_6_1_e' 1007 ]); 1008 $this->assertInstanceOf('\tool_dataprivacy\purpose', $purpose); 1009 $this->assertEquals('bbb', $purpose->get('name')); 1010 $this->assertEquals('PT1M', $purpose->get('retentionperiod')); 1011 $this->assertEquals('gdpr_art_6_1_a,gdpr_art_6_1_c,gdpr_art_6_1_e', $purpose->get('lawfulbases')); 1012 1013 // Update. 1014 $purpose->set('retentionperiod', 'PT2M'); 1015 $purpose = api::update_purpose($purpose->to_record()); 1016 $this->assertEquals('PT2M', $purpose->get('retentionperiod')); 1017 1018 // Retrieve. 1019 $purpose = api::create_purpose((object)['name' => 'aaa', 'retentionperiod' => 'PT1M', 'lawfulbases' => 'gdpr_art_6_1_a']); 1020 $purposes = api::get_purposes(); 1021 $this->assertCount(2, $purposes); 1022 $this->assertEquals('aaa', $purposes[0]->get('name')); 1023 $this->assertEquals('bbb', $purposes[1]->get('name')); 1024 1025 // Delete. 1026 api::delete_purpose($purposes[0]->get('id')); 1027 $this->assertCount(1, api::get_purposes()); 1028 api::delete_purpose($purposes[1]->get('id')); 1029 $this->assertCount(0, api::get_purposes()); 1030 } 1031 1032 /** 1033 * Test data categories CRUD actions. 1034 * 1035 * @return null 1036 */ 1037 public function test_category_crud() { 1038 $this->resetAfterTest(); 1039 1040 $this->setAdminUser(); 1041 1042 // Add. 1043 $category = api::create_category((object)[ 1044 'name' => 'bbb', 1045 'description' => '<b>yeah</b>', 1046 'descriptionformat' => 1 1047 ]); 1048 $this->assertInstanceOf('\tool_dataprivacy\category', $category); 1049 $this->assertEquals('bbb', $category->get('name')); 1050 1051 // Update. 1052 $category->set('name', 'bcd'); 1053 $category = api::update_category($category->to_record()); 1054 $this->assertEquals('bcd', $category->get('name')); 1055 1056 // Retrieve. 1057 $category = api::create_category((object)['name' => 'aaa']); 1058 $categories = api::get_categories(); 1059 $this->assertCount(2, $categories); 1060 $this->assertEquals('aaa', $categories[0]->get('name')); 1061 $this->assertEquals('bcd', $categories[1]->get('name')); 1062 1063 // Delete. 1064 api::delete_category($categories[0]->get('id')); 1065 $this->assertCount(1, api::get_categories()); 1066 api::delete_category($categories[1]->get('id')); 1067 $this->assertCount(0, api::get_categories()); 1068 } 1069 1070 /** 1071 * Test context instances. 1072 * 1073 * @return null 1074 */ 1075 public function test_context_instances() { 1076 global $DB; 1077 1078 $this->resetAfterTest(); 1079 1080 $this->setAdminUser(); 1081 1082 list($purposes, $categories, $courses, $modules) = $this->add_purposes_and_categories(); 1083 1084 $coursecontext1 = \context_course::instance($courses[0]->id); 1085 $coursecontext2 = \context_course::instance($courses[1]->id); 1086 1087 $record1 = (object)['contextid' => $coursecontext1->id, 'purposeid' => $purposes[0]->get('id'), 1088 'categoryid' => $categories[0]->get('id')]; 1089 $contextinstance1 = api::set_context_instance($record1); 1090 1091 $record2 = (object)['contextid' => $coursecontext2->id, 'purposeid' => $purposes[1]->get('id'), 1092 'categoryid' => $categories[1]->get('id')]; 1093 $contextinstance2 = api::set_context_instance($record2); 1094 1095 $this->assertCount(2, $DB->get_records('tool_dataprivacy_ctxinstance')); 1096 1097 api::unset_context_instance($contextinstance1); 1098 $this->assertCount(1, $DB->get_records('tool_dataprivacy_ctxinstance')); 1099 1100 $update = (object)['id' => $contextinstance2->get('id'), 'contextid' => $coursecontext2->id, 1101 'purposeid' => $purposes[0]->get('id'), 'categoryid' => $categories[0]->get('id')]; 1102 $contextinstance2 = api::set_context_instance($update); 1103 $this->assertCount(1, $DB->get_records('tool_dataprivacy_ctxinstance')); 1104 } 1105 1106 /** 1107 * Test contextlevel. 1108 * 1109 * @return null 1110 */ 1111 public function test_contextlevel() { 1112 global $DB; 1113 1114 $this->resetAfterTest(); 1115 1116 $this->setAdminUser(); 1117 list($purposes, $categories, $courses, $modules) = $this->add_purposes_and_categories(); 1118 1119 $record = (object)[ 1120 'purposeid' => $purposes[0]->get('id'), 1121 'categoryid' => $categories[0]->get('id'), 1122 'contextlevel' => CONTEXT_SYSTEM, 1123 ]; 1124 $contextlevel = api::set_contextlevel($record); 1125 $this->assertInstanceOf('\tool_dataprivacy\contextlevel', $contextlevel); 1126 $this->assertEquals($record->contextlevel, $contextlevel->get('contextlevel')); 1127 $this->assertEquals($record->purposeid, $contextlevel->get('purposeid')); 1128 $this->assertEquals($record->categoryid, $contextlevel->get('categoryid')); 1129 1130 // Now update it. 1131 $record->purposeid = $purposes[1]->get('id'); 1132 $contextlevel = api::set_contextlevel($record); 1133 $this->assertEquals($record->contextlevel, $contextlevel->get('contextlevel')); 1134 $this->assertEquals($record->purposeid, $contextlevel->get('purposeid')); 1135 $this->assertEquals(1, $DB->count_records('tool_dataprivacy_ctxlevel')); 1136 1137 $record->contextlevel = CONTEXT_USER; 1138 $contextlevel = api::set_contextlevel($record); 1139 $this->assertEquals(2, $DB->count_records('tool_dataprivacy_ctxlevel')); 1140 } 1141 1142 /** 1143 * Test effective context levels purpose and category defaults. 1144 * 1145 * @return null 1146 */ 1147 public function test_effective_contextlevel_defaults() { 1148 $this->setAdminUser(); 1149 1150 $this->resetAfterTest(); 1151 1152 list($purposes, $categories, $courses, $modules) = $this->add_purposes_and_categories(); 1153 1154 list($purposeid, $categoryid) = data_registry::get_effective_default_contextlevel_purpose_and_category(CONTEXT_SYSTEM); 1155 $this->assertEquals(false, $purposeid); 1156 $this->assertEquals(false, $categoryid); 1157 1158 list($purposevar, $categoryvar) = data_registry::var_names_from_context( 1159 \context_helper::get_class_for_level(CONTEXT_SYSTEM) 1160 ); 1161 set_config($purposevar, $purposes[0]->get('id'), 'tool_dataprivacy'); 1162 1163 list($purposeid, $categoryid) = data_registry::get_effective_default_contextlevel_purpose_and_category(CONTEXT_SYSTEM); 1164 $this->assertEquals($purposes[0]->get('id'), $purposeid); 1165 $this->assertEquals(false, $categoryid); 1166 1167 // Course defined values should have preference. 1168 list($purposevar, $categoryvar) = data_registry::var_names_from_context( 1169 \context_helper::get_class_for_level(CONTEXT_COURSE) 1170 ); 1171 set_config($purposevar, $purposes[1]->get('id'), 'tool_dataprivacy'); 1172 set_config($categoryvar, $categories[0]->get('id'), 'tool_dataprivacy'); 1173 1174 list($purposeid, $categoryid) = data_registry::get_effective_default_contextlevel_purpose_and_category(CONTEXT_COURSE); 1175 $this->assertEquals($purposes[1]->get('id'), $purposeid); 1176 $this->assertEquals($categories[0]->get('id'), $categoryid); 1177 1178 // Context level defaults are also allowed to be set to 'inherit'. 1179 set_config($purposevar, context_instance::INHERIT, 'tool_dataprivacy'); 1180 } 1181 1182 /** 1183 * Ensure that when nothing is configured, all values return false. 1184 */ 1185 public function test_get_effective_contextlevel_unset() { 1186 // Before setup, get_effective_contextlevel_purpose will return false. 1187 $this->assertFalse(api::get_effective_contextlevel_category(CONTEXT_SYSTEM)); 1188 $this->assertFalse(api::get_effective_contextlevel_purpose(CONTEXT_SYSTEM)); 1189 1190 $this->assertFalse(api::get_effective_contextlevel_category(CONTEXT_USER)); 1191 $this->assertFalse(api::get_effective_contextlevel_purpose(CONTEXT_USER)); 1192 } 1193 1194 /** 1195 * Ensure that when nothing is configured, all values return false. 1196 */ 1197 public function test_get_effective_context_unset() { 1198 // Before setup, get_effective_contextlevel_purpose will return false. 1199 $this->assertFalse(api::get_effective_context_category(\context_system::instance())); 1200 $this->assertFalse(api::get_effective_context_purpose(\context_system::instance())); 1201 } 1202 1203 /** 1204 * Ensure that fetching the effective value for context levels is only available to system, and user context levels. 1205 * 1206 * @dataProvider invalid_effective_contextlevel_provider 1207 * @param int $contextlevel 1208 */ 1209 public function test_set_contextlevel_invalid_contextlevels($contextlevel) { 1210 1211 $this->expectException(\coding_exception::class); 1212 api::set_contextlevel((object) [ 1213 'contextlevel' => $contextlevel, 1214 ]); 1215 1216 } 1217 1218 /** 1219 * Test effective contextlevel return. 1220 */ 1221 public function test_effective_contextlevel() { 1222 $this->resetAfterTest(); 1223 1224 // Set the initial purpose and category. 1225 $purpose1 = api::create_purpose((object)['name' => 'p1', 'retentionperiod' => 'PT1H', 'lawfulbases' => 'gdpr_art_6_1_a']); 1226 $category1 = api::create_category((object)['name' => 'a']); 1227 api::set_contextlevel((object)[ 1228 'contextlevel' => CONTEXT_SYSTEM, 1229 'purposeid' => $purpose1->get('id'), 1230 'categoryid' => $category1->get('id'), 1231 ]); 1232 1233 $this->assertEquals($purpose1, api::get_effective_contextlevel_purpose(CONTEXT_SYSTEM)); 1234 $this->assertEquals($category1, api::get_effective_contextlevel_category(CONTEXT_SYSTEM)); 1235 1236 // The user context inherits from the system context when not set. 1237 $this->assertEquals($purpose1, api::get_effective_contextlevel_purpose(CONTEXT_USER)); 1238 $this->assertEquals($category1, api::get_effective_contextlevel_category(CONTEXT_USER)); 1239 1240 // Forcing the behaviour to inherit will have the same result. 1241 api::set_contextlevel((object) [ 1242 'contextlevel' => CONTEXT_USER, 1243 'purposeid' => context_instance::INHERIT, 1244 'categoryid' => context_instance::INHERIT, 1245 ]); 1246 $this->assertEquals($purpose1, api::get_effective_contextlevel_purpose(CONTEXT_USER)); 1247 $this->assertEquals($category1, api::get_effective_contextlevel_category(CONTEXT_USER)); 1248 1249 // Setting specific values will override the inheritance behaviour. 1250 $purpose2 = api::create_purpose((object)['name' => 'p2', 'retentionperiod' => 'PT2H', 'lawfulbases' => 'gdpr_art_6_1_a']); 1251 $category2 = api::create_category((object)['name' => 'b']); 1252 // Set the system context level to purpose 1. 1253 api::set_contextlevel((object) [ 1254 'contextlevel' => CONTEXT_USER, 1255 'purposeid' => $purpose2->get('id'), 1256 'categoryid' => $category2->get('id'), 1257 ]); 1258 1259 $this->assertEquals($purpose2, api::get_effective_contextlevel_purpose(CONTEXT_USER)); 1260 $this->assertEquals($category2, api::get_effective_contextlevel_category(CONTEXT_USER)); 1261 } 1262 1263 /** 1264 * Ensure that fetching the effective value for context levels is only available to system, and user context levels. 1265 * 1266 * @dataProvider invalid_effective_contextlevel_provider 1267 * @param int $contextlevel 1268 */ 1269 public function test_effective_contextlevel_invalid_contextlevels($contextlevel) { 1270 $this->resetAfterTest(); 1271 1272 $purpose1 = api::create_purpose((object)['name' => 'p1', 'retentionperiod' => 'PT1H', 'lawfulbases' => 'gdpr_art_6_1_a']); 1273 $category1 = api::create_category((object)['name' => 'a']); 1274 api::set_contextlevel((object)[ 1275 'contextlevel' => CONTEXT_SYSTEM, 1276 'purposeid' => $purpose1->get('id'), 1277 'categoryid' => $category1->get('id'), 1278 ]); 1279 1280 $this->expectException(\coding_exception::class); 1281 api::get_effective_contextlevel_purpose($contextlevel); 1282 } 1283 1284 /** 1285 * Data provider for invalid contextlevel fetchers. 1286 */ 1287 public function invalid_effective_contextlevel_provider() { 1288 return [ 1289 [CONTEXT_COURSECAT], 1290 [CONTEXT_COURSE], 1291 [CONTEXT_MODULE], 1292 [CONTEXT_BLOCK], 1293 ]; 1294 } 1295 1296 /** 1297 * Ensure that context inheritance works up the context tree. 1298 */ 1299 public function test_effective_context_inheritance() { 1300 $this->resetAfterTest(); 1301 1302 $systemdata = $this->create_and_set_purpose_for_contextlevel('PT1S', CONTEXT_SYSTEM); 1303 1304 /* 1305 * System 1306 * - Cat 1307 * - Subcat 1308 * - Course 1309 * - Forum 1310 * - User 1311 * - User block 1312 */ 1313 $cat = $this->getDataGenerator()->create_category(); 1314 $subcat = $this->getDataGenerator()->create_category(['parent' => $cat->id]); 1315 $course = $this->getDataGenerator()->create_course(['category' => $subcat->id]); 1316 $forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]); 1317 list(, $forumcm) = get_course_and_cm_from_instance($forum->id, 'forum'); 1318 1319 $user = $this->getDataGenerator()->create_user(); 1320 1321 $contextsystem = \context_system::instance(); 1322 $contextcat = \context_coursecat::instance($cat->id); 1323 $contextsubcat = \context_coursecat::instance($subcat->id); 1324 $contextcourse = \context_course::instance($course->id); 1325 $contextforum = \context_module::instance($forumcm->id); 1326 $contextuser = \context_user::instance($user->id); 1327 1328 // Initially everything is set to Inherit. 1329 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextsystem)); 1330 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextcat)); 1331 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextcat, "-1")); 1332 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextcat, "0")); 1333 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextsubcat)); 1334 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextsubcat, "-1")); 1335 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextsubcat, "0")); 1336 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextcourse)); 1337 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextcourse, "-1")); 1338 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextcourse, "0")); 1339 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextforum)); 1340 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextforum, "-1")); 1341 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextforum, "0")); 1342 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextuser)); 1343 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextuser, "-1")); 1344 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextuser, "0")); 1345 1346 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextsystem)); 1347 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextcat)); 1348 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextcat, "-1")); 1349 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextcat, "0")); 1350 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextsubcat)); 1351 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextsubcat, "-1")); 1352 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextsubcat, "0")); 1353 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextcourse)); 1354 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextcourse, "-1")); 1355 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextcourse, "0")); 1356 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextforum)); 1357 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextforum, "-1")); 1358 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextforum, "0")); 1359 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextuser)); 1360 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextuser, "-1")); 1361 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextuser, "0")); 1362 1363 // When actively set, user will use the specified value. 1364 $userdata = $this->create_and_set_purpose_for_contextlevel('PT1S', CONTEXT_USER); 1365 1366 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextsystem)); 1367 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextcat)); 1368 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextcat, "-1")); 1369 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextcat, "0")); 1370 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextsubcat)); 1371 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextsubcat, "-1")); 1372 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextsubcat, "0")); 1373 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextcourse)); 1374 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextcourse, "-1")); 1375 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextcourse, "0")); 1376 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextforum)); 1377 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextforum, "-1")); 1378 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextforum, "0")); 1379 $this->assertEquals($userdata->purpose, api::get_effective_context_purpose($contextuser)); 1380 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextuser, "-1")); 1381 1382 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextsystem)); 1383 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextcat)); 1384 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextcat, "-1")); 1385 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextcat, "0")); 1386 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextsubcat)); 1387 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextsubcat, "-1")); 1388 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextsubcat, "0")); 1389 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextcourse)); 1390 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextcourse, "-1")); 1391 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextcourse, "0")); 1392 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextforum)); 1393 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextforum, "-1")); 1394 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextforum, "0")); 1395 $this->assertEquals($userdata->category, api::get_effective_context_category($contextuser)); 1396 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextuser, "-1")); 1397 1398 // Set a context for the top category. 1399 $catpurpose = new purpose(0, (object) [ 1400 'name' => 'Purpose', 1401 'retentionperiod' => 'P1D', 1402 'lawfulbases' => 'gdpr_art_6_1_a', 1403 ]); 1404 $catpurpose->save(); 1405 $catcategory = new category(0, (object) ['name' => 'Category']); 1406 $catcategory->save(); 1407 api::set_context_instance((object) [ 1408 'contextid' => $contextcat->id, 1409 'purposeid' => $catpurpose->get('id'), 1410 'categoryid' => $catcategory->get('id'), 1411 ]); 1412 1413 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextsystem)); 1414 $this->assertEquals($catpurpose, api::get_effective_context_purpose($contextcat)); 1415 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextcat, "-1")); 1416 $this->assertEquals($catpurpose, api::get_effective_context_purpose($contextcat, "0")); 1417 $this->assertEquals($catpurpose, api::get_effective_context_purpose($contextsubcat)); 1418 $this->assertEquals($catpurpose, api::get_effective_context_purpose($contextsubcat, "-1")); 1419 $this->assertEquals($catpurpose, api::get_effective_context_purpose($contextsubcat, "0")); 1420 $this->assertEquals($catpurpose, api::get_effective_context_purpose($contextcourse)); 1421 $this->assertEquals($catpurpose, api::get_effective_context_purpose($contextcourse, "-1")); 1422 $this->assertEquals($catpurpose, api::get_effective_context_purpose($contextcourse, "0")); 1423 $this->assertEquals($catpurpose, api::get_effective_context_purpose($contextforum)); 1424 $this->assertEquals($catpurpose, api::get_effective_context_purpose($contextforum, "-1")); 1425 $this->assertEquals($catpurpose, api::get_effective_context_purpose($contextforum, "0")); 1426 $this->assertEquals($catpurpose, api::get_effective_context_purpose($contextforum, "0")); 1427 1428 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextsystem)); 1429 $this->assertEquals($catcategory, api::get_effective_context_category($contextcat)); 1430 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextcat, "-1")); 1431 $this->assertEquals($catcategory, api::get_effective_context_category($contextcat, "0")); 1432 $this->assertEquals($catcategory, api::get_effective_context_category($contextsubcat)); 1433 $this->assertEquals($catcategory, api::get_effective_context_category($contextsubcat, "-1")); 1434 $this->assertEquals($catcategory, api::get_effective_context_category($contextsubcat, "0")); 1435 $this->assertEquals($catcategory, api::get_effective_context_category($contextcourse)); 1436 $this->assertEquals($catcategory, api::get_effective_context_category($contextcourse, "-1")); 1437 $this->assertEquals($catcategory, api::get_effective_context_category($contextcourse, "0")); 1438 $this->assertEquals($catcategory, api::get_effective_context_category($contextforum)); 1439 $this->assertEquals($catcategory, api::get_effective_context_category($contextforum, "-1")); 1440 $this->assertEquals($catcategory, api::get_effective_context_category($contextforum, "0")); 1441 1442 // Set a context for the sub category. 1443 $subcatpurpose = new purpose(0, (object) [ 1444 'name' => 'Purpose', 1445 'retentionperiod' => 'P1D', 1446 'lawfulbases' => 'gdpr_art_6_1_a', 1447 ]); 1448 $subcatpurpose->save(); 1449 $subcatcategory = new category(0, (object) ['name' => 'Category']); 1450 $subcatcategory->save(); 1451 api::set_context_instance((object) [ 1452 'contextid' => $contextsubcat->id, 1453 'purposeid' => $subcatpurpose->get('id'), 1454 'categoryid' => $subcatcategory->get('id'), 1455 ]); 1456 1457 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextsystem)); 1458 $this->assertEquals($catpurpose, api::get_effective_context_purpose($contextcat)); 1459 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextcat, "-1")); 1460 $this->assertEquals($catpurpose, api::get_effective_context_purpose($contextcat, "0")); 1461 $this->assertEquals($subcatpurpose, api::get_effective_context_purpose($contextsubcat)); 1462 $this->assertEquals($catpurpose, api::get_effective_context_purpose($contextsubcat, "-1")); 1463 $this->assertEquals($subcatpurpose, api::get_effective_context_purpose($contextsubcat, "0")); 1464 $this->assertEquals($subcatpurpose, api::get_effective_context_purpose($contextcourse)); 1465 $this->assertEquals($subcatpurpose, api::get_effective_context_purpose($contextcourse, "-1")); 1466 $this->assertEquals($subcatpurpose, api::get_effective_context_purpose($contextcourse, "0")); 1467 $this->assertEquals($subcatpurpose, api::get_effective_context_purpose($contextforum)); 1468 $this->assertEquals($subcatpurpose, api::get_effective_context_purpose($contextforum, "-1")); 1469 $this->assertEquals($subcatpurpose, api::get_effective_context_purpose($contextforum, "0")); 1470 1471 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextsystem)); 1472 $this->assertEquals($catcategory, api::get_effective_context_category($contextcat)); 1473 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextcat, "-1")); 1474 $this->assertEquals($catcategory, api::get_effective_context_category($contextcat, "0")); 1475 $this->assertEquals($subcatcategory, api::get_effective_context_category($contextsubcat)); 1476 $this->assertEquals($catcategory, api::get_effective_context_category($contextsubcat, "-1")); 1477 $this->assertEquals($subcatcategory, api::get_effective_context_category($contextsubcat, "0")); 1478 $this->assertEquals($subcatcategory, api::get_effective_context_category($contextcourse)); 1479 $this->assertEquals($subcatcategory, api::get_effective_context_category($contextcourse, "-1")); 1480 $this->assertEquals($subcatcategory, api::get_effective_context_category($contextcourse, "0")); 1481 $this->assertEquals($subcatcategory, api::get_effective_context_category($contextforum)); 1482 $this->assertEquals($subcatcategory, api::get_effective_context_category($contextforum, "-1")); 1483 $this->assertEquals($subcatcategory, api::get_effective_context_category($contextforum, "0")); 1484 1485 // Set a context for the course. 1486 $coursepurpose = new purpose(0, (object) [ 1487 'name' => 'Purpose', 1488 'retentionperiod' => 'P1D', 1489 'lawfulbases' => 'gdpr_art_6_1_a', 1490 ]); 1491 $coursepurpose->save(); 1492 $coursecategory = new category(0, (object) ['name' => 'Category']); 1493 $coursecategory->save(); 1494 api::set_context_instance((object) [ 1495 'contextid' => $contextcourse->id, 1496 'purposeid' => $coursepurpose->get('id'), 1497 'categoryid' => $coursecategory->get('id'), 1498 ]); 1499 1500 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextsystem)); 1501 $this->assertEquals($catpurpose, api::get_effective_context_purpose($contextcat)); 1502 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextcat, "-1")); 1503 $this->assertEquals($catpurpose, api::get_effective_context_purpose($contextcat, "0")); 1504 $this->assertEquals($subcatpurpose, api::get_effective_context_purpose($contextsubcat)); 1505 $this->assertEquals($catpurpose, api::get_effective_context_purpose($contextsubcat, "-1")); 1506 $this->assertEquals($subcatpurpose, api::get_effective_context_purpose($contextsubcat, "0")); 1507 $this->assertEquals($coursepurpose, api::get_effective_context_purpose($contextcourse)); 1508 $this->assertEquals($subcatpurpose, api::get_effective_context_purpose($contextcourse, "-1")); 1509 $this->assertEquals($coursepurpose, api::get_effective_context_purpose($contextcourse, "0")); 1510 $this->assertEquals($coursepurpose, api::get_effective_context_purpose($contextforum)); 1511 $this->assertEquals($coursepurpose, api::get_effective_context_purpose($contextforum, "-1")); 1512 $this->assertEquals($coursepurpose, api::get_effective_context_purpose($contextforum, "0")); 1513 1514 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextsystem)); 1515 $this->assertEquals($catcategory, api::get_effective_context_category($contextcat)); 1516 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextcat, "-1")); 1517 $this->assertEquals($catcategory, api::get_effective_context_category($contextcat, "0")); 1518 $this->assertEquals($subcatcategory, api::get_effective_context_category($contextsubcat)); 1519 $this->assertEquals($catcategory, api::get_effective_context_category($contextsubcat, "-1")); 1520 $this->assertEquals($subcatcategory, api::get_effective_context_category($contextsubcat, "0")); 1521 $this->assertEquals($coursecategory, api::get_effective_context_category($contextcourse)); 1522 $this->assertEquals($subcatcategory, api::get_effective_context_category($contextcourse, "-1")); 1523 $this->assertEquals($coursecategory, api::get_effective_context_category($contextcourse, "0")); 1524 $this->assertEquals($coursecategory, api::get_effective_context_category($contextforum)); 1525 $this->assertEquals($coursecategory, api::get_effective_context_category($contextforum, "-1")); 1526 $this->assertEquals($coursecategory, api::get_effective_context_category($contextforum, "0")); 1527 1528 // Set a context for the forum. 1529 $forumpurpose = new purpose(0, (object) [ 1530 'name' => 'Purpose', 1531 'retentionperiod' => 'P1D', 1532 'lawfulbases' => 'gdpr_art_6_1_a', 1533 ]); 1534 $forumpurpose->save(); 1535 $forumcategory = new category(0, (object) ['name' => 'Category']); 1536 $forumcategory->save(); 1537 api::set_context_instance((object) [ 1538 'contextid' => $contextforum->id, 1539 'purposeid' => $forumpurpose->get('id'), 1540 'categoryid' => $forumcategory->get('id'), 1541 ]); 1542 1543 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextsystem)); 1544 $this->assertEquals($catpurpose, api::get_effective_context_purpose($contextcat)); 1545 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextcat, "-1")); 1546 $this->assertEquals($catpurpose, api::get_effective_context_purpose($contextcat, "0")); 1547 $this->assertEquals($subcatpurpose, api::get_effective_context_purpose($contextsubcat)); 1548 $this->assertEquals($catpurpose, api::get_effective_context_purpose($contextsubcat, "-1")); 1549 $this->assertEquals($subcatpurpose, api::get_effective_context_purpose($contextsubcat, "0")); 1550 $this->assertEquals($coursepurpose, api::get_effective_context_purpose($contextcourse)); 1551 $this->assertEquals($subcatpurpose, api::get_effective_context_purpose($contextcourse, "-1")); 1552 $this->assertEquals($coursepurpose, api::get_effective_context_purpose($contextcourse, "0")); 1553 $this->assertEquals($forumpurpose, api::get_effective_context_purpose($contextforum)); 1554 $this->assertEquals($coursepurpose, api::get_effective_context_purpose($contextforum, "-1")); 1555 $this->assertEquals($forumpurpose, api::get_effective_context_purpose($contextforum, "0")); 1556 1557 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextsystem)); 1558 $this->assertEquals($catcategory, api::get_effective_context_category($contextcat)); 1559 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextcat, "-1")); 1560 $this->assertEquals($catcategory, api::get_effective_context_category($contextcat, "0")); 1561 $this->assertEquals($subcatcategory, api::get_effective_context_category($contextsubcat)); 1562 $this->assertEquals($catcategory, api::get_effective_context_category($contextsubcat, "-1")); 1563 $this->assertEquals($subcatcategory, api::get_effective_context_category($contextsubcat, "0")); 1564 $this->assertEquals($coursecategory, api::get_effective_context_category($contextcourse)); 1565 $this->assertEquals($subcatcategory, api::get_effective_context_category($contextcourse, "-1")); 1566 $this->assertEquals($coursecategory, api::get_effective_context_category($contextcourse, "0")); 1567 $this->assertEquals($forumcategory, api::get_effective_context_category($contextforum)); 1568 $this->assertEquals($coursecategory, api::get_effective_context_category($contextforum, "-1")); 1569 $this->assertEquals($forumcategory, api::get_effective_context_category($contextforum, "0")); 1570 } 1571 1572 /** 1573 * Ensure that context inheritance works up the context tree when inherit values are explicitly set at the 1574 * contextlevel. 1575 * 1576 * Although it should not be possible to set hard INHERIT values at this level, there may be legacy data which still 1577 * contains this. 1578 */ 1579 public function test_effective_context_inheritance_explicitly_set() { 1580 $this->resetAfterTest(); 1581 1582 $systemdata = $this->create_and_set_purpose_for_contextlevel('PT1S', CONTEXT_SYSTEM); 1583 1584 /* 1585 * System 1586 * - Cat 1587 * - Subcat 1588 * - Course 1589 * - Forum 1590 * - User 1591 * - User block 1592 */ 1593 $cat = $this->getDataGenerator()->create_category(); 1594 $subcat = $this->getDataGenerator()->create_category(['parent' => $cat->id]); 1595 $course = $this->getDataGenerator()->create_course(['category' => $subcat->id]); 1596 $forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]); 1597 list(, $forumcm) = get_course_and_cm_from_instance($forum->id, 'forum'); 1598 1599 $contextsystem = \context_system::instance(); 1600 $contextcat = \context_coursecat::instance($cat->id); 1601 $contextsubcat = \context_coursecat::instance($subcat->id); 1602 $contextcourse = \context_course::instance($course->id); 1603 $contextforum = \context_module::instance($forumcm->id); 1604 1605 // Initially everything is set to Inherit. 1606 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextsystem)); 1607 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextcat)); 1608 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextsubcat)); 1609 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextcourse)); 1610 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextforum)); 1611 1612 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextsystem)); 1613 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextcat)); 1614 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextsubcat)); 1615 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextcourse)); 1616 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextforum)); 1617 1618 // Set a default value of inherit for CONTEXT_COURSECAT. 1619 $classname = \context_helper::get_class_for_level(CONTEXT_COURSECAT); 1620 list($purposevar, $categoryvar) = data_registry::var_names_from_context($classname); 1621 set_config($purposevar, '-1', 'tool_dataprivacy'); 1622 set_config($categoryvar, '-1', 'tool_dataprivacy'); 1623 1624 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextsystem)); 1625 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextcat)); 1626 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextsubcat)); 1627 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextcourse)); 1628 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextforum)); 1629 1630 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextsystem)); 1631 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextcat)); 1632 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextsubcat)); 1633 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextcourse)); 1634 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextforum)); 1635 1636 // Set a default value of inherit for CONTEXT_COURSE. 1637 $classname = \context_helper::get_class_for_level(CONTEXT_COURSE); 1638 list($purposevar, $categoryvar) = data_registry::var_names_from_context($classname); 1639 set_config($purposevar, '-1', 'tool_dataprivacy'); 1640 set_config($categoryvar, '-1', 'tool_dataprivacy'); 1641 1642 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextsystem)); 1643 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextcat)); 1644 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextsubcat)); 1645 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextcourse)); 1646 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextforum)); 1647 1648 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextsystem)); 1649 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextcat)); 1650 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextsubcat)); 1651 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextcourse)); 1652 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextforum)); 1653 1654 // Set a default value of inherit for CONTEXT_MODULE. 1655 $classname = \context_helper::get_class_for_level(CONTEXT_MODULE); 1656 list($purposevar, $categoryvar) = data_registry::var_names_from_context($classname); 1657 set_config($purposevar, '-1', 'tool_dataprivacy'); 1658 set_config($categoryvar, '-1', 'tool_dataprivacy'); 1659 1660 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextsystem)); 1661 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextcat)); 1662 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextsubcat)); 1663 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextcourse)); 1664 $this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextforum)); 1665 1666 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextsystem)); 1667 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextcat)); 1668 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextsubcat)); 1669 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextcourse)); 1670 $this->assertEquals($systemdata->category, api::get_effective_context_category($contextforum)); 1671 } 1672 1673 /** 1674 * Creates test purposes and categories. 1675 * 1676 * @return null 1677 */ 1678 protected function add_purposes_and_categories() { 1679 $this->resetAfterTest(); 1680 1681 $purpose1 = api::create_purpose((object)['name' => 'p1', 'retentionperiod' => 'PT1H', 'lawfulbases' => 'gdpr_art_6_1_a']); 1682 $purpose2 = api::create_purpose((object)['name' => 'p2', 'retentionperiod' => 'PT2H', 'lawfulbases' => 'gdpr_art_6_1_b']); 1683 $purpose3 = api::create_purpose((object)['name' => 'p3', 'retentionperiod' => 'PT3H', 'lawfulbases' => 'gdpr_art_6_1_c']); 1684 1685 $cat1 = api::create_category((object)['name' => 'a']); 1686 $cat2 = api::create_category((object)['name' => 'b']); 1687 $cat3 = api::create_category((object)['name' => 'c']); 1688 1689 $course1 = $this->getDataGenerator()->create_course(); 1690 $course2 = $this->getDataGenerator()->create_course(); 1691 1692 $module1 = $this->getDataGenerator()->create_module('resource', array('course' => $course1)); 1693 $module2 = $this->getDataGenerator()->create_module('resource', array('course' => $course2)); 1694 1695 return [ 1696 [$purpose1, $purpose2, $purpose3], 1697 [$cat1, $cat2, $cat3], 1698 [$course1, $course2], 1699 [$module1, $module2] 1700 ]; 1701 } 1702 1703 /** 1704 * Test that delete requests do not filter out protected purpose contexts if the the site is properly configured. 1705 */ 1706 public function test_get_approved_contextlist_collection_for_collection_delete_course_no_site_config() { 1707 $this->resetAfterTest(); 1708 1709 $user = $this->getDataGenerator()->create_user(); 1710 1711 $course = $this->getDataGenerator()->create_course(['startdate' => time() - YEARSECS, 'enddate' => time() - YEARSECS]); 1712 $coursecontext = \context_course::instance($course->id); 1713 1714 $forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]); 1715 list(, $forumcm) = get_course_and_cm_from_instance($forum->id, 'forum'); 1716 $contextforum = \context_module::instance($forumcm->id); 1717 1718 $this->getDataGenerator()->enrol_user($user->id, $course->id, 'student'); 1719 1720 // Create the initial contextlist. 1721 $initialcollection = new \core_privacy\local\request\contextlist_collection($user->id); 1722 1723 $contextlist = new \core_privacy\local\request\contextlist(); 1724 $contextlist->add_from_sql('SELECT id FROM {context} WHERE id = :contextid', ['contextid' => $coursecontext->id]); 1725 $contextlist->set_component('tool_dataprivacy'); 1726 $initialcollection->add_contextlist($contextlist); 1727 1728 $contextlist = new \core_privacy\local\request\contextlist(); 1729 $contextlist->add_from_sql('SELECT id FROM {context} WHERE id = :contextid', ['contextid' => $contextforum->id]); 1730 $contextlist->set_component('mod_forum'); 1731 $initialcollection->add_contextlist($contextlist); 1732 1733 $collection = api::get_approved_contextlist_collection_for_collection( 1734 $initialcollection, $user, api::DATAREQUEST_TYPE_DELETE); 1735 1736 $this->assertCount(2, $collection); 1737 1738 $list = $collection->get_contextlist_for_component('tool_dataprivacy'); 1739 $this->assertCount(1, $list); 1740 1741 $list = $collection->get_contextlist_for_component('mod_forum'); 1742 $this->assertCount(1, $list); 1743 } 1744 1745 /** 1746 * Test that delete requests do not filter out protected purpose contexts if they are already expired. 1747 */ 1748 public function test_get_approved_contextlist_collection_for_collection_delete_course_expired_protected() { 1749 $this->resetAfterTest(); 1750 1751 $purposes = $this->setup_basics('PT1H', 'PT1H', 'PT1H'); 1752 $purposes->course->purpose->set('protected', 1)->save(); 1753 1754 $user = $this->getDataGenerator()->create_user(); 1755 $course = $this->getDataGenerator()->create_course(['startdate' => time() - YEARSECS, 'enddate' => time() - YEARSECS]); 1756 $coursecontext = \context_course::instance($course->id); 1757 1758 $this->getDataGenerator()->enrol_user($user->id, $course->id, 'student'); 1759 1760 // Create the initial contextlist. 1761 $contextlist = new \core_privacy\local\request\contextlist(); 1762 $contextlist->add_from_sql('SELECT id FROM {context} WHERE id = :contextid', ['contextid' => $coursecontext->id]); 1763 $contextlist->set_component('tool_dataprivacy'); 1764 1765 $initialcollection = new \core_privacy\local\request\contextlist_collection($user->id); 1766 $initialcollection->add_contextlist($contextlist); 1767 1768 $purposes->course->purpose->set('protected', 1)->save(); 1769 $collection = api::get_approved_contextlist_collection_for_collection( 1770 $initialcollection, $user, api::DATAREQUEST_TYPE_DELETE); 1771 1772 $this->assertCount(1, $collection); 1773 1774 $list = $collection->get_contextlist_for_component('tool_dataprivacy'); 1775 $this->assertCount(1, $list); 1776 } 1777 1778 /** 1779 * Test that delete requests does filter out protected purpose contexts which are not expired. 1780 */ 1781 public function test_get_approved_contextlist_collection_for_collection_delete_course_unexpired_protected() { 1782 $this->resetAfterTest(); 1783 1784 $purposes = $this->setup_basics('PT1H', 'PT1H', 'P1Y'); 1785 $purposes->course->purpose->set('protected', 1)->save(); 1786 1787 $user = $this->getDataGenerator()->create_user(); 1788 $course = $this->getDataGenerator()->create_course(['startdate' => time() - YEARSECS, 'enddate' => time()]); 1789 $coursecontext = \context_course::instance($course->id); 1790 1791 $this->getDataGenerator()->enrol_user($user->id, $course->id, 'student'); 1792 1793 // Create the initial contextlist. 1794 $contextlist = new \core_privacy\local\request\contextlist(); 1795 $contextlist->add_from_sql('SELECT id FROM {context} WHERE id = :contextid', ['contextid' => $coursecontext->id]); 1796 $contextlist->set_component('tool_dataprivacy'); 1797 1798 $initialcollection = new \core_privacy\local\request\contextlist_collection($user->id); 1799 $initialcollection->add_contextlist($contextlist); 1800 1801 $purposes->course->purpose->set('protected', 1)->save(); 1802 $collection = api::get_approved_contextlist_collection_for_collection( 1803 $initialcollection, $user, api::DATAREQUEST_TYPE_DELETE); 1804 1805 $this->assertCount(0, $collection); 1806 1807 $list = $collection->get_contextlist_for_component('tool_dataprivacy'); 1808 $this->assertEmpty($list); 1809 } 1810 1811 /** 1812 * Test that delete requests do not filter out unexpired contexts if they are not protected. 1813 */ 1814 public function test_get_approved_contextlist_collection_for_collection_delete_course_unexpired_unprotected() { 1815 $this->resetAfterTest(); 1816 1817 $purposes = $this->setup_basics('PT1H', 'PT1H', 'P1Y'); 1818 $purposes->course->purpose->set('protected', 1)->save(); 1819 1820 $user = $this->getDataGenerator()->create_user(); 1821 $course = $this->getDataGenerator()->create_course(['startdate' => time() - YEARSECS, 'enddate' => time()]); 1822 $coursecontext = \context_course::instance($course->id); 1823 1824 $this->getDataGenerator()->enrol_user($user->id, $course->id, 'student'); 1825 1826 // Create the initial contextlist. 1827 $contextlist = new \core_privacy\local\request\contextlist(); 1828 $contextlist->add_from_sql('SELECT id FROM {context} WHERE id = :contextid', ['contextid' => $coursecontext->id]); 1829 $contextlist->set_component('tool_dataprivacy'); 1830 1831 $initialcollection = new \core_privacy\local\request\contextlist_collection($user->id); 1832 $initialcollection->add_contextlist($contextlist); 1833 1834 $purposes->course->purpose->set('protected', 0)->save(); 1835 $collection = api::get_approved_contextlist_collection_for_collection( 1836 $initialcollection, $user, api::DATAREQUEST_TYPE_DELETE); 1837 1838 $this->assertCount(1, $collection); 1839 1840 $list = $collection->get_contextlist_for_component('tool_dataprivacy'); 1841 $this->assertCount(1, $list); 1842 } 1843 1844 /** 1845 * Data provider for \tool_dataprivacy_api_testcase::test_set_context_defaults 1846 */ 1847 public function set_context_defaults_provider() { 1848 $contextlevels = [ 1849 [CONTEXT_COURSECAT], 1850 [CONTEXT_COURSE], 1851 [CONTEXT_MODULE], 1852 [CONTEXT_BLOCK], 1853 ]; 1854 $paramsets = [ 1855 [true, true, false, false], // Inherit category and purpose, Not for activity, Don't override. 1856 [true, false, false, false], // Inherit category but not purpose, Not for activity, Don't override. 1857 [false, true, false, false], // Inherit purpose but not category, Not for activity, Don't override. 1858 [false, false, false, false], // Don't inherit both category and purpose, Not for activity, Don't override. 1859 [false, false, false, true], // Don't inherit both category and purpose, Not for activity, Override instances. 1860 ]; 1861 $data = []; 1862 foreach ($contextlevels as $level) { 1863 foreach ($paramsets as $set) { 1864 $data[] = array_merge($level, $set); 1865 } 1866 if ($level == CONTEXT_MODULE) { 1867 // Add a combination where defaults for activity is being set. 1868 $data[] = [CONTEXT_MODULE, false, false, true, false]; 1869 $data[] = [CONTEXT_MODULE, false, false, true, true]; 1870 } 1871 } 1872 return $data; 1873 } 1874 1875 /** 1876 * Test for \tool_dataprivacy\api::set_context_defaults() 1877 * 1878 * @dataProvider set_context_defaults_provider 1879 * @param int $contextlevel The context level 1880 * @param bool $inheritcategory Whether to set category value as INHERIT. 1881 * @param bool $inheritpurpose Whether to set purpose value as INHERIT. 1882 * @param bool $foractivity Whether to set defaults for an activity. 1883 * @param bool $override Whether to override instances. 1884 */ 1885 public function test_set_context_defaults($contextlevel, $inheritcategory, $inheritpurpose, $foractivity, $override) { 1886 $this->resetAfterTest(); 1887 1888 $generator = $this->getDataGenerator(); 1889 1890 // Generate course cat, course, block, assignment, forum instances. 1891 $coursecat = $generator->create_category(); 1892 $course = $generator->create_course(['category' => $coursecat->id]); 1893 $block = $generator->create_block('online_users'); 1894 $assign = $generator->create_module('assign', ['course' => $course->id]); 1895 $forum = $generator->create_module('forum', ['course' => $course->id]); 1896 1897 $coursecatcontext = \context_coursecat::instance($coursecat->id); 1898 $coursecontext = \context_course::instance($course->id); 1899 $blockcontext = \context_block::instance($block->id); 1900 1901 list($course, $assigncm) = get_course_and_cm_from_instance($assign->id, 'assign'); 1902 list($course, $forumcm) = get_course_and_cm_from_instance($forum->id, 'forum'); 1903 $assigncontext = \context_module::instance($assigncm->id); 1904 $forumcontext = \context_module::instance($forumcm->id); 1905 1906 // Generate purposes and categories. 1907 $category1 = api::create_category((object)['name' => 'Test category 1']); 1908 $category2 = api::create_category((object)['name' => 'Test category 2']); 1909 $purpose1 = api::create_purpose((object)[ 1910 'name' => 'Test purpose 1', 'retentionperiod' => 'PT1M', 'lawfulbases' => 'gdpr_art_6_1_a' 1911 ]); 1912 $purpose2 = api::create_purpose((object)[ 1913 'name' => 'Test purpose 2', 'retentionperiod' => 'PT1M', 'lawfulbases' => 'gdpr_art_6_1_a' 1914 ]); 1915 1916 // Assign purposes and categories to contexts. 1917 $coursecatctxinstance = api::set_context_instance((object) [ 1918 'contextid' => $coursecatcontext->id, 1919 'purposeid' => $purpose1->get('id'), 1920 'categoryid' => $category1->get('id'), 1921 ]); 1922 $coursectxinstance = api::set_context_instance((object) [ 1923 'contextid' => $coursecontext->id, 1924 'purposeid' => $purpose1->get('id'), 1925 'categoryid' => $category1->get('id'), 1926 ]); 1927 $blockctxinstance = api::set_context_instance((object) [ 1928 'contextid' => $blockcontext->id, 1929 'purposeid' => $purpose1->get('id'), 1930 'categoryid' => $category1->get('id'), 1931 ]); 1932 $assignctxinstance = api::set_context_instance((object) [ 1933 'contextid' => $assigncontext->id, 1934 'purposeid' => $purpose1->get('id'), 1935 'categoryid' => $category1->get('id'), 1936 ]); 1937 $forumctxinstance = api::set_context_instance((object) [ 1938 'contextid' => $forumcontext->id, 1939 'purposeid' => $purpose1->get('id'), 1940 'categoryid' => $category1->get('id'), 1941 ]); 1942 1943 $categoryid = $inheritcategory ? context_instance::INHERIT : $category2->get('id'); 1944 $purposeid = $inheritpurpose ? context_instance::INHERIT : $purpose2->get('id'); 1945 $activity = ''; 1946 if ($contextlevel == CONTEXT_MODULE && $foractivity) { 1947 $activity = 'assign'; 1948 } 1949 $result = api::set_context_defaults($contextlevel, $categoryid, $purposeid, $activity, $override); 1950 $this->assertTrue($result); 1951 1952 $targetctxinstance = false; 1953 switch ($contextlevel) { 1954 case CONTEXT_COURSECAT: 1955 $targetctxinstance = $coursecatctxinstance; 1956 break; 1957 case CONTEXT_COURSE: 1958 $targetctxinstance = $coursectxinstance; 1959 break; 1960 case CONTEXT_MODULE: 1961 $targetctxinstance = $assignctxinstance; 1962 break; 1963 case CONTEXT_BLOCK: 1964 $targetctxinstance = $blockctxinstance; 1965 break; 1966 } 1967 $this->assertNotFalse($targetctxinstance); 1968 1969 // Check the context instances. 1970 $instanceexists = context_instance::record_exists($targetctxinstance->get('id')); 1971 if ($override) { 1972 // If overridden, context instances on this context level would have been deleted. 1973 $this->assertFalse($instanceexists); 1974 1975 // Check forum context instance. 1976 $forumctxexists = context_instance::record_exists($forumctxinstance->get('id')); 1977 if ($contextlevel != CONTEXT_MODULE || $foractivity) { 1978 // The forum context instance won't be affected in this test if: 1979 // - The overridden defaults are not for context modules. 1980 // - Only the defaults for assign have been set. 1981 $this->assertTrue($forumctxexists); 1982 } else { 1983 // If we're overriding for the whole course module context level, 1984 // then this forum context instance will be deleted as well. 1985 $this->assertFalse($forumctxexists); 1986 } 1987 } else { 1988 // Otherwise, the context instance record remains. 1989 $this->assertTrue($instanceexists); 1990 } 1991 1992 // Check defaults. 1993 list($defaultpurpose, $defaultcategory) = data_registry::get_defaults($contextlevel, $activity); 1994 if (!$inheritpurpose) { 1995 $this->assertEquals($purposeid, $defaultpurpose); 1996 } 1997 if (!$inheritcategory) { 1998 $this->assertEquals($categoryid, $defaultcategory); 1999 } 2000 } 2001 2002 /** 2003 * Setup the basics with the specified retention period. 2004 * 2005 * @param string $system Retention policy for the system. 2006 * @param string $user Retention policy for users. 2007 * @param string $course Retention policy for courses. 2008 * @param string $activity Retention policy for activities. 2009 */ 2010 protected function setup_basics(string $system, string $user, string $course = null, string $activity = null) : \stdClass { 2011 $this->resetAfterTest(); 2012 2013 $purposes = (object) [ 2014 'system' => $this->create_and_set_purpose_for_contextlevel($system, CONTEXT_SYSTEM), 2015 'user' => $this->create_and_set_purpose_for_contextlevel($user, CONTEXT_USER), 2016 ]; 2017 2018 if (null !== $course) { 2019 $purposes->course = $this->create_and_set_purpose_for_contextlevel($course, CONTEXT_COURSE); 2020 } 2021 2022 if (null !== $activity) { 2023 $purposes->activity = $this->create_and_set_purpose_for_contextlevel($activity, CONTEXT_MODULE); 2024 } 2025 2026 return $purposes; 2027 } 2028 2029 /** 2030 * Create a retention period and set it for the specified context level. 2031 * 2032 * @param string $retention 2033 * @param int $contextlevel 2034 */ 2035 protected function create_and_set_purpose_for_contextlevel(string $retention, int $contextlevel) { 2036 $purpose = new purpose(0, (object) [ 2037 'name' => 'Test purpose ' . rand(1, 1000), 2038 'retentionperiod' => $retention, 2039 'lawfulbases' => 'gdpr_art_6_1_a', 2040 ]); 2041 $purpose->create(); 2042 2043 $cat = new category(0, (object) ['name' => 'Test category']); 2044 $cat->create(); 2045 2046 if ($contextlevel <= CONTEXT_USER) { 2047 $record = (object) [ 2048 'purposeid' => $purpose->get('id'), 2049 'categoryid' => $cat->get('id'), 2050 'contextlevel' => $contextlevel, 2051 ]; 2052 api::set_contextlevel($record); 2053 } else { 2054 list($purposevar, ) = data_registry::var_names_from_context( 2055 \context_helper::get_class_for_level(CONTEXT_COURSE) 2056 ); 2057 set_config($purposevar, $purpose->get('id'), 'tool_dataprivacy'); 2058 } 2059 2060 return (object) [ 2061 'purpose' => $purpose, 2062 'category' => $cat, 2063 ]; 2064 } 2065 2066 /** 2067 * Ensure that the find_ongoing_request_types_for_users only returns requests which are active. 2068 */ 2069 public function test_find_ongoing_request_types_for_users() { 2070 $this->resetAfterTest(); 2071 2072 // Create users and their requests:. 2073 // - u1 has no requests of any type. 2074 // - u2 has one rejected export request. 2075 // - u3 has one rejected other request. 2076 // - u4 has one rejected delete request. 2077 // - u5 has one active and one rejected export request. 2078 // - u6 has one active and one rejected other request. 2079 // - u7 has one active and one rejected delete request. 2080 // - u8 has one active export, and one active delete request. 2081 $u1 = $this->getDataGenerator()->create_user(); 2082 $u1expect = (object) []; 2083 2084 $u2 = $this->getDataGenerator()->create_user(); 2085 $this->create_request_with_type_and_status($u2->id, api::DATAREQUEST_TYPE_EXPORT, api::DATAREQUEST_STATUS_REJECTED); 2086 $u2expect = (object) []; 2087 2088 $u3 = $this->getDataGenerator()->create_user(); 2089 $this->create_request_with_type_and_status($u3->id, api::DATAREQUEST_TYPE_OTHERS, api::DATAREQUEST_STATUS_REJECTED); 2090 $u3expect = (object) []; 2091 2092 $u4 = $this->getDataGenerator()->create_user(); 2093 $this->create_request_with_type_and_status($u4->id, api::DATAREQUEST_TYPE_DELETE, api::DATAREQUEST_STATUS_REJECTED); 2094 $u4expect = (object) []; 2095 2096 $u5 = $this->getDataGenerator()->create_user(); 2097 $this->create_request_with_type_and_status($u5->id, api::DATAREQUEST_TYPE_EXPORT, api::DATAREQUEST_STATUS_REJECTED); 2098 $this->create_request_with_type_and_status($u5->id, api::DATAREQUEST_TYPE_EXPORT, api::DATAREQUEST_STATUS_APPROVED); 2099 $u5expect = (object) [ 2100 api::DATAREQUEST_TYPE_EXPORT => true, 2101 ]; 2102 2103 $u6 = $this->getDataGenerator()->create_user(); 2104 $this->create_request_with_type_and_status($u6->id, api::DATAREQUEST_TYPE_OTHERS, api::DATAREQUEST_STATUS_REJECTED); 2105 $this->create_request_with_type_and_status($u6->id, api::DATAREQUEST_TYPE_OTHERS, api::DATAREQUEST_STATUS_APPROVED); 2106 $u6expect = (object) [ 2107 api::DATAREQUEST_TYPE_OTHERS => true, 2108 ]; 2109 2110 $u7 = $this->getDataGenerator()->create_user(); 2111 $this->create_request_with_type_and_status($u7->id, api::DATAREQUEST_TYPE_DELETE, api::DATAREQUEST_STATUS_REJECTED); 2112 $this->create_request_with_type_and_status($u7->id, api::DATAREQUEST_TYPE_DELETE, api::DATAREQUEST_STATUS_APPROVED); 2113 $u7expect = (object) [ 2114 api::DATAREQUEST_TYPE_DELETE => true, 2115 ]; 2116 2117 $u8 = $this->getDataGenerator()->create_user(); 2118 $this->create_request_with_type_and_status($u8->id, api::DATAREQUEST_TYPE_EXPORT, api::DATAREQUEST_STATUS_APPROVED); 2119 $this->create_request_with_type_and_status($u8->id, api::DATAREQUEST_TYPE_DELETE, api::DATAREQUEST_STATUS_APPROVED); 2120 $u8expect = (object) [ 2121 api::DATAREQUEST_TYPE_EXPORT => true, 2122 api::DATAREQUEST_TYPE_DELETE => true, 2123 ]; 2124 2125 // Test with no users specified. 2126 $result = api::find_ongoing_request_types_for_users([]); 2127 $this->assertEquals([], $result); 2128 2129 // Fetch a subset of the users. 2130 $result = api::find_ongoing_request_types_for_users([$u3->id, $u4->id, $u5->id]); 2131 $this->assertEquals([ 2132 $u3->id => $u3expect, 2133 $u4->id => $u4expect, 2134 $u5->id => $u5expect, 2135 ], $result); 2136 2137 // Fetch the empty user. 2138 $result = api::find_ongoing_request_types_for_users([$u1->id]); 2139 $this->assertEquals([ 2140 $u1->id => $u1expect, 2141 ], $result); 2142 2143 // Fetch all. 2144 $result = api::find_ongoing_request_types_for_users( 2145 [$u1->id, $u2->id, $u3->id, $u4->id, $u5->id, $u6->id, $u7->id, $u8->id]); 2146 $this->assertEquals([ 2147 $u1->id => $u1expect, 2148 $u2->id => $u2expect, 2149 $u3->id => $u3expect, 2150 $u4->id => $u4expect, 2151 $u5->id => $u5expect, 2152 $u6->id => $u6expect, 2153 $u7->id => $u7expect, 2154 $u8->id => $u8expect, 2155 ], $result); 2156 } 2157 2158 /** 2159 * Create a new data request for the user with the type and status specified. 2160 * 2161 * @param int $userid 2162 * @param int $type 2163 * @param int $status 2164 * @return \tool_dataprivacy\data_request 2165 */ 2166 protected function create_request_with_type_and_status(int $userid, int $type, int $status) : \tool_dataprivacy\data_request { 2167 $request = new \tool_dataprivacy\data_request(0, (object) [ 2168 'userid' => $userid, 2169 'type' => $type, 2170 'status' => $status, 2171 ]); 2172 2173 $request->save(); 2174 2175 return $request; 2176 } 2177 2178 /** 2179 * Test whether user can create data download request for themselves 2180 */ 2181 public function test_can_create_data_download_request_for_self(): void { 2182 global $DB; 2183 2184 $this->resetAfterTest(); 2185 2186 $user = $this->getDataGenerator()->create_user(); 2187 $this->setUser($user); 2188 2189 // The default user role allows for the creation of download data requests. 2190 $this->assertTrue(api::can_create_data_download_request_for_self()); 2191 2192 // Prohibit that capability. 2193 $userrole = $DB->get_field('role', 'id', ['shortname' => 'user'], MUST_EXIST); 2194 assign_capability('tool/dataprivacy:downloadownrequest', CAP_PROHIBIT, $userrole, \context_user::instance($user->id)); 2195 2196 $this->assertFalse(api::can_create_data_download_request_for_self()); 2197 } 2198 2199 /** 2200 * Test user cannot create data deletion request for themselves if they don't have 2201 * "tool/dataprivacy:requestdelete" capability. 2202 * 2203 * @throws coding_exception 2204 */ 2205 public function test_can_create_data_deletion_request_for_self_no() { 2206 $this->resetAfterTest(); 2207 $userid = $this->getDataGenerator()->create_user()->id; 2208 $roleid = $this->getDataGenerator()->create_role(); 2209 assign_capability('tool/dataprivacy:requestdelete', CAP_PROHIBIT, $roleid, \context_user::instance($userid)); 2210 role_assign($roleid, $userid, \context_user::instance($userid)); 2211 $this->setUser($userid); 2212 $this->assertFalse(api::can_create_data_deletion_request_for_self()); 2213 } 2214 2215 /** 2216 * Test primary admin cannot create data deletion request for themselves 2217 */ 2218 public function test_can_create_data_deletion_request_for_self_primary_admin() { 2219 $this->resetAfterTest(); 2220 $this->setAdminUser(); 2221 $this->assertFalse(api::can_create_data_deletion_request_for_self()); 2222 } 2223 2224 /** 2225 * Test secondary admin can create data deletion request for themselves 2226 */ 2227 public function test_can_create_data_deletion_request_for_self_secondary_admin() { 2228 $this->resetAfterTest(); 2229 2230 $admin1 = $this->getDataGenerator()->create_user(); 2231 $admin2 = $this->getDataGenerator()->create_user(); 2232 2233 // The primary admin is the one listed first in the 'siteadmins' config. 2234 set_config('siteadmins', implode(',', [$admin1->id, $admin2->id])); 2235 2236 // Set the current user as the second admin (non-primary). 2237 $this->setUser($admin2); 2238 2239 $this->assertTrue(api::can_create_data_deletion_request_for_self()); 2240 } 2241 2242 /** 2243 * Test user can create data deletion request for themselves if they have 2244 * "tool/dataprivacy:requestdelete" capability. 2245 * 2246 * @throws coding_exception 2247 */ 2248 public function test_can_create_data_deletion_request_for_self_yes() { 2249 $this->resetAfterTest(); 2250 $userid = $this->getDataGenerator()->create_user()->id; 2251 $this->setUser($userid); 2252 $this->assertTrue(api::can_create_data_deletion_request_for_self()); 2253 } 2254 2255 /** 2256 * Test user cannot create data deletion request for another user if they 2257 * don't have "tool/dataprivacy:requestdeleteforotheruser" capability. 2258 * 2259 * @throws coding_exception 2260 * @throws dml_exception 2261 */ 2262 public function test_can_create_data_deletion_request_for_other_no() { 2263 $this->resetAfterTest(); 2264 $userid = $this->getDataGenerator()->create_user()->id; 2265 $this->setUser($userid); 2266 $this->assertFalse(api::can_create_data_deletion_request_for_other()); 2267 } 2268 2269 /** 2270 * Test user can create data deletion request for another user if they 2271 * don't have "tool/dataprivacy:requestdeleteforotheruser" capability. 2272 * 2273 * @throws coding_exception 2274 */ 2275 public function test_can_create_data_deletion_request_for_other_yes() { 2276 $this->resetAfterTest(); 2277 $userid = $this->getDataGenerator()->create_user()->id; 2278 $roleid = $this->getDataGenerator()->create_role(); 2279 $contextsystem = \context_system::instance(); 2280 assign_capability('tool/dataprivacy:requestdeleteforotheruser', CAP_ALLOW, $roleid, $contextsystem); 2281 role_assign($roleid, $userid, $contextsystem); 2282 $this->setUser($userid); 2283 $this->assertTrue(api::can_create_data_deletion_request_for_other($userid)); 2284 } 2285 2286 /** 2287 * Check parents can create data deletion request for their children (unless the child is the primary admin), 2288 * but not other users. 2289 * 2290 * @throws coding_exception 2291 * @throws dml_exception 2292 */ 2293 public function test_can_create_data_deletion_request_for_children() { 2294 $this->resetAfterTest(); 2295 2296 $parent = $this->getDataGenerator()->create_user(); 2297 $child = $this->getDataGenerator()->create_user(); 2298 $otheruser = $this->getDataGenerator()->create_user(); 2299 2300 $contextsystem = \context_system::instance(); 2301 $parentrole = $this->getDataGenerator()->create_role(); 2302 assign_capability('tool/dataprivacy:makedatarequestsforchildren', CAP_ALLOW, 2303 $parentrole, $contextsystem); 2304 assign_capability('tool/dataprivacy:makedatadeletionrequestsforchildren', CAP_ALLOW, 2305 $parentrole, $contextsystem); 2306 role_assign($parentrole, $parent->id, \context_user::instance($child->id)); 2307 2308 $this->setUser($parent); 2309 $this->assertTrue(api::can_create_data_deletion_request_for_children($child->id)); 2310 $this->assertFalse(api::can_create_data_deletion_request_for_children($otheruser->id)); 2311 2312 // Now make child the primary admin, confirm parent can't make deletion request. 2313 set_config('siteadmins', $child->id); 2314 $this->assertFalse(api::can_create_data_deletion_request_for_children($child->id)); 2315 } 2316 2317 /** 2318 * Data provider function for testing \tool_dataprivacy\api::queue_data_request_task(). 2319 * 2320 * @return array 2321 */ 2322 public function queue_data_request_task_provider() { 2323 return [ 2324 'With user ID provided' => [true], 2325 'Without user ID provided' => [false], 2326 ]; 2327 } 2328 2329 /** 2330 * Test for \tool_dataprivacy\api::queue_data_request_task(). 2331 * 2332 * @dataProvider queue_data_request_task_provider 2333 * @param bool $withuserid 2334 */ 2335 public function test_queue_data_request_task(bool $withuserid) { 2336 $this->resetAfterTest(); 2337 2338 $this->setAdminUser(); 2339 2340 if ($withuserid) { 2341 $user = $this->getDataGenerator()->create_user(); 2342 api::queue_data_request_task(1, $user->id); 2343 $expecteduserid = $user->id; 2344 } else { 2345 api::queue_data_request_task(1); 2346 $expecteduserid = null; 2347 } 2348 2349 // Test number of queued data request tasks. 2350 $datarequesttasks = manager::get_adhoc_tasks(process_data_request_task::class); 2351 $this->assertCount(1, $datarequesttasks); 2352 $requesttask = reset($datarequesttasks); 2353 $this->assertEquals($expecteduserid, $requesttask->get_userid()); 2354 } 2355 2356 /** 2357 * Data provider for test_is_automatic_request_approval_on(). 2358 */ 2359 public function automatic_request_approval_setting_provider() { 2360 return [ 2361 'Data export, not set' => [ 2362 'automaticdataexportapproval', api::DATAREQUEST_TYPE_EXPORT, null, false 2363 ], 2364 'Data export, turned on' => [ 2365 'automaticdataexportapproval', api::DATAREQUEST_TYPE_EXPORT, true, true 2366 ], 2367 'Data export, turned off' => [ 2368 'automaticdataexportapproval', api::DATAREQUEST_TYPE_EXPORT, false, false 2369 ], 2370 'Data deletion, not set' => [ 2371 'automaticdatadeletionapproval', api::DATAREQUEST_TYPE_DELETE, null, false 2372 ], 2373 'Data deletion, turned on' => [ 2374 'automaticdatadeletionapproval', api::DATAREQUEST_TYPE_DELETE, true, true 2375 ], 2376 'Data deletion, turned off' => [ 2377 'automaticdatadeletionapproval', api::DATAREQUEST_TYPE_DELETE, false, false 2378 ], 2379 ]; 2380 } 2381 2382 /** 2383 * Test for \tool_dataprivacy\api::is_automatic_request_approval_on(). 2384 * 2385 * @dataProvider automatic_request_approval_setting_provider 2386 * @param string $setting The automatic approval setting. 2387 * @param int $type The data request type. 2388 * @param bool $value The setting's value. 2389 * @param bool $expected The expected result. 2390 */ 2391 public function test_is_automatic_request_approval_on($setting, $type, $value, $expected) { 2392 $this->resetAfterTest(); 2393 2394 if ($value !== null) { 2395 set_config($setting, $value, 'tool_dataprivacy'); 2396 } 2397 2398 $this->assertEquals($expected, api::is_automatic_request_approval_on($type)); 2399 } 2400 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body