diff options
47 files changed, 360 insertions, 201 deletions
@@ -53,14 +53,25 @@ if (!empty($_REQUEST['do'])) { Module::init(); if (Module::isAvailable($module)) { - $module = 'modules/' . $module . '/api.inc.php'; + $moduleFile = 'modules/' . $module . '/api.inc.php'; } else { - $module = 'apis/' . $module . '.inc.php'; + $moduleFile = 'apis/' . $module . '.inc.php'; } -if (!file_exists($module)) { +if (!file_exists($moduleFile)) { ErrorHandler::traceError('Invalid module, or module without API: ' . $module); } + +// Auditing - log any post requests, but mask potential password fields. +// This REQUIRES naming those form fields accordingly +if (isLocalExecution() || ($_SERVER['REQUEST_METHOD'] ?? 'POST') === 'POST') { + if ($module !== 'clientlog' && $module !== 'cb' && $module !== 'cron' + && $module !== 'remoteaccess' && $module !== 'taskmanager') { + User::load(); + Audit::run($module); + } +} + if (php_sapi_name() === 'cli') { register_shutdown_function(function() { if (class_exists('Message', false)) { @@ -79,6 +90,6 @@ if (php_sapi_name() === 'cli') { ob_start('ob_gzhandler'); } // Load module - it will execute pre-processing, or act upon request parameters -require_once($module); +require_once($moduleFile); diff --git a/inc/audit.inc.php b/inc/audit.inc.php index b3d9bbc7..443b5ca6 100644 --- a/inc/audit.inc.php +++ b/inc/audit.inc.php @@ -1 +1,97 @@ <?php + +class Audit +{ + + /** @var ?int */ + private static $overrideResponseCode = null; + + /** + * Logs the current POST parameters to the audit table, applying filtering to sensistive data. + * + * @param string $module The name of the module being executed. + */ + public static function run(string $module): void + { + $maxTotalLen = User::isLoggedIn() ? 1000000 : 10000; + $hadLongString = true; + $filtered = null; + for ($maxlen = 1000; $hadLongString && $maxlen > 100; $maxlen -= 100) { + $hadLongString = false; + $filtered = self::processPostArray($_POST, $maxlen, $hadLongString); + $filtered = json_encode($filtered); + if (strlen($filtered) < $maxTotalLen) + break; + $filtered = null; + } + $data = [ + 'dateline' => time(), + 'userid' => User::getId(), + 'ipaddr' => $_SERVER['REMOTE_ADDR'] ?? 'cli', + 'module' => $module, + 'action' => $_REQUEST['action'] ?? '', + 'data' => $filtered ?? 'EXCESS', + ]; + $ret = Database::exec('INSERT IGNORE INTO audit (dateline, userid, ipaddr, module, action, data) + VALUES (:dateline, :userid, :ipaddr, :module, :action, :data)', $data, true); + if ($ret) { + $rowId = Database::lastInsertId(); + register_shutdown_function(function() use ($rowId) { + $code = self::$overrideResponseCode ?? http_response_code(); + if ($code) { + Database::exec("UPDATE IGNORE audit SET response = :code WHERE id = :id", + ['id' => $rowId, 'code' => $code], true); + } + }); + } + if ($filtered === null) + ErrorHandler::traceError('POST payload exceeded limit'); + } + + + /** + * Process the provided (POST)array recursively, applying filters and truncation as needed. + * + * @param array $array The array to process + * @param int $maxlen The maximum length allowed for strings + * @param bool &$hadLongString A reference variable to track if a long string was encountered + * @return array The processed array with filtered and shortened values + */ + private static function processPostArray(array $array, int $maxlen, bool &$hadLongString): array + { + $filtered = []; + foreach ($array as $key => $value) { + if ($key === 'prevent_autofill' || $key === 'password_fake' + || $key === 'do' || $key === 'action' || $key === 'token') + continue; // These don't matter + if (strpos($key, 'pass') !== false || strpos($key, 'token') !== false + || substr($key, -3) === 'key' // privatekey, hmac-key, ... + || substr($key, 0, 2) === 'pw') { + $value = '*****'; // Censor + } elseif (is_array($value)) { + $value = self::processPostArray($value, $maxlen, $hadLongString); + } elseif (mb_strlen($value) > $maxlen) { + // Shorten + $hadLongString = true; + $value = mb_substr($value, 0, $maxlen) . '...'; + } + if (!empty($value)) { + $filtered[$key] = $value; + } + } + return $filtered; + } + + /** + * Sets a custom HTTP response code to be used for logging in the audit table. + * + * @param int $responseCode The custom HTTP response code to be set. + */ + public static function overrideResponseCode(int $responseCode, bool $replaceExisting = true): void + { + if ($replaceExisting || self::$overrideResponseCode === null) { + self::$overrideResponseCode = $responseCode; + } + } + +} diff --git a/inc/message.inc.php b/inc/message.inc.php index 2f565b9a..2db5bda9 100644 --- a/inc/message.inc.php +++ b/inc/message.inc.php @@ -39,7 +39,7 @@ class Message */ private static function add(string $type, string $id, array $params): void { - if (strstr($id, '.') === false) { + if (strpos($id, '.') === false) { $id = Page::getModule()->getIdentifier() . '.' . $id; } if (!empty($params) && $params[0] === true) { @@ -190,5 +190,19 @@ class Message return implode('&', $parts); } + /** + * Check if any of the given error messages exist in the error list. + * @param string[] $message The error messages to check for. + * @return bool True if the specified error message of type 'danger' exists in the error list, false otherwise. + */ + public static function hasError(string ...$message): bool + { + foreach (self::$list as $item) { + if (in_array($item['id'], $message, true) && $item['type'] === 'danger') + return true; + } + return false; + } + } diff --git a/inc/request.inc.php b/inc/request.inc.php index 54884475..3f1d96c6 100644 --- a/inc/request.inc.php +++ b/inc/request.inc.php @@ -74,7 +74,7 @@ class Request if ($default === self::REQUIRED_EMPTY || $default === self::REQUIRED) { if (!array_key_exists($key, $array)) { $err = 'missing'; - } elseif ($default === self::REQUIRED && $array[$key] === '') { + } elseif ($default === self::REQUIRED && ($array[$key] === '' || $array[$key] === [])) { $err = 'empty'; } } diff --git a/inc/user.inc.php b/inc/user.inc.php index 0bb345c3..cd35ac29 100644 --- a/inc/user.inc.php +++ b/inc/user.inc.php @@ -75,12 +75,14 @@ class User if (User::hasPermission($permission, $locationid)) return; if (AJAX) { + Message::addError('main.no-permission'); Message::renderList(); + Audit::overrideResponseCode(403); exit; } if (!is_null($redirect)) { Message::addError('main.no-permission'); - Util::redirect($redirect); + Util::redirect($redirect, 403); } elseif (Module::isAvailable('permissionmanager')) { if ($permission[0] !== '.') { $module = Page::getModule(); @@ -88,10 +90,10 @@ class User $permission = '.' . $module->getIdentifier() . '.' . $permission; } } - Util::redirect('?do=permissionmanager&show=denied&permission=' . urlencode($permission)); + Util::redirect('?do=permissionmanager&show=denied&permission=' . urlencode($permission), 403); } else { Message::addError('main.no-permission'); - Util::redirect('?do=main'); + Util::redirect('?do=main', 403); } } diff --git a/inc/util.inc.php b/inc/util.inc.php index 91232f46..003da9fa 100644 --- a/inc/util.inc.php +++ b/inc/util.inc.php @@ -14,11 +14,12 @@ class Util * been displayed yet will be appended to the redirect. * * @param string|false $location Location to redirect to. "false" to redirect to same URL (useful after POSTs) + * @param ?int $virtualResponseCode Which response code to treat this redirect as for audit purposes * @param bool $preferRedirectPost if true, use the value from $_POST['redirect'] instead of $location - * @param bool $ignoreRedirectParams if true, ignore any additional params set via ::addRedirectParam()) + * @param bool $ignoreRedirectParams if true, ignore any additional params set via ::addRedirectParam() */ #[NoReturn] - public static function redirect($location = false, bool $preferRedirectPost = false, bool $ignoreRedirectParams = false): void + public static function redirect($location = false, ?int $virtualResponseCode = null, bool $preferRedirectPost = false, bool $ignoreRedirectParams = false): void { if ($location === false) { $location = preg_replace('/([&?])message\[\]=[^&]*/', '\1', $_SERVER['REQUEST_URI']); @@ -50,6 +51,17 @@ class Util . Database::getQueryCount() . ' queries, ' . round(Database::getQueryTime(), 3) . 's query time total'); } + // For cosmetic reasons, allow overriding the HTTP response code we log to the audit table. + // This is only for logging, this doesn't get send to the client. + if ($virtualResponseCode !== null) { + Audit::overrideResponseCode($virtualResponseCode, false); + } elseif (Message::hasError('main.parameter-empty', 'main.parameter-missing', 'main.value-invalid', 'main.invalid-action')) { + Audit::overrideResponseCode(400, false); + } elseif (Message::hasError('main.no-permission')) { + Audit::overrideResponseCode(403, false); + } elseif (Message::hasError('main.error-read', 'main.error-write')) { + Audit::overrideResponseCode(500, false); + } Header('Location: ' . $location); exit(0); } @@ -178,6 +178,13 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { } } +// Auditing - log any post requests, but mask potential password fields. +// This REQUIRES naming those form fields accordingly +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $mod = Page::getModule(); + Audit::run($mod ? $mod->getIdentifier() : strtolower($_POST['do'] ?? '???')); +} + // AJAX Stuff? Just do so. Otherwise, run preprocessing if (AJAX) { ob_start('ob_gzhandler'); diff --git a/modules-available/backup/page.inc.php b/modules-available/backup/page.inc.php index 9a2d08be..44511367 100644 --- a/modules-available/backup/page.inc.php +++ b/modules-available/backup/page.inc.php @@ -70,11 +70,11 @@ class Page_Backup extends Page ]); if (!isset($task['id'])) { Message::addError('backup-failed'); - Util::redirect('?do=Backup'); + Util::redirect('?do=Backup', 500); } $task = Taskmanager::waitComplete($task, 60000); if (!Taskmanager::isFinished($task) || !isset($task['data']['backupFile'])) { - Util::redirect('?do=backup&errtaskid=' . $task['id']); + Util::redirect('?do=backup&errtaskid=' . $task['id'], 500); } while ((@ob_get_level()) > 0) @ob_end_clean(); @@ -110,11 +110,11 @@ class Page_Backup extends Page { if (!isset($_FILES['backupfile'])) { Message::addError('missing-file'); - Util::redirect('?do=Backup'); + Util::redirect('?do=Backup', 400); } if ($_FILES['backupfile']['error'] != UPLOAD_ERR_OK) { Message::addError('upload-failed', Util::uploadErrorString($_FILES['backupfile']['error'])); - Util::redirect('?do=Backup'); + Util::redirect('?do=Backup', 400); } $password = trim(Request::post('passwd', '', 'string')); if (empty($password)) { @@ -136,7 +136,7 @@ class Page_Backup extends Page @unlink($tempfile . '2'); if (Taskmanager::isFailed($task)) { @unlink($tempfile); - Util::redirect('?do=backup&errtaskid=' . $task['id']); + Util::redirect('?do=backup&errtaskid=' . $task['id'], 500); } // Got uploaded file, now shut down all the daemons etc. $parent = Trigger::stopDaemons(null, $this->templateData); @@ -196,7 +196,7 @@ class Page_Backup extends Page if ($mode !== BackupRestore::BACKUP_MODE_OFF && $mode !== BackupRestore::BACKUP_MODE_ROOTHOME && $mode !== BackupRestore::BACKUP_MODE_VMSTORE) { Message::addError('invalid-auto-backup-mode', $mode); - Util::redirect('?do=backup'); + Util::redirect('?do=backup', 400); } Property::set(BackupRestore::PROP_AUTO_BACKUP_MODE, $mode); Property::set(BackupRestore::PROP_AUTO_BACKUP_PASS, $password); diff --git a/modules-available/baseconfig/page.inc.php b/modules-available/baseconfig/page.inc.php index 5d684a8e..1b6e947d 100644 --- a/modules-available/baseconfig/page.inc.php +++ b/modules-available/baseconfig/page.inc.php @@ -88,11 +88,11 @@ class Page_BaseConfig extends Page } Message::addSuccess('settings-updated'); if ($this->targetModule === false) { - Util::redirect('?do=BaseConfig', true); + Util::redirect('?do=BaseConfig', null, true); } elseif (empty($this->qry_extra['field'])) { - Util::redirect('?do=BaseConfig&module=' . $this->targetModule, true); + Util::redirect('?do=BaseConfig&module=' . $this->targetModule, null, true); } else { - Util::redirect('?do=BaseConfig&module=' . $this->targetModule . '&' . $this->qry_extra['field'] . '=' . $this->qry_extra['field_value'], true); + Util::redirect('?do=BaseConfig&module=' . $this->targetModule . '&' . $this->qry_extra['field'] . '=' . $this->qry_extra['field_value'], null, true); } } // Load categories so we can define them as sub menu items @@ -241,17 +241,17 @@ class Page_BaseConfig extends Page //\\//\\//\\ if (!Module::isAvailable($module)) { Message::addError('main.no-such-module', $module); - Util::redirect('?do=baseconfig'); + Util::redirect('?do=baseconfig', 404); } $file = 'modules/' . $module . '/baseconfig/hook.json'; if (!file_exists($file)) { Message::addError('no-module-hook', $module); - Util::redirect('?do=baseconfig'); + Util::redirect('?do=baseconfig', 404); } $hook = json_decode(file_get_contents($file), true); if (empty($hook['table'])) { Message::addError('invalid-hook', $module); - Util::redirect('?do=baseconfig'); + Util::redirect('?do=baseconfig', 500); } if (isset($hook['field'])) { $hook['field_value'] = Request::any($hook['field'], '0', 'string'); diff --git a/modules-available/baseconfig_bwlp/baseconfig/settings.json b/modules-available/baseconfig_bwlp/baseconfig/settings.json index 6371eda5..a3b6c25f 100644 --- a/modules-available/baseconfig_bwlp/baseconfig/settings.json +++ b/modules-available/baseconfig_bwlp/baseconfig/settings.json @@ -244,7 +244,7 @@ "catid": "other", "defaultvalue": "", "permissions": "2", - "validator": "regex:/^(\\s*(\\d+x\\d+(@[0-9.]+)?)(\\s+\\d+x\\d+(@[0-9.]+)?)*\\s*|)$/i" + "validator": "regex:/^(\\s*((\\d+x\\d+)?(@[0-9.]+)?(:(left|right|inverted|normal))?)(\\s+(\\d+x\\d+)?(@[0-9.]+)?(:(left|right|inverted|normal))?)*\\s*|)$/i" }, "SLX_RESOLUTION_MAPPING": { "catid": "other", @@ -263,5 +263,11 @@ "defaultvalue": "ON", "permissions": "2", "validator": "list:ON|OFF" + }, + "SLX_MINIMAL_GUI": { + "catid": "sysconfig", + "defaultvalue": "OFF", + "permissions": "2", + "validator": "list:ON|OFF" } } diff --git a/modules-available/dnbd3/inc/dnbd3rpc.inc.php b/modules-available/dnbd3/inc/dnbd3rpc.inc.php index 69437304..de5f9c5b 100644 --- a/modules-available/dnbd3/inc/dnbd3rpc.inc.php +++ b/modules-available/dnbd3/inc/dnbd3rpc.inc.php @@ -52,7 +52,7 @@ class Dnbd3Rpc { return $ret; } - public static function getCacheMap($server, $imgId) + public static function getCacheMap($server, $imgId, ?int &$code) { $server = self::translateServer($server); $str = Download::asString('http://' . $server . '/cachemap?id=' . $imgId, 3, $code); diff --git a/modules-available/dnbd3/page.inc.php b/modules-available/dnbd3/page.inc.php index 06eb9b4f..b772c9cc 100644 --- a/modules-available/dnbd3/page.inc.php +++ b/modules-available/dnbd3/page.inc.php @@ -461,7 +461,7 @@ class Page_Dnbd3 extends Page die('Invalid server id: ' . $serverId); } Message::addError('server-non-existent', $serverId); - Util::redirect('?do=dnbd3'); + Util::redirect('?do=dnbd3', 404); } if (!is_null($server['fixedip'])) { $server['ip'] = $server['fixedip']; diff --git a/modules-available/dozmod/pages/actionlog.inc.php b/modules-available/dozmod/pages/actionlog.inc.php index 182198c2..1606f6c8 100644 --- a/modules-available/dozmod/pages/actionlog.inc.php +++ b/modules-available/dozmod/pages/actionlog.inc.php @@ -46,7 +46,7 @@ class SubPage LIMIT 1', array('uuid' => self::$uuid)); if ($user === false) { Message::addError('unknown-userid', self::$uuid); - Util::redirect('?do=dozmod§ion=actionlog'); + Util::redirect('?do=dozmod§ion=actionlog', 404); } // Mangle date and render $user['lastlogin_s'] = date('d.m.Y H:i', $user['lastlogin']); diff --git a/modules-available/dozmod/pages/networkrules.inc.php b/modules-available/dozmod/pages/networkrules.inc.php index 218b7b06..8b48ee91 100644 --- a/modules-available/dozmod/pages/networkrules.inc.php +++ b/modules-available/dozmod/pages/networkrules.inc.php @@ -90,7 +90,7 @@ class SubPage FROM sat.presetnetworkrule WHERE ruleid = :ruleid', ['ruleid' => $ruleid]); if ($data === false) { Message::addError('networkrule-invalid-ruleid', $ruleid); - Util::redirect('?do=dozmod§ion=networkrules'); + Util::redirect('?do=dozmod§ion=networkrules', 404); } $dec = json_decode($data['ruledata'], true); if (!is_array($dec) || !isset($dec[0])) { diff --git a/modules-available/dozmod/pages/networkshares.inc.php b/modules-available/dozmod/pages/networkshares.inc.php index 852a8c67..ad03d227 100644 --- a/modules-available/dozmod/pages/networkshares.inc.php +++ b/modules-available/dozmod/pages/networkshares.inc.php @@ -89,7 +89,7 @@ class SubPage FROM sat.presetnetworkshare WHERE shareid = :shareid', ['shareid' => $shareid]); if ($data === false) { Message::addError('networkshare-invalid-shareid', $shareid); - Util::redirect('?do=dozmod§ion=networkshares'); + Util::redirect('?do=dozmod§ion=networkshares', 404); } $dec = json_decode($data['sharedata'], true); if (is_array($dec)) { diff --git a/modules-available/dozmod/pages/runscripts.inc.php b/modules-available/dozmod/pages/runscripts.inc.php index 5665ba83..c469d416 100644 --- a/modules-available/dozmod/pages/runscripts.inc.php +++ b/modules-available/dozmod/pages/runscripts.inc.php @@ -131,7 +131,7 @@ class SubPage $row['isglobal_' . $row['isglobal'] . '_checked'] = 'checked'; if ($row === false) { Message::addError('runscript-invalid-id', $id); - Util::redirect('?do=dozmod§ion=runscripts'); + Util::redirect('?do=dozmod§ion=runscripts', 404); } } // Get OS diff --git a/modules-available/dozmod/pages/templates.inc.php b/modules-available/dozmod/pages/templates.inc.php index b916e14c..ee21af43 100644 --- a/modules-available/dozmod/pages/templates.inc.php +++ b/modules-available/dozmod/pages/templates.inc.php @@ -76,32 +76,28 @@ class SubPage private static function handleSave() { - $data = Request::post('templates'); - if (is_array($data)) { - self::fetchTemplates(); - foreach (self::$templates as &$template) { - if (isset($data[$template['name']])) { - if (self::forcmp($template['template']) !== self::forcmp($data[$template['name']]['template'])) { - if (empty($template['original_template'])) { - $template['original_template'] = $template['template']; - } - $template['edit_version'] = $template['version']; - } - $template['original'] = (empty($template['original_template']) && $template['original']) - || self::forcmp($template['original_template']) === self::forcmp($data[$template['name']]['template']); - if ($template['original']) { - $template['original_template'] = ''; + $data = Request::post('templates', Request::REQUIRED, 'array'); + self::fetchTemplates(); + foreach (self::$templates as &$template) { + if (isset($data[$template['name']])) { + if (self::forcmp($template['template']) !== self::forcmp($data[$template['name']]['template'])) { + if (empty($template['original_template'])) { + $template['original_template'] = $template['template']; } - $template['template'] = $data[$template['name']]['template']; + $template['edit_version'] = $template['version']; + } + $template['original'] = (empty($template['original_template']) && $template['original']) + || self::forcmp($template['original_template']) === self::forcmp($data[$template['name']]['template']); + if ($template['original']) { + $template['original_template'] = ''; } + $template['template'] = $data[$template['name']]['template']; } - unset($template); - $data = json_encode(array('templates' => self::$templates)); - Database::exec("UPDATE sat.configuration SET value = :value WHERE parameter = 'templates'", array('value' => $data)); - Message::addSuccess('templates-saved'); - } else { - Message::addError('nothing-submitted'); } + unset($template); + $data = json_encode(array('templates' => self::$templates)); + Database::exec("UPDATE sat.configuration SET value = :value WHERE parameter = 'templates'", array('value' => $data)); + Message::addSuccess('templates-saved'); Util::redirect('?do=dozmod§ion=templates'); } @@ -109,13 +105,14 @@ class SubPage { $result = Download::asStringPost('http://127.0.0.1:9080/do/reset-mail-templates', array(), 10, $code); if ($code == 999) { + $code = 500; Message::addError('timeout'); - } elseif ($code != 200) { + } elseif (floor($code / 100) != 2) { Message::addError('dozmod-error', $code); } else { Message::addSuccess('all-templates-reset', $result); } - Util::redirect('?do=dozmod§ion=templates'); + Util::redirect('?do=dozmod§ion=templates', $code); } private static function fetchTemplates() { diff --git a/modules-available/eventlog/pages/mailconfigs.inc.php b/modules-available/eventlog/pages/mailconfigs.inc.php index 0ba71104..096d27e5 100644 --- a/modules-available/eventlog/pages/mailconfigs.inc.php +++ b/modules-available/eventlog/pages/mailconfigs.inc.php @@ -88,7 +88,7 @@ class SubPage WHERE configid = :id', ['id' => $id]); if ($data === false) { Message::addError('invalid-mailconfig-id', $id); - Util::redirect('?do=eventlog&show=mailconfigs'); + Util::redirect('?do=eventlog&show=mailconfigs', 404); } } else { $data = ['configid' => 0]; diff --git a/modules-available/eventlog/pages/rules.inc.php b/modules-available/eventlog/pages/rules.inc.php index ca40714f..94ca473b 100644 --- a/modules-available/eventlog/pages/rules.inc.php +++ b/modules-available/eventlog/pages/rules.inc.php @@ -39,7 +39,7 @@ class SubPage unset($item); if (empty($filters)) { Message::addError('no-valid-filters'); - Util::redirect('?do=eventlog&show=rules'); + Util::redirect('?do=eventlog&show=rules', 400); } if ($id === 0) { $id = null; diff --git a/modules-available/exams/page.inc.php b/modules-available/exams/page.inc.php index 76ce1a5d..c4f807c4 100644 --- a/modules-available/exams/page.inc.php +++ b/modules-available/exams/page.inc.php @@ -317,15 +317,15 @@ class Page_Exams extends Page $autologin = Request::post('autologin', '', 'string'); if (!$this->isDateSane($starttime)) { Message::addError('starttime-invalid', Request::post('starttime_date') . " " . Request::post('starttime_time')); - Util::redirect('?do=exams'); + Util::redirect('?do=exams', 400); } if (!$this->isDateSane($endtime)) { Message::addError('endtime-invalid', Request::post('endtime_date') . " " . Request::post('endtime_time')); - Util::redirect('?do=exams'); + Util::redirect('?do=exams', 400); } if ($endtime <= $starttime) { Message::addError('end-before-start'); - Util::redirect('?do=exams'); + Util::redirect('?do=exams', 400); } if ($examid === 0) { @@ -339,6 +339,7 @@ class Page_Exams extends Page } if ($res === false) { Message::addError('exam-not-added'); + Audit::overrideResponseCode(500); } else { Message::addInfo('exam-added-success'); } @@ -349,7 +350,7 @@ class Page_Exams extends Page $this->currentExam = Database::queryFirst("SELECT * FROM exams WHERE examid = :examid", array('examid' => $examid)); if ($this->currentExam === false) { Message::addError('invalid-exam-id', $examid); - Util::redirect('?do=exams'); + Util::redirect('?do=exams', 404); } /* update fields */ @@ -365,6 +366,7 @@ class Page_Exams extends Page Message::addInfo("changes-successfully-saved"); } else { Message::addError("error-while-saving-changes"); + Audit::overrideResponseCode(500); } Util::redirect('?do=exams'); } @@ -417,7 +419,7 @@ class Page_Exams extends Page $this->currentExam = Database::queryFirst("SELECT * FROM exams WHERE examid = :examid", array('examid' => $examid)); if ($this->currentExam === false) { Message::addError('invalid-exam-id', $examid); - Util::redirect('?do=exams'); + Util::redirect('?do=exams', 404); } $this->readLocations($examid); $this->readLectures(); @@ -439,6 +441,7 @@ class Page_Exams extends Page $res2 = Database::exec("DELETE FROM exams_x_location WHERE examid = :examid;", compact('examid')); if ($res1 === false || $res2 === false) { Message::addError('exam-not-deleted-error'); + Audit::overrideResponseCode(500); } else { Message::addInfo('exam-deleted-success'); } diff --git a/modules-available/locationinfo/page.inc.php b/modules-available/locationinfo/page.inc.php index decaa8f2..b5171200 100644 --- a/modules-available/locationinfo/page.inc.php +++ b/modules-available/locationinfo/page.inc.php @@ -232,7 +232,7 @@ class Page_LocationInfo extends Page $paneluuid = Request::post('uuid', false, 'string'); if (($paneluuid === false || strlen($paneluuid) !== 36) && $paneluuid !== 'new') { Message::addError('invalid-panel-id', $paneluuid); - Util::redirect('?do=locationinfo'); + Util::redirect('?do=locationinfo', 400); } // Check panel type $paneltype = Request::post('ptype', false, 'string'); @@ -245,7 +245,7 @@ class Page_LocationInfo extends Page $params = $this->preparePanelConfigSummary(); } else { Message::addError('invalid-panel-type', $paneltype); - Util::redirect('?do=locationinfo'); + Util::redirect('?do=locationinfo', 400); } // Permission @@ -404,7 +404,7 @@ class Page_LocationInfo extends Page if ($backend === false) { Message::addError('invalid-backend-type', $servertype); - Util::redirect('?do=locationinfo'); + Util::redirect('?do=locationinfo', 400); } $tmptypeArray = $backend->getCredentialDefinitions(); @@ -522,7 +522,7 @@ class Page_LocationInfo extends Page WHERE serverid = :id', ['id' => $id]); if ($server === false) { Message::addError('invalid-server-id', $id); - Util::redirect('?do=locationinfo'); + Util::redirect('?do=locationinfo', 400); } $server['list'] = []; $res = Database::simpleQuery('SELECT dateline, message FROM locationinfo_backendlog diff --git a/modules-available/locations/page.inc.php b/modules-available/locations/page.inc.php index 97886bd2..7df30290 100644 --- a/modules-available/locations/page.inc.php +++ b/modules-available/locations/page.inc.php @@ -8,7 +8,7 @@ class Page_Locations extends Page $page = Request::any('page', 'locations', 'string'); if (!in_array($page, ['locations', 'subnets', 'details', 'cleanup'])) { Message::addError('main.invalid-action', $page); - Util::redirect('?do=Main'); + Util::redirect('?do=Main', 400); } require_once Page::getModule()->getDir() . '/pages/' . $page . '.inc.php'; } @@ -43,7 +43,7 @@ class Page_Locations extends Page $getAction = Request::get('action', false, 'string'); if (!SubPage::doRender($getAction)) { Message::addError('main.invalid-action', $getAction); - Util::redirect('?do=locations'); + Util::redirect('?do=locations', 400); } } diff --git a/modules-available/locations/pages/locations.inc.php b/modules-available/locations/pages/locations.inc.php index 78818328..c25232f3 100644 --- a/modules-available/locations/pages/locations.inc.php +++ b/modules-available/locations/pages/locations.inc.php @@ -39,12 +39,8 @@ class SubPage private static function addLocations() { - $names = Request::post('newlocation', false); - $parents = Request::post('newparent', []); - if (!is_array($names) || !is_array($parents)) { - Message::addError('main.empty-field'); - Util::redirect('?do=Locations'); - } + $names = Request::post('newlocation', Request::REQUIRED_EMPTY, 'array'); + $parents = Request::post('newparent', Request::REQUIRED_EMPTY, 'array'); $locs = Location::getLocations(); $count = 0; foreach ($names as $idx => $name) { diff --git a/modules-available/locations/pages/subnets.inc.php b/modules-available/locations/pages/subnets.inc.php index 7628486b..d02a7479 100644 --- a/modules-available/locations/pages/subnets.inc.php +++ b/modules-available/locations/pages/subnets.inc.php @@ -17,13 +17,9 @@ class SubPage User::assertPermission('subnets.edit', NULL, '?do=locations'); $editCount = 0; $deleteCount = 0; - $starts = Request::post('startaddr', false); - $ends = Request::post('endaddr', false); - $locs = Request::post('location', false); - if (!is_array($starts) || !is_array($ends) || !is_array($locs)) { - Message::addError('main.empty-field'); - Util::redirect('?do=Locations'); - } + $starts = Request::post('startaddr', Request::REQUIRED, 'array'); + $ends = Request::post('endaddr', Request::REQUIRED, 'array'); + $locs = Request::post('location', Request::REQUIRED, 'array'); $existingLocs = Location::getLocationsAssoc(); $stmt = Database::prepare("UPDATE subnet SET startaddr = :startLong, endaddr = :endLong, locationid = :loc WHERE subnetid = :subnetid"); foreach ($starts as $subnetid => $start) { diff --git a/modules-available/main/install.inc.php b/modules-available/main/install.inc.php index 69c0da8f..d5ce1992 100644 --- a/modules-available/main/install.inc.php +++ b/modules-available/main/install.inc.php @@ -87,6 +87,18 @@ $res[] = tableCreate('mail_config', " PRIMARY KEY (`configid`) "); +$res[] = tableCreate('audit', " + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `dateline` int(10) unsigned NOT NULL, + `userid` int(10) unsigned NULL, + `ipaddr` varchar(50) NOT NULL, + `module` varchar(100) NOT NULL, + `action` varchar(100) NOT NULL, + `data` mediumblob NOT NULL, + `response` smallint(3) unsigned DEFAULT NULL, + PRIMARY KEY (`id`) +"); + // Update path // ####################### @@ -153,6 +165,9 @@ if ($someUser !== false && (int)$someUser['userid'] !== 1) { $res[] = tableAddConstraint('mail_queue', 'configid', 'mail_config', 'configid', 'ON UPDATE CASCADE ON DELETE CASCADE'); +$res[] = tableAddConstraint('audit', 'userid', 'user', 'userid', + 'ON UPDATE CASCADE ON DELETE SET NULL'); + // Create response for browser if (in_array(UPDATE_DONE, $res)) { diff --git a/modules-available/minilinux/page.inc.php b/modules-available/minilinux/page.inc.php index 4dd49c3d..7193658b 100644 --- a/modules-available/minilinux/page.inc.php +++ b/modules-available/minilinux/page.inc.php @@ -246,6 +246,7 @@ class Page_MiniLinux extends Page return Dictionary::translate('file-not-readable'); case self::FILE_OK: return Dictionary::translate('file-ok'); + default: } return '???'; } @@ -274,6 +275,7 @@ class Page_MiniLinux extends Page ['versionid' => $versionid]); if ($version === false) { Message::addError('no-such-version'); + Audit::overrideResponseCode(404); return; } $path = CONFIG_HTTP_DIR . '/' . $version['versionid']; @@ -285,6 +287,7 @@ class Page_MiniLinux extends Page $task = Taskmanager::waitComplete($task, 2500); if (Taskmanager::isFailed($task)) { MiniLinux::setInstalledState($version['versionid'], MiniLinux::INSTALL_BROKEN); + Audit::overrideResponseCode(500); Message::addError('delete-error', $versionid, $task['data']['error']); } else { MiniLinux::setInstalledState($version['versionid'], MiniLinux::INSTALL_MISSING); @@ -314,6 +317,7 @@ class Page_MiniLinux extends Page ['versionid' => $versionid]); if ($version === false) { Message::addError('no-such-version'); + Audit::overrideResponseCode(404); return; } MiniLinux::setDefaultVersion($version['versionid']); diff --git a/modules-available/news/page.inc.php b/modules-available/news/page.inc.php index 291f15fc..99264a66 100644 --- a/modules-available/news/page.inc.php +++ b/modules-available/news/page.inc.php @@ -88,7 +88,7 @@ class Page_News extends Page $this->locationId = Request::post('locationid', Request::REQUIRED_EMPTY, 'int'); if (!array_key_exists($pageType, self::TYPES)) { Message::addError('invalid-type', $pageType); - Util::redirect('?do=news&locationid=' . $this->locationId); + Util::redirect('?do=news&locationid=' . $this->locationId, 404); } if ($action === 'save') { diff --git a/modules-available/permissionmanager/page.inc.php b/modules-available/permissionmanager/page.inc.php index a637148d..d51d34e7 100644 --- a/modules-available/permissionmanager/page.inc.php +++ b/modules-available/permissionmanager/page.inc.php @@ -35,11 +35,7 @@ class Page_PermissionManager extends Page User::assertPermission('roles.edit'); $roleID = Request::post("roleid", Request::REQUIRED_EMPTY, 'int'); $this->denyActionIfBuiltin($roleID); - $roleName = Request::post("rolename", '', 'string'); - if (empty($roleName)) { - Message::addError('main.parameter-empty', 'rolename'); - Util::redirect('?do=permissionmanager'); - } + $roleName = Request::post("rolename", Request::REQUIRED, 'string'); $roleDescription = Request::post('roledescription', '', 'string'); $locations = self::processLocations(Request::post("locations", [], 'array')); $permissions = self::processPermissions(Request::post("permissions")); @@ -118,7 +114,7 @@ class Page_PermissionManager extends Page $role = GetPermissionData::getRoleData($roleid); if ($role === null) { Message::addError('invalid-role-id', $roleid); - Util::redirect('?do=permissionmanager'); + Util::redirect('?do=permissionmanager', 404); } if ($role['builtin']) { // Copy the role, as it's builtin @@ -315,11 +311,11 @@ class Page_PermissionManager extends Page $existing = GetPermissionData::getRole($roleID); if ($existing === false) { Message::addError('invalid-role-id', $roleID); - Util::redirect('?do=permissionmanager'); + Util::redirect('?do=permissionmanager', 404); } if ($existing['builtin']) { Message::addError('builtin-role', $existing['rolename']); - Util::redirect('?do=permissionmanager'); + Util::redirect('?do=permissionmanager', 403); } } } diff --git a/modules-available/rebootcontrol/page.inc.php b/modules-available/rebootcontrol/page.inc.php index eda37495..9f20431a 100644 --- a/modules-available/rebootcontrol/page.inc.php +++ b/modules-available/rebootcontrol/page.inc.php @@ -36,7 +36,7 @@ class Page_RebootControl extends Page SubPage::doPreprocess(); } else { Message::addError('main.invalid-action', $section); - return; + Util::redirect('?do=rebootcontrol', 404); } } else { $action = Request::post('action', 'show', 'string'); diff --git a/modules-available/rebootcontrol/pages/jumphost.inc.php b/modules-available/rebootcontrol/pages/jumphost.inc.php index d9aae234..b1a60785 100644 --- a/modules-available/rebootcontrol/pages/jumphost.inc.php +++ b/modules-available/rebootcontrol/pages/jumphost.inc.php @@ -40,8 +40,10 @@ class SubPage // Permcheck in caller $host = self::getJumpHost($hostid); $task = RebootControl::wakeViaJumpHost($host, '255.255.255.255', [['macaddr' => '00:11:22:33:44:55']]); - if (!Taskmanager::isTask($task)) + if (!Taskmanager::isTask($task)) { + http_response_code(500); return; + } Util::redirect('?do=rebootcontrol&show=task&type=checkhost&what=task&taskid=' . $task['id']); } @@ -214,7 +216,7 @@ class SubPage WHERE hostid = :id', ['id' => $hostid]); if ($host === false) { Message::addError('no-such-jumphost', $hostid); - Util::redirect('?do=rebootcontrol'); + Util::redirect('?do=rebootcontrol', 404); } return $host; } diff --git a/modules-available/roomplanner/page.inc.php b/modules-available/roomplanner/page.inc.php index 94bc3f78..913e86c2 100644 --- a/modules-available/roomplanner/page.inc.php +++ b/modules-available/roomplanner/page.inc.php @@ -25,14 +25,19 @@ class Page_Roomplanner extends Page private function loadRequestedLocation() { - $this->locationid = Request::get('locationid', null, 'integer'); - if ($this->locationid !== null) { - $locs = Location::getLocationsAssoc(); - if (isset($locs[$this->locationid])) { - $this->location = $locs[$this->locationid]; - $this->isLeaf = empty($this->location['children']); + $this->locationid = Request::get('locationid', Request::REQUIRED, 'int'); + $locs = Location::getLocationsAssoc(); + if (!isset($locs[$this->locationid])) { + Message::addError('locations.invalid-location-id', $this->locationid); + if (AJAX) { + http_response_code(404); + Message::renderList(); + exit; } + Util::redirect('?do=locations', 404); } + $this->location = $locs[$this->locationid]; + $this->isLeaf = empty($this->location['children']); } protected function doPreprocess() @@ -46,14 +51,6 @@ class Page_Roomplanner extends Page $this->action = Request::any('action', 'show', 'string'); $this->loadRequestedLocation(); - if ($this->locationid === null) { - Message::addError('need-locationid'); - Util::redirect('?do=locations'); - } - if ($this->location === null) { - Message::addError('locations.invalid-location-id', $this->locationid); - Util::redirect('?do=locations'); - } if ($this->action === 'save') { $this->handleSaveRequest(false); @@ -85,7 +82,7 @@ class Page_Roomplanner extends Page if ($config === false) { $config = ['managerip' => '', 'tutoruuid' => '']; } - $runmode = RunMode::getForMode(Page::getModule(), $this->locationid, true); + $runmode = RunMode::getForMode(Page::getModule(), (string)$this->locationid, true); if (empty($runmode)) { $config['dedicatedmgr'] = false; } else { @@ -206,12 +203,6 @@ class Page_Roomplanner extends Page } elseif ($this->action === 'save') { // Save roomplan - give feedback if it failed so the window can stay open $this->loadRequestedLocation(); - if ($this->locationid === null) { - die('Missing locationid in save data'); - } - if ($this->location === null) { - die('Location with id ' . $this->locationid . ' does not exist.'); - } $this->handleSaveRequest(true); die('SUCCESS'); } else { @@ -225,10 +216,11 @@ class Page_Roomplanner extends Page $leaf = (bool)Request::post('isleaf', 1, 'int'); if ($leaf !== $this->isLeaf) { if ($isAjax) { + http_response_code(400); die('Leaf mode mismatch. Did you restructure locations while editing this room?'); } Message::addError('leaf-mode-mismatch'); - Util::redirect("?do=roomplanner&locationid={$this->locationid}&action=show"); + Util::redirect("?do=roomplanner&locationid={$this->locationid}&action=show", 400); } if ($this->isLeaf) { $this->saveLeafRoom($isAjax); @@ -247,7 +239,7 @@ class Page_Roomplanner extends Page die('JSON data incomplete'); } Message::addError('json-data-invalid'); - Util::redirect("?do=roomplanner&locationid={$this->locationid}&action=show"); + Util::redirect("?do=roomplanner&locationid={$this->locationid}&action=show", 400); } $tutorUuid = Request::post('tutoruuid', '', 'string'); if (empty($tutorUuid)) { @@ -256,10 +248,11 @@ class Page_Roomplanner extends Page $ret = Database::queryFirst('SELECT machineuuid FROM machine WHERE machineuuid = :uuid', ['uuid' => $tutorUuid]); if ($ret === false) { if ($isAjax) { + http_response_code(400); die('Invalid tutor UUID'); } Message::addError('invalid-tutor-uuid'); - Util::redirect("?do=roomplanner&locationid={$this->locationid}&action=show"); + Util::redirect("?do=roomplanner&locationid={$this->locationid}&action=show", 400); } } $this->saveRoomConfig($config['furniture'], $tutorUuid); @@ -274,10 +267,11 @@ class Page_Roomplanner extends Page ['lid' => $this->locationid, 'plan' => $room->serialize()]); if ($res === false) { if ($isAjax) { + http_response_code(500); die('Error writing config to DB'); } Message::addError('db-error'); - Util::redirect("?do=roomplanner&locationid={$this->locationid}&action=show"); + Util::redirect("?do=roomplanner&locationid={$this->locationid}&action=show", 500); } } diff --git a/modules-available/runmode/page.inc.php b/modules-available/runmode/page.inc.php index 5654456a..adc12541 100644 --- a/modules-available/runmode/page.inc.php +++ b/modules-available/runmode/page.inc.php @@ -118,7 +118,7 @@ class Page_RunMode extends Page $deleted = 0; } Message::addSuccess('runmode.enabled-removed-save', $active, $deleted); - Util::redirect('?do=runmode&module=' . $module . '&modeid=' . $modeId, true); + Util::redirect('?do=runmode&module=' . $module . '&modeid=' . $modeId, null, true); } private function handleDeleteMachine() @@ -168,13 +168,13 @@ class Page_RunMode extends Page $module = Module::get($moduleId); if ($module === false) { Message::addError('main.no-such-module', $moduleId); - Util::redirect('?do=runmode'); + Util::redirect('?do=runmode', 404); } $module->activate(1, false); $config = RunMode::getModuleConfig($moduleId); if ($config === null) { Message::addError('module-hasnt-runmode', $moduleId); - Util::redirect('?do=runmode'); + Util::redirect('?do=runmode', 400); } // Given modeId? $modeId = Request::get('modeid', false, 'string'); @@ -266,11 +266,11 @@ class Page_RunMode extends Page } if ($modeName === false) { Message::addError('invalid-modeid', $moduleId, $modeId); - Util::redirect($redirect); + Util::redirect($redirect, 404); } if (!RunMode::getModuleConfig($moduleId)->allowGenericEditor) { Message::addError('runmode.cannot-edit-module', $moduleId); - Util::redirect($redirect); + Util::redirect($redirect, 400); } // Permissions if ($config->userHasPermission(null)) { diff --git a/modules-available/serversetup-bwlp-ipxe/page.inc.php b/modules-available/serversetup-bwlp-ipxe/page.inc.php index c9260687..0de71eff 100644 --- a/modules-available/serversetup-bwlp-ipxe/page.inc.php +++ b/modules-available/serversetup-bwlp-ipxe/page.inc.php @@ -330,7 +330,7 @@ class Page_ServerSetup extends Page if (empty($bootentryTable)) { if (Property::getServerIp() === 'invalid') { Message::addError('no-ip-set'); - Util::redirect('?do=serversetup&show=address'); + Util::redirect('?do=serversetup&show=address', 400); } IPxe::importLegacyMenu(true); $num = IPxe::importSubnetPxeMenus('/srv/openslx/tftp/pxelinux.cfg'); @@ -415,7 +415,7 @@ class Page_ServerSetup extends Page if ($menu === false) { Message::addError('invalid-menu-id', $id); - Util::redirect('?do=serversetup&show=menu'); + Util::redirect('?do=serversetup&show=menu', 404); } $highlight = Request::get('highlight', false, 'string'); if ($id !== 0 && !$this->hasMenuPermission($id, 'ipxe.menu.edit')) { @@ -531,18 +531,18 @@ class Page_ServerSetup extends Page WHERE entryid = :id LIMIT 1', ['id' => $id]); if ($row === false) { Message::addError('invalid-boot-entry', $id); - Util::redirect('?do=serversetup'); + Util::redirect('?do=serversetup', 404); } if ($row['module'] === '.special') { Message::addError('cannot-edit-special', $id); - Util::redirect('?do=serversetup'); + Util::redirect('?do=serversetup', 403); } if ($row['module'][0] === '.') { // either script or exec entry $entry = BootEntry::fromJson($row['module'], $row['data']); if ($entry === null) { Message::addError('unknown-bootentry-type', $id); - Util::redirect('?do=serversetup&show=bootentry'); + Util::redirect('?do=serversetup&show=bootentry', 404); } $entry->addFormFields($params); } else { @@ -884,7 +884,7 @@ class Page_ServerSetup extends Page $entry = null; } if ($entry === null) { - Message::addError('main.empty-field'); + Message::addError('main.value-invalid', 'type', $type); Util::redirect('?do=serversetup&show=bootentry'); } $entryData = json_encode($entry->toArray()); @@ -995,7 +995,12 @@ class Page_ServerSetup extends Page Render::addTemplate('menu-assign-location', $data); } - private function saveLocationMenu() + /** + * Save location <-> menu assignment. + * Gets menu and location from POSt data, checks permissions and existence of menu/location. + * Also allows overriding the default entry id of the menu. + */ + private function saveLocationMenu(): void { $locationId = Request::post('locationid', false, 'int'); $loc = Location::get($locationId); @@ -1082,7 +1087,7 @@ class Page_ServerSetup extends Page $id = IPxe::insertMenu($menu, 'Imported Menu', null, 0, [], []); if ($id === null) { Message::addError('import-error'); - Util::redirect('?do=serversetup&show=import'); + Util::redirect('?do=serversetup&show=import', 500); } else { Util::redirect('?do=serversetup&show=editmenu&id=' . $id); } diff --git a/modules-available/session/page.inc.php b/modules-available/session/page.inc.php index 5f5e5d28..c59af63a 100644 --- a/modules-available/session/page.inc.php +++ b/modules-available/session/page.inc.php @@ -19,6 +19,7 @@ class Page_Session extends Page } // Login credentials wrong - delay and show error message sleep(1); + http_response_code(403); Message::addError('loginfail'); } elseif ($action === 'logout') { // Log user out (or do nothing if not logged in) @@ -27,25 +28,21 @@ class Page_Session extends Page if (!User::isLoggedIn()) { Util::redirect('?do=main'); } - // Now check if the user supplied the corrent current password, and the new password twice - $old = Request::post('old', false, 'string'); - $new = Request::post('newpass1', false, 'string'); - if ($old === false || $new === false) { - Message::addError('main.empty-field'); - Util::redirect('?do=session'); - } + // Now check if the user supplied the current password, and the new password twice + $old = Request::post('old', Request::REQUIRED, 'string'); + $new = Request::post('newpass1', Request::REQUIRED, 'string'); if (!User::testPassword(User::getId(), $old)) { sleep(1); Message::addError('wrong-password'); - Util::redirect('?do=session'); + Util::redirect('?do=session', 403); } if (strlen($new) < 4) { Message::addError('pass-too-short'); - Util::redirect('?do=session'); + Util::redirect('?do=session', 400); } if ($new !== Request::post('newpass2', false, 'string')) { Message::addError('adduser.password-mismatch'); - Util::redirect('?do=session'); + Util::redirect('?do=session', 400); } if (Request::post('kill-other-sessions', false, 'bool')) { Session::deleteAllButCurrent(); @@ -55,7 +52,7 @@ class Page_Session extends Page } else { Message::addWarning('password-unchanged'); } - Util::redirect('?do=session'); + Util::redirect('?do=session', 200); } else { // No action, change title to session list Render::setTitle(Dictionary::translate('page-title-session-list')); diff --git a/modules-available/statistics/page.inc.php b/modules-available/statistics/page.inc.php index 3a938ff9..0090ad23 100644 --- a/modules-available/statistics/page.inc.php +++ b/modules-available/statistics/page.inc.php @@ -67,7 +67,7 @@ class Page_Statistics extends Page array('uuid' => $uuid)); if ($res === false) { Message::addError('unknown-machine', $uuid); - Util::redirect('?do=statistics'); + Util::redirect('?do=statistics', 404); } User::assertPermission("machine.note.edit", (int)$res['locationid']); $text = Request::post('content', null, 'string'); @@ -84,7 +84,7 @@ class Page_Statistics extends Page $this->deleteMachines(true); } elseif ($action === 'delmachines') { $this->deleteMachines(false); - Util::redirect('?do=statistics', true); + Util::redirect('?do=statistics', null, true); } elseif ($action === 'rebootmachines') { $this->rebootControl(true); } elseif ($action === 'shutdownmachines') { diff --git a/modules-available/sysconfig/addconfig.inc.php b/modules-available/sysconfig/addconfig.inc.php index 456ee989..2b6d7624 100644 --- a/modules-available/sysconfig/addconfig.inc.php +++ b/modules-available/sysconfig/addconfig.inc.php @@ -23,7 +23,7 @@ abstract class AddConfig_Base { if (empty($step) || !class_exists($step) || get_parent_class($step) !== 'AddConfig_Base') { Message::addError('invalid-action', $step); - Util::redirect('?do=SysConfig'); + Util::redirect('?do=SysConfig', 400); } self::$instance = new $step(); if (($editId = Request::any('edit', 0, 'int')) !== 0) { @@ -170,12 +170,8 @@ class AddConfig_Finish extends AddConfig_Base protected function preprocessInternal() { - $modules = Request::post('module'); + $modules = Request::post('module', Request::REQUIRED_EMPTY, 'array'); $title = Request::post('title', Request::REQUIRED, 'string'); - if (!is_array($modules)) { - Message::addError('missing-file'); - Util::redirect('?do=SysConfig&action=addconfig'); - } if ($this->edit === null) { $this->config = ConfigTgz::insert($title, $modules); } else { @@ -184,7 +180,7 @@ class AddConfig_Finish extends AddConfig_Base } if ($this->config->generate($this->edit === null, 150) === false) { Message::addError('unsuccessful-action'); - Util::redirect('?do=SysConfig&action=addconfig'); + Util::redirect('?do=SysConfig&action=addconfig', 400); } } diff --git a/modules-available/sysconfig/addmodule.inc.php b/modules-available/sysconfig/addmodule.inc.php index 32752c03..37f6e0da 100644 --- a/modules-available/sysconfig/addmodule.inc.php +++ b/modules-available/sysconfig/addmodule.inc.php @@ -30,7 +30,7 @@ abstract class AddModule_Base { if (empty($step) || !class_exists($step) || get_parent_class($step) !== 'AddModule_Base') { Message::addError('invalid-action', $step); - Util::redirect('?do=SysConfig'); + Util::redirect('?do=SysConfig', 400); } self::$instance = new $step(); if ($editId === null) { @@ -49,7 +49,7 @@ abstract class AddModule_Base protected function tmError() { Message::addError('main.taskmanager-error'); - Util::redirect('?do=SysConfig'); + Util::redirect('?do=SysConfig', 500); } protected function taskError($status) @@ -62,7 +62,7 @@ abstract class AddModule_Base $error = Dictionary::translate('lang_unknwonTaskManager'); } Message::addError('main.task-error', $error); - Util::redirect('?do=SysConfig'); + Util::redirect('?do=SysConfig', 500); } /** @@ -151,8 +151,7 @@ class AddModule_Assign extends AddModule_Base protected function preprocessInternal() { - if (!User::hasPermission('config.edit')) - Util::redirect('?do=SysConfig', false, true); + User::assertPermission('config.edit'); $assign = Request::any('assign', false, 'boolean'); @@ -189,7 +188,7 @@ class AddModule_Assign extends AddModule_Base $config->generate(); } - Util::redirect('?do=SysConfig', false, true); + Util::redirect('?do=SysConfig', null, false, true); } } @@ -197,7 +196,7 @@ class AddModule_Assign extends AddModule_Base { $data = ['configs' => SysConfig::getAll()]; if (count($data['configs']) === 0) // Nothing to do - Util::redirect('?do=SysConfig', false, true); + Util::redirect('?do=SysConfig', null, false, true); $moduleType = $this->edit->moduleType(); // If this is a module of type unique, mark all configs that already contain a module of that type diff --git a/modules-available/sysconfig/addmodule_adauth.inc.php b/modules-available/sysconfig/addmodule_adauth.inc.php index 42187171..54771487 100644 --- a/modules-available/sysconfig/addmodule_adauth.inc.php +++ b/modules-available/sysconfig/addmodule_adauth.inc.php @@ -135,7 +135,7 @@ class AdAuth_SelfSearch extends AddModule_Base $bindpw = Request::post('bindpw'); $ssl = Request::post('ssl', 'off') === 'on'; if ($ssl && !Request::post('fingerprint')) { - Message::addError('main.error-read', 'fingerprint'); + Message::addError('main.parameter-empty', 'fingerprint'); AddModule_Base::setStep('AdAuth_Start'); // Continues with AdAuth_Start for render() return; } @@ -229,7 +229,7 @@ class AdAuth_HomeAttrCheck extends AddModule_Base $bindpw = Request::post('bindpw'); $ssl = Request::post('ssl', 'off') === 'on'; if ($ssl && !Request::post('fingerprint')) { - Message::addError('main.error-read', 'fingerprint'); + Message::addError('main.parameter-empty', 'fingerprint'); AddModule_Base::setStep('AdAuth_Start'); // Continues with AdAuth_Start for render() return; } @@ -302,7 +302,7 @@ class AdAuth_CheckCredentials extends AddModule_Base $bindpw = Request::post('bindpw'); $ssl = Request::post('ssl', 'off') === 'on'; if ($ssl && !Request::post('fingerprint')) { - Message::addError('main.error-read', 'fingerprint'); + Message::addError('main.parameter-empty', 'fingerprint'); AddModule_Base::setStep('AdAuth_Start'); // Continues with AdAuth_Start for render() return; } diff --git a/modules-available/sysconfig/addmodule_branding.inc.php b/modules-available/sysconfig/addmodule_branding.inc.php index 54b2ad57..4f35c83b 100644 --- a/modules-available/sysconfig/addmodule_branding.inc.php +++ b/modules-available/sysconfig/addmodule_branding.inc.php @@ -28,7 +28,7 @@ class Branding_ProcessFile extends AddModule_Base $url = Request::post('url'); if ((!isset($_FILES['file']['error']) || $_FILES['file']['error'] === UPLOAD_ERR_NO_FILE) && empty($url)) { Message::addError('main.empty-field'); - Util::redirect('?do=SysConfig&action=addmodule&step=Branding_Start'); + Util::redirect('?do=SysConfig&action=addmodule&step=Branding_Start', 400); } $this->svgFile = tempnam(sys_get_temp_dir(), 'bwlp-'); @@ -36,11 +36,11 @@ class Branding_ProcessFile extends AddModule_Base // Prefer uploaded image over URL (in case both are given) if ($_FILES['file']['error'] !== UPLOAD_ERR_OK) { Message::addError('upload-failed', Util::uploadErrorString($_FILES['file']['error'])); - Util::redirect('?do=SysConfig&action=addmodule&step=Branding_Start'); + Util::redirect('?do=SysConfig&action=addmodule&step=Branding_Start', 400); } if (!move_uploaded_file($_FILES["file"]["tmp_name"], $this->svgFile)) { Message::addError('upload-failed', 'Moving temp file failed'); - Util::redirect('?do=SysConfig&action=addmodule&step=Branding_Start'); + Util::redirect('?do=SysConfig&action=addmodule&step=Branding_Start', 500); } } else { // URL - launch task that fetches the SVG file from it @@ -63,7 +63,7 @@ class Branding_ProcessFile extends AddModule_Base if (Taskmanager::isFailed($this->task)) { @unlink($this->svgFile); Taskmanager::addErrorMessage($this->task); - Util::redirect('?do=SysConfig&action=addmodule&step=Branding_Start'); + Util::redirect('?do=SysConfig&action=addmodule&step=Branding_Start', 500); } Session::set('logo_tgz', $tarFile); } @@ -213,9 +213,10 @@ class Branding_Finish extends AddModule_Base protected function preprocessInternal() { - $title = Request::post('title'); - if (empty($title)) + $title = Request::post('title', '', 'string'); + if (empty($title)) { $title = Session::get('logo_name'); + } if (empty($title)) { Message::addError('missing-title'); // TODO: Ask for title again instead of starting over Util::redirect('?do=SysConfig&action=addmodule&step=Branding_Start'); @@ -236,9 +237,9 @@ class Branding_Finish extends AddModule_Base else $ret = $module->insert($title); if (!$ret) - Util::redirect('?do=SysConfig&action=addmodule&step=Branding_Start'); + Util::redirect('?do=SysConfig&action=addmodule&step=Branding_Start', 500); elseif ($module->generate($this->edit === null, NULL, 200) === false) - Util::redirect('?do=SysConfig&action=addmodule&step=Branding_Start'); + Util::redirect('?do=SysConfig&action=addmodule&step=Branding_Start', 500); Session::set('logo_tgz', false); Session::set('logo_name', false); // Yay diff --git a/modules-available/sysconfig/addmodule_custommodule.inc.php b/modules-available/sysconfig/addmodule_custommodule.inc.php index 3afdad0a..f1199db7 100644 --- a/modules-available/sysconfig/addmodule_custommodule.inc.php +++ b/modules-available/sysconfig/addmodule_custommodule.inc.php @@ -34,11 +34,11 @@ class CustomModule_ProcessUpload extends AddModule_Base { if (!isset($_FILES['modulefile'])) { Message::addError('missing-file'); - Util::redirect('?do=SysConfig'); + Util::redirect('?do=SysConfig', 400); } if ($_FILES['modulefile']['error'] != UPLOAD_ERR_OK) { Message::addError('upload-failed', Util::uploadErrorString($_FILES['modulefile']['error'])); - Util::redirect('?do=SysConfig'); + Util::redirect('?do=SysConfig', 400); } $tempfile = '/tmp/bwlp-' . mt_rand(1, 100000) . '-' . crc32($_SERVER['REMOTE_ADDR']) . '.tmp'; if (!move_uploaded_file($_FILES['modulefile']['tmp_name'], $tempfile)) { @@ -92,11 +92,15 @@ class CustomModule_CompressModule extends AddModule_Base protected function preprocessInternal() { - $title = Request::post('title'); + $title = Request::post('title', Request::REQUIRED, 'string'); $tempfile = Session::get('mod_temp'); - if (empty($title) || empty($tempfile) || !file_exists($tempfile)) { + if (empty($tempfile)) { Message::addError('main.empty-field'); - Util::redirect('?do=SysConfig&action=addmodule&step=CustomModule_Start'); + Util::redirect('?do=SysConfig&action=addmodule&step=CustomModule_Start', 400); + } + if (!file_exists($tempfile)) { + Message::addError('main.error-read', $tempfile); + Util::redirect('?do=SysConfig&action=addmodule&step=CustomModule_Start', 500); } // Recompress using task manager $taskId = 'tgzmod' . mt_rand() . '-' . microtime(true); @@ -127,10 +131,11 @@ class CustomModule_CompressModule extends AddModule_Base } else { $ret = $module->insert($title); } - if (!$ret) - Util::redirect('?do=SysConfig&action=addmodule&step=CustomModule_Start'); - elseif (!$module->generate($this->edit === null, NULL, 200)) - Util::redirect('?do=SysConfig&action=addmodule&step=CustomModule_Start'); + if (!$ret) { + Util::redirect('?do=SysConfig&action=addmodule&step=CustomModule_Start', 500); + } elseif (!$module->generate($this->edit === null, NULL, 200)) { + Util::redirect('?do=SysConfig&action=addmodule&step=CustomModule_Start', 500); + } Session::set('mod_temp', false); // Yay if ($this->edit !== null) { diff --git a/modules-available/sysconfig/addmodule_ldapauth.inc.php b/modules-available/sysconfig/addmodule_ldapauth.inc.php index 6a385d9c..c40452c7 100644 --- a/modules-available/sysconfig/addmodule_ldapauth.inc.php +++ b/modules-available/sysconfig/addmodule_ldapauth.inc.php @@ -104,7 +104,7 @@ class LdapAuth_CheckCredentials extends AddModule_Base $bindpw = Request::post('bindpw'); $ssl = Request::post('ssl', 'off') === 'on'; if ($ssl && !Request::post('fingerprint')) { - Message::addError('main.error-read', 'fingerprint'); + Message::addError('main.parameter-empty', 'fingerprint'); AddModule_Base::setStep('LdapAuth_Start'); // Continues with LdapAuth_Start for render() return; } diff --git a/modules-available/sysconfig/addmodule_screensaver.inc.php b/modules-available/sysconfig/addmodule_screensaver.inc.php index 36353c33..43024592 100644 --- a/modules-available/sysconfig/addmodule_screensaver.inc.php +++ b/modules-available/sysconfig/addmodule_screensaver.inc.php @@ -149,7 +149,7 @@ class Screensaver_Finish extends AddModule_Base if (empty($session_data['title'])) { Message::addError('missing-title'); - Util::redirect('?do=SysConfig'); + Util::redirect('?do=SysConfig', 400); } /* Only create an instance, if it's a new one */ @@ -196,7 +196,7 @@ class Screensaver_Helper $session_data['title'] = Request::post('title', $session_data['title'], 'string'); if (empty($session_data['title'])) { Message::addError('missing-title'); - Util::redirect('?do=SysConfig'); + Util::redirect('?do=SysConfig', 400); } $session_data['qss'] = Request::post('qss', $session_data['qss'], 'string'); $helperMode = Request::post('helper_mode', 'false', 'string'); diff --git a/modules-available/sysconfig/addmodule_shibauth.inc.php b/modules-available/sysconfig/addmodule_shibauth.inc.php index e599294a..9f243abc 100644 --- a/modules-available/sysconfig/addmodule_shibauth.inc.php +++ b/modules-available/sysconfig/addmodule_shibauth.inc.php @@ -102,14 +102,14 @@ class ShibAuth_Finish extends AddModule_Base $userIdpList = Request::post('idp', [], 'array'); if (empty($userIdpList)) { Message::addError('no-organization-selected'); - Util::redirect('?do=sysconfig&action=addmodule&step=ShibAuth_Orgs'); + Util::redirect('?do=sysconfig&action=addmodule&step=ShibAuth_Orgs', 400); } $session_data = Session::get(SHIB_SESSION_DATA); if (!is_array($session_data)) { $session_data = ['idp' => $userIdpList]; Session::set(SHIB_SESSION_DATA, $session_data); Message::addError('no-session-data'); - Util::redirect('?do=sysconfig&action=addmodule&step=ShibAuth_Start&back=true'); + Util::redirect('?do=sysconfig&action=addmodule&step=ShibAuth_Start&back=true', 400); } /* Only create an instance if it's a new one */ diff --git a/modules-available/sysconfig/addmodule_sshkey.inc.php b/modules-available/sysconfig/addmodule_sshkey.inc.php index 9f5bd1d3..2337a898 100644 --- a/modules-available/sysconfig/addmodule_sshkey.inc.php +++ b/modules-available/sysconfig/addmodule_sshkey.inc.php @@ -54,9 +54,9 @@ class SshKey_Finish extends AddModule_Base $ret = $module->insert($title); } if (!$ret) { - Util::redirect('?do=SysConfig&action=addmodule&step=SshKey_Start'); + Util::redirect('?do=SysConfig&action=addmodule&step=SshKey_Start', 500); } elseif (!$module->generate($this->edit === null, NULL, 200)) { - Util::redirect('?do=SysConfig&action=addmodule&step=SshKey_Start'); + Util::redirect('?do=SysConfig&action=addmodule&step=SshKey_Start', 500); } // Yay if ($this->edit !== null) { diff --git a/modules-available/sysconfig/page.inc.php b/modules-available/sysconfig/page.inc.php index c015d059..c9d63a27 100644 --- a/modules-available/sysconfig/page.inc.php +++ b/modules-available/sysconfig/page.inc.php @@ -42,7 +42,7 @@ class Page_SysConfig extends Page // Location valid? if ($this->currentLoc !== 0 && !isset($this->locations[$this->currentLoc])) { Message::addError('locations.invalid-location-id', $this->currentLoc); - Util::redirect('?do=sysconfig'); + Util::redirect('?do=sysconfig', 404); } // Action handling @@ -256,7 +256,7 @@ class Page_SysConfig extends Page $row = Database::queryFirst("SELECT title, filepath FROM configtgz_module WHERE moduleid = :moduleid LIMIT 1", array('moduleid' => $moduleid)); if ($row === false) { Message::addError('config-invalid', $moduleid); - Util::redirect('?do=sysconfig&locationid=' . $this->currentLoc); + Util::redirect('?do=sysconfig&locationid=' . $this->currentLoc, 404); } // find files in that archive @@ -267,7 +267,7 @@ class Page_SysConfig extends Page $status = Taskmanager::waitComplete($status, 4000); if (!Taskmanager::isFinished($status) || Taskmanager::isFailed($status)) { Taskmanager::addErrorMessage($status); - Util::redirect('?do=sysconfig&locationid=' . $this->currentLoc); + Util::redirect('?do=sysconfig&locationid=' . $this->currentLoc, 500); } $list = SysConfig::archiveContentsFromTask($status); @@ -283,7 +283,7 @@ class Page_SysConfig extends Page $config = Database::queryFirst("SELECT title FROM configtgz WHERE configid = :configid LIMIT 1", array('configid' => $configid)); if ($config === false) { Message::addError('config-invalid', $configid); - Util::redirect('?do=sysconfig&locationid=' . $this->currentLoc); + Util::redirect('?do=sysconfig&locationid=' . $this->currentLoc, 404); } // fetch the data $res = Database::simpleQuery("SELECT module.moduleid, module.title AS moduletitle" @@ -316,7 +316,7 @@ class Page_SysConfig extends Page $row = Database::queryFirst("SELECT title, filepath FROM configtgz WHERE configid = :configid LIMIT 1", array('configid' => $configid)); if ($row === false) { Message::addError('config-invalid', $configid); - Util::redirect('?do=sysconfig&locationid=' . $this->currentLoc); + Util::redirect('?do=sysconfig&locationid=' . $this->currentLoc, 404); } } $locationid = $this->currentLoc; @@ -340,7 +340,7 @@ class Page_SysConfig extends Page $config = ConfigTgz::get($configid); if ($config === null) { Message::addError('config-invalid', $configid); - Util::redirect('?do=sysconfig&locationid=' . $this->currentLoc); + Util::redirect('?do=sysconfig&locationid=' . $this->currentLoc, 404); } $ret = $config->generate(false, 500); // TODO if ($ret === true) @@ -358,7 +358,7 @@ class Page_SysConfig extends Page $module = Database::queryFirst("SELECT title, filepath FROM configtgz_module WHERE moduleid = :moduleid LIMIT 1", array('moduleid' => $moduleid)); if ($module === false) { Message::addError('config-invalid', $moduleid); - Util::redirect('?do=sysconfig'); + Util::redirect('?do=sysconfig', 404); } // Get config.tgz using this module *before* deleting it $existing = Database::simpleQuery("SELECT configid FROM configtgz_x_module @@ -392,10 +392,10 @@ class Page_SysConfig extends Page $row = Database::queryFirst("SELECT title, filepath FROM configtgz_module WHERE moduleid = :moduleid LIMIT 1", array('moduleid' => $moduleid)); if ($row === false) { Message::addError('config-invalid', $moduleid); - Util::redirect('?do=sysconfig'); + Util::redirect('?do=sysconfig', 404); } if (!Util::sendFile($row['filepath'], $row['title'] . '.tgz')) - Util::redirect('?do=sysconfig'); + Util::redirect('?do=sysconfig', 500); exit(0); } @@ -405,15 +405,18 @@ class Page_SysConfig extends Page $module = ConfigModule::get($moduleid); if ($module === null) { Message::addError('config-invalid', $moduleid); - Util::redirect('?do=sysconfig'); + Util::redirect('?do=sysconfig', 404); } $ret = $module->generate(false, null, 500); - if ($ret === true) - Message::addSuccess('module-rebuilt', $module->title()); - elseif ($ret === false) + if ($ret === false) { Message::addError('module-rebuild-failed', $module->title()); - else + Util::redirect('?do=sysconfig', 500); + } + if ($ret === true) { + Message::addSuccess('module-rebuilt', $module->title()); + } else { Message::addInfo('module-rebuilding', $module->title()); + } Util::redirect('?do=sysconfig'); } @@ -423,10 +426,11 @@ class Page_SysConfig extends Page $config = ConfigTgz::get($configid); if ($config === null) { Message::addError('config-invalid', $configid); - Util::redirect('?do=sysconfig&locationid=' . $this->currentLoc); + Util::redirect('?do=sysconfig&locationid=' . $this->currentLoc, 404); } if ($config->delete() === false) { Message::addError('config-delete-error', Database::lastError()); + Audit::overrideResponseCode(500); } else { Message::addSuccess('config-deleted', $config->title()); } diff --git a/modules-available/syslog/page.inc.php b/modules-available/syslog/page.inc.php index 401d9dd8..bbc12e92 100644 --- a/modules-available/syslog/page.inc.php +++ b/modules-available/syslog/page.inc.php @@ -17,7 +17,8 @@ class Page_SysLog extends Page if (($days = Request::post('anondays', false, 'int')) !== false) { User::assertPermission('configure-anonymization', NULL,'?do=syslog'); - if ($days < 0 || $days > 180) { + if ($days < 0 || $days > 366) { + Audit::overrideResponseCode(400); Message::addError('anon-days-out-of-range', $days); } else { Property::set(self::PROP_ANON_DAYS, $days); diff --git a/modules-available/vmstore/page.inc.php b/modules-available/vmstore/page.inc.php index a873ba88..61cec74e 100644 --- a/modules-available/vmstore/page.inc.php +++ b/modules-available/vmstore/page.inc.php @@ -198,7 +198,7 @@ class Page_VmStore extends Page $list = Dnbd3Rpc::getStatsMulti(array_keys($lookup), [Dnbd3Rpc::QUERY_IMAGES]); if (empty($list)) { Message::addError('dnbd3-failed'); - Util::redirect('?do=vmstore'); + Util::redirect('?do=vmstore', 500); } $images = []; foreach ($list as $json) { |
