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 * Privacy tests for enrol_flatfile.
19 *
20 * @package    enrol_flatfile
21 * @category   test
22 * @copyright  2018 Jake Dallimore <jrhdallimore@gmail.com>
23 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 */
25
26defined('MOODLE_INTERNAL') || die();
27
28use core_privacy\local\metadata\collection;
29use core_privacy\tests\provider_testcase;
30use core_privacy\local\request\approved_contextlist;
31use core_privacy\local\request\writer;
32use enrol_flatfile\privacy\provider;
33
34/**
35 * Privacy tests for enrol_flatfile.
36 *
37 * @copyright  2018 Jake Dallimore <jrhdallimore@gmail.com>
38 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
39 */
40class enrol_flatfile_privacy_testcase extends provider_testcase {
41
42    /** @var \stdClass $user1 a test user.*/
43    protected $user1;
44
45    /** @var \stdClass $user2 a test user.*/
46    protected $user2;
47
48    /** @var \stdClass $user3 a test user.*/
49    protected $user3;
50
51    /** @var \stdClass $user4 a test user.*/
52    protected $user4;
53
54    /** @var \context $coursecontext1 a course context.*/
55    protected $coursecontext1;
56
57    /** @var \context $coursecontext2 a course context.*/
58    protected $coursecontext2;
59
60    /** @var \context $coursecontext3 a course context.*/
61    protected $coursecontext3;
62
63    /**
64     * Called before every test.
65     */
66    public function setUp(): void {
67        $this->resetAfterTest(true);
68    }
69
70    /**
71     * Verify that get_metadata returns the database table mapping.
72     */
73    public function test_get_metadata() {
74        $collection = new collection('enrol_flatfile');
75        $collection = provider::get_metadata($collection);
76        $collectiondata = $collection->get_collection();
77        $this->assertNotEmpty($collectiondata);
78        $this->assertInstanceOf(\core_privacy\local\metadata\types\database_table::class, $collectiondata[0]);
79    }
80
81    /**
82     * Verify that the relevant course contexts are returned for users with pending enrolment records.
83     */
84    public function test_get_contexts_for_user() {
85        global $DB;
86        // Create, via flatfile syncing, the future enrolments entries in the enrol_flatfile table.
87        $this->create_future_enrolments();
88
89        $this->assertEquals(5, $DB->count_records('enrol_flatfile'));
90
91        // We expect to see 2 entries for user1, in course1 and course3.
92        $contextlist = provider::get_contexts_for_userid($this->user1->id);
93        $this->assertEquals(2, $contextlist->count());
94        $contextids = $contextlist->get_contextids();
95        $this->assertContains($this->coursecontext1->id, $contextids);
96        $this->assertContains($this->coursecontext3->id, $contextids);
97
98        // And 1 for user2 on course2.
99        $contextlist = provider::get_contexts_for_userid($this->user2->id);
100        $this->assertEquals(1, $contextlist->count());
101        $contextids = $contextlist->get_contextids();
102        $this->assertContains($this->coursecontext2->id, $contextids);
103    }
104
105    /**
106     * Verify the export includes any future enrolment records for the user.
107     */
108    public function test_export_user_data() {
109        // Create, via flatfile syncing, the future enrolments entries in the enrol_flatfile table.
110        $this->create_future_enrolments();
111
112        // Get contexts containing user data.
113        $contextlist = provider::get_contexts_for_userid($this->user1->id);
114        $this->assertEquals(2, $contextlist->count());
115
116        $approvedcontextlist = new approved_contextlist(
117            $this->user1,
118            'enrol_flatfile',
119            $contextlist->get_contextids()
120        );
121
122        // Export for the approved contexts.
123        provider::export_user_data($approvedcontextlist);
124
125        // Verify we see one future course enrolment in course1, and one in course3.
126        $subcontext = \core_enrol\privacy\provider::get_subcontext([get_string('pluginname', 'enrol_flatfile')]);
127
128        $writer = writer::with_context($this->coursecontext1);
129        $this->assertNotEmpty($writer->get_data($subcontext));
130
131        $writer = writer::with_context($this->coursecontext3);
132        $this->assertNotEmpty($writer->get_data($subcontext));
133
134        // Verify we have nothing in course 2 for this user.
135        $writer = writer::with_context($this->coursecontext2);
136        $this->assertEmpty($writer->get_data($subcontext));
137    }
138
139    /**
140     * Verify export will limit any future enrolment records to only those contextids provided.
141     */
142    public function test_export_user_data_restricted_context_subset() {
143        // Create, via flatfile syncing, the future enrolments entries in the enrol_flatfile table.
144        $this->create_future_enrolments();
145
146        // Now, limit the export scope to just course1's context and verify only that data is seen in any export.
147        $subsetapprovedcontextlist = new approved_contextlist(
148            $this->user1,
149            'enrol_flatfile',
150            [$this->coursecontext1->id]
151        );
152
153        // Export for the approved contexts.
154        provider::export_user_data($subsetapprovedcontextlist);
155
156        // Verify we see one future course enrolment in course1 only.
157        $subcontext = \core_enrol\privacy\provider::get_subcontext([get_string('pluginname', 'enrol_flatfile')]);
158
159        $writer = writer::with_context($this->coursecontext1);
160        $this->assertNotEmpty($writer->get_data($subcontext));
161
162        // And nothing in the course3 context.
163        $writer = writer::with_context($this->coursecontext3);
164        $this->assertEmpty($writer->get_data($subcontext));
165    }
166
167    /**
168     * Verify that records can be deleted by context.
169     */
170    public function test_delete_data_for_all_users_in_context() {
171        global $DB;
172        // Create, via flatfile syncing, the future enrolments entries in the enrol_flatfile table.
173        $this->create_future_enrolments();
174
175        // Verify we have 3 future enrolments for course 1.
176        $this->assertEquals(3, $DB->count_records('enrol_flatfile', ['courseid' => $this->coursecontext1->instanceid]));
177
178        // Now, run delete by context and confirm that all records are removed.
179        provider::delete_data_for_all_users_in_context($this->coursecontext1);
180        $this->assertEquals(0, $DB->count_records('enrol_flatfile', ['courseid' => $this->coursecontext1->instanceid]));
181    }
182
183    public function test_delete_data_for_user() {
184        global $DB;
185        // Create, via flatfile syncing, the future enrolments entries in the enrol_flatfile table.
186        $this->create_future_enrolments();
187
188        // Verify we have 2 future enrolments for course 1 and course 3.
189        $contextlist = provider::get_contexts_for_userid($this->user1->id);
190        $this->assertEquals(2, $contextlist->count());
191        $contextids = $contextlist->get_contextids();
192        $this->assertContains($this->coursecontext1->id, $contextids);
193        $this->assertContains($this->coursecontext3->id, $contextids);
194
195        $approvedcontextlist = new approved_contextlist(
196            $this->user1,
197            'enrol_flatfile',
198            $contextids
199        );
200
201        // Now, run delete for user and confirm that both records are removed.
202        provider::delete_data_for_user($approvedcontextlist);
203        $contextlist = provider::get_contexts_for_userid($this->user1->id);
204        $this->assertEquals(0, $contextlist->count());
205        $this->assertEquals(0, $DB->count_records('enrol_flatfile', ['userid' => $this->user1->id]));
206    }
207
208    /**
209     * Test for provider::get_users_in_context().
210     */
211    public function test_get_users_in_context() {
212        global $DB;
213        // Create, via flatfile syncing, the future enrolments entries in the enrol_flatfile table.
214        $this->create_future_enrolments();
215
216        $this->assertEquals(5, $DB->count_records('enrol_flatfile'));
217
218        // We expect to see 3 entries for course1, and that's user1, user3 and user4.
219        $userlist = new \core_privacy\local\request\userlist($this->coursecontext1, 'enrol_flatfile');
220        provider::get_users_in_context($userlist);
221        $this->assertEqualsCanonicalizing(
222                [$this->user1->id, $this->user3->id, $this->user4->id],
223                $userlist->get_userids());
224
225        // And 1 for course2 which is for user2.
226        $userlist = new \core_privacy\local\request\userlist($this->coursecontext2, 'enrol_flatfile');
227        provider::get_users_in_context($userlist);
228        $this->assertEquals([$this->user2->id], $userlist->get_userids());
229
230        // And 1 for course3 which is for user1 again.
231        $userlist = new \core_privacy\local\request\userlist($this->coursecontext3, 'enrol_flatfile');
232        provider::get_users_in_context($userlist);
233        $this->assertEquals([$this->user1->id], $userlist->get_userids());
234    }
235
236    /**
237     * Test for provider::delete_data_for_users().
238     */
239    public function test_delete_data_for_users() {
240        global $DB;
241
242        // Create, via flatfile syncing, the future enrolments entries in the enrol_flatfile table.
243        $this->create_future_enrolments();
244
245        // Verify we have 3 future enrolment for user 1, user 3 and user 4.
246        $userlist = new \core_privacy\local\request\userlist($this->coursecontext1, 'enrol_flatfile');
247        provider::get_users_in_context($userlist);
248        $this->assertEqualsCanonicalizing(
249                [$this->user1->id, $this->user3->id, $this->user4->id],
250                $userlist->get_userids());
251
252        $approveduserlist = new \core_privacy\local\request\approved_userlist($this->coursecontext1, 'enrol_flatfile',
253                [$this->user1->id, $this->user3->id]);
254
255        // Now, run delete for user and confirm that the record is removed.
256        provider::delete_data_for_users($approveduserlist);
257        $userlist = new \core_privacy\local\request\userlist($this->coursecontext1, 'enrol_flatfile');
258        provider::get_users_in_context($userlist);
259        $this->assertEquals([$this->user4->id], $userlist->get_userids());
260        $this->assertEquals(
261                [$this->user4->id],
262                $DB->get_fieldset_select('enrol_flatfile', 'userid', 'courseid = ?', [$this->coursecontext1->instanceid])
263        );
264    }
265
266    /**
267     * Helper to sync a file and create the enrol_flatfile DB entries, for use with the get, export and delete tests.
268     */
269    protected function create_future_enrolments() {
270        global $CFG;
271        $this->user1 = $this->getDataGenerator()->create_user(['idnumber' => 'u1']);
272        $this->user2 = $this->getDataGenerator()->create_user(['idnumber' => 'u2']);
273        $this->user3 = $this->getDataGenerator()->create_user(['idnumber' => 'u3']);
274        $this->user4 = $this->getDataGenerator()->create_user(['idnumber' => 'u4']);
275
276        $course1 = $this->getDataGenerator()->create_course(['idnumber' => 'c1']);
277        $course2 = $this->getDataGenerator()->create_course(['idnumber' => 'c2']);
278        $course3 = $this->getDataGenerator()->create_course(['idnumber' => 'c3']);
279        $this->coursecontext1 = context_course::instance($course1->id);
280        $this->coursecontext2 = context_course::instance($course2->id);
281        $this->coursecontext3 = context_course::instance($course3->id);
282
283        $now = time();
284        $future = $now + 60 * 60 * 5;
285        $farfuture = $now + 60 * 60 * 24 * 5;
286
287        $file = "$CFG->dataroot/enrol.txt";
288        $data = "add,student,u1,c1,$future,0
289                 add,student,u2,c2,$future,0
290                 add,student,u3,c1,$future,0
291                 add,student,u4,c1,$future,0
292                 add,student,u1,c3,$future,$farfuture";
293        file_put_contents($file, $data);
294
295        $trace = new null_progress_trace();
296        $this->enable_plugin();
297        $flatfileplugin = enrol_get_plugin('flatfile');
298        $flatfileplugin->set_config('location', $file);
299        $flatfileplugin->sync($trace);
300    }
301
302    /**
303     * Enables the flatfile plugin for testing.
304     */
305    protected function enable_plugin() {
306        $enabled = enrol_get_plugins(true);
307        $enabled['flatfile'] = true;
308        $enabled = array_keys($enabled);
309        set_config('enrol_plugins_enabled', implode(',', $enabled));
310    }
311}
312