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 defined('MOODLE_INTERNAL') || die(); 20 21 require_once (__DIR__ . '/test_helper_trait.php'); 22 23 /** 24 * PHPUnit tests for the access manager. 25 * 26 * @package quizaccess_seb 27 * @author Andrew Madden <andrewmadden@catalyst-au.net> 28 * @copyright 2020 Catalyst IT 29 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 30 */ 31 class access_manager_test extends \advanced_testcase { 32 use \quizaccess_seb_test_helper_trait; 33 34 /** 35 * Called before every test. 36 */ 37 public function setUp(): void { 38 parent::setUp(); 39 40 $this->resetAfterTest(); 41 $this->setAdminUser(); 42 $this->course = $this->getDataGenerator()->create_course(); 43 } 44 45 /** 46 * Test access_manager private property quizsettings is null. 47 */ 48 public function test_access_manager_quizsettings_null() { 49 $this->quiz = $this->create_test_quiz($this->course); 50 51 $accessmanager = $this->get_access_manager(); 52 53 $this->assertFalse($accessmanager->seb_required()); 54 55 $reflection = new \ReflectionClass('\quizaccess_seb\access_manager'); 56 $property = $reflection->getProperty('quizsettings'); 57 $property->setAccessible(true); 58 59 $this->assertFalse($property->getValue($accessmanager)); 60 } 61 62 /** 63 * Test that SEB is not required. 64 */ 65 public function test_seb_required_false() { 66 $this->quiz = $this->create_test_quiz($this->course); 67 68 $accessmanager = $this->get_access_manager(); 69 $this->assertFalse($accessmanager->seb_required()); 70 } 71 72 /** 73 * Test that SEB is required. 74 */ 75 public function test_seb_required_true() { 76 $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY); 77 78 $accessmanager = $this->get_access_manager(); 79 $this->assertTrue($accessmanager->seb_required()); 80 } 81 82 /** 83 * Test that user has capability to bypass SEB check. 84 */ 85 public function test_user_can_bypass_seb_check() { 86 $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY); 87 88 $user = $this->getDataGenerator()->create_user(); 89 $this->setUser($user); 90 91 // Set the bypass SEB check capability to $USER. 92 $this->assign_user_capability('quizaccess/seb:bypassseb', \context_module::instance($this->quiz->cmid)->id); 93 94 $accessmanager = $this->get_access_manager(); 95 $this->assertTrue($accessmanager->can_bypass_seb()); 96 } 97 98 /** 99 * Test user does not have capability to bypass SEB check. 100 */ 101 public function test_user_cannot_bypass_seb_check() { 102 $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY); 103 104 $user = $this->getDataGenerator()->create_user(); 105 $this->setUser($user); 106 107 $accessmanager = $this->get_access_manager(); 108 $this->assertFalse($accessmanager->can_bypass_seb()); 109 } 110 111 /** 112 * Test we can detect SEB usage. 113 */ 114 public function test_is_using_seb() { 115 $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY); 116 117 $accessmanager = $this->get_access_manager(); 118 119 $this->assertFalse($accessmanager->is_using_seb()); 120 121 $_SERVER['HTTP_USER_AGENT'] = 'Test'; 122 $this->assertFalse($accessmanager->is_using_seb()); 123 124 $_SERVER['HTTP_USER_AGENT'] = 'SEB'; 125 $this->assertTrue($accessmanager->is_using_seb()); 126 } 127 128 /** 129 * Test that the quiz Config Key matches the incoming request header. 130 */ 131 public function test_access_keys_validate_with_config_key() { 132 global $FULLME; 133 $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY); 134 135 $accessmanager = $this->get_access_manager(); 136 137 $configkey = quiz_settings::get_record(['quizid' => $this->quiz->id])->get_config_key(); 138 139 // Set up dummy request. 140 $FULLME = 'https://example.com/moodle/mod/quiz/attempt.php?attemptid=123&page=4'; 141 $expectedhash = hash('sha256', $FULLME . $configkey); 142 $_SERVER['HTTP_X_SAFEEXAMBROWSER_CONFIGKEYHASH'] = $expectedhash; 143 144 $this->assertTrue($accessmanager->validate_browser_exam_keys()); 145 $this->assertTrue($accessmanager->validate_config_key()); 146 } 147 148 /** 149 * Test that the quiz Config Key does not match the incoming request header. 150 */ 151 public function test_access_keys_fail_to_validate_with_config_key() { 152 $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY); 153 $accessmanager = $this->get_access_manager(); 154 155 $this->assertFalse($accessmanager->validate_config_key()); 156 $this->assertTrue($accessmanager->validate_browser_exam_keys()); 157 } 158 159 /** 160 * Test that config key is not checked when using client configuration with SEB. 161 */ 162 public function test_config_key_not_checked_if_client_requirement_is_selected() { 163 $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CLIENT_CONFIG); 164 $accessmanager = $this->get_access_manager(); 165 $this->assertTrue($accessmanager->validate_config_key()); 166 $this->assertTrue($accessmanager->validate_browser_exam_keys()); 167 } 168 169 /** 170 * Test that if there are no browser exam keys for quiz, check is skipped. 171 */ 172 public function test_no_browser_exam_keys_cause_check_to_be_skipped() { 173 $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CLIENT_CONFIG); 174 175 $settings = quiz_settings::get_record(['quizid' => $this->quiz->id]); 176 $settings->set('allowedbrowserexamkeys', ''); 177 $settings->save(); 178 $accessmanager = $this->get_access_manager(); 179 $this->assertTrue($accessmanager->validate_config_key()); 180 $this->assertTrue($accessmanager->validate_browser_exam_keys()); 181 } 182 183 /** 184 * Test that access fails if there is no hash in header. 185 */ 186 public function test_access_keys_fail_if_browser_exam_key_header_does_not_exist() { 187 $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CLIENT_CONFIG); 188 189 $settings = quiz_settings::get_record(['quizid' => $this->quiz->id]); 190 $settings->set('allowedbrowserexamkeys', hash('sha256', 'one') . "\n" . hash('sha256', 'two')); 191 $settings->save(); 192 $accessmanager = $this->get_access_manager(); 193 $this->assertTrue($accessmanager->validate_config_key()); 194 $this->assertFalse($accessmanager->validate_browser_exam_keys()); 195 } 196 197 /** 198 * Test that access fails if browser exam key doesn't match hash in header. 199 */ 200 public function test_access_keys_fail_if_browser_exam_key_header_does_not_match_provided_hash() { 201 $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CLIENT_CONFIG); 202 203 $settings = quiz_settings::get_record(['quizid' => $this->quiz->id]); 204 $settings->set('allowedbrowserexamkeys', hash('sha256', 'one') . "\n" . hash('sha256', 'two')); 205 $settings->save(); 206 $accessmanager = $this->get_access_manager(); 207 $_SERVER['HTTP_X_SAFEEXAMBROWSER_REQUESTHASH'] = hash('sha256', 'notwhatyouwereexpectinghuh'); 208 $this->assertTrue($accessmanager->validate_config_key()); 209 $this->assertFalse($accessmanager->validate_browser_exam_keys()); 210 } 211 212 /** 213 * Test that browser exam key matches hash in header. 214 */ 215 public function test_browser_exam_keys_match_header_hash() { 216 global $FULLME; 217 218 $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CLIENT_CONFIG); 219 $settings = quiz_settings::get_record(['quizid' => $this->quiz->id]); 220 $browserexamkey = hash('sha256', 'browserexamkey'); 221 $settings->set('allowedbrowserexamkeys', $browserexamkey); // Add a hashed BEK. 222 $settings->save(); 223 $accessmanager = $this->get_access_manager(); 224 225 // Set up dummy request. 226 $FULLME = 'https://example.com/moodle/mod/quiz/attempt.php?attemptid=123&page=4'; 227 $expectedhash = hash('sha256', $FULLME . $browserexamkey); 228 $_SERVER['HTTP_X_SAFEEXAMBROWSER_REQUESTHASH'] = $expectedhash; 229 $this->assertTrue($accessmanager->validate_config_key()); 230 $this->assertTrue($accessmanager->validate_browser_exam_keys()); 231 } 232 233 /** 234 * Test can get received config key. 235 */ 236 public function test_get_received_config_key() { 237 $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CLIENT_CONFIG); 238 $accessmanager = $this->get_access_manager(); 239 240 $this->assertNull($accessmanager->get_received_config_key()); 241 242 $_SERVER['HTTP_X_SAFEEXAMBROWSER_CONFIGKEYHASH'] = 'Test key'; 243 $this->assertEquals('Test key', $accessmanager->get_received_config_key()); 244 } 245 246 /** 247 * Test can get received browser key. 248 */ 249 public function get_received_browser_exam_key() { 250 $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CLIENT_CONFIG); 251 $accessmanager = $this->get_access_manager(); 252 253 $this->assertNull($accessmanager->get_received_browser_exam_key()); 254 255 $_SERVER['HTTP_X_SAFEEXAMBROWSER_REQUESTHASH'] = 'Test browser key'; 256 $this->assertEquals('Test browser key', $accessmanager->get_received_browser_exam_key()); 257 } 258 259 /** 260 * Test can correctly get type of SEB usage for the quiz. 261 */ 262 public function test_get_seb_use_type() { 263 // No SEB. 264 $this->quiz = $this->create_test_quiz($this->course); 265 $accessmanager = $this->get_access_manager(); 266 $this->assertEquals(settings_provider::USE_SEB_NO, $accessmanager->get_seb_use_type()); 267 268 // Manually. 269 $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY); 270 $accessmanager = $this->get_access_manager(); 271 $this->assertEquals(settings_provider::USE_SEB_CONFIG_MANUALLY, $accessmanager->get_seb_use_type()); 272 273 // Use template. 274 $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY); 275 $quizsettings = quiz_settings::get_record(['quizid' => $this->quiz->id]); 276 $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_TEMPLATE); 277 $quizsettings->set('templateid', $this->create_template()->get('id')); 278 $quizsettings->save(); 279 $accessmanager = $this->get_access_manager(); 280 $this->assertEquals(settings_provider::USE_SEB_TEMPLATE, $accessmanager->get_seb_use_type()); 281 282 // Use uploaded config. 283 $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY); 284 $quizsettings = quiz_settings::get_record(['quizid' => $this->quiz->id]); 285 $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_UPLOAD_CONFIG); // Doesn't check basic header. 286 $xml = file_get_contents(__DIR__ . '/fixtures/unencrypted.seb'); 287 $this->create_module_test_file($xml, $this->quiz->cmid); 288 $quizsettings->save(); 289 $accessmanager = $this->get_access_manager(); 290 $this->assertEquals(settings_provider::USE_SEB_UPLOAD_CONFIG, $accessmanager->get_seb_use_type()); 291 292 // Use client config. 293 $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CLIENT_CONFIG); 294 $accessmanager = $this->get_access_manager(); 295 $this->assertEquals(settings_provider::USE_SEB_CLIENT_CONFIG, $accessmanager->get_seb_use_type()); 296 } 297 298 /** 299 * Data provider for self::test_should_validate_basic_header. 300 * 301 * @return array 302 */ 303 public function should_validate_basic_header_data_provider() { 304 return [ 305 [settings_provider::USE_SEB_NO, false], 306 [settings_provider::USE_SEB_CONFIG_MANUALLY, false], 307 [settings_provider::USE_SEB_TEMPLATE, false], 308 [settings_provider::USE_SEB_UPLOAD_CONFIG, false], 309 [settings_provider::USE_SEB_CLIENT_CONFIG, true], 310 ]; 311 } 312 313 /** 314 * Test we know when we should validate basic header. 315 * 316 * @param int $type Type of SEB usage. 317 * @param bool $expected Expected result. 318 * 319 * @dataProvider should_validate_basic_header_data_provider 320 */ 321 public function test_should_validate_basic_header($type, $expected) { 322 $accessmanager = $this->getMockBuilder(access_manager::class) 323 ->disableOriginalConstructor() 324 ->onlyMethods(['get_seb_use_type']) 325 ->getMock(); 326 $accessmanager->method('get_seb_use_type')->willReturn($type); 327 328 $this->assertEquals($expected, $accessmanager->should_validate_basic_header()); 329 330 } 331 332 /** 333 * Data provider for self::test_should_validate_config_key. 334 * 335 * @return array 336 */ 337 public function should_validate_config_key_data_provider() { 338 return [ 339 [settings_provider::USE_SEB_NO, false], 340 [settings_provider::USE_SEB_CONFIG_MANUALLY, true], 341 [settings_provider::USE_SEB_TEMPLATE, true], 342 [settings_provider::USE_SEB_UPLOAD_CONFIG, true], 343 [settings_provider::USE_SEB_CLIENT_CONFIG, false], 344 ]; 345 } 346 347 /** 348 * Test we know when we should validate config key. 349 * 350 * @param int $type Type of SEB usage. 351 * @param bool $expected Expected result. 352 * 353 * @dataProvider should_validate_config_key_data_provider 354 */ 355 public function test_should_validate_config_key($type, $expected) { 356 $accessmanager = $this->getMockBuilder(access_manager::class) 357 ->disableOriginalConstructor() 358 ->onlyMethods(['get_seb_use_type']) 359 ->getMock(); 360 $accessmanager->method('get_seb_use_type')->willReturn($type); 361 362 $this->assertEquals($expected, $accessmanager->should_validate_config_key()); 363 } 364 365 /** 366 * Data provider for self::test_should_validate_browser_exam_key. 367 * 368 * @return array 369 */ 370 public function should_validate_browser_exam_key_data_provider() { 371 return [ 372 [settings_provider::USE_SEB_NO, false], 373 [settings_provider::USE_SEB_CONFIG_MANUALLY, false], 374 [settings_provider::USE_SEB_TEMPLATE, false], 375 [settings_provider::USE_SEB_UPLOAD_CONFIG, true], 376 [settings_provider::USE_SEB_CLIENT_CONFIG, true], 377 ]; 378 } 379 380 /** 381 * Test we know when we should browser exam key. 382 * 383 * @param int $type Type of SEB usage. 384 * @param bool $expected Expected result. 385 * 386 * @dataProvider should_validate_browser_exam_key_data_provider 387 */ 388 public function test_should_validate_browser_exam_key($type, $expected) { 389 $accessmanager = $this->getMockBuilder(access_manager::class) 390 ->disableOriginalConstructor() 391 ->onlyMethods(['get_seb_use_type']) 392 ->getMock(); 393 $accessmanager->method('get_seb_use_type')->willReturn($type); 394 395 $this->assertEquals($expected, $accessmanager->should_validate_browser_exam_key()); 396 } 397 398 /** 399 * Test that access manager uses cached Config Key. 400 */ 401 public function test_access_manager_uses_cached_config_key() { 402 global $FULLME; 403 $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY); 404 405 $accessmanager = $this->get_access_manager(); 406 407 $configkey = $accessmanager->get_valid_config_key(); 408 409 // Set up dummy request. 410 $FULLME = 'https://example.com/moodle/mod/quiz/attempt.php?attemptid=123&page=4'; 411 $expectedhash = hash('sha256', $FULLME . $configkey); 412 $_SERVER['HTTP_X_SAFEEXAMBROWSER_CONFIGKEYHASH'] = $expectedhash; 413 414 $this->assertTrue($accessmanager->validate_config_key()); 415 416 // Change settings (but don't save) and check that still can validate config key. 417 $quizsettings = quiz_settings::get_record(['quizid' => $this->quiz->id]); 418 $quizsettings->set('showsebtaskbar', 0); 419 $this->assertNotEquals($quizsettings->get_config_key(), $configkey); 420 $this->assertTrue($accessmanager->validate_config_key()); 421 422 // Now save settings which should purge caches but access manager still has config key. 423 $quizsettings->save(); 424 $this->assertNotEquals($quizsettings->get_config_key(), $configkey); 425 $this->assertTrue($accessmanager->validate_config_key()); 426 427 // Initialise a new access manager. Now validation should fail. 428 $accessmanager = $this->get_access_manager(); 429 $this->assertFalse($accessmanager->validate_config_key()); 430 } 431 432 /** 433 * Check that valid SEB config key is null if quiz doesn't have SEB settings. 434 */ 435 public function test_valid_config_key_is_null_if_no_settings() { 436 $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_NO); 437 $accessmanager = $this->get_access_manager(); 438 439 $this->assertEmpty(quiz_settings::get_record(['quizid' => $this->quiz->id])); 440 $this->assertNull($accessmanager->get_valid_config_key()); 441 442 } 443 444 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body