diff --git a/ChangeLog b/ChangeLog index a79e19f98a..d93d1dcff9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -25,6 +25,7 @@ phpMyAdmin - ChangeLog - [core] Dropped support for PHP 5.2. + rfe #487 and rfe #1405 Find and Replacing column wise + rfe #1373 Use same create view dialog for editing a view ++ rfe #316 Configurable menus; allow user groups with customized menus per group 4.0.5.0 (not yet released) - bug #3977 Not detected configuration storage diff --git a/config.sample.inc.php b/config.sample.inc.php index 37a2f099b5..7c40fb40e3 100644 --- a/config.sample.inc.php +++ b/config.sample.inc.php @@ -59,6 +59,8 @@ $cfg['Servers'][$i]['AllowNoPassword'] = false; // $cfg['Servers'][$i]['designer_coords'] = 'pma__designer_coords'; // $cfg['Servers'][$i]['userconfig'] = 'pma__userconfig'; // $cfg['Servers'][$i]['recent'] = 'pma__recent'; +// $cfg['Servers'][$i]['users'] = 'pma__users'; +// $cfg['Servers'][$i]['usergroups'] = 'pma__usergroups'; /* Contrib / Swekey authentication */ // $cfg['Servers'][$i]['auth_swekey_config'] = '/etc/swekey-pma.conf'; diff --git a/doc/config.rst b/doc/config.rst index 5b9657c8c7..e97b6596ba 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -110,7 +110,7 @@ Basic settings column names match with words which are MySQL reserved. If you want to turn off this warning, you can set it to ``true`` and - warning will not longer be displayed + warning will no longer be displayed. .. config:option:: $cfg['TranslationWarningThreshold'] @@ -649,6 +649,29 @@ Server connection settings * put the table name in :config:option:`$cfg['Servers'][$i]['table\_uiprefs']` (e.g. ``pma__table_uiprefs``) +.. _configurablemenus: +.. config:option:: $cfg['Servers'][$i]['users'] + + :type: string + :default: ``''`` + +.. config:option:: $cfg['Servers'][$i]['usergroups'] + + :type: string + :default: ``''`` + + Since release 4.1.0 you can create different user groups with menu items + attached to them. Users can be assigned to these groups and the logged in + user would only see menu items configured to the usergroup he is assigned to. + To do this it needs two tables "usergroups" (storing allowed menu items for each + user group) and "users" (storing users and their assignments to user groups). + + To allow the usage of this functionality: + + * set up :config:option:`$cfg['Servers'][$i]['pmadb']` and the phpMyAdmin configuration storage + * put the correct table names in + :config:option:`$cfg['Servers'][$i]['users']` (e.g. ``pma__users``) and + :config:option:`$cfg['Servers'][$i]['usergroups']` (e.g. ``pma__usergroups``) .. _tracking: .. config:option:: $cfg['Servers'][$i]['tracking'] diff --git a/examples/config.manyhosts.inc.php b/examples/config.manyhosts.inc.php index 1395ecc3d0..b9165355ee 100644 --- a/examples/config.manyhosts.inc.php +++ b/examples/config.manyhosts.inc.php @@ -45,4 +45,6 @@ foreach ($hosts as $host) { $cfg['Servers'][$i]['designer_coords'] = 'pma__designer_coords'; $cfg['Servers'][$i]['userconfig'] = 'pma__userconfig'; $cfg['Servers'][$i]['recent'] = 'pma__recent'; + $cfg['Servers'][$i]['users'] = 'pma__users'; + $cfg['Servers'][$i]['usergroups'] = 'pma__usergroups'; } diff --git a/examples/create_tables.sql b/examples/create_tables.sql index 723334c776..ae2c81408c 100644 --- a/examples/create_tables.sql +++ b/examples/create_tables.sql @@ -239,3 +239,66 @@ CREATE TABLE IF NOT EXISTS `pma__userconfig` ( ) COMMENT='User preferences storage for phpMyAdmin' DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `pma__users` +-- + +CREATE TABLE IF NOT EXISTS `pma__users` ( + `username` varchar(64) NOT NULL, + `usergroup` varchar(64) NOT NULL, + PRIMARY KEY (`username`,`usergroup`) +) + COMMENT='Users and their assignments to user groups' + DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `pma__usergroups` +-- + +CREATE TABLE IF NOT EXISTS `pma__usergroups` ( + `usergroup` varchar(64) NOT NULL, + `server_databases` enum('Y','N') NOT NULL DEFAULT 'N', + `server_sql` enum('Y','N') NOT NULL DEFAULT 'N', + `server_status` enum('Y','N') NOT NULL DEFAULT 'N', + `server_rights` enum('Y','N') NOT NULL DEFAULT 'N', + `server_export` enum('Y','N') NOT NULL DEFAULT 'N', + `server_import` enum('Y','N') NOT NULL DEFAULT 'N', + `server_settings` enum('Y','N') NOT NULL DEFAULT 'N', + `server_binlog` enum('Y','N') NOT NULL DEFAULT 'N', + `server_replication` enum('Y','N') NOT NULL DEFAULT 'N', + `server_vars` enum('Y','N') NOT NULL DEFAULT 'N', + `server_charset` enum('Y','N') NOT NULL DEFAULT 'N', + `server_plugins` enum('Y','N') NOT NULL DEFAULT 'N', + `server_engine` enum('Y','N') NOT NULL DEFAULT 'N', + `db_structure` enum('Y','N') NOT NULL DEFAULT 'N', + `db_sql` enum('Y','N') NOT NULL DEFAULT 'N', + `db_search` enum('Y','N') NOT NULL DEFAULT 'N', + `db_qbe` enum('Y','N') NOT NULL DEFAULT 'N', + `db_export` enum('Y','N') NOT NULL DEFAULT 'N', + `db_import` enum('Y','N') NOT NULL DEFAULT 'N', + `db_operation` enum('Y','N') NOT NULL DEFAULT 'N', + `db_privileges` enum('Y','N') NOT NULL DEFAULT 'N', + `db_routines` enum('Y','N') NOT NULL DEFAULT 'N', + `db_events` enum('Y','N') NOT NULL DEFAULT 'N', + `db_triggers` enum('Y','N') NOT NULL DEFAULT 'N', + `db_tracking` enum('Y','N') NOT NULL DEFAULT 'N', + `db_designer` enum('Y','N') NOT NULL DEFAULT 'N', + `table_browse` enum('Y','N') NOT NULL DEFAULT 'N', + `table_structure` enum('Y','N') NOT NULL DEFAULT 'N', + `table_sql` enum('Y','N') NOT NULL DEFAULT 'N', + `table_search` enum('Y','N') NOT NULL DEFAULT 'N', + `table_insert` enum('Y','N') NOT NULL DEFAULT 'N', + `table_export` enum('Y','N') NOT NULL DEFAULT 'N', + `table_import` enum('Y','N') NOT NULL DEFAULT 'N', + `table_operation` enum('Y','N') NOT NULL DEFAULT 'N', + `table_tracking` enum('Y','N') NOT NULL DEFAULT 'N', + `table_triggers` enum('Y','N') NOT NULL DEFAULT 'N', + PRIMARY KEY (`usergroup`) +) + COMMENT='User groups with configured menu items' + DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; diff --git a/examples/create_tables_drizzle.sql b/examples/create_tables_drizzle.sql index 92de90afec..257018815a 100644 --- a/examples/create_tables_drizzle.sql +++ b/examples/create_tables_drizzle.sql @@ -225,3 +225,66 @@ CREATE TABLE IF NOT EXISTS `pma__userconfig` ( ) COMMENT='User preferences storage for phpMyAdmin' COLLATE utf8_bin; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `pma__users` +-- + +CREATE TABLE IF NOT EXISTS `pma__users` ( + `username` varchar(64) NOT NULL, + `usergroup` varchar(64) NOT NULL, + PRIMARY KEY (`username`,`usergroup`) +) + COMMENT='Users and their assignments to user groups' + COLLATE utf8_bin; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `pma__usergroups` +-- + +CREATE TABLE IF NOT EXISTS `pma__usergroups` ( + `usergroup` varchar(64) NOT NULL, + `server_databases` enum('Y','N') NOT NULL DEFAULT 'N', + `server_sql` enum('Y','N') NOT NULL DEFAULT 'N', + `server_status` enum('Y','N') NOT NULL DEFAULT 'N', + `server_rights` enum('Y','N') NOT NULL DEFAULT 'N', + `server_export` enum('Y','N') NOT NULL DEFAULT 'N', + `server_import` enum('Y','N') NOT NULL DEFAULT 'N', + `server_settings` enum('Y','N') NOT NULL DEFAULT 'N', + `server_binlog` enum('Y','N') NOT NULL DEFAULT 'N', + `server_replication` enum('Y','N') NOT NULL DEFAULT 'N', + `server_vars` enum('Y','N') NOT NULL DEFAULT 'N', + `server_charset` enum('Y','N') NOT NULL DEFAULT 'N', + `server_plugins` enum('Y','N') NOT NULL DEFAULT 'N', + `server_engine` enum('Y','N') NOT NULL DEFAULT 'N', + `db_structure` enum('Y','N') NOT NULL DEFAULT 'N', + `db_sql` enum('Y','N') NOT NULL DEFAULT 'N', + `db_search` enum('Y','N') NOT NULL DEFAULT 'N', + `db_qbe` enum('Y','N') NOT NULL DEFAULT 'N', + `db_export` enum('Y','N') NOT NULL DEFAULT 'N', + `db_import` enum('Y','N') NOT NULL DEFAULT 'N', + `db_operation` enum('Y','N') NOT NULL DEFAULT 'N', + `db_privileges` enum('Y','N') NOT NULL DEFAULT 'N', + `db_routines` enum('Y','N') NOT NULL DEFAULT 'N', + `db_events` enum('Y','N') NOT NULL DEFAULT 'N', + `db_triggers` enum('Y','N') NOT NULL DEFAULT 'N', + `db_tracking` enum('Y','N') NOT NULL DEFAULT 'N', + `db_designer` enum('Y','N') NOT NULL DEFAULT 'N', + `table_browse` enum('Y','N') NOT NULL DEFAULT 'N', + `table_structure` enum('Y','N') NOT NULL DEFAULT 'N', + `table_sql` enum('Y','N') NOT NULL DEFAULT 'N', + `table_search` enum('Y','N') NOT NULL DEFAULT 'N', + `table_insert` enum('Y','N') NOT NULL DEFAULT 'N', + `table_export` enum('Y','N') NOT NULL DEFAULT 'N', + `table_import` enum('Y','N') NOT NULL DEFAULT 'N', + `table_operation` enum('Y','N') NOT NULL DEFAULT 'N', + `table_tracking` enum('Y','N') NOT NULL DEFAULT 'N', + `table_triggers` enum('Y','N') NOT NULL DEFAULT 'N', + PRIMARY KEY (`usergroup`) +) + COMMENT='User groups with configured menu items' + COLLATE utf8_bin; diff --git a/js/server_privileges.js b/js/server_privileges.js index 7f31c93392..935c99f0c5 100644 --- a/js/server_privileges.js +++ b/js/server_privileges.js @@ -172,6 +172,7 @@ AJAX.registerTeardown('server_privileges.js', function () { $('form[name=usersForm]').unbind('submit'); $("#fieldset_delete_user_footer #buttonGo.ajax").die('click'); $("a.edit_user_anchor.ajax").die('click'); + $("a.edit_user_group_anchor.ajax").die('click'); $("#edit_user_dialog").find("form.ajax").die('submit'); $("button.mult_submit[value=export]").die('click'); $("a.export_user_anchor.ajax").die('click'); @@ -300,6 +301,75 @@ AJAX.registerOnload('server_privileges.js', function () { }); // end $.post() }); // end Revoke User + $("a.edit_user_group_anchor.ajax").live('click', function (event) { + event.preventDefault(); + $(this).parents('tr').addClass('current_row'); + var token = $(this).parents('form').find('input[name="token"]').val(); + var $msg = PMA_ajaxShowMessage(); + $.get( + $(this).attr('href'), + { + 'ajax_request': true, + 'edit_user_group_dialog': true, + 'token': token + }, + function (data) { + if (data.success === true) { + PMA_ajaxRemoveMessage($msg); + var buttonOptions = {}; + buttonOptions[PMA_messages.strGo] = function () { + var usrGroup = $('#changeUserGroupDialog') + .find('select[name="userGroup"]') + .val(); + var $message = PMA_ajaxShowMessage(); + $.get( + 'server_privileges.php', + $('#changeUserGroupDialog').find('form').serialize() + '&ajax_request=1', + function (data) { + PMA_ajaxRemoveMessage($message); + if (data.success === true) { + $("#usersForm") + .find('.current_row') + .removeClass('current_row') + .find('.usrGroup') + .text(usrGroup); + } else { + PMA_ajaxShowMessage(data.error, false); + $("#usersForm") + .find('.current_row') + .removeClass('current_row'); + } + } + ); + $(this).dialog("close"); + }; + buttonOptions[PMA_messages.strClose] = function () { + $(this).dialog("close"); + }; + var $dialog = $('
') + .attr('id', 'changeUserGroupDialog') + .append(data.message) + .dialog({ + width: 500, + minWidth: 300, + modal: true, + buttons: buttonOptions, + title: $('legend', $(data.message)).text(), + close: function () { + $(this).remove(); + } + }); + $dialog.find('legend').remove(); + } else { + PMA_ajaxShowMessage(data.error, false); + $("#usersForm") + .find('.current_row') + .removeClass('current_row'); + } + } + ); + }); + /** * AJAX handler for 'Edit User' * diff --git a/libraries/Menu.class.php b/libraries/Menu.class.php index 687c20332a..d1f60d3b40 100644 --- a/libraries/Menu.class.php +++ b/libraries/Menu.class.php @@ -99,17 +99,64 @@ class PMA_Menu { $tabs = array(); $url_params = array('db' => $this->_db); + $level = ''; + if (strlen($this->_table)) { $tabs = $this->_getTableTabs(); $url_params['table'] = $this->_table; + $level = 'table'; } else if (strlen($this->_db)) { $tabs = $this->_getDbTabs(); + $level = 'db'; } else { $tabs = $this->_getServerTabs(); + $level = 'server'; + } + + $allowedTabs = $this->_getAllowedTabs($level); + foreach ($tabs as $key => $value) { + if (! array_key_exists($key, $allowedTabs)) { + unset($tabs[$key]); + } } return PMA_Util::getHtmlTabs($tabs, $url_params, 'topmenu', true); } + /** + * Returns a list of allowed tabs for the current user for the given level + * + * @param string $level 'server', 'db' or 'table' level + * + * @return array list of allowed tabs + */ + private function _getAllowedTabs($level) + { + $allowedTabs = PMA_Util::getMenuTabList($level); + if ($GLOBALS['cfgRelation']['menuswork']) { + $groupTable = PMA_Util::backquote($GLOBALS['cfg']['Server']['pmadb']) + . "." . PMA_Util::backquote($GLOBALS['cfg']['Server']['usergroups']); + $userTable = PMA_Util::backquote($GLOBALS['cfg']['Server']['pmadb']) + . "." . PMA_Util::backquote($GLOBALS['cfg']['Server']['users']); + + $sql_query = "SELECT * FROM " . $groupTable + . " WHERE `usergroup` = (SELECT usergroup FROM " + . $userTable . " WHERE `username` = '" + . PMA_Util::sqlAddSlashes($GLOBALS['cfg']['Server']['user']) . "')"; + + $result = PMA_queryAsControlUser($sql_query, false); + if ($result) { + $row = $GLOBALS['dbi']->fetchAssoc($result); + foreach ($allowedTabs as $key => $tab) { + $colName = $level . '_' . $key; + if (isset($row[$colName]) && $row[$colName] == 'N') { + unset($allowedTabs[$key]); + } + } + } + } + return $allowedTabs; + } + /** * Returns the breadcrumbs as HTML * @@ -130,21 +177,13 @@ class PMA_Menu $item = ''; - if (in_array( - $GLOBALS['cfg']['TabsMode'], - array('text', 'both') - ) - ) { + if (in_array($GLOBALS['cfg']['TabsMode'], array('text', 'both'))) { $item .= '%4$s: '; } $item .= '%3$s'; $retval .= ""; $retval .= "' . __('Quick steps to setup advanced features:') . '
'; @@ -347,22 +364,23 @@ function PMA_getDiagMessageForParameter($parameter, */ function PMA_checkRelationsParam() { - $cfgRelation = array(); - $cfgRelation['relwork'] = false; - $cfgRelation['displaywork'] = false; - $cfgRelation['bookmarkwork']= false; - $cfgRelation['pdfwork'] = false; - $cfgRelation['commwork'] = false; - $cfgRelation['mimework'] = false; - $cfgRelation['historywork'] = false; - $cfgRelation['recentwork'] = false; - $cfgRelation['uiprefswork'] = false; - $cfgRelation['trackingwork'] = false; - $cfgRelation['designerwork'] = false; + $cfgRelation = array(); + $cfgRelation['relwork'] = false; + $cfgRelation['displaywork'] = false; + $cfgRelation['bookmarkwork'] = false; + $cfgRelation['pdfwork'] = false; + $cfgRelation['commwork'] = false; + $cfgRelation['mimework'] = false; + $cfgRelation['historywork'] = false; + $cfgRelation['recentwork'] = false; + $cfgRelation['uiprefswork'] = false; + $cfgRelation['trackingwork'] = false; + $cfgRelation['designerwork'] = false; $cfgRelation['userconfigwork'] = false; - $cfgRelation['allworks'] = false; - $cfgRelation['user'] = null; - $cfgRelation['db'] = null; + $cfgRelation['menuswork'] = false; + $cfgRelation['allworks'] = false; + $cfgRelation['user'] = null; + $cfgRelation['db'] = null; if ($GLOBALS['server'] == 0 || empty($GLOBALS['cfg']['Server']['pmadb']) @@ -408,21 +426,25 @@ function PMA_checkRelationsParam() } elseif ($curr_table[0] == $GLOBALS['cfg']['Server']['table_coords']) { $cfgRelation['table_coords'] = $curr_table[0]; } elseif ($curr_table[0] == $GLOBALS['cfg']['Server']['designer_coords']) { - $cfgRelation['designer_coords'] = $curr_table[0]; + $cfgRelation['designer_coords'] = $curr_table[0]; } elseif ($curr_table[0] == $GLOBALS['cfg']['Server']['column_info']) { - $cfgRelation['column_info'] = $curr_table[0]; + $cfgRelation['column_info'] = $curr_table[0]; } elseif ($curr_table[0] == $GLOBALS['cfg']['Server']['pdf_pages']) { $cfgRelation['pdf_pages'] = $curr_table[0]; } elseif ($curr_table[0] == $GLOBALS['cfg']['Server']['history']) { - $cfgRelation['history'] = $curr_table[0]; + $cfgRelation['history'] = $curr_table[0]; } elseif ($curr_table[0] == $GLOBALS['cfg']['Server']['recent']) { - $cfgRelation['recent'] = $curr_table[0]; + $cfgRelation['recent'] = $curr_table[0]; } elseif ($curr_table[0] == $GLOBALS['cfg']['Server']['table_uiprefs']) { - $cfgRelation['table_uiprefs'] = $curr_table[0]; + $cfgRelation['table_uiprefs'] = $curr_table[0]; } elseif ($curr_table[0] == $GLOBALS['cfg']['Server']['tracking']) { - $cfgRelation['tracking'] = $curr_table[0]; + $cfgRelation['tracking'] = $curr_table[0]; } elseif ($curr_table[0] == $GLOBALS['cfg']['Server']['userconfig']) { - $cfgRelation['userconfig'] = $curr_table[0]; + $cfgRelation['userconfig'] = $curr_table[0]; + } elseif ($curr_table[0] == $GLOBALS['cfg']['Server']['users']) { + $cfgRelation['users'] = $curr_table[0]; + } elseif ($curr_table[0] == $GLOBALS['cfg']['Server']['usergroups']) { + $cfgRelation['usergroups'] = $curr_table[0]; } } // end while $GLOBALS['dbi']->freeResult($tab_rs); @@ -430,7 +452,7 @@ function PMA_checkRelationsParam() if (isset($cfgRelation['relation'])) { $cfgRelation['relwork'] = true; if (isset($cfgRelation['table_info'])) { - $cfgRelation['displaywork'] = true; + $cfgRelation['displaywork'] = true; } } @@ -473,12 +495,17 @@ function PMA_checkRelationsParam() $cfgRelation['bookmarkwork'] = true; } + if (isset($cfgRelation['users']) && isset($cfgRelation['usergroups'])) { + $cfgRelation['menuswork'] = true; + } + if ($cfgRelation['relwork'] && $cfgRelation['displaywork'] && $cfgRelation['pdfwork'] && $cfgRelation['commwork'] && $cfgRelation['mimework'] && $cfgRelation['historywork'] && $cfgRelation['recentwork'] && $cfgRelation['uiprefswork'] && $cfgRelation['trackingwork'] && $cfgRelation['userconfigwork'] && $cfgRelation['bookmarkwork'] && $cfgRelation['designerwork'] + && $cfgRelation['menuswork'] ) { $cfgRelation['allworks'] = true; } diff --git a/libraries/server_privileges.lib.php b/libraries/server_privileges.lib.php index 46a35c6970..543c121b21 100644 --- a/libraries/server_privileges.lib.php +++ b/libraries/server_privileges.lib.php @@ -390,6 +390,103 @@ function PMA_getSqlQueryForDisplayPrivTable($db, $table, $username, $hostname) ." AND `Db` = '" . PMA_Util::unescapeMysqlWildcards($db) . "'" ." AND `Table_name` = '" . PMA_Util::sqlAddSlashes($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 + */ +function PMA_getHtmlToChoseUserGroup($username) +{ + $html_output = ''; + return $html_output; +} + +/** + * Sets the user group from request values + * + * @param string $username username + * @param string $userGroup user group to set + * + * @return void + */ +function PMA_setUserGroup($username, $userGroup) +{ + $userTable = PMA_Util::backquote($GLOBALS['cfg']['Server']['pmadb']) + . "." . PMA_Util::backquote($GLOBALS['cfg']['Server']['users']); + + $sql_query = "SELECT `usergroup` FROM " . $userTable + . " WHERE `username` = '" . PMA_Util::sqlAddSlashes($username) . "'"; + $oldUserGroup = $GLOBALS['dbi']->fetchValue( + $sql_query, 0, 0, $GLOBALS['controllink'] + ); + + if ($oldUserGroup === false) { + $upd_query = "INSERT INTO " . $userTable . "(`username`, `usergroup`)" + . " VALUES ('" . PMA_Util::sqlAddSlashes($username) . "', " + . "'" . PMA_Util::sqlAddSlashes($userGroup) . "')"; + } else { + if (empty($userGroup)) { + $upd_query = "DELETE FROM " . $userTable + . " WHERE `username`='" . PMA_Util::sqlAddSlashes($username) . "'"; + } elseif ($oldUserGroup != $userGroup) { + $upd_query = "UPDATE " . $userTable + . " SET `usergroup`='" . PMA_Util::sqlAddSlashes($userGroup) . "'" + . " WHERE `username`='" . PMA_Util::sqlAddSlashes($username) . "'"; + } + } + if (isset($upd_query)) { + PMA_queryAsControlUser($upd_query); + } +} + /** * Displays the privileges form table * @@ -591,7 +688,7 @@ function PMA_getHtmlForDisplayResourceLimits($row) * @param string $db the database * @param string $table the table * @param boolean $columns columns array - * @param $row current privileges row + * @param array $row current privileges row * * @return string $html_output */ @@ -2429,9 +2526,12 @@ function PMA_getUsersOverview($result, $db_rights, $link_edit, $pmaThemeImage, . PMA_Util::showHint( __('Note: MySQL privilege names are expressed in English') ) - . '' . "\n" - . '' . "\n"
. '' . implode(',' . "\n" . ' ', $host['privs']) . "\n"
- . '' + . __('No users were found belonging to this user group') + . '
'; + } else { + $html_output .= '| # | ' . __('User') . ' |
|---|---|
| ' . $i . ' | ' + . '' . htmlspecialchars($row[0]) . ' | ' + . '