|
51 | 51 | use function reset; |
52 | 52 | use function sprintf; |
53 | 53 | use function str_contains; |
| 54 | +use function str_replace; |
54 | 55 | use function str_starts_with; |
55 | 56 | use function stripos; |
56 | 57 | use function strnatcasecmp; |
@@ -110,6 +111,9 @@ class DatabaseInterface implements DbalInterface |
110 | 111 | /** @var array<int, string>|null */ |
111 | 112 | private array|null $currentUserAndHost = null; |
112 | 113 |
|
| 114 | + /** @var array<int, array<int, string>>|null Current role and host cache */ |
| 115 | + private array|null $currentRoleAndHost = null; |
| 116 | + |
113 | 117 | /** |
114 | 118 | * @var int|null lower_case_table_names value cache |
115 | 119 | * @psalm-var 0|1|2|null |
@@ -1430,6 +1434,38 @@ public function getCurrentUser(): string |
1430 | 1434 | return '@'; |
1431 | 1435 | } |
1432 | 1436 |
|
| 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 | + |
1433 | 1469 | public function isSuperUser(): bool |
1434 | 1470 | { |
1435 | 1471 | if (SessionCache::has('is_superuser')) { |
@@ -1478,6 +1514,21 @@ public function isGrantUser(): bool |
1478 | 1514 | $query = QueryGenerator::getInformationSchemaDataForGranteeRequest($user, $host); |
1479 | 1515 | $hasGrantPrivilege = (bool) $this->fetchValue($query); |
1480 | 1516 |
|
| 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 | + |
1481 | 1532 | SessionCache::set('is_grantuser', $hasGrantPrivilege); |
1482 | 1533 |
|
1483 | 1534 | return $hasGrantPrivilege; |
@@ -1514,6 +1565,21 @@ public function isCreateUser(): bool |
1514 | 1565 | $query = QueryGenerator::getInformationSchemaDataForCreateRequest($user, $host); |
1515 | 1566 | $hasCreatePrivilege = (bool) $this->fetchValue($query); |
1516 | 1567 |
|
| 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 | + |
1517 | 1583 | SessionCache::set('is_createuser', $hasCreatePrivilege); |
1518 | 1584 |
|
1519 | 1585 | return $hasCreatePrivilege; |
@@ -1548,6 +1614,24 @@ public function getCurrentUserAndHost(): array |
1548 | 1614 | return $this->currentUserAndHost; |
1549 | 1615 | } |
1550 | 1616 |
|
| 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 | + |
1551 | 1635 | /** |
1552 | 1636 | * Returns value for lower_case_table_names variable |
1553 | 1637 | * |
|
0 commit comments