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