diff --git a/libraries/classes/CheckUserPrivileges.php b/libraries/classes/CheckUserPrivileges.php index 1da40d5782..61a093abfa 100644 --- a/libraries/classes/CheckUserPrivileges.php +++ b/libraries/classes/CheckUserPrivileges.php @@ -25,91 +25,42 @@ class CheckUserPrivileges { } - /** - * Extracts details from a result row of a SHOW GRANT query - * - * @param string $row grant row - * - * @return array - */ - 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; diff --git a/libraries/classes/ShowGrants.php b/libraries/classes/ShowGrants.php new file mode 100644 index 0000000000..393adef49c --- /dev/null +++ b/libraries/classes/ShowGrants.php @@ -0,0 +1,70 @@ +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, '`'); + } +} diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 1a9b227f09..4801b729d4 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -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 diff --git a/psalm-baseline.xml b/psalm-baseline.xml index f3bc731dc7..e15132b5b0 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -137,17 +137,6 @@ - - $tableNameEndOffset - $tableNameStartOffset - $tableNameStartOffset - $tableNameStartOffset - - - - - $row[0] - @@ -14089,12 +14078,6 @@ assertTrue assertTrue - - $showGrantsStr - $showGrantsStr - $showGrantsTblname - $showGrantsTblname - diff --git a/test/classes/CheckUserPrivilegesTest.php b/test/classes/CheckUserPrivilegesTest.php index 43d6c330c5..0dc2a11abd 100644 --- a/test/classes/CheckUserPrivilegesTest.php +++ b/test/classes/CheckUserPrivilegesTest.php @@ -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']); diff --git a/test/classes/ShowGrantsTest.php b/test/classes/ShowGrantsTest.php new file mode 100644 index 0000000000..b83eae3ca4 --- /dev/null +++ b/test/classes/ShowGrantsTest.php @@ -0,0 +1,49 @@ +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); + } +}