Skip to content

Commit a82c7fc

Browse files
committed
Merge branch 'QA_5_2'
Signed-off-by: Maurício Meneghini Fauth <mauricio@fauth.dev>
2 parents 713ae87 + 78335aa commit a82c7fc

File tree

9 files changed

+246
-4
lines changed

9 files changed

+246
-4
lines changed

ChangeLog

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ phpMyAdmin - ChangeLog
7878
- issue #17363 Fix duplicate route parameter after logging in
7979
- issue #15670 Fix case where the data is truncated after changing a longtext column's collation
8080
- issue #18865 Fix missing text-nowrap for timestamps columns
81+
- issue #19022 Fix case where tables from wrong database is loaded in navigation tree
8182

8283
5.2.1 (2023-02-07)
8384
- issue #17522 Fix case where the routes cache file is invalid

phpstan-baseline.neon

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6715,6 +6715,16 @@ parameters:
67156715
count: 1
67166716
path: src/DatabaseInterface.php
67176717

6718+
-
6719+
message: "#^Method PhpMyAdmin\\\\DatabaseInterface\\:\\:getCurrentRoles\\(\\) should return array\\<int, array\\<int, string\\>\\> but returns array\\<int, string\\>\\.$#"
6720+
count: 1
6721+
path: src/DatabaseInterface.php
6722+
6723+
-
6724+
message: "#^Method PhpMyAdmin\\\\DatabaseInterface\\:\\:getCurrentRoles\\(\\) should return array\\<int, array\\<int, string\\>\\> but returns mixed\\.$#"
6725+
count: 1
6726+
path: src/DatabaseInterface.php
6727+
67186728
-
67196729
message: "#^Method PhpMyAdmin\\\\DatabaseInterface\\:\\:getCurrentUser\\(\\) should return string but returns mixed\\.$#"
67206730
count: 1
@@ -6745,6 +6755,11 @@ parameters:
67456755
count: 1
67466756
path: src/DatabaseInterface.php
67476757

6758+
-
6759+
message: "#^Only booleans are allowed in an if condition, PhpMyAdmin\\\\Dbal\\\\ResultInterface\\|false given\\.$#"
6760+
count: 2
6761+
path: src/DatabaseInterface.php
6762+
67486763
-
67496764
message: "#^Only booleans are allowed in an if condition, int\\|false given\\.$#"
67506765
count: 2
@@ -6755,6 +6770,11 @@ parameters:
67556770
count: 1
67566771
path: src/DatabaseInterface.php
67576772

6773+
-
6774+
message: "#^Parameter \\#1 \\$callback of function array_map expects \\(callable\\(array\\<int, string\\>\\)\\: mixed\\)\\|null, Closure\\(string\\)\\: non\\-empty\\-array\\<int, string\\> given\\.$#"
6775+
count: 1
6776+
path: src/DatabaseInterface.php
6777+
67586778
-
67596779
message: "#^Parameter \\#1 \\$callback of function array_map expects \\(callable\\(mixed\\)\\: mixed\\)\\|null, Closure\\(string\\)\\: non\\-empty\\-string given\\.$#"
67606780
count: 1
@@ -6805,6 +6825,11 @@ parameters:
68056825
count: 1
68066826
path: src/DatabaseInterface.php
68076827

6828+
-
6829+
message: "#^Variable \\$roleHost on left side of \\?\\? always exists and is not nullable\\.$#"
6830+
count: 2
6831+
path: src/DatabaseInterface.php
6832+
68086833
-
68096834
message: "#^Method PhpMyAdmin\\\\Dbal\\\\MysqliResult\\:\\:fetchAllAssoc\\(\\) should return array\\<int, array\\<string\\|null\\>\\> but returns array\\.$#"
68106835
count: 1
@@ -15905,6 +15930,16 @@ parameters:
1590515930
count: 1
1590615931
path: tests/unit/DatabaseInterfaceTest.php
1590715932

15933+
-
15934+
message: "#^Call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertSame\\(\\) with array\\<string\\> and array\\<int, array\\<int, string\\>\\> will always evaluate to false\\.$#"
15935+
count: 1
15936+
path: tests/unit/DatabaseInterfaceTest.php
15937+
15938+
-
15939+
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\\.$#"
15940+
count: 1
15941+
path: tests/unit/DatabaseInterfaceTest.php
15942+
1590815943
-
1590915944
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, \\.\\.\\.\\}\\.$#"
1591015945
count: 1

psalm-baseline.xml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4911,6 +4911,15 @@
49114911
<DeprecatedProperty>
49124912
<code><![CDATA[Routing::$route]]></code>
49134913
</DeprecatedProperty>
4914+
<DocblockTypeContradiction>
4915+
<code><![CDATA['']]></code>
4916+
<code><![CDATA['']]></code>
4917+
</DocblockTypeContradiction>
4918+
<InvalidArgument>
4919+
<code><![CDATA[static function (string $role) {
4920+
return explode('@', $role);
4921+
}]]></code>
4922+
</InvalidArgument>
49144923
<InvalidOperand>
49154924
<code><![CDATA[$row['Data_free']]]></code>
49164925
<code><![CDATA[$row['Data_length']]]></code>
@@ -4919,6 +4928,9 @@
49194928
<code><![CDATA[$row['Max_data_length']]]></code>
49204929
<code><![CDATA[$row['Rows']]]></code>
49214930
</InvalidOperand>
4931+
<InvalidReturnStatement>
4932+
<code><![CDATA[$role]]></code>
4933+
</InvalidReturnStatement>
49224934
<MixedArgument>
49234935
<code><![CDATA[$a]]></code>
49244936
<code><![CDATA[$b]]></code>
@@ -4953,6 +4965,7 @@
49534965
<code><![CDATA[$this->versionString]]></code>
49544966
</MixedAssignment>
49554967
<MixedInferredReturnType>
4968+
<code><![CDATA[array<int, array<int, string>>]]></code>
49564969
<code><![CDATA[mixed[]]]></code>
49574970
<code><![CDATA[mixed[]]]></code>
49584971
<code><![CDATA[string]]></code>
@@ -4963,6 +4976,7 @@
49634976
</MixedOperand>
49644977
<MixedReturnStatement>
49654978
<code><![CDATA[$resultRows]]></code>
4979+
<code><![CDATA[SessionCache::get('mysql_cur_role')]]></code>
49664980
<code><![CDATA[SessionCache::get('mysql_cur_user')]]></code>
49674981
<code><![CDATA[reset($columns)]]></code>
49684982
<code><![CDATA[reset($columns)]]></code>
@@ -5029,6 +5043,10 @@
50295043
<code><![CDATA[bool]]></code>
50305044
<code><![CDATA[bool]]></code>
50315045
</PossiblyUnusedReturnValue>
5046+
<RedundantConditionGivenDocblockType>
5047+
<code><![CDATA[$roleHost]]></code>
5048+
<code><![CDATA[$roleHost]]></code>
5049+
</RedundantConditionGivenDocblockType>
50325050
<RiskyTruthyFalsyComparison>
50335051
<code><![CDATA[$index->isUnique()]]></code>
50345052
<code><![CDATA[$limitCount]]></code>
@@ -13357,6 +13375,9 @@
1335713375
</PossiblyUnusedMethod>
1335813376
</file>
1335913377
<file src="tests/unit/DatabaseInterfaceTest.php">
13378+
<ArgumentTypeCoercion>
13379+
<code><![CDATA[$value]]></code>
13380+
</ArgumentTypeCoercion>
1336013381
<DeprecatedMethod>
1336113382
<code><![CDATA[Config::getInstance()]]></code>
1336213383
<code><![CDATA[Config::getInstance()]]></code>
@@ -13369,6 +13390,7 @@
1336913390
<code><![CDATA[DatabaseInterface::getInstance()]]></code>
1337013391
</DeprecatedMethod>
1337113392
<PossiblyUnusedMethod>
13393+
<code><![CDATA[currentRolesData]]></code>
1337213394
<code><![CDATA[currentUserData]]></code>
1337313395
<code><![CDATA[errorData]]></code>
1337413396
<code><![CDATA[isAmazonRdsData]]></code>

src/DatabaseInterface.php

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
use function reset;
5252
use function sprintf;
5353
use function str_contains;
54+
use function str_replace;
5455
use function str_starts_with;
5556
use function stripos;
5657
use function strnatcasecmp;
@@ -110,6 +111,9 @@ class DatabaseInterface implements DbalInterface
110111
/** @var array<int, string>|null */
111112
private array|null $currentUserAndHost = null;
112113

114+
/** @var array<int, array<int, string>>|null Current role and host cache */
115+
private array|null $currentRoleAndHost = null;
116+
113117
/**
114118
* @var int|null lower_case_table_names value cache
115119
* @psalm-var 0|1|2|null
@@ -1430,6 +1434,38 @@ public function getCurrentUser(): string
14301434
return '@';
14311435
}
14321436

1437+
/**
1438+
* gets the current role with host. Role maybe multiple separated by comma
1439+
* Support start from MySQL 8.x / MariaDB 10.0.5
1440+
*
1441+
* @see https://dev.mysql.com/doc/refman/8.0/en/roles.html
1442+
* @see https://dev.mysql.com/doc/refman/8.0/en/information-functions.html#function_current-role
1443+
* @see https://mariadb.com/kb/en/mariadb-1005-release-notes/#newly-implemented-features
1444+
* @see https://mariadb.com/kb/en/roles_overview/
1445+
*
1446+
* @return array<int, array<int, string>> the current roles i.e. array of role@host
1447+
*/
1448+
public function getCurrentRoles(): array
1449+
{
1450+
if (($this->isMariaDB() && $this->getVersion() < 100500) || $this->getVersion() < 80000) {
1451+
return [];
1452+
}
1453+
1454+
if (SessionCache::has('mysql_cur_role')) {
1455+
return SessionCache::get('mysql_cur_role');
1456+
}
1457+
1458+
$role = $this->fetchValue('SELECT CURRENT_ROLE();');
1459+
if ($role === false || $role === null || $role === 'NONE') {
1460+
return [];
1461+
}
1462+
1463+
$role = array_map('trim', explode(',', str_replace('`', '', $role)));
1464+
SessionCache::set('mysql_cur_role', $role);
1465+
1466+
return $role;
1467+
}
1468+
14331469
public function isSuperUser(): bool
14341470
{
14351471
if (SessionCache::has('is_superuser')) {
@@ -1478,6 +1514,21 @@ public function isGrantUser(): bool
14781514
$query = QueryGenerator::getInformationSchemaDataForGranteeRequest($user, $host);
14791515
$hasGrantPrivilege = (bool) $this->fetchValue($query);
14801516

1517+
if (! $hasGrantPrivilege) {
1518+
foreach ($this->getCurrentRolesAndHost() as [$role, $roleHost]) {
1519+
$query = QueryGenerator::getInformationSchemaDataForGranteeRequest($role, $roleHost ?? '');
1520+
$result = $this->tryQuery($query);
1521+
1522+
if ($result) {
1523+
$hasGrantPrivilege = (bool) $result->numRows();
1524+
}
1525+
1526+
if ($hasGrantPrivilege) {
1527+
break;
1528+
}
1529+
}
1530+
}
1531+
14811532
SessionCache::set('is_grantuser', $hasGrantPrivilege);
14821533

14831534
return $hasGrantPrivilege;
@@ -1514,6 +1565,21 @@ public function isCreateUser(): bool
15141565
$query = QueryGenerator::getInformationSchemaDataForCreateRequest($user, $host);
15151566
$hasCreatePrivilege = (bool) $this->fetchValue($query);
15161567

1568+
if (! $hasCreatePrivilege) {
1569+
foreach ($this->getCurrentRolesAndHost() as [$role, $roleHost]) {
1570+
$query = QueryGenerator::getInformationSchemaDataForCreateRequest($role, $roleHost ?? '');
1571+
$result = $this->tryQuery($query);
1572+
1573+
if ($result) {
1574+
$hasCreatePrivilege = (bool) $result->numRows();
1575+
}
1576+
1577+
if ($hasCreatePrivilege) {
1578+
break;
1579+
}
1580+
}
1581+
}
1582+
15171583
SessionCache::set('is_createuser', $hasCreatePrivilege);
15181584

15191585
return $hasCreatePrivilege;
@@ -1548,6 +1614,24 @@ public function getCurrentUserAndHost(): array
15481614
return $this->currentUserAndHost;
15491615
}
15501616

1617+
/**
1618+
* Get the current role and host.
1619+
*
1620+
* @return array<int, array<int, string>> array of role and hostname
1621+
*/
1622+
public function getCurrentRolesAndHost(): array
1623+
{
1624+
if ($this->currentRoleAndHost === null) {
1625+
$roles = $this->getCurrentRoles();
1626+
1627+
$this->currentRoleAndHost = array_map(static function (string $role) {
1628+
return explode('@', $role);
1629+
}, $roles);
1630+
}
1631+
1632+
return $this->currentRoleAndHost;
1633+
}
1634+
15511635
/**
15521636
* Returns value for lower_case_table_names variable
15531637
*

src/Query/Generator.php

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,13 +240,20 @@ public static function getCreateTrigger(Trigger $newTrigger, string $delimiter):
240240

241241
public static function getInformationSchemaDataForCreateRequest(string $user, string $host): string
242242
{
243+
// second part of query is for MariaDB that not show roles inside INFORMATION_SCHEMA db
243244
return 'SELECT 1 FROM `INFORMATION_SCHEMA`.`USER_PRIVILEGES` '
244245
. "WHERE `PRIVILEGE_TYPE` = 'CREATE USER' AND "
245-
. "'''" . $user . "''@''" . $host . "''' LIKE `GRANTEE` LIMIT 1";
246+
. "'''" . $user . "''@''" . $host . "''' LIKE `GRANTEE`"
247+
. ' UNION '
248+
. 'SELECT 1 FROM mysql.user '
249+
. "WHERE `create_user_priv` = 'Y' COLLATE utf8mb4_general_ci AND "
250+
. "'" . $user . "' LIKE `User` AND '' LIKE `Host`"
251+
. ' LIMIT 1';
246252
}
247253

248254
public static function getInformationSchemaDataForGranteeRequest(string $user, string $host): string
249255
{
256+
// second part of query is for MariaDB that not show roles inside INFORMATION_SCHEMA db
250257
return 'SELECT 1 FROM ('
251258
. 'SELECT `GRANTEE`, `IS_GRANTABLE` FROM '
252259
. '`INFORMATION_SCHEMA`.`COLUMN_PRIVILEGES` UNION '
@@ -257,7 +264,12 @@ public static function getInformationSchemaDataForGranteeRequest(string $user, s
257264
. 'SELECT `GRANTEE`, `IS_GRANTABLE` FROM '
258265
. '`INFORMATION_SCHEMA`.`USER_PRIVILEGES`) t '
259266
. "WHERE `IS_GRANTABLE` = 'YES' AND "
260-
. "'''" . $user . "''@''" . $host . "''' LIKE `GRANTEE` LIMIT 1";
267+
. "'''" . $user . "''@''" . $host . "''' LIKE `GRANTEE` "
268+
. ' UNION '
269+
. 'SELECT 1 FROM mysql.user '
270+
. "WHERE `create_user_priv` = 'Y' COLLATE utf8mb4_general_ci AND "
271+
. "'" . $user . "' LIKE `User` AND '' LIKE `Host`"
272+
. ' LIMIT 1';
261273
}
262274

263275
public static function getInformationSchemaForeignKeyConstraintsRequest(

src/Util.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,7 @@ public static function clearUserCache(): void
839839
SessionCache::remove('is_createuser');
840840
SessionCache::remove('is_grantuser');
841841
SessionCache::remove('mysql_cur_user');
842+
SessionCache::remove('mysql_cur_role');
842843
}
843844

844845
/**

tests/unit/DatabaseInterfaceTest.php

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,75 @@ public static function currentUserData(): array
101101
];
102102
}
103103

104+
/**
105+
* Tests for DBI::getCurrentRole() method.
106+
*
107+
* @param string[][]|false $value
108+
* @param string[] $string
109+
* @param string[][] $expected
110+
*/
111+
#[DataProvider('currentRolesData')]
112+
public function testGetCurrentRoles(
113+
string $version,
114+
bool $isRoleSupported,
115+
array|false $value,
116+
array $string,
117+
array $expected,
118+
): void {
119+
$dbiDummy = $this->createDbiDummy();
120+
$dbi = $this->createDatabaseInterface($dbiDummy);
121+
$dbi->setVersion(['@@version' => $version]);
122+
123+
SessionCache::remove('mysql_cur_role');
124+
125+
if ($isRoleSupported) {
126+
$dbiDummy->addResult('SELECT CURRENT_ROLE();', $value);
127+
}
128+
129+
self::assertSame($expected, $dbi->getCurrentRolesAndHost());
130+
131+
self::assertSame($string, $dbi->getCurrentRoles());
132+
133+
$dbiDummy->assertAllQueriesConsumed();
134+
}
135+
136+
/**
137+
* Data provider for getCurrentRole() tests.
138+
*
139+
* @return mixed[]
140+
*/
141+
public static function currentRolesData(): array
142+
{
143+
return [
144+
['10.4.99-MariaDB', false, false, [], []],
145+
['5.7.35 - MySQL Community Server (GPL)', false, false, [], []],
146+
[
147+
'8.0.0 - MySQL Community Server - GPL',
148+
true,
149+
[['`role`@`localhost`']],
150+
['role@localhost'],
151+
[['role', 'localhost']],
152+
],
153+
[
154+
'8.0.0 - MySQL Community Server - GPL',
155+
true,
156+
[['`role`@`localhost`, `role2`@`localhost`']],
157+
['role@localhost', 'role2@localhost'],
158+
[['role', 'localhost'], ['role2', 'localhost']],
159+
],
160+
['8.0.0 - MySQL Community Server - GPL', true, [['@`localhost`']], ['@localhost'], [['', 'localhost']]],
161+
['10.5.0-MariaDB', true, [['`role`@`localhost`']], ['role@localhost'], [['role', 'localhost']]],
162+
[
163+
'10.5.0-MariaDB',
164+
true,
165+
[['`role`@`localhost`, `role2`@`localhost`']],
166+
['role@localhost', 'role2@localhost'],
167+
[['role', 'localhost'], ['role2', 'localhost']],
168+
],
169+
['10.5.0-MariaDB', true, [['@`localhost`']], ['@localhost'], [['', 'localhost']]],
170+
];
171+
}
172+
104173
/**
105174
* Tests for DBI::getSystemDatabase() method.
106175
*/

0 commit comments

Comments
 (0)