2 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
18 * Data provider tests.
20 * @package logstore_database
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
27 defined('MOODLE_INTERNAL') || die();
30 use core_privacy\tests\provider_testcase;
31 use core_privacy\local\request\contextlist;
32 use core_privacy\local\request\approved_contextlist;
33 use core_privacy\local\request\transform;
34 use core_privacy\local\request\writer;
35 use logstore_database\privacy\provider;
37 require_once(__DIR__ . '/fixtures/event.php');
40 * Data provider testcase class.
42 * This testcase is almost identical to the logstore_standard testcase, aside from the
43 * initialisation of the relevant logstore obviously.
45 * @package logstore_database
47 * @copyright 2018 Frédéric Massart
48 * @author Frédéric Massart <fred@branchup.tech>
49 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
51 class logstore_database_privacy_testcase extends provider_testcase {
53 public function setUp() {
55 $this->resetAfterTest();
56 $this->preventResetByRollback(); // Logging waits till the transaction gets committed.
58 // Fake the settings, we will abuse the standard plugin table here...
59 set_config('dbdriver', $CFG->dblibrary . '/' . $CFG->dbtype, 'logstore_database');
60 set_config('dbhost', $CFG->dbhost, 'logstore_database');
61 set_config('dbuser', $CFG->dbuser, 'logstore_database');
62 set_config('dbpass', $CFG->dbpass, 'logstore_database');
63 set_config('dbname', $CFG->dbname, 'logstore_database');
64 set_config('dbtable', $CFG->prefix . 'logstore_standard_log', 'logstore_database');
65 if (!empty($CFG->dboptions['dbpersist'])) {
66 set_config('dbpersist', 1, 'logstore_database');
68 set_config('dbpersist', 0, 'logstore_database');
70 if (!empty($CFG->dboptions['dbsocket'])) {
71 set_config('dbsocket', $CFG->dboptions['dbsocket'], 'logstore_database');
73 set_config('dbsocket', '', 'logstore_database');
75 if (!empty($CFG->dboptions['dbport'])) {
76 set_config('dbport', $CFG->dboptions['dbport'], 'logstore_database');
78 set_config('dbport', '', 'logstore_database');
80 if (!empty($CFG->dboptions['dbschema'])) {
81 set_config('dbschema', $CFG->dboptions['dbschema'], 'logstore_database');
83 set_config('dbschema', '', 'logstore_database');
85 if (!empty($CFG->dboptions['dbcollation'])) {
86 set_config('dbcollation', $CFG->dboptions['dbcollation'], 'logstore_database');
88 set_config('dbcollation', '', 'logstore_database');
90 if (!empty($CFG->dboptions['dbhandlesoptions'])) {
91 set_config('dbhandlesoptions', $CFG->dboptions['dbhandlesoptions'], 'logstore_database');
93 set_config('dbhandlesoptions', false, 'logstore_database');
97 public function test_get_contexts_for_userid() {
98 $admin = \core_user::get_user(2);
99 $u1 = $this->getDataGenerator()->create_user();
100 $u2 = $this->getDataGenerator()->create_user();
101 $u3 = $this->getDataGenerator()->create_user();
103 $c1 = $this->getDataGenerator()->create_course();
104 $cm1 = $this->getDataGenerator()->create_module('url', ['course' => $c1]);
105 $c2 = $this->getDataGenerator()->create_course();
106 $cm2 = $this->getDataGenerator()->create_module('url', ['course' => $c2]);
108 $sysctx = context_system::instance();
109 $c1ctx = context_course::instance($c1->id);
110 $c2ctx = context_course::instance($c2->id);
111 $cm1ctx = context_module::instance($cm1->cmid);
112 $cm2ctx = context_module::instance($cm2->cmid);
114 $this->enable_logging();
115 $manager = get_log_manager(true);
117 // User 1 is the author.
119 $this->assert_contextlist_equals($this->get_contextlist_for_user($u1), []);
120 $e = \logstore_database\event\unittest_executed::create(['context' => $cm1ctx]);
122 $this->assert_contextlist_equals($this->get_contextlist_for_user($u1), [$cm1ctx]);
124 // User 2 is the related user.
126 $this->assert_contextlist_equals($this->get_contextlist_for_user($u2), []);
127 $e = \logstore_database\event\unittest_executed::create(['context' => $cm2ctx, 'relateduserid' => $u2->id]);
129 $this->assert_contextlist_equals($this->get_contextlist_for_user($u2), [$cm2ctx]);
131 // Admin user is the real user.
132 $this->assert_contextlist_equals($this->get_contextlist_for_user($admin), []);
133 $this->assert_contextlist_equals($this->get_contextlist_for_user($u3), []);
134 $this->setAdminUser();
135 \core\session\manager::loginas($u3->id, $sysctx);
136 $e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx]);
138 $this->assert_contextlist_equals($this->get_contextlist_for_user($admin), [$sysctx, $c1ctx]);
139 $this->assert_contextlist_equals($this->get_contextlist_for_user($u3), [$sysctx, $c1ctx]);
141 // By admin user masquerading u1 related to u3.
142 $this->assert_contextlist_equals($this->get_contextlist_for_user($u1), [$cm1ctx]);
143 $this->assert_contextlist_equals($this->get_contextlist_for_user($u3), [$sysctx, $c1ctx]);
144 $this->assert_contextlist_equals($this->get_contextlist_for_user($admin), [$sysctx, $c1ctx]);
145 $this->setAdminUser();
146 \core\session\manager::loginas($u1->id, context_system::instance());
147 $e = \logstore_database\event\unittest_executed::create(['context' => $c2ctx, 'relateduserid' => $u3->id]);
149 $this->assert_contextlist_equals($this->get_contextlist_for_user($u1), [$sysctx, $cm1ctx, $c2ctx]);
150 $this->assert_contextlist_equals($this->get_contextlist_for_user($u3), [$sysctx, $c1ctx, $c2ctx]);
151 $this->assert_contextlist_equals($this->get_contextlist_for_user($admin), [$sysctx, $c1ctx, $c2ctx]);
154 public function test_delete_data_for_user() {
156 $u1 = $this->getDataGenerator()->create_user();
157 $u2 = $this->getDataGenerator()->create_user();
158 $c1 = $this->getDataGenerator()->create_course();
159 $c2 = $this->getDataGenerator()->create_course();
160 $sysctx = context_system::instance();
161 $c1ctx = context_course::instance($c1->id);
162 $c2ctx = context_course::instance($c2->id);
164 $this->enable_logging();
165 $manager = get_log_manager(true);
167 // User 1 is the author.
169 $e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx]);
171 $e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx]);
173 $e = \logstore_database\event\unittest_executed::create(['context' => $c2ctx]);
176 // User 2 is the author.
178 $e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx]);
180 $e = \logstore_database\event\unittest_executed::create(['context' => $c2ctx]);
183 // Confirm data present.
184 $this->assertTrue($DB->record_exists('logstore_standard_log', ['userid' => $u1->id, 'contextid' => $c1ctx->id]));
185 $this->assertEquals(3, $DB->count_records('logstore_standard_log', ['userid' => $u1->id]));
186 $this->assertEquals(2, $DB->count_records('logstore_standard_log', ['userid' => $u2->id]));
188 // Delete all the things!
189 provider::delete_data_for_user(new approved_contextlist($u1, 'logstore_database', [$c1ctx->id]));
190 $this->assertFalse($DB->record_exists('logstore_standard_log', ['userid' => $u1->id, 'contextid' => $c1ctx->id]));
191 $this->assertEquals(1, $DB->count_records('logstore_standard_log', ['userid' => $u1->id]));
192 $this->assertEquals(2, $DB->count_records('logstore_standard_log', ['userid' => $u2->id]));
195 public function test_delete_data_for_all_users_in_context() {
197 $u1 = $this->getDataGenerator()->create_user();
198 $u2 = $this->getDataGenerator()->create_user();
199 $c1 = $this->getDataGenerator()->create_course();
200 $c2 = $this->getDataGenerator()->create_course();
201 $sysctx = context_system::instance();
202 $c1ctx = context_course::instance($c1->id);
203 $c2ctx = context_course::instance($c2->id);
205 $this->enable_logging();
206 $manager = get_log_manager(true);
208 // User 1 is the author.
210 $e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx]);
212 $e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx]);
214 $e = \logstore_database\event\unittest_executed::create(['context' => $c2ctx]);
217 // User 2 is the author.
219 $e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx]);
221 $e = \logstore_database\event\unittest_executed::create(['context' => $c2ctx]);
224 // Confirm data present.
225 $this->assertTrue($DB->record_exists('logstore_standard_log', ['contextid' => $c1ctx->id]));
226 $this->assertEquals(3, $DB->count_records('logstore_standard_log', ['userid' => $u1->id]));
227 $this->assertEquals(2, $DB->count_records('logstore_standard_log', ['userid' => $u2->id]));
229 // Delete all the things!
230 provider::delete_data_for_all_users_in_context($c1ctx);
231 $this->assertFalse($DB->record_exists('logstore_standard_log', ['contextid' => $c1ctx->id]));
232 $this->assertEquals(1, $DB->count_records('logstore_standard_log', ['userid' => $u1->id]));
233 $this->assertEquals(1, $DB->count_records('logstore_standard_log', ['userid' => $u2->id]));
236 public function test_export_data_for_user() {
237 $admin = \core_user::get_user(2);
238 $u1 = $this->getDataGenerator()->create_user();
239 $u2 = $this->getDataGenerator()->create_user();
240 $u3 = $this->getDataGenerator()->create_user();
241 $u4 = $this->getDataGenerator()->create_user();
242 $c1 = $this->getDataGenerator()->create_course();
243 $cm1 = $this->getDataGenerator()->create_module('url', ['course' => $c1]);
244 $c2 = $this->getDataGenerator()->create_course();
245 $cm2 = $this->getDataGenerator()->create_module('url', ['course' => $c2]);
246 $sysctx = context_system::instance();
247 $c1ctx = context_course::instance($c1->id);
248 $c2ctx = context_course::instance($c2->id);
249 $cm1ctx = context_module::instance($cm1->cmid);
250 $cm2ctx = context_module::instance($cm2->cmid);
252 $path = [get_string('privacy:path:logs', 'tool_log'), get_string('pluginname', 'logstore_database')];
253 $this->enable_logging();
254 $manager = get_log_manager(true);
256 // User 1 is the author.
258 $e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx, 'other' => ['i' => 0]]);
261 // User 2 is related.
263 $e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx, 'relateduserid' => $u2->id,
264 'other' => ['i' => 1]]);
267 // Admin user masquerades u3, which is related to u4.
268 $this->setAdminUser();
269 \core\session\manager::loginas($u3->id, $sysctx);
270 $e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx, 'relateduserid' => $u4->id,
271 'other' => ['i' => 2]]);
274 // Confirm data present for u1.
275 provider::export_user_data(new approved_contextlist($u1, 'logstore_database', [$c2ctx->id, $c1ctx->id]));
276 $data = writer::with_context($c2ctx)->get_data($path);
277 $this->assertEmpty($data);
278 $data = writer::with_context($c1ctx)->get_data($path);
279 $this->assertCount(1, $data->logs);
280 $this->assertEquals(transform::yesno(true), $data->logs[0]['author_of_the_action_was_you']);
281 $this->assertSame(0, $data->logs[0]['other']['i']);
283 // Confirm data present for u2.
285 provider::export_user_data(new approved_contextlist($u2, 'logstore_database', [$c2ctx->id, $c1ctx->id]));
286 $data = writer::with_context($c2ctx)->get_data($path);
287 $this->assertEmpty($data);
288 $data = writer::with_context($c1ctx)->get_data($path);
289 $this->assertCount(1, $data->logs);
290 $this->assertEquals(transform::yesno(false), $data->logs[0]['author_of_the_action_was_you']);
291 $this->assertEquals(transform::yesno(true), $data->logs[0]['related_user_was_you']);
292 $this->assertSame(1, $data->logs[0]['other']['i']);
294 // Confirm data present for u3.
296 provider::export_user_data(new approved_contextlist($u3, 'logstore_database', [$c2ctx->id, $c1ctx->id]));
297 $data = writer::with_context($c2ctx)->get_data($path);
298 $this->assertEmpty($data);
299 $data = writer::with_context($c1ctx)->get_data($path);
300 $this->assertCount(1, $data->logs);
301 $this->assertEquals(transform::yesno(true), $data->logs[0]['author_of_the_action_was_you']);
302 $this->assertEquals(transform::yesno(false), $data->logs[0]['related_user_was_you']);
303 $this->assertEquals(transform::yesno(true), $data->logs[0]['author_of_the_action_was_masqueraded']);
304 $this->assertEquals(transform::yesno(false), $data->logs[0]['masquerading_user_was_you']);
305 $this->assertSame(2, $data->logs[0]['other']['i']);
307 // Confirm data present for u4.
309 provider::export_user_data(new approved_contextlist($u4, 'logstore_database', [$c2ctx->id, $c1ctx->id]));
310 $data = writer::with_context($c2ctx)->get_data($path);
311 $this->assertEmpty($data);
312 $data = writer::with_context($c1ctx)->get_data($path);
313 $this->assertCount(1, $data->logs);
314 $this->assertEquals(transform::yesno(false), $data->logs[0]['author_of_the_action_was_you']);
315 $this->assertEquals(transform::yesno(true), $data->logs[0]['related_user_was_you']);
316 $this->assertEquals(transform::yesno(true), $data->logs[0]['author_of_the_action_was_masqueraded']);
317 $this->assertEquals(transform::yesno(false), $data->logs[0]['masquerading_user_was_you']);
318 $this->assertSame(2, $data->logs[0]['other']['i']);
320 // Add anonymous events.
322 $e = \logstore_database\event\unittest_executed::create(['context' => $c2ctx, 'relateduserid' => $u2->id,
323 'anonymous' => true]);
325 $this->setAdminUser();
326 \core\session\manager::loginas($u3->id, $sysctx);
327 $e = \logstore_database\event\unittest_executed::create(['context' => $c2ctx, 'relateduserid' => $u4->id,
328 'anonymous' => true]);
331 // Confirm data present for u1.
332 provider::export_user_data(new approved_contextlist($u1, 'logstore_database', [$c2ctx->id]));
333 $data = writer::with_context($c2ctx)->get_data($path);
334 $this->assertCount(1, $data->logs);
335 $this->assertEquals(transform::yesno(true), $data->logs[0]['action_was_done_anonymously']);
336 $this->assertEquals(transform::yesno(true), $data->logs[0]['author_of_the_action_was_you']);
338 // Confirm data present for u2.
340 provider::export_user_data(new approved_contextlist($u2, 'logstore_database', [$c2ctx->id]));
341 $data = writer::with_context($c2ctx)->get_data($path);
342 $this->assertCount(1, $data->logs);
343 $this->assertEquals(transform::yesno(true), $data->logs[0]['action_was_done_anonymously']);
344 $this->assertArrayNotHasKey('author_of_the_action_was_you', $data->logs[0]);
345 $this->assertArrayNotHasKey('authorid', $data->logs[0]);
346 $this->assertEquals(transform::yesno(true), $data->logs[0]['related_user_was_you']);
348 // Confirm data present for u3.
350 provider::export_user_data(new approved_contextlist($u3, 'logstore_database', [$c2ctx->id]));
351 $data = writer::with_context($c2ctx)->get_data($path);
352 $this->assertCount(1, $data->logs);
353 $this->assertEquals(transform::yesno(true), $data->logs[0]['action_was_done_anonymously']);
354 $this->assertEquals(transform::yesno(true), $data->logs[0]['author_of_the_action_was_you']);
355 $this->assertEquals(transform::yesno(true), $data->logs[0]['author_of_the_action_was_masqueraded']);
356 $this->assertArrayNotHasKey('masquerading_user_was_you', $data->logs[0]);
357 $this->assertArrayNotHasKey('masqueradinguserid', $data->logs[0]);
359 // Confirm data present for u4.
361 provider::export_user_data(new approved_contextlist($u4, 'logstore_database', [$c2ctx->id]));
362 $data = writer::with_context($c2ctx)->get_data($path);
363 $this->assertCount(1, $data->logs);
364 $this->assertEquals(transform::yesno(true), $data->logs[0]['action_was_done_anonymously']);
365 $this->assertArrayNotHasKey('author_of_the_action_was_you', $data->logs[0]);
366 $this->assertArrayNotHasKey('authorid', $data->logs[0]);
367 $this->assertEquals(transform::yesno(true), $data->logs[0]['related_user_was_you']);
368 $this->assertEquals(transform::yesno(true), $data->logs[0]['author_of_the_action_was_masqueraded']);
369 $this->assertArrayNotHasKey('masquerading_user_was_you', $data->logs[0]);
370 $this->assertArrayNotHasKey('masqueradinguserid', $data->logs[0]);
374 * Assert the content of a context list.
376 * @param contextlist $contextlist The collection.
377 * @param array $expected List of expected contexts or IDs.
380 protected function assert_contextlist_equals($contextlist, array $expected) {
381 $expectedids = array_map(function($context) {
382 if (is_object($context)) {
387 $contextids = array_map('intval', $contextlist->get_contextids());
390 $this->assertEquals($expectedids, $contextids);
398 protected function enable_logging() {
399 set_config('enabled_stores', 'logstore_database', 'tool_log');
400 set_config('buffersize', 0, 'logstore_database');
401 set_config('logguests', 1, 'logstore_database');
402 get_log_manager(true);
406 * Get the contextlist for a user.
408 * @param object $user The user.
409 * @return contextlist
411 protected function get_contextlist_for_user($user) {
412 $contextlist = new contextlist();
413 provider::add_contexts_for_userid($contextlist, $user->id);