Merge branch 'QA_5_2'
Signed-off-by: Maurício Meneghini Fauth <mauricio@fauth.dev>
This commit is contained in:
commit
a82c7fcb6f
@ -78,6 +78,7 @@ phpMyAdmin - ChangeLog
|
||||
- issue #17363 Fix duplicate route parameter after logging in
|
||||
- issue #15670 Fix case where the data is truncated after changing a longtext column's collation
|
||||
- issue #18865 Fix missing text-nowrap for timestamps columns
|
||||
- issue #19022 Fix case where tables from wrong database is loaded in navigation tree
|
||||
|
||||
5.2.1 (2023-02-07)
|
||||
- issue #17522 Fix case where the routes cache file is invalid
|
||||
|
||||
@ -6715,6 +6715,16 @@ parameters:
|
||||
count: 1
|
||||
path: src/DatabaseInterface.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpMyAdmin\\\\DatabaseInterface\\:\\:getCurrentRoles\\(\\) should return array\\<int, array\\<int, string\\>\\> but returns array\\<int, string\\>\\.$#"
|
||||
count: 1
|
||||
path: src/DatabaseInterface.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpMyAdmin\\\\DatabaseInterface\\:\\:getCurrentRoles\\(\\) should return array\\<int, array\\<int, string\\>\\> but returns mixed\\.$#"
|
||||
count: 1
|
||||
path: src/DatabaseInterface.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpMyAdmin\\\\DatabaseInterface\\:\\:getCurrentUser\\(\\) should return string but returns mixed\\.$#"
|
||||
count: 1
|
||||
@ -6745,6 +6755,11 @@ parameters:
|
||||
count: 1
|
||||
path: src/DatabaseInterface.php
|
||||
|
||||
-
|
||||
message: "#^Only booleans are allowed in an if condition, PhpMyAdmin\\\\Dbal\\\\ResultInterface\\|false given\\.$#"
|
||||
count: 2
|
||||
path: src/DatabaseInterface.php
|
||||
|
||||
-
|
||||
message: "#^Only booleans are allowed in an if condition, int\\|false given\\.$#"
|
||||
count: 2
|
||||
@ -6755,6 +6770,11 @@ parameters:
|
||||
count: 1
|
||||
path: src/DatabaseInterface.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$callback of function array_map expects \\(callable\\(array\\<int, string\\>\\)\\: mixed\\)\\|null, Closure\\(string\\)\\: non\\-empty\\-array\\<int, string\\> given\\.$#"
|
||||
count: 1
|
||||
path: src/DatabaseInterface.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$callback of function array_map expects \\(callable\\(mixed\\)\\: mixed\\)\\|null, Closure\\(string\\)\\: non\\-empty\\-string given\\.$#"
|
||||
count: 1
|
||||
@ -6805,6 +6825,11 @@ parameters:
|
||||
count: 1
|
||||
path: src/DatabaseInterface.php
|
||||
|
||||
-
|
||||
message: "#^Variable \\$roleHost on left side of \\?\\? always exists and is not nullable\\.$#"
|
||||
count: 2
|
||||
path: src/DatabaseInterface.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpMyAdmin\\\\Dbal\\\\MysqliResult\\:\\:fetchAllAssoc\\(\\) should return array\\<int, array\\<string\\|null\\>\\> but returns array\\.$#"
|
||||
count: 1
|
||||
@ -15905,6 +15930,16 @@ parameters:
|
||||
count: 1
|
||||
path: tests/unit/DatabaseInterfaceTest.php
|
||||
|
||||
-
|
||||
message: "#^Call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertSame\\(\\) with array\\<string\\> and array\\<int, array\\<int, string\\>\\> will always evaluate to false\\.$#"
|
||||
count: 1
|
||||
path: tests/unit/DatabaseInterfaceTest.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$result of method PhpMyAdmin\\\\Tests\\\\Stubs\\\\DbiDummy\\:\\:addResult\\(\\) expects array\\<int, non\\-empty\\-array\\<int, float\\|int\\|string\\|null\\>\\>\\|bool, array\\<array\\<string\\>\\>\\|false given\\.$#"
|
||||
count: 1
|
||||
path: tests/unit/DatabaseInterfaceTest.php
|
||||
|
||||
-
|
||||
message: "#^Property PhpMyAdmin\\\\Config\\:\\:\\$selectedServer \\(array\\{host\\: string, port\\: string, socket\\: string, ssl\\: bool, ssl_key\\: string\\|null, ssl_cert\\: string\\|null, ssl_ca\\: string\\|null, ssl_ca_path\\: string\\|null, \\.\\.\\.\\}\\) does not accept array\\{host\\: string, port\\: string, socket\\: string, ssl\\: bool, ssl_key\\: string\\|null, ssl_cert\\: string\\|null, ssl_ca\\: string\\|null, ssl_ca_path\\: string\\|null, \\.\\.\\.\\}\\.$#"
|
||||
count: 1
|
||||
|
||||
@ -4911,6 +4911,15 @@
|
||||
<DeprecatedProperty>
|
||||
<code><![CDATA[Routing::$route]]></code>
|
||||
</DeprecatedProperty>
|
||||
<DocblockTypeContradiction>
|
||||
<code><![CDATA['']]></code>
|
||||
<code><![CDATA['']]></code>
|
||||
</DocblockTypeContradiction>
|
||||
<InvalidArgument>
|
||||
<code><![CDATA[static function (string $role) {
|
||||
return explode('@', $role);
|
||||
}]]></code>
|
||||
</InvalidArgument>
|
||||
<InvalidOperand>
|
||||
<code><![CDATA[$row['Data_free']]]></code>
|
||||
<code><![CDATA[$row['Data_length']]]></code>
|
||||
@ -4919,6 +4928,9 @@
|
||||
<code><![CDATA[$row['Max_data_length']]]></code>
|
||||
<code><![CDATA[$row['Rows']]]></code>
|
||||
</InvalidOperand>
|
||||
<InvalidReturnStatement>
|
||||
<code><![CDATA[$role]]></code>
|
||||
</InvalidReturnStatement>
|
||||
<MixedArgument>
|
||||
<code><![CDATA[$a]]></code>
|
||||
<code><![CDATA[$b]]></code>
|
||||
@ -4953,6 +4965,7 @@
|
||||
<code><![CDATA[$this->versionString]]></code>
|
||||
</MixedAssignment>
|
||||
<MixedInferredReturnType>
|
||||
<code><![CDATA[array<int, array<int, string>>]]></code>
|
||||
<code><![CDATA[mixed[]]]></code>
|
||||
<code><![CDATA[mixed[]]]></code>
|
||||
<code><![CDATA[string]]></code>
|
||||
@ -4963,6 +4976,7 @@
|
||||
</MixedOperand>
|
||||
<MixedReturnStatement>
|
||||
<code><![CDATA[$resultRows]]></code>
|
||||
<code><![CDATA[SessionCache::get('mysql_cur_role')]]></code>
|
||||
<code><![CDATA[SessionCache::get('mysql_cur_user')]]></code>
|
||||
<code><![CDATA[reset($columns)]]></code>
|
||||
<code><![CDATA[reset($columns)]]></code>
|
||||
@ -5029,6 +5043,10 @@
|
||||
<code><![CDATA[bool]]></code>
|
||||
<code><![CDATA[bool]]></code>
|
||||
</PossiblyUnusedReturnValue>
|
||||
<RedundantConditionGivenDocblockType>
|
||||
<code><![CDATA[$roleHost]]></code>
|
||||
<code><![CDATA[$roleHost]]></code>
|
||||
</RedundantConditionGivenDocblockType>
|
||||
<RiskyTruthyFalsyComparison>
|
||||
<code><![CDATA[$index->isUnique()]]></code>
|
||||
<code><![CDATA[$limitCount]]></code>
|
||||
@ -13357,6 +13375,9 @@
|
||||
</PossiblyUnusedMethod>
|
||||
</file>
|
||||
<file src="tests/unit/DatabaseInterfaceTest.php">
|
||||
<ArgumentTypeCoercion>
|
||||
<code><![CDATA[$value]]></code>
|
||||
</ArgumentTypeCoercion>
|
||||
<DeprecatedMethod>
|
||||
<code><![CDATA[Config::getInstance()]]></code>
|
||||
<code><![CDATA[Config::getInstance()]]></code>
|
||||
@ -13369,6 +13390,7 @@
|
||||
<code><![CDATA[DatabaseInterface::getInstance()]]></code>
|
||||
</DeprecatedMethod>
|
||||
<PossiblyUnusedMethod>
|
||||
<code><![CDATA[currentRolesData]]></code>
|
||||
<code><![CDATA[currentUserData]]></code>
|
||||
<code><![CDATA[errorData]]></code>
|
||||
<code><![CDATA[isAmazonRdsData]]></code>
|
||||
|
||||
@ -51,6 +51,7 @@ use function openlog;
|
||||
use function reset;
|
||||
use function sprintf;
|
||||
use function str_contains;
|
||||
use function str_replace;
|
||||
use function str_starts_with;
|
||||
use function stripos;
|
||||
use function strnatcasecmp;
|
||||
@ -110,6 +111,9 @@ class DatabaseInterface implements DbalInterface
|
||||
/** @var array<int, string>|null */
|
||||
private array|null $currentUserAndHost = null;
|
||||
|
||||
/** @var array<int, array<int, string>>|null Current role and host cache */
|
||||
private array|null $currentRoleAndHost = null;
|
||||
|
||||
/**
|
||||
* @var int|null lower_case_table_names value cache
|
||||
* @psalm-var 0|1|2|null
|
||||
@ -1430,6 +1434,38 @@ class DatabaseInterface implements DbalInterface
|
||||
return '@';
|
||||
}
|
||||
|
||||
/**
|
||||
* gets the current role with host. Role maybe multiple separated by comma
|
||||
* Support start from MySQL 8.x / MariaDB 10.0.5
|
||||
*
|
||||
* @see https://dev.mysql.com/doc/refman/8.0/en/roles.html
|
||||
* @see https://dev.mysql.com/doc/refman/8.0/en/information-functions.html#function_current-role
|
||||
* @see https://mariadb.com/kb/en/mariadb-1005-release-notes/#newly-implemented-features
|
||||
* @see https://mariadb.com/kb/en/roles_overview/
|
||||
*
|
||||
* @return array<int, array<int, string>> the current roles i.e. array of role@host
|
||||
*/
|
||||
public function getCurrentRoles(): array
|
||||
{
|
||||
if (($this->isMariaDB() && $this->getVersion() < 100500) || $this->getVersion() < 80000) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (SessionCache::has('mysql_cur_role')) {
|
||||
return SessionCache::get('mysql_cur_role');
|
||||
}
|
||||
|
||||
$role = $this->fetchValue('SELECT CURRENT_ROLE();');
|
||||
if ($role === false || $role === null || $role === 'NONE') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$role = array_map('trim', explode(',', str_replace('`', '', $role)));
|
||||
SessionCache::set('mysql_cur_role', $role);
|
||||
|
||||
return $role;
|
||||
}
|
||||
|
||||
public function isSuperUser(): bool
|
||||
{
|
||||
if (SessionCache::has('is_superuser')) {
|
||||
@ -1478,6 +1514,21 @@ class DatabaseInterface implements DbalInterface
|
||||
$query = QueryGenerator::getInformationSchemaDataForGranteeRequest($user, $host);
|
||||
$hasGrantPrivilege = (bool) $this->fetchValue($query);
|
||||
|
||||
if (! $hasGrantPrivilege) {
|
||||
foreach ($this->getCurrentRolesAndHost() as [$role, $roleHost]) {
|
||||
$query = QueryGenerator::getInformationSchemaDataForGranteeRequest($role, $roleHost ?? '');
|
||||
$result = $this->tryQuery($query);
|
||||
|
||||
if ($result) {
|
||||
$hasGrantPrivilege = (bool) $result->numRows();
|
||||
}
|
||||
|
||||
if ($hasGrantPrivilege) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SessionCache::set('is_grantuser', $hasGrantPrivilege);
|
||||
|
||||
return $hasGrantPrivilege;
|
||||
@ -1514,6 +1565,21 @@ class DatabaseInterface implements DbalInterface
|
||||
$query = QueryGenerator::getInformationSchemaDataForCreateRequest($user, $host);
|
||||
$hasCreatePrivilege = (bool) $this->fetchValue($query);
|
||||
|
||||
if (! $hasCreatePrivilege) {
|
||||
foreach ($this->getCurrentRolesAndHost() as [$role, $roleHost]) {
|
||||
$query = QueryGenerator::getInformationSchemaDataForCreateRequest($role, $roleHost ?? '');
|
||||
$result = $this->tryQuery($query);
|
||||
|
||||
if ($result) {
|
||||
$hasCreatePrivilege = (bool) $result->numRows();
|
||||
}
|
||||
|
||||
if ($hasCreatePrivilege) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SessionCache::set('is_createuser', $hasCreatePrivilege);
|
||||
|
||||
return $hasCreatePrivilege;
|
||||
@ -1548,6 +1614,24 @@ class DatabaseInterface implements DbalInterface
|
||||
return $this->currentUserAndHost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current role and host.
|
||||
*
|
||||
* @return array<int, array<int, string>> array of role and hostname
|
||||
*/
|
||||
public function getCurrentRolesAndHost(): array
|
||||
{
|
||||
if ($this->currentRoleAndHost === null) {
|
||||
$roles = $this->getCurrentRoles();
|
||||
|
||||
$this->currentRoleAndHost = array_map(static function (string $role) {
|
||||
return explode('@', $role);
|
||||
}, $roles);
|
||||
}
|
||||
|
||||
return $this->currentRoleAndHost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns value for lower_case_table_names variable
|
||||
*
|
||||
|
||||
@ -240,13 +240,20 @@ class Generator
|
||||
|
||||
public static function getInformationSchemaDataForCreateRequest(string $user, string $host): string
|
||||
{
|
||||
// second part of query is for MariaDB that not show roles inside INFORMATION_SCHEMA db
|
||||
return 'SELECT 1 FROM `INFORMATION_SCHEMA`.`USER_PRIVILEGES` '
|
||||
. "WHERE `PRIVILEGE_TYPE` = 'CREATE USER' AND "
|
||||
. "'''" . $user . "''@''" . $host . "''' LIKE `GRANTEE` LIMIT 1";
|
||||
. "'''" . $user . "''@''" . $host . "''' LIKE `GRANTEE`"
|
||||
. ' UNION '
|
||||
. 'SELECT 1 FROM mysql.user '
|
||||
. "WHERE `create_user_priv` = 'Y' COLLATE utf8mb4_general_ci AND "
|
||||
. "'" . $user . "' LIKE `User` AND '' LIKE `Host`"
|
||||
. ' LIMIT 1';
|
||||
}
|
||||
|
||||
public static function getInformationSchemaDataForGranteeRequest(string $user, string $host): string
|
||||
{
|
||||
// second part of query is for MariaDB that not show roles inside INFORMATION_SCHEMA db
|
||||
return 'SELECT 1 FROM ('
|
||||
. 'SELECT `GRANTEE`, `IS_GRANTABLE` FROM '
|
||||
. '`INFORMATION_SCHEMA`.`COLUMN_PRIVILEGES` UNION '
|
||||
@ -257,7 +264,12 @@ class Generator
|
||||
. 'SELECT `GRANTEE`, `IS_GRANTABLE` FROM '
|
||||
. '`INFORMATION_SCHEMA`.`USER_PRIVILEGES`) t '
|
||||
. "WHERE `IS_GRANTABLE` = 'YES' AND "
|
||||
. "'''" . $user . "''@''" . $host . "''' LIKE `GRANTEE` LIMIT 1";
|
||||
. "'''" . $user . "''@''" . $host . "''' LIKE `GRANTEE` "
|
||||
. ' UNION '
|
||||
. 'SELECT 1 FROM mysql.user '
|
||||
. "WHERE `create_user_priv` = 'Y' COLLATE utf8mb4_general_ci AND "
|
||||
. "'" . $user . "' LIKE `User` AND '' LIKE `Host`"
|
||||
. ' LIMIT 1';
|
||||
}
|
||||
|
||||
public static function getInformationSchemaForeignKeyConstraintsRequest(
|
||||
|
||||
@ -839,6 +839,7 @@ class Util
|
||||
SessionCache::remove('is_createuser');
|
||||
SessionCache::remove('is_grantuser');
|
||||
SessionCache::remove('mysql_cur_user');
|
||||
SessionCache::remove('mysql_cur_role');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -101,6 +101,75 @@ class DatabaseInterfaceTest extends AbstractTestCase
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for DBI::getCurrentRole() method.
|
||||
*
|
||||
* @param string[][]|false $value
|
||||
* @param string[] $string
|
||||
* @param string[][] $expected
|
||||
*/
|
||||
#[DataProvider('currentRolesData')]
|
||||
public function testGetCurrentRoles(
|
||||
string $version,
|
||||
bool $isRoleSupported,
|
||||
array|false $value,
|
||||
array $string,
|
||||
array $expected,
|
||||
): void {
|
||||
$dbiDummy = $this->createDbiDummy();
|
||||
$dbi = $this->createDatabaseInterface($dbiDummy);
|
||||
$dbi->setVersion(['@@version' => $version]);
|
||||
|
||||
SessionCache::remove('mysql_cur_role');
|
||||
|
||||
if ($isRoleSupported) {
|
||||
$dbiDummy->addResult('SELECT CURRENT_ROLE();', $value);
|
||||
}
|
||||
|
||||
self::assertSame($expected, $dbi->getCurrentRolesAndHost());
|
||||
|
||||
self::assertSame($string, $dbi->getCurrentRoles());
|
||||
|
||||
$dbiDummy->assertAllQueriesConsumed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for getCurrentRole() tests.
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
public static function currentRolesData(): array
|
||||
{
|
||||
return [
|
||||
['10.4.99-MariaDB', false, false, [], []],
|
||||
['5.7.35 - MySQL Community Server (GPL)', false, false, [], []],
|
||||
[
|
||||
'8.0.0 - MySQL Community Server - GPL',
|
||||
true,
|
||||
[['`role`@`localhost`']],
|
||||
['role@localhost'],
|
||||
[['role', 'localhost']],
|
||||
],
|
||||
[
|
||||
'8.0.0 - MySQL Community Server - GPL',
|
||||
true,
|
||||
[['`role`@`localhost`, `role2`@`localhost`']],
|
||||
['role@localhost', 'role2@localhost'],
|
||||
[['role', 'localhost'], ['role2', 'localhost']],
|
||||
],
|
||||
['8.0.0 - MySQL Community Server - GPL', true, [['@`localhost`']], ['@localhost'], [['', 'localhost']]],
|
||||
['10.5.0-MariaDB', true, [['`role`@`localhost`']], ['role@localhost'], [['role', 'localhost']]],
|
||||
[
|
||||
'10.5.0-MariaDB',
|
||||
true,
|
||||
[['`role`@`localhost`, `role2`@`localhost`']],
|
||||
['role@localhost', 'role2@localhost'],
|
||||
[['role', 'localhost'], ['role2', 'localhost']],
|
||||
],
|
||||
['10.5.0-MariaDB', true, [['@`localhost`']], ['@localhost'], [['', 'localhost']]],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for DBI::getSystemDatabase() method.
|
||||
*/
|
||||
|
||||
@ -96,6 +96,20 @@ final class NodeTest extends AbstractTestCase
|
||||
self::assertSame($child, $parent->getChild('child real name', true));
|
||||
}
|
||||
|
||||
public function testGetChildNodeWithEqualNumericName(): void
|
||||
{
|
||||
$config = new Config();
|
||||
$parent = new Node($config, 'parent');
|
||||
$childOne = new Node($config, '0');
|
||||
$childTwo = new Node($config, '00');
|
||||
$parent->addChild($childOne);
|
||||
$parent->addChild($childTwo);
|
||||
self::assertSame($childTwo, $parent->getChild('00'));
|
||||
self::assertSame($childOne, $parent->getChild('0'));
|
||||
self::assertSame($childTwo, $parent->getChild('00', true));
|
||||
self::assertSame($childOne, $parent->getChild('0', true));
|
||||
}
|
||||
|
||||
public function testRemoveChildNode(): void
|
||||
{
|
||||
$config = new Config();
|
||||
|
||||
@ -369,7 +369,9 @@ class DbiDummy implements DbiExtension
|
||||
[
|
||||
'query' => 'SELECT 1 FROM `INFORMATION_SCHEMA`.`USER_PRIVILEGES`'
|
||||
. " WHERE `PRIVILEGE_TYPE` = 'CREATE USER'"
|
||||
. " AND '''pma_test''@''localhost''' LIKE `GRANTEE` LIMIT 1",
|
||||
. " AND '''pma_test''@''localhost''' LIKE `GRANTEE`"
|
||||
. " UNION SELECT 1 FROM mysql.user WHERE `create_user_priv` = 'Y' COLLATE utf8mb4_general_ci"
|
||||
. " AND 'pma_test' LIKE `User` AND '' LIKE `Host` LIMIT 1",
|
||||
'result' => [['1']],
|
||||
],
|
||||
[
|
||||
@ -382,7 +384,9 @@ class DbiDummy implements DbiExtension
|
||||
. ' UNION SELECT `GRANTEE`, `IS_GRANTABLE`'
|
||||
. ' FROM `INFORMATION_SCHEMA`.`USER_PRIVILEGES`) t'
|
||||
. " WHERE `IS_GRANTABLE` = 'YES'"
|
||||
. " AND '''pma_test''@''localhost''' LIKE `GRANTEE` LIMIT 1",
|
||||
. " AND '''pma_test''@''localhost''' LIKE `GRANTEE`"
|
||||
. " UNION SELECT 1 FROM mysql.user WHERE `create_user_priv` = 'Y' COLLATE utf8mb4_general_ci"
|
||||
. " AND 'pma_test' LIKE `User` AND '' LIKE `Host` LIMIT 1",
|
||||
'result' => [['1']],
|
||||
],
|
||||
[
|
||||
|
||||
Loading…
Reference in New Issue
Block a user