aboutsummaryrefslogtreecommitdiff
path: root/srcs/phpmyadmin/libraries/classes/Config/Validator.php
diff options
context:
space:
mode:
Diffstat (limited to 'srcs/phpmyadmin/libraries/classes/Config/Validator.php')
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/Validator.php594
1 files changed, 594 insertions, 0 deletions
diff --git a/srcs/phpmyadmin/libraries/classes/Config/Validator.php b/srcs/phpmyadmin/libraries/classes/Config/Validator.php
new file mode 100644
index 0000000..8294a90
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/Validator.php
@@ -0,0 +1,594 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * Form validation for configuration editor
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config;
+
+use PhpMyAdmin\Config\ConfigFile;
+use PhpMyAdmin\Core;
+use PhpMyAdmin\DatabaseInterface;
+use PhpMyAdmin\Util;
+use function mysql_close;
+use function mysql_connect;
+use function mysqli_close;
+use function mysqli_connect;
+
+/**
+ * Validation class for various validation functions
+ *
+ * Validation function takes two argument: id for which it is called
+ * and array of fields' values (usually values for entire formset, as defined
+ * in forms.inc.php).
+ * The function must always return an array with an error (or error array)
+ * assigned to a form element (formset name or field path). Even if there are
+ * no errors, key must be set with an empty value.
+ *
+ * Validation functions are assigned in $cfg_db['_validators'] (config.values.php).
+ *
+ * @package PhpMyAdmin
+ */
+class Validator
+{
+ /**
+ * Returns validator list
+ *
+ * @param ConfigFile $cf Config file instance
+ *
+ * @return array
+ */
+ public static function getValidators(ConfigFile $cf)
+ {
+ static $validators = null;
+
+ if ($validators !== null) {
+ return $validators;
+ }
+
+ $validators = $cf->getDbEntry('_validators', []);
+ if ($GLOBALS['PMA_Config']->get('is_setup')) {
+ return $validators;
+ }
+
+ // not in setup script: load additional validators for user
+ // preferences we need original config values not overwritten
+ // by user preferences, creating a new PhpMyAdmin\Config instance is a
+ // better idea than hacking into its code
+ $uvs = $cf->getDbEntry('_userValidators', []);
+ foreach ($uvs as $field => $uvList) {
+ $uvList = (array) $uvList;
+ foreach ($uvList as &$uv) {
+ if (! is_array($uv)) {
+ continue;
+ }
+ for ($i = 1, $nb = count($uv); $i < $nb; $i++) {
+ if (mb_substr($uv[$i], 0, 6) == 'value:') {
+ $uv[$i] = Core::arrayRead(
+ mb_substr($uv[$i], 6),
+ $GLOBALS['PMA_Config']->base_settings
+ );
+ }
+ }
+ }
+ $validators[$field] = isset($validators[$field])
+ ? array_merge((array) $validators[$field], $uvList)
+ : $uvList;
+ }
+ return $validators;
+ }
+
+ /**
+ * Runs validation $validator_id on values $values and returns error list.
+ *
+ * Return values:
+ * o array, keys - field path or formset id, values - array of errors
+ * when $isPostSource is true values is an empty array to allow for error list
+ * cleanup in HTML document
+ * o false - when no validators match name(s) given by $validator_id
+ *
+ * @param ConfigFile $cf Config file instance
+ * @param string|array $validatorId ID of validator(s) to run
+ * @param array $values Values to validate
+ * @param bool $isPostSource tells whether $values are directly from
+ * POST request
+ *
+ * @return bool|array
+ */
+ public static function validate(
+ ConfigFile $cf,
+ $validatorId,
+ array &$values,
+ $isPostSource
+ ) {
+ // find validators
+ $validatorId = (array) $validatorId;
+ $validators = static::getValidators($cf);
+ $vids = [];
+ foreach ($validatorId as &$vid) {
+ $vid = $cf->getCanonicalPath($vid);
+ if (isset($validators[$vid])) {
+ $vids[] = $vid;
+ }
+ }
+ if (empty($vids)) {
+ return false;
+ }
+
+ // create argument list with canonical paths and remember path mapping
+ $arguments = [];
+ $keyMap = [];
+ foreach ($values as $k => $v) {
+ $k2 = $isPostSource ? str_replace('-', '/', $k) : $k;
+ $k2 = mb_strpos($k2, '/')
+ ? $cf->getCanonicalPath($k2)
+ : $k2;
+ $keyMap[$k2] = $k;
+ $arguments[$k2] = $v;
+ }
+
+ // validate
+ $result = [];
+ foreach ($vids as $vid) {
+ // call appropriate validation functions
+ foreach ((array) $validators[$vid] as $validator) {
+ $vdef = (array) $validator;
+ $vname = array_shift($vdef);
+ $vname = 'PhpMyAdmin\Config\Validator::' . $vname;
+ $args = array_merge([$vid, &$arguments], $vdef);
+ $r = call_user_func_array($vname, $args);
+
+ // merge results
+ if (! is_array($r)) {
+ continue;
+ }
+
+ foreach ($r as $key => $errorList) {
+ // skip empty values if $isPostSource is false
+ if (! $isPostSource && empty($errorList)) {
+ continue;
+ }
+ if (! isset($result[$key])) {
+ $result[$key] = [];
+ }
+ $result[$key] = array_merge(
+ $result[$key],
+ (array) $errorList
+ );
+ }
+ }
+ }
+
+ // restore original paths
+ $newResult = [];
+ foreach ($result as $k => $v) {
+ $k2 = isset($keyMap[$k]) ? $keyMap[$k] : $k;
+ if (is_array($v)) {
+ $newResult[$k2] = array_map('htmlspecialchars', $v);
+ } else {
+ $newResult[$k2] = htmlspecialchars($v);
+ }
+ }
+ return empty($newResult) ? true : $newResult;
+ }
+
+ /**
+ * Test database connection
+ *
+ * @param string $host host name
+ * @param string $port tcp port to use
+ * @param string $socket socket to use
+ * @param string $user username to use
+ * @param string $pass password to use
+ * @param string $errorKey key to use in return array
+ *
+ * @return bool|array
+ */
+ public static function testDBConnection(
+ $host,
+ $port,
+ $socket,
+ $user,
+ $pass = null,
+ $errorKey = 'Server'
+ ) {
+ if ($GLOBALS['cfg']['DBG']['demo']) {
+ // Connection test disabled on the demo server!
+ return true;
+ }
+
+ $error = null;
+ $host = Core::sanitizeMySQLHost($host);
+
+ error_clear_last();
+
+ if (DatabaseInterface::checkDbExtension('mysqli')) {
+ $socket = empty($socket) ? null : $socket;
+ $port = empty($port) ? null : $port;
+ $extension = 'mysqli';
+ } else {
+ $socket = empty($socket) ? null : ':' . ($socket[0] == '/' ? '' : '/') . $socket;
+ $port = empty($port) ? null : ':' . $port;
+ $extension = 'mysql';
+ }
+
+ if ($extension == 'mysql') {
+ $conn = @mysql_connect($host . $port . $socket, $user, $pass);
+ if (! $conn) {
+ $error = __('Could not connect to the database server!');
+ } else {
+ mysql_close($conn);
+ }
+ } else {
+ $conn = @mysqli_connect($host, $user, $pass, null, $port, $socket);
+ if (! $conn) {
+ $error = __('Could not connect to the database server!');
+ } else {
+ mysqli_close($conn);
+ }
+ }
+ if ($error !== null) {
+ $lastError = error_get_last();
+ if ($lastError !== null) {
+ $error .= ' - ' . $lastError['message'];
+ }
+ }
+ return $error === null ? true : [$errorKey => $error];
+ }
+
+ /**
+ * Validate server config
+ *
+ * @param string $path path to config, not used
+ * keep this parameter since the method is invoked using
+ * reflection along with other similar methods
+ * @param array $values config values
+ *
+ * @return array
+ */
+ public static function validateServer($path, array $values)
+ {
+ $result = [
+ 'Server' => '',
+ 'Servers/1/user' => '',
+ 'Servers/1/SignonSession' => '',
+ 'Servers/1/SignonURL' => '',
+ ];
+ $error = false;
+ if (empty($values['Servers/1/auth_type'])) {
+ $values['Servers/1/auth_type'] = '';
+ $result['Servers/1/auth_type'] = __('Invalid authentication type!');
+ $error = true;
+ }
+ if ($values['Servers/1/auth_type'] == 'config'
+ && empty($values['Servers/1/user'])
+ ) {
+ $result['Servers/1/user'] = __(
+ 'Empty username while using [kbd]config[/kbd] authentication method!'
+ );
+ $error = true;
+ }
+ if ($values['Servers/1/auth_type'] == 'signon'
+ && empty($values['Servers/1/SignonSession'])
+ ) {
+ $result['Servers/1/SignonSession'] = __(
+ 'Empty signon session name '
+ . 'while using [kbd]signon[/kbd] authentication method!'
+ );
+ $error = true;
+ }
+ if ($values['Servers/1/auth_type'] == 'signon'
+ && empty($values['Servers/1/SignonURL'])
+ ) {
+ $result['Servers/1/SignonURL'] = __(
+ 'Empty signon URL while using [kbd]signon[/kbd] authentication '
+ . 'method!'
+ );
+ $error = true;
+ }
+
+ if (! $error && $values['Servers/1/auth_type'] == 'config') {
+ $password = '';
+ if (! empty($values['Servers/1/password'])) {
+ $password = $values['Servers/1/password'];
+ }
+ $test = static::testDBConnection(
+ empty($values['Servers/1/host']) ? '' : $values['Servers/1/host'],
+ empty($values['Servers/1/port']) ? '' : $values['Servers/1/port'],
+ empty($values['Servers/1/socket']) ? '' : $values['Servers/1/socket'],
+ empty($values['Servers/1/user']) ? '' : $values['Servers/1/user'],
+ $password,
+ 'Server'
+ );
+
+ if ($test !== true) {
+ $result = array_merge($result, $test);
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Validate pmadb config
+ *
+ * @param string $path path to config, not used
+ * keep this parameter since the method is invoked using
+ * reflection along with other similar methods
+ * @param array $values config values
+ *
+ * @return array
+ */
+ public static function validatePMAStorage($path, array $values)
+ {
+ $result = [
+ 'Server_pmadb' => '',
+ 'Servers/1/controluser' => '',
+ 'Servers/1/controlpass' => '',
+ ];
+ $error = false;
+
+ if (empty($values['Servers/1/pmadb'])) {
+ return $result;
+ }
+
+ $result = [];
+ if (empty($values['Servers/1/controluser'])) {
+ $result['Servers/1/controluser'] = __(
+ 'Empty phpMyAdmin control user while using phpMyAdmin configuration '
+ . 'storage!'
+ );
+ $error = true;
+ }
+ if (empty($values['Servers/1/controlpass'])) {
+ $result['Servers/1/controlpass'] = __(
+ 'Empty phpMyAdmin control user password while using phpMyAdmin '
+ . 'configuration storage!'
+ );
+ $error = true;
+ }
+ if (! $error) {
+ $test = static::testDBConnection(
+ empty($values['Servers/1/host']) ? '' : $values['Servers/1/host'],
+ empty($values['Servers/1/port']) ? '' : $values['Servers/1/port'],
+ empty($values['Servers/1/socket']) ? '' : $values['Servers/1/socket'],
+ empty($values['Servers/1/controluser']) ? '' : $values['Servers/1/controluser'],
+ empty($values['Servers/1/controlpass']) ? '' : $values['Servers/1/controlpass'],
+ 'Server_pmadb'
+ );
+ if ($test !== true) {
+ $result = array_merge($result, $test);
+ }
+ }
+ return $result;
+ }
+
+
+ /**
+ * Validates regular expression
+ *
+ * @param string $path path to config
+ * @param array $values config values
+ *
+ * @return array
+ */
+ public static function validateRegex($path, array $values)
+ {
+ $result = [$path => ''];
+
+ if (empty($values[$path])) {
+ return $result;
+ }
+
+ error_clear_last();
+
+ $matches = [];
+ // in libraries/ListDatabase.php _checkHideDatabase(),
+ // a '/' is used as the delimiter for hide_db
+ @preg_match('/' . Util::requestString($values[$path]) . '/', '', $matches);
+
+ $currentError = error_get_last();
+
+ if ($currentError !== null) {
+ $error = preg_replace('/^preg_match\(\): /', '', $currentError['message']);
+ return [$path => $error];
+ }
+
+ return $result;
+ }
+
+ /**
+ * Validates TrustedProxies field
+ *
+ * @param string $path path to config
+ * @param array $values config values
+ *
+ * @return array
+ */
+ public static function validateTrustedProxies($path, array $values)
+ {
+ $result = [$path => []];
+
+ if (empty($values[$path])) {
+ return $result;
+ }
+
+ if (is_array($values[$path]) || is_object($values[$path])) {
+ // value already processed by FormDisplay::save
+ $lines = [];
+ foreach ($values[$path] as $ip => $v) {
+ $v = Util::requestString($v);
+ $lines[] = preg_match('/^-\d+$/', $ip)
+ ? $v
+ : $ip . ': ' . $v;
+ }
+ } else {
+ // AJAX validation
+ $lines = explode("\n", $values[$path]);
+ }
+ foreach ($lines as $line) {
+ $line = trim($line);
+ $matches = [];
+ // we catch anything that may (or may not) be an IP
+ if (! preg_match("/^(.+):(?:[ ]?)\\w+$/", $line, $matches)) {
+ $result[$path][] = __('Incorrect value:') . ' '
+ . htmlspecialchars($line);
+ continue;
+ }
+ // now let's check whether we really have an IP address
+ if (filter_var($matches[1], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false
+ && filter_var($matches[1], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false
+ ) {
+ $ip = htmlspecialchars(trim($matches[1]));
+ $result[$path][] = sprintf(__('Incorrect IP address: %s'), $ip);
+ continue;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Tests integer value
+ *
+ * @param string $path path to config
+ * @param array $values config values
+ * @param bool $allowNegative allow negative values
+ * @param bool $allowZero allow zero
+ * @param int $maxValue max allowed value
+ * @param string $errorString error message string
+ *
+ * @return string empty string if test is successful
+ */
+ public static function validateNumber(
+ $path,
+ array $values,
+ $allowNegative,
+ $allowZero,
+ $maxValue,
+ $errorString
+ ) {
+ if (empty($values[$path])) {
+ return '';
+ }
+
+ $value = Util::requestString($values[$path]);
+
+ if (intval($value) != $value
+ || (! $allowNegative && $value < 0)
+ || (! $allowZero && $value == 0)
+ || $value > $maxValue
+ ) {
+ return $errorString;
+ }
+
+ return '';
+ }
+
+ /**
+ * Validates port number
+ *
+ * @param string $path path to config
+ * @param array $values config values
+ *
+ * @return array
+ */
+ public static function validatePortNumber($path, array $values)
+ {
+ return [
+ $path => static::validateNumber(
+ $path,
+ $values,
+ false,
+ false,
+ 65535,
+ __('Not a valid port number!')
+ ),
+ ];
+ }
+
+ /**
+ * Validates positive number
+ *
+ * @param string $path path to config
+ * @param array $values config values
+ *
+ * @return array
+ */
+ public static function validatePositiveNumber($path, array $values)
+ {
+ return [
+ $path => static::validateNumber(
+ $path,
+ $values,
+ false,
+ false,
+ PHP_INT_MAX,
+ __('Not a positive number!')
+ ),
+ ];
+ }
+
+ /**
+ * Validates non-negative number
+ *
+ * @param string $path path to config
+ * @param array $values config values
+ *
+ * @return array
+ */
+ public static function validateNonNegativeNumber($path, array $values)
+ {
+ return [
+ $path => static::validateNumber(
+ $path,
+ $values,
+ false,
+ true,
+ PHP_INT_MAX,
+ __('Not a non-negative number!')
+ ),
+ ];
+ }
+
+ /**
+ * Validates value according to given regular expression
+ * Pattern and modifiers must be a valid for PCRE <b>and</b> JavaScript RegExp
+ *
+ * @param string $path path to config
+ * @param array $values config values
+ * @param string $regex regular expression to match
+ *
+ * @return array|string
+ */
+ public static function validateByRegex($path, array $values, $regex)
+ {
+ if (! isset($values[$path])) {
+ return '';
+ }
+ $result = preg_match($regex, Util::requestString($values[$path]));
+ return [$path => $result ? '' : __('Incorrect value!')];
+ }
+
+ /**
+ * Validates upper bound for numeric inputs
+ *
+ * @param string $path path to config
+ * @param array $values config values
+ * @param int $maxValue maximal allowed value
+ *
+ * @return array
+ */
+ public static function validateUpperBound($path, array $values, $maxValue)
+ {
+ $result = $values[$path] <= $maxValue;
+ return [
+ $path => $result ? '' : sprintf(
+ __('Value must be less than or equal to %s!'),
+ $maxValue
+ ),
+ ];
+ }
+}