From 4e534c977f7bf51aa2c5c6879d2112c79d1bedda Mon Sep 17 00:00:00 2001 From: Deven Bansod Date: Fri, 9 Sep 2016 14:12:50 +0530 Subject: [PATCH 1/9] Fix Export and Copy of Tables with Generated/Virtual columns We include only the non-generated columns insert using INSERT INTO .. SELECT .. FROM .. while copying data. In Export too, we export the data of only the non-generated columns Fix #12221 Fix #12518 Signed-off-by: Deven Bansod --- libraries/Table.php | 43 ++++++++++++++++++++++++++++++++++++---- libraries/export.lib.php | 14 +++++++++++-- 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/libraries/Table.php b/libraries/Table.php index ccc9df8731..f8b0264ad8 100644 --- a/libraries/Table.php +++ b/libraries/Table.php @@ -1017,10 +1017,17 @@ class Table $GLOBALS['dbi']->query($sql_set_mode); $GLOBALS['sql_query'] .= "\n\n" . $sql_set_mode . ';'; - $sql_insert_data = 'INSERT INTO ' . $target - . ' SELECT * FROM ' . $source; - $GLOBALS['dbi']->query($sql_insert_data); - $GLOBALS['sql_query'] .= "\n\n" . $sql_insert_data . ';'; + $_old_table = new Table($source_table, $source_db); + $nonGeneratedCols = $_old_table->getNonGeneratedColumns(true); + if (count($nonGeneratedCols) > 0) { + $sql_insert_data = 'INSERT INTO ' . $target . '(' + . implode(', ', $nonGeneratedCols) + . ') SELECT ' . implode(', ', $nonGeneratedCols) + . ' FROM ' . $source; + + $GLOBALS['dbi']->query($sql_insert_data); + $GLOBALS['sql_query'] .= "\n\n" . $sql_insert_data . ';'; + } } PMA_getRelationsParam(); @@ -1484,6 +1491,34 @@ class Table } } + /** + * Get non-generated columns in table + * + * @param bool $backquoted whether to quote name with backticks `` + * + * @return array + */ + public function getNonGeneratedColumns($backquoted = true) + { + $columns_meta_query = sprintf( + 'SHOW COLUMNS FROM %s.%s', + Util::backquote($this->_db_name), + Util::backquote($this->_name) + ); + $ret = array(); + + $columns_meta_query_result = $this->_dbi->tryQuery($columns_meta_query); + if ($columns_meta_query_result !== false) { + foreach ($columns_meta_query_result as $column) { + if (strpos($column['Extra'], 'GENERATED') === false) { + array_push($ret, Util::backquote($column['Field'])); + } + } + } + + return $ret; + } + /** * Return UI preferences for this table from phpMyAdmin database. * diff --git a/libraries/export.lib.php b/libraries/export.lib.php index 20567d9698..e432d1ceb8 100644 --- a/libraries/export.lib.php +++ b/libraries/export.lib.php @@ -682,8 +682,13 @@ function PMA_exportDatabase( && in_array($table, $table_data) && ! ($is_view) ) { - $local_query = 'SELECT * FROM ' . PMA\libraries\Util::backquote($db) + $tableObj = new PMA\libraries\Table($table, $db); + $nonGeneratedCols = $tableObj->getNonGeneratedColumns(true); + + $local_query = 'SELECT ' . implode(', ', $nonGeneratedCols) + . ' FROM ' . PMA\libraries\Util::backquote($db) . '.' . PMA\libraries\Util::backquote($table); + if (! $export_plugin->exportData( $db, $table, $crlf, $err_url, $local_query, $aliases )) { @@ -862,7 +867,12 @@ function PMA_exportTable( $local_query = $sql_query . $add_query; $GLOBALS['dbi']->selectDb($db); } else { - $local_query = 'SELECT * FROM ' . PMA\libraries\Util::backquote($db) + // Data is exported only for Non-generated columns + $tableObj = new PMA\libraries\Table($table, $db); + $nonGeneratedCols = $tableObj->getNonGeneratedColumns(true); + + $local_query = 'SELECT ' . implode(', ', $nonGeneratedCols) + . ' FROM ' . PMA\libraries\Util::backquote($db) . '.' . PMA\libraries\Util::backquote($table) . $add_query; } if (! $export_plugin->exportData( From 429c679f776e780db22481e94d31e07d81977dbd Mon Sep 17 00:00:00 2001 From: Deven Bansod Date: Fri, 9 Sep 2016 15:56:22 +0530 Subject: [PATCH 2/9] Fix tests, added testcase for Generated columns in moveCopy Signed-off-by: Deven Bansod --- libraries/Table.php | 15 ++++++++------- test/classes/TableTest.php | 33 ++++++++++++++++++++++++++++++--- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/libraries/Table.php b/libraries/Table.php index f8b0264ad8..ce49bcb610 100644 --- a/libraries/Table.php +++ b/libraries/Table.php @@ -1500,15 +1500,16 @@ class Table */ public function getNonGeneratedColumns($backquoted = true) { - $columns_meta_query = sprintf( - 'SHOW COLUMNS FROM %s.%s', - Util::backquote($this->_db_name), - Util::backquote($this->_name) - ); + $columns_meta_query = 'SHOW COLUMNS FROM ' . $this->getFullName(true); $ret = array(); - $columns_meta_query_result = $this->_dbi->tryQuery($columns_meta_query); - if ($columns_meta_query_result !== false) { + $columns_meta_query_result = $this->_dbi->fetchResult( + $columns_meta_query + ); + + if ($columns_meta_query_result + && $columns_meta_query_result !== false + ) { foreach ($columns_meta_query_result as $column) { if (strpos($column['Extra'], 'GENERATED') === false) { array_push($ret, Util::backquote($column['Field'])); diff --git a/test/classes/TableTest.php b/test/classes/TableTest.php index 55f1c9b9d6..0fe7073670 100644 --- a/test/classes/TableTest.php +++ b/test/classes/TableTest.php @@ -180,6 +180,31 @@ class TableTest extends PMATestCase 'ALL' ) ), + array( + 'SHOW COLUMNS FROM `PMA`.`PMA_BookMark`', + null, + null, + null, + 0, + array( + array( + 'Field'=>'COLUMN_NAME1', + 'Type'=> 'INT(10)', + 'Null'=> 'NO', + 'Key'=> '', + 'Default'=> NULL, + 'Extra'=>'' + ), + array( + 'Field'=>'COLUMN_NAME2', + 'Type'=> 'INT(10)', + 'Null'=> 'YES', + 'Key'=> '', + 'Default'=> NULL, + 'Extra'=>'STORED GENERATED' + ) + ) + ), ); $dbi = $this->getMockBuilder('PMA\libraries\DatabaseInterface') @@ -1047,7 +1072,8 @@ class TableTest extends PMATestCase $expect, $return ); - $sql_query = "INSERT INTO `PMA_new`.`PMA_BookMark_new` SELECT * FROM " + $sql_query = "INSERT INTO `PMA_new`.`PMA_BookMark_new`(`COLUMN_NAME1`)" + . " SELECT `COLUMN_NAME1` FROM " . "`PMA`.`PMA_BookMark`"; $this->assertContains( $sql_query, @@ -1070,8 +1096,9 @@ class TableTest extends PMATestCase $expect, $return ); - $sql_query = "INSERT INTO `PMA_new`.`PMA_BookMark_new` SELECT * FROM " - . "`PMA`.`PMA_BookMark`;"; + $sql_query = "INSERT INTO `PMA_new`.`PMA_BookMark_new`(`COLUMN_NAME1`)" + . " SELECT `COLUMN_NAME1` FROM " + . "`PMA`.`PMA_BookMark`"; $this->assertContains( $sql_query, $GLOBALS['sql_query'] From 5145c8c3238eaca1d0a6b80adc9f7c8e07d6bfef Mon Sep 17 00:00:00 2001 From: Deven Bansod Date: Tue, 13 Sep 2016 13:09:34 +0530 Subject: [PATCH 3/9] Fix #12320 : Copying a user does not copy usergroup Signed-off-by: Deven Bansod --- libraries/server_privileges.lib.php | 44 +++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/libraries/server_privileges.lib.php b/libraries/server_privileges.lib.php index 059595e619..932d7a3d13 100644 --- a/libraries/server_privileges.lib.php +++ b/libraries/server_privileges.lib.php @@ -2951,6 +2951,33 @@ function PMA_getUserGroupCount() return $user_group_count; } +/** + * Returns name of user group that user is part of + * + * @param string $username User name + * + * @return mixed usergroup if found or null if not found + */ +function PMA_getUserGroupForUser($username) +{ + $cfgRelation = PMA_getRelationsParam(); + $user_table = Util::backquote($cfgRelation['db']) + . '.' . Util::backquote($cfgRelation['users']); + $sql_query = 'SELECT `usergroup` FROM ' . $user_table + . ' WHERE `username` = \'' . $username . '\'' + . ' LIMIT 1'; + + $usergroup = $GLOBALS['dbi']->fetchValue( + $sql_query, 0, 0, $GLOBALS['controllink'] + ); + + if ($usergroup === false) { + return null; + } + + return $usergroup; +} + /** * This function return the extra data array for the ajax behavior * @@ -3129,8 +3156,15 @@ function PMA_getChangeLoginInformationHtmlForm($username, $hostname) . '' . "\n" . '' . "\n" - . '
' . "\n" + . 'value="' . htmlspecialchars($hostname) . '" />' . "\n"; + + $usergroup = PMA_getUserGroupForUser($username); + if ($usergroup !== null) { + $html_output .= '' . "\n"; + } + + $html_output .= '
' . "\n" . '' . "\n" . __('Change login information / Copy user account') . '' . "\n" @@ -4292,6 +4326,12 @@ function PMA_addUser( ); } + // Copy the user group while copying a user + $old_usergroup = + $_REQUEST['old_usergroup'] ? $_REQUEST['old_usergroup'] : null; + PMA_setUserGroup($_REQUEST['username'], $old_usergroup); + + if (isset($create_user_real)) { $queries[] = $create_user_real; } From 37ff86315b448cfa70814a6d4fa7f070491e0acc Mon Sep 17 00:00:00 2001 From: Deven Bansod Date: Tue, 13 Sep 2016 16:31:59 +0530 Subject: [PATCH 4/9] Fix usage of backquote parameter in getNonGenerated func Signed-off-by: Deven Bansod --- libraries/Table.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/Table.php b/libraries/Table.php index ce49bcb610..d3f8032c60 100644 --- a/libraries/Table.php +++ b/libraries/Table.php @@ -1511,8 +1511,13 @@ class Table && $columns_meta_query_result !== false ) { foreach ($columns_meta_query_result as $column) { + $value = $column['Field']; + if ($backquoted === true) { + $value = Util::backquote($value); + } + if (strpos($column['Extra'], 'GENERATED') === false) { - array_push($ret, Util::backquote($column['Field'])); + array_push($ret, $value); } } } From 093620495bb5d2ce4a5571b85905ec91b78dd793 Mon Sep 17 00:00:00 2001 From: Deven Bansod Date: Tue, 13 Sep 2016 22:19:01 +0530 Subject: [PATCH 5/9] Added tests for Usergroup as hidden field in Copy User form Signed-off-by: Deven Bansod --- test/libraries/PMA_server_privileges_test.php | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/test/libraries/PMA_server_privileges_test.php b/test/libraries/PMA_server_privileges_test.php index 62c9030cd1..abf318bf01 100644 --- a/test/libraries/PMA_server_privileges_test.php +++ b/test/libraries/PMA_server_privileges_test.php @@ -1638,6 +1638,7 @@ class PMA_ServerPrivileges_Test extends PHPUnit_Framework_TestCase { $username = "pma_username"; $hostname = "pma_hostname"; + $GLOBALS['cfgRelation']['menuswork'] = true; $dbi_old = $GLOBALS['dbi']; $dbi = $this->getMockBuilder('PMA\libraries\DatabaseInterface') @@ -1650,6 +1651,11 @@ class PMA_ServerPrivileges_Test extends PHPUnit_Framework_TestCase $dbi->expects($this->any())->method('fetchResult') ->will($this->returnValue($fields_info)); + $expected_userGroup = "pma_usergroup"; + + $dbi->expects($this->any())->method('fetchValue') + ->will($this->returnValue($expected_userGroup)); + $GLOBALS['dbi'] = $dbi; //PMA_getChangeLoginInformationHtmlForm @@ -1677,6 +1683,12 @@ class PMA_ServerPrivileges_Test extends PHPUnit_Framework_TestCase $html ); + $this->assertContains( + '', + $html + ); + //Create a new user with the same privileges $this->assertContains( "Create a new user account with the same privileges", @@ -1686,6 +1698,38 @@ class PMA_ServerPrivileges_Test extends PHPUnit_Framework_TestCase $GLOBALS['dbi'] = $dbi_old; } + /** + * Test for PMA_getUserGroupForUser + * + * @return void + */ + public function testPMAGetUserGroupForUser() + { + $username = "pma_username"; + $hostname = "pma_hostname"; + $GLOBALS['cfgRelation']['menuswork'] = true; + + $dbi_old = $GLOBALS['dbi']; + $dbi = $this->getMockBuilder('PMA\libraries\DatabaseInterface') + ->disableOriginalConstructor() + ->getMock(); + $expected_userGroup = "pma_usergroup"; + + $dbi->expects($this->any())->method('fetchValue') + ->will($this->returnValue($expected_userGroup)); + + $GLOBALS['dbi'] = $dbi; + + $returned_userGroup = PMA_getUserGroupForUser($username); + + $this->assertEquals( + $expected_userGroup, + $returned_userGroup + ); + + $GLOBALS['dbi'] = $dbi_old; + } + /** * Test for PMA_getLinkToDbAndTable * From ea90f285517e79f6db80b5038e563f483269839b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Fri, 16 Sep 2016 10:34:55 +0200 Subject: [PATCH 6/9] Changelog for #12548 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michal Čihař --- ChangeLog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ChangeLog b/ChangeLog index 703d330f71..d65567cf9f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -30,6 +30,8 @@ phpMyAdmin - ChangeLog - issue #12473 Code can throw unhandled exception - issue #12550 Do not try to keep alive session even after expiry - issue #12512 Fixed rendering BBCode links in setup +- issue #12518 Fixed copy of table with generated columns +- issue #12221 Fixed export of table with generated columns 4.6.4 (2016-08-16) - issue [security] Weaknesses with cookie encryption, see PMASA-2016-29 From 350be95a53d496ea774d35c8a9d02bd377f3b0da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Fri, 16 Sep 2016 10:37:22 +0200 Subject: [PATCH 7/9] Changelog entry for #12553 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michal Čihař --- ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog b/ChangeLog index d65567cf9f..36b8a5e4cc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -32,6 +32,7 @@ phpMyAdmin - ChangeLog - issue #12512 Fixed rendering BBCode links in setup - issue #12518 Fixed copy of table with generated columns - issue #12221 Fixed export of table with generated columns +- issue #12320 Copying a user does not copy usergroup 4.6.4 (2016-08-16) - issue [security] Weaknesses with cookie encryption, see PMASA-2016-29 From b18426f887ef1958c696a0e6c8404dfd1b51673c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Fri, 16 Sep 2016 11:20:42 +0200 Subject: [PATCH 8/9] Correctly reference Math.min (issue #12550) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michal Čihař --- js/functions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/functions.js b/js/functions.js index 9fa445f314..a6587bbad1 100644 --- a/js/functions.js +++ b/js/functions.js @@ -906,7 +906,7 @@ AJAX.registerOnload('functions.js', function () { var remaining = PMA_commonParams.get('LoginCookieValidity') - _idleSecondsCounter; if (remaining > 5) { // max value for setInterval() function - var interval = min(remaining * 1000, Math.pow(2, 31) - 1); + var interval = Math.min(remaining * 1000, Math.pow(2, 31) - 1); updateTimeout = window.setTimeout(UpdateIdleTime, interval); } else if (remaining > 0) { // We're close to session expiry From bd9bbc4b4dc2121a3c5f3a2b7bf7bfc60559f15c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Fri, 16 Sep 2016 11:38:13 +0200 Subject: [PATCH 9/9] Document test removal suggestion for production usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue #12547 Signed-off-by: Michal Čihař --- doc/setup.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/setup.rst b/doc/setup.rst index 66eb0469e4..474a93e866 100644 --- a/doc/setup.rst +++ b/doc/setup.rst @@ -756,6 +756,7 @@ are always ways to make your installation more secure: * Serve phpMyAdmin on HTTPS only. Preferably, you should use HSTS as well, so that you're protected from protocol downgrade attacks. +* Remove the ``test`` directory from phpMyAdmin, unless you are developing and need test suite. * Remove the ``setup`` directory from phpMyAdmin, you will probably not use it after the initial setup. * Properly choose an authentication method - :ref:`cookie`