From 5db5bed9140650dd4b81069d7006e5d1dcb8a8ba Mon Sep 17 00:00:00 2001 From: Daniel Thee Roperto Date: Wed, 9 Nov 2016 16:56:04 +1100 Subject: [PATCH] Issue #22 - Created bootstrap.php to intercept the execution before Moodle finished loading. --- README.md | 7 +- bootstrap.php | 64 +++++++++++++++++++ .../controllers/maintenance_static_page.php | 2 +- classes/local/outagelib.php | 43 ++++++------- file.php | 31 +++------ settings.php | 2 +- 6 files changed, 98 insertions(+), 51 deletions(-) create mode 100644 bootstrap.php diff --git a/README.md b/README.md index 9463dae..274c884 100644 --- a/README.md +++ b/README.md @@ -81,11 +81,8 @@ enable the `Outage manager` plugin and place it on the top. after your `$CFG->dataroot` is set: ``` -if (file_exists("$CFG->dataroot/climaintenance.php")) { - $CFG->dirroot = __DIR__; - require("$CFG->dataroot/climaintenance.php"); -} else { - $CFG->auth_outage_check = 1; +if (file_exists(__DIR__.'/auth/outage/bootstrap.php')) { + require(__DIR__.'/auth/outage/bootstrap.php'); } ``` diff --git a/bootstrap.php b/bootstrap.php new file mode 100644 index 0000000..847a1d7 --- /dev/null +++ b/bootstrap.php @@ -0,0 +1,64 @@ +. +/** + * 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. + * 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. + * + * @package auth_outage + * @author Daniel Thee Roperto + * @copyright 2016 Catalyst IT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * + * @var stdClass $CFG + */ + +// This file does nothing if running from CLI. +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'; + + // 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); +} + +// 2) Check for allowed IPs during outages. +if (file_exists($CFG->dataroot.'/climaintenance.php')) { + $CFG->dirroot = dirname(dirname(dirname(__FILE__))); // It is not defined yet but the script below needs it. + require($CFG->dataroot.'/climaintenance.php'); // This call may terminate the script here or not. +} + +// 3) Set flag this file was loaded. +$CFG->auth_outage_check = 2; diff --git a/classes/local/controllers/maintenance_static_page.php b/classes/local/controllers/maintenance_static_page.php index cb4a406..c342dac 100644 --- a/classes/local/controllers/maintenance_static_page.php +++ b/classes/local/controllers/maintenance_static_page.php @@ -133,7 +133,7 @@ class maintenance_static_page { public function get_resources_folder() { global $CFG; - // If you change the path, also change file auth/outage/file.php as it does not use this reference. + // If you change the path, also change file auth/outage/bootstrap.php as it does not use this reference. $dir = $CFG->dataroot.'/auth_outage/climaintenance'; if ($this->preview) { diff --git a/classes/local/outagelib.php b/classes/local/outagelib.php index 8966744..6c96c69 100644 --- a/classes/local/outagelib.php +++ b/classes/local/outagelib.php @@ -224,32 +224,29 @@ class outagelib { $code = <<<'EOT' = {{STARTTIME}}) { - if (!defined('CLI_SCRIPT') || !CLI_SCRIPT) { - define('MOODLE_INTERNAL', true); - require_once($CFG->dirroot.'/lib/moodlelib.php'); - if (!remoteip_in_list('{{ALLOWEDIPS}}')) { - 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('{{ALLOWEDIPS}}')) { + 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; $search = ['{{STARTTIME}}', '{{ALLOWEDIPS}}', '{{YOURIP}}']; $replace = [$starttime, $allowedips, getremoteaddr('n/a')]; diff --git a/file.php b/file.php index 2f6e215..09d2d45 100644 --- a/file.php +++ b/file.php @@ -27,31 +27,14 @@ * @var stdClass $CFG */ -if (!isset($_GET['file'])) { +// File should have at least 3 characters as we will check the extension below. +if (!isset($_GET['file']) || (strlen($_GET['file']) < 3)) { http_response_code(400); die('Missing file parameter.'); } -define('NO_DEBUG_DISPLAY', true); -define('ABORT_AFTER_CONFIG', true); -require_once(__DIR__.'/../../config.php'); - -// 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.'); -} - // Detect type, we only support css or PNG images. -header('Content-Type: '.(substr($file, -3) == 'css' ? 'text/css' : 'image/png')); +header('Content-Type: '.(substr($_GET['file'], -3) == 'css' ? 'text/css' : 'image/png')); // Use cache. $lifetime = 60 * 60 * 24; // 1 day. @@ -60,4 +43,10 @@ header('Pragma: '); header('Cache-Control: public, max-age='.$lifetime); header('Accept-Ranges: none'); -readfile($file); +define('AUTH_OUTAGE_FILE', $_GET['file']); +require_once(__DIR__.'/../../config.php'); + +// We should never reach here if config.php and auth/outage/bootstrap.php intercepted it correctly. +debugging('Your config.php is not properly configured for auth/outage plugin. '. + 'Please check the plugin settings for information.'); +exit(1); \ No newline at end of file diff --git a/settings.php b/settings.php index 7ae864c..0069fdf 100644 --- a/settings.php +++ b/settings.php @@ -86,7 +86,7 @@ if ($hassiteconfig && is_enabled_auth('outage')) { $allowedips = outagelib::get_config()->allowedips; $description = ''; - if (!isset($CFG->auth_outage_check) || !$CFG->auth_outage_check) { + if (!isset($CFG->auth_outage_check) || ($CFG->auth_outage_check != 2)) { $description .= $OUTPUT->notification(get_string('allowedipsnoconfig', 'auth_outage'), 'notifyfailure'); }