Proyectos de Subversion Moodle

Rev

Rev 11 | | 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 auth_lti;
18
 
19
/**
20
 * Tests for the auth_plugin_lti class.
21
 *
22
 * @package    auth_lti
23
 * @copyright  2021 Jake Dallimore <jrhdallimore@gmail.com>
24
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25
 * @coversDefaultClass \auth_plugin_lti
26
 */
1441 ariadna 27
final class auth_test extends \advanced_testcase {
1 efrain 28
 
29
    /** @var string issuer URL used for test cases. */
1441 ariadna 30
    protected static string $issuer = 'https://lms.example.org';
1 efrain 31
 
32
    /** @var int const representing cases where no PII is present. */
33
    protected const PII_NONE = 0;
34
 
35
    /** @var int const representing cases where only names are included in PII. */
36
    protected const PII_NAMES_ONLY = 1;
37
 
38
    /** @var int const representing cases where only email is included in PII. */
39
    protected const PII_EMAILS_ONLY = 2;
40
 
41
    /** @var int const representing cases where both names and email are included in PII. */
42
    protected const PII_ALL = 3;
43
 
44
    /**
45
     * Verify the user's profile picture has been set, which is useful to verify picture syncs.
46
     *
47
     * @param int $userid the id of the Moodle user.
48
     */
49
    protected function verify_user_profile_image_updated(int $userid): void {
50
        global $CFG;
51
        $user = \core_user::get_user($userid);
52
        $usercontext = \context_user::instance($user->id);
53
        $expected = $CFG->wwwroot . '/pluginfile.php/' . $usercontext->id . '/user/icon/boost/f2?rev='. $user->picture;
54
 
55
        $page = new \moodle_page();
56
        $page->set_url('/user/profile.php');
57
        $page->set_context(\context_system::instance());
58
        $renderer = $page->get_renderer('core');
59
        $userpicture = new \user_picture($user);
60
        $this->assertEquals($expected, $userpicture->get_url($page, $renderer)->out(false));
61
    }
62
 
63
    /**
64
     * Get a list of users ready for use with mock authentication requests by providing an array of user ids.
65
     *
66
     * @param array $ids the platform user_ids for the users.
67
     * @param string $role the LTI role to include in the user data.
68
     * @param bool $includenames whether to include the firstname and lastname of the user
69
     * @param bool $includeemail whether to include the email of the user
70
     * @param bool $includepicture whether to include a profile picture or not (slows tests, so defaults to false).
71
     * @return array the users list.
72
     */
1441 ariadna 73
    protected static function get_mock_users_with_ids(
74
        array $ids,
75
        string $role = 'http://purl.imsglobal.org/vocab/lis/v2/membership#Instructor',
76
        bool $includenames = true,
77
        bool $includeemail = true,
78
        bool $includepicture = false,
79
    ): array {
1 efrain 80
        $users = [];
81
        foreach ($ids as $id) {
82
            $user = [
83
                'user_id' => $id,
84
                'given_name' => 'Firstname' . $id,
85
                'family_name' => 'Surname' . $id,
86
                'email' => "firstname.surname{$id}@lms.example.org",
87
                'roles' => [$role]
88
            ];
89
            if (!$includenames) {
90
                unset($user['given_name']);
91
                unset($user['family_name']);
92
            }
93
            if (!$includeemail) {
94
                unset($user['email']);
95
            }
96
            if ($includepicture) {
1441 ariadna 97
                $user['picture'] = self::getExternalTestFileUrl('/test.jpg');
1 efrain 98
            }
99
            $users[] = $user;
100
        }
101
        return $users;
102
    }
103
 
104
    /**
105
     * Get a mock member structure based on a mock user and, optionally, a legacy user id.
106
     *
107
     * @param array $mockuser the user data
108
     * @param string $legacyuserid the id of the user in the platform in 1.1, if different from the id used in 1.3.
109
     * @return array
110
     */
111
    protected function get_mock_member_data_for_user(array $mockuser, string $legacyuserid = ''): array {
112
        $data = [
113
            'user_id' => $mockuser['user_id'],
114
            'roles' => $mockuser['roles']
115
        ];
116
        if (isset($mockuser['given_name'])) {
117
            $data['given_name'] = $mockuser['given_name'];
118
        }
119
        if (isset($mockuser['family_name'])) {
120
            $data['family_name'] = $mockuser['family_name'];
121
        }
122
        if (isset($mockuser['email'])) {
123
            $data['email'] = $mockuser['email'];
124
        }
125
        if (!empty($mockuser['picture'])) {
126
            $data['picture'] = $mockuser['picture'];
127
        }
128
        if (!empty($legacyuserid)) {
129
            $data['lti11_legacy_user_id'] = $legacyuserid;
130
        }
131
        return $data;
132
    }
133
 
134
    /**
135
     * Get mocked JWT data for the given user, including optionally the migration claim information if provided.
136
     *
137
     * @param array $mockuser the user data
138
     * @param array $mockmigration information needed to mock the migration claim
139
     * @return array the mock JWT data
140
     */
141
    protected function get_mock_launchdata_for_user(array $mockuser, array $mockmigration = []): array {
142
        $data = [
1441 ariadna 143
            'iss' => self::$issuer, // Must match registration in create_test_environment.
1 efrain 144
            'aud' => '123', // Must match registration in create_test_environment.
145
            'sub' => $mockuser['user_id'], // User id on the platform site.
146
            'exp' => time() + 60,
147
            'nonce' => 'some-nonce-value-123',
148
            'https://purl.imsglobal.org/spec/lti/claim/deployment_id' => '1', // Must match registration.
149
            'https://purl.imsglobal.org/spec/lti/claim/roles' => $mockuser['roles'],
150
            'https://purl.imsglobal.org/spec/lti/claim/resource_link' => [
151
                'title' => "Res link title",
152
                'id' => 'res-link-id-123',
153
            ],
154
            "https://purl.imsglobal.org/spec/lti/claim/context" => [
155
                "id" => "context-id-12345",
156
                "label" => "ITS 123",
157
                "title" => "ITS 123 Machine Learning",
158
                "type" => ["http://purl.imsglobal.org/vocab/lis/v2/course#CourseOffering"]
159
            ],
160
            'https://purl.imsglobal.org/spec/lti/claim/target_link_uri' =>
161
                'https://this-moodle-tool.example.org/context/24/resource/14',
162
            'https://purl.imsglobal.org/spec/lti/claim/custom' => [
163
                'id' => '1'
164
            ]
165
        ];
166
 
167
        if (isset($mockuser['given_name'])) {
168
            $data['given_name'] = $mockuser['given_name'];
169
        }
170
        if (isset($mockuser['family_name'])) {
171
            $data['family_name'] = $mockuser['family_name'];
172
        }
173
        if (isset($mockuser['email'])) {
174
            $data['email'] = $mockuser['email'];
175
        }
176
 
177
        if (!empty($mockuser['picture'])) {
178
            $data['picture'] = $mockuser['picture'];
179
        }
180
 
181
        if ($mockmigration) {
182
            if (isset($mockmigration['consumer_key'])) {
183
                $base = [
184
                    $mockmigration['consumer_key'],
185
                    $data['https://purl.imsglobal.org/spec/lti/claim/deployment_id'],
186
                    $data['iss'],
187
                    $data['aud'],
188
                    $data['exp'],
189
                    $data['nonce']
190
                ];
191
                $basestring = implode('&', $base);
192
 
193
                $data['https://purl.imsglobal.org/spec/lti/claim/lti1p1'] = [
194
                    'oauth_consumer_key' => $mockmigration['consumer_key'],
195
                ];
196
 
197
                if (isset($mockmigration['signing_secret'])) {
198
                    $sig = base64_encode(hash_hmac('sha256', $basestring, $mockmigration['signing_secret']));
199
                    $data['https://purl.imsglobal.org/spec/lti/claim/lti1p1']['oauth_consumer_key_sign'] = $sig;
200
                }
201
            }
202
 
203
            if (isset($mockmigration['user_id'])) {
204
                $data['https://purl.imsglobal.org/spec/lti/claim/lti1p1']['user_id'] =
205
                    $mockmigration['user_id'];
206
            }
207
        }
208
        return $data;
209
    }
210
 
211
    /**
212
     * Test which verifies a user account can be created/found using the find_or_create_user_from_launch() method.
213
     *
1441 ariadna 214
     * @dataProvider launchdata_provider
1 efrain 215
     * @param array|null $legacydata legacy user and tool data, if testing migration cases.
216
     * @param array $launchdata data describing the launch, including user data and migration claim data.
217
     * @param array $expected the test case expectations.
218
     * @covers ::find_or_create_user_from_launch
219
     */
11 efrain 220
    public function test_find_or_create_user_from_launch(?array $legacydata, array $launchdata, array $expected = []): void {
1 efrain 221
        $this->resetAfterTest();
222
        global $DB;
223
        $auth = get_auth_plugin('lti');
224
 
225
        // When testing platform users who have authenticated before, make that first auth call.
226
        if (!empty($launchdata['has_authenticated_before'])) {
227
            $mockjwtdata = $this->get_mock_launchdata_for_user($launchdata['user']);
228
            $firstauthuser = $auth->find_or_create_user_from_launch($mockjwtdata);
229
        }
230
 
231
        // Create legacy users and mocked tool secrets if desired.
232
        $legacysecrets = [];
233
        if ($legacydata) {
234
            $legacyusers = [];
235
            $generator = $this->getDataGenerator();
236
            foreach ($legacydata['users'] as $legacyuser) {
237
                $username = 'enrol_lti' . sha1($legacydata['consumer_key'] . '::' . $legacydata['consumer_key'] .
238
                        ':' . $legacyuser['user_id']);
239
 
240
                $legacyusers[] = $generator->create_user([
241
                    'username' => $username,
242
                    'auth' => 'lti'
243
                ]);
244
            }
245
            // In a real usage, legacy tool secrets are only passed for a consumer, as indicated in the migration claim.
246
            if (!empty($launchdata['migration_claim'])) {
247
                $legacysecrets = array_column($legacydata['tools'], 'secret');
248
            }
249
        }
250
 
251
        // Mock the launchdata.
252
        $mockjwtdata = $this->get_mock_launchdata_for_user($launchdata['user'], $launchdata['migration_claim'] ?? []);
253
 
254
        // Authenticate the platform user.
255
        $sink = $this->redirectEvents();
256
        $countusersbefore = $DB->count_records('user');
257
        $user = $auth->find_or_create_user_from_launch($mockjwtdata, $legacysecrets);
258
        if (!empty($expected['migration_debugging'])) {
259
            $this->assertDebuggingCalled();
260
        }
261
        $countusersafter = $DB->count_records('user');
262
        $events = $sink->get_events();
263
        $sink->close();
264
 
265
        // Verify user count is correct. i.e. no user is created when migration claim is correctly processed or when
266
        // the user has authenticated with the tool before.
267
        $numnewusers = (!empty($expected['migrated'])) ? 0 : 1;
268
        $numnewusers = (!empty($launchdata['has_authenticated_before'])) ?
269
 
270
        $this->assertEquals($numnewusers, $countusersafter - $countusersbefore);
271
 
272
        if (!empty($expected['migrated'])) {
273
            // If migrated, verify the user account is reusing the legacy user account.
274
            $legacyuserids = array_column($legacyusers, 'id');
275
            $this->assertContains($user->id, $legacyuserids);
276
            $this->assertEmpty($events); // No updates as part of this method.
277
        } else if (isset($firstauthuser)) {
278
            // If the user is authenticating a second time, confirm the same account is being returned.
279
            $this->assertEquals($firstauthuser->id, $user->id);
280
            $this->assertEmpty($events); // No updates as part of this method.
281
        } else {
282
            // The user wasn't migrated and hasn't launched before, so we expect a user_created event.
283
            $this->assertInstanceOf(\core\event\user_created::class, $events[0]);
284
        }
285
    }
286
 
287
    /**
288
     * Data provider for testing launch-based authentication.
289
     *
290
     * @return array the test case data.
291
     */
1441 ariadna 292
    public static function launchdata_provider(): array {
1 efrain 293
        return [
294
            'New (unlinked) platform learner including PII, no legacy user, no migration claim' => [
1441 ariadna 295
                'legacydata' => null,
296
                'launchdata' => [
297
                    'user' => self::get_mock_users_with_ids(
1 efrain 298
                        ['1'],
299
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner'
300
                    )[0],
301
                    'migration_claim' => null
302
                ],
303
            ],
304
            'New (unlinked) platform learner excluding names, no legacy user, no migration claim' => [
1441 ariadna 305
                'legacydata' => null,
306
                'launchdata' => [
307
                    'user' => self::get_mock_users_with_ids(
1 efrain 308
                        ['1'],
309
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner',
310
                        false
311
                    )[0],
312
                    'migration_claim' => null
313
                ],
314
            ],
315
            'New (unlinked) platform learner excluding emails, no legacy user, no migration claim' => [
1441 ariadna 316
                'legacydata' => null,
317
                'launchdata' => [
318
                    'user' => self::get_mock_users_with_ids(
1 efrain 319
                        ['1'],
320
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner',
321
                        true,
322
                        false
323
                    )[0],
324
                    'migration_claim' => null
325
                ],
326
            ],
327
            'New (unlinked) platform learner excluding all PII, no legacy user, no migration claim' => [
1441 ariadna 328
                'legacydata' => null,
329
                'launchdata' => [
330
                    'user' => self::get_mock_users_with_ids(
1 efrain 331
                        ['1'],
332
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner',
333
                        false,
334
                        false
335
                    )[0],
336
                    'migration_claim' => null
337
                ],
338
            ],
339
            'New (unlinked) platform learner including PII, existing legacy user, valid migration claim' => [
1441 ariadna 340
                'legacydata' => [
1 efrain 341
                    'users' => [
342
                        ['user_id' => '123-abc'],
343
                    ],
344
                    'consumer_key' => 'CONSUMER_1',
345
                    'tools' => [
346
                        ['secret' => 'toolsecret1'],
347
                        ['secret' => 'toolsecret2'],
348
                    ]
349
                ],
1441 ariadna 350
                'launchdata' => [
351
                    'user' => self::get_mock_users_with_ids(
1 efrain 352
                        ['1'],
353
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner'
354
                    )[0],
355
                    'migration_claim' => [
356
                        'consumer_key' => 'CONSUMER_1',
357
                        'signing_secret' => 'toolsecret1',
358
                        'user_id' => '123-abc',
359
                        'context_id' => 'd345b',
360
                        'tool_consumer_instance_guid' => '12345-123',
361
                        'resource_link_id' => '4b6fa'
362
                    ]
363
                ],
364
                'expected' => [
365
                    'migrated' => true
366
                ]
367
            ],
368
            'New (unlinked) platform learner including PII, existing legacy user, no migration claim' => [
1441 ariadna 369
                'legacydata' => [
1 efrain 370
                    'users' => [
371
                        ['user_id' => '123-abc'],
372
                    ],
373
                    'consumer_key' => 'CONSUMER_1',
374
                    'tools' => [
375
                        ['secret' => 'toolsecret1'],
376
                        ['secret' => 'toolsecret2'],
377
                    ]
378
                ],
1441 ariadna 379
                'launchdata' => [
380
                    'user' => self::get_mock_users_with_ids(
1 efrain 381
                        ['1'],
382
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner'
383
                    )[0],
384
                    'migration_claim' => null,
385
                ],
386
                'expected' => [
387
                    'migrated' => false,
388
                ]
389
            ],
390
            'New (unlinked) platform learner including PII, existing legacy user, migration missing consumer_key' => [
1441 ariadna 391
                'legacydata' => [
1 efrain 392
                    'users' => [
393
                        ['user_id' => '123-abc'],
394
                    ],
395
                    'consumer_key' => 'CONSUMER_1',
396
                    'tools' => [
397
                        ['secret' => 'toolsecret1'],
398
                        ['secret' => 'toolsecret2'],
399
                    ]
400
                ],
1441 ariadna 401
                'launchdata' => [
402
                    'user' => self::get_mock_users_with_ids(
1 efrain 403
                        ['1'],
404
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner'
405
                    )[0],
406
                    'migration_claim' => [
407
                        'signing_secret' => 'toolsecret1',
408
                        'user_id' => '123-abc',
409
                        'context_id' => 'd345b',
410
                        'tool_consumer_instance_guid' => '12345-123',
411
                        'resource_link_id' => '4b6fa'
412
                    ]
413
                ],
414
                'expected' => [
415
                    'migrated' => false,
416
                    'migration_debugging' => true,
417
                ]
418
            ],
419
            'New (unlinked) platform learner including PII, existing legacy user, migration bad consumer_key' => [
1441 ariadna 420
                'legacydata' => [
1 efrain 421
                    'users' => [
422
                        ['user_id' => '123-abc'],
423
                    ],
424
                    'consumer_key' => 'CONSUMER_1',
425
                    'tools' => [
426
                        ['secret' => 'toolsecret1'],
427
                        ['secret' => 'toolsecret2'],
428
                    ]
429
                ],
1441 ariadna 430
                'launchdata' => [
431
                    'user' => self::get_mock_users_with_ids(
1 efrain 432
                        ['1'],
433
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner'
434
                    )[0],
435
                    'migration_claim' => [
436
                        'consumer_key' => 'CONSUMER_BAD',
437
                        'signing_secret' => 'toolsecret1',
438
                        'user_id' => '123-abc',
439
                        'context_id' => 'd345b',
440
                        'tool_consumer_instance_guid' => '12345-123',
441
                        'resource_link_id' => '4b6fa'
442
                    ]
443
                ],
444
                'expected' => [
445
                    'migrated' => false,
446
                ]
447
            ],
448
            'New (unlinked) platform learner including PII, existing legacy user, migration user not matched' => [
1441 ariadna 449
                'legacydata' => [
1 efrain 450
                    'users' => [
451
                        ['user_id' => '123-abc'],
452
                    ],
453
                    'consumer_key' => 'CONSUMER_1',
454
                    'tools' => [
455
                        ['secret' => 'toolsecret1'],
456
                        ['secret' => 'toolsecret2'],
457
                    ]
458
                ],
1441 ariadna 459
                'launchdata' => [
460
                    'user' => self::get_mock_users_with_ids(
1 efrain 461
                        ['1'],
462
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner'
463
                    )[0],
464
                    'migration_claim' => [
465
                        'consumer_key' => 'CONSUMER_1',
466
                        'signing_secret' => 'toolsecret1',
467
                        'user_id' => '234-bcd',
468
                        'context_id' => 'd345b',
469
                        'tool_consumer_instance_guid' => '12345-123',
470
                        'resource_link_id' => '4b6fa'
471
                    ]
472
                ],
473
                'expected' => [
474
                    'migrated' => false
475
                ]
476
            ],
477
            'New (unlinked) platform learner including PII, existing legacy user, valid migration claim secret2' => [
1441 ariadna 478
                'legacydata' => [
1 efrain 479
                    'users' => [
480
                        ['user_id' => '123-abc'],
481
                    ],
482
                    'consumer_key' => 'CONSUMER_1',
483
                    'tools' => [
484
                        ['secret' => 'toolsecret1'],
485
                        ['secret' => 'toolsecret2'],
486
                    ]
487
                ],
1441 ariadna 488
                'launchdata' => [
489
                    'user' => self::get_mock_users_with_ids(
1 efrain 490
                        ['1'],
491
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner'
492
                    )[0],
493
                    'migration_claim' => [
494
                        'consumer_key' => 'CONSUMER_1',
495
                        'signing_secret' => 'toolsecret2',
496
                        'user_id' => '123-abc',
497
                        'context_id' => 'd345b',
498
                        'tool_consumer_instance_guid' => '12345-123',
499
                        'resource_link_id' => '4b6fa'
500
                    ]
501
                ],
502
                'expected' => [
503
                    'migrated' => true
504
                ]
505
            ],
506
            'New (unlinked) platform learner including PII, existing legacy user, migration claim bad secret' => [
1441 ariadna 507
                'legacydata' => [
1 efrain 508
                    'users' => [
509
                        ['user_id' => '123-abc'],
510
                    ],
511
                    'consumer_key' => 'CONSUMER_1',
512
                    'tools' => [
513
                        ['secret' => 'toolsecret1'],
514
                        ['secret' => 'toolsecret2'],
515
                    ]
516
                ],
1441 ariadna 517
                'launchdata' => [
518
                    'user' => self::get_mock_users_with_ids(
1 efrain 519
                        ['1'],
520
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner'
521
                    )[0],
522
                    'migration_claim' => [
523
                        'consumer_key' => 'CONSUMER_1',
524
                        'signing_secret' => 'bad_secret',
525
                        'user_id' => '123-abc',
526
                        'context_id' => 'd345b',
527
                        'tool_consumer_instance_guid' => '12345-123',
528
                        'resource_link_id' => '4b6fa'
529
                    ]
530
                ],
531
                'expected' => [
532
                    'migrated' => false,
533
                    'migration_debugging' => true,
534
                ]
535
            ],
536
            'New (unlinked) platform learner including PII, no legacy user, valid migration claim' => [
1441 ariadna 537
                'legacydata' => [
1 efrain 538
                    'users' => [],
539
                    'consumer_key' => 'CONSUMER_1',
540
                    'tools' => [
541
                        ['secret' => 'toolsecret1'],
542
                        ['secret' => 'toolsecret2'],
543
                    ]
544
                ],
1441 ariadna 545
                'launchdata' => [
546
                    'user' => self::get_mock_users_with_ids(
1 efrain 547
                        ['1'],
548
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner'
549
                    )[0],
550
                    'migration_claim' => [
551
                        'consumer_key' => 'CONSUMER_1',
552
                        'signing_secret' => 'toolsecret2',
553
                        'user_id' => '123-abc',
554
                        'context_id' => 'd345b',
555
                        'tool_consumer_instance_guid' => '12345-123',
556
                        'resource_link_id' => '4b6fa'
557
                    ]
558
                ],
559
                'expected' => [
560
                    'migrated' => false
561
                ]
562
            ],
563
            'New (unlinked) platform learner excluding PII, existing legacy user, valid migration claim' => [
1441 ariadna 564
                'legacydata' => [
1 efrain 565
                    'users' => [
566
                        ['user_id' => '123-abc'],
567
                    ],
568
                    'consumer_key' => 'CONSUMER_1',
569
                    'tools' => [
570
                        ['secret' => 'toolsecret1'],
571
                        ['secret' => 'toolsecret2'],
572
                    ]
573
                ],
1441 ariadna 574
                'launchdata' => [
575
                    'user' => self::get_mock_users_with_ids(
1 efrain 576
                        ['1'],
577
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner',
578
                        false,
579
                        false
580
                    )[0],
581
                    'migration_claim' => [
582
                        'consumer_key' => 'CONSUMER_1',
583
                        'signing_secret' => 'toolsecret1',
584
                        'user_id' => '123-abc',
585
                        'context_id' => 'd345b',
586
                        'tool_consumer_instance_guid' => '12345-123',
587
                        'resource_link_id' => '4b6fa'
588
                    ]
589
                ],
590
                'expected' => [
591
                    'migrated' => true
592
                ]
593
            ],
594
            'New (unlinked) platform instructor including PII, no legacy user, no migration claim' => [
1441 ariadna 595
                'legacydata' => null,
596
                'launchdata' => [
597
                    'user' => self::get_mock_users_with_ids(
1 efrain 598
                        ['1'],
599
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Instructor'
600
                    )[0],
601
                    'migration_claim' => null
602
                ],
603
            ],
604
            'New (unlinked) platform instructor excluding PII, no legacy user, no migration claim' => [
1441 ariadna 605
                'legacydata' => null,
606
                'launchdata' => [
607
                    'user' => self::get_mock_users_with_ids(
1 efrain 608
                        ['1'],
609
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Instructor',
610
                        false,
611
                        false
612
                    )[0],
613
                    'migration_claim' => null
614
                ],
615
            ],
616
            'New (unlinked) platform instructor including PII, existing legacy user, valid migration claim' => [
1441 ariadna 617
                'legacydata' => [
1 efrain 618
                    'users' => [
619
                        ['user_id' => '123-abc'],
620
                    ],
621
                    'consumer_key' => 'CONSUMER_1',
622
                    'tools' => [
623
                        ['secret' => 'toolsecret1'],
624
                        ['secret' => 'toolsecret2'],
625
                    ]
626
                ],
1441 ariadna 627
                'launchdata' => [
628
                    'user' => self::get_mock_users_with_ids(
1 efrain 629
                        ['1'],
630
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Instructor'
631
                    )[0],
632
                    'migration_claim' => [
633
                        'consumer_key' => 'CONSUMER_1',
634
                        'signing_secret' => 'toolsecret1',
635
                        'user_id' => '123-abc',
636
                        'context_id' => 'd345b',
637
                        'tool_consumer_instance_guid' => '12345-123',
638
                        'resource_link_id' => '4b6fa'
639
                    ]
640
                ],
641
                'expected' => [
642
                    'migrated' => true
643
                ]
644
            ],
645
            'Existing (linked) platform learner including PII, no legacy user, no migration claim' => [
1441 ariadna 646
                'legacydata' => null,
647
                'launchdata' => [
1 efrain 648
                    'has_authenticated_before' => true,
1441 ariadna 649
                    'user' => self::get_mock_users_with_ids(
1 efrain 650
                        ['1'],
651
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner'
652
                    )[0],
653
                    'migration_claim' => null
654
                ],
655
            ],
656
            'Existing (linked) platform learner excluding PII, no legacy user, no migration claim' => [
1441 ariadna 657
                'legacydata' => null,
658
                'launchdata' => [
1 efrain 659
                    'has_authenticated_before' => true,
1441 ariadna 660
                    'user' => self::get_mock_users_with_ids(
1 efrain 661
                        ['1'],
662
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner',
663
                        false,
664
                        false
665
                    )[0],
666
                    'migration_claim' => null
667
                ],
668
            ],
669
            'Existing (linked) platform instructor including PII, no legacy user, no migration claim' => [
1441 ariadna 670
                'legacydata' => null,
671
                'launchdata' => [
1 efrain 672
                    'has_authenticated_before' => true,
1441 ariadna 673
                    'user' => self::get_mock_users_with_ids(
1 efrain 674
                        ['1'],
675
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Instructor'
676
                    )[0],
677
                    'migration_claim' => null
678
                ],
679
            ],
680
            'Existing (linked) platform instructor excluding PII, no legacy user, no migration claim' => [
1441 ariadna 681
                'legacydata' => null,
682
                'launchdata' => [
1 efrain 683
                    'has_authenticated_before' => true,
1441 ariadna 684
                    'user' => self::get_mock_users_with_ids(
1 efrain 685
                        ['1'],
686
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Instructor',
687
                        false,
688
                        false
689
                    )[0],
690
                    'migration_claim' => null
691
                ],
692
            ],
693
            'New (unlinked) platform instructor excluding PII, picture included' => [
1441 ariadna 694
                'legacydata' => null,
695
                'launchdata' => [
1 efrain 696
                    'has_authenticated_before' => false,
1441 ariadna 697
                    'user' => self::get_mock_users_with_ids(
1 efrain 698
                        ['1'],
699
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Instructor',
700
                        false,
701
                        false,
702
                        true
703
                    )[0],
704
                    'migration_claim' => null
705
                ],
706
            ]
707
        ];
708
    }
709
 
710
    /**
711
     * Test which verifies a user account can be created/found using the find_or_create_user_from_membership() method.
712
     *
713
     * @dataProvider membership_data_provider
714
     * @param array|null $legacydata legacy user and tool data, if testing migration cases.
715
     * @param array $memberdata data describing the membership data, including user data and legacy user id info.
716
     * @param string $iss the issuer URL string
717
     * @param string|null $legacyconsumerkey optional legacy consumer_key value for testing user migration
718
     * @param array $expected the test case expectations.
719
     * @covers ::find_or_create_user_from_membership
720
     */
721
    public function test_find_or_create_user_from_membership(?array $legacydata, array $memberdata, string $iss,
11 efrain 722
            ?string $legacyconsumerkey, array $expected): void {
1 efrain 723
 
724
        $this->resetAfterTest();
725
        global $DB;
726
        $auth = get_auth_plugin('lti');
727
 
728
        // When testing platform users who have authenticated before, make that first auth call.
729
        if (!empty($memberdata['has_authenticated_before'])) {
730
            $mockmemberdata = $this->get_mock_member_data_for_user($memberdata['user'],
731
                $memberdata['legacy_user_id'] ?? '');
732
            $firstauthuser = $auth->find_or_create_user_from_membership($mockmemberdata, $iss,
733
                $legacyconsumerkey ?? '');
734
        }
735
 
736
        // Create legacy users and mocked tool secrets if desired.
737
        if ($legacydata) {
738
            $legacyusers = [];
739
            $generator = $this->getDataGenerator();
740
            foreach ($legacydata['users'] as $legacyuser) {
741
                $username = 'enrol_lti' . sha1($legacydata['consumer_key'] . '::' . $legacydata['consumer_key'] .
742
                        ':' . $legacyuser['user_id']);
743
 
744
                $legacyusers[] = $generator->create_user([
745
                    'username' => $username,
746
                    'auth' => 'lti'
747
                ]);
748
            }
749
        }
750
 
751
        // Mock the membership data.
752
        $mockmemberdata = $this->get_mock_member_data_for_user($memberdata['user'], $memberdata['legacy_user_id'] ?? '');
753
 
754
        // Authenticate the platform user.
755
        $sink = $this->redirectEvents();
756
        $countusersbefore = $DB->count_records('user');
757
        $user = $auth->find_or_create_user_from_membership($mockmemberdata, $iss, $legacyconsumerkey ?? '');
758
        $countusersafter = $DB->count_records('user');
759
        $events = $sink->get_events();
760
        $sink->close();
761
 
762
        // Verify user count is correct. i.e. no user is created when migration claim is correctly processed or when
763
        // the user has authenticated with the tool before.
764
        $numnewusers = (!empty($expected['migrated'])) ? 0 : 1;
765
        $numnewusers = (!empty($memberdata['has_authenticated_before'])) ?
766
 
767
        $this->assertEquals($numnewusers, $countusersafter - $countusersbefore);
768
 
769
        // Verify PII is updated appropriately.
770
        switch ($expected['PII']) {
771
            case self::PII_ALL:
772
                $this->assertEquals($memberdata['user']['given_name'], $user->firstname);
773
                $this->assertEquals($memberdata['user']['family_name'], $user->lastname);
774
                $this->assertEquals($memberdata['user']['email'], $user->email);
775
                break;
776
            case self::PII_NAMES_ONLY:
777
                $this->assertEquals($memberdata['user']['given_name'], $user->firstname);
778
                $this->assertEquals($memberdata['user']['family_name'], $user->lastname);
779
                $email = 'enrol_lti_13_' . sha1($iss . '_' . $mockmemberdata['user_id']) . "@example.com";
780
                $this->assertEquals($email, $user->email);
781
                break;
782
            case self::PII_EMAILS_ONLY:
783
                $this->assertEquals($iss, $user->lastname);
784
                $this->assertEquals($mockmemberdata['user_id'], $user->firstname);
785
                $this->assertEquals($memberdata['user']['email'], $user->email);
786
                break;
787
            default:
788
            case self::PII_NONE:
789
                $this->assertEquals($iss, $user->lastname);
790
                $this->assertEquals($mockmemberdata['user_id'], $user->firstname);
791
                $email = 'enrol_lti_13_' . sha1($iss . '_' . $mockmemberdata['user_id']) . "@example.com";
792
                $this->assertEquals($email, $user->email);
793
                break;
794
        }
795
 
796
        if (!empty($expected['migrated'])) {
797
            // If migrated, verify the user account is reusing the legacy user account.
798
            $legacyuserids = array_column($legacyusers, 'id');
799
            $this->assertContains($user->id, $legacyuserids);
800
            $this->assertInstanceOf(\core\event\user_updated::class, $events[0]);
801
        } else if (isset($firstauthuser)) {
802
            // If the user is authenticating a second time, confirm the same account is being returned.
803
            $this->assertEquals($firstauthuser->id, $user->id);
804
            $this->assertEmpty($events); // The user authenticated with the same data once before, so we don't expect an update.
805
        } else {
806
            // The user wasn't migrated and hasn't launched before, so we expect a user_created event.
807
            $this->assertInstanceOf(\core\event\user_created::class, $events[0]);
808
        }
809
    }
810
 
811
    /**
812
     * Data provider for testing membership-service-based authentication.
813
     *
814
     * @return array the test case data.
815
     */
1441 ariadna 816
    public static function membership_data_provider(): array {
1 efrain 817
        return [
818
            'New (unlinked) platform learner including PII, no legacy data, no consumer key bound, no legacy id' => [
1441 ariadna 819
                'legacydata' => null,
820
                'memberdata' => [
821
                    'user' => self::get_mock_users_with_ids(
1 efrain 822
                        ['1'],
823
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner'
824
                    )[0],
825
                ],
1441 ariadna 826
                'iss' => self::$issuer,
827
                'legacyconsumerkey' => null,
1 efrain 828
                'expected' => [
829
                    'PII' => self::PII_ALL,
830
                    'migrated' => false
831
                ]
832
            ],
833
            'New (unlinked) platform learner excluding PII, no legacy data, no consumer key bound, no legacy id' => [
1441 ariadna 834
                'legacydata' => null,
835
                'memberdata' => [
836
                    'user' => self::get_mock_users_with_ids(
1 efrain 837
                        ['1'],
838
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner',
839
                        false,
840
                        false
841
                    )[0],
842
                ],
1441 ariadna 843
                'iss' => self::$issuer,
844
                'legacyconsumerkey' => null,
1 efrain 845
                'expected' => [
846
                    'PII' => self::PII_NONE,
847
                    'migrated' => false
848
                ]
849
            ],
850
            'New (unlinked) platform learner excluding names, no legacy data, no consumer key bound, no legacy id' => [
1441 ariadna 851
                'legacydata' => null,
852
                'memberdata' => [
853
                    'user' => self::get_mock_users_with_ids(
1 efrain 854
                        ['1'],
855
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner',
856
                        false,
857
                    )[0],
858
                ],
1441 ariadna 859
                'iss' => self::$issuer,
860
                'legacyconsumerkey' => null,
1 efrain 861
                'expected' => [
862
                    'PII' => self::PII_EMAILS_ONLY,
863
                    'migrated' => false
864
                ]
865
            ],
866
            'New (unlinked) platform learner excluding email, no legacy data, no consumer key bound, no legacy id' => [
1441 ariadna 867
                'legacydata' => null,
868
                'memberdata' => [
869
                    'user' => self::get_mock_users_with_ids(
1 efrain 870
                        ['1'],
871
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner',
872
                        true,
873
                        false
874
                    )[0],
875
                ],
1441 ariadna 876
                'iss' => self::$issuer,
877
                'legacyconsumerkey' => null,
1 efrain 878
                'expected' => [
879
                    'PII' => self::PII_NAMES_ONLY,
880
                    'migrated' => false
881
                ]
882
            ],
883
            'New (unlinked) platform learner including PII, legacy user, consumer key bound, legacy user id sent' => [
1441 ariadna 884
                'legacydata' => [
1 efrain 885
                    'users' => [
886
                        ['user_id' => '123-abc'],
887
                    ],
888
                    'consumer_key' => 'CONSUMER_1',
889
                ],
1441 ariadna 890
                'memberdata' => [
891
                    'user' => self::get_mock_users_with_ids(
1 efrain 892
                        ['1'],
893
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner'
894
                    )[0],
895
                    'legacy_user_id' => '123-abc'
896
                ],
1441 ariadna 897
                'iss' => self::$issuer,
898
                'legacyconsumerkey' => 'CONSUMER_1',
1 efrain 899
                'expected' => [
900
                    'PII' => self::PII_ALL,
901
                    'migrated' => true
902
                ]
903
            ],
904
            'New (unlinked) platform learner including PII, legacy user, consumer key bound, legacy user id omitted' => [
1441 ariadna 905
                'legacydata' => [
1 efrain 906
                    'users' => [
907
                        ['user_id' => '123-abc'],
908
                    ],
909
                    'consumer_key' => 'CONSUMER_1',
910
                ],
1441 ariadna 911
                'memberdata' => [
912
                    'user' => self::get_mock_users_with_ids(
1 efrain 913
                        ['1'],
914
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner'
915
                    )[0],
916
                ],
1441 ariadna 917
                'iss' => self::$issuer,
918
                'legacyconsumerkey' => 'CONSUMER_1',
1 efrain 919
                'expected' => [
920
                    'PII' => self::PII_ALL,
921
                    'migrated' => false,
922
                ]
923
            ],
924
            'New (unlinked) platform learner including PII, legacy user, consumer key bound, no change in user id' => [
1441 ariadna 925
                'legacydata' => [
1 efrain 926
                    'users' => [
927
                        ['user_id' => '123-abc'],
928
                    ],
929
                    'consumer_key' => 'CONSUMER_1',
930
                ],
1441 ariadna 931
                'memberdata' => [
932
                    'user' => self::get_mock_users_with_ids(
1 efrain 933
                        ['123-abc'],
934
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner'
935
                    )[0],
936
                ],
1441 ariadna 937
                'iss' => self::$issuer,
938
                'legacyconsumerkey' => 'CONSUMER_1',
1 efrain 939
                'expected' => [
940
                    'PII' => self::PII_ALL,
941
                    'migrated' => true
942
                ]
943
            ],
944
            'New (unlinked) platform learner including PII, legacy user, unexpected consumer key bound, no change in user id' => [
1441 ariadna 945
                'legacydata' => [
1 efrain 946
                    'users' => [
947
                        ['user_id' => '123-abc'],
948
                    ],
949
                    'consumer_key' => 'CONSUMER_1',
950
                ],
1441 ariadna 951
                'memberdata' => [
952
                    'user' => self::get_mock_users_with_ids(
1 efrain 953
                        ['123-abc'],
954
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner'
955
                    )[0],
956
                ],
1441 ariadna 957
                'iss' => self::$issuer,
958
                'legacyconsumerkey' => 'CONSUMER_ABCDEF',
1 efrain 959
                'expected' => [
960
                    'PII' => self::PII_ALL,
961
                    'migrated' => false,
962
                ]
963
            ],
964
            'New (unlinked) platform learner including PII, legacy user, consumer key not bound, legacy user id sent' => [
1441 ariadna 965
                'legacydata' => [
1 efrain 966
                    'users' => [
967
                        ['user_id' => '123-abc'],
968
                    ],
969
                    'consumer_key' => 'CONSUMER_1',
970
                ],
1441 ariadna 971
                'memberdata' => [
972
                    'user' => self::get_mock_users_with_ids(
1 efrain 973
                        ['1'],
974
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner'
975
                    )[0],
976
                    'legacy_user_id' => '123-abc'
977
                ],
1441 ariadna 978
                'iss' => self::$issuer,
979
                'legacyconsumerkey' => null,
1 efrain 980
                'expected' => [
981
                    'PII' => self::PII_ALL,
982
                    'migrated' => false
983
                ]
984
            ],
985
            'New (unlinked) platform learner including PII, no legacy data, consumer key bound, legacy user id sent' => [
1441 ariadna 986
                'legacydata' => null,
987
                'memberdata' => [
988
                    'user' => self::get_mock_users_with_ids(
1 efrain 989
                        ['1'],
990
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner'
991
                    )[0],
992
                    'legacy_user_id' => '123-abc'
993
                ],
1441 ariadna 994
                'iss' => self::$issuer,
995
                'legacyconsumerkey' => 'CONSUMER_1',
1 efrain 996
                'expected' => [
997
                    'PII' => self::PII_ALL,
998
                    'migrated' => false
999
                ]
1000
            ],
1001
            'New (unlinked) platform instructor including PII, no legacy data, no consumer key bound, no legacy id' => [
1441 ariadna 1002
                'legacydata' => null,
1003
                'memberdata' => [
1004
                    'user' => self::get_mock_users_with_ids(
1 efrain 1005
                        ['1'],
1006
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Instructor'
1007
                    )[0],
1008
                ],
1441 ariadna 1009
                'iss' => self::$issuer,
1010
                'legacyconsumerkey' => null,
1 efrain 1011
                'expected' => [
1012
                    'PII' => self::PII_ALL,
1013
                    'migrated' => false
1014
                ]
1015
            ],
1016
            'New (unlinked) platform instructor excluding PII, no legacy data, no consumer key bound, no legacy id' => [
1441 ariadna 1017
                'legacydata' => null,
1018
                'memberdata' => [
1019
                    'user' => self::get_mock_users_with_ids(
1 efrain 1020
                        ['1'],
1021
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Instructor',
1022
                        false,
1023
                        false
1024
                    )[0],
1025
                ],
1441 ariadna 1026
                'iss' => self::$issuer,
1027
                'legacyconsumerkey' => null,
1 efrain 1028
                'expected' => [
1029
                    'PII' => self::PII_NONE,
1030
                    'migrated' => false
1031
                ]
1032
            ],
1033
            'Existing (linked) platform learner including PII, no legacy data, no consumer key bound, no legacy id' => [
1441 ariadna 1034
                'legacydata' => null,
1035
                'memberdata' => [
1 efrain 1036
                    'has_authenticated_before' => true,
1441 ariadna 1037
                    'user' => self::get_mock_users_with_ids(
1 efrain 1038
                        ['1'],
1039
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner'
1040
                    )[0],
1041
                ],
1441 ariadna 1042
                'iss' => self::$issuer,
1043
                'legacyconsumerkey' => null,
1 efrain 1044
                'expected' => [
1045
                    'PII' => self::PII_ALL,
1046
                    'migrated' => false
1047
                ]
1048
            ],
1049
        ];
1050
    }
1051
 
1052
    /**
1053
     * Test the behaviour of create_user_binding().
1054
     *
1055
     * @covers ::create_user_binding
1056
     */
11 efrain 1057
    public function test_create_user_binding(): void {
1 efrain 1058
        $this->resetAfterTest();
1059
        global $DB;
1060
        $auth = get_auth_plugin('lti');
1061
        $user = $this->getDataGenerator()->create_user();
1441 ariadna 1062
        $mockiss = self::$issuer;
1 efrain 1063
        $mocksub = '1';
1064
 
1065
        // Create a binding and verify it exists.
1066
        $this->assertFalse($DB->record_exists('auth_lti_linked_login', ['userid' => $user->id]));
1067
        $auth->create_user_binding($mockiss, $mocksub, $user->id);
1068
        $this->assertTrue($DB->record_exists('auth_lti_linked_login', ['userid' => $user->id]));
1069
 
1070
        // Now, try to get an authenticated user USING that binding. Verify the bound user is returned.
1071
        $numusersbefore = $DB->count_records('user');
1072
        $matcheduser = $auth->find_or_create_user_from_launch(
1073
            $this->get_mock_launchdata_for_user(
1441 ariadna 1074
                self::get_mock_users_with_ids([$mocksub])[0]
1 efrain 1075
            )
1076
        );
1077
        $numusersafter = $DB->count_records('user');
1078
        $this->assertEquals($numusersafter, $numusersbefore);
1079
        $this->assertEquals($user->id, $matcheduser->id);
1080
 
1081
        // Assert idempotency of the bind call.
1082
        $this->assertNull($auth->create_user_binding($mockiss, $mocksub, $user->id));
1083
    }
1084
 
1085
    /**
1086
     * Test updating a user account based on a given set of launchdata.
1087
     *
1088
     * @param array $firstlaunchdata the data from the first launch the user made.
1089
     * @param array $launchdata the current launch data, which will dictate what data is updated.
1090
     * @param array $expected array of test expectations
1091
     * @dataProvider update_user_account_provider
1092
     * @covers ::update_user_account
1093
     */
1094
    public function test_update_user_account(array $firstlaunchdata, array $launchdata, array $expected): void {
1095
        $this->resetAfterTest();
1096
        $auth = get_auth_plugin('lti');
1097
 
1098
        // Mock the first authentication of the user.
1099
        $firstmockjwtdata = $this->get_mock_launchdata_for_user($firstlaunchdata['user']);
1100
        $user = $auth->find_or_create_user_from_launch($firstmockjwtdata);
1101
 
1102
        // Now, mock the recent authentication, confirming updates.
1103
        $mockjwtdata = $this->get_mock_launchdata_for_user($launchdata['user']);
1104
        $sink = $this->redirectEvents();
1105
        $auth->update_user_account($user, $mockjwtdata, $mockjwtdata['iss']);
1106
        $user = \core_user::get_user($user->id);
1107
        $events = $sink->get_events();
1108
        $sink->close();
1109
 
1110
        if (!empty($expected['user_updated'])) {
1111
            $this->assertInstanceOf(\core\event\user_updated::class, $events[0]);
1112
        } else {
1113
            $this->assertEmpty($events);
1114
        }
1115
 
1116
        // Verify PII is updated appropriately.
1117
        switch ($expected['PII']) {
1118
            case self::PII_ALL:
1119
                $this->assertEquals($launchdata['user']['given_name'], $user->firstname);
1120
                $this->assertEquals($launchdata['user']['family_name'], $user->lastname);
1121
                $this->assertEquals($launchdata['user']['email'], $user->email);
1122
                break;
1123
            case self::PII_NAMES_ONLY:
1124
                $this->assertEquals($launchdata['user']['given_name'], $user->firstname);
1125
                $this->assertEquals($launchdata['user']['family_name'], $user->lastname);
1126
                $email = 'enrol_lti_13_' . sha1($mockjwtdata['iss'] . '_' . $mockjwtdata['sub']) . "@example.com";
1127
                $this->assertEquals($email, $user->email);
1128
                break;
1129
            case self::PII_EMAILS_ONLY:
1130
                $this->assertEquals($mockjwtdata['iss'], $user->lastname);
1131
                $this->assertEquals($mockjwtdata['sub'], $user->firstname);
1132
                $this->assertEquals($launchdata['user']['email'], $user->email);
1133
                break;
1134
            default:
1135
            case self::PII_NONE:
1136
                $this->assertEquals($mockjwtdata['iss'], $user->lastname);
1137
                $this->assertEquals($mockjwtdata['sub'], $user->firstname);
1138
                $email = 'enrol_lti_13_' . sha1($mockjwtdata['iss'] . '_' . $mockjwtdata['sub']) . "@example.com";
1139
                $this->assertEquals($email, $user->email);
1140
                break;
1141
        }
1142
 
1143
        // Verify picture sync occurs, if expected.
1144
        if (!empty($expected['picture_updated'])) {
1145
            $this->verify_user_profile_image_updated($user->id);
1146
        }
1147
    }
1148
 
1149
    /**
1150
     * Data provider for testing user user_update_account.
1151
     *
1152
     * @return array the test case data.
1153
     */
1441 ariadna 1154
    public static function update_user_account_provider(): array {
1 efrain 1155
        return [
1156
            'Full PII included in both auths, no picture in either' => [
1441 ariadna 1157
                'firstlaunchdata' => [
1158
                     'user' => self::get_mock_users_with_ids(
1 efrain 1159
                        ['1'],
1160
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner'
1161
                    )[0]
1162
                ],
1441 ariadna 1163
                'launchdata' => [
1164
                    'user' => self::get_mock_users_with_ids(
1 efrain 1165
                        ['1'],
1166
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner'
1167
                    )[0],
1168
                ],
1169
                'expected' => [
1170
                    'PII' => self::PII_ALL,
1171
                    'user_updated' => false,
1172
                    'picture_updated' => false
1173
                ]
1174
            ],
1175
            'No PII included in both auths, no picture in either' => [
1441 ariadna 1176
                'firstlaunchdata' => [
1177
                    'user' => self::get_mock_users_with_ids(
1 efrain 1178
                        ['1'],
1179
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner',
1180
                        false,
1181
                        false
1182
                    )[0]
1183
                ],
1441 ariadna 1184
                'launchdata' => [
1185
                    'user' => self::get_mock_users_with_ids(
1 efrain 1186
                        ['1'],
1187
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner',
1188
                        false,
1189
                        false
1190
                    )[0],
1191
                ],
1192
                'expected' => [
1193
                    'PII' => self::PII_NONE,
1194
                    'user_updated' => false,
1195
                    'picture_updated' => false
1196
                ]
1197
            ],
1198
            'First auth no PII, second auth including PII, no picture in either' => [
1441 ariadna 1199
                'firstlaunchdata' => [
1200
                    'user' => self::get_mock_users_with_ids(
1 efrain 1201
                        ['1'],
1202
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner',
1203
                        false,
1204
                        false
1205
                    )[0]
1206
                ],
1441 ariadna 1207
                'launchdata' => [
1208
                    'user' => self::get_mock_users_with_ids(
1 efrain 1209
                        ['1'],
1210
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner'
1211
                    )[0],
1212
                ],
1213
                'expected' => [
1214
                    'PII' => self::PII_ALL,
1215
                    'user_updated' => true,
1216
                    'picture_updated' => false
1217
                ]
1218
            ],
1219
            'First auth full PII, second auth no PII, no picture in either' => [
1441 ariadna 1220
                'firstlaunchdata' => [
1221
                    'user' => self::get_mock_users_with_ids(
1 efrain 1222
                        ['1'],
1223
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner',
1224
                    )[0]
1225
                ],
1441 ariadna 1226
                'launchdata' => [
1227
                    'user' => self::get_mock_users_with_ids(
1 efrain 1228
                        ['1'],
1229
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner',
1230
                        false,
1231
                        false
1232
                    )[0],
1233
                ],
1234
                'expected' => [
1235
                    'PII' => self::PII_NONE,
1236
                    'user_updated' => true,
1237
                    'picture_updated' => false
1238
                ]
1239
            ],
1240
            'First auth full PII, second auth emails only, no picture in either' => [
1441 ariadna 1241
                'firstlaunchdata' => [
1242
                    'user' => self::get_mock_users_with_ids(
1 efrain 1243
                        ['1'],
1244
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner',
1245
                    )[0]
1246
                ],
1441 ariadna 1247
                'launchdata' => [
1248
                    'user' => self::get_mock_users_with_ids(
1 efrain 1249
                        ['1'],
1250
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner',
1251
                        false
1252
                    )[0],
1253
                ],
1254
                'expected' => [
1255
                    'PII' => self::PII_EMAILS_ONLY,
1256
                    'user_updated' => true,
1257
                    'picture_updated' => false
1258
                ]
1259
            ],
1260
            'First auth full PII, second auth names only, no picture in either' => [
1441 ariadna 1261
                'firstlaunchdata' => [
1262
                    'user' => self::get_mock_users_with_ids(
1 efrain 1263
                        ['1'],
1264
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner',
1265
                    )[0]
1266
                ],
1441 ariadna 1267
                'launchdata' => [
1268
                    'user' => self::get_mock_users_with_ids(
1 efrain 1269
                        ['1'],
1270
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner',
1271
                        true,
1272
                        false
1273
                    )[0],
1274
                ],
1275
                'expected' => [
1276
                    'PII' => self::PII_NAMES_ONLY,
1277
                    'user_updated' => true,
1278
                    'picture_updated' => false
1279
                ]
1280
            ],
1281
            'Full PII included in both auths, picture included in the second auth' => [
1441 ariadna 1282
                'firstlaunchdata' => [
1283
                    'user' => self::get_mock_users_with_ids(
1 efrain 1284
                        ['1'],
1285
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner'
1286
                    )[0]
1287
                ],
1441 ariadna 1288
                'launchdata' => [
1289
                    'user' => self::get_mock_users_with_ids(
1 efrain 1290
                        ['1'],
1291
                        'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner',
1292
                        true,
1293
                        true,
1294
                        true
1295
                    )[0],
1296
                ],
1297
                'expected' => [
1298
                    'PII' => self::PII_ALL,
1299
                    'user_updated' => false,
1300
                    'picture_updated' => false
1301
                ]
1302
            ],
1303
        ];
1304
    }
1305
 
1306
}