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