diff --git a/bootstrap.php b/bootstrap.php index 847a1d7..c95cfba 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -17,7 +17,7 @@ * This file should run before config.php requires '/lib/setup.php'. * * Main purpose of this file: - * 1) Allow to 'file.php' to know where dataroot is located and serve files during a maintenance. + * 1) Create a hook allowing other scripts to run before Moodle loads, but after the $CFG is defined. * 2) Allow to 'pretend' maintenance mode for non-allowed IPs by calling 'climaintenance.php'. * 3) Set a flag that this file was loaded so we can warn users if this config is not working. * @@ -34,24 +34,14 @@ if (defined('CLI_SCRIPT') && CLI_SCRIPT) { return; } -// 1) Are we fetching a file? Non-allowed IPs can still see them. -if (defined('AUTH_OUTAGE_FILE')) { - // We are not using any external libraries or references in this file (cli maintenance is active). - // If you change the path below maybe you need to change maintenance_static_page::get_resources_folder() as well. - $resourcedir = $CFG->dataroot.'/auth_outage/climaintenance'; +// We need the CFG->dataroot, if not set yet this script is called too early in config.php file. +if (!isset($CFG->dataroot)) { + return; +} - // Protect against path traversal attacks. - $file = $resourcedir.'/'.$_GET['file']; - if (realpath($file) !== $file) { - // @codingStandardsIgnoreStart - error_log('Invalid file: '.$_GET['file']); - // @codingStandardsIgnoreEnd - http_response_code(404); - die('Not found.'); - } - - readfile($file); - exit(0); +// 1) Check and run the hook. +if (isset($auth_outage_callback) && is_callable($auth_outage_callback)) { + $auth_outage_bootstrap_callback(); } // 2) Check for allowed IPs during outages. @@ -61,4 +51,4 @@ if (file_exists($CFG->dataroot.'/climaintenance.php')) { } // 3) Set flag this file was loaded. -$CFG->auth_outage_check = 2; +$CFG->auth_outage_check = 1; diff --git a/file.php b/file.php index 09d2d45..b12ec9e 100644 --- a/file.php +++ b/file.php @@ -43,7 +43,27 @@ header('Pragma: '); header('Cache-Control: public, max-age='.$lifetime); header('Accept-Ranges: none'); -define('AUTH_OUTAGE_FILE', $_GET['file']); +$auth_outage_bootstrap_callback = function () { + global $CFG; + + // We are not using any external libraries or references in this file (cli maintenance is active). + // If you change the path below maybe you need to change maintenance_static_page::get_resources_folder() as well. + $resourcedir = $CFG->dataroot.'/auth_outage/climaintenance'; + + // Protect against path traversal attacks. + $file = $resourcedir.'/'.$_GET['file']; + if (realpath($file) !== $file) { + // @codingStandardsIgnoreStart + error_log('Invalid file: '.$_GET['file']); + // @codingStandardsIgnoreEnd + http_response_code(404); + die('Not found.'); + } + + readfile($file); + exit(0); +}; + require_once(__DIR__.'/../../config.php'); // We should never reach here if config.php and auth/outage/bootstrap.php intercepted it correctly. diff --git a/settings.php b/settings.php index 0069fdf..239dca2 100644 --- a/settings.php +++ b/settings.php @@ -21,6 +21,11 @@ * @author Daniel Thee Roperto * @copyright 2016 Catalyst IT * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * + * @var stdClass $CFG + * @var admin_settingpage $settings + * @var bootstrap_renderer $OUTPUT + * @var admin_root $ADMIN */ use auth_outage\local\outagelib; @@ -86,7 +91,7 @@ if ($hassiteconfig && is_enabled_auth('outage')) { $allowedips = outagelib::get_config()->allowedips; $description = ''; - if (!isset($CFG->auth_outage_check) || ($CFG->auth_outage_check != 2)) { + if (!isset($CFG->auth_outage_check) || !$CFG->auth_outage_check) { $description .= $OUTPUT->notification(get_string('allowedipsnoconfig', 'auth_outage'), 'notifyfailure'); } diff --git a/tests/phpunit/local/controllers/maintenance_static_page_test.php b/tests/phpunit/local/controllers/maintenance_static_page_test.php index 4fa27e3..d334b38 100644 --- a/tests/phpunit/local/controllers/maintenance_static_page_test.php +++ b/tests/phpunit/local/controllers/maintenance_static_page_test.php @@ -83,7 +83,7 @@ class maintenance_static_page_test extends auth_outage_base_testcase { 'Content'; $generated = $this->generated_page_html($html); - self::assertContains('http://www.example.com/moodle/auth/outage/maintenance.php?file=', $generated); + self::assertContains('http://www.example.com/moodle/auth/outage/file.php?file=', $generated); self::assertNotContains($link1, $generated); self::assertNotContains($link2, $generated); self::assertContains($link3, $generated); @@ -98,7 +98,7 @@ class maintenance_static_page_test extends auth_outage_base_testcase { 'Content'; $generated = $this->generated_page_html($html); - self::assertContains('http://www.example.com/moodle/auth/outage/maintenance.php?file=', $generated); + self::assertContains('http://www.example.com/moodle/auth/outage/file.php?file=', $generated); self::assertNotContains($link1, $generated); self::assertNotContains($link2, $generated); self::assertContains($link3, $generated); @@ -112,7 +112,7 @@ class maintenance_static_page_test extends auth_outage_base_testcase { $generated = $this->generated_page_html($html); self::assertNotContains($link, $generated); - self::assertContains('http://www.example.com/moodle/auth/outage/maintenance.php?file=', $generated); + self::assertContains('http://www.example.com/moodle/auth/outage/file.php?file=', $generated); } public function test_previewpath() { @@ -126,7 +126,7 @@ class maintenance_static_page_test extends auth_outage_base_testcase { $generated = trim(file_get_contents($page->get_template_file())); self::assertNotContains($link, $generated); - self::assertContains('http://www.example.com/moodle/auth/outage/maintenance.php?file=preview%2F', $generated); + self::assertContains('http://www.example.com/moodle/auth/outage/file.php?file=preview%2F', $generated); } /** diff --git a/tests/phpunit/local/outagelib_test.php b/tests/phpunit/local/outagelib_test.php index c39e6db..afb1223 100644 --- a/tests/phpunit/local/outagelib_test.php +++ b/tests/phpunit/local/outagelib_test.php @@ -283,34 +283,31 @@ class outagelib_test extends advanced_testcase { $expected = <<<'EOT' = 123) { - if (!defined('CLI_SCRIPT') || !CLI_SCRIPT) { - define('MOODLE_INTERNAL', true); - require_once($CFG->dirroot.'/lib/moodlelib.php'); - if (!remoteip_in_list('heyyou + define('MOODLE_INTERNAL', true); + require_once($CFG->dirroot.'/lib/moodlelib.php'); + if (!remoteip_in_list('heyyou a.b.c.d e.e.e.e/20')) { - header($_SERVER['SERVER_PROTOCOL'] . ' 503 Moodle under maintenance'); - header('Status: 503 Moodle under maintenance'); - header('Retry-After: 300'); - header('Content-type: text/html; charset=utf-8'); - header('X-UA-Compatible: IE=edge'); - header('Cache-Control: no-store, no-cache, must-revalidate'); - header('Cache-Control: post-check=0, pre-check=0', false); - header('Pragma: no-cache'); - header('Expires: Mon, 20 Aug 1969 09:23:00 GMT'); - header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); - header('Accept-Ranges: none'); - echo ''; - if (file_exists($CFG->dataroot.'/climaintenance.template.html')) { - require($CFG->dataroot.'/climaintenance.template.html'); - exit(0); - } - // The file above should always exist, but just in case... - die('We are currently under maintentance, please try again later.'); + header($_SERVER['SERVER_PROTOCOL'] . ' 503 Moodle under maintenance'); + header('Status: 503 Moodle under maintenance'); + header('Retry-After: 300'); + header('Content-type: text/html; charset=utf-8'); + header('X-UA-Compatible: IE=edge'); + header('Cache-Control: no-store, no-cache, must-revalidate'); + header('Cache-Control: post-check=0, pre-check=0', false); + header('Pragma: no-cache'); + header('Expires: Mon, 20 Aug 1969 09:23:00 GMT'); + header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); + header('Accept-Ranges: none'); + echo ''; + if (file_exists($CFG->dataroot.'/climaintenance.template.html')) { + require($CFG->dataroot.'/climaintenance.template.html'); + exit(0); } + // The file above should always exist, but just in case... + die('We are currently under maintentance, please try again later.'); } } -$CFG->auth_outage_check = 1; EOT; $found = outagelib::create_climaintenancephp_code(123, 456, "hey'\"you\na.b.c.d\ne.e.e.e/20"); self::assertSame($expected, $found); @@ -323,32 +320,29 @@ EOT; $expected = <<<'EOT' = 123) { - if (!defined('CLI_SCRIPT') || !CLI_SCRIPT) { - define('MOODLE_INTERNAL', true); - require_once($CFG->dirroot.'/lib/moodlelib.php'); - if (!remoteip_in_list('127.0.0.1')) { - header($_SERVER['SERVER_PROTOCOL'] . ' 503 Moodle under maintenance'); - header('Status: 503 Moodle under maintenance'); - header('Retry-After: 300'); - header('Content-type: text/html; charset=utf-8'); - header('X-UA-Compatible: IE=edge'); - header('Cache-Control: no-store, no-cache, must-revalidate'); - header('Cache-Control: post-check=0, pre-check=0', false); - header('Pragma: no-cache'); - header('Expires: Mon, 20 Aug 1969 09:23:00 GMT'); - header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); - header('Accept-Ranges: none'); - echo ''; - if (file_exists($CFG->dataroot.'/climaintenance.template.html')) { - require($CFG->dataroot.'/climaintenance.template.html'); - exit(0); - } - // The file above should always exist, but just in case... - die('We are currently under maintentance, please try again later.'); + define('MOODLE_INTERNAL', true); + require_once($CFG->dirroot.'/lib/moodlelib.php'); + if (!remoteip_in_list('127.0.0.1')) { + header($_SERVER['SERVER_PROTOCOL'] . ' 503 Moodle under maintenance'); + header('Status: 503 Moodle under maintenance'); + header('Retry-After: 300'); + header('Content-type: text/html; charset=utf-8'); + header('X-UA-Compatible: IE=edge'); + header('Cache-Control: no-store, no-cache, must-revalidate'); + header('Cache-Control: post-check=0, pre-check=0', false); + header('Pragma: no-cache'); + header('Expires: Mon, 20 Aug 1969 09:23:00 GMT'); + header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); + header('Accept-Ranges: none'); + echo ''; + if (file_exists($CFG->dataroot.'/climaintenance.template.html')) { + require($CFG->dataroot.'/climaintenance.template.html'); + exit(0); } + // The file above should always exist, but just in case... + die('We are currently under maintentance, please try again later.'); } } -$CFG->auth_outage_check = 1; EOT; $outage = new outage([ 'starttime' => 123,