Proyectos de Subversion Moodle

Rev

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

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
namespace quizaccess_seb;
18
 
19
use quizaccess_seb;
20
 
21
defined('MOODLE_INTERNAL') || die();
22
 
23
require_once(__DIR__ . '/test_helper_trait.php');
24
 
25
/**
26
 * PHPUnit tests for plugin rule class.
27
 *
28
 * @package    quizaccess_seb
29
 * @author     Andrew Madden <andrewmadden@catalyst-au.net>
30
 * @copyright  2020 Catalyst IT
31
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
32
 * @covers \quizaccess_seb
33
 */
34
class rule_test extends \advanced_testcase {
35
    use \quizaccess_seb_test_helper_trait;
36
 
37
    /**
38
     * Called before every test.
39
     */
40
    public function setUp(): void {
41
        parent::setUp();
42
 
43
        $this->resetAfterTest();
44
        $this->course = $this->getDataGenerator()->create_course();
45
    }
46
 
47
    /**
48
     * Called after every test.
49
     */
50
    public function tearDown(): void {
51
        global $SESSION;
52
 
53
        if (!empty($this->quiz)) {
54
            unset($SESSION->quizaccess_seb_access);
55
        }
56
    }
57
 
58
    /**
59
     * Helper method to get SEB download link for testing.
60
     *
61
     * @return string
62
     */
63
    private function get_seb_download_link() {
64
        return 'https://safeexambrowser.org/download_en.html';
65
    }
66
 
67
    /**
68
     * Helper method to get SEB launch link for testing.
69
     *
70
     * @return string
71
     */
72
    private function get_seb_launch_link() {
73
        return 'sebs://www.example.com/moodle/mod/quiz/accessrule/seb/config.php';
74
    }
75
 
76
    /**
77
     * Helper method to get SEB config download link for testing.
78
     *
79
     * @return string
80
     */
81
    private function get_seb_config_download_link() {
82
        return 'https://www.example.com/moodle/mod/quiz/accessrule/seb/config.php';
83
    }
84
 
85
    /**
86
     * Provider to return valid form field data when saving settings.
87
     *
88
     * @return array
89
     */
90
    public function valid_form_data_provider(): array {
91
        return [
92
            'valid seb_requiresafeexambrowser' => ['seb_requiresafeexambrowser', '0'],
93
            'valid seb_linkquitseb0' => ['seb_linkquitseb', 'http://safeexambrowser.org/macosx'],
94
            'valid seb_linkquitseb1' => ['seb_linkquitseb', 'safeexambrowser.org/macosx'],
95
            'valid seb_linkquitseb2' => ['seb_linkquitseb', 'www.safeexambrowser.org/macosx'],
96
            'valid seb_linkquitseb3' => ['seb_linkquitseb', 'any.type.of.url.looking.thing'],
97
            'valid seb_linkquitseb4' => ['seb_linkquitseb', 'http://any.type.of.url.looking.thing'],
98
        ];
99
    }
100
 
101
    /**
102
     * Provider to return invalid form field data when saving settings.
103
     *
104
     * @return array
105
     */
106
    public function invalid_form_data_provider(): array {
107
        return [
108
            'invalid seb_requiresafeexambrowser' => ['seb_requiresafeexambrowser', 'Uh oh!'],
109
            'invalid seb_linkquitseb0' => ['seb_linkquitseb', '\0'],
110
            'invalid seb_linkquitseb1' => ['seb_linkquitseb', 'invalid url'],
111
            'invalid seb_linkquitseb2' => ['seb_linkquitseb', 'http]://safeexambrowser.org/macosx'],
112
            'invalid seb_linkquitseb3' => ['seb_linkquitseb', '0'],
113
            'invalid seb_linkquitseb4' => ['seb_linkquitseb', 'seb://any.type.of.url.looking.thing'],
114
        ];
115
    }
116
 
117
    /**
118
     * Test no errors are found with valid data.
119
     *
120
     * @param string $setting
121
     * @param string $data
122
     *
123
     * @dataProvider valid_form_data_provider
124
     */
11 efrain 125
    public function test_validate_settings_with_valid_data(string $setting, string $data): void {
1 efrain 126
        $this->setAdminUser();
127
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
128
 
129
        $form = $this->createMock('mod_quiz_mod_form');
130
        $form->method('get_context')->willReturn(\context_module::instance($this->quiz->cmid));
131
 
132
        // Validate settings with a dummy form.
133
        $errors = quizaccess_seb::validate_settings_form_fields([], [
134
            'instance' => $this->quiz->id,
135
            'coursemodule' => $this->quiz->cmid,
136
            $setting => $data
137
        ], [], $form);
138
        $this->assertEmpty($errors);
139
    }
140
 
141
    /**
142
     * Test errors are found with invalid data.
143
     *
144
     * @param string $setting
145
     * @param string $data
146
     *
147
     * @dataProvider invalid_form_data_provider
148
     */
11 efrain 149
    public function test_validate_settings_with_invalid_data(string $setting, string $data): void {
1 efrain 150
        $this->setAdminUser();
151
 
152
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
153
        $form = $this->createMock('mod_quiz_mod_form');
154
        $form->method('get_context')->willReturn(\context_module::instance($this->quiz->cmid));
155
 
156
        // Validate settings with a dummy form and quiz instance.
157
        $errors = quizaccess_seb::validate_settings_form_fields([], [
158
            'instance' => $this->quiz->id,
159
            'coursemodule' => $this->quiz->cmid,
160
            $setting => $data
161
        ], [], $form);
162
        $this->assertEquals([$setting => 'Data submitted is invalid'], $errors);
163
    }
164
 
165
    /**
166
     * Test settings validation is not run if settings are locked.
167
     */
11 efrain 168
    public function test_settings_validation_is_not_run_if_settings_are_locked(): void {
1 efrain 169
        $user = $this->getDataGenerator()->create_user();
170
        $this->quiz = $this->create_test_quiz($this->course);
171
        $this->attempt_quiz($this->quiz, $user);
172
 
173
        $this->setAdminUser();
174
 
175
        $form = $this->createMock('mod_quiz_mod_form');
176
        $form->method('get_context')->willReturn(\context_module::instance($this->quiz->cmid));
177
 
178
        // Validate settings with a dummy form and quiz instance.
179
        $errors = quizaccess_seb::validate_settings_form_fields([], [
180
            'instance' => $this->quiz->id,
181
            'coursemodule' => $this->quiz->cmid, 'seb_requiresafeexambrowser' => 'Uh oh!'
182
        ], [], $form);
183
        $this->assertEmpty($errors);
184
    }
185
 
186
    /**
187
     * Test settings validation is not run if settings are conflicting.
188
     */
11 efrain 189
    public function test_settings_validation_is_not_run_if_conflicting_permissions(): void {
1 efrain 190
        $this->setAdminUser();
191
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
192
 
193
        $form = $this->createMock('mod_quiz_mod_form');
194
        $form->method('get_context')->willReturn(\context_module::instance($this->quiz->cmid));
195
 
196
        $user = $this->getDataGenerator()->create_user();
197
        $roleid = $this->getDataGenerator()->create_role();
198
        $context = \context_module::instance($this->quiz->cmid);
199
        assign_capability('quizaccess/seb:manage_seb_requiresafeexambrowser', CAP_ALLOW, $roleid, $context->id);
200
        $this->getDataGenerator()->role_assign($roleid, $user->id, $context->id);
201
 
202
        // By default The user won't have permissions to configure manually.
203
        $this->setUser($user);
204
 
205
        // Validate settings with a dummy form and quiz instance.
206
        $errors = quizaccess_seb::validate_settings_form_fields([], [
207
            'instance' => $this->quiz->id,
208
            'coursemodule' => $this->quiz->cmid,
209
            'seb_requiresafeexambrowser' => 'Uh oh!'
210
        ], [], $form);
211
        $this->assertEmpty($errors);
212
    }
213
 
214
    /**
215
     * Test bypassing validation if user don't have permissions to manage seb settings.
216
     */
11 efrain 217
    public function test_validate_settings_is_not_run_if_a_user_do_not_have_permissions_to_manage_seb_settings(): void {
1 efrain 218
        // Set the user who can't change seb settings. So validation should be bypassed.
219
        $user = $this->getDataGenerator()->create_user();
220
        $this->setUser($user);
221
 
222
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
223
        $form = $this->createMock('mod_quiz_mod_form');
224
        $form->method('get_context')->willReturn(\context_module::instance($this->quiz->cmid));
225
 
226
        // Validate settings with a dummy form and quiz instance.
227
        $errors = quizaccess_seb::validate_settings_form_fields([], [
228
            'instance' => $this->quiz->id,
229
            'coursemodule' => $this->quiz->cmid, 'seb_requiresafeexambrowser' => 'Uh oh!'
230
        ], [], $form);
231
        $this->assertEmpty($errors);
232
    }
233
 
234
    /**
235
     * Test settings are saved to DB.
236
     */
11 efrain 237
    public function test_create_settings_with_existing_quiz(): void {
1 efrain 238
        global $DB;
239
 
240
        $this->setAdminUser();
241
 
242
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_NO);
243
        $this->assertFalse($DB->record_exists('quizaccess_seb_quizsettings', ['quizid' => $this->quiz->id]));
244
 
245
        $this->quiz->seb_requiresafeexambrowser = settings_provider::USE_SEB_CONFIG_MANUALLY;
246
        quizaccess_seb::save_settings($this->quiz);
247
        $this->assertNotFalse($DB->record_exists('quizaccess_seb_quizsettings', ['quizid' => $this->quiz->id]));
248
    }
249
 
250
    /**
251
     * Test settings are not saved to DB if settings are locked.
252
     */
11 efrain 253
    public function test_settings_are_not_saved_if_settings_are_locked(): void {
1 efrain 254
        global $DB;
255
 
256
        $this->setAdminUser();
257
        $this->quiz = $this->create_test_quiz($this->course);
258
 
259
        $user = $this->getDataGenerator()->create_user();
260
        $this->setUser($user);
261
        $this->attempt_quiz($this->quiz, $user);
262
 
263
        $this->setAdminUser();
264
        $this->quiz->seb_requiresafeexambrowser = settings_provider::USE_SEB_CONFIG_MANUALLY;
265
        quizaccess_seb::save_settings($this->quiz);
266
        $this->assertFalse($DB->record_exists('quizaccess_seb_quizsettings', ['quizid' => $this->quiz->id]));
267
    }
268
 
269
    /**
270
     * Test settings are not saved to DB if conflicting permissions.
271
     */
11 efrain 272
    public function test_settings_are_not_saved_if_conflicting_permissions(): void {
1 efrain 273
        $this->setAdminUser();
274
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
275
 
276
        $user = $this->getDataGenerator()->create_user();
277
        $roleid = $this->getDataGenerator()->create_role();
278
        $context = \context_module::instance($this->quiz->cmid);
279
        assign_capability('quizaccess/seb:manage_seb_requiresafeexambrowser', CAP_ALLOW, $roleid, $context->id);
280
        $this->getDataGenerator()->role_assign($roleid, $user->id, $context->id);
281
 
282
        // By default The user won't have permissions to configure manually.
283
        $this->setUser($user);
284
 
285
        $this->quiz->seb_requiresafeexambrowser = settings_provider::USE_SEB_NO;
286
        quizaccess_seb::save_settings($this->quiz);
287
 
288
        $quizsettings = seb_quiz_settings::get_record(['quizid' => $this->quiz->id]);
289
        $this->assertEquals(settings_provider::USE_SEB_CONFIG_MANUALLY, $quizsettings->get('requiresafeexambrowser'));
290
    }
291
 
292
    /**
293
     * Test exception thrown if cm could not be found while saving settings.
294
     */
11 efrain 295
    public function test_save_settings_throw_an_exception_if_cm_not_found(): void {
1 efrain 296
        global $DB;
297
 
298
        $this->expectException(\dml_missing_record_exception::class);
299
        $this->expectExceptionMessage('Can\'t find data record in database.');
300
 
301
        $this->setAdminUser();
302
 
303
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
304
        $DB->delete_records('quiz', ['id' => $this->quiz->id]);
305
        $this->quiz->seb_requiresafeexambrowser = settings_provider::USE_SEB_NO;
306
        quizaccess_seb::save_settings($this->quiz);
307
    }
308
 
309
    /**
310
     * Test nothing happens when deleted is called without settings saved.
311
     */
11 efrain 312
    public function test_delete_settings_without_existing_settings(): void {
1 efrain 313
        global $DB;
314
        $this->setAdminUser();
315
 
316
        $quiz = new \stdClass();
317
        $quiz->id = 1;
318
        quizaccess_seb::delete_settings($quiz);
319
        $this->assertFalse($DB->record_exists('quizaccess_seb_quizsettings', ['quizid' => $quiz->id]));
320
    }
321
 
322
    /**
323
     * Test settings are deleted from DB.
324
     */
11 efrain 325
    public function test_delete_settings_with_existing_settings(): void {
1 efrain 326
        global $DB;
327
        $this->setAdminUser();
328
 
329
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
330
 
331
        // Using a generator will create the quiz_settings record.
332
        $this->assertNotFalse($DB->record_exists('quizaccess_seb_quizsettings', ['quizid' => $this->quiz->id]));
333
        quizaccess_seb::delete_settings($this->quiz);
334
        $this->assertFalse($DB->record_exists('quizaccess_seb_quizsettings', ['quizid' => $this->quiz->id]));
335
    }
336
 
337
    /**
338
     * A helper method to check invalid config key.
339
     */
340
    protected function check_invalid_config_key() {
341
        // Create an event sink, trigger event and retrieve event.
342
        $sink = $this->redirectEvents();
343
 
344
        // Check that correct error message is returned.
345
        $errormsg = $this->make_rule()->prevent_access();
346
        $this->assertNotEmpty($errormsg);
347
        $this->assertStringContainsString("The Safe Exam Browser keys could not be validated. "
348
            . "Check that you're using Safe Exam Browser with the correct configuration file.", $errormsg);
349
        $this->assertStringContainsString($this->get_seb_download_link(), $errormsg);
350
        $this->assertStringContainsString($this->get_seb_launch_link(), $errormsg);
351
        $this->assertStringContainsString($this->get_seb_config_download_link(), $errormsg);
352
 
353
        $events = $sink->get_events();
354
        $this->assertEquals(1, count($events));
355
        $event = reset($events);
356
 
357
        // Test that the event data is as expected.
358
        $this->assertInstanceOf('\quizaccess_seb\event\access_prevented', $event);
359
        $this->assertEquals('Invalid SEB config key', $event->other['reason']);
360
    }
361
 
362
    /**
363
     * Test access prevented if config key is invalid.
364
     */
11 efrain 365
    public function test_access_prevented_if_config_key_invalid(): void {
1 efrain 366
        global $FULLME;
367
 
368
        $this->setAdminUser();
369
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
370
 
371
        // Set up dummy request.
372
        $FULLME = 'https://example.com/moodle/mod/quiz/attempt.php?attemptid=123&page=4';
373
        $_SERVER['HTTP_X_SAFEEXAMBROWSER_CONFIGKEYHASH'] = 'Broken config key';
374
 
375
        $user = $this->getDataGenerator()->create_user();
376
        $this->setUser($user);
377
 
378
        $this->check_invalid_config_key();
379
    }
380
 
381
    /**
382
     * Test access prevented if config keys is invalid and using uploaded config.
383
     */
11 efrain 384
    public function test_access_prevented_if_config_key_invalid_uploaded_config(): void {
1 efrain 385
        global $FULLME;
386
 
387
        $this->setAdminUser();
388
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
389
 
390
        // Set quiz setting to require seb and save BEK.
391
        $quizsettings = seb_quiz_settings::get_record(['quizid' => $this->quiz->id]);
392
        $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_UPLOAD_CONFIG);
393
        $xml = file_get_contents(__DIR__ . '/fixtures/unencrypted.seb');
394
        $this->create_module_test_file($xml, $this->quiz->cmid);
395
        $quizsettings->save();
396
 
397
        // Set up dummy request.
398
        $FULLME = 'https://example.com/moodle/mod/quiz/attempt.php?attemptid=123&page=4';
399
        $_SERVER['HTTP_X_SAFEEXAMBROWSER_CONFIGKEYHASH'] = 'Broken config key';
400
 
401
        $user = $this->getDataGenerator()->create_user();
402
        $this->setUser($user);
403
 
404
        $this->check_invalid_config_key();
405
    }
406
 
407
    /**
408
     * Test access prevented if config keys is invalid and using template.
409
     */
11 efrain 410
    public function test_access_prevented_if_config_key_invalid_uploaded_template(): void {
1 efrain 411
        global $FULLME;
412
 
413
        $this->setAdminUser();
414
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
415
 
416
        // Set quiz setting to require seb and save BEK.
417
        $quizsettings = seb_quiz_settings::get_record(['quizid' => $this->quiz->id]);
418
        $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_TEMPLATE);
419
        $quizsettings->set('templateid', $this->create_template()->get('id'));
420
        $quizsettings->save();
421
 
422
        // Set up dummy request.
423
        $FULLME = 'https://example.com/moodle/mod/quiz/attempt.php?attemptid=123&page=4';
424
        $_SERVER['HTTP_X_SAFEEXAMBROWSER_CONFIGKEYHASH'] = 'Broken config key';
425
 
426
        $user = $this->getDataGenerator()->create_user();
427
        $this->setUser($user);
428
 
429
        $this->check_invalid_config_key();
430
    }
431
 
432
    /**
433
     * Test access not prevented if config key matches header.
434
     */
11 efrain 435
    public function test_access_allowed_if_config_key_valid(): void {
1 efrain 436
        global $FULLME;
437
 
438
        $this->setAdminUser();
439
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
440
 
441
        $user = $this->getDataGenerator()->create_user();
442
        $this->setUser($user);
443
 
444
        // Set quiz setting to require seb.
445
        $quizsettings = seb_quiz_settings::get_record(['quizid' => $this->quiz->id]);
446
 
447
        // Set up dummy request.
448
        $FULLME = 'https://example.com/moodle/mod/quiz/attempt.php?attemptid=123&page=4';
449
        $expectedhash = hash('sha256', $FULLME . $quizsettings->get_config_key());
450
        $_SERVER['HTTP_X_SAFEEXAMBROWSER_CONFIGKEYHASH'] = $expectedhash;
451
 
452
        // Check that correct error message is returned.
453
        $this->assertFalse($this->make_rule()->prevent_access());
454
    }
455
 
456
    /**
457
     * Test access not prevented if config key matches header.
458
     */
11 efrain 459
    public function test_access_allowed_if_config_key_valid_uploaded_config(): void {
1 efrain 460
        global $FULLME;
461
 
462
        $this->setAdminUser();
463
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
464
 
465
        // Set quiz setting to require seb and save BEK.
466
        $quizsettings = seb_quiz_settings::get_record(['quizid' => $this->quiz->id]);
467
        $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_TEMPLATE);
468
        $quizsettings->set('templateid', $this->create_template()->get('id'));
469
        $quizsettings->save();
470
 
471
        $user = $this->getDataGenerator()->create_user();
472
        $this->setUser($user);
473
 
474
        // Set up dummy request.
475
        $FULLME = 'https://example.com/moodle/mod/quiz/attempt.php?attemptid=123&page=4';
476
        $expectedhash = hash('sha256', $FULLME . $quizsettings->get_config_key());
477
        $_SERVER['HTTP_X_SAFEEXAMBROWSER_CONFIGKEYHASH'] = $expectedhash;
478
 
479
        // Check that correct error message is returned.
480
        $this->assertFalse($this->make_rule()->prevent_access());
481
    }
482
 
483
    /**
484
     * Test access not prevented if config key matches header.
485
     */
11 efrain 486
    public function test_access_allowed_if_config_key_valid_template(): void {
1 efrain 487
        global $FULLME;
488
 
489
        $this->setAdminUser();
490
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
491
 
492
        // Set quiz setting to require seb and save BEK.
493
        $quizsettings = seb_quiz_settings::get_record(['quizid' => $this->quiz->id]);
494
        $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_UPLOAD_CONFIG);
495
        $xml = file_get_contents(__DIR__ . '/fixtures/unencrypted.seb');
496
        $this->create_module_test_file($xml, $this->quiz->cmid);
497
        $quizsettings->save();
498
 
499
        $user = $this->getDataGenerator()->create_user();
500
        $this->setUser($user);
501
 
502
        // Set up dummy request.
503
        $FULLME = 'https://example.com/moodle/mod/quiz/attempt.php?attemptid=123&page=4';
504
        $expectedhash = hash('sha256', $FULLME . $quizsettings->get_config_key());
505
        $_SERVER['HTTP_X_SAFEEXAMBROWSER_CONFIGKEYHASH'] = $expectedhash;
506
 
507
        // Check that correct error message is returned.
508
        $this->assertFalse($this->make_rule()->prevent_access());
509
    }
510
 
511
    /**
512
     * Test access not prevented if browser exam keys match headers.
513
     */
11 efrain 514
    public function test_access_allowed_if_browser_exam_keys_valid(): void {
1 efrain 515
        global $FULLME;
516
 
517
        $this->setAdminUser();
518
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
519
 
520
        $user = $this->getDataGenerator()->create_user();
521
        $this->setUser($user);
522
 
523
        // Set quiz setting to require seb and save BEK.
524
        $browserexamkey = hash('sha256', 'testkey');
525
        $quizsettings = seb_quiz_settings::get_record(['quizid' => $this->quiz->id]);
526
        $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_CLIENT_CONFIG); // Doesn't check config key.
527
        $quizsettings->set('allowedbrowserexamkeys', $browserexamkey);
528
        $quizsettings->save();
529
 
530
        // Set up dummy request.
531
        $FULLME = 'https://example.com/moodle/mod/quiz/attempt.php?attemptid=123&page=4';
532
        $expectedhash = hash('sha256', $FULLME . $browserexamkey);
533
        $_SERVER['HTTP_X_SAFEEXAMBROWSER_REQUESTHASH'] = $expectedhash;
534
        $_SERVER['HTTP_USER_AGENT'] = 'SEB';
535
 
536
        // Check that correct error message is returned.
537
        $this->assertFalse($this->make_rule()->prevent_access());
538
    }
539
 
540
    /**
541
     * Test access not prevented if browser exam keys match headers.
542
     */
11 efrain 543
    public function test_access_allowed_if_browser_exam_keys_valid_use_uploaded_file(): void {
1 efrain 544
        global $FULLME;
545
 
546
        $this->setAdminUser();
547
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
548
 
549
        // Set quiz setting to require seb and save BEK.
550
        $browserexamkey = hash('sha256', 'testkey');
551
        $quizsettings = seb_quiz_settings::get_record(['quizid' => $this->quiz->id]);
552
        $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_UPLOAD_CONFIG);
553
        $quizsettings->set('allowedbrowserexamkeys', $browserexamkey);
554
        $xml = file_get_contents(__DIR__ . '/fixtures/unencrypted.seb');
555
        $this->create_module_test_file($xml, $this->quiz->cmid);
556
        $quizsettings->save();
557
 
558
        // Set up dummy request.
559
        $FULLME = 'https://example.com/moodle/mod/quiz/attempt.php?attemptid=123&page=4';
560
        $expectedbrowserkey = hash('sha256', $FULLME . $browserexamkey);
561
        $_SERVER['HTTP_X_SAFEEXAMBROWSER_REQUESTHASH'] = $expectedbrowserkey;
562
        $expectedconfigkey = hash('sha256', $FULLME . $quizsettings->get_config_key());
563
        $_SERVER['HTTP_X_SAFEEXAMBROWSER_CONFIGKEYHASH'] = $expectedconfigkey;
564
 
565
        $user = $this->getDataGenerator()->create_user();
566
        $this->setUser($user);
567
 
568
        // Check that correct error message is returned.
569
        $this->assertFalse($this->make_rule()->prevent_access());
570
    }
571
 
11 efrain 572
    public function test_access_allowed_if_access_state_stored_in_session(): void {
1 efrain 573
        global $SESSION;
574
 
575
        $this->setAdminUser();
576
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CLIENT_CONFIG);
577
 
578
        $user = $this->getDataGenerator()->create_user();
579
        $this->setUser($user);
580
 
581
        // Check that access is prevented.
582
        $this->check_invalid_basic_header();
583
 
584
        $SESSION->quizaccess_seb_access = [$this->quiz->cmid => true];
585
 
586
        // Check access is now not prevented.
587
        $this->assertFalse($this->make_rule()->prevent_access());
588
    }
589
 
590
    /**
591
     * A helper method to check invalid browser key.
592
     *
593
     * @param bool $downloadseblink Make sure download SEB link is present.
594
     * @param bool $launchlink Make sure launch SEB link is present.
595
     * @param bool $downloadconfiglink Make download config link is present.
596
     */
597
    protected function check_invalid_browser_exam_key($downloadseblink = true, $launchlink = true, $downloadconfiglink = true) {
598
        // Create an event sink, trigger event and retrieve event.
599
        $sink = $this->redirectEvents();
600
 
601
        // Check that correct error message is returned.
602
        $errormsg = $this->make_rule()->prevent_access();
603
        $this->assertNotEmpty($errormsg);
604
        $this->assertStringContainsString("The Safe Exam Browser keys could not be validated. "
605
            . "Check that you're using Safe Exam Browser with the correct configuration file.", $errormsg);
606
 
607
        if ($downloadseblink) {
608
            $this->assertStringContainsString($this->get_seb_download_link(), $errormsg);
609
        } else {
610
            $this->assertStringNotContainsString($this->get_seb_download_link(), $errormsg);
611
        }
612
 
613
        if ($launchlink) {
614
            $this->assertStringContainsString($this->get_seb_launch_link(), $errormsg);
615
        } else {
616
            $this->assertStringNotContainsString($this->get_seb_launch_link(), $errormsg);
617
        }
618
 
619
        if ($downloadconfiglink) {
620
            $this->assertStringContainsString($this->get_seb_config_download_link(), $errormsg);
621
        } else {
622
            $this->assertStringNotContainsString($this->get_seb_config_download_link(), $errormsg);
623
        }
624
 
625
        $events = $sink->get_events();
626
        $this->assertEquals(1, count($events));
627
        $event = reset($events);
628
 
629
        // Test that the event data is as expected.
630
        $this->assertInstanceOf('\quizaccess_seb\event\access_prevented', $event);
631
        $this->assertEquals('Invalid SEB browser key', $event->other['reason']);
632
    }
633
 
634
    /**
635
     * Test access prevented if browser exam keys do not match headers.
636
     */
11 efrain 637
    public function test_access_prevented_if_browser_exam_keys_are_invalid(): void {
1 efrain 638
        $this->setAdminUser();
639
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
640
 
641
        $user = $this->getDataGenerator()->create_user();
642
        $this->setUser($user);
643
 
644
        // Set quiz setting to require seb and save BEK.
645
        $browserexamkey = hash('sha256', 'testkey');
646
        $quizsettings = seb_quiz_settings::get_record(['quizid' => $this->quiz->id]);
647
        $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_CLIENT_CONFIG); // Doesn't check config key.
648
        $quizsettings->set('allowedbrowserexamkeys', $browserexamkey);
649
        $quizsettings->save();
650
 
651
        // Set up dummy request.
652
        $_SERVER['HTTP_X_SAFEEXAMBROWSER_REQUESTHASH'] = 'Broken browser key';
653
        $_SERVER['HTTP_USER_AGENT'] = 'SEB';
654
 
655
        $this->check_invalid_browser_exam_key(true, false, false);
656
    }
657
 
658
    /**
659
     * Test access prevented if browser exam keys do not match headers and using uploaded config.
660
     */
11 efrain 661
    public function test_access_prevented_if_browser_exam_keys_are_invalid_use_uploaded_file(): void {
1 efrain 662
        global $FULLME;
663
 
664
        $this->setAdminUser();
665
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
666
 
667
        // Set quiz setting to require seb and save BEK.
668
        $browserexamkey = hash('sha256', 'testkey');
669
        $quizsettings = seb_quiz_settings::get_record(['quizid' => $this->quiz->id]);
670
        $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_UPLOAD_CONFIG);
671
        $quizsettings->set('allowedbrowserexamkeys', $browserexamkey);
672
        $xml = file_get_contents(__DIR__ . '/fixtures/unencrypted.seb');
673
        $this->create_module_test_file($xml, $this->quiz->cmid);
674
        $quizsettings->save();
675
 
676
        // Set up dummy request.
677
        $FULLME = 'https://example.com/moodle/mod/quiz/attempt.php?attemptid=123&page=4';
678
        $expectedhash = hash('sha256', $FULLME . $quizsettings->get_config_key());
679
        $_SERVER['HTTP_X_SAFEEXAMBROWSER_CONFIGKEYHASH'] = $expectedhash;
680
 
681
        // Set  up broken browser key.
682
        $_SERVER['HTTP_X_SAFEEXAMBROWSER_REQUESTHASH'] = 'Broken browser key';
683
 
684
        $user = $this->getDataGenerator()->create_user();
685
        $this->setUser($user);
686
 
687
        $this->check_invalid_browser_exam_key();
688
    }
689
 
690
    /**
691
     * Test access not prevented if browser exam keys do not match headers and using template.
692
     */
11 efrain 693
    public function test_access_prevented_if_browser_exam_keys_are_invalid_use_template(): void {
1 efrain 694
        global $FULLME;
695
 
696
        $this->setAdminUser();
697
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
698
 
699
        // Set quiz setting to require seb and save BEK.
700
        $browserexamkey = hash('sha256', 'testkey');
701
        $quizsettings = seb_quiz_settings::get_record(['quizid' => $this->quiz->id]);
702
        $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_TEMPLATE);
703
        $quizsettings->set('allowedbrowserexamkeys', $browserexamkey);
704
        $quizsettings->set('templateid', $this->create_template()->get('id'));
705
        $quizsettings->save();
706
 
707
        // Set up dummy request.
708
        $FULLME = 'https://example.com/moodle/mod/quiz/attempt.php?attemptid=123&page=4';
709
        $expectedhash = hash('sha256', $FULLME . $quizsettings->get_config_key());
710
        $_SERVER['HTTP_X_SAFEEXAMBROWSER_CONFIGKEYHASH'] = $expectedhash;
711
 
712
        // Set  up broken browser key.
713
        $_SERVER['HTTP_X_SAFEEXAMBROWSER_REQUESTHASH'] = 'Broken browser key';
714
 
715
        $user = $this->getDataGenerator()->create_user();
716
        $this->setUser($user);
717
 
718
        // Check that correct error message is returned.
719
        $this->assertFalse($this->make_rule()->prevent_access());
720
    }
721
 
722
    /**
723
     * Test access allowed if using client configuration and SEB user agent header is valid.
724
     */
11 efrain 725
    public function test_access_allowed_if_using_client_config_basic_header_is_valid(): void {
1 efrain 726
        $this->setAdminUser();
727
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
728
 
729
        $user = $this->getDataGenerator()->create_user();
730
        $this->setUser($user);
731
 
732
        // Set quiz setting to require seb.
733
        $quizsettings = seb_quiz_settings::get_record(['quizid' => $this->quiz->id]);
734
        $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_CLIENT_CONFIG); // Doesn't check config key.
735
        $quizsettings->save();
736
 
737
        // Set up basic dummy request.
738
        $_SERVER['HTTP_USER_AGENT'] = 'SEB_TEST_SITE';
739
 
740
        // Check that correct error message is returned.
741
        $this->assertFalse($this->make_rule()->prevent_access());
742
    }
743
 
744
    /**
745
     * Test access prevented if using client configuration and SEB user agent header is invalid.
746
     */
11 efrain 747
    public function test_access_prevented_if_using_client_configuration_and_basic_head_is_invalid(): void {
1 efrain 748
        $this->setAdminUser();
749
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
750
 
751
        $user = $this->getDataGenerator()->create_user();
752
        $this->setUser($user);
753
 
754
        // Set quiz setting to require seb.
755
        $quizsettings = seb_quiz_settings::get_record(['quizid' => $this->quiz->id]);
756
        $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_CLIENT_CONFIG); // Doesn't check config key.
757
        $quizsettings->save();
758
 
759
        // Set up basic dummy request.
760
        $_SERVER['HTTP_USER_AGENT'] = 'WRONG_TEST_SITE';
761
 
762
        // Create an event sink, trigger event and retrieve event.
763
        $this->check_invalid_basic_header();
764
    }
765
 
766
    /**
767
     * A helper method to check invalid basic header.
768
     */
769
    protected function check_invalid_basic_header() {
770
        // Create an event sink, trigger event and retrieve event.
771
        $sink = $this->redirectEvents();
772
 
773
        // Check that correct error message is returned.
774
        $this->assertStringContainsString(
775
            'This quiz has been configured to use the Safe Exam Browser with client configuration.',
776
            $this->make_rule()->prevent_access()
777
        );
778
 
779
        $events = $sink->get_events();
780
        $this->assertEquals(1, count($events));
781
        $event = reset($events);
782
 
783
        // Test that the event data is as expected.
784
        $this->assertInstanceOf('\quizaccess_seb\event\access_prevented', $event);
785
        $this->assertEquals('No Safe Exam Browser is being used.', $event->other['reason']);
786
    }
787
 
788
    /**
789
     * Test access allowed if using client configuration and SEB user agent header is invalid and use uploaded file.
790
     */
11 efrain 791
    public function test_access_allowed_if_using_client_configuration_and_basic_head_is_invalid_use_uploaded_config(): void {
1 efrain 792
        global $FULLME;
793
 
794
        $this->setAdminUser();
795
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
796
 
797
        // Set quiz setting to require seb.
798
        $quizsettings = seb_quiz_settings::get_record(['quizid' => $this->quiz->id]);
799
        $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_UPLOAD_CONFIG); // Doesn't check basic header.
800
        $xml = file_get_contents(__DIR__ . '/fixtures/unencrypted.seb');
801
        $this->create_module_test_file($xml, $this->quiz->cmid);
802
        $quizsettings->save();
803
 
804
        // Set up dummy request.
805
        $FULLME = 'https://example.com/moodle/mod/quiz/attempt.php?attemptid=123&page=4';
806
        $expectedhash = hash('sha256', $FULLME . $quizsettings->get_config_key());
807
        $_SERVER['HTTP_X_SAFEEXAMBROWSER_CONFIGKEYHASH'] = $expectedhash;
808
        $_SERVER['HTTP_USER_AGENT'] = 'WRONG_TEST_SITE';
809
 
810
        $user = $this->getDataGenerator()->create_user();
811
        $this->setUser($user);
812
 
813
        // Check that correct error message is returned.
814
        $this->assertFalse($this->make_rule()->prevent_access());
815
    }
816
 
817
    /**
818
     * Test access allowed if using client configuration and SEB user agent header is invalid and use template.
819
     */
11 efrain 820
    public function test_access_allowed_if_using_client_configuration_and_basic_head_is_invalid_use_template(): void {
1 efrain 821
        global $FULLME;
822
 
823
        $this->setAdminUser();
824
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
825
 
826
        // Set quiz setting to require seb.
827
        $quizsettings = seb_quiz_settings::get_record(['quizid' => $this->quiz->id]);
828
        $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_TEMPLATE);
829
        $quizsettings->set('templateid', $this->create_template()->get('id'));
830
        $quizsettings->save();
831
 
832
        // Set up dummy request.
833
        $FULLME = 'https://example.com/moodle/mod/quiz/attempt.php?attemptid=123&page=4';
834
        $expectedhash = hash('sha256', $FULLME . $quizsettings->get_config_key());
835
        $_SERVER['HTTP_X_SAFEEXAMBROWSER_CONFIGKEYHASH'] = $expectedhash;
836
        $_SERVER['HTTP_USER_AGENT'] = 'WRONG_TEST_SITE';
837
 
838
        $user = $this->getDataGenerator()->create_user();
839
        $this->setUser($user);
840
 
841
        // Check that correct error message is returned.
842
        $this->assertFalse($this->make_rule()->prevent_access());
843
    }
844
 
845
    /**
846
     * Test access not prevented if SEB not required.
847
     */
11 efrain 848
    public function test_access_allowed_if_seb_not_required(): void {
1 efrain 849
        $this->setAdminUser();
850
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
851
 
852
        $user = $this->getDataGenerator()->create_user();
853
        $this->setUser($user);
854
 
855
        // Set quiz setting to not require seb.
856
        $quizsettings = seb_quiz_settings::get_record(['quizid' => $this->quiz->id]);
857
        $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_NO);
858
        $quizsettings->save();
859
 
860
        // The rule will not exist as the settings are not configured for SEB usage.
861
        $this->assertNull($this->make_rule());
862
    }
863
 
864
    /**
865
     * Test access not prevented if USER has bypass capability.
866
     */
11 efrain 867
    public function test_access_allowed_if_user_has_bypass_capability(): void {
1 efrain 868
        $this->setAdminUser();
869
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
870
 
871
        $user = $this->getDataGenerator()->create_user();
872
        $this->setUser($user);
873
 
874
        // Set the bypass SEB check capability to $USER.
875
        $this->assign_user_capability('quizaccess/seb:bypassseb', \context_module::instance($this->quiz->cmid)->id);
876
 
877
        // Check that correct error message is returned.
878
        $this->assertFalse($this->make_rule()->prevent_access());
879
    }
880
 
881
    /**
882
     * Test that quiz form cannot be saved if using template, but not actually pick one.
883
     */
11 efrain 884
    public function test_mod_quiz_form_cannot_be_saved_using_template_and_template_is_not_set(): void {
1 efrain 885
        $this->setAdminUser();
886
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
887
 
888
        $form = $this->createMock('mod_quiz_mod_form');
889
        $form->method('get_context')->willReturn(\context_module::instance($this->quiz->cmid));
890
 
891
        // Validate settings with a dummy form.
892
        $errors = quizaccess_seb::validate_settings_form_fields([], [
893
            'instance' => $this->quiz->id,
894
            'coursemodule' => $this->quiz->cmid,
895
            'seb_requiresafeexambrowser' => settings_provider::USE_SEB_TEMPLATE
896
        ], [], $form);
897
 
898
        $this->assertContains(get_string('invalidtemplate', 'quizaccess_seb'), $errors);
899
    }
900
 
901
    /**
902
     * Test that quiz form cannot be saved if uploaded invalid file.
903
     */
11 efrain 904
    public function test_mod_quiz_form_cannot_be_saved_using_uploaded_file_and_file_is_not_valid(): void {
1 efrain 905
        $this->setAdminUser();
906
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
907
 
908
        $form = $this->createMock('mod_quiz_mod_form');
909
        $form->method('get_context')->willReturn(\context_module::instance($this->quiz->cmid));
910
 
911
        // Validate settings with a dummy form.
912
        $errors = quizaccess_seb::validate_settings_form_fields([], [
913
            'instance' => $this->quiz->id,
914
            'coursemodule' => $this->quiz->cmid,
915
            'seb_requiresafeexambrowser' => settings_provider::USE_SEB_UPLOAD_CONFIG,
916
            'filemanager_sebconfigfile' => 0,
917
        ], [], $form);
918
 
919
        $this->assertContainsEquals(get_string('filenotpresent', 'quizaccess_seb'), $errors);
920
    }
921
 
922
    /**
923
     * Test that quiz form cannot be saved if the global settings are set to require a password and no password is set.
924
     */
11 efrain 925
    public function test_mod_quiz_form_cannot_be_saved_if_global_settings_force_quiz_password_and_none_is_set(): void {
1 efrain 926
        $this->setAdminUser();
927
        // Set global settings to require quiz password but set password to be empty.
928
        set_config('quizpasswordrequired', '1', 'quizaccess_seb');
929
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
930
 
931
        $form = $this->createMock('mod_quiz_mod_form');
932
        $form->method('get_context')->willReturn(\context_module::instance($this->quiz->cmid));
933
 
934
        // Validate settings with a dummy form.
935
        $errors = quizaccess_seb::validate_settings_form_fields([], [
936
            'instance' => $this->quiz->id,
937
            'coursemodule' => $this->quiz->cmid,
938
            'seb_requiresafeexambrowser' => settings_provider::USE_SEB_CONFIG_MANUALLY,
939
        ], [], $form);
940
 
941
        $this->assertContains(get_string('passwordnotset', 'quizaccess_seb'), $errors);
942
    }
943
 
944
    /**
945
     * Test that access to quiz is allowed if global setting is set to restrict quiz if no quiz password is set, and global quiz
946
     * password is set.
947
     */
11 efrain 948
    public function test_mod_quiz_form_can_be_saved_if_global_settings_force_quiz_password_and_is_set(): void {
1 efrain 949
        $this->setAdminUser();
950
        // Set global settings to require quiz password but set password to be empty.
951
        set_config('quizpasswordrequired', '1', 'quizaccess_seb');
952
 
953
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
954
 
955
        $form = $this->createMock('mod_quiz_mod_form');
956
        $form->method('get_context')->willReturn(\context_module::instance($this->quiz->cmid));
957
 
958
        // Validate settings with a dummy form.
959
        $errors = quizaccess_seb::validate_settings_form_fields([], [
960
            'instance' => $this->quiz->id,
961
            'coursemodule' => $this->quiz->cmid,
962
            'quizpassword' => 'set',
963
            'seb_requiresafeexambrowser' => settings_provider::USE_SEB_CONFIG_MANUALLY,
964
        ], [], $form);
965
        $this->assertNotContains(get_string('passwordnotset', 'quizaccess_seb'), $errors);
966
    }
967
 
968
    /**
969
     * Test that quiz form can be saved if the global settings are set to require a password and no seb usage selected.
970
     */
11 efrain 971
    public function test_mod_quiz_form_can_be_saved_if_global_settings_force_quiz_password_and_none_no_seb(): void {
1 efrain 972
        $this->setAdminUser();
973
        // Set global settings to require quiz password but set password to be empty.
974
        set_config('quizpasswordrequired', '1', 'quizaccess_seb');
975
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_NO);
976
 
977
        $form = $this->createMock('mod_quiz_mod_form');
978
        $form->method('get_context')->willReturn(\context_module::instance($this->quiz->cmid));
979
 
980
        // Validate settings with a dummy form.
981
        $errors = quizaccess_seb::validate_settings_form_fields([], [
982
            'instance' => $this->quiz->id,
983
            'coursemodule' => $this->quiz->cmid,
984
            'seb_requiresafeexambrowser' => settings_provider::USE_SEB_NO,
985
        ], [], $form);
986
 
987
        $this->assertNotContains(get_string('passwordnotset', 'quizaccess_seb'), $errors);
988
    }
989
 
990
    /**
991
     * Test get_download_seb_button, checks for empty config setting quizaccess_seb/downloadlink.
992
     */
11 efrain 993
    public function test_get_download_seb_button(): void {
1 efrain 994
        $this->setAdminUser();
995
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
996
 
997
        $user = $this->getDataGenerator()->create_user();
998
        $this->setUser($user);
999
 
1000
        $reflection = new \ReflectionClass('quizaccess_seb');
1001
        $method = $reflection->getMethod('get_download_seb_button');
1002
 
1003
        // The current default contents.
1004
        $this->assertStringContainsString($this->get_seb_download_link(), $method->invoke($this->make_rule()));
1005
 
1006
        set_config('downloadlink', '', 'quizaccess_seb');
1007
 
1008
        // Will not return any button if the URL is empty.
1009
        $this->assertSame('', $method->invoke($this->make_rule()));
1010
    }
1011
 
1012
    /**
1013
     * Test get_download_seb_button shows download SEB link when required,
1014
     */
11 efrain 1015
    public function test_get_get_action_buttons_shows_download_seb_link(): void {
1 efrain 1016
        $this->setAdminUser();
1017
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
1018
 
1019
        $user = $this->getDataGenerator()->create_user();
1020
        $this->setUser($user);
1021
 
1022
        $reflection = new \ReflectionClass('quizaccess_seb');
1023
        $method = $reflection->getMethod('get_action_buttons');
1024
 
1025
        $this->assertStringContainsString($this->get_seb_download_link(), $method->invoke($this->make_rule()));
1026
 
1027
        $this->quiz->seb_showsebdownloadlink = 0;
1028
        $this->assertStringNotContainsString($this->get_seb_download_link(), $method->invoke($this->make_rule()));
1029
    }
1030
 
1031
    /**
1032
     * Test get_download_seb_button shows SEB config related links when required.
1033
     */
11 efrain 1034
    public function test_get_get_action_buttons_shows_launch_and_download_config_links(): void {
1 efrain 1035
        $this->setAdminUser();
1036
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
1037
 
1038
        $user = $this->getDataGenerator()->create_user();
1039
        $this->setUser($user);
1040
 
1041
        $reflection = new \ReflectionClass('quizaccess_seb');
1042
        $method = $reflection->getMethod('get_action_buttons');
1043
 
1044
        $quizsettings = seb_quiz_settings::get_record(['quizid' => $this->quiz->id]);
1045
 
1046
        // Should see link when using manually.
1047
        $this->assertStringContainsString($this->get_seb_launch_link(), $method->invoke($this->make_rule()));
1048
        $this->assertStringContainsString($this->get_seb_config_download_link(), $method->invoke($this->make_rule()));
1049
 
1050
        // Should see links when using template.
1051
        $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_TEMPLATE);
1052
        $quizsettings->set('templateid', $this->create_template()->get('id'));
1053
        $quizsettings->save();
1054
        $this->assertStringContainsString($this->get_seb_launch_link(), $method->invoke($this->make_rule()));
1055
        $this->assertStringContainsString($this->get_seb_config_download_link(), $method->invoke($this->make_rule()));
1056
 
1057
        // Should see links when using uploaded config.
1058
        $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_UPLOAD_CONFIG);
1059
        $xml = file_get_contents(__DIR__ . '/fixtures/unencrypted.seb');
1060
        $this->create_module_test_file($xml, $this->quiz->cmid);
1061
        $quizsettings->save();
1062
        $this->assertStringContainsString($this->get_seb_launch_link(), $method->invoke($this->make_rule()));
1063
        $this->assertStringContainsString($this->get_seb_config_download_link(), $method->invoke($this->make_rule()));
1064
 
1065
        // Shouldn't see links if using client config.
1066
        $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_CLIENT_CONFIG);
1067
        $quizsettings->save();
1068
        $this->assertStringNotContainsString($this->get_seb_launch_link(), $method->invoke($this->make_rule()));
1069
        $this->assertStringNotContainsString($this->get_seb_config_download_link(), $method->invoke($this->make_rule()));
1070
    }
1071
 
1072
    /**
1073
     * Test get_download_seb_button shows SEB config related links as configured in "showseblinks".
1074
     */
11 efrain 1075
    public function test_get_get_action_buttons_shows_launch_and_download_config_links_as_configured(): void {
1 efrain 1076
        $this->setAdminUser();
1077
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
1078
 
1079
        $user = $this->getDataGenerator()->create_user();
1080
        $this->setUser($user);
1081
 
1082
        $reflection = new \ReflectionClass('quizaccess_seb');
1083
        $method = $reflection->getMethod('get_action_buttons');
1084
 
1085
        set_config('showseblinks', 'seb,http', 'quizaccess_seb');
1086
        $this->assertStringContainsString($this->get_seb_launch_link(), $method->invoke($this->make_rule()));
1087
        $this->assertStringContainsString($this->get_seb_config_download_link(), $method->invoke($this->make_rule()));
1088
 
1089
        set_config('showseblinks', 'http', 'quizaccess_seb');
1090
        $this->assertStringNotContainsString($this->get_seb_launch_link(), $method->invoke($this->make_rule()));
1091
        $this->assertStringContainsString($this->get_seb_config_download_link(), $method->invoke($this->make_rule()));
1092
 
1093
        set_config('showseblinks', 'seb', 'quizaccess_seb');
1094
        $this->assertStringContainsString($this->get_seb_launch_link(), $method->invoke($this->make_rule()));
1095
        $this->assertStringNotContainsString($this->get_seb_config_download_link(), $method->invoke($this->make_rule()));
1096
 
1097
        set_config('showseblinks', '', 'quizaccess_seb');
1098
        $this->assertStringNotContainsString($this->get_seb_launch_link(), $method->invoke($this->make_rule()));
1099
        $this->assertStringNotContainsString($this->get_seb_config_download_link(), $method->invoke($this->make_rule()));
1100
    }
1101
 
1102
    /**
1103
     * Test get_quit_button. If attempt count is greater than 0
1104
     */
11 efrain 1105
    public function test_get_quit_button(): void {
1 efrain 1106
        $this->setAdminUser();
1107
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CLIENT_CONFIG);
1108
        $this->quiz->seb_linkquitseb = "http://test.quit.link";
1109
 
1110
        $user = $this->getDataGenerator()->create_user();
1111
        $this->attempt_quiz($this->quiz, $user);
1112
        $this->setUser($user);
1113
 
1114
        // Set-up the button to be called.
1115
        $reflection = new \ReflectionClass('quizaccess_seb');
1116
        $method = $reflection->getMethod('get_quit_button');
1117
 
1118
        $button = $method->invoke($this->make_rule());
1119
        $this->assertStringContainsString("http://test.quit.link", $button);
1120
    }
1121
 
1122
    /**
1123
     * Test description, checks for a valid SEB session and attempt count .
1124
     */
11 efrain 1125
    public function test_description(): void {
1 efrain 1126
        $this->setAdminUser();
1127
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CLIENT_CONFIG);
1128
 
1129
        $this->quiz->seb_linkquitseb = "http://test.quit.link";
1130
 
1131
        // Set up basic dummy request.
1132
        $_SERVER['HTTP_USER_AGENT'] = 'SEB_TEST_SITE';
1133
 
1134
        $user = $this->getDataGenerator()->create_user();
1135
        $this->attempt_quiz($this->quiz, $user);
1136
 
1137
        $description = $this->make_rule()->description();
1138
        $this->assertCount(2, $description);
1139
        $this->assertEquals($description[0], get_string('sebrequired', 'quizaccess_seb'));
1140
        $this->assertEquals($description[1], '');
1141
 
1142
        // Set the user as display_quit_button() uses the global $USER.
1143
        $this->setUser($user);
1144
        $description = $this->make_rule()->description();
1145
        $this->assertCount(2, $description);
1146
        $this->assertEquals($description[0], get_string('sebrequired', 'quizaccess_seb'));
1147
 
1148
        // The button is contained in the description when a quiz attempt is finished.
1149
        $this->assertStringContainsString("http://test.quit.link", $description[1]);
1150
    }
1151
 
1152
    /**
1153
     * Test description displays download SEB config button when required.
1154
     */
11 efrain 1155
    public function test_description_shows_download_config_link_when_required(): void {
1 efrain 1156
        $this->setAdminUser();
1157
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
1158
 
1159
        $quizsettings = seb_quiz_settings::get_record(['quizid' => $this->quiz->id]);
1160
 
1161
        $user = $this->getDataGenerator()->create_user();
1162
        $roleid = $this->getDataGenerator()->create_role();
1163
        $context = \context_module::instance($this->quiz->cmid);
1164
        assign_capability('quizaccess/seb:bypassseb', CAP_ALLOW, $roleid, $context->id);
1165
 
1166
        $this->setUser($user);
1167
 
1168
        // Can see just basic description with standard perms.
1169
        $description = $this->make_rule()->description();
1170
        $this->assertCount(1, $description);
1171
        $this->assertEquals($description[0], get_string('sebrequired', 'quizaccess_seb'));
1172
 
1173
        // Can see download config link as have bypass SEB permissions.
1174
        $this->getDataGenerator()->role_assign($roleid, $user->id, $context->id);
1175
        $description = $this->make_rule()->description();
1176
        $this->assertCount(3, $description);
1177
        $this->assertEquals($description[0], get_string('sebrequired', 'quizaccess_seb'));
1178
        $this->assertStringContainsString($this->get_seb_config_download_link(), $description[1]);
1179
 
1180
        // Can't see download config link as usage method doesn't have SEB config to download.
1181
        $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_CLIENT_CONFIG);
1182
        $quizsettings->save();
1183
        $description = $this->make_rule()->description();
1184
        $this->assertCount(2, $description);
1185
        $this->assertEquals($description[0], get_string('sebrequired', 'quizaccess_seb'));
1186
    }
1187
 
1188
    /**
1189
     * Test block display before a quiz started.
1190
     */
11 efrain 1191
    public function test_blocks_display_before_attempt_started(): void {
1 efrain 1192
        global $PAGE;
1193
 
1194
        $this->setAdminUser();
1195
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CLIENT_CONFIG);
1196
 
1197
        $user = $this->getDataGenerator()->create_user();
1198
        $this->setUser($user);
1199
 
1200
        // We will check if we show only fake blocks. Which means no other blocks on a page.
1201
        $reflection = new \ReflectionClass('block_manager');
1202
        $property = $reflection->getProperty('fakeblocksonly');
1203
 
1204
        $this->assertFalse($property->getValue($PAGE->blocks));
1205
 
1206
        // Don't display blocks before start.
1207
        set_config('displayblocksbeforestart', 0, 'quizaccess_seb');
1208
        $this->set_up_quiz_view_page();
1209
        $this->make_rule()->prevent_access();
1210
        $this->assertEquals('secure', $PAGE->pagelayout);
1211
        $this->assertTrue($property->getValue($PAGE->blocks));
1212
 
1213
        // Display blocks before start.
1214
        set_config('displayblocksbeforestart', 1, 'quizaccess_seb');
1215
        $this->set_up_quiz_view_page();
1216
        $this->make_rule()->prevent_access();
1217
        $this->assertEquals('secure', $PAGE->pagelayout);
1218
        $this->assertFalse($property->getValue($PAGE->blocks));
1219
    }
1220
 
1221
    /**
1222
     * Test block display after a quiz completed.
1223
     */
11 efrain 1224
    public function test_blocks_display_after_attempt_finished(): void {
1 efrain 1225
        global $PAGE;
1226
 
1227
        $this->setAdminUser();
1228
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CLIENT_CONFIG);
1229
 
1230
        // Finish the quiz.
1231
        $user = $this->getDataGenerator()->create_user();
1232
        $this->attempt_quiz($this->quiz, $user);
1233
        $this->setUser($user);
1234
 
1235
        // We will check if we show only fake blocks. Which means no other blocks on a page.
1236
        $reflection = new \ReflectionClass('block_manager');
1237
        $property = $reflection->getProperty('fakeblocksonly');
1238
 
1239
        $this->assertFalse($property->getValue($PAGE->blocks));
1240
 
1241
        // Don't display blocks after finish.
1242
        set_config('displayblockswhenfinished', 0, 'quizaccess_seb');
1243
        $this->set_up_quiz_view_page();
1244
        $this->make_rule()->prevent_access();
1245
        $this->assertEquals('secure', $PAGE->pagelayout);
1246
        $this->assertTrue($property->getValue($PAGE->blocks));
1247
 
1248
        // Display blocks after finish.
1249
        set_config('displayblockswhenfinished', 1, 'quizaccess_seb');
1250
        $this->set_up_quiz_view_page();
1251
        $this->make_rule()->prevent_access();
1252
        $this->assertEquals('secure', $PAGE->pagelayout);
1253
        $this->assertFalse($property->getValue($PAGE->blocks));
1254
    }
1255
 
1256
    /**
1257
     * Test cleanup when quiz is completed.
1258
     */
11 efrain 1259
    public function test_current_attempt_finished(): void {
1 efrain 1260
        global $SESSION;
1261
        $this->setAdminUser();
1262
 
1263
        $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
1264
        $quizsettings = seb_quiz_settings::get_record(['quizid' => $this->quiz->id]);
1265
        $quizsettings->save();
1266
        // Set access for Moodle session.
1267
        $SESSION->quizaccess_seb_access = [$this->quiz->cmid => true];
1268
        $this->make_rule()->current_attempt_finished();
1269
 
1270
        $this->assertTrue(empty($SESSION->quizaccess_seb_access[$this->quiz->cmid]));
1271
    }
1272
}