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