Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

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