PK Default page
One Hat Cyber Team
  • Dir : ~/usr/share/phpmyadmin/libraries/classes/Server/
  • Edit File: Privileges.php
    '; return [ $title, $export, ]; } /** * Get HTML for display Add userfieldset * * @param string $db the database * @param string $table the table name * * @return string html output */ public function getAddUserHtmlFieldset($db = '', $table = '') { if (! $this->dbi->isCreateUser()) { return ''; } $relParams = []; $urlParams = ['adduser' => 1]; if (! empty($db)) { $urlParams['dbname'] = $relParams['checkprivsdb'] = $db; } if (! empty($table)) { $urlParams['tablename'] = $relParams['checkprivstable'] = $table; } return $this->template->render('server/privileges/add_user_fieldset', [ 'url_params' => $urlParams, 'rel_params' => $relParams, ]); } /** * Get HTML snippet for display user overview page * * @param string $textDir text directory * * @return string */ public function getHtmlForUserOverview($textDir) { $passwordColumn = 'Password'; $serverVersion = $this->dbi->getVersion(); if (Compatibility::isMySqlOrPerconaDb() && $serverVersion >= 50706) { $passwordColumn = 'authentication_string'; } // $sql_query is for the initial-filtered, // $sql_query_all is for counting the total no. of users $sqlQuery = $sqlQueryAll = 'SELECT *,' . ' IF(`' . $passwordColumn . "` = _latin1 '', 'N', 'Y') AS 'Password'" . ' FROM `mysql`.`user`'; $sqlQuery .= (isset($_GET['initial']) ? $this->rangeOfUsers($_GET['initial']) : ''); $sqlQuery .= ' ORDER BY `User` ASC, `Host` ASC;'; $sqlQueryAll .= ' ;'; $res = $this->dbi->tryQuery($sqlQuery); $resAll = $this->dbi->tryQuery($sqlQueryAll); $errorMessages = ''; if (! $res) { // the query failed! This may have two reasons: // - the user does not have enough privileges // - the privilege tables use a structure of an earlier version. // so let's try a more simple query unset($resAll); $sqlQuery = 'SELECT * FROM `mysql`.`user`'; $res = $this->dbi->tryQuery($sqlQuery); if (! $res) { $errorMessages .= $this->getHtmlForViewUsersError(); $errorMessages .= $this->getAddUserHtmlFieldset(); } else { // This message is hardcoded because I will replace it by // a automatic repair feature soon. $raw = 'Your privilege table structure seems to be older than' . ' this MySQL version!
    ' . 'Please run the mysql_upgrade command' . ' that should be included in your MySQL server distribution' . ' to solve this problem!'; $errorMessages .= Message::rawError($raw)->getDisplay(); } unset($res); } else { $dbRights = $this->getDbRightsForUserOverview(); // for all initials, even non A-Z $arrayInitials = []; foreach ($dbRights as $right) { foreach ($right as $account) { if (empty($account['User']) && $account['Host'] === 'localhost') { $emptyUserNotice = Message::notice( __( 'A user account allowing any user from localhost to ' . 'connect is present. This will prevent other users ' . 'from connecting if the host part of their account ' . 'allows a connection from any (%) host.' ) . MySQLDocumentation::show('problems-connecting') )->getDisplay(); break 2; } } } /** * Displays the initials * Also not necessary if there is less than 20 privileges */ if ($resAll && $resAll->numRows() > 20) { $initials = $this->getHtmlForInitials($arrayInitials); } /** * Display the user overview * (if less than 50 users, display them immediately) */ if (isset($_GET['initial']) || isset($_GET['showall']) || $res->numRows() < 50) { $usersOverview = $this->getUsersOverview($res, $dbRights, $textDir); $usersOverview .= $this->template->render('export_modal'); } $response = ResponseRenderer::getInstance(); if (! $response->isAjax() || ! empty($_REQUEST['ajax_page_request'])) { if ($GLOBALS['is_reload_priv']) { $flushnote = new Message( __( 'Note: phpMyAdmin gets the users’ privileges directly ' . 'from MySQL’s privilege tables. The content of these ' . 'tables may differ from the privileges the server uses, ' . 'if they have been changed manually. In this case, ' . 'you should %sreload the privileges%s before you continue.' ), Message::NOTICE ); $flushnote->addParamHtml( '' ); $flushnote->addParamHtml(''); } else { $flushnote = new Message( __( 'Note: phpMyAdmin gets the users’ privileges directly ' . 'from MySQL’s privilege tables. The content of these ' . 'tables may differ from the privileges the server uses, ' . 'if they have been changed manually. In this case, ' . 'the privileges have to be reloaded but currently, you ' . 'don\'t have the RELOAD privilege.' ) . MySQLDocumentation::show( 'privileges-provided', false, null, null, 'priv_reload' ), Message::NOTICE ); } $flushNotice = $flushnote->getDisplay(); } } return $this->template->render('server/privileges/user_overview', [ 'error_messages' => $errorMessages, 'empty_user_notice' => $emptyUserNotice ?? '', 'initials' => $initials ?? '', 'users_overview' => $usersOverview ?? '', 'is_createuser' => $this->dbi->isCreateUser(), 'flush_notice' => $flushNotice ?? '', ]); } /** * Get HTML snippet for display user properties * * @param bool $dbnameIsWildcard whether database name is wildcard or not * @param string $urlDbname url database name that urlencode() string * @param string $username username * @param string $hostname host name * @param string|array $dbname database name * @param string $tablename table name * * @return string */ public function getHtmlForUserProperties( $dbnameIsWildcard, $urlDbname, $username, $hostname, $dbname, $tablename ) { global $cfg; $sql = "SELECT '1' FROM `mysql`.`user`" . " WHERE `User` = '" . $this->dbi->escapeString($username) . "'" . " AND `Host` = '" . $this->dbi->escapeString($hostname) . "';"; $userDoesNotExists = ! $this->dbi->fetchValue($sql); $loginInformationFields = ''; if ($userDoesNotExists) { $loginInformationFields = $this->getHtmlForLoginInformationFields(); } $params = [ 'username' => $username, 'hostname' => $hostname, ]; if (! is_array($dbname) && strlen($dbname) > 0) { $params['dbname'] = $dbname; if (strlen($tablename) > 0) { $params['tablename'] = $tablename; } } else { $params['dbname'] = $dbname; } $privilegesTable = $this->getHtmlToDisplayPrivilegesTable( // If $dbname is an array, pass any one db as all have same privs. is_string($dbname) && strlen($dbname) > 0 ? $dbname : (is_array($dbname) ? (string) $dbname[0] : '*'), strlen($tablename) > 0 ? $tablename : '*' ); $tableSpecificRights = ''; if (! is_array($dbname) && strlen($tablename) === 0 && empty($dbnameIsWildcard)) { // no table name was given, display all table specific rights // but only if $dbname contains no wildcards if (strlen($dbname) === 0) { $tableSpecificRights .= $this->getHtmlForAllTableSpecificRights($username, $hostname, 'database'); } else { // unescape wildcards in dbname at table level $unescapedDb = Util::unescapeMysqlWildcards($dbname); $tableSpecificRights .= $this->getHtmlForAllTableSpecificRights( $username, $hostname, 'table', $unescapedDb ); $tableSpecificRights .= $this->getHtmlForAllTableSpecificRights( $username, $hostname, 'routine', $unescapedDb ); } } $databaseUrl = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database'); $databaseUrlTitle = Util::getTitleForTarget($cfg['DefaultTabDatabase']); $tableUrl = Util::getScriptNameForOption($cfg['DefaultTabTable'], 'table'); $tableUrlTitle = Util::getTitleForTarget($cfg['DefaultTabTable']); $changePassword = ''; $userGroup = ''; $changeLoginInfoFields = ''; if (! is_array($dbname) && strlen($dbname) === 0 && ! $userDoesNotExists) { //change login information $changePassword = $this->getFormForChangePassword($username, $hostname, true); $userGroup = $this->getUserGroupForUser($username); $changeLoginInfoFields = $this->getHtmlForLoginInformationFields('change', $username, $hostname); } return $this->template->render('server/privileges/user_properties', [ 'user_does_not_exists' => $userDoesNotExists, 'login_information_fields' => $loginInformationFields, 'params' => $params, 'privileges_table' => $privilegesTable, 'table_specific_rights' => $tableSpecificRights, 'change_password' => $changePassword, 'database' => $dbname, 'dbname' => $urlDbname, 'username' => $username, 'hostname' => $hostname, 'is_databases' => $dbnameIsWildcard || is_array($dbname) && count($dbname) > 1, 'is_wildcard' => $dbnameIsWildcard, 'table' => $tablename, 'current_user' => $this->dbi->getCurrentUser(), 'user_group' => $userGroup, 'change_login_info_fields' => $changeLoginInfoFields, 'database_url' => $databaseUrl, 'database_url_title' => $databaseUrlTitle, 'table_url' => $tableUrl, 'table_url_title' => $tableUrlTitle, ]); } /** * Get queries for Table privileges to change or copy user * * @param string $userHostCondition user host condition to * select relevant table privileges * @param array $queries queries array * @param string $username username * @param string $hostname host name * * @return array */ public function getTablePrivsQueriesForChangeOrCopyUser( $userHostCondition, array $queries, $username, $hostname ) { $res = $this->dbi->query( 'SELECT `Db`, `Table_name`, `Table_priv` FROM `mysql`.`tables_priv`' . $userHostCondition ); while ($row = $res->fetchAssoc()) { $res2 = $this->dbi->query( 'SELECT `Column_name`, `Column_priv`' . ' FROM `mysql`.`columns_priv`' . ' WHERE `User`' . ' = \'' . $this->dbi->escapeString($_POST['old_username']) . "'" . ' AND `Host`' . ' = \'' . $this->dbi->escapeString($_POST['old_username']) . '\'' . ' AND `Db`' . ' = \'' . $this->dbi->escapeString($row['Db']) . "'" . ' AND `Table_name`' . ' = \'' . $this->dbi->escapeString($row['Table_name']) . "'" . ';' ); $tmpPrivs1 = $this->extractPrivInfo($row); $tmpPrivs2 = [ 'Select' => [], 'Insert' => [], 'Update' => [], 'References' => [], ]; while ($row2 = $res2->fetchAssoc()) { $tmpArray = explode(',', $row2['Column_priv']); if (in_array('Select', $tmpArray)) { $tmpPrivs2['Select'][] = $row2['Column_name']; } if (in_array('Insert', $tmpArray)) { $tmpPrivs2['Insert'][] = $row2['Column_name']; } if (in_array('Update', $tmpArray)) { $tmpPrivs2['Update'][] = $row2['Column_name']; } if (! in_array('References', $tmpArray)) { continue; } $tmpPrivs2['References'][] = $row2['Column_name']; } if (count($tmpPrivs2['Select']) > 0 && ! in_array('SELECT', $tmpPrivs1)) { $tmpPrivs1[] = 'SELECT (`' . implode('`, `', $tmpPrivs2['Select']) . '`)'; } if (count($tmpPrivs2['Insert']) > 0 && ! in_array('INSERT', $tmpPrivs1)) { $tmpPrivs1[] = 'INSERT (`' . implode('`, `', $tmpPrivs2['Insert']) . '`)'; } if (count($tmpPrivs2['Update']) > 0 && ! in_array('UPDATE', $tmpPrivs1)) { $tmpPrivs1[] = 'UPDATE (`' . implode('`, `', $tmpPrivs2['Update']) . '`)'; } if (count($tmpPrivs2['References']) > 0 && ! in_array('REFERENCES', $tmpPrivs1)) { $tmpPrivs1[] = 'REFERENCES (`' . implode('`, `', $tmpPrivs2['References']) . '`)'; } $queries[] = 'GRANT ' . implode(', ', $tmpPrivs1) . ' ON ' . Util::backquote($row['Db']) . '.' . Util::backquote($row['Table_name']) . ' TO \'' . $this->dbi->escapeString($username) . '\'@\'' . $this->dbi->escapeString($hostname) . '\'' . (in_array('Grant', explode(',', $row['Table_priv'])) ? ' WITH GRANT OPTION;' : ';'); } return $queries; } /** * Get queries for database specific privileges for change or copy user * * @param array $queries queries array with string * @param string $username username * @param string $hostname host name * * @return array */ public function getDbSpecificPrivsQueriesForChangeOrCopyUser( array $queries, string $username, string $hostname ) { $userHostCondition = ' WHERE `User`' . ' = \'' . $this->dbi->escapeString($_POST['old_username']) . "'" . ' AND `Host`' . ' = \'' . $this->dbi->escapeString($_POST['old_hostname']) . '\';'; $res = $this->dbi->query('SELECT * FROM `mysql`.`db`' . $userHostCondition); while ($row = $res->fetchAssoc()) { $queries[] = 'GRANT ' . implode(', ', $this->extractPrivInfo($row)) . ' ON ' . Util::backquote($row['Db']) . '.*' . ' TO \'' . $this->dbi->escapeString($username) . '\'@\'' . $this->dbi->escapeString($hostname) . '\'' . ($row['Grant_priv'] === 'Y' ? ' WITH GRANT OPTION;' : ';'); } return $this->getTablePrivsQueriesForChangeOrCopyUser($userHostCondition, $queries, $username, $hostname); } /** * Prepares queries for adding users and * also create database and return query and message * * @param bool $error whether user create or not * @param string $realSqlQuery SQL query for add a user * @param string $sqlQuery SQL query to be displayed * @param string $username username * @param string $hostname host name * @param string $dbname database name * @param string $alterRealSqlQuery SQL query for ALTER USER * @param string $alterSqlQuery SQL query for ALTER USER to be displayed * * @return array */ public function addUserAndCreateDatabase( $error, $realSqlQuery, $sqlQuery, $username, $hostname, $dbname, $alterRealSqlQuery, $alterSqlQuery, bool $createDb1, bool $createDb2, bool $createDb3 ): array { if ($error || (! empty($realSqlQuery) && ! $this->dbi->tryQuery($realSqlQuery))) { $createDb1 = $createDb2 = $createDb3 = false; $message = Message::rawError($this->dbi->getError()); } elseif ($alterRealSqlQuery !== '' && ! $this->dbi->tryQuery($alterRealSqlQuery)) { $createDb1 = $createDb2 = $createDb3 = false; $message = Message::rawError($this->dbi->getError()); } else { $sqlQuery .= $alterSqlQuery; $message = Message::success(__('You have added a new user.')); } if ($createDb1) { // Create database with same name and grant all privileges $query = 'CREATE DATABASE IF NOT EXISTS ' . Util::backquote($username) . ';'; $sqlQuery .= $query; if (! $this->dbi->tryQuery($query)) { $message = Message::rawError($this->dbi->getError()); } /** * Reload the navigation */ $GLOBALS['reload'] = true; $GLOBALS['db'] = $username; $query = 'GRANT ALL PRIVILEGES ON ' . Util::backquote( Util::escapeMysqlWildcards($username) ) . '.* TO \'' . $this->dbi->escapeString($username) . '\'@\'' . $this->dbi->escapeString($hostname) . '\';'; $sqlQuery .= $query; if (! $this->dbi->tryQuery($query)) { $message = Message::rawError($this->dbi->getError()); } } if ($createDb2) { // Grant all privileges on wildcard name (username\_%) $query = 'GRANT ALL PRIVILEGES ON ' . Util::backquote( Util::escapeMysqlWildcards($username) . '\_%' ) . '.* TO \'' . $this->dbi->escapeString($username) . '\'@\'' . $this->dbi->escapeString($hostname) . '\';'; $sqlQuery .= $query; if (! $this->dbi->tryQuery($query)) { $message = Message::rawError($this->dbi->getError()); } } if ($createDb3) { // Grant all privileges on the specified database to the new user $query = 'GRANT ALL PRIVILEGES ON ' . Util::backquote($dbname) . '.* TO \'' . $this->dbi->escapeString($username) . '\'@\'' . $this->dbi->escapeString($hostname) . '\';'; $sqlQuery .= $query; if (! $this->dbi->tryQuery($query)) { $message = Message::rawError($this->dbi->getError()); } } return [ $sqlQuery, $message, ]; } /** * Get the hashed string for password * * @param string $password password * * @return string */ public function getHashedPassword($password) { $password = $this->dbi->escapeString($password); $result = $this->dbi->fetchSingleRow("SELECT PASSWORD('" . $password . "') AS `password`;"); return $result['password']; } /** * Check if MariaDB's 'simple_password_check' * OR 'cracklib_password_check' is ACTIVE */ public function checkIfMariaDBPwdCheckPluginActive(): bool { $serverVersion = $this->dbi->getVersion(); if (! (Compatibility::isMariaDb() && $serverVersion >= 100002)) { return false; } $result = $this->dbi->tryQuery('SHOW PLUGINS SONAME LIKE \'%_password_check%\''); /* Plugins are not working, for example directory does not exists */ if ($result === false) { return false; } while ($row = $result->fetchAssoc()) { if ($row['Status'] === 'ACTIVE') { return true; } } return false; } /** * Get SQL queries for Display and Add user * * @param string $username username * @param string $hostname host name * @param string $password password * * @return array ($create_user_real, $create_user_show, $real_sql_query, $sql_query * $password_set_real, $password_set_show, $alter_real_sql_query, $alter_sql_query) */ public function getSqlQueriesForDisplayAndAddUser($username, $hostname, $password) { $slashedUsername = $this->dbi->escapeString($username); $slashedHostname = $this->dbi->escapeString($hostname); $slashedPassword = $this->dbi->escapeString($password); $serverVersion = $this->dbi->getVersion(); $createUserStmt = sprintf('CREATE USER \'%s\'@\'%s\'', $slashedUsername, $slashedHostname); $isMariaDBPwdPluginActive = $this->checkIfMariaDBPwdCheckPluginActive(); // See https://github.com/phpmyadmin/phpmyadmin/pull/11560#issuecomment-147158219 // for details regarding details of syntax usage for various versions // 'IDENTIFIED WITH auth_plugin' // is supported by MySQL 5.5.7+ if (Compatibility::isMySqlOrPerconaDb() && $serverVersion >= 50507 && isset($_POST['authentication_plugin'])) { $createUserStmt .= ' IDENTIFIED WITH ' . $_POST['authentication_plugin']; } // 'IDENTIFIED VIA auth_plugin' // is supported by MariaDB 5.2+ if ( Compatibility::isMariaDb() && $serverVersion >= 50200 && isset($_POST['authentication_plugin']) && ! $isMariaDBPwdPluginActive ) { $createUserStmt .= ' IDENTIFIED VIA ' . $_POST['authentication_plugin']; } $createUserReal = $createUserStmt; $createUserShow = $createUserStmt; $passwordSetStmt = 'SET PASSWORD FOR \'%s\'@\'%s\' = \'%s\''; $passwordSetShow = sprintf($passwordSetStmt, $slashedUsername, $slashedHostname, '***'); $sqlQueryStmt = sprintf( 'GRANT %s ON *.* TO \'%s\'@\'%s\'', implode(', ', $this->extractPrivInfo()), $slashedUsername, $slashedHostname ); $realSqlQuery = $sqlQuery = $sqlQueryStmt; // Set the proper hashing method if (isset($_POST['authentication_plugin'])) { $this->setProperPasswordHashing($_POST['authentication_plugin']); } // Use 'CREATE USER ... WITH ... AS ..' syntax for // newer MySQL versions // and 'CREATE USER ... VIA .. USING ..' syntax for // newer MariaDB versions if ( (Compatibility::isMySqlOrPerconaDb() && $serverVersion >= 50706) || (Compatibility::isMariaDb() && $serverVersion >= 50200) ) { $passwordSetReal = null; // Required for binding '%' with '%s' $createUserStmt = str_replace('%', '%%', $createUserStmt); // MariaDB uses 'USING' whereas MySQL uses 'AS' // but MariaDB with validation plugin needs cleartext password if (Compatibility::isMariaDb() && ! $isMariaDBPwdPluginActive) { $createUserStmt .= ' USING \'%s\''; } elseif (Compatibility::isMariaDb()) { $createUserStmt .= ' IDENTIFIED BY \'%s\''; } elseif (Compatibility::isMySqlOrPerconaDb() && $serverVersion >= 80011) { if (! str_contains($createUserStmt, 'IDENTIFIED')) { // Maybe the authentication_plugin was not posted and then a part is missing $createUserStmt .= ' IDENTIFIED BY \'%s\''; } else { $createUserStmt .= ' BY \'%s\''; } } else { $createUserStmt .= ' AS \'%s\''; } if ($_POST['pred_password'] === 'keep') { $createUserReal = sprintf($createUserStmt, $slashedPassword); $createUserShow = sprintf($createUserStmt, '***'); } elseif ($_POST['pred_password'] === 'none') { $createUserReal = sprintf($createUserStmt, null); $createUserShow = sprintf($createUserStmt, '***'); } else { if ( ! ((Compatibility::isMariaDb() && $isMariaDBPwdPluginActive) || Compatibility::isMySqlOrPerconaDb() && $serverVersion >= 80011) ) { $hashedPassword = $this->getHashedPassword($_POST['pma_pw']); } else { // MariaDB with validation plugin needs cleartext password $hashedPassword = $_POST['pma_pw']; } $createUserReal = sprintf($createUserStmt, $hashedPassword); $createUserShow = sprintf($createUserStmt, '***'); } } else { // Use 'SET PASSWORD' syntax for pre-5.7.6 MySQL versions // and pre-5.2.0 MariaDB versions if ($_POST['pred_password'] === 'keep') { $passwordSetReal = sprintf($passwordSetStmt, $slashedUsername, $slashedHostname, $slashedPassword); } elseif ($_POST['pred_password'] === 'none') { $passwordSetReal = sprintf($passwordSetStmt, $slashedUsername, $slashedHostname, null); } else { $hashedPassword = $this->getHashedPassword($_POST['pma_pw']); $passwordSetReal = sprintf($passwordSetStmt, $slashedUsername, $slashedHostname, $hashedPassword); } } $alterRealSqlQuery = ''; $alterSqlQuery = ''; if (Compatibility::isMySqlOrPerconaDb() && $serverVersion >= 80011) { $sqlQueryStmt = ''; if ( (isset($_POST['Grant_priv']) && $_POST['Grant_priv'] === 'Y') || (isset($GLOBALS['Grant_priv']) && $GLOBALS['Grant_priv'] === 'Y') ) { $sqlQueryStmt = ' WITH GRANT OPTION'; } $realSqlQuery .= $sqlQueryStmt; $sqlQuery .= $sqlQueryStmt; $alterSqlQueryStmt = sprintf('ALTER USER \'%s\'@\'%s\'', $slashedUsername, $slashedHostname); $alterRealSqlQuery = $alterSqlQueryStmt; $alterSqlQuery = $alterSqlQueryStmt; } // add REQUIRE clause $requireClause = $this->getRequireClause(); $withClause = $this->getWithClauseForAddUserAndUpdatePrivs(); if (Compatibility::isMySqlOrPerconaDb() && $serverVersion >= 80011) { $alterRealSqlQuery .= $requireClause; $alterSqlQuery .= $requireClause; $alterRealSqlQuery .= $withClause; $alterSqlQuery .= $withClause; } else { $realSqlQuery .= $requireClause; $sqlQuery .= $requireClause; $realSqlQuery .= $withClause; $sqlQuery .= $withClause; } if ($alterRealSqlQuery !== '') { $alterRealSqlQuery .= ';'; $alterSqlQuery .= ';'; } $createUserReal .= ';'; $createUserShow .= ';'; $realSqlQuery .= ';'; $sqlQuery .= ';'; // No Global GRANT_OPTION privilege if (! $this->dbi->isGrantUser()) { $realSqlQuery = ''; $sqlQuery = ''; } // Use 'SET PASSWORD' for pre-5.7.6 MySQL versions // and pre-5.2.0 MariaDB if ( (Compatibility::isMySqlOrPerconaDb() && $serverVersion >= 50706) || (Compatibility::isMariaDb() && $serverVersion >= 50200) ) { $passwordSetReal = null; $passwordSetShow = null; } else { if ($passwordSetReal !== null) { $passwordSetReal .= ';'; } $passwordSetShow .= ';'; } return [ $createUserReal, $createUserShow, $realSqlQuery, $sqlQuery, $passwordSetReal, $passwordSetShow, $alterRealSqlQuery, $alterSqlQuery, ]; } /** * Returns the type ('PROCEDURE' or 'FUNCTION') of the routine * * @param string $dbname database * @param string $routineName routine * * @return string type */ public function getRoutineType(string $dbname, string $routineName) { $routineData = $this->dbi->getRoutines($dbname); $routineName = mb_strtolower($routineName); foreach ($routineData as $routine) { if (mb_strtolower($routine['name']) === $routineName) { return $routine['type']; } } return ''; } /** * @param string $username User name * @param string $hostname Host name * @param string $database Database name * @param string $routine Routine name * * @return array */ private function getRoutinePrivileges( string $username, string $hostname, string $database, string $routine ): array { $sql = 'SELECT `Proc_priv`' . ' FROM `mysql`.`procs_priv`' . " WHERE `User` = '" . $this->dbi->escapeString($username) . "'" . " AND `Host` = '" . $this->dbi->escapeString($hostname) . "'" . " AND `Db` = '" . $this->dbi->escapeString(Util::unescapeMysqlWildcards($database)) . "'" . " AND `Routine_name` LIKE '" . $this->dbi->escapeString($routine) . "';"; $privileges = $this->dbi->fetchValue($sql); if ($privileges === false) { $privileges = ''; } return $this->parseProcPriv($privileges); } public function getFormForChangePassword(string $username, string $hostname, bool $editOthers): string { global $route; $isPrivileges = $route === '/server/privileges'; $serverVersion = $this->dbi->getVersion(); $origAuthPlugin = $this->getCurrentAuthenticationPlugin('change', $username, $hostname); $isNew = (Compatibility::isMySqlOrPerconaDb() && $serverVersion >= 50507) || (Compatibility::isMariaDb() && $serverVersion >= 50200); $hasMoreAuthPlugins = (Compatibility::isMySqlOrPerconaDb() && $serverVersion >= 50706) || ($this->dbi->isSuperUser() && $editOthers); $activeAuthPlugins = ['mysql_native_password' => __('Native MySQL authentication')]; if ($isNew && $hasMoreAuthPlugins) { $activeAuthPlugins = $this->plugins->getAuthentication(); if (isset($activeAuthPlugins['mysql_old_password'])) { unset($activeAuthPlugins['mysql_old_password']); } } return $this->template->render('server/privileges/change_password', [ 'username' => $username, 'hostname' => $hostname, 'is_privileges' => $isPrivileges, 'is_new' => $isNew, 'has_more_auth_plugins' => $hasMoreAuthPlugins, 'active_auth_plugins' => $activeAuthPlugins, 'orig_auth_plugin' => $origAuthPlugin, ]); } /** * @see https://dev.mysql.com/doc/refman/en/account-locking.html * @see https://mariadb.com/kb/en/account-locking/ * * @return array|null */ private function getUserPrivileges(string $user, string $host, bool $hasAccountLocking): ?array { $query = 'SELECT * FROM `mysql`.`user` WHERE `User` = ? AND `Host` = ?;'; /** @var mysqli_stmt|false $statement */ $statement = $this->dbi->prepare($query); if ($statement === false || ! $statement->bind_param('ss', $user, $host) || ! $statement->execute()) { return null; } $result = new MysqliResult($statement->get_result()); /** @var array|null $userPrivileges */ $userPrivileges = $result->fetchAssoc(); if ($userPrivileges === []) { return null; } if (! $hasAccountLocking || ! $this->dbi->isMariaDB()) { return $userPrivileges; } $userPrivileges['account_locked'] = 'N'; $query = 'SELECT * FROM `mysql`.`global_priv` WHERE `User` = ? AND `Host` = ?;'; /** @var mysqli_stmt|false $statement */ $statement = $this->dbi->prepare($query); if ($statement === false || ! $statement->bind_param('ss', $user, $host) || ! $statement->execute()) { return $userPrivileges; } $result = new MysqliResult($statement->get_result()); /** @var array|null $globalPrivileges */ $globalPrivileges = $result->fetchAssoc(); if ($globalPrivileges === []) { return $userPrivileges; } $privileges = json_decode($globalPrivileges['Priv'] ?? '[]', true); if (! is_array($privileges)) { return $userPrivileges; } if (isset($privileges['account_locked']) && $privileges['account_locked']) { $userPrivileges['account_locked'] = 'Y'; } return $userPrivileges; } }