aboutsummaryrefslogtreecommitdiff
path: root/srcs/phpmyadmin/libraries/classes/Server/Privileges.php
diff options
context:
space:
mode:
Diffstat (limited to 'srcs/phpmyadmin/libraries/classes/Server/Privileges.php')
-rw-r--r--srcs/phpmyadmin/libraries/classes/Server/Privileges.php5649
1 files changed, 5649 insertions, 0 deletions
diff --git a/srcs/phpmyadmin/libraries/classes/Server/Privileges.php b/srcs/phpmyadmin/libraries/classes/Server/Privileges.php
new file mode 100644
index 0000000..1e50fbb
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Server/Privileges.php
@@ -0,0 +1,5649 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * set of functions with the Privileges section in pma
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Server;
+
+use PhpMyAdmin\Core;
+use PhpMyAdmin\DatabaseInterface;
+use PhpMyAdmin\Display\ChangePassword;
+use PhpMyAdmin\Message;
+use PhpMyAdmin\Relation;
+use PhpMyAdmin\RelationCleanup;
+use PhpMyAdmin\Response;
+use PhpMyAdmin\Template;
+use PhpMyAdmin\Url;
+use PhpMyAdmin\Util;
+
+/**
+ * Privileges class
+ *
+ * @package PhpMyAdmin
+ */
+class Privileges
+{
+ /**
+ * @var Template
+ */
+ public $template;
+
+ /**
+ * @var RelationCleanup
+ */
+ private $relationCleanup;
+
+ /**
+ * @var DatabaseInterface
+ */
+ public $dbi;
+
+ /**
+ * @var Relation
+ */
+ public $relation;
+
+ /**
+ * Privileges constructor.
+ *
+ * @param Template $template Template object
+ * @param DatabaseInterface $dbi DatabaseInterface object
+ * @param Relation $relation Relation object
+ * @param RelationCleanup $relationCleanup RelationCleanup object
+ */
+ public function __construct(
+ Template $template,
+ $dbi,
+ Relation $relation,
+ RelationCleanup $relationCleanup
+ ) {
+ $this->template = $template;
+ $this->dbi = $dbi;
+ $this->relation = $relation;
+ $this->relationCleanup = $relationCleanup;
+ }
+
+ /**
+ * Get Html for User Group Dialog
+ *
+ * @param string $username username
+ * @param bool $is_menuswork Is menuswork set in configuration
+ *
+ * @return string html
+ */
+ public function getHtmlForUserGroupDialog($username, $is_menuswork)
+ {
+ $html = '';
+ if (! empty($_GET['edit_user_group_dialog']) && $is_menuswork) {
+ $dialog = $this->getHtmlToChooseUserGroup($username);
+ $response = Response::getInstance();
+ if ($response->isAjax()) {
+ $response->addJSON('message', $dialog);
+ exit;
+ } else {
+ $html .= $dialog;
+ }
+ }
+
+ return $html;
+ }
+
+ /**
+ * Escapes wildcard in a database+table specification
+ * before using it in a GRANT statement.
+ *
+ * Escaping a wildcard character in a GRANT is only accepted at the global
+ * or database level, not at table level; this is why I remove
+ * the escaping character. Internally, in mysql.tables_priv.Db there are
+ * no escaping (for example test_db) but in mysql.db you'll see test\_db
+ * for a db-specific privilege.
+ *
+ * @param string $dbname Database name
+ * @param string $tablename Table name
+ *
+ * @return string the escaped (if necessary) database.table
+ */
+ public function wildcardEscapeForGrant($dbname, $tablename)
+ {
+ if (strlen($dbname) === 0) {
+ $db_and_table = '*.*';
+ } else {
+ if (strlen($tablename) > 0) {
+ $db_and_table = Util::backquote(
+ Util::unescapeMysqlWildcards($dbname)
+ )
+ . '.' . Util::backquote($tablename);
+ } else {
+ $db_and_table = Util::backquote($dbname) . '.*';
+ }
+ }
+ return $db_and_table;
+ }
+
+ /**
+ * Generates a condition on the user name
+ *
+ * @param string $initial the user's initial
+ *
+ * @return string the generated condition
+ */
+ public function rangeOfUsers($initial = '')
+ {
+ // strtolower() is used because the User field
+ // might be BINARY, so LIKE would be case sensitive
+ if ($initial === null || $initial === '') {
+ return '';
+ }
+
+ $ret = " WHERE `User` LIKE '"
+ . $this->dbi->escapeString($initial) . "%'"
+ . " OR `User` LIKE '"
+ . $this->dbi->escapeString(mb_strtolower($initial))
+ . "%'";
+ return $ret;
+ } // end function
+
+ /**
+ * Formats privilege name for a display
+ *
+ * @param array $privilege Privilege information
+ * @param boolean $html Whether to use HTML
+ *
+ * @return string
+ */
+ public function formatPrivilege(array $privilege, $html)
+ {
+ if ($html) {
+ return '<dfn title="' . $privilege[2] . '">'
+ . $privilege[1] . '</dfn>';
+ }
+
+ return $privilege[1];
+ }
+
+ /**
+ * Parses privileges into an array, it modifies the array
+ *
+ * @param array $row Results row from
+ *
+ * @return void
+ */
+ public function fillInTablePrivileges(array &$row)
+ {
+ $row1 = $this->dbi->fetchSingleRow(
+ 'SHOW COLUMNS FROM `mysql`.`tables_priv` LIKE \'Table_priv\';',
+ 'ASSOC'
+ );
+ // note: in MySQL 5.0.3 we get "Create View', 'Show view';
+ // the View for Create is spelled with uppercase V
+ // the view for Show is spelled with lowercase v
+ // and there is a space between the words
+
+ $av_grants = explode(
+ '\',\'',
+ mb_substr(
+ $row1['Type'],
+ mb_strpos($row1['Type'], '(') + 2,
+ mb_strpos($row1['Type'], ')')
+ - mb_strpos($row1['Type'], '(') - 3
+ )
+ );
+
+ $users_grants = explode(',', $row['Table_priv']);
+
+ foreach ($av_grants as $current_grant) {
+ $row[$current_grant . '_priv']
+ = in_array($current_grant, $users_grants) ? 'Y' : 'N';
+ }
+ unset($row['Table_priv']);
+ }
+
+
+ /**
+ * Extracts the privilege information of a priv table row
+ *
+ * @param array|null $row the row
+ * @param boolean $enableHTML add <dfn> tag with tooltips
+ * @param boolean $tablePrivs whether row contains table privileges
+ *
+ * @global resource $user_link the database connection
+ *
+ * @return array
+ */
+ public function extractPrivInfo($row = null, $enableHTML = false, $tablePrivs = false)
+ {
+ if ($tablePrivs) {
+ $grants = $this->getTableGrantsArray();
+ } else {
+ $grants = $this->getGrantsArray();
+ }
+
+ if ($row !== null && isset($row['Table_priv'])) {
+ $this->fillInTablePrivileges($row);
+ }
+
+ $privs = [];
+ $allPrivileges = true;
+ foreach ($grants as $current_grant) {
+ if (($row !== null && isset($row[$current_grant[0]]))
+ || ($row === null && isset($GLOBALS[$current_grant[0]]))
+ ) {
+ if (($row !== null && $row[$current_grant[0]] == 'Y')
+ || ($row === null
+ && ($GLOBALS[$current_grant[0]] == 'Y'
+ || (is_array($GLOBALS[$current_grant[0]])
+ && count($GLOBALS[$current_grant[0]]) == $_REQUEST['column_count']
+ && empty($GLOBALS[$current_grant[0] . '_none']))))
+ ) {
+ $privs[] = $this->formatPrivilege($current_grant, $enableHTML);
+ } elseif (! empty($GLOBALS[$current_grant[0]])
+ && is_array($GLOBALS[$current_grant[0]])
+ && empty($GLOBALS[$current_grant[0] . '_none'])
+ ) {
+ // Required for proper escaping of ` (backtick) in a column name
+ $grant_cols = array_map(
+ function ($val) {
+ return Util::backquote($val);
+ },
+ $GLOBALS[$current_grant[0]]
+ );
+
+ $privs[] = $this->formatPrivilege($current_grant, $enableHTML)
+ . ' (' . implode(', ', $grant_cols) . ')';
+ } else {
+ $allPrivileges = false;
+ }
+ }
+ }
+ if (empty($privs)) {
+ if ($enableHTML) {
+ $privs[] = '<dfn title="' . __('No privileges.') . '">USAGE</dfn>';
+ } else {
+ $privs[] = 'USAGE';
+ }
+ } elseif ($allPrivileges
+ && (! isset($_POST['grant_count']) || count($privs) == $_POST['grant_count'])
+ ) {
+ if ($enableHTML) {
+ $privs = ['<dfn title="'
+ . __('Includes all privileges except GRANT.')
+ . '">ALL PRIVILEGES</dfn>',
+ ];
+ } else {
+ $privs = ['ALL PRIVILEGES'];
+ }
+ }
+ return $privs;
+ }
+
+ /**
+ * Returns an array of table grants and their descriptions
+ *
+ * @return array array of table grants
+ */
+ public function getTableGrantsArray()
+ {
+ return [
+ [
+ 'Delete',
+ 'DELETE',
+ $GLOBALS['strPrivDescDelete'],
+ ],
+ [
+ 'Create',
+ 'CREATE',
+ $GLOBALS['strPrivDescCreateTbl'],
+ ],
+ [
+ 'Drop',
+ 'DROP',
+ $GLOBALS['strPrivDescDropTbl'],
+ ],
+ [
+ 'Index',
+ 'INDEX',
+ $GLOBALS['strPrivDescIndex'],
+ ],
+ [
+ 'Alter',
+ 'ALTER',
+ $GLOBALS['strPrivDescAlter'],
+ ],
+ [
+ 'Create View',
+ 'CREATE_VIEW',
+ $GLOBALS['strPrivDescCreateView'],
+ ],
+ [
+ 'Show view',
+ 'SHOW_VIEW',
+ $GLOBALS['strPrivDescShowView'],
+ ],
+ [
+ 'Trigger',
+ 'TRIGGER',
+ $GLOBALS['strPrivDescTrigger'],
+ ],
+ ];
+ }
+
+ /**
+ * Get the grants array which contains all the privilege types
+ * and relevant grant messages
+ *
+ * @return array
+ */
+ public function getGrantsArray()
+ {
+ return [
+ [
+ 'Select_priv',
+ 'SELECT',
+ __('Allows reading data.'),
+ ],
+ [
+ 'Insert_priv',
+ 'INSERT',
+ __('Allows inserting and replacing data.'),
+ ],
+ [
+ 'Update_priv',
+ 'UPDATE',
+ __('Allows changing data.'),
+ ],
+ [
+ 'Delete_priv',
+ 'DELETE',
+ __('Allows deleting data.'),
+ ],
+ [
+ 'Create_priv',
+ 'CREATE',
+ __('Allows creating new databases and tables.'),
+ ],
+ [
+ 'Drop_priv',
+ 'DROP',
+ __('Allows dropping databases and tables.'),
+ ],
+ [
+ 'Reload_priv',
+ 'RELOAD',
+ __('Allows reloading server settings and flushing the server\'s caches.'),
+ ],
+ [
+ 'Shutdown_priv',
+ 'SHUTDOWN',
+ __('Allows shutting down the server.'),
+ ],
+ [
+ 'Process_priv',
+ 'PROCESS',
+ __('Allows viewing processes of all users.'),
+ ],
+ [
+ 'File_priv',
+ 'FILE',
+ __('Allows importing data from and exporting data into files.'),
+ ],
+ [
+ 'References_priv',
+ 'REFERENCES',
+ __('Has no effect in this MySQL version.'),
+ ],
+ [
+ 'Index_priv',
+ 'INDEX',
+ __('Allows creating and dropping indexes.'),
+ ],
+ [
+ 'Alter_priv',
+ 'ALTER',
+ __('Allows altering the structure of existing tables.'),
+ ],
+ [
+ 'Show_db_priv',
+ 'SHOW DATABASES',
+ __('Gives access to the complete list of databases.'),
+ ],
+ [
+ 'Super_priv',
+ 'SUPER',
+ __(
+ 'Allows connecting, even if maximum number of connections '
+ . 'is reached; required for most administrative operations '
+ . 'like setting global variables or killing threads of other users.'
+ ),
+ ],
+ [
+ 'Create_tmp_table_priv',
+ 'CREATE TEMPORARY TABLES',
+ __('Allows creating temporary tables.'),
+ ],
+ [
+ 'Lock_tables_priv',
+ 'LOCK TABLES',
+ __('Allows locking tables for the current thread.'),
+ ],
+ [
+ 'Repl_slave_priv',
+ 'REPLICATION SLAVE',
+ __('Needed for the replication slaves.'),
+ ],
+ [
+ 'Repl_client_priv',
+ 'REPLICATION CLIENT',
+ __('Allows the user to ask where the slaves / masters are.'),
+ ],
+ [
+ 'Create_view_priv',
+ 'CREATE VIEW',
+ __('Allows creating new views.'),
+ ],
+ [
+ 'Event_priv',
+ 'EVENT',
+ __('Allows to set up events for the event scheduler.'),
+ ],
+ [
+ 'Trigger_priv',
+ 'TRIGGER',
+ __('Allows creating and dropping triggers.'),
+ ],
+ // for table privs:
+ [
+ 'Create View_priv',
+ 'CREATE VIEW',
+ __('Allows creating new views.'),
+ ],
+ [
+ 'Show_view_priv',
+ 'SHOW VIEW',
+ __('Allows performing SHOW CREATE VIEW queries.'),
+ ],
+ // for table privs:
+ [
+ 'Show view_priv',
+ 'SHOW VIEW',
+ __('Allows performing SHOW CREATE VIEW queries.'),
+ ],
+ [
+ 'Delete_history_priv',
+ 'DELETE HISTORY',
+ $GLOBALS['strPrivDescDeleteHistoricalRows'],
+ ],
+ [
+ 'Delete versioning rows_priv',
+ 'DELETE HISTORY',
+ $GLOBALS['strPrivDescDeleteHistoricalRows'],
+ ],
+ [
+ 'Create_routine_priv',
+ 'CREATE ROUTINE',
+ __('Allows creating stored routines.'),
+ ],
+ [
+ 'Alter_routine_priv',
+ 'ALTER ROUTINE',
+ __('Allows altering and dropping stored routines.'),
+ ],
+ [
+ 'Create_user_priv',
+ 'CREATE USER',
+ __('Allows creating, dropping and renaming user accounts.'),
+ ],
+ [
+ 'Execute_priv',
+ 'EXECUTE',
+ __('Allows executing stored routines.'),
+ ],
+ ];
+ }
+
+ /**
+ * Displays on which column(s) a table-specific privilege is granted
+ *
+ * @param array $columns columns array
+ * @param array $row first row from result or boolean false
+ * @param string $name_for_select privilege types - Select_priv, Insert_priv
+ * Update_priv, References_priv
+ * @param string $priv_for_header privilege for header
+ * @param string $name privilege name: insert, select, update, references
+ * @param string $name_for_dfn name for dfn
+ * @param string $name_for_current name for current
+ *
+ * @return string html snippet
+ */
+ public function getHtmlForColumnPrivileges(
+ array $columns,
+ array $row,
+ $name_for_select,
+ $priv_for_header,
+ $name,
+ $name_for_dfn,
+ $name_for_current
+ ) {
+ return $this->template->render('server/privileges/column_privileges', [
+ 'columns' => $columns,
+ 'row' => $row,
+ 'name_for_select' => $name_for_select,
+ 'priv_for_header' => $priv_for_header,
+ 'name' => $name,
+ 'name_for_dfn' => $name_for_dfn,
+ 'name_for_current' => $name_for_current,
+ ]);
+ }
+
+ /**
+ * Get sql query for display privileges table
+ *
+ * @param string $db the database
+ * @param string $table the table
+ * @param string $username username for database connection
+ * @param string $hostname hostname for database connection
+ *
+ * @return string sql query
+ */
+ public function getSqlQueryForDisplayPrivTable($db, $table, $username, $hostname)
+ {
+ if ($db == '*') {
+ return "SELECT * FROM `mysql`.`user`"
+ . " WHERE `User` = '" . $this->dbi->escapeString($username) . "'"
+ . " AND `Host` = '" . $this->dbi->escapeString($hostname) . "';";
+ } elseif ($table == '*') {
+ return "SELECT * FROM `mysql`.`db`"
+ . " WHERE `User` = '" . $this->dbi->escapeString($username) . "'"
+ . " AND `Host` = '" . $this->dbi->escapeString($hostname) . "'"
+ . " AND '" . $this->dbi->escapeString(Util::unescapeMysqlWildcards($db)) . "'"
+ . " LIKE `Db`;";
+ }
+ return "SELECT `Table_priv`"
+ . " FROM `mysql`.`tables_priv`"
+ . " WHERE `User` = '" . $this->dbi->escapeString($username) . "'"
+ . " AND `Host` = '" . $this->dbi->escapeString($hostname) . "'"
+ . " AND `Db` = '" . $this->dbi->escapeString(Util::unescapeMysqlWildcards($db)) . "'"
+ . " AND `Table_name` = '" . $this->dbi->escapeString($table) . "';";
+ }
+
+ /**
+ * Displays a dropdown to select the user group
+ * with menu items configured to each of them.
+ *
+ * @param string $username username
+ *
+ * @return string html to select the user group
+ */
+ public function getHtmlToChooseUserGroup($username)
+ {
+ $cfgRelation = $this->relation->getRelationsParam();
+ $groupTable = Util::backquote($cfgRelation['db'])
+ . "." . Util::backquote($cfgRelation['usergroups']);
+ $userTable = Util::backquote($cfgRelation['db'])
+ . "." . Util::backquote($cfgRelation['users']);
+
+ $userGroup = '';
+ if (isset($GLOBALS['username'])) {
+ $sql_query = "SELECT `usergroup` FROM " . $userTable
+ . " WHERE `username` = '" . $this->dbi->escapeString($username) . "'";
+ $userGroup = $this->dbi->fetchValue(
+ $sql_query,
+ 0,
+ 0,
+ DatabaseInterface::CONNECT_CONTROL
+ );
+ }
+
+ $allUserGroups = ['' => ''];
+ $sql_query = "SELECT DISTINCT `usergroup` FROM " . $groupTable;
+ $result = $this->relation->queryAsControlUser($sql_query, false);
+ if ($result) {
+ while ($row = $this->dbi->fetchRow($result)) {
+ $allUserGroups[$row[0]] = $row[0];
+ }
+ }
+ $this->dbi->freeResult($result);
+
+ return $this->template->render('server/privileges/choose_user_group', [
+ 'all_user_groups' => $allUserGroups,
+ 'user_group' => $userGroup,
+ 'params' => ['username' => $username],
+ ]);
+ }
+
+ /**
+ * Sets the user group from request values
+ *
+ * @param string $username username
+ * @param string $userGroup user group to set
+ *
+ * @return void
+ */
+ public function setUserGroup($username, $userGroup)
+ {
+ $userGroup = $userGroup === null ? '' : $userGroup;
+ $cfgRelation = $this->relation->getRelationsParam();
+ if (empty($cfgRelation['db']) || empty($cfgRelation['users']) || empty($cfgRelation['usergroups'])) {
+ return;
+ }
+
+ $userTable = Util::backquote($cfgRelation['db'])
+ . "." . Util::backquote($cfgRelation['users']);
+
+ $sql_query = "SELECT `usergroup` FROM " . $userTable
+ . " WHERE `username` = '" . $this->dbi->escapeString($username) . "'";
+ $oldUserGroup = $this->dbi->fetchValue(
+ $sql_query,
+ 0,
+ 0,
+ DatabaseInterface::CONNECT_CONTROL
+ );
+
+ if ($oldUserGroup === false) {
+ $upd_query = "INSERT INTO " . $userTable . "(`username`, `usergroup`)"
+ . " VALUES ('" . $this->dbi->escapeString($username) . "', "
+ . "'" . $this->dbi->escapeString($userGroup) . "')";
+ } else {
+ if (empty($userGroup)) {
+ $upd_query = "DELETE FROM " . $userTable
+ . " WHERE `username`='" . $this->dbi->escapeString($username) . "'";
+ } elseif ($oldUserGroup != $userGroup) {
+ $upd_query = "UPDATE " . $userTable
+ . " SET `usergroup`='" . $this->dbi->escapeString($userGroup) . "'"
+ . " WHERE `username`='" . $this->dbi->escapeString($username) . "'";
+ }
+ }
+ if (isset($upd_query)) {
+ $this->relation->queryAsControlUser($upd_query);
+ }
+ }
+
+ /**
+ * Displays the privileges form table
+ *
+ * @param string $db the database
+ * @param string $table the table
+ * @param boolean $submit whether to display the submit button or not
+ *
+ * @global array $cfg the phpMyAdmin configuration
+ * @global resource $user_link the database connection
+ *
+ * @return string html snippet
+ */
+ public function getHtmlToDisplayPrivilegesTable(
+ $db = '*',
+ $table = '*',
+ $submit = true
+ ) {
+ $html_output = '';
+ $sql_query = '';
+
+ if ($db == '*') {
+ $table = '*';
+ }
+ $username = '';
+ $hostname = '';
+ if (isset($GLOBALS['username'])) {
+ $username = $GLOBALS['username'];
+ $hostname = $GLOBALS['hostname'];
+ $sql_query = $this->getSqlQueryForDisplayPrivTable(
+ $db,
+ $table,
+ $username,
+ $hostname
+ );
+ $row = $this->dbi->fetchSingleRow($sql_query);
+ }
+ if (empty($row)) {
+ if ($table == '*' && $this->dbi->isSuperuser()) {
+ $row = [];
+ if ($db == '*') {
+ $sql_query = 'SHOW COLUMNS FROM `mysql`.`user`;';
+ } elseif ($table == '*') {
+ $sql_query = 'SHOW COLUMNS FROM `mysql`.`db`;';
+ }
+ $res = $this->dbi->query($sql_query);
+ while ($row1 = $this->dbi->fetchRow($res)) {
+ if (mb_substr($row1[0], 0, 4) == 'max_') {
+ $row[$row1[0]] = 0;
+ } elseif (mb_substr($row1[0], 0, 5) == 'x509_'
+ || mb_substr($row1[0], 0, 4) == 'ssl_'
+ ) {
+ $row[$row1[0]] = '';
+ } else {
+ $row[$row1[0]] = 'N';
+ }
+ }
+ $this->dbi->freeResult($res);
+ } elseif ($table == '*') {
+ $row = [];
+ } else {
+ $row = ['Table_priv' => ''];
+ }
+ }
+ if (isset($row['Table_priv'])) {
+ $this->fillInTablePrivileges($row);
+
+ // get columns
+ $res = $this->dbi->tryQuery(
+ 'SHOW COLUMNS FROM '
+ . Util::backquote(
+ Util::unescapeMysqlWildcards($db)
+ )
+ . '.' . Util::backquote($table) . ';'
+ );
+ $columns = [];
+ if ($res) {
+ while ($row1 = $this->dbi->fetchRow($res)) {
+ $columns[$row1[0]] = [
+ 'Select' => false,
+ 'Insert' => false,
+ 'Update' => false,
+ 'References' => false,
+ ];
+ }
+ $this->dbi->freeResult($res);
+ }
+ unset($res, $row1);
+ }
+ // table-specific privileges
+ if (! empty($columns)) {
+ $html_output .= $this->getHtmlForTableSpecificPrivileges(
+ $username,
+ $hostname,
+ $db,
+ $table,
+ $columns,
+ $row
+ );
+ } else {
+ // global or db-specific
+ $html_output .= $this->getHtmlForGlobalOrDbSpecificPrivs($db, $table, $row);
+ }
+ $html_output .= '</fieldset>' . "\n";
+ if ($submit) {
+ $html_output .= '<fieldset id="fieldset_user_privtable_footer" '
+ . 'class="tblFooters">' . "\n"
+ . '<input type="hidden" name="update_privs" value="1">' . "\n"
+ . '<input class="btn btn-primary" type="submit" value="' . __('Go') . '">' . "\n"
+ . '</fieldset>' . "\n";
+ }
+ return $html_output;
+ } // end of the 'PMA_displayPrivTable()' function
+
+ /**
+ * Get HTML for "Require"
+ *
+ * @param array $row privilege array
+ *
+ * @return string html snippet
+ */
+ public function getHtmlForRequires(array $row)
+ {
+ $specified = (isset($row['ssl_type']) && $row['ssl_type'] == 'SPECIFIED');
+ $require_options = [
+ [
+ 'name' => 'ssl_type',
+ 'value' => 'NONE',
+ 'description' => __(
+ 'Does not require SSL-encrypted connections.'
+ ),
+ 'label' => 'REQUIRE NONE',
+ 'checked' => isset($row['ssl_type'])
+ && ($row['ssl_type'] == 'NONE'
+ || $row['ssl_type'] == '')
+ ? 'checked="checked"'
+ : '',
+ 'disabled' => false,
+ 'radio' => true,
+ ],
+ [
+ 'name' => 'ssl_type',
+ 'value' => 'ANY',
+ 'description' => __(
+ 'Requires SSL-encrypted connections.'
+ ),
+ 'label' => 'REQUIRE SSL',
+ 'checked' => isset($row['ssl_type']) && ($row['ssl_type'] == 'ANY')
+ ? 'checked="checked"'
+ : '',
+ 'disabled' => false,
+ 'radio' => true,
+ ],
+ [
+ 'name' => 'ssl_type',
+ 'value' => 'X509',
+ 'description' => __(
+ 'Requires a valid X509 certificate.'
+ ),
+ 'label' => 'REQUIRE X509',
+ 'checked' => isset($row['ssl_type']) && ($row['ssl_type'] == 'X509')
+ ? 'checked="checked"'
+ : '',
+ 'disabled' => false,
+ 'radio' => true,
+ ],
+ [
+ 'name' => 'ssl_type',
+ 'value' => 'SPECIFIED',
+ 'description' => '',
+ 'label' => 'SPECIFIED',
+ 'checked' => $specified ? 'checked="checked"' : '',
+ 'disabled' => false,
+ 'radio' => true,
+ ],
+ [
+ 'name' => 'ssl_cipher',
+ 'value' => isset($row['ssl_cipher'])
+ ? htmlspecialchars($row['ssl_cipher']) : '',
+ 'description' => __(
+ 'Requires that a specific cipher method be used for a connection.'
+ ),
+ 'label' => 'REQUIRE CIPHER',
+ 'checked' => '',
+ 'disabled' => ! $specified,
+ 'radio' => false,
+ ],
+ [
+ 'name' => 'x509_issuer',
+ 'value' => isset($row['x509_issuer'])
+ ? htmlspecialchars($row['x509_issuer']) : '',
+ 'description' => __(
+ 'Requires that a valid X509 certificate issued by this CA be presented.'
+ ),
+ 'label' => 'REQUIRE ISSUER',
+ 'checked' => '',
+ 'disabled' => ! $specified,
+ 'radio' => false,
+ ],
+ [
+ 'name' => 'x509_subject',
+ 'value' => isset($row['x509_subject'])
+ ? htmlspecialchars($row['x509_subject']) : '',
+ 'description' => __(
+ 'Requires that a valid X509 certificate with this subject be presented.'
+ ),
+ 'label' => 'REQUIRE SUBJECT',
+ 'checked' => '',
+ 'disabled' => ! $specified,
+ 'radio' => false,
+ ],
+ ];
+
+ return $this->template->render('server/privileges/require_options', [
+ 'require_options' => $require_options,
+ ]);
+ }
+
+ /**
+ * Get HTML for "Resource limits"
+ *
+ * @param array $row first row from result or boolean false
+ *
+ * @return string html snippet
+ */
+ public function getHtmlForResourceLimits(array $row)
+ {
+ $limits = [
+ [
+ 'input_name' => 'max_questions',
+ 'name_main' => 'MAX QUERIES PER HOUR',
+ 'value' => isset($row['max_questions']) ? $row['max_questions'] : '0',
+ 'description' => __(
+ 'Limits the number of queries the user may send to the server per hour.'
+ ),
+ ],
+ [
+ 'input_name' => 'max_updates',
+ 'name_main' => 'MAX UPDATES PER HOUR',
+ 'value' => isset($row['max_updates']) ? $row['max_updates'] : '0',
+ 'description' => __(
+ 'Limits the number of commands that change any table '
+ . 'or database the user may execute per hour.'
+ ),
+ ],
+ [
+ 'input_name' => 'max_connections',
+ 'name_main' => 'MAX CONNECTIONS PER HOUR',
+ 'value' => isset($row['max_connections']) ? $row['max_connections'] : '0',
+ 'description' => __(
+ 'Limits the number of new connections the user may open per hour.'
+ ),
+ ],
+ [
+ 'input_name' => 'max_user_connections',
+ 'name_main' => 'MAX USER_CONNECTIONS',
+ 'value' => isset($row['max_user_connections']) ?
+ $row['max_user_connections'] : '0',
+ 'description' => __(
+ 'Limits the number of simultaneous connections '
+ . 'the user may have.'
+ ),
+ ],
+ ];
+
+ return $this->template->render('server/privileges/resource_limits', [
+ 'limits' => $limits,
+ ]);
+ }
+
+ /**
+ * Get the HTML snippet for routine specific privileges
+ *
+ * @param string $username username for database connection
+ * @param string $hostname hostname for database connection
+ * @param string $db the database
+ * @param string $routine the routine
+ * @param string $url_dbname url encoded db name
+ *
+ * @return string
+ */
+ public function getHtmlForRoutineSpecificPrivileges(
+ $username,
+ $hostname,
+ $db,
+ $routine,
+ $url_dbname
+ ) {
+ $header = $this->getHtmlHeaderForUserProperties(
+ false,
+ $url_dbname,
+ $db,
+ $username,
+ $hostname,
+ $routine,
+ 'routine'
+ );
+
+ $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($db)) . "'"
+ . " AND `Routine_name` LIKE '" . $this->dbi->escapeString($routine) . "';";
+ $res = $this->dbi->fetchValue($sql);
+
+ $privs = $this->parseProcPriv($res);
+
+ $routineArray = [$this->getTriggerPrivilegeTable()];
+ $privTableNames = [__('Routine')];
+ $privCheckboxes = $this->getHtmlForGlobalPrivTableWithCheckboxes(
+ $routineArray,
+ $privTableNames,
+ $privs
+ );
+
+ return $this->template->render('server/privileges/edit_routine_privileges', [
+ 'username' => $username,
+ 'hostname' => $hostname,
+ 'database' => $db,
+ 'routine' => $routine,
+ 'grant_count' => count($privs),
+ 'priv_checkboxes' => $privCheckboxes,
+ 'header' => $header,
+ ]);
+ }
+
+ /**
+ * Get routine privilege table as an array
+ *
+ * @return array privilege type array
+ */
+ public function getTriggerPrivilegeTable()
+ {
+ $routinePrivTable = [
+ [
+ 'Grant',
+ 'GRANT',
+ __(
+ 'Allows user to give to other users or remove from other users '
+ . 'privileges that user possess on this routine.'
+ ),
+ ],
+ [
+ 'Alter_routine',
+ 'ALTER ROUTINE',
+ __('Allows altering and dropping this routine.'),
+ ],
+ [
+ 'Execute',
+ 'EXECUTE',
+ __('Allows executing this routine.'),
+ ],
+ ];
+ return $routinePrivTable;
+ }
+
+ /**
+ * Get the HTML snippet for table specific privileges
+ *
+ * @param string $username username for database c