Create value object ShowGrants

Signed-off-by: Kamil Tekiela <tekiela246@gmail.com>
This commit is contained in:
Kamil Tekiela 2023-04-15 23:52:32 +01:00
parent b0ef88c6f1
commit 862e69dd68
6 changed files with 151 additions and 234 deletions

View File

@ -25,91 +25,42 @@ class CheckUserPrivileges
{
}
/**
* Extracts details from a result row of a SHOW GRANT query
*
* @param string $row grant row
*
* @return array<int,string>
*/
public function getItemsFromShowGrantsRow(string $row): array
{
$dbNameOffset = mb_strpos($row, ' ON ') + 4;
$tableNameEndOffset = mb_strpos($row, ' TO ');
$tableNameStartOffset = false;
$tableNameStartOffset2 = mb_strpos($row, '`.', $dbNameOffset);
if ($tableNameStartOffset2 && $tableNameStartOffset2 < $tableNameEndOffset) {
$tableNameStartOffset = $tableNameStartOffset2 + 1;
}
if ($tableNameStartOffset === false) {
$tableNameStartOffset = mb_strpos($row, '.', $dbNameOffset);
}
$showGrantsDbName = mb_substr($row, $dbNameOffset, $tableNameStartOffset - $dbNameOffset);
$showGrantsDbName = Util::unQuote($showGrantsDbName, '`');
$showGrantsString = mb_substr(
$row,
6,
mb_strpos($row, ' ON ') - 6,
);
$showGrantsTableName = mb_substr(
$row,
$tableNameStartOffset + 1,
$tableNameEndOffset - $tableNameStartOffset - 1,
);
$showGrantsTableName = Util::unQuote($showGrantsTableName, '`');
return [$showGrantsString, $showGrantsDbName, $showGrantsTableName];
}
/**
* Check if user has required privileges for
* performing 'Adjust privileges' operations
*
* @param string $showGrantsString string containing grants for user
* @param string $showGrantsDbName name of db extracted from grant string
* @param string $showGrantsTableName name of table extracted from grant string
*/
public function checkRequiredPrivilegesForAdjust(
string $showGrantsString,
string $showGrantsDbName,
string $showGrantsTableName,
ShowGrants $showGrants,
): void {
// '... ALL PRIVILEGES ON *.* ...' OR '... ALL PRIVILEGES ON `mysql`.* ..'
// OR
// SELECT, INSERT, UPDATE, DELETE .... ON *.* OR `mysql`.*
if (
$showGrantsString !== 'ALL'
&& $showGrantsString !== 'ALL PRIVILEGES'
&& (mb_strpos($showGrantsString, 'SELECT, INSERT, UPDATE, DELETE') === false)
$showGrants->grants !== 'ALL'
&& $showGrants->grants !== 'ALL PRIVILEGES'
&& (mb_strpos($showGrants->grants, 'SELECT, INSERT, UPDATE, DELETE') === false)
) {
return;
}
if ($showGrantsDbName === '*' && $showGrantsTableName === '*') {
if ($showGrants->dbName === '*' && $showGrants->tableName === '*') {
$GLOBALS['col_priv'] = true;
$GLOBALS['db_priv'] = true;
$GLOBALS['proc_priv'] = true;
$GLOBALS['table_priv'] = true;
if ($showGrantsString === 'ALL PRIVILEGES' || $showGrantsString === 'ALL') {
if ($showGrants->grants === 'ALL PRIVILEGES' || $showGrants->grants === 'ALL') {
$GLOBALS['is_reload_priv'] = true;
}
}
// check for specific tables in `mysql` db
// Ex. '... ALL PRIVILEGES on `mysql`.`columns_priv` .. '
if ($showGrantsDbName !== 'mysql') {
if ($showGrants->dbName !== 'mysql') {
return;
}
switch ($showGrantsTableName) {
switch ($showGrants->tableName) {
case 'columns_priv':
$GLOBALS['col_priv'] = true;
break;
@ -184,37 +135,37 @@ class CheckUserPrivileges
$re1 = '(^|[^\\\\])(\\\)+'; // escaped wildcards
while ($showGrants = $showGrantsResult->fetchValue()) {
[$showGrantsString, $showGrantsDbName, $showGrantsTableName] = $this->getItemsFromShowGrantsRow($showGrants);
$showGrants = new ShowGrants($showGrants);
if ($showGrantsDbName === '*') {
if ($showGrantsString !== 'USAGE') {
if ($showGrants->dbName === '*') {
if ($showGrants->grants !== 'USAGE') {
$GLOBALS['dbs_to_test'] = false;
}
} elseif ($GLOBALS['dbs_to_test'] !== false) {
$GLOBALS['dbs_to_test'][] = $showGrantsDbName;
$GLOBALS['dbs_to_test'][] = $showGrants->dbName;
}
if (str_contains($showGrantsString, 'RELOAD')) {
if (str_contains($showGrants->grants, 'RELOAD')) {
$GLOBALS['is_reload_priv'] = true;
}
// check for the required privileges for adjust
$this->checkRequiredPrivilegesForAdjust($showGrantsString, $showGrantsDbName, $showGrantsTableName);
$this->checkRequiredPrivilegesForAdjust($showGrants);
/**
* @todo if we find CREATE VIEW but not CREATE, do not offer
* the create database dialog box
*/
if (
$showGrantsString !== 'ALL'
&& $showGrantsString !== 'ALL PRIVILEGES'
&& $showGrantsString !== 'CREATE'
&& ! str_contains($showGrantsString, 'CREATE,')
$showGrants->grants !== 'ALL'
&& $showGrants->grants !== 'ALL PRIVILEGES'
&& $showGrants->grants !== 'CREATE'
&& ! str_contains($showGrants->grants, 'CREATE,')
) {
continue;
}
if ($showGrantsDbName === '*') {
if ($showGrants->dbName === '*') {
// a global CREATE privilege
$GLOBALS['is_create_db_priv'] = true;
$GLOBALS['is_reload_priv'] = true;
@ -224,7 +175,7 @@ class CheckUserPrivileges
break;
}
$dbNameToTest = Util::backquote($showGrantsDbName);
$dbNameToTest = Util::backquote($showGrants->dbName);
if ($GLOBALS['is_create_db_priv']) {
// no need for any more tests if we already know this
@ -233,8 +184,8 @@ class CheckUserPrivileges
// does this db exist?
if (
(! preg_match('/' . $re0 . '%|_/', $showGrantsDbName)
|| preg_match('/\\\\%|\\\\_/', $showGrantsDbName))
(! preg_match('/' . $re0 . '%|_/', $showGrants->dbName)
|| preg_match('/\\\\%|\\\\_/', $showGrants->dbName))
&& ($this->dbi->tryQuery(
'USE ' . preg_replace(
'/' . $re1 . '(%|_)/',
@ -251,7 +202,7 @@ class CheckUserPrivileges
* Do not handle the underscore wildcard
* (this case must be rare anyway)
*/
$GLOBALS['db_to_create'] = preg_replace('/' . $re0 . '%/', '\\1', $showGrantsDbName);
$GLOBALS['db_to_create'] = preg_replace('/' . $re0 . '%/', '\\1', $showGrants->dbName);
$GLOBALS['db_to_create'] = preg_replace('/' . $re1 . '(%|_)/', '\\1\\3', $GLOBALS['db_to_create']);
$GLOBALS['is_create_db_priv'] = true;

View File

@ -0,0 +1,70 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin;
use function mb_strpos;
use function mb_substr;
final class ShowGrants
{
public readonly string $grants;
public readonly string $tableName;
public readonly string $dbName;
private int $tableNameEndOffset;
private int $tableNameStartOffset;
private int $dbNameOffset;
public function __construct(string $showGrants)
{
$this->dbNameOffset = (int) mb_strpos($showGrants, ' ON ') + 4;
$this->tableNameEndOffset = (int) mb_strpos($showGrants, ' TO ');
$this->tableNameStartOffset = $this->getTableNameStartOffset($showGrants);
$this->grants = $this->getShowGrantsString($showGrants);
$this->tableName = $this->getGrantsTableName($showGrants);
$this->dbName = $this->getGrantsDbName($showGrants);
}
private function getTableNameStartOffset(string $showGrants): int
{
$tableNameStartOffset = mb_strpos($showGrants, '`.', $this->dbNameOffset);
if (
$tableNameStartOffset !== false
&& $tableNameStartOffset !== 0
&& $tableNameStartOffset < $this->tableNameEndOffset
) {
return $tableNameStartOffset + 1;
}
return (int) mb_strpos($showGrants, '.', $this->dbNameOffset);
}
private function getShowGrantsString(string $showGrants): string
{
return mb_substr($showGrants, 6, (int) mb_strpos($showGrants, ' ON ') - 6);
}
private function getGrantsTableName(string $showGrants): string
{
$showGrantsTableName = mb_substr(
$showGrants,
$this->tableNameStartOffset + 1,
$this->tableNameEndOffset - $this->tableNameStartOffset - 1,
);
return Util::unQuote($showGrantsTableName, '`');
}
private function getGrantsDbName(string $showGrants): string
{
$showGrantsDbName = mb_substr(
$showGrants,
$this->dbNameOffset,
$this->tableNameStartOffset - $this->dbNameOffset,
);
return Util::unQuote($showGrantsDbName, '`');
}
}

View File

@ -90,26 +90,6 @@ parameters:
count: 1
path: libraries/classes/Charsets.php
-
message: "#^Only numeric types are allowed in \\+, int\\<0, max\\>\\|false given on the left side\\.$#"
count: 2
path: libraries/classes/CheckUserPrivileges.php
-
message: "#^Only numeric types are allowed in \\-, int\\<0, max\\>\\|false given on the left side\\.$#"
count: 3
path: libraries/classes/CheckUserPrivileges.php
-
message: "#^Only numeric types are allowed in \\-, int\\<0, max\\>\\|false given on the right side\\.$#"
count: 1
path: libraries/classes/CheckUserPrivileges.php
-
message: "#^Parameter \\#1 \\$row of method PhpMyAdmin\\\\CheckUserPrivileges\\:\\:getItemsFromShowGrantsRow\\(\\) expects string, string\\|null given\\.$#"
count: 1
path: libraries/classes/CheckUserPrivileges.php
-
message: "#^Parameter \\#3 \\$subject of function preg_replace expects array\\|string, string\\|null given\\.$#"
count: 1

View File

@ -137,17 +137,6 @@
<code><![CDATA[$GLOBALS['proc_priv']]]></code>
<code><![CDATA[$GLOBALS['table_priv']]]></code>
</MixedAssignment>
<PossiblyFalseOperand>
<code>$tableNameEndOffset</code>
<code>$tableNameStartOffset</code>
<code>$tableNameStartOffset</code>
<code>$tableNameStartOffset</code>
<code><![CDATA[mb_strpos($row, ' ON ')]]></code>
<code><![CDATA[mb_strpos($row, ' ON ')]]></code>
</PossiblyFalseOperand>
<PossiblyNullArgument>
<code>$row[0]</code>
</PossiblyNullArgument>
</file>
<file src="libraries/classes/Command/CacheWarmupCommand.php">
<UnusedClass>
@ -14089,12 +14078,6 @@
<code>assertTrue</code>
<code>assertTrue</code>
</RedundantConditionGivenDocblockType>
<UnusedVariable>
<code>$showGrantsStr</code>
<code>$showGrantsStr</code>
<code>$showGrantsTblname</code>
<code>$showGrantsTblname</code>
</UnusedVariable>
</file>
<file src="test/classes/Command/SetVersionCommandTest.php">
<PossiblyInvalidArgument>

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace PhpMyAdmin\Tests;
use PhpMyAdmin\CheckUserPrivileges;
use PhpMyAdmin\ShowGrants;
/** @covers \PhpMyAdmin\CheckUserPrivileges */
class CheckUserPrivilegesTest extends AbstractTestCase
@ -30,103 +31,16 @@ class CheckUserPrivilegesTest extends AbstractTestCase
$this->checkUserPrivileges = new CheckUserPrivileges($GLOBALS['dbi']);
}
/**
* Test for getItemsFromShowGrantsRow
*/
public function testGetItemsFromShowGrantsRow(): void
{
// TEST CASE 1
[
$showGrantsStr,
$showGrantsDbname,
$showGrantsTblname,
] = $this->checkUserPrivileges->getItemsFromShowGrantsRow(
'GRANT ALL PRIVILEGES ON *.* TO \'root\'@\'localhost\' WITH GRANT OPTION',
);
$this->assertEquals('ALL PRIVILEGES', $showGrantsStr);
$this->assertEquals('*', $showGrantsDbname);
$this->assertEquals('*', $showGrantsTblname);
// TEST CASE 2
[
$showGrantsStr,
$showGrantsDbname,
$showGrantsTblname,
] = $this->checkUserPrivileges->getItemsFromShowGrantsRow(
'GRANT ALL PRIVILEGES ON `mysql`.* TO \'root\'@\'localhost\' WITH GRANT OPTION',
);
$this->assertEquals('ALL PRIVILEGES', $showGrantsStr);
$this->assertEquals('mysql', $showGrantsDbname);
$this->assertEquals('*', $showGrantsTblname);
// TEST CASE 3
[
$showGrantsStr,
$showGrantsDbname,
$showGrantsTblname,
] = $this->checkUserPrivileges->getItemsFromShowGrantsRow(
'GRANT SELECT, INSERT, UPDATE, DELETE ON `mysql`.`columns_priv` TO \'root\'@\'localhost\'',
);
$this->assertEquals('SELECT, INSERT, UPDATE, DELETE', $showGrantsStr);
$this->assertEquals('mysql', $showGrantsDbname);
$this->assertEquals('columns_priv', $showGrantsTblname);
// TEST CASE 4
[
$showGrantsStr,
$showGrantsDbname,
$showGrantsTblname,
] = $this->checkUserPrivileges->getItemsFromShowGrantsRow(
'GRANT ALL PRIVILEGES ON `cptest\_.`.* TO \'cptest\'@\'localhost\'',
);
$this->assertEquals('cptest\_.', $showGrantsDbname);
[
$showGrantsStr,
$showGrantsDbname,
$showGrantsTblname,
] = $this->checkUserPrivileges->getItemsFromShowGrantsRow(
'GRANT ALL PRIVILEGES ON `cptest\_.a.b.c.d.e.f.g.h.i.j.k.'
. 'l.m.n.o.p.q.r.s.t.u.v.w.x.y.z`.* TO \'cptest\'@\'localhost\'',
);
$this->assertEquals('cptest\_.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z', $showGrantsDbname);
}
/**
* Test for checkRequiredPrivilegesForAdjust
*/
public function testCheckRequiredPrivilegesForAdjust(): void
{
// TEST CASE 1
[
$showGrantsStr,
$showGrantsDbname,
$showGrantsTblname,
] = $this->checkUserPrivileges->getItemsFromShowGrantsRow(
'GRANT ALL PRIVILEGES ON *.* TO \'root\'@\'localhost\' WITH GRANT OPTION',
);
$showGrants = new ShowGrants('GRANT ALL PRIVILEGES ON *.* TO \'root\'@\'localhost\' WITH GRANT OPTION');
// call the to-be-tested function
$this->checkUserPrivileges->checkRequiredPrivilegesForAdjust(
$showGrantsStr,
$showGrantsDbname,
$showGrantsTblname,
);
$this->checkUserPrivileges->checkRequiredPrivilegesForAdjust($showGrants);
$this->assertTrue($GLOBALS['col_priv']);
@ -140,20 +54,10 @@ class CheckUserPrivilegesTest extends AbstractTestCase
$this->setUp();
// TEST CASE 2
[
$showGrantsStr,
$showGrantsDbname,
$showGrantsTblname,
] = $this->checkUserPrivileges->getItemsFromShowGrantsRow(
'GRANT ALL PRIVILEGES ON `mysql`.* TO \'root\'@\'localhost\' WITH GRANT OPTION',
);
$showGrants = new ShowGrants('GRANT ALL PRIVILEGES ON `mysql`.* TO \'root\'@\'localhost\' WITH GRANT OPTION');
// call the to-be-tested function
$this->checkUserPrivileges->checkRequiredPrivilegesForAdjust(
$showGrantsStr,
$showGrantsDbname,
$showGrantsTblname,
);
$this->checkUserPrivileges->checkRequiredPrivilegesForAdjust($showGrants);
$this->assertTrue($GLOBALS['col_priv']);
@ -167,20 +71,10 @@ class CheckUserPrivilegesTest extends AbstractTestCase
$this->setUp();
// TEST CASE 3
[
$showGrantsStr,
$showGrantsDbname,
$showGrantsTblname,
] = $this->checkUserPrivileges->getItemsFromShowGrantsRow(
'GRANT SELECT, INSERT, UPDATE, DELETE ON `mysql`.* TO \'root\'@\'localhost\'',
);
$showGrants = new ShowGrants('GRANT SELECT, INSERT, UPDATE, DELETE ON `mysql`.* TO \'root\'@\'localhost\'');
// call the to-be-tested function
$this->checkUserPrivileges->checkRequiredPrivilegesForAdjust(
$showGrantsStr,
$showGrantsDbname,
$showGrantsTblname,
);
$this->checkUserPrivileges->checkRequiredPrivilegesForAdjust($showGrants);
$this->assertTrue($GLOBALS['col_priv']);
@ -194,20 +88,10 @@ class CheckUserPrivilegesTest extends AbstractTestCase
$this->setUp();
// TEST CASE 4
[
$showGrantsStr,
$showGrantsDbname,
$showGrantsTblname,
] = $this->checkUserPrivileges->getItemsFromShowGrantsRow(
'GRANT SELECT, INSERT, UPDATE, DELETE ON `mysql`.`db` TO \'root\'@\'localhost\'',
);
$showGrants = new ShowGrants('GRANT SELECT, INSERT, UPDATE, DELETE ON `mysql`.`db` TO \'root\'@\'localhost\'');
// call the to-be-tested function
$this->checkUserPrivileges->checkRequiredPrivilegesForAdjust(
$showGrantsStr,
$showGrantsDbname,
$showGrantsTblname,
);
$this->checkUserPrivileges->checkRequiredPrivilegesForAdjust($showGrants);
$this->assertFalse($GLOBALS['col_priv']);

View File

@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Tests;
use PhpMyAdmin\ShowGrants;
/** @covers \PhpMyAdmin\ShowGrants */
class ShowGrantsTest extends AbstractTestCase
{
public function test1(): void
{
$showGrants = new ShowGrants('GRANT ALL PRIVILEGES ON *.* TO \'root\'@\'localhost\' WITH GRANT OPTION');
$this->assertEquals('ALL PRIVILEGES', $showGrants->grants);
$this->assertEquals('*', $showGrants->dbName);
$this->assertEquals('*', $showGrants->tableName);
}
public function test2(): void
{
$showGrants = new ShowGrants('GRANT ALL PRIVILEGES ON `mysql`.* TO \'root\'@\'localhost\' WITH GRANT OPTION');
$this->assertEquals('ALL PRIVILEGES', $showGrants->grants);
$this->assertEquals('mysql', $showGrants->dbName);
$this->assertEquals('*', $showGrants->tableName);
}
public function test3(): void
{
$showGrants = new ShowGrants(
'GRANT SELECT, INSERT, UPDATE, DELETE ON `mysql`.`columns_priv` TO \'root\'@\'localhost\'',
);
$this->assertEquals('SELECT, INSERT, UPDATE, DELETE', $showGrants->grants);
$this->assertEquals('mysql', $showGrants->dbName);
$this->assertEquals('columns_priv', $showGrants->tableName);
}
public function test4(): void
{
$showGrants = new ShowGrants('GRANT ALL PRIVILEGES ON `cptest\_.`.* TO \'cptest\'@\'localhost\'');
$this->assertEquals('cptest\_.', $showGrants->dbName);
$showGrants = new ShowGrants(
'GRANT ALL PRIVILEGES ON `cptest\_.a.b.c.d.e.f.g.h.i.j.k.'
. 'l.m.n.o.p.q.r.s.t.u.v.w.x.y.z`.* TO \'cptest\'@\'localhost\'',
);
$this->assertEquals('cptest\_.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z', $showGrants->dbName);
}
}