Total Complexity | 552 |
Total Lines | 4110 |
Duplicated Lines | 0 % |
Changes | 2 | ||
Bugs | 0 | Features | 0 |
Complex classes like Privileges often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Privileges, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
27 | class Privileges |
||
28 | { |
||
29 | /** |
||
30 | * @var Template |
||
31 | */ |
||
32 | public $template; |
||
33 | |||
34 | /** |
||
35 | * @var RelationCleanup |
||
36 | */ |
||
37 | private $relationCleanup; |
||
38 | |||
39 | /** |
||
40 | * @var DatabaseInterface |
||
41 | */ |
||
42 | public $dbi; |
||
43 | |||
44 | /** |
||
45 | * @var Relation |
||
46 | */ |
||
47 | public $relation; |
||
48 | |||
49 | /** |
||
50 | * Privileges constructor. |
||
51 | * |
||
52 | * @param Template $template Template object |
||
53 | * @param DatabaseInterface $dbi DatabaseInterface object |
||
54 | * @param Relation $relation Relation object |
||
55 | * @param RelationCleanup $relationCleanup RelationCleanup object |
||
56 | */ |
||
57 | public function __construct( |
||
58 | Template $template, |
||
59 | $dbi, |
||
60 | Relation $relation, |
||
61 | RelationCleanup $relationCleanup |
||
62 | ) { |
||
63 | $this->template = $template; |
||
64 | $this->dbi = $dbi; |
||
65 | $this->relation = $relation; |
||
66 | $this->relationCleanup = $relationCleanup; |
||
67 | } |
||
68 | |||
69 | /** |
||
70 | * Get Html for User Group Dialog |
||
71 | * |
||
72 | * @param string $username username |
||
73 | * @param bool $is_menuswork Is menuswork set in configuration |
||
74 | * |
||
75 | * @return string html |
||
76 | */ |
||
77 | public function getHtmlForUserGroupDialog($username, $is_menuswork) |
||
78 | { |
||
79 | $html = ''; |
||
80 | if (! empty($_GET['edit_user_group_dialog']) && $is_menuswork) { |
||
81 | $dialog = $this->getHtmlToChooseUserGroup($username); |
||
82 | $response = Response::getInstance(); |
||
83 | if ($response->isAjax()) { |
||
84 | $response->addJSON('message', $dialog); |
||
85 | exit; |
||
|
|||
86 | } else { |
||
87 | $html .= $dialog; |
||
88 | } |
||
89 | } |
||
90 | |||
91 | return $html; |
||
92 | } |
||
93 | |||
94 | /** |
||
95 | * Escapes wildcard in a database+table specification |
||
96 | * before using it in a GRANT statement. |
||
97 | * |
||
98 | * Escaping a wildcard character in a GRANT is only accepted at the global |
||
99 | * or database level, not at table level; this is why I remove |
||
100 | * the escaping character. Internally, in mysql.tables_priv.Db there are |
||
101 | * no escaping (for example test_db) but in mysql.db you'll see test\_db |
||
102 | * for a db-specific privilege. |
||
103 | * |
||
104 | * @param string $dbname Database name |
||
105 | * @param string $tablename Table name |
||
106 | * |
||
107 | * @return string the escaped (if necessary) database.table |
||
108 | */ |
||
109 | public function wildcardEscapeForGrant($dbname, $tablename) |
||
110 | { |
||
111 | if (strlen($dbname) === 0) { |
||
112 | $db_and_table = '*.*'; |
||
113 | } else { |
||
114 | if (strlen($tablename) > 0) { |
||
115 | $db_and_table = Util::backquote( |
||
116 | Util::unescapeMysqlWildcards($dbname) |
||
117 | ) |
||
118 | . '.' . Util::backquote($tablename); |
||
119 | } else { |
||
120 | $db_and_table = Util::backquote($dbname) . '.*'; |
||
121 | } |
||
122 | } |
||
123 | return $db_and_table; |
||
124 | } |
||
125 | |||
126 | /** |
||
127 | * Generates a condition on the user name |
||
128 | * |
||
129 | * @param string $initial the user's initial |
||
130 | * |
||
131 | * @return string the generated condition |
||
132 | */ |
||
133 | public function rangeOfUsers($initial = '') |
||
134 | { |
||
135 | // strtolower() is used because the User field |
||
136 | // might be BINARY, so LIKE would be case sensitive |
||
137 | if ($initial === null || $initial === '') { |
||
138 | return ''; |
||
139 | } |
||
140 | |||
141 | $ret = " WHERE `User` LIKE '" |
||
142 | . $this->dbi->escapeString($initial) . "%'" |
||
143 | . " OR `User` LIKE '" |
||
144 | . $this->dbi->escapeString(mb_strtolower($initial)) |
||
145 | . "%'"; |
||
146 | return $ret; |
||
147 | } |
||
148 | |||
149 | /** |
||
150 | * Parses privileges into an array, it modifies the array |
||
151 | * |
||
152 | * @param array $row Results row from |
||
153 | * |
||
154 | * @return void |
||
155 | */ |
||
156 | public function fillInTablePrivileges(array &$row) |
||
184 | } |
||
185 | |||
186 | |||
187 | /** |
||
188 | * Extracts the privilege information of a priv table row |
||
189 | * |
||
190 | * @param array|null $row the row |
||
191 | * @param boolean $enableHTML add <dfn> tag with tooltips |
||
192 | * @param boolean $tablePrivs whether row contains table privileges |
||
193 | * |
||
194 | * @global resource $user_link the database connection |
||
195 | * |
||
196 | * @return array |
||
197 | */ |
||
198 | public function extractPrivInfo($row = null, $enableHTML = false, $tablePrivs = false) |
||
199 | { |
||
200 | if ($tablePrivs) { |
||
201 | $grants = $this->getTableGrantsArray(); |
||
202 | } else { |
||
203 | $grants = $this->getGrantsArray(); |
||
204 | } |
||
205 | |||
206 | if ($row !== null && isset($row['Table_priv'])) { |
||
207 | $this->fillInTablePrivileges($row); |
||
208 | } |
||
209 | |||
210 | $privs = []; |
||
211 | $allPrivileges = true; |
||
212 | foreach ($grants as $current_grant) { |
||
213 | if (($row !== null && isset($row[$current_grant[0]])) |
||
214 | || ($row === null && isset($GLOBALS[$current_grant[0]])) |
||
215 | ) { |
||
216 | if (($row !== null && $row[$current_grant[0]] == 'Y') |
||
217 | || ($row === null |
||
218 | && ($GLOBALS[$current_grant[0]] == 'Y' |
||
219 | || (is_array($GLOBALS[$current_grant[0]]) |
||
220 | && count($GLOBALS[$current_grant[0]]) == $_REQUEST['column_count'] |
||
221 | && empty($GLOBALS[$current_grant[0] . '_none'])))) |
||
222 | ) { |
||
223 | if ($enableHTML) { |
||
224 | $privs[] = '<dfn title="' . $current_grant[2] . '">' |
||
225 | . $current_grant[1] . '</dfn>'; |
||
226 | } else { |
||
227 | $privs[] = $current_grant[1]; |
||
228 | } |
||
229 | } elseif (! empty($GLOBALS[$current_grant[0]]) |
||
230 | && is_array($GLOBALS[$current_grant[0]]) |
||
231 | && empty($GLOBALS[$current_grant[0] . '_none']) |
||
232 | ) { |
||
233 | // Required for proper escaping of ` (backtick) in a column name |
||
234 | $grant_cols = array_map( |
||
235 | function ($val) { |
||
236 | return Util::backquote($val); |
||
237 | }, |
||
238 | $GLOBALS[$current_grant[0]] |
||
239 | ); |
||
240 | |||
241 | if ($enableHTML) { |
||
242 | $privs[] = '<dfn title="' . $current_grant[2] . '">' |
||
243 | . $current_grant[1] . '</dfn>' |
||
244 | . ' (' . implode(', ', $grant_cols) . ')'; |
||
245 | } else { |
||
246 | $privs[] = $current_grant[1] |
||
247 | . ' (' . implode(', ', $grant_cols) . ')'; |
||
248 | } |
||
249 | } else { |
||
250 | $allPrivileges = false; |
||
251 | } |
||
252 | } |
||
253 | } |
||
254 | if (empty($privs)) { |
||
255 | if ($enableHTML) { |
||
256 | $privs[] = '<dfn title="' . __('No privileges.') . '">USAGE</dfn>'; |
||
257 | } else { |
||
258 | $privs[] = 'USAGE'; |
||
259 | } |
||
260 | } elseif ($allPrivileges |
||
261 | && (! isset($_POST['grant_count']) || count($privs) == $_POST['grant_count']) |
||
262 | ) { |
||
263 | if ($enableHTML) { |
||
264 | $privs = ['<dfn title="' |
||
265 | . __('Includes all privileges except GRANT.') |
||
266 | . '">ALL PRIVILEGES</dfn>', |
||
267 | ]; |
||
268 | } else { |
||
269 | $privs = ['ALL PRIVILEGES']; |
||
270 | } |
||
271 | } |
||
272 | return $privs; |
||
273 | } |
||
274 | |||
275 | /** |
||
276 | * Returns an array of table grants and their descriptions |
||
277 | * |
||
278 | * @return array array of table grants |
||
279 | */ |
||
280 | public function getTableGrantsArray() |
||
281 | { |
||
282 | return [ |
||
283 | [ |
||
284 | 'Delete', |
||
285 | 'DELETE', |
||
286 | $GLOBALS['strPrivDescDelete'], |
||
287 | ], |
||
288 | [ |
||
289 | 'Create', |
||
290 | 'CREATE', |
||
291 | $GLOBALS['strPrivDescCreateTbl'], |
||
292 | ], |
||
293 | [ |
||
294 | 'Drop', |
||
295 | 'DROP', |
||
296 | $GLOBALS['strPrivDescDropTbl'], |
||
297 | ], |
||
298 | [ |
||
299 | 'Index', |
||
300 | 'INDEX', |
||
301 | $GLOBALS['strPrivDescIndex'], |
||
302 | ], |
||
303 | [ |
||
304 | 'Alter', |
||
305 | 'ALTER', |
||
306 | $GLOBALS['strPrivDescAlter'], |
||
307 | ], |
||
308 | [ |
||
309 | 'Create View', |
||
310 | 'CREATE_VIEW', |
||
311 | $GLOBALS['strPrivDescCreateView'], |
||
312 | ], |
||
313 | [ |
||
314 | 'Show view', |
||
315 | 'SHOW_VIEW', |
||
316 | $GLOBALS['strPrivDescShowView'], |
||
317 | ], |
||
318 | [ |
||
319 | 'Trigger', |
||
320 | 'TRIGGER', |
||
321 | $GLOBALS['strPrivDescTrigger'], |
||
322 | ], |
||
323 | ]; |
||
324 | } |
||
325 | |||
326 | /** |
||
327 | * Get the grants array which contains all the privilege types |
||
328 | * and relevant grant messages |
||
329 | * |
||
330 | * @return array |
||
331 | */ |
||
332 | public function getGrantsArray() |
||
495 | ], |
||
496 | ]; |
||
497 | } |
||
498 | |||
499 | /** |
||
500 | * Get sql query for display privileges table |
||
501 | * |
||
502 | * @param string $db the database |
||
503 | * @param string $table the table |
||
504 | * @param string $username username for database connection |
||
505 | * @param string $hostname hostname for database connection |
||
506 | * |
||
507 | * @return string sql query |
||
508 | */ |
||
509 | public function getSqlQueryForDisplayPrivTable($db, $table, $username, $hostname) |
||
510 | { |
||
511 | if ($db == '*') { |
||
512 | return "SELECT * FROM `mysql`.`user`" |
||
513 | . " WHERE `User` = '" . $this->dbi->escapeString($username) . "'" |
||
514 | . " AND `Host` = '" . $this->dbi->escapeString($hostname) . "';"; |
||
515 | } elseif ($table == '*') { |
||
516 | return "SELECT * FROM `mysql`.`db`" |
||
517 | . " WHERE `User` = '" . $this->dbi->escapeString($username) . "'" |
||
518 | . " AND `Host` = '" . $this->dbi->escapeString($hostname) . "'" |
||
519 | . " AND '" . $this->dbi->escapeString(Util::unescapeMysqlWildcards($db)) . "'" |
||
520 | . " LIKE `Db`;"; |
||
521 | } |
||
522 | return "SELECT `Table_priv`" |
||
523 | . " FROM `mysql`.`tables_priv`" |
||
524 | . " WHERE `User` = '" . $this->dbi->escapeString($username) . "'" |
||
525 | . " AND `Host` = '" . $this->dbi->escapeString($hostname) . "'" |
||
526 | . " AND `Db` = '" . $this->dbi->escapeString(Util::unescapeMysqlWildcards($db)) . "'" |
||
527 | . " AND `Table_name` = '" . $this->dbi->escapeString($table) . "';"; |
||
528 | } |
||
529 | |||
530 | /** |
||
531 | * Displays a dropdown to select the user group |
||
532 | * with menu items configured to each of them. |
||
533 | * |
||
534 | * @param string $username username |
||
535 | * |
||
536 | * @return string html to select the user group |
||
537 | */ |
||
538 | public function getHtmlToChooseUserGroup($username) |
||
539 | { |
||
540 | $cfgRelation = $this->relation->getRelationsParam(); |
||
541 | $groupTable = Util::backquote($cfgRelation['db']) |
||
542 | . "." . Util::backquote($cfgRelation['usergroups']); |
||
543 | $userTable = Util::backquote($cfgRelation['db']) |
||
544 | . "." . Util::backquote($cfgRelation['users']); |
||
545 | |||
546 | $userGroup = ''; |
||
547 | if (isset($GLOBALS['username'])) { |
||
548 | $sql_query = "SELECT `usergroup` FROM " . $userTable |
||
549 | . " WHERE `username` = '" . $this->dbi->escapeString($username) . "'"; |
||
550 | $userGroup = $this->dbi->fetchValue( |
||
551 | $sql_query, |
||
552 | 0, |
||
553 | 0, |
||
554 | DatabaseInterface::CONNECT_CONTROL |
||
555 | ); |
||
556 | } |
||
557 | |||
558 | $allUserGroups = ['' => '']; |
||
559 | $sql_query = "SELECT DISTINCT `usergroup` FROM " . $groupTable; |
||
560 | $result = $this->relation->queryAsControlUser($sql_query, false); |
||
561 | if ($result) { |
||
562 | while ($row = $this->dbi->fetchRow($result)) { |
||
563 | $allUserGroups[$row[0]] = $row[0]; |
||
564 | } |
||
565 | } |
||
566 | $this->dbi->freeResult($result); |
||
567 | |||
568 | return $this->template->render('server/privileges/choose_user_group', [ |
||
569 | 'all_user_groups' => $allUserGroups, |
||
570 | 'user_group' => $userGroup, |
||
571 | 'params' => ['username' => $username], |
||
572 | ]); |
||
573 | } |
||
574 | |||
575 | /** |
||
576 | * Sets the user group from request values |
||
577 | * |
||
578 | * @param string $username username |
||
579 | * @param string $userGroup user group to set |
||
580 | * |
||
581 | * @return void |
||
582 | */ |
||
583 | public function setUserGroup($username, $userGroup) |
||
584 | { |
||
585 | $userGroup = $userGroup === null ? '' : $userGroup; |
||
586 | $cfgRelation = $this->relation->getRelationsParam(); |
||
587 | if (empty($cfgRelation['db']) || empty($cfgRelation['users']) || empty($cfgRelation['usergroups'])) { |
||
588 | return; |
||
589 | } |
||
590 | |||
591 | $userTable = Util::backquote($cfgRelation['db']) |
||
592 | . "." . Util::backquote($cfgRelation['users']); |
||
593 | |||
594 | $sql_query = "SELECT `usergroup` FROM " . $userTable |
||
595 | . " WHERE `username` = '" . $this->dbi->escapeString($username) . "'"; |
||
596 | $oldUserGroup = $this->dbi->fetchValue( |
||
597 | $sql_query, |
||
598 | 0, |
||
599 | 0, |
||
600 | DatabaseInterface::CONNECT_CONTROL |
||
601 | ); |
||
602 | |||
603 | if ($oldUserGroup === false) { |
||
604 | $upd_query = "INSERT INTO " . $userTable . "(`username`, `usergroup`)" |
||
605 | . " VALUES ('" . $this->dbi->escapeString($username) . "', " |
||
606 | . "'" . $this->dbi->escapeString($userGroup) . "')"; |
||
607 | } else { |
||
608 | if (empty($userGroup)) { |
||
609 | $upd_query = "DELETE FROM " . $userTable |
||
610 | . " WHERE `username`='" . $this->dbi->escapeString($username) . "'"; |
||
611 | } elseif ($oldUserGroup != $userGroup) { |
||
612 | $upd_query = "UPDATE " . $userTable |
||
613 | . " SET `usergroup`='" . $this->dbi->escapeString($userGroup) . "'" |
||
614 | . " WHERE `username`='" . $this->dbi->escapeString($username) . "'"; |
||
615 | } |
||
616 | } |
||
617 | if (isset($upd_query)) { |
||
618 | $this->relation->queryAsControlUser($upd_query); |
||
619 | } |
||
620 | } |
||
621 | |||
622 | /** |
||
623 | * Displays the privileges form table |
||
624 | * |
||
625 | * @param string $db the database |
||
626 | * @param string $table the table |
||
627 | * @param boolean $submit whether to display the submit button or not |
||
628 | * |
||
629 | * @global array $cfg the phpMyAdmin configuration |
||
630 | * @global resource $user_link the database connection |
||
631 | * |
||
632 | * @return string html snippet |
||
633 | */ |
||
634 | public function getHtmlToDisplayPrivilegesTable( |
||
635 | $db = '*', |
||
636 | $table = '*', |
||
637 | $submit = true |
||
638 | ) { |
||
639 | $sql_query = ''; |
||
640 | |||
641 | if ($db == '*') { |
||
642 | $table = '*'; |
||
643 | } |
||
644 | $username = ''; |
||
645 | $hostname = ''; |
||
646 | if (isset($GLOBALS['username'])) { |
||
647 | $username = $GLOBALS['username']; |
||
648 | $hostname = $GLOBALS['hostname']; |
||
649 | $sql_query = $this->getSqlQueryForDisplayPrivTable( |
||
650 | $db, |
||
651 | $table, |
||
652 | $username, |
||
653 | $hostname |
||
654 | ); |
||
655 | $row = $this->dbi->fetchSingleRow($sql_query); |
||
656 | } |
||
657 | if (empty($row)) { |
||
658 | if ($table == '*' && $this->dbi->isSuperuser()) { |
||
659 | $row = []; |
||
660 | if ($db == '*') { |
||
661 | $sql_query = 'SHOW COLUMNS FROM `mysql`.`user`;'; |
||
662 | } elseif ($table == '*') { |
||
663 | $sql_query = 'SHOW COLUMNS FROM `mysql`.`db`;'; |
||
664 | } |
||
665 | $res = $this->dbi->query($sql_query); |
||
666 | while ($row1 = $this->dbi->fetchRow($res)) { |
||
667 | if (mb_substr($row1[0], 0, 4) == 'max_') { |
||
668 | $row[$row1[0]] = 0; |
||
669 | } elseif (mb_substr($row1[0], 0, 5) == 'x509_' |
||
670 | || mb_substr($row1[0], 0, 4) == 'ssl_' |
||
671 | ) { |
||
672 | $row[$row1[0]] = ''; |
||
673 | } else { |
||
674 | $row[$row1[0]] = 'N'; |
||
675 | } |
||
676 | } |
||
677 | $this->dbi->freeResult($res); |
||
678 | } elseif ($table == '*') { |
||
679 | $row = []; |
||
680 | } else { |
||
681 | $row = ['Table_priv' => '']; |
||
682 | } |
||
683 | } |
||
684 | if (isset($row['Table_priv'])) { |
||
685 | $this->fillInTablePrivileges($row); |
||
686 | |||
687 | // get columns |
||
688 | $res = $this->dbi->tryQuery( |
||
689 | 'SHOW COLUMNS FROM ' |
||
690 | . Util::backquote( |
||
691 | Util::unescapeMysqlWildcards($db) |
||
692 | ) |
||
693 | . '.' . Util::backquote($table) . ';' |
||
694 | ); |
||
695 | $columns = []; |
||
696 | if ($res) { |
||
697 | while ($row1 = $this->dbi->fetchRow($res)) { |
||
698 | $columns[$row1[0]] = [ |
||
699 | 'Select' => false, |
||
700 | 'Insert' => false, |
||
701 | 'Update' => false, |
||
702 | 'References' => false, |
||
703 | ]; |
||
704 | } |
||
705 | $this->dbi->freeResult($res); |
||
706 | } |
||
707 | } |
||
708 | |||
709 | if (! empty($columns)) { |
||
710 | $res = $this->dbi->query( |
||
711 | 'SELECT `Column_name`, `Column_priv`' |
||
712 | . ' FROM `mysql`.`columns_priv`' |
||
713 | . ' WHERE `User`' |
||
714 | . ' = \'' . $this->dbi->escapeString($username) . "'" |
||
715 | . ' AND `Host`' |
||
716 | . ' = \'' . $this->dbi->escapeString($hostname) . "'" |
||
717 | . ' AND `Db`' |
||
718 | . ' = \'' . $this->dbi->escapeString( |
||
719 | Util::unescapeMysqlWildcards($db) |
||
720 | ) . "'" |
||
721 | . ' AND `Table_name`' |
||
722 | . ' = \'' . $this->dbi->escapeString($table) . '\';' |
||
723 | ); |
||
724 | |||
725 | while ($row1 = $this->dbi->fetchRow($res)) { |
||
726 | $row1[1] = explode(',', $row1[1]); |
||
727 | foreach ($row1[1] as $current) { |
||
728 | $columns[$row1[0]][$current] = true; |
||
729 | } |
||
730 | } |
||
731 | $this->dbi->freeResult($res); |
||
732 | } |
||
733 | |||
734 | return $this->template->render('server/privileges/privileges_table', [ |
||
735 | 'is_global' => $db === '*', |
||
736 | 'is_database' => $table === '*', |
||
737 | 'row' => $row, |
||
738 | 'columns' => $columns ?? [], |
||
739 | 'has_submit' => $submit, |
||
740 | ]); |
||
741 | } |
||
742 | |||
743 | /** |
||
744 | * Get the HTML snippet for routine specific privileges |
||
745 | * |
||
746 | * @param string $username username for database connection |
||
747 | * @param string $hostname hostname for database connection |
||
748 | * @param string $db the database |
||
749 | * @param string $routine the routine |
||
750 | * @param string $url_dbname url encoded db name |
||
751 | * |
||
752 | * @return string |
||
753 | */ |
||
754 | public function getHtmlForRoutineSpecificPrivileges( |
||
755 | $username, |
||
756 | $hostname, |
||
757 | $db, |
||
758 | $routine, |
||
759 | $url_dbname |
||
760 | ) { |
||
761 | $privileges = $this->getRoutinePrivileges($username, $hostname, $db, $routine); |
||
762 | |||
763 | return $this->template->render('server/privileges/edit_routine_privileges', [ |
||
764 | 'username' => $username, |
||
765 | 'hostname' => $hostname, |
||
766 | 'database' => $db, |
||
767 | 'routine' => $routine, |
||
768 | 'privileges' => $privileges, |
||
769 | 'dbname' => $url_dbname, |
||
770 | 'current_user' => $this->dbi->getCurrentUser(), |
||
771 | ]); |
||
772 | } |
||
773 | |||
774 | /** |
||
775 | * Gets the currently active authentication plugins |
||
776 | * |
||
777 | * @param string $orig_auth_plugin Default Authentication plugin |
||
778 | * @param string $mode are we creating a new user or are we just |
||
779 | * changing one? |
||
780 | * (allowed values: 'new', 'edit', 'change_pw') |
||
781 | * @param string $versions Is MySQL version newer or older than 5.5.7 |
||
782 | * |
||
783 | * @return string |
||
784 | */ |
||
785 | public function getHtmlForAuthPluginsDropdown( |
||
786 | $orig_auth_plugin, |
||
787 | $mode = 'new', |
||
788 | $versions = 'new' |
||
789 | ) { |
||
790 | $select_id = 'select_authentication_plugin' |
||
791 | . ($mode == 'change_pw' ? '_cp' : ''); |
||
792 | |||
793 | if ($versions == 'new') { |
||
794 | $active_auth_plugins = $this->getActiveAuthPlugins(); |
||
795 | |||
796 | if (isset($active_auth_plugins['mysql_old_password'])) { |
||
797 | unset($active_auth_plugins['mysql_old_password']); |
||
798 | } |
||
799 | } else { |
||
800 | $active_auth_plugins = [ |
||
801 | 'mysql_native_password' => __('Native MySQL authentication'), |
||
802 | ]; |
||
803 | } |
||
804 | |||
805 | $html_output = Util::getDropdown( |
||
806 | 'authentication_plugin', |
||
807 | $active_auth_plugins, |
||
808 | $orig_auth_plugin, |
||
809 | $select_id |
||
810 | ); |
||
811 | |||
812 | return $html_output; |
||
813 | } |
||
814 | |||
815 | /** |
||
816 | * Gets the currently active authentication plugins |
||
817 | * |
||
818 | * @return array array of plugin names and descriptions |
||
819 | */ |
||
820 | public function getActiveAuthPlugins() |
||
821 | { |
||
822 | $get_plugins_query = "SELECT `PLUGIN_NAME`, `PLUGIN_DESCRIPTION`" |
||
823 | . " FROM `information_schema`.`PLUGINS` " |
||
824 | . "WHERE `PLUGIN_TYPE` = 'AUTHENTICATION';"; |
||
825 | $resultset = $this->dbi->query($get_plugins_query); |
||
826 | |||
827 | $result = []; |
||
828 | |||
829 | while ($row = $this->dbi->fetchAssoc($resultset)) { |
||
830 | // if description is known, enable its translation |
||
831 | if ('mysql_native_password' == $row['PLUGIN_NAME']) { |
||
832 | $row['PLUGIN_DESCRIPTION'] = __('Native MySQL authentication'); |
||
833 | } elseif ('sha256_password' == $row['PLUGIN_NAME']) { |
||
834 | $row['PLUGIN_DESCRIPTION'] = __('SHA256 password authentication'); |
||
835 | } |
||
836 | |||
837 | $result[$row['PLUGIN_NAME']] = $row['PLUGIN_DESCRIPTION']; |
||
838 | } |
||
839 | |||
840 | return $result; |
||
841 | } |
||
842 | |||
843 | /** |
||
844 | * Displays the fields used by the "new user" form as well as the |
||
845 | * "change login information / copy user" form. |
||
846 | * |
||
847 | * @param string $mode are we creating a new user or are we just |
||
848 | * changing one? (allowed values: 'new', 'change') |
||
849 | * @param string $user User name |
||
850 | * @param string $host Host name |
||
851 | * |
||
852 | * @return string a HTML snippet |
||
853 | */ |
||
854 | public function getHtmlForLoginInformationFields( |
||
855 | $mode = 'new', |
||
856 | $user = null, |
||
857 | $host = null |
||
858 | ) { |
||
859 | global $pred_username, $pred_hostname, $username, $hostname, $new_username; |
||
860 | |||
861 | list($usernameLength, $hostnameLength) = $this->getUsernameAndHostnameLength(); |
||
862 | |||
863 | if (isset($username) && strlen($username) === 0) { |
||
864 | $pred_username = 'any'; |
||
865 | } |
||
866 | |||
867 | $currentUser = $this->dbi->fetchValue('SELECT USER();'); |
||
868 | $thisHost = null; |
||
869 | if (! empty($currentUser)) { |
||
870 | $thisHost = str_replace( |
||
871 | '\'', |
||
872 | '', |
||
873 | mb_substr( |
||
874 | $currentUser, |
||
875 | mb_strrpos($currentUser, '@') + 1 |
||
876 | ) |
||
877 | ); |
||
878 | } |
||
879 | |||
880 | if (! isset($pred_hostname) && isset($hostname)) { |
||
881 | switch (mb_strtolower($hostname)) { |
||
882 | case 'localhost': |
||
883 | case '127.0.0.1': |
||
884 | $pred_hostname = 'localhost'; |
||
885 | break; |
||
886 | case '%': |
||
887 | $pred_hostname = 'any'; |
||
888 | break; |
||
889 | default: |
||
890 | $pred_hostname = 'userdefined'; |
||
891 | break; |
||
892 | } |
||
893 | } |
||
894 | |||
895 | $serverType = Util::getServerType(); |
||
896 | $serverVersion = $this->dbi->getVersion(); |
||
897 | $authPlugin = $this->getCurrentAuthenticationPlugin( |
||
898 | $mode, |
||
899 | $user, |
||
900 | $host |
||
901 | ); |
||
902 | |||
903 | if (($serverType == 'MySQL' |
||
904 | && $serverVersion >= 50507) |
||
905 | || ($serverType == 'MariaDB' |
||
906 | && $serverVersion >= 50200) |
||
907 | ) { |
||
908 | $isNew = true; |
||
909 | $authPluginDropdown = $this->getHtmlForAuthPluginsDropdown( |
||
910 | $authPlugin, |
||
911 | $mode, |
||
912 | 'new' |
||
913 | ); |
||
914 | } else { |
||
915 | $isNew = false; |
||
916 | $authPluginDropdown = $this->getHtmlForAuthPluginsDropdown( |
||
917 | $authPlugin, |
||
918 | $mode, |
||
919 | 'old' |
||
920 | ); |
||
921 | } |
||
922 | |||
923 | return $this->template->render('server/privileges/login_information_fields', [ |
||
924 | 'pred_username' => $pred_username ?? null, |
||
925 | 'pred_hostname' => $pred_hostname ?? null, |
||
926 | 'username_length' => $usernameLength, |
||
927 | 'hostname_length' => $hostnameLength, |
||
928 | 'username' => $username ?? null, |
||
929 | 'new_username' => $new_username ?? null, |
||
930 | 'hostname' => $hostname ?? null, |
||
931 | 'this_host' => $thisHost, |
||
932 | 'is_change' => $mode === 'change', |
||
933 | 'auth_plugin' => $authPlugin, |
||
934 | 'auth_plugin_dropdown' => $authPluginDropdown, |
||
935 | 'is_new' => $isNew, |
||
936 | ]); |
||
937 | } |
||
938 | |||
939 | /** |
||
940 | * Get username and hostname length |
||
941 | * |
||
942 | * @return array username length and hostname length |
||
943 | */ |
||
944 | public function getUsernameAndHostnameLength() |
||
967 | ]; |
||
968 | } |
||
969 | |||
970 | /** |
||
971 | * Get current authentication plugin in use - for a user or globally |
||
972 | * |
||
973 | * @param string $mode are we creating a new user or are we just |
||
974 | * changing one? (allowed values: 'new', 'change') |
||
975 | * @param string $username User name |
||
976 | * @param string $hostname Host name |
||
977 | * |
||
978 | * @return string authentication plugin in use |
||
979 | */ |
||
980 | public function getCurrentAuthenticationPlugin( |
||
981 | $mode = 'new', |
||
982 | $username = null, |
||
983 | $hostname = null |
||
984 | ) { |
||
985 | /* Fallback (standard) value */ |
||
986 | $authentication_plugin = 'mysql_native_password'; |
||
987 | $serverVersion = $this->dbi->getVersion(); |
||
988 | |||
989 | if (isset($username, $hostname) && $mode == 'change') { |
||
990 | $row = $this->dbi->fetchSingleRow( |
||
991 | 'SELECT `plugin` FROM `mysql`.`user` WHERE ' |
||
992 | . '`User` = "' . $username . '" AND `Host` = "' . $hostname . '" LIMIT 1' |
||
993 | ); |
||
994 | // Table 'mysql'.'user' may not exist for some previous |
||
995 | // versions of MySQL - in that case consider fallback value |
||
996 | if (is_array($row) && isset($row['plugin'])) { |
||
997 | $authentication_plugin = $row['plugin']; |
||
998 | } |
||
999 | } elseif ($mode == 'change') { |
||
1000 | list($username, $hostname) = $this->dbi->getCurrentUserAndHost(); |
||
1001 | |||
1002 | $row = $this->dbi->fetchSingleRow( |
||
1003 | 'SELECT `plugin` FROM `mysql`.`user` WHERE ' |
||
1004 | . '`User` = "' . $username . '" AND `Host` = "' . $hostname . '"' |
||
1005 | ); |
||
1006 | if (is_array($row) && isset($row['plugin'])) { |
||
1007 | $authentication_plugin = $row['plugin']; |
||
1008 | } |
||
1009 | } elseif ($serverVersion >= 50702) { |
||
1010 | $row = $this->dbi->fetchSingleRow( |
||
1011 | 'SELECT @@default_authentication_plugin' |
||
1012 | ); |
||
1013 | $authentication_plugin = is_array($row) ? $row['@@default_authentication_plugin'] : null; |
||
1014 | } |
||
1015 | |||
1016 | return $authentication_plugin; |
||
1017 | } |
||
1018 | |||
1019 | /** |
||
1020 | * Returns all the grants for a certain user on a certain host |
||
1021 | * Used in the export privileges for all users section |
||
1022 | * |
||
1023 | * @param string $user User name |
||
1024 | * @param string $host Host name |
||
1025 | * |
||
1026 | * @return string containing all the grants text |
||
1027 | */ |
||
1028 | public function getGrants($user, $host) |
||
1029 | { |
||
1030 | $grants = $this->dbi->fetchResult( |
||
1031 | "SHOW GRANTS FOR '" |
||
1032 | . $this->dbi->escapeString($user) . "'@'" |
||
1033 | . $this->dbi->escapeString($host) . "'" |
||
1034 | ); |
||
1035 | $response = ''; |
||
1036 | foreach ($grants as $one_grant) { |
||
1037 | $response .= $one_grant . ";\n\n"; |
||
1038 | } |
||
1039 | return $response; |
||
1040 | } |
||
1041 | |||
1042 | /** |
||
1043 | * Update password and get message for password updating |
||
1044 | * |
||
1045 | * @param string $err_url error url |
||
1046 | * @param string $username username |
||
1047 | * @param string $hostname hostname |
||
1048 | * |
||
1049 | * @return Message success or error message after updating password |
||
1050 | */ |
||
1051 | public function updatePassword($err_url, $username, $hostname) |
||
1052 | { |
||
1053 | // similar logic in /user_password |
||
1054 | $message = null; |
||
1055 | |||
1056 | if (isset($_POST['pma_pw'], $_POST['pma_pw2']) && empty($_POST['nopass'])) { |
||
1057 | if ($_POST['pma_pw'] != $_POST['pma_pw2']) { |
||
1058 | $message = Message::error(__('The passwords aren\'t the same!')); |
||
1059 | } elseif (empty($_POST['pma_pw']) || empty($_POST['pma_pw2'])) { |
||
1060 | $message = Message::error(__('The password is empty!')); |
||
1061 | } |
||
1062 | } |
||
1063 | |||
1064 | // here $nopass could be == 1 |
||
1065 | if ($message === null) { |
||
1066 | $hashing_function = 'PASSWORD'; |
||
1067 | $serverType = Util::getServerType(); |
||
1068 | $serverVersion = $this->dbi->getVersion(); |
||
1069 | $authentication_plugin |
||
1070 | = (isset($_POST['authentication_plugin']) |
||
1071 | ? $_POST['authentication_plugin'] |
||
1072 | : $this->getCurrentAuthenticationPlugin( |
||
1073 | 'change', |
||
1074 | $username, |
||
1075 | $hostname |
||
1076 | )); |
||
1077 | |||
1078 | // Use 'ALTER USER ...' syntax for MySQL 5.7.6+ |
||
1079 | if ($serverType == 'MySQL' |
||
1080 | && $serverVersion >= 50706 |
||
1081 | ) { |
||
1082 | if ($authentication_plugin != 'mysql_old_password') { |
||
1083 | $query_prefix = "ALTER USER '" |
||
1084 | . $this->dbi->escapeString($username) |
||
1085 | . "'@'" . $this->dbi->escapeString($hostname) . "'" |
||
1086 | . " IDENTIFIED WITH " |
||
1087 | . $authentication_plugin |
||
1088 | . " BY '"; |
||
1089 | } else { |
||
1090 | $query_prefix = "ALTER USER '" |
||
1091 | . $this->dbi->escapeString($username) |
||
1092 | . "'@'" . $this->dbi->escapeString($hostname) . "'" |
||
1093 | . " IDENTIFIED BY '"; |
||
1094 | } |
||
1095 | |||
1096 | // in $sql_query which will be displayed, hide the password |
||
1097 | $sql_query = $query_prefix . "*'"; |
||
1098 | |||
1099 | $local_query = $query_prefix |
||
1100 | . $this->dbi->escapeString($_POST['pma_pw']) . "'"; |
||
1101 | } elseif ($serverType == 'MariaDB' && $serverVersion >= 10000) { |
||
1102 | // MariaDB uses "SET PASSWORD" syntax to change user password. |
||
1103 | // On Galera cluster only DDL queries are replicated, since |
||
1104 | // users are stored in MyISAM storage engine. |
||
1105 | $query_prefix = "SET PASSWORD FOR '" |
||
1106 | . $this->dbi->escapeString($username) |
||
1107 | . "'@'" . $this->dbi->escapeString($hostname) . "'" |
||
1108 | . " = PASSWORD ('"; |
||
1109 | $sql_query = $local_query = $query_prefix |
||
1110 | . $this->dbi->escapeString($_POST['pma_pw']) . "')"; |
||
1111 | } elseif ($serverType == 'MariaDB' |
||
1112 | && $serverVersion >= 50200 |
||
1113 | && $this->dbi->isSuperuser() |
||
1114 | ) { |
||
1115 | // Use 'UPDATE `mysql`.`user` ...' Syntax for MariaDB 5.2+ |
||
1116 | if ($authentication_plugin == 'mysql_native_password') { |
||
1117 | // Set the hashing method used by PASSWORD() |
||
1118 | // to be 'mysql_native_password' type |
||
1119 | $this->dbi->tryQuery('SET old_passwords = 0;'); |
||
1120 | } elseif ($authentication_plugin == 'sha256_password') { |
||
1121 | // Set the hashing method used by PASSWORD() |
||
1122 | // to be 'sha256_password' type |
||
1123 | $this->dbi->tryQuery('SET `old_passwords` = 2;'); |
||
1124 | } |
||
1125 | |||
1126 | $hashedPassword = $this->getHashedPassword($_POST['pma_pw']); |
||
1127 | |||
1128 | $sql_query = 'SET PASSWORD FOR \'' |
||
1129 | . $this->dbi->escapeString($username) |
||
1130 | . '\'@\'' . $this->dbi->escapeString($hostname) . '\' = ' |
||
1131 | . ($_POST['pma_pw'] == '' |
||
1132 | ? '\'\'' |
||
1133 | : $hashing_function . '(\'' |
||
1134 | . preg_replace('@.@s', '*', $_POST['pma_pw']) . '\')'); |
||
1135 | |||
1136 | $local_query = "UPDATE `mysql`.`user` SET " |
||
1137 | . " `authentication_string` = '" . $hashedPassword |
||
1138 | . "', `Password` = '', " |
||
1139 | . " `plugin` = '" . $authentication_plugin . "'" |
||
1140 | . " WHERE `User` = '" . $username . "' AND Host = '" |
||
1141 | . $hostname . "';"; |
||
1142 | } else { |
||
1143 | // USE 'SET PASSWORD ...' syntax for rest of the versions |
||
1144 | // Backup the old value, to be reset later |
||
1145 | $row = $this->dbi->fetchSingleRow( |
||
1146 | 'SELECT @@old_passwords;' |
||
1147 | ); |
||
1148 | $orig_value = $row['@@old_passwords']; |
||
1149 | $update_plugin_query = "UPDATE `mysql`.`user` SET" |
||
1150 | . " `plugin` = '" . $authentication_plugin . "'" |
||
1151 | . " WHERE `User` = '" . $username . "' AND Host = '" |
||
1152 | . $hostname . "';"; |
||
1153 | |||
1154 | // Update the plugin for the user |
||
1155 | if (! $this->dbi->tryQuery($update_plugin_query)) { |
||
1156 | Util::mysqlDie( |
||
1157 | $this->dbi->getError(), |
||
1158 | $update_plugin_query, |
||
1159 | false, |
||
1160 | $err_url |
||
1161 | ); |
||
1162 | } |
||
1163 | $this->dbi->tryQuery("FLUSH PRIVILEGES;"); |
||
1164 | |||
1165 | if ($authentication_plugin == 'mysql_native_password') { |
||
1166 | // Set the hashing method used by PASSWORD() |
||
1167 | // to be 'mysql_native_password' type |
||
1168 | $this->dbi->tryQuery('SET old_passwords = 0;'); |
||
1169 | } elseif ($authentication_plugin == 'sha256_password') { |
||
1170 | // Set the hashing method used by PASSWORD() |
||
1171 | // to be 'sha256_password' type |
||
1172 | $this->dbi->tryQuery('SET `old_passwords` = 2;'); |
||
1173 | } |
||
1174 | $sql_query = 'SET PASSWORD FOR \'' |
||
1175 | . $this->dbi->escapeString($username) |
||
1176 | . '\'@\'' . $this->dbi->escapeString($hostname) . '\' = ' |
||
1177 | . ($_POST['pma_pw'] == '' |
||
1178 | ? '\'\'' |
||
1179 | : $hashing_function . '(\'' |
||
1180 | . preg_replace('@.@s', '*', $_POST['pma_pw']) . '\')'); |
||
1181 | |||
1182 | $local_query = 'SET PASSWORD FOR \'' |
||
1183 | . $this->dbi->escapeString($username) |
||
1184 | . '\'@\'' . $this->dbi->escapeString($hostname) . '\' = ' |
||
1185 | . ($_POST['pma_pw'] == '' ? '\'\'' : $hashing_function |
||
1186 | . '(\'' . $this->dbi->escapeString($_POST['pma_pw']) . '\')'); |
||
1187 | } |
||
1188 | |||
1189 | if (! $this->dbi->tryQuery($local_query)) { |
||
1190 | Util::mysqlDie( |
||
1191 | $this->dbi->getError(), |
||
1192 | $sql_query, |
||
1193 | false, |
||
1194 | $err_url |
||
1195 | ); |
||
1196 | } |
||
1197 | // Flush privileges after successful password change |
||
1198 | $this->dbi->tryQuery("FLUSH PRIVILEGES;"); |
||
1199 | |||
1200 | $message = Message::success( |
||
1201 | __('The password for %s was changed successfully.') |
||
1202 | ); |
||
1203 | $message->addParam('\'' . $username . '\'@\'' . $hostname . '\''); |
||
1204 | if (isset($orig_value)) { |
||
1205 | $this->dbi->tryQuery( |
||
1206 | 'SET `old_passwords` = ' . $orig_value . ';' |
||
1207 | ); |
||
1208 | } |
||
1209 | } |
||
1210 | return $message; |
||
1211 | } |
||
1212 | |||
1213 | /** |
||
1214 | * Revokes privileges and get message and SQL query for privileges revokes |
||
1215 | * |
||
1216 | * @param string $dbname database name |
||
1217 | * @param string $tablename table name |
||
1218 | * @param string $username username |
||
1219 | * @param string $hostname host name |
||
1220 | * @param string $itemType item type |
||
1221 | * |
||
1222 | * @return array ($message, $sql_query) |
||
1223 | */ |
||
1224 | public function getMessageAndSqlQueryForPrivilegesRevoke( |
||
1225 | $dbname, |
||
1226 | $tablename, |
||
1227 | $username, |
||
1228 | $hostname, |
||
1229 | $itemType |
||
1230 | ) { |
||
1231 | $db_and_table = $this->wildcardEscapeForGrant($dbname, $tablename); |
||
1232 | |||
1233 | $sql_query0 = 'REVOKE ALL PRIVILEGES ON ' . $itemType . ' ' . $db_and_table |
||
1234 | . ' FROM \'' |
||
1235 | . $this->dbi->escapeString($username) . '\'@\'' |
||
1236 | . $this->dbi->escapeString($hostname) . '\';'; |
||
1237 | |||
1238 | $sql_query1 = 'REVOKE GRANT OPTION ON ' . $itemType . ' ' . $db_and_table |
||
1239 | . ' FROM \'' . $this->dbi->escapeString($username) . '\'@\'' |
||
1240 | . $this->dbi->escapeString($hostname) . '\';'; |
||
1241 | |||
1242 | $this->dbi->query($sql_query0); |
||
1243 | if (! $this->dbi->tryQuery($sql_query1)) { |
||
1244 | // this one may fail, too... |
||
1245 | $sql_query1 = ''; |
||
1246 | } |
||
1247 | $sql_query = $sql_query0 . ' ' . $sql_query1; |
||
1248 | $message = Message::success( |
||
1249 | __('You have revoked the privileges for %s.') |
||
1250 | ); |
||
1251 | $message->addParam('\'' . $username . '\'@\'' . $hostname . '\''); |
||
1252 | |||
1253 | return [ |
||
1254 | $message, |
||
1255 | $sql_query, |
||
1256 | ]; |
||
1257 | } |
||
1258 | |||
1259 | /** |
||
1260 | * Get REQUIRE cluase |
||
1261 | * |
||
1262 | * @return string REQUIRE clause |
||
1263 | */ |
||
1264 | public function getRequireClause() |
||
1295 | } |
||
1296 | |||
1297 | /** |
||
1298 | * Get a WITH clause for 'update privileges' and 'add user' |
||
1299 | * |
||
1300 | * @return string |
||
1301 | */ |
||
1302 | public function getWithClauseForAddUserAndUpdatePrivs() |
||
1303 | { |
||
1304 | $sql_query = ''; |
||
1305 | if (((isset($_POST['Grant_priv']) && $_POST['Grant_priv'] == 'Y') |
||
1306 | || (isset($GLOBALS['Grant_priv']) && $GLOBALS['Grant_priv'] == 'Y')) |
||
1307 | && ! ((Util::getServerType() == 'MySQL' || Util::getServerType() == 'Percona Server') |
||
1308 | && $this->dbi->getVersion() >= 80011) |
||
1309 | ) { |
||
1310 | $sql_query .= ' GRANT OPTION'; |
||
1311 | } |
||
1312 | if (isset($_POST['max_questions']) || isset($GLOBALS['max_questions'])) { |
||
1313 | $max_questions = isset($_POST['max_questions']) |
||
1314 | ? (int) $_POST['max_questions'] : (int) $GLOBALS['max_questions']; |
||
1315 | $max_questions = max(0, $max_questions); |
||
1316 | $sql_query .= ' MAX_QUERIES_PER_HOUR ' . $max_questions; |
||
1317 | } |
||
1318 | if (isset($_POST['max_connections']) || isset($GLOBALS['max_connections'])) { |
||
1319 | $max_connections = isset($_POST['max_connections']) |
||
1320 | ? (int) $_POST['max_connections'] : (int) $GLOBALS['max_connections']; |
||
1321 | $max_connections = max(0, $max_connections); |
||
1322 | $sql_query .= ' MAX_CONNECTIONS_PER_HOUR ' . $max_connections; |
||
1323 | } |
||
1324 | if (isset($_POST['max_updates']) || isset($GLOBALS['max_updates'])) { |
||
1325 | $max_updates = isset($_POST['max_updates']) |
||
1326 | ? (int) $_POST['max_updates'] : (int) $GLOBALS['max_updates']; |
||
1327 | $max_updates = max(0, $max_updates); |
||
1328 | $sql_query .= ' MAX_UPDATES_PER_HOUR ' . $max_updates; |
||
1329 | } |
||
1330 | if (isset($_POST['max_user_connections']) |
||
1331 | || isset($GLOBALS['max_user_connections']) |
||
1332 | ) { |
||
1333 | $max_user_connections = isset($_POST['max_user_connections']) |
||
1334 | ? (int) $_POST['max_user_connections'] |
||
1335 | : (int) $GLOBALS['max_user_connections']; |
||
1336 | $max_user_connections = max(0, $max_user_connections); |
||
1337 | $sql_query .= ' MAX_USER_CONNECTIONS ' . $max_user_connections; |
||
1338 | } |
||
1339 | return (! empty($sql_query) ? ' WITH' . $sql_query : ''); |
||
1340 | } |
||
1341 | |||
1342 | /** |
||
1343 | * Get HTML for addUsersForm, This function call if isset($_GET['adduser']) |
||
1344 | * |
||
1345 | * @param string $dbname database name |
||
1346 | * |
||
1347 | * @return string HTML for addUserForm |
||
1348 | */ |
||
1349 | public function getHtmlForAddUser($dbname) |
||
1364 | ]); |
||
1365 | } |
||
1366 | |||
1367 | /** |
||
1368 | * Get the HTML for user form and check the privileges for a particular database. |
||
1369 | * |
||
1370 | * @param string $db database name |
||
1371 | * |
||
1372 | * @return string |
||
1373 | */ |
||
1374 | public function getHtmlForSpecificDbPrivileges(string $db): string |
||
1375 | { |
||
1376 | global $cfg, $pmaThemeImage, $text_dir, $is_createuser, $is_grantuser; |
||
1377 | |||
1378 | $scriptName = Util::getScriptNameForOption( |
||
1379 | $cfg['DefaultTabDatabase'], |
||
1380 | 'database' |
||
1381 | ); |
||
1382 | |||
1383 | $privileges = []; |
||
1384 | if ($this->dbi->isSuperuser()) { |
||
1385 | $privileges = $this->getAllPrivileges($db); |
||
1386 | } |
||
1387 | |||
1388 | $response = Response::getInstance(); |
||
1389 | if ($response->isAjax() === true |
||
1390 | && empty($_REQUEST['ajax_page_request']) |
||
1391 | ) { |
||
1392 | $message = Message::success(__('User has been added.')); |
||
1393 | $response->addJSON('message', $message); |
||
1394 | exit; |
||
1395 | } |
||
1396 | |||
1397 | return $this->template->render('server/privileges/database', [ |
||
1398 | 'is_superuser' => $this->dbi->isSuperuser(), |
||
1399 | 'db' => $db, |
||
1400 | 'database_url' => $scriptName, |
||
1401 | 'pma_theme_image' => $pmaThemeImage, |
||
1402 | 'text_dir' => $text_dir, |
||
1403 | 'is_createuser' => $is_createuser, |
||
1404 | 'is_grantuser' => $is_grantuser, |
||
1405 | 'privileges' => $privileges, |
||
1406 | ]); |
||
1407 | } |
||
1408 | |||
1409 | /** |
||
1410 | * Get the HTML for user form and check the privileges for a particular table. |
||
1411 | * |
||
1412 | * @param string $db database name |
||
1413 | * @param string $table table name |
||
1414 | * |
||
1415 | * @return string |
||
1416 | */ |
||
1417 | public function getHtmlForSpecificTablePrivileges(string $db, string $table): string |
||
1418 | { |
||
1419 | global $cfg, $pmaThemeImage, $text_dir, $is_createuser, $is_grantuser; |
||
1420 | |||
1421 | $scriptName = Util::getScriptNameForOption( |
||
1422 | $cfg['DefaultTabTable'], |
||
1423 | 'table' |
||
1424 | ); |
||
1425 | |||
1426 | $privileges = []; |
||
1427 | if ($this->dbi->isSuperuser()) { |
||
1428 | $privileges = $this->getAllPrivileges($db, $table); |
||
1429 | } |
||
1430 | |||
1431 | return $this->template->render('server/privileges/table', [ |
||
1432 | 'db' => $db, |
||
1433 | 'table' => $table, |
||
1434 | 'is_superuser' => $this->dbi->isSuperuser(), |
||
1435 | 'table_url' => $scriptName, |
||
1436 | 'pma_theme_image' => $pmaThemeImage, |
||
1437 | 'text_dir' => $text_dir, |
||
1438 | 'is_createuser' => $is_createuser, |
||
1439 | 'is_grantuser' => $is_grantuser, |
||
1440 | 'privileges' => $privileges, |
||
1441 | ]); |
||
1442 | } |
||
1443 | |||
1444 | /** |
||
1445 | * @param string $db database name |
||
1446 | * @param string $table table name |
||
1447 | * |
||
1448 | * @return array |
||
1449 | */ |
||
1450 | private function getAllPrivileges(string $db, string $table = ''): array |
||
1511 | } |
||
1512 | |||
1513 | /** |
||
1514 | * @param string $db database name |
||
1515 | * |
||
1516 | * @return array |
||
1517 | */ |
||
1518 | private function getGlobalAndDatabasePrivileges(string $db): array |
||
1519 | { |
||
1520 | $listOfPrivileges = '`Select_priv`, |
||
1521 | `Insert_priv`, |
||
1522 | `Update_priv`, |
||
1523 | `Delete_priv`, |
||
1524 | `Create_priv`, |
||
1525 | `Drop_priv`, |
||
1526 | `Grant_priv`, |
||
1527 | `Index_priv`, |
||
1528 | `Alter_priv`, |
||
1529 | `References_priv`, |
||
1530 | `Create_tmp_table_priv`, |
||
1531 | `Lock_tables_priv`, |
||
1532 | `Create_view_priv`, |
||
1533 | `Show_view_priv`, |
||
1534 | `Create_routine_priv`, |
||
1535 | `Alter_routine_priv`, |
||
1536 | `Execute_priv`, |
||
1537 | `Event_priv`, |
||
1538 | `Trigger_priv`,'; |
||
1539 | |||
1540 | $listOfComparedPrivileges = '`Select_priv` = \'N\' AND |
||
1541 | `Insert_priv` = \'N\' AND |
||
1542 | `Update_priv` = \'N\' AND |
||
1543 | `Delete_priv` = \'N\' AND |
||
1544 | `Create_priv` = \'N\' AND |
||
1545 | `Drop_priv` = \'N\' AND |
||
1546 | `Grant_priv` = \'N\' AND |
||
1547 | `References_priv` = \'N\' AND |
||
1548 | `Create_tmp_table_priv` = \'N\' AND |
||
1549 | `Lock_tables_priv` = \'N\' AND |
||
1550 | `Create_view_priv` = \'N\' AND |
||
1551 | `Show_view_priv` = \'N\' AND |
||
1552 | `Create_routine_priv` = \'N\' AND |
||
1553 | `Alter_routine_priv` = \'N\' AND |
||
1554 | `Execute_priv` = \'N\' AND |
||
1555 | `Event_priv` = \'N\' AND |
||
1556 | `Trigger_priv` = \'N\''; |
||
1557 | |||
1558 | $query = ' |
||
1559 | ( |
||
1560 | SELECT `User`, `Host`, ' . $listOfPrivileges . ' \'*\' AS `Db`, \'g\' AS `Type` |
||
1561 | FROM `mysql`.`user` |
||
1562 | WHERE NOT (' . $listOfComparedPrivileges . ') |
||
1563 | ) |
||
1564 | UNION |
||
1565 | ( |
||
1566 | SELECT `User`, `Host`, ' . $listOfPrivileges . ' `Db`, \'d\' AS `Type` |
||
1567 | FROM `mysql`.`db` |
||
1568 | WHERE \'' . $this->dbi->escapeString($db) . '\' LIKE `Db` AND NOT (' . $listOfComparedPrivileges . ') |
||
1569 | ) |
||
1570 | ORDER BY `User` ASC, `Host` ASC, `Db` ASC; |
||
1571 | '; |
||
1572 | $result = $this->dbi->query($query); |
||
1573 | if ($result === false) { |
||
1574 | return []; |
||
1575 | } |
||
1576 | |||
1577 | $privileges = []; |
||
1578 | while ($row = $this->dbi->fetchAssoc($result)) { |
||
1579 | $privileges[] = $row; |
||
1580 | } |
||
1581 | return $privileges; |
||
1582 | } |
||
1583 | |||
1584 | /** |
||
1585 | * @param string $db database name |
||
1586 | * @param string $table table name |
||
1587 | * |
||
1588 | * @return array |
||
1589 | */ |
||
1590 | private function getTablePrivileges(string $db, string $table): array |
||
1591 | { |
||
1592 | $query = ' |
||
1593 | SELECT `User`, `Host`, `Db`, \'t\' AS `Type`, `Table_name`, `Table_priv` |
||
1594 | FROM `mysql`.`tables_priv` |
||
1595 | WHERE |
||
1596 | \'' . $this->dbi->escapeString($db) . '\' LIKE `Db` AND |
||
1597 | \'' . $this->dbi->escapeString($table) . '\' LIKE `Table_name` AND |
||
1598 | NOT (`Table_priv` = \'\' AND Column_priv = \'\') |
||
1599 | ORDER BY `User` ASC, `Host` ASC, `Db` ASC, `Table_priv` ASC; |
||
1600 | '; |
||
1601 | $result = $this->dbi->query($query); |
||
1602 | if ($result === false) { |
||
1603 | return []; |
||
1604 | } |
||
1605 | |||
1606 | $privileges = []; |
||
1607 | while ($row = $this->dbi->fetchAssoc($result)) { |
||
1608 | $privileges[] = $row; |
||
1609 | } |
||
1610 | return $privileges; |
||
1611 | } |
||
1612 | |||
1613 | /** |
||
1614 | * @param string $db database name |
||
1615 | * |
||
1616 | * @return array |
||
1617 | */ |
||
1618 | private function getRoutinesPrivileges(string $db): array |
||
1619 | { |
||
1620 | $query = ' |
||
1621 | SELECT *, \'r\' AS `Type` |
||
1622 | FROM `mysql`.`procs_priv` |
||
1623 | WHERE Db = \'' . $this->dbi->escapeString($db) . '\'; |
||
1624 | '; |
||
1625 | $result = $this->dbi->query($query); |
||
1626 | if ($result === false) { |
||
1627 | return []; |
||
1628 | } |
||
1629 | |||
1630 | $privileges = []; |
||
1631 | while ($row = $this->dbi->fetchAssoc($result)) { |
||
1632 | $privileges[] = $row; |
||
1633 | } |
||
1634 | return $privileges; |
||
1635 | } |
||
1636 | |||
1637 | /** |
||
1638 | * Get HTML error for View Users form |
||
1639 | * For non superusers such as grant/create users |
||
1640 | * |
||
1641 | * @return string |
||
1642 | */ |
||
1643 | public function getHtmlForViewUsersError() |
||
1644 | { |
||
1645 | return Message::error( |
||
1646 | __('Not enough privilege to view users.') |
||
1647 | )->getDisplay(); |
||
1648 | } |
||
1649 | |||
1650 | /** |
||
1651 | * Returns edit, revoke or export link for a user. |
||
1652 | * |
||
1653 | * @param string $linktype The link type (edit | revoke | export) |
||
1654 | * @param string $username User name |
||
1655 | * @param string $hostname Host name |
||
1656 | * @param string $dbname Database name |
||
1657 | * @param string $tablename Table name |
||
1658 | * @param string $routinename Routine name |
||
1659 | * @param string $initial Initial value |
||
1660 | * |
||
1661 | * @return string HTML code with link |
||
1662 | */ |
||
1663 | public function getUserLink( |
||
1664 | $linktype, |
||
1665 | $username, |
||
1666 | $hostname, |
||
1667 | $dbname = '', |
||
1668 | $tablename = '', |
||
1669 | $routinename = '', |
||
1670 | $initial = '' |
||
1671 | ) { |
||
1672 | $html = '<a'; |
||
1673 | switch ($linktype) { |
||
1674 | case 'edit': |
||
1675 | $html .= ' class="edit_user_anchor"'; |
||
1676 | break; |
||
1677 | case 'export': |
||
1678 | $html .= ' class="export_user_anchor ajax"'; |
||
1679 | break; |
||
1680 | } |
||
1681 | $params = [ |
||
1682 | 'username' => $username, |
||
1683 | 'hostname' => $hostname, |
||
1684 | ]; |
||
1685 | switch ($linktype) { |
||
1686 | case 'edit': |
||
1687 | $params['dbname'] = $dbname; |
||
1688 | $params['tablename'] = $tablename; |
||
1689 | $params['routinename'] = $routinename; |
||
1690 | break; |
||
1691 | case 'revoke': |
||
1692 | $params['dbname'] = $dbname; |
||
1693 | $params['tablename'] = $tablename; |
||
1694 | $params['routinename'] = $routinename; |
||
1695 | $params['revokeall'] = 1; |
||
1696 | break; |
||
1697 | case 'export': |
||
1698 | $params['initial'] = $initial; |
||
1699 | $params['export'] = 1; |
||
1700 | break; |
||
1701 | } |
||
1702 | |||
1703 | $html .= ' href="' . Url::getFromRoute('/server/privileges'); |
||
1704 | if ($linktype == 'revoke') { |
||
1705 | $html .= '" data-post="' . Url::getCommon($params, ''); |
||
1706 | } else { |
||
1707 | $html .= Url::getCommon($params, '&'); |
||
1708 | } |
||
1709 | $html .= '">'; |
||
1710 | |||
1711 | switch ($linktype) { |
||
1712 | case 'edit': |
||
1713 | $html .= Util::getIcon('b_usredit', __('Edit privileges')); |
||
1714 | break; |
||
1715 | case 'revoke': |
||
1716 | $html .= Util::getIcon('b_usrdrop', __('Revoke')); |
||
1717 | break; |
||
1718 | case 'export': |
||
1719 | $html .= Util::getIcon('b_tblexport', __('Export')); |
||
1720 | break; |
||
1721 | } |
||
1722 | $html .= '</a>'; |
||
1723 | |||
1724 | return $html; |
||
1725 | } |
||
1726 | |||
1727 | /** |
||
1728 | * Returns user group edit link |
||
1729 | * |
||
1730 | * @param string $username User name |
||
1731 | * |
||
1732 | * @return string HTML code with link |
||
1733 | */ |
||
1734 | public function getUserGroupEditLink($username) |
||
1735 | { |
||
1736 | return '<a class="edit_user_group_anchor ajax"' |
||
1737 | . ' href="' . Url::getFromRoute('/server/privileges', ['username' => $username]) |
||
1738 | . '">' |
||
1739 | . Util::getIcon('b_usrlist', __('Edit user group')) |
||
1740 | . '</a>'; |
||
1741 | } |
||
1742 | |||
1743 | /** |
||
1744 | * Returns number of defined user groups |
||
1745 | * |
||
1746 | * @return integer |
||
1747 | */ |
||
1748 | public function getUserGroupCount() |
||
1749 | { |
||
1750 | $cfgRelation = $this->relation->getRelationsParam(); |
||
1751 | $user_group_table = Util::backquote($cfgRelation['db']) |
||
1752 | . '.' . Util::backquote($cfgRelation['usergroups']); |
||
1753 | $sql_query = 'SELECT COUNT(*) FROM ' . $user_group_table; |
||
1754 | $user_group_count = $this->dbi->fetchValue( |
||
1755 | $sql_query, |
||
1756 | 0, |
||
1757 | 0, |
||
1758 | DatabaseInterface::CONNECT_CONTROL |
||
1759 | ); |
||
1760 | |||
1761 | return $user_group_count; |
||
1762 | } |
||
1763 | |||
1764 | /** |
||
1765 | * Returns name of user group that user is part of |
||
1766 | * |
||
1767 | * @param string $username User name |
||
1768 | * |
||
1769 | * @return mixed usergroup if found or null if not found |
||
1770 | */ |
||
1771 | public function getUserGroupForUser($username) |
||
1772 | { |
||
1773 | $cfgRelation = $this->relation->getRelationsParam(); |
||
1774 | |||
1775 | if (empty($cfgRelation['db']) |
||
1776 | || empty($cfgRelation['users']) |
||
1777 | ) { |
||
1778 | return null; |
||
1779 | } |
||
1780 | |||
1781 | $user_table = Util::backquote($cfgRelation['db']) |
||
1782 | . '.' . Util::backquote($cfgRelation['users']); |
||
1783 | $sql_query = 'SELECT `usergroup` FROM ' . $user_table |
||
1784 | . ' WHERE `username` = \'' . $username . '\'' |
||
1785 | . ' LIMIT 1'; |
||
1786 | |||
1787 | $usergroup = $this->dbi->fetchValue( |
||
1788 | $sql_query, |
||
1789 | 0, |
||
1790 | 0, |
||
1791 | DatabaseInterface::CONNECT_CONTROL |
||
1792 | ); |
||
1793 | |||
1794 | if ($usergroup === false) { |
||
1795 | return null; |
||
1796 | } |
||
1797 | |||
1798 | return $usergroup; |
||
1799 | } |
||
1800 | |||
1801 | /** |
||
1802 | * This function return the extra data array for the ajax behavior |
||
1803 | * |
||
1804 | * @param string $password password |
||
1805 | * @param string $sql_query sql query |
||
1806 | * @param string $hostname hostname |
||
1807 | * @param string $username username |
||
1808 | * |
||
1809 | * @return array |
||
1810 | */ |
||
1811 | public function getExtraDataForAjaxBehavior( |
||
1812 | $password, |
||
1813 | $sql_query, |
||
1814 | $hostname, |
||
1815 | $username |
||
1816 | ) { |
||
1817 | if (isset($GLOBALS['dbname'])) { |
||
1818 | //if (preg_match('/\\\\(?:_|%)/i', $dbname)) { |
||
1819 | if (preg_match('/(?<!\\\\)(?:_|%)/', $GLOBALS['dbname'])) { |
||
1820 | $dbname_is_wildcard = true; |
||
1821 | } else { |
||
1822 | $dbname_is_wildcard = false; |
||
1823 | } |
||
1824 | } |
||
1825 | |||
1826 | $user_group_count = 0; |
||
1827 | if ($GLOBALS['cfgRelation']['menuswork']) { |
||
1828 | $user_group_count = $this->getUserGroupCount(); |
||
1829 | } |
||
1830 | |||
1831 | $extra_data = []; |
||
1832 | if (strlen($sql_query) > 0) { |
||
1833 | $extra_data['sql_query'] = Util::getMessage(null, $sql_query); |
||
1834 | } |
||
1835 | |||
1836 | if (isset($_POST['change_copy'])) { |
||
1837 | /** |
||
1838 | * generate html on the fly for the new user that was just created. |
||
1839 | */ |
||
1840 | $new_user_string = '<tr>' . "\n" |
||
1841 | . '<td> <input type="checkbox" name="selected_usr[]" ' |
||
1842 | . 'id="checkbox_sel_users_"' |
||
1843 | . 'value="' |
||
1844 | . htmlspecialchars($username) |
||
1845 | . '&#27;' . htmlspecialchars($hostname) . '">' |
||
1846 | . '</td>' . "\n" |
||
1847 | . '<td><label for="checkbox_sel_users_">' |
||
1848 | . (empty($_POST['username']) |
||
1849 | ? '<span style="color: #FF0000">' . __('Any') . '</span>' |
||
1850 | : htmlspecialchars($username) ) . '</label></td>' . "\n" |
||
1851 | . '<td>' . htmlspecialchars($hostname) . '</td>' . "\n"; |
||
1852 | |||
1853 | $new_user_string .= '<td>'; |
||
1854 | |||
1855 | if (! empty($password) || isset($_POST['pma_pw'])) { |
||
1856 | $new_user_string .= __('Yes'); |
||
1857 | } else { |
||
1858 | $new_user_string .= '<span style="color: #FF0000">' |
||
1859 | . __('No') |
||
1860 | . '</span>'; |
||
1861 | } |
||
1862 | |||
1863 | $new_user_string .= '</td>' . "\n"; |
||
1864 | $new_user_string .= '<td>' |
||
1865 | . '<code>' . implode(', ', $this->extractPrivInfo(null, true)) . '</code>' |
||
1866 | . '</td>'; //Fill in privileges here |
||
1867 | |||
1868 | // if $cfg['Servers'][$i]['users'] and $cfg['Servers'][$i]['usergroups'] are |
||
1869 | // enabled |
||
1870 | $cfgRelation = $this->relation->getRelationsParam(); |
||
1871 | if (! empty($cfgRelation['users']) && ! empty($cfgRelation['usergroups'])) { |
||
1872 | $new_user_string .= '<td class="usrGroup"></td>'; |
||
1873 | } |
||
1874 | |||
1875 | $new_user_string .= '<td>'; |
||
1876 | if (isset($_POST['Grant_priv']) && $_POST['Grant_priv'] == 'Y') { |
||
1877 | $new_user_string .= __('Yes'); |
||
1878 | } else { |
||
1879 | $new_user_string .= __('No'); |
||
1880 | } |
||
1881 | $new_user_string .= '</td>'; |
||
1882 | |||
1883 | if ($GLOBALS['is_grantuser']) { |
||
1884 | $new_user_string .= '<td>' |
||
1885 | . $this->getUserLink('edit', $username, $hostname) |
||
1886 | . '</td>' . "\n"; |
||
1887 | } |
||
1888 | |||
1889 | if ($cfgRelation['menuswork'] && $user_group_count > 0) { |
||
1890 | $new_user_string .= '<td>' |
||
1891 | . $this->getUserGroupEditLink($username) |
||
1892 | . '</td>' . "\n"; |
||
1893 | } |
||
1894 | |||
1895 | $new_user_string .= '<td>' |
||
1896 | . $this->getUserLink( |
||
1897 | 'export', |
||
1898 | $username, |
||
1899 | $hostname, |
||
1900 | '', |
||
1901 | '', |
||
1902 | '', |
||
1903 | isset($_GET['initial']) ? $_GET['initial'] : '' |
||
1904 | ) |
||
1905 | . '</td>' . "\n"; |
||
1906 | |||
1907 | $new_user_string .= '</tr>'; |
||
1908 | |||
1909 | $extra_data['new_user_string'] = $new_user_string; |
||
1910 | |||
1911 | /** |
||
1912 | * Generate the string for this alphabet's initial, to update the user |
||
1913 | * pagination |
||
1914 | */ |
||
1915 | $new_user_initial = mb_strtoupper( |
||
1916 | mb_substr($username, 0, 1) |
||
1917 | ); |
||
1918 | $newUserInitialString = '<a href="' . Url::getFromRoute('/server/privileges', ['initial' => $new_user_initial]) . '">' |
||
1919 | . $new_user_initial . '</a>'; |
||
1920 | $extra_data['new_user_initial'] = $new_user_initial; |
||
1921 | $extra_data['new_user_initial_string'] = $newUserInitialString; |
||
1922 | } |
||
1923 | |||
1924 | if (isset($_POST['update_privs'])) { |
||
1925 | $extra_data['db_specific_privs'] = false; |
||
1926 | $extra_data['db_wildcard_privs'] = false; |
||
1927 | if (isset($dbname_is_wildcard)) { |
||
1928 | $extra_data['db_specific_privs'] = ! $dbname_is_wildcard; |
||
1929 | $extra_data['db_wildcard_privs'] = $dbname_is_wildcard; |
||
1930 | } |
||
1931 | $new_privileges = implode(', ', $this->extractPrivInfo(null, true)); |
||
1932 | |||
1933 | $extra_data['new_privileges'] = $new_privileges; |
||
1934 | } |
||
1935 | |||
1936 | if (isset($_GET['validate_username'])) { |
||
1937 | $sql_query = "SELECT * FROM `mysql`.`user` WHERE `User` = '" |
||
1938 | . $_GET['username'] . "';"; |
||
1939 | $res = $this->dbi->query($sql_query); |
||
1940 | $row = $this->dbi->fetchRow($res); |
||
1941 | if (empty($row)) { |
||
1942 | $extra_data['user_exists'] = false; |
||
1943 | } else { |
||
1944 | $extra_data['user_exists'] = true; |
||
1945 | } |
||
1946 | } |
||
1947 | |||
1948 | return $extra_data; |
||
1949 | } |
||
1950 | |||
1951 | /** |
||
1952 | * Get the HTML snippet for change user login information |
||
1953 | * |
||
1954 | * @param string $username username |
||
1955 | * @param string $hostname host name |
||
1956 | * |
||
1957 | * @return string HTML snippet |
||
1958 | */ |
||
1959 | public function getChangeLoginInformationHtmlForm($username, $hostname) |
||
2016 | } |
||
2017 | |||
2018 | /** |
||
2019 | * Provide a line with links to the relevant database and table |
||
2020 | * |
||
2021 | * @param string $url_dbname url database name that urlencode() string |
||
2022 | * @param string $dbname database name |
||
2023 | * @param string $tablename table name |
||
2024 | * |
||
2025 | * @return string HTML snippet |
||
2026 | */ |
||
2027 | public function getLinkToDbAndTable($url_dbname, $dbname, $tablename) |
||
2028 | { |
||
2029 | $scriptName = Util::getScriptNameForOption( |
||
2030 | $GLOBALS['cfg']['DefaultTabDatabase'], |
||
2031 | 'database' |
||
2032 | ); |
||
2033 | $html_output = '[ ' . __('Database') |
||
2034 | . ' <a href="' . $scriptName |
||
2035 | . Url::getCommon([ |
||
2036 | 'db' => $url_dbname, |
||
2037 | 'reload' => 1, |
||
2038 | ], strpos($scriptName, '?') === false ? '?' : '&') |
||
2039 | . '">' |
||
2040 | . htmlspecialchars(Util::unescapeMysqlWildcards($dbname)) . ': ' |
||
2041 | . Util::getTitleForTarget( |
||
2042 | $GLOBALS['cfg']['DefaultTabDatabase'] |
||
2043 | ) |
||
2044 | . "</a> ]\n"; |
||
2045 | |||
2046 | if (strlen($tablename) > 0) { |
||
2047 | $scriptName = Util::getScriptNameForOption( |
||
2048 | $GLOBALS['cfg']['DefaultTabTable'], |
||
2049 | 'table' |
||
2050 | ); |
||
2051 | $html_output .= ' [ ' . __('Table') . ' <a href="' |
||
2052 | . $scriptName |
||
2053 | . Url::getCommon([ |
||
2054 | 'db' => $url_dbname, |
||
2055 | 'table' => $tablename, |
||
2056 | 'reload' => 1, |
||
2057 | ], strpos($scriptName, '?') === false ? '?' : '&') |
||
2058 | . '">' . htmlspecialchars($tablename) . ': ' |
||
2059 | . Util::getTitleForTarget( |
||
2060 | $GLOBALS['cfg']['DefaultTabTable'] |
||
2061 | ) |
||
2062 | . "</a> ]\n"; |
||
2063 | } |
||
2064 | return $html_output; |
||
2065 | } |
||
2066 | |||
2067 | /** |
||
2068 | * no db name given, so we want all privs for the given user |
||
2069 | * db name was given, so we want all user specific rights for this db |
||
2070 | * So this function returns user rights as an array |
||
2071 | * |
||
2072 | * @param string $username username |
||
2073 | * @param string $hostname host name |
||
2074 | * @param string $type database or table |
||
2075 | * @param string $dbname database name |
||
2076 | * |
||
2077 | * @return array database rights |
||
2078 | */ |
||
2079 | public function getUserSpecificRights($username, $hostname, $type, $dbname = '') |
||
2080 | { |
||
2081 | $user_host_condition = " WHERE `User`" |
||
2082 | . " = '" . $this->dbi->escapeString($username) . "'" |
||
2083 | . " AND `Host`" |
||
2084 | . " = '" . $this->dbi->escapeString($hostname) . "'"; |
||
2085 | |||
2086 | if ($type == 'database') { |
||
2087 | $tables_to_search_for_users = [ |
||
2088 | 'tables_priv', |
||
2089 | 'columns_priv', |
||
2090 | 'procs_priv', |
||
2091 | ]; |
||
2092 | $dbOrTableName = 'Db'; |
||
2093 | } elseif ($type == 'table') { |
||
2094 | $user_host_condition .= " AND `Db` LIKE '" |
||
2095 | . $this->dbi->escapeString($dbname) . "'"; |
||
2096 | $tables_to_search_for_users = ['columns_priv']; |
||
2097 | $dbOrTableName = 'Table_name'; |
||
2098 | } else { // routine |
||
2099 | $user_host_condition .= " AND `Db` LIKE '" |
||
2100 | . $this->dbi->escapeString($dbname) . "'"; |
||
2101 | $tables_to_search_for_users = ['procs_priv']; |
||
2102 | $dbOrTableName = 'Routine_name'; |
||
2103 | } |
||
2104 | |||
2105 | // we also want privileges for this user not in table `db` but in other table |
||
2106 | $tables = $this->dbi->fetchResult('SHOW TABLES FROM `mysql`;'); |
||
2107 | |||
2108 | $db_rights_sqls = []; |
||
2109 | foreach ($tables_to_search_for_users as $table_search_in) { |
||
2110 | if (in_array($table_search_in, $tables)) { |
||
2111 | $db_rights_sqls[] = ' |
||
2112 | SELECT DISTINCT `' . $dbOrTableName . '` |
||
2113 | FROM `mysql`.' . Util::backquote($table_search_in) |
||
2114 | . $user_host_condition; |
||
2115 | } |
||
2116 | } |
||
2117 | |||
2118 | $user_defaults = [ |
||
2119 | $dbOrTableName => '', |
||
2120 | 'Grant_priv' => 'N', |
||
2121 | 'privs' => ['USAGE'], |
||
2122 | 'Column_priv' => true, |
||
2123 | ]; |
||
2124 | |||
2125 | // for the rights |
||
2126 | $db_rights = []; |
||
2127 | |||
2128 | $db_rights_sql = '(' . implode(') UNION (', $db_rights_sqls) . ')' |
||
2129 | . ' ORDER BY `' . $dbOrTableName . '` ASC'; |
||
2130 | |||
2131 | $db_rights_result = $this->dbi->query($db_rights_sql); |
||
2132 | |||
2133 | while ($db_rights_row = $this->dbi->fetchAssoc($db_rights_result)) { |
||
2134 | $db_rights_row = array_merge($user_defaults, $db_rights_row); |
||
2135 | if ($type == 'database') { |
||
2136 | // only Db names in the table `mysql`.`db` uses wildcards |
||
2137 | // as we are in the db specific rights display we want |
||
2138 | // all db names escaped, also from other sources |
||
2139 | $db_rights_row['Db'] = Util::escapeMysqlWildcards( |
||
2140 | $db_rights_row['Db'] |
||
2141 | ); |
||
2142 | } |
||
2143 | $db_rights[$db_rights_row[$dbOrTableName]] = $db_rights_row; |
||
2144 | } |
||
2145 | |||
2146 | $this->dbi->freeResult($db_rights_result); |
||
2147 | |||
2148 | if ($type == 'database') { |
||
2149 | $sql_query = 'SELECT * FROM `mysql`.`db`' |
||
2150 | . $user_host_condition . ' ORDER BY `Db` ASC'; |
||
2151 | } elseif ($type == 'table') { |
||
2152 | $sql_query = 'SELECT `Table_name`,' |
||
2153 | . ' `Table_priv`,' |
||
2154 | . ' IF(`Column_priv` = _latin1 \'\', 0, 1)' |
||
2155 | . ' AS \'Column_priv\'' |
||
2156 | . ' FROM `mysql`.`tables_priv`' |
||
2157 | . $user_host_condition |
||
2158 | . ' ORDER BY `Table_name` ASC;'; |
||
2159 | } else { |
||
2160 | $sql_query = "SELECT `Routine_name`, `Proc_priv`" |
||
2161 | . " FROM `mysql`.`procs_priv`" |
||
2162 | . $user_host_condition |
||
2163 | . " ORDER BY `Routine_name`"; |
||
2164 | } |
||
2165 | |||
2166 | $result = $this->dbi->query($sql_query); |
||
2167 | |||
2168 | while ($row = $this->dbi->fetchAssoc($result)) { |
||
2169 | if (isset($db_rights[$row[$dbOrTableName]])) { |
||
2170 | $db_rights[$row[$dbOrTableName]] |
||
2171 | = array_merge($db_rights[$row[$dbOrTableName]], $row); |
||
2172 | } else { |
||
2173 | $db_rights[$row[$dbOrTableName]] = $row; |
||
2174 | } |
||
2175 | if ($type == 'database') { |
||
2176 | // there are db specific rights for this user |
||
2177 | // so we can drop this db rights |
||
2178 | $db_rights[$row['Db']]['can_delete'] = true; |
||
2179 | } |
||
2180 | } |
||
2181 | $this->dbi->freeResult($result); |
||
2182 | return $db_rights; |
||
2183 | } |
||
2184 | |||
2185 | /** |
||
2186 | * Parses Proc_priv data |
||
2187 | * |
||
2188 | * @param string $privs Proc_priv |
||
2189 | * |
||
2190 | * @return array |
||
2191 | */ |
||
2192 | public function parseProcPriv($privs) |
||
2193 | { |
||
2194 | $result = [ |
||
2195 | 'Alter_routine_priv' => 'N', |
||
2196 | 'Execute_priv' => 'N', |
||
2197 | 'Grant_priv' => 'N', |
||
2198 | ]; |
||
2199 | foreach (explode(',', (string) $privs) as $priv) { |
||
2200 | if ($priv == 'Alter Routine') { |
||
2201 | $result['Alter_routine_priv'] = 'Y'; |
||
2202 | } else { |
||
2203 | $result[$priv . '_priv'] = 'Y'; |
||
2204 | } |
||
2205 | } |
||
2206 | return $result; |
||
2207 | } |
||
2208 | |||
2209 | /** |
||
2210 | * Get a HTML table for display user's tabel specific or database specific rights |
||
2211 | * |
||
2212 | * @param string $username username |
||
2213 | * @param string $hostname host name |
||
2214 | * @param string $type database, table or routine |
||
2215 | * @param string $dbname database name |
||
2216 | * |
||
2217 | * @return string |
||
2218 | */ |
||
2219 | public function getHtmlForAllTableSpecificRights( |
||
2220 | $username, |
||
2221 | $hostname, |
||
2222 | $type, |
||
2223 | $dbname = '' |
||
2224 | ) { |
||
2225 | $uiData = [ |
||
2226 | 'database' => [ |
||
2227 | 'form_id' => 'database_specific_priv', |
||
2228 | 'sub_menu_label' => __('Database'), |
||
2229 | 'legend' => __('Database-specific privileges'), |
||
2230 | 'type_label' => __('Database'), |
||
2231 | ], |
||
2232 | 'table' => [ |
||
2233 | 'form_id' => 'table_specific_priv', |
||
2234 | 'sub_menu_label' => __('Table'), |
||
2235 | 'legend' => __('Table-specific privileges'), |
||
2236 | 'type_label' => __('Table'), |
||
2237 | ], |
||
2238 | 'routine' => [ |
||
2239 | 'form_id' => 'routine_specific_priv', |
||
2240 | 'sub_menu_label' => __('Routine'), |
||
2241 | 'legend' => __('Routine-specific privileges'), |
||
2242 | 'type_label' => __('Routine'), |
||
2243 | ], |
||
2244 | ]; |
||
2245 | |||
2246 | /** |
||
2247 | * no db name given, so we want all privs for the given user |
||
2248 | * db name was given, so we want all user specific rights for this db |
||
2249 | */ |
||
2250 | $db_rights = $this->getUserSpecificRights($username, $hostname, $type, $dbname); |
||
2251 | ksort($db_rights); |
||
2252 | |||
2253 | $foundRows = []; |
||
2254 | $privileges = []; |
||
2255 | foreach ($db_rights as $row) { |
||
2256 | $onePrivilege = []; |
||
2257 | |||
2258 | $paramTableName = ''; |
||
2259 | $paramRoutineName = ''; |
||
2260 | |||
2261 | if ($type == 'database') { |
||
2262 | $name = $row['Db']; |
||
2263 | $onePrivilege['grant'] = $row['Grant_priv'] == 'Y'; |
||
2264 | $onePrivilege['table_privs'] = ! empty($row['Table_priv']) |
||
2265 | || ! empty($row['Column_priv']); |
||
2266 | $onePrivilege['privileges'] = implode(',', $this->extractPrivInfo($row, true)); |
||
2267 | |||
2268 | $paramDbName = $row['Db']; |
||
2269 | } elseif ($type == 'table') { |
||
2270 | $name = $row['Table_name']; |
||
2271 | $onePrivilege['grant'] = in_array( |
||
2272 | 'Grant', |
||
2273 | explode(',', $row['Table_priv']) |
||
2274 | ); |
||
2275 | $onePrivilege['column_privs'] = ! empty($row['Column_priv']); |
||
2276 | $onePrivilege['privileges'] = implode(',', $this->extractPrivInfo($row, true)); |
||
2277 | |||
2278 | $paramDbName = $dbname; |
||
2279 | $paramTableName = $row['Table_name']; |
||
2280 | } else { // routine |
||
2281 | $name = $row['Routine_name']; |
||
2282 | $onePrivilege['grant'] = in_array( |
||
2283 | 'Grant', |
||
2284 | explode(',', $row['Proc_priv']) |
||
2285 | ); |
||
2286 | |||
2287 | $privs = $this->parseProcPriv($row['Proc_priv']); |
||
2288 | $onePrivilege['privileges'] = implode( |
||
2289 | ',', |
||
2290 | $this->extractPrivInfo($privs, true) |
||
2291 | ); |
||
2292 | |||
2293 | $paramDbName = $dbname; |
||
2294 | $paramRoutineName = $row['Routine_name']; |
||
2295 | } |
||
2296 | |||
2297 | $foundRows[] = $name; |
||
2298 | $onePrivilege['name'] = $name; |
||
2299 | |||
2300 | $onePrivilege['edit_link'] = ''; |
||
2301 | if ($GLOBALS['is_grantuser']) { |
||
2302 | $onePrivilege['edit_link'] = $this->getUserLink( |
||
2303 | 'edit', |
||
2304 | $username, |
||
2305 | $hostname, |
||
2306 | $paramDbName, |
||
2307 | $paramTableName, |
||
2308 | $paramRoutineName |
||
2309 | ); |
||
2310 | } |
||
2311 | |||
2312 | $onePrivilege['revoke_link'] = ''; |
||
2313 | if ($type != 'database' || ! empty($row['can_delete'])) { |
||
2314 | $onePrivilege['revoke_link'] = $this->getUserLink( |
||
2315 | 'revoke', |
||
2316 | $username, |
||
2317 | $hostname, |
||
2318 | $paramDbName, |
||
2319 | $paramTableName, |
||
2320 | $paramRoutineName |
||
2321 | ); |
||
2322 | } |
||
2323 | |||
2324 | $privileges[] = $onePrivilege; |
||
2325 | } |
||
2326 | |||
2327 | $data = $uiData[$type]; |
||
2328 | $data['privileges'] = $privileges; |
||
2329 | $data['username'] = $username; |
||
2330 | $data['hostname'] = $hostname; |
||
2331 | $data['database'] = $dbname; |
||
2332 | $data['type'] = $type; |
||
2333 | |||
2334 | if ($type == 'database') { |
||
2335 | // we already have the list of databases from libraries/common.inc.php |
||
2336 | // via $pma = new PMA; |
||
2337 | $pred_db_array = $GLOBALS['dblist']->databases; |
||
2338 | $databases_to_skip = [ |
||
2339 | 'information_schema', |
||
2340 | 'performance_schema', |
||
2341 | ]; |
||
2342 | |||
2343 | $databases = []; |
||
2344 | if (! empty($pred_db_array)) { |
||
2345 | foreach ($pred_db_array as $current_db) { |
||
2346 | if (in_array($current_db, $databases_to_skip)) { |
||
2347 | continue; |
||
2348 | } |
||
2349 | $current_db_escaped = Util::escapeMysqlWildcards($current_db); |
||
2350 | // cannot use array_diff() once, outside of the loop, |
||
2351 | // because the list of databases has special characters |
||
2352 | // already escaped in $foundRows, |
||
2353 | // contrary to the output of SHOW DATABASES |
||
2354 | if (! in_array($current_db_escaped, $foundRows)) { |
||
2355 | $databases[] = $current_db; |
||
2356 | } |
||
2357 | } |
||
2358 | } |
||
2359 | $data['databases'] = $databases; |
||
2360 | } elseif ($type == 'table') { |
||
2361 | $result = @$this->dbi->tryQuery( |
||
2362 | "SHOW TABLES FROM " . Util::backquote($dbname), |
||
2363 | DatabaseInterface::CONNECT_USER, |
||
2364 | DatabaseInterface::QUERY_STORE |
||
2365 | ); |
||
2366 | |||
2367 | $tables = []; |
||
2368 | if ($result) { |
||
2369 | while ($row = $this->dbi->fetchRow($result)) { |
||
2370 | if (! in_array($row[0], $foundRows)) { |
||
2371 | $tables[] = $row[0]; |
||
2372 | } |
||
2373 | } |
||
2374 | $this->dbi->freeResult($result); |
||
2375 | } |
||
2376 | $data['tables'] = $tables; |
||
2377 | } else { // routine |
||
2378 | $routineData = $this->dbi->getRoutines($dbname); |
||
2379 | |||
2380 | $routines = []; |
||
2381 | foreach ($routineData as $routine) { |
||
2382 | if (! in_array($routine['name'], $foundRows)) { |
||
2383 | $routines[] = $routine['name']; |
||
2384 | } |
||
2385 | } |
||
2386 | $data['routines'] = $routines; |
||
2387 | } |
||
2388 | |||
2389 | return $this->template->render('server/privileges/privileges_summary', $data); |
||
2390 | } |
||
2391 | |||
2392 | /** |
||
2393 | * Get HTML for display the users overview |
||
2394 | * (if less than 50 users, display them immediately) |
||
2395 | * |
||
2396 | * @param array $result ran sql query |
||
2397 | * @param array $db_rights user's database rights array |
||
2398 | * @param string $pmaThemeImage a image source link |
||
2399 | * @param string $text_dir text directory |
||
2400 | * |
||
2401 | * @return string HTML snippet |
||
2402 | */ |
||
2403 | public function getUsersOverview($result, array $db_rights, $pmaThemeImage, $text_dir) |
||
2404 | { |
||
2405 | global $is_grantuser, $is_createuser; |
||
2406 | |||
2407 | $cfgRelation = $this->relation->getRelationsParam(); |
||
2408 | |||
2409 | while ($row = $this->dbi->fetchAssoc($result)) { |
||
2410 | $row['privs'] = $this->extractPrivInfo($row, true); |
||
2411 | $db_rights[$row['User']][$row['Host']] = $row; |
||
2412 | } |
||
2413 | $this->dbi->freeResult($result); |
||
2414 | |||
2415 | $user_group_count = 0; |
||
2416 | if ($cfgRelation['menuswork']) { |
||
2417 | $sql_query = 'SELECT * FROM ' . Util::backquote($cfgRelation['db']) |
||
2418 | . '.' . Util::backquote($cfgRelation['users']); |
||
2419 | $result = $this->relation->queryAsControlUser($sql_query, false); |
||
2420 | $group_assignment = []; |
||
2421 | if ($result) { |
||
2422 | while ($row = $this->dbi->fetchAssoc($result)) { |
||
2423 | $group_assignment[$row['username']] = $row['usergroup']; |
||
2424 | } |
||
2425 | } |
||
2426 | $this->dbi->freeResult($result); |
||
2427 | |||
2428 | $user_group_count = $this->getUserGroupCount(); |
||
2429 | } |
||
2430 | |||
2431 | $hosts = []; |
||
2432 | foreach ($db_rights as $user) { |
||
2433 | ksort($user); |
||
2434 | foreach ($user as $host) { |
||
2435 | $check_plugin_query = "SELECT * FROM `mysql`.`user` WHERE " |
||
2436 | . "`User` = '" . $host['User'] . "' AND `Host` = '" |
||
2437 | . $host['Host'] . "'"; |
||
2438 | $res = $this->dbi->fetchSingleRow($check_plugin_query); |
||
2439 | |||
2440 | $hasPassword = false; |
||
2441 | if ((isset($res['authentication_string']) |
||
2442 | && ! empty($res['authentication_string'])) |
||
2443 | || (isset($res['Password']) |
||
2444 | && ! empty($res['Password'])) |
||
2445 | ) { |
||
2446 | $hasPassword = true; |
||
2447 | } |
||
2448 | |||
2449 | $hosts[] = [ |
||
2450 | 'user' => $host['User'], |
||
2451 | 'host' => $host['Host'], |
||
2452 | 'has_password' => $hasPassword, |
||
2453 | 'has_select_priv' => isset($host['Select_priv']), |
||
2454 | 'privileges' => $host['privs'], |
||
2455 | 'group' => $group_assignment[$host['User']] ?? '', |
||
2456 | 'has_grant' => $host['Grant_priv'] == 'Y', |
||
2457 | ]; |
||
2458 | } |
||
2459 | } |
||
2460 | |||
2461 | return $this->template->render('server/privileges/users_overview', [ |
||
2462 | 'menus_work' => $cfgRelation['menuswork'], |
||
2463 | 'user_group_count' => $user_group_count, |
||
2464 | 'pma_theme_image' => $pmaThemeImage, |
||
2465 | 'text_dir' => $text_dir, |
||
2466 | 'initial' => $_GET['initial'] ?? '', |
||
2467 | 'hosts' => $hosts, |
||
2468 | 'is_grantuser' => $is_grantuser, |
||
2469 | 'is_createuser' => $is_createuser, |
||
2470 | ]); |
||
2471 | } |
||
2472 | |||
2473 | /** |
||
2474 | * Get HTML for Displays the initials |
||
2475 | * |
||
2476 | * @param array $array_initials array for all initials, even non A-Z |
||
2477 | * |
||
2478 | * @return string HTML snippet |
||
2479 | */ |
||
2480 | public function getHtmlForInitials(array $array_initials) |
||
2481 | { |
||
2482 | // initialize to false the letters A-Z |
||
2483 | for ($letter_counter = 1; $letter_counter < 27; $letter_counter++) { |
||
2484 | if (! isset($array_initials[mb_chr($letter_counter + 64)])) { |
||
2485 | $array_initials[mb_chr($letter_counter + 64)] = false; |
||
2486 | } |
||
2487 | } |
||
2488 | |||
2489 | $initials = $this->dbi->tryQuery( |
||
2490 | 'SELECT DISTINCT UPPER(LEFT(`User`,1)) FROM `user`' |
||
2491 | . ' ORDER BY UPPER(LEFT(`User`,1)) ASC', |
||
2492 | DatabaseInterface::CONNECT_USER, |
||
2493 | DatabaseInterface::QUERY_STORE |
||
2494 | ); |
||
2495 | if ($initials) { |
||
2496 | while (list($tmp_initial) = $this->dbi->fetchRow($initials)) { |
||
2497 | $array_initials[$tmp_initial] = true; |
||
2498 | } |
||
2499 | } |
||
2500 | |||
2501 | // Display the initials, which can be any characters, not |
||
2502 | // just letters. For letters A-Z, we add the non-used letters |
||
2503 | // as greyed out. |
||
2504 | |||
2505 | uksort($array_initials, "strnatcasecmp"); |
||
2506 | |||
2507 | return $this->template->render('server/privileges/initials_row', [ |
||
2508 | 'array_initials' => $array_initials, |
||
2509 | 'initial' => isset($_GET['initial']) ? $_GET['initial'] : null, |
||
2510 | ]); |
||
2511 | } |
||
2512 | |||
2513 | /** |
||
2514 | * Get the database rights array for Display user overview |
||
2515 | * |
||
2516 | * @return array database rights array |
||
2517 | */ |
||
2518 | public function getDbRightsForUserOverview() |
||
2519 | { |
||
2520 | // we also want users not in table `user` but in other table |
||
2521 | $tables = $this->dbi->fetchResult('SHOW TABLES FROM `mysql`;'); |
||
2522 | |||
2523 | $tablesSearchForUsers = [ |
||
2524 | 'user', |
||
2525 | 'db', |
||
2526 | 'tables_priv', |
||
2527 | 'columns_priv', |
||
2528 | 'procs_priv', |
||
2529 | ]; |
||
2530 | |||
2531 | $db_rights_sqls = []; |
||
2532 | foreach ($tablesSearchForUsers as $table_search_in) { |
||
2533 | if (in_array($table_search_in, $tables)) { |
||
2534 | $db_rights_sqls[] = 'SELECT DISTINCT `User`, `Host` FROM `mysql`.`' |
||
2535 | . $table_search_in . '` ' |
||
2536 | . (isset($_GET['initial']) |
||
2537 | ? $this->rangeOfUsers($_GET['initial']) |
||
2538 | : ''); |
||
2539 | } |
||
2540 | } |
||
2541 | $user_defaults = [ |
||
2542 | 'User' => '', |
||
2543 | 'Host' => '%', |
||
2544 | 'Password' => '?', |
||
2545 | 'Grant_priv' => 'N', |
||
2546 | 'privs' => ['USAGE'], |
||
2547 | ]; |
||
2548 | |||
2549 | // for the rights |
||
2550 | $db_rights = []; |
||
2551 | |||
2552 | $db_rights_sql = '(' . implode(') UNION (', $db_rights_sqls) . ')' |
||
2553 | . ' ORDER BY `User` ASC, `Host` ASC'; |
||
2554 | |||
2555 | $db_rights_result = $this->dbi->query($db_rights_sql); |
||
2556 | |||
2557 | while ($db_rights_row = $this->dbi->fetchAssoc($db_rights_result)) { |
||
2558 | $db_rights_row = array_merge($user_defaults, $db_rights_row); |
||
2559 | $db_rights[$db_rights_row['User']][$db_rights_row['Host']] |
||
2560 | = $db_rights_row; |
||
2561 | } |
||
2562 | $this->dbi->freeResult($db_rights_result); |
||
2563 | ksort($db_rights); |
||
2564 | |||
2565 | return $db_rights; |
||
2566 | } |
||
2567 | |||
2568 | /** |
||
2569 | * Delete user and get message and sql query for delete user in privileges |
||
2570 | * |
||
2571 | * @param array $queries queries |
||
2572 | * |
||
2573 | * @return array Message |
||
2574 | */ |
||
2575 | public function deleteUser(array $queries) |
||
2576 | { |
||
2577 | $sql_query = ''; |
||
2578 | if (empty($queries)) { |
||
2579 | $message = Message::error(__('No users selected for deleting!')); |
||
2580 | } else { |
||
2581 | if ($_POST['mode'] == 3) { |
||
2582 | $queries[] = '# ' . __('Reloading the privileges') . ' …'; |
||
2583 | $queries[] = 'FLUSH PRIVILEGES;'; |
||
2584 | } |
||
2585 | $drop_user_error = ''; |
||
2586 | foreach ($queries as $sql_query) { |
||
2587 | if ($sql_query[0] != '#') { |
||
2588 | if (! $this->dbi->tryQuery($sql_query)) { |
||
2589 | $drop_user_error .= $this->dbi->getError() . "\n"; |
||
2590 | } |
||
2591 | } |
||
2592 | } |
||
2593 | // tracking sets this, causing the deleted db to be shown in navi |
||
2594 | unset($GLOBALS['db']); |
||
2595 | |||
2596 | $sql_query = implode("\n", $queries); |
||
2597 | if (! empty($drop_user_error)) { |
||
2598 | $message = Message::rawError($drop_user_error); |
||
2599 | } else { |
||
2600 | $message = Message::success( |
||
2601 | __('The selected users have been deleted successfully.') |
||
2602 | ); |
||
2603 | } |
||
2604 | } |
||
2605 | return [ |
||
2606 | $sql_query, |
||
2607 | $message, |
||
2608 | ]; |
||
2609 | } |
||
2610 | |||
2611 | /** |
||
2612 | * Update the privileges and return the success or error message |
||
2613 | * |
||
2614 | * @param string $username username |
||
2615 | * @param string $hostname host name |
||
2616 | * @param string $tablename table name |
||
2617 | * @param string $dbname database name |
||
2618 | * @param string $itemType item type |
||
2619 | * |
||
2620 | * @return array success message or error message for update |
||
2621 | */ |
||
2622 | public function updatePrivileges($username, $hostname, $tablename, $dbname, $itemType) |
||
2623 | { |
||
2624 | $db_and_table = $this->wildcardEscapeForGrant($dbname, $tablename); |
||
2625 | |||
2626 | $sql_query0 = 'REVOKE ALL PRIVILEGES ON ' . $itemType . ' ' . $db_and_table |
||
2627 | . ' FROM \'' . $this->dbi->escapeString($username) |
||
2628 | . '\'@\'' . $this->dbi->escapeString($hostname) . '\';'; |
||
2629 | |||
2630 | if (! isset($_POST['Grant_priv']) || $_POST['Grant_priv'] != 'Y') { |
||
2631 | $sql_query1 = 'REVOKE GRANT OPTION ON ' . $itemType . ' ' . $db_and_table |
||
2632 | . ' FROM \'' . $this->dbi->escapeString($username) . '\'@\'' |
||
2633 | . $this->dbi->escapeString($hostname) . '\';'; |
||
2634 | } else { |
||
2635 | $sql_query1 = ''; |
||
2636 | } |
||
2637 | |||
2638 | // Should not do a GRANT USAGE for a table-specific privilege, it |
||
2639 | // causes problems later (cannot revoke it) |
||
2640 | if (! (strlen($tablename) > 0 |
||
2641 | && 'USAGE' == implode('', $this->extractPrivInfo())) |
||
2642 | ) { |
||
2643 | $sql_query2 = 'GRANT ' . implode(', ', $this->extractPrivInfo()) |
||
2644 | . ' ON ' . $itemType . ' ' . $db_and_table |
||
2645 | . ' TO \'' . $this->dbi->escapeString($username) . '\'@\'' |
||
2646 | . $this->dbi->escapeString($hostname) . '\''; |
||
2647 | |||
2648 | if (strlen($dbname) === 0) { |
||
2649 | // add REQUIRE clause |
||
2650 | $sql_query2 .= $this->getRequireClause(); |
||
2651 | } |
||
2652 | |||
2653 | if ((isset($_POST['Grant_priv']) && $_POST['Grant_priv'] == 'Y') |
||
2654 | || (strlen($dbname) === 0 |
||
2655 | && (isset($_POST['max_questions']) || isset($_POST['max_connections']) |
||
2656 | || isset($_POST['max_updates']) |
||
2657 | || isset($_POST['max_user_connections']))) |
||
2658 | ) { |
||
2659 | $sql_query2 .= $this->getWithClauseForAddUserAndUpdatePrivs(); |
||
2660 | } |
||
2661 | $sql_query2 .= ';'; |
||
2662 | } |
||
2663 | if (! $this->dbi->tryQuery($sql_query0)) { |
||
2664 | // This might fail when the executing user does not have |
||
2665 | // ALL PRIVILEGES himself. |
||
2666 | // See https://github.com/phpmyadmin/phpmyadmin/issues/9673 |
||
2667 | $sql_query0 = ''; |
||
2668 | } |
||
2669 | if (! empty($sql_query1) && ! $this->dbi->tryQuery($sql_query1)) { |
||
2670 | // this one may fail, too... |
||
2671 | $sql_query1 = ''; |
||
2672 | } |
||
2673 | if (! empty($sql_query2)) { |
||
2674 | $this->dbi->query($sql_query2); |
||
2675 | } else { |
||
2676 | $sql_query2 = ''; |
||
2677 | } |
||
2678 | $sql_query = $sql_query0 . ' ' . $sql_query1 . ' ' . $sql_query2; |
||
2679 | $message = Message::success(__('You have updated the privileges for %s.')); |
||
2680 | $message->addParam('\'' . $username . '\'@\'' . $hostname . '\''); |
||
2681 | |||
2682 | return [ |
||
2683 | $sql_query, |
||
2684 | $message, |
||
2685 | ]; |
||
2686 | } |
||
2687 | |||
2688 | /** |
||
2689 | * Get List of information: Changes / copies a user |
||
2690 | * |
||
2691 | * @return array |
||
2692 | */ |
||
2693 | public function getDataForChangeOrCopyUser() |
||
2694 | { |
||
2695 | $queries = null; |
||
2696 | $password = null; |
||
2697 | |||
2698 | if (isset($_POST['change_copy'])) { |
||
2699 | $user_host_condition = ' WHERE `User` = ' |
||
2700 | . "'" . $this->dbi->escapeString($_POST['old_username']) . "'" |
||
2701 | . ' AND `Host` = ' |
||
2702 | . "'" . $this->dbi->escapeString($_POST['old_hostname']) . "';"; |
||
2703 | $row = $this->dbi->fetchSingleRow( |
||
2704 | 'SELECT * FROM `mysql`.`user` ' . $user_host_condition |
||
2705 | ); |
||
2706 | if (! $row) { |
||
2707 | $response = Response::getInstance(); |
||
2708 | $response->addHTML( |
||
2709 | Message::notice(__('No user found.'))->getDisplay() |
||
2710 | ); |
||
2711 | unset($_POST['change_copy']); |
||
2712 | } else { |
||
2713 | foreach ($row as $key => $value) { |
||
2714 | $GLOBALS[$key] = $value; |
||
2715 | } |
||
2716 | $serverVersion = $this->dbi->getVersion(); |
||
2717 | // Recent MySQL versions have the field "Password" in mysql.user, |
||
2718 | // so the previous extract creates $row['Password'] but this script |
||
2719 | // uses $password |
||
2720 | if (! isset($row['password']) && isset($row['Password'])) { |
||
2721 | $row['password'] = $row['Password']; |
||
2722 | } |
||
2723 | if (Util::getServerType() == 'MySQL' |
||
2724 | && $serverVersion >= 50606 |
||
2725 | && $serverVersion < 50706 |
||
2726 | && ((isset($row['authentication_string']) |
||
2727 | && empty($row['password'])) |
||
2728 | || (isset($row['plugin']) |
||
2729 | && $row['plugin'] == 'sha256_password')) |
||
2730 | ) { |
||
2731 | $row['password'] = $row['authentication_string']; |
||
2732 | } |
||
2733 | |||
2734 | if (Util::getServerType() == 'MariaDB' |
||
2735 | && $serverVersion >= 50500 |
||
2736 | && isset($row['authentication_string']) |
||
2737 | && empty($row['password']) |
||
2738 | ) { |
||
2739 | $row['password'] = $row['authentication_string']; |
||
2740 | } |
||
2741 | |||
2742 | // Always use 'authentication_string' column |
||
2743 | // for MySQL 5.7.6+ since it does not have |
||
2744 | // the 'password' column at all |
||
2745 | if (in_array(Util::getServerType(), ['MySQL', 'Percona Server']) |
||
2746 | && $serverVersion >= 50706 |
||
2747 | && isset($row['authentication_string']) |
||
2748 | ) { |
||
2749 | $row['password'] = $row['authentication_string']; |
||
2750 | } |
||
2751 | $password = $row['password']; |
||
2752 | $queries = []; |
||
2753 | } |
||
2754 | } |
||
2755 | |||
2756 | return [ |
||
2757 | $queries, |
||
2758 | $password, |
||
2759 | ]; |
||
2760 | } |
||
2761 | |||
2762 | /** |
||
2763 | * Update Data for information: Deletes users |
||
2764 | * |
||
2765 | * @param array $queries queries array |
||
2766 | * |
||
2767 | * @return array |
||
2768 | */ |
||
2769 | public function getDataForDeleteUsers($queries) |
||
2770 | { |
||
2771 | if (isset($_POST['change_copy'])) { |
||
2772 | $selected_usr = [ |
||
2773 | $_POST['old_username'] . '&#27;' . $_POST['old_hostname'], |
||
2774 | ]; |
||
2775 | } else { |
||
2776 | $selected_usr = $_POST['selected_usr']; |
||
2777 | $queries = []; |
||
2778 | } |
||
2779 | |||
2780 | // this happens, was seen in https://reports.phpmyadmin.net/reports/view/17146 |
||
2781 | if (! is_array($selected_usr)) { |
||
2782 | return []; |
||
2783 | } |
||
2784 | |||
2785 | foreach ($selected_usr as $each_user) { |
||
2786 | list($this_user, $this_host) = explode('&#27;', $each_user); |
||
2787 | $queries[] = '# ' |
||
2788 | . sprintf( |
||
2789 | __('Deleting %s'), |
||
2790 | '\'' . $this_user . '\'@\'' . $this_host . '\'' |
||
2791 | ) |
||
2792 | . ' ...'; |
||
2793 | $queries[] = 'DROP USER \'' |
||
2794 | . $this->dbi->escapeString($this_user) |
||
2795 | . '\'@\'' . $this->dbi->escapeString($this_host) . '\';'; |
||
2796 | $this->relationCleanup->user($this_user); |
||
2797 | |||
2798 | if (isset($_POST['drop_users_db'])) { |
||
2799 | $queries[] = 'DROP DATABASE IF EXISTS ' |
||
2800 | . Util::backquote($this_user) . ';'; |
||
2801 | $GLOBALS['reload'] = true; |
||
2802 | } |
||
2803 | } |
||
2804 | return $queries; |
||
2805 | } |
||
2806 | |||
2807 | /** |
||
2808 | * update Message For Reload |
||
2809 | * |
||
2810 | * @return Message|null |
||
2811 | */ |
||
2812 | public function updateMessageForReload(): ?Message |
||
2813 | { |
||
2814 | $message = null; |
||
2815 | if (isset($_GET['flush_privileges'])) { |
||
2816 | $sql_query = 'FLUSH PRIVILEGES;'; |
||
2817 | $this->dbi->query($sql_query); |
||
2818 | $message = Message::success( |
||
2819 | __('The privileges were reloaded successfully.') |
||
2820 | ); |
||
2821 | } |
||
2822 | |||
2823 | if (isset($_GET['validate_username'])) { |
||
2824 | $message = Message::success(); |
||
2825 | } |
||
2826 | |||
2827 | return $message; |
||
2828 | } |
||
2829 | |||
2830 | /** |
||
2831 | * update Data For Queries from queries_for_display |
||
2832 | * |
||
2833 | * @param array $queries queries array |
||
2834 | * @param array|null $queries_for_display queries array for display |
||
2835 | * |
||
2836 | * @return array |
||
2837 | */ |
||
2838 | public function getDataForQueries(array $queries, $queries_for_display) |
||
2839 | { |
||
2840 | $tmp_count = 0; |
||
2841 | foreach ($queries as $sql_query) { |
||
2842 | if ($sql_query[0] != '#') { |
||
2843 | $this->dbi->query($sql_query); |
||
2844 | } |
||
2845 | // when there is a query containing a hidden password, take it |
||
2846 | // instead of the real query sent |
||
2847 | if (isset($queries_for_display[$tmp_count])) { |
||
2848 | $queries[$tmp_count] = $queries_for_display[$tmp_count]; |
||
2849 | } |
||
2850 | $tmp_count++; |
||
2851 | } |
||
2852 | |||
2853 | return $queries; |
||
2854 | } |
||
2855 | |||
2856 | /** |
||
2857 | * update Data for information: Adds a user |
||
2858 | * |
||
2859 | * @param string|array|null $dbname db name |
||
2860 | * @param string $username user name |
||
2861 | * @param string $hostname host name |
||
2862 | * @param string|null $password password |
||
2863 | * @param bool $is_menuwork is_menuwork set? |
||
2864 | * |
||
2865 | * @return array |
||
2866 | */ |
||
2867 | public function addUser( |
||
2868 | $dbname, |
||
2869 | $username, |
||
2870 | $hostname, |
||
2871 | ?string $password, |
||
2872 | $is_menuwork |
||
2873 | ) { |
||
2874 | $_add_user_error = false; |
||
2875 | $message = null; |
||
2876 | $queries = null; |
||
2877 | $queries_for_display = null; |
||
2878 | $sql_query = null; |
||
2879 | |||
2880 | if (! isset($_POST['adduser_submit']) && ! isset($_POST['change_copy'])) { |
||
2881 | return [ |
||
2882 | $message, |
||
2883 | $queries, |
||
2884 | $queries_for_display, |
||
2885 | $sql_query, |
||
2886 | $_add_user_error, |
||
2887 | ]; |
||
2888 | } |
||
2889 | |||
2890 | $sql_query = ''; |
||
2891 | if ($_POST['pred_username'] == 'any') { |
||
2892 | $username = ''; |
||
2893 | } |
||
2894 | switch ($_POST['pred_hostname']) { |
||
2895 | case 'any': |
||
2896 | $hostname = '%'; |
||
2897 | break; |
||
2898 | case 'localhost': |
||
2899 | $hostname = 'localhost'; |
||
2900 | break; |
||
2901 | case 'hosttable': |
||
2902 | $hostname = ''; |
||
2903 | break; |
||
2904 | case 'thishost': |
||
2905 | $_user_name = $this->dbi->fetchValue('SELECT USER()'); |
||
2906 | $hostname = mb_substr( |
||
2907 | $_user_name, |
||
2908 | mb_strrpos($_user_name, '@') + 1 |
||
2909 | ); |
||
2910 | unset($_user_name); |
||
2911 | break; |
||
2912 | } |
||
2913 | $sql = "SELECT '1' FROM `mysql`.`user`" |
||
2914 | . " WHERE `User` = '" . $this->dbi->escapeString($username) . "'" |
||
2915 | . " AND `Host` = '" . $this->dbi->escapeString($hostname) . "';"; |
||
2916 | if ($this->dbi->fetchValue($sql) == 1) { |
||
2917 | $message = Message::error(__('The user %s already exists!')); |
||
2918 | $message->addParam('[em]\'' . $username . '\'@\'' . $hostname . '\'[/em]'); |
||
2919 | $_GET['adduser'] = true; |
||
2920 | $_add_user_error = true; |
||
2921 | |||
2922 | return [ |
||
2923 | $message, |
||
2924 | $queries, |
||
2925 | $queries_for_display, |
||
2926 | $sql_query, |
||
2927 | $_add_user_error, |
||
2928 | ]; |
||
2929 | } |
||
2930 | |||
2931 | list( |
||
2932 | $create_user_real, |
||
2933 | $create_user_show, |
||
2934 | $real_sql_query, |
||
2935 | $sql_query, |
||
2936 | $password_set_real, |
||
2937 | $password_set_show, |
||
2938 | $alter_real_sql_query, |
||
2939 | $alter_sql_query |
||
2940 | ) = $this->getSqlQueriesForDisplayAndAddUser( |
||
2941 | $username, |
||
2942 | $hostname, |
||
2943 | (isset($password) ? $password : '') |
||
2944 | ); |
||
2945 | |||
2946 | if (empty($_POST['change_copy'])) { |
||
2947 | $_error = false; |
||
2948 | |||
2949 | if ($create_user_real !== null) { |
||
2950 | if (! $this->dbi->tryQuery($create_user_real)) { |
||
2951 | $_error = true; |
||
2952 | } |
||
2953 | if (isset($password_set_real, $_POST['authentication_plugin']) && ! empty($password_set_real)) { |
||
2954 | $this->setProperPasswordHashing( |
||
2955 | $_POST['authentication_plugin'] |
||
2956 | ); |
||
2957 | if ($this->dbi->tryQuery($password_set_real)) { |
||
2958 | $sql_query .= $password_set_show; |
||
2959 | } |
||
2960 | } |
||
2961 | $sql_query = $create_user_show . $sql_query; |
||
2962 | } |
||
2963 | |||
2964 | list($sql_query, $message) = $this->addUserAndCreateDatabase( |
||
2965 | $_error, |
||
2966 | $real_sql_query, |
||
2967 | $sql_query, |
||
2968 | $username, |
||
2969 | $hostname, |
||
2970 | $dbname, |
||
2971 | $alter_real_sql_query, |
||
2972 | $alter_sql_query |
||
2973 | ); |
||
2974 | if (! empty($_POST['userGroup']) && $is_menuwork) { |
||
2975 | $this->setUserGroup($GLOBALS['username'], $_POST['userGroup']); |
||
2976 | } |
||
2977 | |||
2978 | return [ |
||
2979 | $message, |
||
2980 | $queries, |
||
2981 | $queries_for_display, |
||
2982 | $sql_query, |
||
2983 | $_add_user_error, |
||
2984 | ]; |
||
2985 | } |
||
2986 | |||
2987 | // Copy the user group while copying a user |
||
2988 | $old_usergroup = |
||
2989 | isset($_POST['old_usergroup']) ? $_POST['old_usergroup'] : null; |
||
2990 | $this->setUserGroup($_POST['username'], $old_usergroup); |
||
2991 | |||
2992 | if ($create_user_real === null) { |
||
2993 | $queries[] = $create_user_real; |
||
2994 | } |
||
2995 | $queries[] = $real_sql_query; |
||
2996 | |||
2997 | if (isset($password_set_real, $_POST['authentication_plugin']) && ! empty($password_set_real)) { |
||
2998 | $this->setProperPasswordHashing( |
||
2999 | $_POST['authentication_plugin'] |
||
3000 | ); |
||
3001 | |||
3002 | $queries[] = $password_set_real; |
||
3003 | } |
||
3004 | // we put the query containing the hidden password in |
||
3005 | // $queries_for_display, at the same position occupied |
||
3006 | // by the real query in $queries |
||
3007 | $tmp_count = count($queries); |
||
3008 | if (isset($create_user_real)) { |
||
3009 | $queries_for_display[$tmp_count - 2] = $create_user_show; |
||
3010 | } |
||
3011 | if (isset($password_set_real) && ! empty($password_set_real)) { |
||
3012 | $queries_for_display[$tmp_count - 3] = $create_user_show; |
||
3013 | $queries_for_display[$tmp_count - 2] = $sql_query; |
||
3014 | $queries_for_display[$tmp_count - 1] = $password_set_show; |
||
3015 | } else { |
||
3016 | $queries_for_display[$tmp_count - 1] = $sql_query; |
||
3017 | } |
||
3018 | |||
3019 | return [ |
||
3020 | $message, |
||
3021 | $queries, |
||
3022 | $queries_for_display, |
||
3023 | $sql_query, |
||
3024 | $_add_user_error, |
||
3025 | ]; |
||
3026 | } |
||
3027 | |||
3028 | /** |
||
3029 | * Sets proper value of `old_passwords` according to |
||
3030 | * the authentication plugin selected |
||
3031 | * |
||
3032 | * @param string $auth_plugin authentication plugin selected |
||
3033 | * |
||
3034 | * @return void |
||
3035 | */ |
||
3036 | public function setProperPasswordHashing($auth_plugin) |
||
3037 | { |
||
3038 | // Set the hashing method used by PASSWORD() |
||
3039 | // to be of type depending upon $authentication_plugin |
||
3040 | if ($auth_plugin == 'sha256_password') { |
||
3041 | $this->dbi->tryQuery('SET `old_passwords` = 2;'); |
||
3042 | } elseif ($auth_plugin == 'mysql_old_password') { |
||
3043 | $this->dbi->tryQuery('SET `old_passwords` = 1;'); |
||
3044 | } else { |
||
3045 | $this->dbi->tryQuery('SET `old_passwords` = 0;'); |
||
3046 | } |
||
3047 | } |
||
3048 | |||
3049 | /** |
||
3050 | * Update DB information: DB, Table, isWildcard |
||
3051 | * |
||
3052 | * @return array |
||
3053 | */ |
||
3054 | public function getDataForDBInfo() |
||
3055 | { |
||
3056 | $username = null; |
||
3057 | $hostname = null; |
||
3058 | $dbname = null; |
||
3059 | $tablename = null; |
||
3060 | $routinename = null; |
||
3061 | $dbname_is_wildcard = null; |
||
3062 | |||
3063 | if (isset($_REQUEST['username'])) { |
||
3064 | $username = $_REQUEST['username']; |
||
3065 | } |
||
3066 | if (isset($_REQUEST['hostname'])) { |
||
3067 | $hostname = $_REQUEST['hostname']; |
||
3068 | } |
||
3069 | /** |
||
3070 | * Checks if a dropdown box has been used for selecting a database / table |
||
3071 | */ |
||
3072 | if (Core::isValid($_POST['pred_tablename'])) { |
||
3073 | $tablename = $_POST['pred_tablename']; |
||
3074 | } elseif (Core::isValid($_REQUEST['tablename'])) { |
||
3075 | $tablename = $_REQUEST['tablename']; |
||
3076 | } else { |
||
3077 | unset($tablename); |
||
3078 | } |
||
3079 | |||
3080 | if (Core::isValid($_POST['pred_routinename'])) { |
||
3081 | $routinename = $_POST['pred_routinename']; |
||
3082 | } elseif (Core::isValid($_REQUEST['routinename'])) { |
||
3083 | $routinename = $_REQUEST['routinename']; |
||
3084 | } else { |
||
3085 | unset($routinename); |
||
3086 | } |
||
3087 | |||
3088 | if (isset($_POST['pred_dbname'])) { |
||
3089 | $is_valid_pred_dbname = true; |
||
3090 | foreach ($_POST['pred_dbname'] as $key => $db_name) { |
||
3091 | if (! Core::isValid($db_name)) { |
||
3092 | $is_valid_pred_dbname = false; |
||
3093 | break; |
||
3094 | } |
||
3095 | } |
||
3096 | } |
||
3097 | |||
3098 | if (isset($_REQUEST['dbname'])) { |
||
3099 | $is_valid_dbname = true; |
||
3100 | if (is_array($_REQUEST['dbname'])) { |
||
3101 | foreach ($_REQUEST['dbname'] as $key => $db_name) { |
||
3102 | if (! Core::isValid($db_name)) { |
||
3103 | $is_valid_dbname = false; |
||
3104 | break; |
||
3105 | } |
||
3106 | } |
||
3107 | } else { |
||
3108 | if (! Core::isValid($_REQUEST['dbname'])) { |
||
3109 | $is_valid_dbname = false; |
||
3110 | } |
||
3111 | } |
||
3112 | } |
||
3113 | |||
3114 | if (isset($is_valid_pred_dbname) && $is_valid_pred_dbname) { |
||
3115 | $dbname = $_POST['pred_dbname']; |
||
3116 | // If dbname contains only one database. |
||
3117 | if (count($dbname) === 1) { |
||
3118 | $dbname = $dbname[0]; |
||
3119 | } |
||
3120 | } elseif (isset($is_valid_dbname) && $is_valid_dbname) { |
||
3121 | $dbname = $_REQUEST['dbname']; |
||
3122 | } else { |
||
3123 | unset($dbname, $tablename); |
||
3124 | } |
||
3125 | |||
3126 | if (isset($dbname)) { |
||
3127 | if (is_array($dbname)) { |
||
3128 | $db_and_table = $dbname; |
||
3129 | foreach ($db_and_table as $key => $db_name) { |
||
3130 | $db_and_table[$key] .= '.'; |
||
3131 | } |
||
3132 | } else { |
||
3133 | $unescaped_db = Util::unescapeMysqlWildcards($dbname); |
||
3134 | $db_and_table = Util::backquote($unescaped_db) . '.'; |
||
3135 | } |
||
3136 | if (isset($tablename)) { |
||
3137 | $db_and_table .= Util::backquote($tablename); |
||
3138 | } else { |
||
3139 | if (is_array($db_and_table)) { |
||
3140 | foreach ($db_and_table as $key => $db_name) { |
||
3141 | $db_and_table[$key] .= '*'; |
||
3142 | } |
||
3143 | } else { |
||
3144 | $db_and_table .= '*'; |
||
3145 | } |
||
3146 | } |
||
3147 | } else { |
||
3148 | $db_and_table = '*.*'; |
||
3149 | } |
||
3150 | |||
3151 | // check if given $dbname is a wildcard or not |
||
3152 | if (isset($dbname)) { |
||
3153 | //if (preg_match('/\\\\(?:_|%)/i', $dbname)) { |
||
3154 | if (! is_array($dbname) && preg_match('/(?<!\\\\)(?:_|%)/', $dbname)) { |
||
3155 | $dbname_is_wildcard = true; |
||
3156 | } else { |
||
3157 | $dbname_is_wildcard = false; |
||
3158 | } |
||
3159 | } |
||
3160 | |||
3161 | return [ |
||
3162 | $username, |
||
3163 | $hostname, |
||
3164 | isset($dbname) ? $dbname : null, |
||
3165 | isset($tablename) ? $tablename : null, |
||
3166 | isset($routinename) ? $routinename : null, |
||
3167 | $db_and_table, |
||
3168 | $dbname_is_wildcard, |
||
3169 | ]; |
||
3170 | } |
||
3171 | |||
3172 | /** |
||
3173 | * Get title and textarea for export user definition in Privileges |
||
3174 | * |
||
3175 | * @param string $username username |
||
3176 | * @param string $hostname host name |
||
3177 | * |
||
3178 | * @return array ($title, $export) |
||
3179 | */ |
||
3180 | public function getListForExportUserDefinition($username, $hostname) |
||
3181 | { |
||
3182 | $export = '<textarea class="export" cols="60" rows="15">'; |
||
3183 | |||
3184 | if (isset($_POST['selected_usr'])) { |
||
3185 | // export privileges for selected users |
||
3186 | $title = __('Privileges'); |
||
3187 | |||
3188 | //For removing duplicate entries of users |
||
3189 | $_POST['selected_usr'] = array_unique($_POST['selected_usr']); |
||
3190 | |||
3191 | foreach ($_POST['selected_usr'] as $export_user) { |
||
3192 | $export_username = mb_substr( |
||
3193 | $export_user, |
||
3194 | 0, |
||
3195 | mb_strpos($export_user, '&') |
||
3196 | ); |
||
3197 | $export_hostname = mb_substr( |
||
3198 | $export_user, |
||
3199 | mb_strrpos($export_user, ';') + 1 |
||
3200 | ); |
||
3201 | $export .= '# ' |
||
3202 | . sprintf( |
||
3203 | __('Privileges for %s'), |
||
3204 | '`' . htmlspecialchars($export_username) |
||
3205 | . '`@`' . htmlspecialchars($export_hostname) . '`' |
||
3206 | ) |
||
3207 | . "\n\n"; |
||
3208 | $export .= $this->getGrants($export_username, $export_hostname) . "\n"; |
||
3209 | } |
||
3210 | } else { |
||
3211 | // export privileges for a single user |
||
3212 | $title = __('User') . ' `' . htmlspecialchars($username) |
||
3213 | . '`@`' . htmlspecialchars($hostname) . '`'; |
||
3214 | $export .= $this->getGrants($username, $hostname); |
||
3215 | } |
||
3216 | // remove trailing whitespace |
||
3217 | $export = trim($export); |
||
3218 | |||
3219 | $export .= '</textarea>'; |
||
3220 | |||
3221 | return [ |
||
3222 | $title, |
||
3223 | $export, |
||
3224 | ]; |
||
3225 | } |
||
3226 | |||
3227 | /** |
||
3228 | * Get HTML for display Add userfieldset |
||
3229 | * |
||
3230 | * @param string $db the database |
||
3231 | * @param string $table the table name |
||
3232 | * |
||
3233 | * @return string html output |
||
3234 | */ |
||
3235 | public function getAddUserHtmlFieldset($db = '', $table = '') |
||
3236 | { |
||
3237 | if (! $GLOBALS['is_createuser']) { |
||
3238 | return ''; |
||
3239 | } |
||
3240 | $rel_params = []; |
||
3241 | $url_params = [ |
||
3242 | 'adduser' => 1, |
||
3243 | ]; |
||
3244 | if (! empty($db)) { |
||
3245 | $url_params['dbname'] |
||
3246 | = $rel_params['checkprivsdb'] |
||
3247 | = $db; |
||
3248 | } |
||
3249 | if (! empty($table)) { |
||
3250 | $url_params['tablename'] |
||
3251 | = $rel_params['checkprivstable'] |
||
3252 | = $table; |
||
3253 | } |
||
3254 | |||
3255 | return $this->template->render('server/privileges/add_user_fieldset', [ |
||
3256 | 'url_params' => $url_params, |
||
3257 | 'rel_params' => $rel_params, |
||
3258 | ]); |
||
3259 | } |
||
3260 | |||
3261 | /** |
||
3262 | * Get HTML snippet for display user overview page |
||
3263 | * |
||
3264 | * @param string $pmaThemeImage a image source link |
||
3265 | * @param string $text_dir text directory |
||
3266 | * |
||
3267 | * @return string |
||
3268 | */ |
||
3269 | public function getHtmlForUserOverview($pmaThemeImage, $text_dir) |
||
3433 | ]); |
||
3434 | } |
||
3435 | |||
3436 | /** |
||
3437 | * Get HTML snippet for display user properties |
||
3438 | * |
||
3439 | * @param boolean $dbname_is_wildcard whether database name is wildcard or not |
||
3440 | * @param string $url_dbname url database name that urlencode() string |
||
3441 | * @param string $username username |
||
3442 | * @param string $hostname host name |
||
3443 | * @param string|array $dbname database name |
||
3444 | * @param string $tablename table name |
||
3445 | * |
||
3446 | * @return string |
||
3447 | */ |
||
3448 | public function getHtmlForUserProperties( |
||
3551 | ]); |
||
3552 | } |
||
3553 | |||
3554 | /** |
||
3555 | * Get queries for Table privileges to change or copy user |
||
3556 | * |
||
3557 | * @param string $user_host_condition user host condition to |
||
3558 | * select relevant table privileges |
||
3559 | * @param array $queries queries array |
||
3560 | * @param string $username username |
||
3561 | * @param string $hostname host name |
||
3562 | * |
||
3563 | * @return array |
||
3564 | */ |
||
3565 | public function getTablePrivsQueriesForChangeOrCopyUser( |
||
3566 | $user_host_condition, |
||
3567 | array $queries, |
||
3568 | $username, |
||
3569 | $hostname |
||
3570 | ) { |
||
3571 | $res = $this->dbi->query( |
||
3572 | 'SELECT `Db`, `Table_name`, `Table_priv` FROM `mysql`.`tables_priv`' |
||
3573 | . $user_host_condition, |
||
3574 | DatabaseInterface::CONNECT_USER, |
||
3575 | DatabaseInterface::QUERY_STORE |
||
3576 | ); |
||
3577 | while ($row = $this->dbi->fetchAssoc($res)) { |
||
3578 | $res2 = $this->dbi->query( |
||
3579 | 'SELECT `Column_name`, `Column_priv`' |
||
3580 | . ' FROM `mysql`.`columns_priv`' |
||
3581 | . ' WHERE `User`' |
||
3582 | . ' = \'' . $this->dbi->escapeString($_POST['old_username']) . "'" |
||
3583 | . ' AND `Host`' |
||
3584 | . ' = \'' . $this->dbi->escapeString($_POST['old_username']) . '\'' |
||
3585 | . ' AND `Db`' |
||
3586 | . ' = \'' . $this->dbi->escapeString($row['Db']) . "'" |
||
3587 | . ' AND `Table_name`' |
||
3588 | . ' = \'' . $this->dbi->escapeString($row['Table_name']) . "'" |
||
3589 | . ';', |
||
3590 | DatabaseInterface::CONNECT_USER, |
||
3591 | DatabaseInterface::QUERY_STORE |
||
3592 | ); |
||
3593 | |||
3594 | $tmp_privs1 = $this->extractPrivInfo($row); |
||
3595 | $tmp_privs2 = [ |
||
3596 | 'Select' => [], |
||
3597 | 'Insert' => [], |
||
3598 | 'Update' => [], |
||
3599 | 'References' => [], |
||
3600 | ]; |
||
3601 | |||
3602 | while ($row2 = $this->dbi->fetchAssoc($res2)) { |
||
3603 | $tmp_array = explode(',', $row2['Column_priv']); |
||
3604 | if (in_array('Select', $tmp_array)) { |
||
3605 | $tmp_privs2['Select'][] = $row2['Column_name']; |
||
3606 | } |
||
3607 | if (in_array('Insert', $tmp_array)) { |
||
3608 | $tmp_privs2['Insert'][] = $row2['Column_name']; |
||
3609 | } |
||
3610 | if (in_array('Update', $tmp_array)) { |
||
3611 | $tmp_privs2['Update'][] = $row2['Column_name']; |
||
3612 | } |
||
3613 | if (in_array('References', $tmp_array)) { |
||
3614 | $tmp_privs2['References'][] = $row2['Column_name']; |
||
3615 | } |
||
3616 | } |
||
3617 | if (count($tmp_privs2['Select']) > 0 && ! in_array('SELECT', $tmp_privs1)) { |
||
3618 | $tmp_privs1[] = 'SELECT (`' . implode('`, `', $tmp_privs2['Select']) . '`)'; |
||
3619 | } |
||
3620 | if (count($tmp_privs2['Insert']) > 0 && ! in_array('INSERT', $tmp_privs1)) { |
||
3621 | $tmp_privs1[] = 'INSERT (`' . implode('`, `', $tmp_privs2['Insert']) . '`)'; |
||
3622 | } |
||
3623 | if (count($tmp_privs2['Update']) > 0 && ! in_array('UPDATE', $tmp_privs1)) { |
||
3624 | $tmp_privs1[] = 'UPDATE (`' . implode('`, `', $tmp_privs2['Update']) . '`)'; |
||
3625 | } |
||
3626 | if (count($tmp_privs2['References']) > 0 |
||
3627 | && ! in_array('REFERENCES', $tmp_privs1) |
||
3628 | ) { |
||
3629 | $tmp_privs1[] |
||
3630 | = 'REFERENCES (`' . implode('`, `', $tmp_privs2['References']) . '`)'; |
||
3631 | } |
||
3632 | |||
3633 | $queries[] = 'GRANT ' . implode(', ', $tmp_privs1) |
||
3634 | . ' ON ' . Util::backquote($row['Db']) . '.' |
||
3635 | . Util::backquote($row['Table_name']) |
||
3636 | . ' TO \'' . $this->dbi->escapeString($username) |
||
3637 | . '\'@\'' . $this->dbi->escapeString($hostname) . '\'' |
||
3638 | . (in_array('Grant', explode(',', $row['Table_priv'])) |
||
3639 | ? ' WITH GRANT OPTION;' |
||
3640 | : ';'); |
||
3641 | } |
||
3642 | return $queries; |
||
3643 | } |
||
3644 | |||
3645 | /** |
||
3646 | * Get queries for database specific privileges for change or copy user |
||
3647 | * |
||
3648 | * @param array $queries queries array with string |
||
3649 | * @param string $username username |
||
3650 | * @param string $hostname host name |
||
3651 | * |
||
3652 | * @return array |
||
3653 | */ |
||
3654 | public function getDbSpecificPrivsQueriesForChangeOrCopyUser( |
||
3655 | array $queries, |
||
3656 | $username, |
||
3657 | $hostname |
||
3658 | ) { |
||
3659 | $user_host_condition = ' WHERE `User`' |
||
3660 | . ' = \'' . $this->dbi->escapeString($_POST['old_username']) . "'" |
||
3661 | . ' AND `Host`' |
||
3662 | . ' = \'' . $this->dbi->escapeString($_POST['old_hostname']) . '\';'; |
||
3663 | |||
3664 | $res = $this->dbi->query( |
||
3665 | 'SELECT * FROM `mysql`.`db`' . $user_host_condition |
||
3666 | ); |
||
3667 | |||
3668 | while ($row = $this->dbi->fetchAssoc($res)) { |
||
3669 | $queries[] = 'GRANT ' . implode(', ', $this->extractPrivInfo($row)) |
||
3670 | . ' ON ' . Util::backquote($row['Db']) . '.*' |
||
3671 | . ' TO \'' . $this->dbi->escapeString($username) |
||
3672 | . '\'@\'' . $this->dbi->escapeString($hostname) . '\'' |
||
3673 | . ($row['Grant_priv'] == 'Y' ? ' WITH GRANT OPTION;' : ';'); |
||
3674 | } |
||
3675 | $this->dbi->freeResult($res); |
||
3676 | |||
3677 | $queries = $this->getTablePrivsQueriesForChangeOrCopyUser( |
||
3678 | $user_host_condition, |
||
3679 | $queries, |
||
3680 | $username, |
||
3681 | $hostname |
||
3682 | ); |
||
3683 | |||
3684 | return $queries; |
||
3685 | } |
||
3686 | |||
3687 | /** |
||
3688 | * Prepares queries for adding users and |
||
3689 | * also create database and return query and message |
||
3690 | * |
||
3691 | * @param boolean $_error whether user create or not |
||
3692 | * @param string $real_sql_query SQL query for add a user |
||
3693 | * @param string $sql_query SQL query to be displayed |
||
3694 | * @param string $username username |
||
3695 | * @param string $hostname host name |
||
3696 | * @param string $dbname database name |
||
3697 | * @param string $alter_real_sql_query SQL query for ALTER USER |
||
3698 | * @param string $alter_sql_query SQL query for ALTER USER to be displayed |
||
3699 | * |
||
3700 | * @return array, $message |
||
3701 | */ |
||
3702 | public function addUserAndCreateDatabase( |
||
3703 | $_error, |
||
3704 | $real_sql_query, |
||
3705 | $sql_query, |
||
3706 | $username, |
||
3707 | $hostname, |
||
3708 | $dbname, |
||
3709 | $alter_real_sql_query, |
||
3710 | $alter_sql_query |
||
3711 | ) { |
||
3712 | if ($_error || (! empty($real_sql_query) |
||
3713 | && ! $this->dbi->tryQuery($real_sql_query)) |
||
3714 | ) { |
||
3715 | $_POST['createdb-1'] = $_POST['createdb-2'] |
||
3716 | = $_POST['createdb-3'] = null; |
||
3717 | $message = Message::rawError($this->dbi->getError()); |
||
3718 | } elseif ($alter_real_sql_query !== '' && ! $this->dbi->tryQuery($alter_real_sql_query)) { |
||
3719 | $_POST['createdb-1'] = $_POST['createdb-2'] |
||
3720 | = $_POST['createdb-3'] = null; |
||
3721 | $message = Message::rawError($this->dbi->getError()); |
||
3722 | } else { |
||
3723 | $sql_query .= $alter_sql_query; |
||
3724 | $message = Message::success(__('You have added a new user.')); |
||
3725 | } |
||
3726 | |||
3727 | if (isset($_POST['createdb-1'])) { |
||
3728 | // Create database with same name and grant all privileges |
||
3729 | $q = 'CREATE DATABASE IF NOT EXISTS ' |
||
3730 | . Util::backquote( |
||
3731 | $this->dbi->escapeString($username) |
||
3732 | ) . ';'; |
||
3733 | $sql_query .= $q; |
||
3734 | if (! $this->dbi->tryQuery($q)) { |
||
3735 | $message = Message::rawError($this->dbi->getError()); |
||
3736 | } |
||
3737 | |||
3738 | /** |
||
3739 | * Reload the navigation |
||
3740 | */ |
||
3741 | $GLOBALS['reload'] = true; |
||
3742 | $GLOBALS['db'] = $username; |
||
3743 | |||
3744 | $q = 'GRANT ALL PRIVILEGES ON ' |
||
3745 | . Util::backquote( |
||
3746 | Util::escapeMysqlWildcards( |
||
3747 | $this->dbi->escapeString($username) |
||
3748 | ) |
||
3749 | ) . '.* TO \'' |
||
3750 | . $this->dbi->escapeString($username) |
||
3751 | . '\'@\'' . $this->dbi->escapeString($hostname) . '\';'; |
||
3752 | $sql_query .= $q; |
||
3753 | if (! $this->dbi->tryQuery($q)) { |
||
3754 | $message = Message::rawError($this->dbi->getError()); |
||
3755 | } |
||
3756 | } |
||
3757 | |||
3758 | if (isset($_POST['createdb-2'])) { |
||
3759 | // Grant all privileges on wildcard name (username\_%) |
||
3760 | $q = 'GRANT ALL PRIVILEGES ON ' |
||
3761 | . Util::backquote( |
||
3762 | Util::escapeMysqlWildcards( |
||
3763 | $this->dbi->escapeString($username) |
||
3764 | ) . '\_%' |
||
3765 | ) . '.* TO \'' |
||
3766 | . $this->dbi->escapeString($username) |
||
3767 | . '\'@\'' . $this->dbi->escapeString($hostname) . '\';'; |
||
3768 | $sql_query .= $q; |
||
3769 | if (! $this->dbi->tryQuery($q)) { |
||
3770 | $message = Message::rawError($this->dbi->getError()); |
||
3771 | } |
||
3772 | } |
||
3773 | |||
3774 | if (isset($_POST['createdb-3'])) { |
||
3775 | // Grant all privileges on the specified database to the new user |
||
3776 | $q = 'GRANT ALL PRIVILEGES ON ' |
||
3777 | . Util::backquote( |
||
3778 | $this->dbi->escapeString($dbname) |
||
3779 | ) . '.* TO \'' |
||
3780 | . $this->dbi->escapeString($username) |
||
3781 | . '\'@\'' . $this->dbi->escapeString($hostname) . '\';'; |
||
3782 | $sql_query .= $q; |
||
3783 | if (! $this->dbi->tryQuery($q)) { |
||
3784 | $message = Message::rawError($this->dbi->getError()); |
||
3785 | } |
||
3786 | } |
||
3787 | return [ |
||
3788 | $sql_query, |
||
3789 | $message, |
||
3790 | ]; |
||
3791 | } |
||
3792 | |||
3793 | /** |
||
3794 | * Get the hashed string for password |
||
3795 | * |
||
3796 | * @param string $password password |
||
3797 | * |
||
3798 | * @return string |
||
3799 | */ |
||
3800 | public function getHashedPassword($password) |
||
3801 | { |
||
3802 | $password = $this->dbi->escapeString($password); |
||
3803 | $result = $this->dbi->fetchSingleRow( |
||
3804 | "SELECT PASSWORD('" . $password . "') AS `password`;" |
||
3805 | ); |
||
3806 | |||
3807 | return $result['password']; |
||
3808 | } |
||
3809 | |||
3810 | /** |
||
3811 | * Check if MariaDB's 'simple_password_check' |
||
3812 | * OR 'cracklib_password_check' is ACTIVE |
||
3813 | * |
||
3814 | * @return boolean if atleast one of the plugins is ACTIVE |
||
3815 | */ |
||
3816 | public function checkIfMariaDBPwdCheckPluginActive() |
||
3817 | { |
||
3818 | $serverVersion = $this->dbi->getVersion(); |
||
3819 | if (! (Util::getServerType() == 'MariaDB' && $serverVersion >= 100002)) { |
||
3820 | return false; |
||
3821 | } |
||
3822 | |||
3823 | $result = $this->dbi->tryQuery( |
||
3824 | 'SHOW PLUGINS SONAME LIKE \'%_password_check%\'' |
||
3825 | ); |
||
3826 | |||
3827 | /* Plugins are not working, for example directory does not exists */ |
||
3828 | if ($result === false) { |
||
3829 | return false; |
||
3830 | } |
||
3831 | |||
3832 | while ($row = $this->dbi->fetchAssoc($result)) { |
||
3833 | if ($row['Status'] === 'ACTIVE') { |
||
3834 | return true; |
||
3835 | } |
||
3836 | } |
||
3837 | |||
3838 | return false; |
||
3839 | } |
||
3840 | |||
3841 | |||
3842 | /** |
||
3843 | * Get SQL queries for Display and Add user |
||
3844 | * |
||
3845 | * @param string $username username |
||
3846 | * @param string $hostname host name |
||
3847 | * @param string $password password |
||
3848 | * |
||
3849 | * @return array ($create_user_real, $create_user_show, $real_sql_query, $sql_query |
||
3850 | * $password_set_real, $password_set_show, $alter_real_sql_query, $alter_sql_query) |
||
3851 | */ |
||
3852 | public function getSqlQueriesForDisplayAndAddUser($username, $hostname, $password) |
||
4088 | ]; |
||
4089 | } |
||
4090 | |||
4091 | /** |
||
4092 | * Returns the type ('PROCEDURE' or 'FUNCTION') of the routine |
||
4093 | * |
||
4094 | * @param string $dbname database |
||
4095 | * @param string $routineName routine |
||
4096 | * |
||
4097 | * @return string type |
||
4098 | */ |
||
4099 | public function getRoutineType($dbname, $routineName) |
||
4100 | { |
||
4101 | $routineData = $this->dbi->getRoutines($dbname); |
||
4102 | |||
4103 | foreach ($routineData as $routine) { |
||
4104 | if ($routine['name'] === $routineName) { |
||
4105 | return $routine['type']; |
||
4106 | } |
||
4107 | } |
||
4108 | return ''; |
||
4109 | } |
||
4110 | |||
4111 | /** |
||
4112 | * @param string $username User name |
||
4113 | * @param string $hostname Host name |
||
4114 | * @param string $database Database name |
||
4115 | * @param string $routine Routine name |
||
4116 | * |
||
4117 | * @return array |
||
4118 | */ |
||
4119 | private function getRoutinePrivileges( |
||
4137 | } |
||
4138 | } |
||
4139 |
In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.