Translated using Weblate (Russian)
[phpmyadmin.git] / src / CheckUserPrivileges.php
blob61a093abfa118e0532e53a1f0a224101f04f32cf
1 <?php
2 /**
3 * Get user's global privileges and some db-specific privileges
4 */
6 declare(strict_types=1);
8 namespace PhpMyAdmin;
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;
19 /**
20 * PhpMyAdmin\CheckUserPrivileges class
22 class CheckUserPrivileges
24 public function __construct(private DatabaseInterface $dbi)
28 /**
29 * Check if user has required privileges for
30 * performing 'Adjust privileges' operations
32 public function checkRequiredPrivilegesForAdjust(
33 ShowGrants $showGrants,
34 ): void {
35 // '... ALL PRIVILEGES ON *.* ...' OR '... ALL PRIVILEGES ON `mysql`.* ..'
36 // OR
37 // SELECT, INSERT, UPDATE, DELETE .... ON *.* OR `mysql`.*
38 if (
39 $showGrants->grants !== 'ALL'
40 && $showGrants->grants !== 'ALL PRIVILEGES'
41 && (mb_strpos($showGrants->grants, 'SELECT, INSERT, UPDATE, DELETE') === false)
42 ) {
43 return;
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') {
60 return;
63 switch ($showGrants->tableName) {
64 case 'columns_priv':
65 $GLOBALS['col_priv'] = true;
66 break;
67 case 'db':
68 $GLOBALS['db_priv'] = true;
69 break;
70 case 'procs_priv':
71 $GLOBALS['proc_priv'] = true;
72 break;
73 case 'tables_priv':
74 $GLOBALS['table_priv'] = true;
75 break;
76 case '*':
77 $GLOBALS['col_priv'] = true;
78 $GLOBALS['db_priv'] = true;
79 $GLOBALS['proc_priv'] = true;
80 $GLOBALS['table_priv'] = true;
81 break;
82 default:
86 /**
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
93 * detection
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');
115 return;
118 // defaults
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) {
131 return;
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
159 if (
160 $showGrants->grants !== 'ALL'
161 && $showGrants->grants !== 'ALL PRIVILEGES'
162 && $showGrants->grants !== 'CREATE'
163 && ! str_contains($showGrants->grants, 'CREATE,')
165 continue;
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.*
175 break;
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
182 continue;
185 // does this db exist?
186 if (
187 (! preg_match('/' . $re0 . '%|_/', $showGrants->dbName)
188 || preg_match('/\\\\%|\\\\_/', $showGrants->dbName))
189 && ($this->dbi->tryQuery(
190 'USE ' . preg_replace(
191 '/' . $re1 . '(%|_)/',
192 '\\1\\3',
193 $dbNameToTest,
196 || mb_substr($this->dbi->getError(), 1, 4) == 1044)
198 continue;
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
214 //break;
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
235 $username = '';
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;
253 return;
256 $this->analyseShowGrant();