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