3 * Get user's global privileges and some db-specific privileges
6 declare(strict_types
=1);
10 use PhpMyAdmin\Query\Utilities
;
11 use PhpMyAdmin\Utils\SessionCache
;
13 use function mb_strpos
;
14 use function mb_substr
;
15 use function preg_match
;
16 use function preg_replace
;
17 use function str_contains
;
20 * PhpMyAdmin\CheckUserPrivileges class
22 class CheckUserPrivileges
24 public function __construct(private DatabaseInterface
$dbi)
29 * Check if user has required privileges for
30 * performing 'Adjust privileges' operations
32 public function checkRequiredPrivilegesForAdjust(
33 ShowGrants
$showGrants,
35 // '... ALL PRIVILEGES ON *.* ...' OR '... ALL PRIVILEGES ON `mysql`.* ..'
37 // SELECT, INSERT, UPDATE, DELETE .... ON *.* OR `mysql`.*
39 $showGrants->grants
!== 'ALL'
40 && $showGrants->grants
!== 'ALL PRIVILEGES'
41 && (mb_strpos($showGrants->grants
, 'SELECT, INSERT, UPDATE, DELETE') === false)
46 if ($showGrants->dbName
=== '*' && $showGrants->tableName
=== '*') {
47 $GLOBALS['col_priv'] = true;
48 $GLOBALS['db_priv'] = true;
49 $GLOBALS['proc_priv'] = true;
50 $GLOBALS['table_priv'] = true;
52 if ($showGrants->grants
=== 'ALL PRIVILEGES' ||
$showGrants->grants
=== 'ALL') {
53 $GLOBALS['is_reload_priv'] = true;
57 // check for specific tables in `mysql` db
58 // Ex. '... ALL PRIVILEGES on `mysql`.`columns_priv` .. '
59 if ($showGrants->dbName
!== 'mysql') {
63 switch ($showGrants->tableName
) {
65 $GLOBALS['col_priv'] = true;
68 $GLOBALS['db_priv'] = true;
71 $GLOBALS['proc_priv'] = true;
74 $GLOBALS['table_priv'] = true;
77 $GLOBALS['col_priv'] = true;
78 $GLOBALS['db_priv'] = true;
79 $GLOBALS['proc_priv'] = true;
80 $GLOBALS['table_priv'] = true;
87 * sets privilege information extracted from SHOW GRANTS result
89 * Detection for some CREATE privilege.
91 * Since MySQL 4.1.2, we can easily detect current user's grants using $userlink
92 * (no control user needed) and we don't have to try any other method for
95 * @todo fix to get really all privileges, not only explicitly defined for this user
96 * from MySQL manual: (https://dev.mysql.com/doc/refman/5.0/en/show-grants.html)
97 * SHOW GRANTS displays only the privileges granted explicitly to the named
98 * account. Other privileges might be available to the account, but they are not
99 * displayed. For example, if an anonymous account exists, the named account
100 * might be able to use its privileges, but SHOW GRANTS will not display them.
102 private function analyseShowGrant(): void
104 if (SessionCache
::has('is_create_db_priv')) {
105 $GLOBALS['is_create_db_priv'] = SessionCache
::get('is_create_db_priv');
106 $GLOBALS['is_reload_priv'] = SessionCache
::get('is_reload_priv');
107 $GLOBALS['db_to_create'] = SessionCache
::get('db_to_create');
108 $GLOBALS['dbs_to_test'] = SessionCache
::get('dbs_to_test');
110 $GLOBALS['db_priv'] = SessionCache
::get('db_priv');
111 $GLOBALS['col_priv'] = SessionCache
::get('col_priv');
112 $GLOBALS['table_priv'] = SessionCache
::get('table_priv');
113 $GLOBALS['proc_priv'] = SessionCache
::get('proc_priv');
119 $GLOBALS['is_create_db_priv'] = false;
120 $GLOBALS['is_reload_priv'] = false;
121 $GLOBALS['db_to_create'] = '';
122 $GLOBALS['dbs_to_test'] = Utilities
::getSystemSchemas();
123 $GLOBALS['proc_priv'] = false;
124 $GLOBALS['db_priv'] = false;
125 $GLOBALS['col_priv'] = false;
126 $GLOBALS['table_priv'] = false;
128 $showGrantsResult = $this->dbi
->tryQuery('SHOW GRANTS');
130 if (! $showGrantsResult) {
134 $re0 = '(^|(\\\\\\\\)+|[^\\\\])'; // non-escaped wildcards
135 $re1 = '(^|[^\\\\])(\\\)+'; // escaped wildcards
137 while ($showGrants = $showGrantsResult->fetchValue()) {
138 $showGrants = new ShowGrants($showGrants);
140 if ($showGrants->dbName
=== '*') {
141 if ($showGrants->grants
!== 'USAGE') {
142 $GLOBALS['dbs_to_test'] = false;
144 } elseif ($GLOBALS['dbs_to_test'] !== false) {
145 $GLOBALS['dbs_to_test'][] = $showGrants->dbName
;
148 if (str_contains($showGrants->grants
, 'RELOAD')) {
149 $GLOBALS['is_reload_priv'] = true;
152 // check for the required privileges for adjust
153 $this->checkRequiredPrivilegesForAdjust($showGrants);
156 * @todo if we find CREATE VIEW but not CREATE, do not offer
157 * the create database dialog box
160 $showGrants->grants
!== 'ALL'
161 && $showGrants->grants
!== 'ALL PRIVILEGES'
162 && $showGrants->grants
!== 'CREATE'
163 && ! str_contains($showGrants->grants
, 'CREATE,')
168 if ($showGrants->dbName
=== '*') {
169 // a global CREATE privilege
170 $GLOBALS['is_create_db_priv'] = true;
171 $GLOBALS['is_reload_priv'] = true;
172 $GLOBALS['db_to_create'] = '';
173 // @todo we should not break here, cause GRANT ALL *.*
174 // could be revoked by a later rule like GRANT SELECT ON db.*
178 $dbNameToTest = Util
::backquote($showGrants->dbName
);
180 if ($GLOBALS['is_create_db_priv']) {
181 // no need for any more tests if we already know this
185 // does this db exist?
187 (! preg_match('/' . $re0 . '%|_/', $showGrants->dbName
)
188 ||
preg_match('/\\\\%|\\\\_/', $showGrants->dbName
))
189 && ($this->dbi
->tryQuery(
190 'USE ' . preg_replace(
191 '/' . $re1 . '(%|_)/',
196 ||
mb_substr($this->dbi
->getError(), 1, 4) == 1044)
202 * Do not handle the underscore wildcard
203 * (this case must be rare anyway)
205 $GLOBALS['db_to_create'] = preg_replace('/' . $re0 . '%/', '\\1', $showGrants->dbName
);
206 $GLOBALS['db_to_create'] = preg_replace('/' . $re1 . '(%|_)/', '\\1\\3', $GLOBALS['db_to_create']);
207 $GLOBALS['is_create_db_priv'] = true;
210 * @todo collect $GLOBALS['db_to_create'] into an array,
211 * to display a drop-down in the "Create database" dialog
213 // we don't break, we want all possible databases
217 // must also cacheUnset() them in
218 // PhpMyAdmin\Plugins\Auth\AuthenticationCookie
219 SessionCache
::set('is_create_db_priv', $GLOBALS['is_create_db_priv']);
220 SessionCache
::set('is_reload_priv', $GLOBALS['is_reload_priv']);
221 SessionCache
::set('db_to_create', $GLOBALS['db_to_create']);
222 SessionCache
::set('dbs_to_test', $GLOBALS['dbs_to_test']);
224 SessionCache
::set('proc_priv', $GLOBALS['proc_priv']);
225 SessionCache
::set('table_priv', $GLOBALS['table_priv']);
226 SessionCache
::set('col_priv', $GLOBALS['col_priv']);
227 SessionCache
::set('db_priv', $GLOBALS['db_priv']);
231 * Get user's global privileges and some db-specific privileges
233 public function getPrivileges(): void
237 $current = $this->dbi
->getCurrentUserAndHost();
238 if ($current !== []) {
239 [$username] = $current;
242 // If MySQL is started with --skip-grant-tables
243 if ($username === '') {
244 $GLOBALS['is_create_db_priv'] = true;
245 $GLOBALS['is_reload_priv'] = true;
246 $GLOBALS['db_to_create'] = '';
247 $GLOBALS['dbs_to_test'] = false;
248 $GLOBALS['db_priv'] = true;
249 $GLOBALS['col_priv'] = true;
250 $GLOBALS['table_priv'] = true;
251 $GLOBALS['proc_priv'] = true;
256 $this->analyseShowGrant();