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