1<?php
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
17/**
18 * Data provider tests.
19 *
20 * @package    logstore_legacy
21 * @category   test
22 * @copyright  2018 Frédéric Massart
23 * @author     Frédéric Massart <fred@branchup.tech>
24 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 */
26
27defined('MOODLE_INTERNAL') || die();
28global $CFG;
29
30use core_privacy\tests\provider_testcase;
31use core_privacy\local\request\contextlist;
32use core_privacy\local\request\approved_contextlist;
33use core_privacy\local\request\transform;
34use core_privacy\local\request\writer;
35use logstore_legacy\privacy\provider;
36use logstore_legacy\event\unittest_executed;
37
38require_once(__DIR__ . '/fixtures/event.php');
39
40/**
41 * Data provider testcase class.
42 *
43 * @package    logstore_legacy
44 * @category   test
45 * @copyright  2018 Frédéric Massart
46 * @author     Frédéric Massart <fred@branchup.tech>
47 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
48 */
49class logstore_legacy_privacy_testcase extends provider_testcase {
50
51    public function setUp() {
52        $this->resetAfterTest();
53    }
54
55    public function test_get_contexts_for_userid() {
56        $u1 = $this->getDataGenerator()->create_user();
57        $u2 = $this->getDataGenerator()->create_user();
58        $u3 = $this->getDataGenerator()->create_user();
59        $c1 = $this->getDataGenerator()->create_course();
60        $cm1 = $this->getDataGenerator()->create_module('url', ['course' => $c1]);
61        $sysctx = context_system::instance();
62        $c1ctx = context_course::instance($c1->id);
63        $cm1ctx = context_module::instance($cm1->cmid);
64
65        $this->enable_logging();
66        $manager = get_log_manager(true);
67
68        // User 1 is the author.
69        $this->setUser($u1);
70        $this->assert_contextlist_equals($this->get_contextlist_for_user($u1), []);
71        $e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 1]]);
72        $e->trigger();
73        $this->assert_contextlist_equals($this->get_contextlist_for_user($u1), [$cm1ctx]);
74
75        // User 2 is the author.
76        $this->setUser($u2);
77        $this->assert_contextlist_equals($this->get_contextlist_for_user($u2), []);
78        $e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 2]]);
79        $e->trigger();
80        $this->assert_contextlist_equals($this->get_contextlist_for_user($u2), [$cm1ctx]);
81
82        // User 3 is the author.
83        $this->setUser($u3);
84        $this->assert_contextlist_equals($this->get_contextlist_for_user($u3), []);
85        $e = unittest_executed::create(['context' => $sysctx, 'other' => ['sample' => 3]]);
86        $e->trigger();
87        $this->assert_contextlist_equals($this->get_contextlist_for_user($u3), [$sysctx]);
88    }
89
90    /**
91     * Test returning user IDs for a given context.
92     */
93    public function test_add_userids_for_context() {
94        $u1 = $this->getDataGenerator()->create_user();
95        $u2 = $this->getDataGenerator()->create_user();
96        $u3 = $this->getDataGenerator()->create_user();
97        $course = $this->getDataGenerator()->create_course();
98        $module = $this->getDataGenerator()->create_module('url', ['course' => $course]);
99        $sysctx = context_system::instance();
100        $c1ctx = context_course::instance($course->id);
101        $cm1ctx = context_module::instance($module->cmid);
102
103        $userctx = context_user::instance($u1->id);
104
105        $this->enable_logging();
106        $manager = get_log_manager(true);
107
108        $this->setUser($u1);
109        $e = unittest_executed::create(['context' => $sysctx, 'other' => ['sample' => 1]]);
110        $e->trigger();
111        $this->setUser($u2);
112        $e = unittest_executed::create(['context' => $sysctx, 'other' => ['sample' => 2]]);
113        $e->trigger();
114        $e = unittest_executed::create(['context' => $sysctx, 'other' => ['sample' => 3]]);
115        $e->trigger();
116        $this->setUser($u3);
117        $e = unittest_executed::create(['context' => $c1ctx, 'other' => ['sample' => 4]]);
118        $e->trigger();
119        $this->setUser($u1);
120        $e = unittest_executed::create(['context' => $c1ctx, 'other' => ['sample' => 5]]);
121        $e->trigger();
122        $e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 6]]);
123        $e->trigger();
124        $this->setUser($u2);
125        $e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 7]]);
126        $e->trigger();
127        $this->setUser($u3);
128        $e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 8]]);
129        $e->trigger();
130
131        // Start with system and check that each of the contexts returns what we expected.
132        $userlist = new \core_privacy\local\request\userlist($sysctx, 'logstore_legacy');
133        provider::add_userids_for_context($userlist);
134        $systemuserids = $userlist->get_userids();
135        $this->assertCount(2, $systemuserids);
136        $this->assertNotFalse(array_search($u1->id, $systemuserids));
137        $this->assertNotFalse(array_search($u2->id, $systemuserids));
138        // Check the course context.
139        $userlist = new \core_privacy\local\request\userlist($c1ctx, 'logstore_legacy');
140        provider::add_userids_for_context($userlist);
141        $courseuserids = $userlist->get_userids();
142        $this->assertCount(2, $courseuserids);
143        $this->assertNotFalse(array_search($u1->id, $courseuserids));
144        $this->assertNotFalse(array_search($u3->id, $courseuserids));
145        // Check the module context.
146        $userlist = new \core_privacy\local\request\userlist($cm1ctx, 'logstore_legacy');
147        provider::add_userids_for_context($userlist);
148        $moduleuserids = $userlist->get_userids();
149        $this->assertCount(3, $moduleuserids);
150        $this->assertNotFalse(array_search($u1->id, $moduleuserids));
151        $this->assertNotFalse(array_search($u2->id, $moduleuserids));
152        $this->assertNotFalse(array_search($u3->id, $moduleuserids));
153    }
154
155    public function test_delete_data_for_user() {
156        global $DB;
157
158        $u1 = $this->getDataGenerator()->create_user();
159        $u2 = $this->getDataGenerator()->create_user();
160        $u3 = $this->getDataGenerator()->create_user();
161        $c1 = $this->getDataGenerator()->create_course();
162        $c2 = $this->getDataGenerator()->create_course();
163        $cm1 = $this->getDataGenerator()->create_module('url', ['course' => $c1]);
164        $sysctx = context_system::instance();
165        $c1ctx = context_course::instance($c1->id);
166        $c2ctx = context_course::instance($c2->id);
167        $cm1ctx = context_module::instance($cm1->cmid);
168
169        $this->enable_logging();
170        $manager = get_log_manager(true);
171
172        // User 1 is the author.
173        $this->setUser($u1);
174        $e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 1]]);
175        $e->trigger();
176        $e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 2]]);
177        $e->trigger();
178        $e = unittest_executed::create(['context' => $c1ctx, 'other' => ['sample' => 3]]);
179        $e->trigger();
180        $e = unittest_executed::create(['context' => $sysctx, 'other' => ['sample' => 4]]);
181        $e->trigger();
182
183        // User 2 is the author.
184        $this->setUser($u2);
185        $e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 5]]);
186        $e->trigger();
187        $e = unittest_executed::create(['context' => $c1ctx, 'other' => ['sample' => 6]]);
188        $e->trigger();
189        $e = unittest_executed::create(['context' => $sysctx, 'other' => ['sample' => 7]]);
190        $e->trigger();
191
192        // Assert what we have.
193        $this->assertTrue($DB->record_exists('log', ['userid' => $u1->id, 'cmid' => $cm1->cmid, 'course' => $c1->id]));
194        $this->assertTrue($DB->record_exists('log', ['userid' => $u1->id, 'cmid' => 0, 'course' => $c1->id]));
195        $this->assertTrue($DB->record_exists('log', ['userid' => $u1->id, 'cmid' => 0, 'course' => 0]));
196        $this->assertEquals(4, $DB->count_records('log', ['userid' => $u1->id]));
197        $this->assertEquals(3, $DB->count_records('log', ['userid' => $u2->id]));
198
199        // Delete other context.
200        provider::delete_data_for_user(new approved_contextlist($u1, 'logstore_legacy', [$c2ctx->id]));
201        $this->assertTrue($DB->record_exists('log', ['userid' => $u1->id, 'cmid' => $cm1->cmid, 'course' => $c1->id]));
202        $this->assertTrue($DB->record_exists('log', ['userid' => $u1->id, 'cmid' => 0, 'course' => $c1->id]));
203        $this->assertTrue($DB->record_exists('log', ['userid' => $u1->id, 'cmid' => 0, 'course' => 0]));
204        $this->assertEquals(4, $DB->count_records('log', ['userid' => $u1->id]));
205        $this->assertEquals(3, $DB->count_records('log', ['userid' => $u2->id]));
206
207        // Delete system.
208        provider::delete_data_for_user(new approved_contextlist($u1, 'logstore_legacy', [$sysctx->id]));
209        $this->assertTrue($DB->record_exists('log', ['userid' => $u1->id, 'cmid' => $cm1->cmid, 'course' => $c1->id]));
210        $this->assertTrue($DB->record_exists('log', ['userid' => $u1->id, 'cmid' => 0, 'course' => $c1->id]));
211        $this->assertFalse($DB->record_exists('log', ['userid' => $u1->id, 'cmid' => 0, 'course' => 0]));
212        $this->assertEquals(3, $DB->count_records('log', ['userid' => $u1->id]));
213        $this->assertEquals(3, $DB->count_records('log', ['userid' => $u2->id]));
214
215        // Delete course.
216        provider::delete_data_for_user(new approved_contextlist($u1, 'logstore_legacy', [$c1ctx->id]));
217        $this->assertTrue($DB->record_exists('log', ['userid' => $u1->id, 'cmid' => $cm1->cmid, 'course' => $c1->id]));
218        $this->assertFalse($DB->record_exists('log', ['userid' => $u1->id, 'cmid' => 0, 'course' => $c1->id]));
219        $this->assertFalse($DB->record_exists('log', ['userid' => $u1->id, 'cmid' => 0, 'course' => 0]));
220        $this->assertEquals(2, $DB->count_records('log', ['userid' => $u1->id]));
221        $this->assertEquals(3, $DB->count_records('log', ['userid' => $u2->id]));
222
223        // Delete course.
224        provider::delete_data_for_user(new approved_contextlist($u1, 'logstore_legacy', [$cm1ctx->id]));
225        $this->assertFalse($DB->record_exists('log', ['userid' => $u1->id, 'cmid' => $cm1->cmid, 'course' => $c1->id]));
226        $this->assertFalse($DB->record_exists('log', ['userid' => $u1->id, 'cmid' => 0, 'course' => $c1->id]));
227        $this->assertFalse($DB->record_exists('log', ['userid' => $u1->id, 'cmid' => 0, 'course' => 0]));
228        $this->assertEquals(0, $DB->count_records('log', ['userid' => $u1->id]));
229        $this->assertEquals(3, $DB->count_records('log', ['userid' => $u2->id]));
230    }
231
232    public function test_delete_data_for_all_users_in_context() {
233        global $DB;
234
235        $u1 = $this->getDataGenerator()->create_user();
236        $u2 = $this->getDataGenerator()->create_user();
237        $u3 = $this->getDataGenerator()->create_user();
238        $c1 = $this->getDataGenerator()->create_course();
239        $c2 = $this->getDataGenerator()->create_course();
240        $cm1 = $this->getDataGenerator()->create_module('url', ['course' => $c1]);
241        $sysctx = context_system::instance();
242        $c1ctx = context_course::instance($c1->id);
243        $c2ctx = context_course::instance($c2->id);
244        $cm1ctx = context_module::instance($cm1->cmid);
245
246        $this->enable_logging();
247        $manager = get_log_manager(true);
248
249        // User 1 is the author.
250        $this->setUser($u1);
251        $e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 1]]);
252        $e->trigger();
253        $e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 2]]);
254        $e->trigger();
255        $e = unittest_executed::create(['context' => $c1ctx, 'other' => ['sample' => 3]]);
256        $e->trigger();
257        $e = unittest_executed::create(['context' => $sysctx, 'other' => ['sample' => 4]]);
258        $e->trigger();
259
260        // User 2 is the author.
261        $this->setUser($u2);
262        $e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 5]]);
263        $e->trigger();
264        $e = unittest_executed::create(['context' => $c1ctx, 'other' => ['sample' => 6]]);
265        $e->trigger();
266        $e = unittest_executed::create(['context' => $sysctx, 'other' => ['sample' => 7]]);
267        $e->trigger();
268
269        // Assert what we have.
270        $this->assertTrue($DB->record_exists('log', ['cmid' => $cm1->cmid, 'course' => $c1->id]));
271        $this->assertTrue($DB->record_exists('log', ['cmid' => 0, 'course' => $c1->id]));
272        $this->assertTrue($DB->record_exists('log', ['cmid' => 0, 'course' => 0]));
273        $this->assertEquals(4, $DB->count_records('log', ['userid' => $u1->id]));
274        $this->assertEquals(3, $DB->count_records('log', ['userid' => $u2->id]));
275
276        // Delete other context.
277        provider::delete_data_for_all_users_in_context($c2ctx);
278        $this->assertTrue($DB->record_exists('log', ['cmid' => $cm1->cmid, 'course' => $c1->id]));
279        $this->assertTrue($DB->record_exists('log', ['cmid' => 0, 'course' => $c1->id]));
280        $this->assertTrue($DB->record_exists('log', ['cmid' => 0, 'course' => 0]));
281        $this->assertEquals(4, $DB->count_records('log', ['userid' => $u1->id]));
282        $this->assertEquals(3, $DB->count_records('log', ['userid' => $u2->id]));
283
284        // Delete system.
285        provider::delete_data_for_all_users_in_context($sysctx);
286        $this->assertTrue($DB->record_exists('log', ['cmid' => $cm1->cmid, 'course' => $c1->id]));
287        $this->assertTrue($DB->record_exists('log', ['cmid' => 0, 'course' => $c1->id]));
288        $this->assertFalse($DB->record_exists('log', ['cmid' => 0, 'course' => 0]));
289        $this->assertEquals(3, $DB->count_records('log', ['userid' => $u1->id]));
290        $this->assertEquals(2, $DB->count_records('log', ['userid' => $u2->id]));
291
292        // Delete course.
293        provider::delete_data_for_all_users_in_context($c1ctx);
294        $this->assertTrue($DB->record_exists('log', ['cmid' => $cm1->cmid, 'course' => $c1->id]));
295        $this->assertFalse($DB->record_exists('log', ['cmid' => 0, 'course' => $c1->id]));
296        $this->assertFalse($DB->record_exists('log', ['cmid' => 0, 'course' => 0]));
297        $this->assertEquals(2, $DB->count_records('log', ['userid' => $u1->id]));
298        $this->assertEquals(1, $DB->count_records('log', ['userid' => $u2->id]));
299
300        // Delete course.
301        provider::delete_data_for_all_users_in_context($cm1ctx);
302        $this->assertFalse($DB->record_exists('log', ['cmid' => $cm1->cmid, 'course' => $c1->id]));
303        $this->assertFalse($DB->record_exists('log', ['cmid' => 0, 'course' => $c1->id]));
304        $this->assertFalse($DB->record_exists('log', ['cmid' => 0, 'course' => 0]));
305        $this->assertEquals(0, $DB->count_records('log', ['userid' => $u1->id]));
306        $this->assertEquals(0, $DB->count_records('log', ['userid' => $u2->id]));
307    }
308
309    /**
310     * Test the deletion of data for a list of users in a context.
311     */
312    public function test_delete_data_for_userlist() {
313        global $DB;
314
315        $u1 = $this->getDataGenerator()->create_user();
316        $u2 = $this->getDataGenerator()->create_user();
317        $u3 = $this->getDataGenerator()->create_user();
318        $course = $this->getDataGenerator()->create_course();
319        $module = $this->getDataGenerator()->create_module('url', ['course' => $course]);
320        $sysctx = context_system::instance();
321        $c1ctx = context_course::instance($course->id);
322        $cm1ctx = context_module::instance($module->cmid);
323
324        $userctx = context_user::instance($u1->id);
325
326        $this->enable_logging();
327        $manager = get_log_manager(true);
328
329        $this->setUser($u1);
330        $e = unittest_executed::create(['context' => $sysctx, 'other' => ['sample' => 1]]);
331        $e->trigger();
332        $this->setUser($u2);
333        $e = unittest_executed::create(['context' => $sysctx, 'other' => ['sample' => 2]]);
334        $e->trigger();
335        $e = unittest_executed::create(['context' => $sysctx, 'other' => ['sample' => 3]]);
336        $e->trigger();
337        $this->setUser($u3);
338        $e = unittest_executed::create(['context' => $c1ctx, 'other' => ['sample' => 4]]);
339        $e->trigger();
340        $this->setUser($u1);
341        $e = unittest_executed::create(['context' => $c1ctx, 'other' => ['sample' => 5]]);
342        $e->trigger();
343        $e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 6]]);
344        $e->trigger();
345        $this->setUser($u2);
346        $e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 7]]);
347        $e->trigger();
348        $this->setUser($u3);
349        $e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 8]]);
350        $e->trigger();
351
352        // System context deleting one user.
353        $this->assertEquals(3, $DB->count_records('log', ['cmid' => 0, 'course' => 0]));
354        $userlist = new \core_privacy\local\request\approved_userlist($sysctx, 'logstore_legacy', [$u2->id]);
355        provider::delete_data_for_userlist($userlist);
356        $this->assertEquals(1, $DB->count_records('log', ['cmid' => 0, 'course' => 0]));
357
358        // Course context deleting one user.
359        $this->assertEquals(2, $DB->count_records('log', ['cmid' => 0, 'course' => $course->id]));
360        $userlist = new \core_privacy\local\request\approved_userlist($c1ctx, 'logstore_legacy', [$u1->id]);
361        provider::delete_data_for_userlist($userlist);
362        $this->assertEquals(1, $DB->count_records('log', ['cmid' => 0, 'course' => $course->id]));
363
364        // Module context deleting two users.
365        $this->assertEquals(3, $DB->count_records('log', ['cmid' => $module->cmid, 'course' => $course->id]));
366        $userlist = new \core_privacy\local\request\approved_userlist($cm1ctx, 'logstore_legacy', [$u1->id, $u3->id]);
367        provider::delete_data_for_userlist($userlist);
368        $this->assertEquals(1, $DB->count_records('log', ['cmid' => $module->cmid, 'course' => $course->id]));
369    }
370
371    public function test_export_data_for_user() {
372        global $DB;
373
374        $u1 = $this->getDataGenerator()->create_user();
375        $u2 = $this->getDataGenerator()->create_user();
376        $u3 = $this->getDataGenerator()->create_user();
377        $c1 = $this->getDataGenerator()->create_course();
378        $c2 = $this->getDataGenerator()->create_course();
379        $cm1 = $this->getDataGenerator()->create_module('url', ['course' => $c1]);
380        $sysctx = context_system::instance();
381        $c1ctx = context_course::instance($c1->id);
382        $c2ctx = context_course::instance($c2->id);
383        $cm1ctx = context_module::instance($cm1->cmid);
384
385        $this->enable_logging();
386        $manager = get_log_manager(true);
387        $path = [get_string('privacy:path:logs', 'tool_log'), get_string('pluginname', 'logstore_legacy')];
388
389        // User 1 is the author.
390        $this->setUser($u1);
391        $e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 1]]);
392        $e->trigger();
393        $e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 2]]);
394        $e->trigger();
395        $e = unittest_executed::create(['context' => $c1ctx, 'other' => ['sample' => 3]]);
396        $e->trigger();
397        $e = unittest_executed::create(['context' => $sysctx, 'other' => ['sample' => 4]]);
398        $e->trigger();
399
400        // User 2 is the author.
401        $this->setUser($u2);
402        $e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 5]]);
403        $e->trigger();
404        $e = unittest_executed::create(['context' => $c1ctx, 'other' => ['sample' => 6]]);
405        $e->trigger();
406        $e = unittest_executed::create(['context' => $sysctx, 'other' => ['sample' => 7]]);
407        $e->trigger();
408
409        // Test export.
410        provider::export_user_data(new approved_contextlist($u1, 'logstore_legacy', [$cm1ctx->id]));
411        $data = writer::with_context($c1ctx)->get_data($path);
412        $this->assertEmpty($data);
413        $data = writer::with_context($cm1ctx)->get_data($path);
414        $this->assertCount(2, $data->logs);
415
416        writer::reset();
417        provider::export_user_data(new approved_contextlist($u1, 'logstore_legacy', [$c1ctx->id]));
418        $data = writer::with_context($cm1ctx)->get_data($path);
419        $this->assertEmpty($data);
420        $data = writer::with_context($c1ctx)->get_data($path);
421        $this->assertCount(1, $data->logs);
422
423        writer::reset();
424        provider::export_user_data(new approved_contextlist($u1, 'logstore_legacy', [$sysctx->id]));
425        $data = writer::with_context($sysctx)->get_data($path);
426        $this->assertCount(1, $data->logs);
427    }
428
429    /**
430     * Assert the content of a context list.
431     *
432     * @param contextlist $contextlist The collection.
433     * @param array $expected List of expected contexts or IDs.
434     * @return void
435     */
436    protected function assert_contextlist_equals($contextlist, array $expected) {
437        $expectedids = array_map(function($context) {
438            if (is_object($context)) {
439                return $context->id;
440            }
441            return $context;
442        }, $expected);
443        $contextids = array_map('intval', $contextlist->get_contextids());
444        sort($contextids);
445        sort($expectedids);
446        $this->assertEquals($expectedids, $contextids);
447    }
448
449    /**
450     * Enable logging.
451     *
452     * @return void
453     */
454    protected function enable_logging() {
455        set_config('enabled_stores', 'logstore_legacy', 'tool_log');
456        set_config('loglegacy', 1, 'logstore_legacy');
457        get_log_manager(true);
458    }
459
460    /**
461     * Get the contextlist for a user.
462     *
463     * @param object $user The user.
464     * @return contextlist
465     */
466    protected function get_contextlist_for_user($user) {
467        $contextlist = new contextlist();
468        provider::add_contexts_for_userid($contextlist, $user->id);
469        return $contextlist;
470    }
471}
472