Total Complexity | 575 |
Total Lines | 4269 |
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 list of privileges and list of compared privileges as strings |
||
1369 | * and return a array that contains both strings |
||
1370 | * |
||
1371 | * @return array $list_of_privileges, $list_of_compared_privileges |
||
1372 | */ |
||
1373 | public function getListOfPrivilegesAndComparedPrivileges() |
||
1422 | ]; |
||
1423 | } |
||
1424 | |||
1425 | /** |
||
1426 | * Get the HTML for routine based privileges |
||
1427 | * |
||
1428 | * @param string $db database name |
||
1429 | * @param string $index_checkbox starting index for rows to be added |
||
1430 | * |
||
1431 | * @return string |
||
1432 | */ |
||
1433 | public function getHtmlTableBodyForSpecificDbRoutinePrivs($db, $index_checkbox) |
||
1434 | { |
||
1435 | global $is_grantuser; |
||
1436 | |||
1437 | $sql_query = 'SELECT * FROM `mysql`.`procs_priv` WHERE Db = \'' . $this->dbi->escapeString($db) . '\';'; |
||
1438 | $res = $this->dbi->query($sql_query); |
||
1439 | |||
1440 | $routines = []; |
||
1441 | while ($row = $this->dbi->fetchAssoc($res)) { |
||
1442 | $routines[] = [ |
||
1443 | 'user' => $row['User'], |
||
1444 | 'host' => $row['Host'], |
||
1445 | 'name' => $row['Routine_name'], |
||
1446 | 'database' => isset($row['Db']) && $row['Db'] != '*' ? $row['Db'] : '', |
||
1447 | 'table' => isset($row['Table_name']) && $row['Table_name'] != '*' ? $row['Table_name'] : '', |
||
1448 | ]; |
||
1449 | } |
||
1450 | |||
1451 | return $this->template->render('server/privileges/routine', [ |
||
1452 | 'routines' => $routines, |
||
1453 | 'is_grantuser' => $is_grantuser, |
||
1454 | 'index' => $index_checkbox, |
||
1455 | ]); |
||
1456 | } |
||
1457 | |||
1458 | /** |
||
1459 | * Get the HTML for user form and check the privileges for a particular database. |
||
1460 | * |
||
1461 | * @param string $db database name |
||
1462 | * |
||
1463 | * @return string |
||
1464 | */ |
||
1465 | public function getHtmlForSpecificDbPrivileges(string $db): string |
||
1466 | { |
||
1467 | global $cfg, $pmaThemeImage, $text_dir, $is_createuser; |
||
1468 | |||
1469 | $scriptName = Util::getScriptNameForOption( |
||
1470 | $cfg['DefaultTabDatabase'], |
||
1471 | 'database' |
||
1472 | ); |
||
1473 | |||
1474 | $tableBody = ''; |
||
1475 | if ($this->dbi->isSuperuser()) { |
||
1476 | $privMap = $this->getPrivMap($db); |
||
1477 | $tableBody = $this->getHtmlTableBodyForSpecificDbOrTablePrivs($privMap, $db); |
||
1478 | } |
||
1479 | |||
1480 | $response = Response::getInstance(); |
||
1481 | if ($response->isAjax() === true |
||
1482 | && empty($_REQUEST['ajax_page_request']) |
||
1483 | ) { |
||
1484 | $message = Message::success(__('User has been added.')); |
||
1485 | $response->addJSON('message', $message); |
||
1486 | exit; |
||
1487 | } |
||
1488 | |||
1489 | return $this->template->render('server/privileges/database', [ |
||
1490 | 'is_superuser' => $this->dbi->isSuperuser(), |
||
1491 | 'db' => $db, |
||
1492 | 'database_url' => $scriptName, |
||
1493 | 'pma_theme_image' => $pmaThemeImage, |
||
1494 | 'text_dir' => $text_dir, |
||
1495 | 'table_body' => $tableBody, |
||
1496 | 'is_createuser' => $is_createuser, |
||
1497 | ]); |
||
1498 | } |
||
1499 | |||
1500 | /** |
||
1501 | * Get the HTML for user form and check the privileges for a particular table. |
||
1502 | * |
||
1503 | * @param string $db database name |
||
1504 | * @param string $table table name |
||
1505 | * |
||
1506 | * @return string |
||
1507 | */ |
||
1508 | public function getHtmlForSpecificTablePrivileges(string $db, string $table): string |
||
1509 | { |
||
1510 | global $cfg, $pmaThemeImage, $text_dir, $is_createuser; |
||
1511 | |||
1512 | $scriptName = Util::getScriptNameForOption( |
||
1513 | $cfg['DefaultTabTable'], |
||
1514 | 'table' |
||
1515 | ); |
||
1516 | |||
1517 | $tableBody = ''; |
||
1518 | if ($this->dbi->isSuperuser()) { |
||
1519 | $privMap = $this->getPrivMap($db); |
||
1520 | $sql_query = "SELECT `User`, `Host`, `Db`," |
||
1521 | . " 't' AS `Type`, `Table_name`, `Table_priv`" |
||
1522 | . " FROM `mysql`.`tables_priv`" |
||
1523 | . " WHERE '" . $this->dbi->escapeString($db) . "' LIKE `Db`" |
||
1524 | . " AND '" . $this->dbi->escapeString($table) . "' LIKE `Table_name`" |
||
1525 | . " AND NOT (`Table_priv` = '' AND Column_priv = '')" |
||
1526 | . " ORDER BY `User` ASC, `Host` ASC, `Db` ASC, `Table_priv` ASC;"; |
||
1527 | $res = $this->dbi->query($sql_query); |
||
1528 | $this->mergePrivMapFromResult($privMap, $res); |
||
1529 | |||
1530 | $tableBody = $this->getHtmlTableBodyForSpecificDbOrTablePrivs($privMap, $db); |
||
1531 | } |
||
1532 | |||
1533 | return $this->template->render('server/privileges/table', [ |
||
1534 | 'db' => $db, |
||
1535 | 'table' => $table, |
||
1536 | 'is_superuser' => $this->dbi->isSuperuser(), |
||
1537 | 'table_url' => $scriptName, |
||
1538 | 'pma_theme_image' => $pmaThemeImage, |
||
1539 | 'text_dir' => $text_dir, |
||
1540 | 'table_body' => $tableBody, |
||
1541 | 'is_createuser' => $is_createuser, |
||
1542 | ]); |
||
1543 | } |
||
1544 | |||
1545 | /** |
||
1546 | * gets privilege map |
||
1547 | * |
||
1548 | * @param string $db the database |
||
1549 | * |
||
1550 | * @return array the privilege map |
||
1551 | */ |
||
1552 | public function getPrivMap($db) |
||
1553 | { |
||
1554 | list($listOfPrivs, $listOfComparedPrivs) |
||
1555 | = $this->getListOfPrivilegesAndComparedPrivileges(); |
||
1556 | $sql_query |
||
1557 | = "(" |
||
1558 | . " SELECT " . $listOfPrivs . ", '*' AS `Db`, 'g' AS `Type`" |
||
1559 | . " FROM `mysql`.`user`" |
||
1560 | . " WHERE NOT (" . $listOfComparedPrivs . ")" |
||
1561 | . ")" |
||
1562 | . " UNION " |
||
1563 | . "(" |
||
1564 | . " SELECT " . $listOfPrivs . ", `Db`, 'd' AS `Type`" |
||
1565 | . " FROM `mysql`.`db`" |
||
1566 | . " WHERE '" . $this->dbi->escapeString($db) . "' LIKE `Db`" |
||
1567 | . " AND NOT (" . $listOfComparedPrivs . ")" |
||
1568 | . ")" |
||
1569 | . " ORDER BY `User` ASC, `Host` ASC, `Db` ASC;"; |
||
1570 | $res = $this->dbi->query($sql_query); |
||
1571 | $privMap = []; |
||
1572 | $this->mergePrivMapFromResult($privMap, $res); |
||
1573 | return $privMap; |
||
1574 | } |
||
1575 | |||
1576 | /** |
||
1577 | * merge privilege map and rows from resultset |
||
1578 | * |
||
1579 | * @param array $privMap the privilege map reference |
||
1580 | * @param object $result the resultset of query |
||
1581 | * |
||
1582 | * @return void |
||
1583 | */ |
||
1584 | public function mergePrivMapFromResult(array &$privMap, $result) |
||
1585 | { |
||
1586 | while ($row = $this->dbi->fetchAssoc($result)) { |
||
1587 | $user = $row['User']; |
||
1588 | $host = $row['Host']; |
||
1589 | if (! isset($privMap[$user])) { |
||
1590 | $privMap[$user] = []; |
||
1591 | } |
||
1592 | if (! isset($privMap[$user][$host])) { |
||
1593 | $privMap[$user][$host] = []; |
||
1594 | } |
||
1595 | $privMap[$user][$host][] = $row; |
||
1596 | } |
||
1597 | } |
||
1598 | |||
1599 | /** |
||
1600 | * Get HTML error for View Users form |
||
1601 | * For non superusers such as grant/create users |
||
1602 | * |
||
1603 | * @return string |
||
1604 | */ |
||
1605 | public function getHtmlForViewUsersError() |
||
1606 | { |
||
1607 | return Message::error( |
||
1608 | __('Not enough privilege to view users.') |
||
1609 | )->getDisplay(); |
||
1610 | } |
||
1611 | |||
1612 | /** |
||
1613 | * Get HTML snippet for table body of specific database or table privileges |
||
1614 | * |
||
1615 | * @param array $privMap privilege map |
||
1616 | * @param string $db database |
||
1617 | * |
||
1618 | * @return string |
||
1619 | */ |
||
1620 | public function getHtmlTableBodyForSpecificDbOrTablePrivs($privMap, $db) |
||
1621 | { |
||
1622 | $html_output = '<tbody>'; |
||
1623 | $index_checkbox = 0; |
||
1624 | if (empty($privMap)) { |
||
1625 | $html_output .= '<tr>' |
||
1626 | . '<td colspan="6">' |
||
1627 | . __('No user found.') |
||
1628 | . '</td>' |
||
1629 | . '</tr>' |
||
1630 | . '</tbody>'; |
||
1631 | return $html_output; |
||
1632 | } |
||
1633 | |||
1634 | foreach ($privMap as $current_user => $val) { |
||
1635 | foreach ($val as $current_host => $current_privileges) { |
||
1636 | $nbPrivileges = count($current_privileges); |
||
1637 | $html_output .= '<tr>'; |
||
1638 | |||
1639 | $value = htmlspecialchars($current_user . '&#27;' . $current_host); |
||
1640 | $html_output .= '<td'; |
||
1641 | if ($nbPrivileges > 1) { |
||
1642 | $html_output .= ' rowspan="' . $nbPrivileges . '"'; |
||
1643 | } |
||
1644 | $html_output .= '>'; |
||
1645 | $html_output .= '<input type="checkbox" class="checkall" ' |
||
1646 | . 'name="selected_usr[]" ' |
||
1647 | . 'id="checkbox_sel_users_' . ($index_checkbox++) . '" ' |
||
1648 | . 'value="' . $value . '"></td>' . "\n"; |
||
1649 | |||
1650 | // user |
||
1651 | $html_output .= '<td'; |
||
1652 | if ($nbPrivileges > 1) { |
||
1653 | $html_output .= ' rowspan="' . $nbPrivileges . '"'; |
||
1654 | } |
||
1655 | $html_output .= '>'; |
||
1656 | if (empty($current_user)) { |
||
1657 | $html_output .= '<span style="color: #FF0000">' |
||
1658 | . __('Any') . '</span>'; |
||
1659 | } else { |
||
1660 | $html_output .= htmlspecialchars($current_user); |
||
1661 | } |
||
1662 | $html_output .= '</td>'; |
||
1663 | |||
1664 | // host |
||
1665 | $html_output .= '<td'; |
||
1666 | if ($nbPrivileges > 1) { |
||
1667 | $html_output .= ' rowspan="' . $nbPrivileges . '"'; |
||
1668 | } |
||
1669 | $html_output .= '>'; |
||
1670 | $html_output .= htmlspecialchars($current_host); |
||
1671 | $html_output .= '</td>'; |
||
1672 | |||
1673 | $html_output .= $this->getHtmlListOfPrivs( |
||
1674 | $db, |
||
1675 | $current_privileges, |
||
1676 | $current_user, |
||
1677 | $current_host |
||
1678 | ); |
||
1679 | } |
||
1680 | } |
||
1681 | |||
1682 | //For fetching routine based privileges |
||
1683 | $html_output .= $this->getHtmlTableBodyForSpecificDbRoutinePrivs($db, $index_checkbox); |
||
1684 | $html_output .= '</tbody>'; |
||
1685 | |||
1686 | return $html_output; |
||
1687 | } |
||
1688 | |||
1689 | /** |
||
1690 | * Get HTML to display privileges |
||
1691 | * |
||
1692 | * @param string $db Database name |
||
1693 | * @param array $current_privileges List of privileges |
||
1694 | * @param string $current_user Current user |
||
1695 | * @param string $current_host Current host |
||
1696 | * |
||
1697 | * @return string HTML to display privileges |
||
1698 | */ |
||
1699 | public function getHtmlListOfPrivs( |
||
1700 | $db, |
||
1701 | array $current_privileges, |
||
1702 | $current_user, |
||
1703 | $current_host |
||
1704 | ) { |
||
1705 | $nbPrivileges = count($current_privileges); |
||
1706 | $html_output = null; |
||
1707 | for ($i = 0; $i < $nbPrivileges; $i++) { |
||
1708 | $current = $current_privileges[$i]; |
||
1709 | |||
1710 | // type |
||
1711 | $html_output .= '<td>'; |
||
1712 | if ($current['Type'] == 'g') { |
||
1713 | $html_output .= __('global'); |
||
1714 | } elseif ($current['Type'] == 'd') { |
||
1715 | if ($current['Db'] == Util::escapeMysqlWildcards($db)) { |
||
1716 | $html_output .= __('database-specific'); |
||
1717 | } else { |
||
1718 | $html_output .= __('wildcard') . ': ' |
||
1719 | . '<code>' |
||
1720 | . htmlspecialchars($current['Db']) |
||
1721 | . '</code>'; |
||
1722 | } |
||
1723 | } elseif ($current['Type'] == 't') { |
||
1724 | $html_output .= __('table-specific'); |
||
1725 | } |
||
1726 | $html_output .= '</td>'; |
||
1727 | |||
1728 | // privileges |
||
1729 | $html_output .= '<td>'; |
||
1730 | if (isset($current['Table_name'])) { |
||
1731 | $privList = explode(',', $current['Table_priv']); |
||
1732 | $privs = []; |
||
1733 | $grantsArr = $this->getTableGrantsArray(); |
||
1734 | foreach ($grantsArr as $grant) { |
||
1735 | $privs[$grant[0]] = 'N'; |
||
1736 | foreach ($privList as $priv) { |
||
1737 | if ($grant[0] == $priv) { |
||
1738 | $privs[$grant[0]] = 'Y'; |
||
1739 | } |
||
1740 | } |
||
1741 | } |
||
1742 | $html_output .= '<code>' |
||
1743 | . implode( |
||
1744 | ',', |
||
1745 | $this->extractPrivInfo($privs, true, true) |
||
1746 | ) |
||
1747 | . '</code>'; |
||
1748 | } else { |
||
1749 | $html_output .= '<code>' |
||
1750 | . implode( |
||
1751 | ',', |
||
1752 | $this->extractPrivInfo($current, true, false) |
||
1753 | ) |
||
1754 | . '</code>'; |
||
1755 | } |
||
1756 | $html_output .= '</td>'; |
||
1757 | |||
1758 | // grant |
||
1759 | $html_output .= '<td>'; |
||
1760 | $containsGrant = false; |
||
1761 | if (isset($current['Table_name'])) { |
||
1762 | $privList = explode(',', $current['Table_priv']); |
||
1763 | foreach ($privList as $priv) { |
||
1764 | if ($priv == 'Grant') { |
||
1765 | $containsGrant = true; |
||
1766 | } |
||
1767 | } |
||
1768 | } else { |
||
1769 | $containsGrant = $current['Grant_priv'] == 'Y'; |
||
1770 | } |
||
1771 | $html_output .= ($containsGrant ? __('Yes') : __('No')); |
||
1772 | $html_output .= '</td>'; |
||
1773 | |||
1774 | // action |
||
1775 | $html_output .= '<td>'; |
||
1776 | $specific_db = isset($current['Db']) && $current['Db'] != '*' |
||
1777 | ? $current['Db'] : ''; |
||
1778 | $specific_table = isset($current['Table_name']) |
||
1779 | && $current['Table_name'] != '*' |
||
1780 | ? $current['Table_name'] : ''; |
||
1781 | if ($GLOBALS['is_grantuser']) { |
||
1782 | $html_output .= $this->getUserLink( |
||
1783 | 'edit', |
||
1784 | $current_user, |
||
1785 | $current_host, |
||
1786 | $specific_db, |
||
1787 | $specific_table |
||
1788 | ); |
||
1789 | } |
||
1790 | $html_output .= '</td>'; |
||
1791 | $html_output .= '<td class="center">' |
||
1792 | . $this->getUserLink( |
||
1793 | 'export', |
||
1794 | $current_user, |
||
1795 | $current_host, |
||
1796 | $specific_db, |
||
1797 | $specific_table |
||
1798 | ) |
||
1799 | . '</td>'; |
||
1800 | |||
1801 | $html_output .= '</tr>'; |
||
1802 | if (($i + 1) < $nbPrivileges) { |
||
1803 | $html_output .= '<tr class="noclick">'; |
||
1804 | } |
||
1805 | } |
||
1806 | return $html_output; |
||
1807 | } |
||
1808 | |||
1809 | /** |
||
1810 | * Returns edit, revoke or export link for a user. |
||
1811 | * |
||
1812 | * @param string $linktype The link type (edit | revoke | export) |
||
1813 | * @param string $username User name |
||
1814 | * @param string $hostname Host name |
||
1815 | * @param string $dbname Database name |
||
1816 | * @param string $tablename Table name |
||
1817 | * @param string $routinename Routine name |
||
1818 | * @param string $initial Initial value |
||
1819 | * |
||
1820 | * @return string HTML code with link |
||
1821 | */ |
||
1822 | public function getUserLink( |
||
1823 | $linktype, |
||
1824 | $username, |
||
1825 | $hostname, |
||
1826 | $dbname = '', |
||
1827 | $tablename = '', |
||
1828 | $routinename = '', |
||
1829 | $initial = '' |
||
1830 | ) { |
||
1831 | $html = '<a'; |
||
1832 | switch ($linktype) { |
||
1833 | case 'edit': |
||
1834 | $html .= ' class="edit_user_anchor"'; |
||
1835 | break; |
||
1836 | case 'export': |
||
1837 | $html .= ' class="export_user_anchor ajax"'; |
||
1838 | break; |
||
1839 | } |
||
1840 | $params = [ |
||
1841 | 'username' => $username, |
||
1842 | 'hostname' => $hostname, |
||
1843 | ]; |
||
1844 | switch ($linktype) { |
||
1845 | case 'edit': |
||
1846 | $params['dbname'] = $dbname; |
||
1847 | $params['tablename'] = $tablename; |
||
1848 | $params['routinename'] = $routinename; |
||
1849 | break; |
||
1850 | case 'revoke': |
||
1851 | $params['dbname'] = $dbname; |
||
1852 | $params['tablename'] = $tablename; |
||
1853 | $params['routinename'] = $routinename; |
||
1854 | $params['revokeall'] = 1; |
||
1855 | break; |
||
1856 | case 'export': |
||
1857 | $params['initial'] = $initial; |
||
1858 | $params['export'] = 1; |
||
1859 | break; |
||
1860 | } |
||
1861 | |||
1862 | $html .= ' href="' . Url::getFromRoute('/server/privileges'); |
||
1863 | if ($linktype == 'revoke') { |
||
1864 | $html .= '" data-post="' . Url::getCommon($params, ''); |
||
1865 | } else { |
||
1866 | $html .= Url::getCommon($params, '&'); |
||
1867 | } |
||
1868 | $html .= '">'; |
||
1869 | |||
1870 | switch ($linktype) { |
||
1871 | case 'edit': |
||
1872 | $html .= Util::getIcon('b_usredit', __('Edit privileges')); |
||
1873 | break; |
||
1874 | case 'revoke': |
||
1875 | $html .= Util::getIcon('b_usrdrop', __('Revoke')); |
||
1876 | break; |
||
1877 | case 'export': |
||
1878 | $html .= Util::getIcon('b_tblexport', __('Export')); |
||
1879 | break; |
||
1880 | } |
||
1881 | $html .= '</a>'; |
||
1882 | |||
1883 | return $html; |
||
1884 | } |
||
1885 | |||
1886 | /** |
||
1887 | * Returns user group edit link |
||
1888 | * |
||
1889 | * @param string $username User name |
||
1890 | * |
||
1891 | * @return string HTML code with link |
||
1892 | */ |
||
1893 | public function getUserGroupEditLink($username) |
||
1894 | { |
||
1895 | return '<a class="edit_user_group_anchor ajax"' |
||
1896 | . ' href="' . Url::getFromRoute('/server/privileges', ['username' => $username]) |
||
1897 | . '">' |
||
1898 | . Util::getIcon('b_usrlist', __('Edit user group')) |
||
1899 | . '</a>'; |
||
1900 | } |
||
1901 | |||
1902 | /** |
||
1903 | * Returns number of defined user groups |
||
1904 | * |
||
1905 | * @return integer |
||
1906 | */ |
||
1907 | public function getUserGroupCount() |
||
1908 | { |
||
1909 | $cfgRelation = $this->relation->getRelationsParam(); |
||
1910 | $user_group_table = Util::backquote($cfgRelation['db']) |
||
1911 | . '.' . Util::backquote($cfgRelation['usergroups']); |
||
1912 | $sql_query = 'SELECT COUNT(*) FROM ' . $user_group_table; |
||
1913 | $user_group_count = $this->dbi->fetchValue( |
||
1914 | $sql_query, |
||
1915 | 0, |
||
1916 | 0, |
||
1917 | DatabaseInterface::CONNECT_CONTROL |
||
1918 | ); |
||
1919 | |||
1920 | return $user_group_count; |
||
1921 | } |
||
1922 | |||
1923 | /** |
||
1924 | * Returns name of user group that user is part of |
||
1925 | * |
||
1926 | * @param string $username User name |
||
1927 | * |
||
1928 | * @return mixed usergroup if found or null if not found |
||
1929 | */ |
||
1930 | public function getUserGroupForUser($username) |
||
1931 | { |
||
1932 | $cfgRelation = $this->relation->getRelationsParam(); |
||
1933 | |||
1934 | if (empty($cfgRelation['db']) |
||
1935 | || empty($cfgRelation['users']) |
||
1936 | ) { |
||
1937 | return null; |
||
1938 | } |
||
1939 | |||
1940 | $user_table = Util::backquote($cfgRelation['db']) |
||
1941 | . '.' . Util::backquote($cfgRelation['users']); |
||
1942 | $sql_query = 'SELECT `usergroup` FROM ' . $user_table |
||
1943 | . ' WHERE `username` = \'' . $username . '\'' |
||
1944 | . ' LIMIT 1'; |
||
1945 | |||
1946 | $usergroup = $this->dbi->fetchValue( |
||
1947 | $sql_query, |
||
1948 | 0, |
||
1949 | 0, |
||
1950 | DatabaseInterface::CONNECT_CONTROL |
||
1951 | ); |
||
1952 | |||
1953 | if ($usergroup === false) { |
||
1954 | return null; |
||
1955 | } |
||
1956 | |||
1957 | return $usergroup; |
||
1958 | } |
||
1959 | |||
1960 | /** |
||
1961 | * This function return the extra data array for the ajax behavior |
||
1962 | * |
||
1963 | * @param string $password password |
||
1964 | * @param string $sql_query sql query |
||
1965 | * @param string $hostname hostname |
||
1966 | * @param string $username username |
||
1967 | * |
||
1968 | * @return array |
||
1969 | */ |
||
1970 | public function getExtraDataForAjaxBehavior( |
||
1971 | $password, |
||
1972 | $sql_query, |
||
1973 | $hostname, |
||
1974 | $username |
||
1975 | ) { |
||
1976 | if (isset($GLOBALS['dbname'])) { |
||
1977 | //if (preg_match('/\\\\(?:_|%)/i', $dbname)) { |
||
1978 | if (preg_match('/(?<!\\\\)(?:_|%)/', $GLOBALS['dbname'])) { |
||
1979 | $dbname_is_wildcard = true; |
||
1980 | } else { |
||
1981 | $dbname_is_wildcard = false; |
||
1982 | } |
||
1983 | } |
||
1984 | |||
1985 | $user_group_count = 0; |
||
1986 | if ($GLOBALS['cfgRelation']['menuswork']) { |
||
1987 | $user_group_count = $this->getUserGroupCount(); |
||
1988 | } |
||
1989 | |||
1990 | $extra_data = []; |
||
1991 | if (strlen($sql_query) > 0) { |
||
1992 | $extra_data['sql_query'] = Util::getMessage(null, $sql_query); |
||
1993 | } |
||
1994 | |||
1995 | if (isset($_POST['change_copy'])) { |
||
1996 | /** |
||
1997 | * generate html on the fly for the new user that was just created. |
||
1998 | */ |
||
1999 | $new_user_string = '<tr>' . "\n" |
||
2000 | . '<td> <input type="checkbox" name="selected_usr[]" ' |
||
2001 | . 'id="checkbox_sel_users_"' |
||
2002 | . 'value="' |
||
2003 | . htmlspecialchars($username) |
||
2004 | . '&#27;' . htmlspecialchars($hostname) . '">' |
||
2005 | . '</td>' . "\n" |
||
2006 | . '<td><label for="checkbox_sel_users_">' |
||
2007 | . (empty($_POST['username']) |
||
2008 | ? '<span style="color: #FF0000">' . __('Any') . '</span>' |
||
2009 | : htmlspecialchars($username) ) . '</label></td>' . "\n" |
||
2010 | . '<td>' . htmlspecialchars($hostname) . '</td>' . "\n"; |
||
2011 | |||
2012 | $new_user_string .= '<td>'; |
||
2013 | |||
2014 | if (! empty($password) || isset($_POST['pma_pw'])) { |
||
2015 | $new_user_string .= __('Yes'); |
||
2016 | } else { |
||
2017 | $new_user_string .= '<span style="color: #FF0000">' |
||
2018 | . __('No') |
||
2019 | . '</span>'; |
||
2020 | } |
||
2021 | |||
2022 | $new_user_string .= '</td>' . "\n"; |
||
2023 | $new_user_string .= '<td>' |
||
2024 | . '<code>' . implode(', ', $this->extractPrivInfo(null, true)) . '</code>' |
||
2025 | . '</td>'; //Fill in privileges here |
||
2026 | |||
2027 | // if $cfg['Servers'][$i]['users'] and $cfg['Servers'][$i]['usergroups'] are |
||
2028 | // enabled |
||
2029 | $cfgRelation = $this->relation->getRelationsParam(); |
||
2030 | if (! empty($cfgRelation['users']) && ! empty($cfgRelation['usergroups'])) { |
||
2031 | $new_user_string .= '<td class="usrGroup"></td>'; |
||
2032 | } |
||
2033 | |||
2034 | $new_user_string .= '<td>'; |
||
2035 | if (isset($_POST['Grant_priv']) && $_POST['Grant_priv'] == 'Y') { |
||
2036 | $new_user_string .= __('Yes'); |
||
2037 | } else { |
||
2038 | $new_user_string .= __('No'); |
||
2039 | } |
||
2040 | $new_user_string .= '</td>'; |
||
2041 | |||
2042 | if ($GLOBALS['is_grantuser']) { |
||
2043 | $new_user_string .= '<td>' |
||
2044 | . $this->getUserLink('edit', $username, $hostname) |
||
2045 | . '</td>' . "\n"; |
||
2046 | } |
||
2047 | |||
2048 | if ($cfgRelation['menuswork'] && $user_group_count > 0) { |
||
2049 | $new_user_string .= '<td>' |
||
2050 | . $this->getUserGroupEditLink($username) |
||
2051 | . '</td>' . "\n"; |
||
2052 | } |
||
2053 | |||
2054 | $new_user_string .= '<td>' |
||
2055 | . $this->getUserLink( |
||
2056 | 'export', |
||
2057 | $username, |
||
2058 | $hostname, |
||
2059 | '', |
||
2060 | '', |
||
2061 | '', |
||
2062 | isset($_GET['initial']) ? $_GET['initial'] : '' |
||
2063 | ) |
||
2064 | . '</td>' . "\n"; |
||
2065 | |||
2066 | $new_user_string .= '</tr>'; |
||
2067 | |||
2068 | $extra_data['new_user_string'] = $new_user_string; |
||
2069 | |||
2070 | /** |
||
2071 | * Generate the string for this alphabet's initial, to update the user |
||
2072 | * pagination |
||
2073 | */ |
||
2074 | $new_user_initial = mb_strtoupper( |
||
2075 | mb_substr($username, 0, 1) |
||
2076 | ); |
||
2077 | $newUserInitialString = '<a href="' . Url::getFromRoute('/server/privileges', ['initial' => $new_user_initial]) . '">' |
||
2078 | . $new_user_initial . '</a>'; |
||
2079 | $extra_data['new_user_initial'] = $new_user_initial; |
||
2080 | $extra_data['new_user_initial_string'] = $newUserInitialString; |
||
2081 | } |
||
2082 | |||
2083 | if (isset($_POST['update_privs'])) { |
||
2084 | $extra_data['db_specific_privs'] = false; |
||
2085 | $extra_data['db_wildcard_privs'] = false; |
||
2086 | if (isset($dbname_is_wildcard)) { |
||
2087 | $extra_data['db_specific_privs'] = ! $dbname_is_wildcard; |
||
2088 | $extra_data['db_wildcard_privs'] = $dbname_is_wildcard; |
||
2089 | } |
||
2090 | $new_privileges = implode(', ', $this->extractPrivInfo(null, true)); |
||
2091 | |||
2092 | $extra_data['new_privileges'] = $new_privileges; |
||
2093 | } |
||
2094 | |||
2095 | if (isset($_GET['validate_username'])) { |
||
2096 | $sql_query = "SELECT * FROM `mysql`.`user` WHERE `User` = '" |
||
2097 | . $_GET['username'] . "';"; |
||
2098 | $res = $this->dbi->query($sql_query); |
||
2099 | $row = $this->dbi->fetchRow($res); |
||
2100 | if (empty($row)) { |
||
2101 | $extra_data['user_exists'] = false; |
||
2102 | } else { |
||
2103 | $extra_data['user_exists'] = true; |
||
2104 | } |
||
2105 | } |
||
2106 | |||
2107 | return $extra_data; |
||
2108 | } |
||
2109 | |||
2110 | /** |
||
2111 | * Get the HTML snippet for change user login information |
||
2112 | * |
||
2113 | * @param string $username username |
||
2114 | * @param string $hostname host name |
||
2115 | * |
||
2116 | * @return string HTML snippet |
||
2117 | */ |
||
2118 | public function getChangeLoginInformationHtmlForm($username, $hostname) |
||
2175 | } |
||
2176 | |||
2177 | /** |
||
2178 | * Provide a line with links to the relevant database and table |
||
2179 | * |
||
2180 | * @param string $url_dbname url database name that urlencode() string |
||
2181 | * @param string $dbname database name |
||
2182 | * @param string $tablename table name |
||
2183 | * |
||
2184 | * @return string HTML snippet |
||
2185 | */ |
||
2186 | public function getLinkToDbAndTable($url_dbname, $dbname, $tablename) |
||
2187 | { |
||
2188 | $scriptName = Util::getScriptNameForOption( |
||
2189 | $GLOBALS['cfg']['DefaultTabDatabase'], |
||
2190 | 'database' |
||
2191 | ); |
||
2192 | $html_output = '[ ' . __('Database') |
||
2193 | . ' <a href="' . $scriptName |
||
2194 | . Url::getCommon([ |
||
2195 | 'db' => $url_dbname, |
||
2196 | 'reload' => 1, |
||
2197 | ], strpos($scriptName, '?') === false ? '?' : '&') |
||
2198 | . '">' |
||
2199 | . htmlspecialchars(Util::unescapeMysqlWildcards($dbname)) . ': ' |
||
2200 | . Util::getTitleForTarget( |
||
2201 | $GLOBALS['cfg']['DefaultTabDatabase'] |
||
2202 | ) |
||
2203 | . "</a> ]\n"; |
||
2204 | |||
2205 | if (strlen($tablename) > 0) { |
||
2206 | $scriptName = Util::getScriptNameForOption( |
||
2207 | $GLOBALS['cfg']['DefaultTabTable'], |
||
2208 | 'table' |
||
2209 | ); |
||
2210 | $html_output .= ' [ ' . __('Table') . ' <a href="' |
||
2211 | . $scriptName |
||
2212 | . Url::getCommon([ |
||
2213 | 'db' => $url_dbname, |
||
2214 | 'table' => $tablename, |
||
2215 | 'reload' => 1, |
||
2216 | ], strpos($scriptName, '?') === false ? '?' : '&') |
||
2217 | . '">' . htmlspecialchars($tablename) . ': ' |
||
2218 | . Util::getTitleForTarget( |
||
2219 | $GLOBALS['cfg']['DefaultTabTable'] |
||
2220 | ) |
||
2221 | . "</a> ]\n"; |
||
2222 | } |
||
2223 | return $html_output; |
||
2224 | } |
||
2225 | |||
2226 | /** |
||
2227 | * no db name given, so we want all privs for the given user |
||
2228 | * db name was given, so we want all user specific rights for this db |
||
2229 | * So this function returns user rights as an array |
||
2230 | * |
||
2231 | * @param string $username username |
||
2232 | * @param string $hostname host name |
||
2233 | * @param string $type database or table |
||
2234 | * @param string $dbname database name |
||
2235 | * |
||
2236 | * @return array database rights |
||
2237 | */ |
||
2238 | public function getUserSpecificRights($username, $hostname, $type, $dbname = '') |
||
2239 | { |
||
2240 | $user_host_condition = " WHERE `User`" |
||
2241 | . " = '" . $this->dbi->escapeString($username) . "'" |
||
2242 | . " AND `Host`" |
||
2243 | . " = '" . $this->dbi->escapeString($hostname) . "'"; |
||
2244 | |||
2245 | if ($type == 'database') { |
||
2246 | $tables_to_search_for_users = [ |
||
2247 | 'tables_priv', |
||
2248 | 'columns_priv', |
||
2249 | 'procs_priv', |
||
2250 | ]; |
||
2251 | $dbOrTableName = 'Db'; |
||
2252 | } elseif ($type == 'table') { |
||
2253 | $user_host_condition .= " AND `Db` LIKE '" |
||
2254 | . $this->dbi->escapeString($dbname) . "'"; |
||
2255 | $tables_to_search_for_users = ['columns_priv']; |
||
2256 | $dbOrTableName = 'Table_name'; |
||
2257 | } else { // routine |
||
2258 | $user_host_condition .= " AND `Db` LIKE '" |
||
2259 | . $this->dbi->escapeString($dbname) . "'"; |
||
2260 | $tables_to_search_for_users = ['procs_priv']; |
||
2261 | $dbOrTableName = 'Routine_name'; |
||
2262 | } |
||
2263 | |||
2264 | // we also want privileges for this user not in table `db` but in other table |
||
2265 | $tables = $this->dbi->fetchResult('SHOW TABLES FROM `mysql`;'); |
||
2266 | |||
2267 | $db_rights_sqls = []; |
||
2268 | foreach ($tables_to_search_for_users as $table_search_in) { |
||
2269 | if (in_array($table_search_in, $tables)) { |
||
2270 | $db_rights_sqls[] = ' |
||
2271 | SELECT DISTINCT `' . $dbOrTableName . '` |
||
2272 | FROM `mysql`.' . Util::backquote($table_search_in) |
||
2273 | . $user_host_condition; |
||
2274 | } |
||
2275 | } |
||
2276 | |||
2277 | $user_defaults = [ |
||
2278 | $dbOrTableName => '', |
||
2279 | 'Grant_priv' => 'N', |
||
2280 | 'privs' => ['USAGE'], |
||
2281 | 'Column_priv' => true, |
||
2282 | ]; |
||
2283 | |||
2284 | // for the rights |
||
2285 | $db_rights = []; |
||
2286 | |||
2287 | $db_rights_sql = '(' . implode(') UNION (', $db_rights_sqls) . ')' |
||
2288 | . ' ORDER BY `' . $dbOrTableName . '` ASC'; |
||
2289 | |||
2290 | $db_rights_result = $this->dbi->query($db_rights_sql); |
||
2291 | |||
2292 | while ($db_rights_row = $this->dbi->fetchAssoc($db_rights_result)) { |
||
2293 | $db_rights_row = array_merge($user_defaults, $db_rights_row); |
||
2294 | if ($type == 'database') { |
||
2295 | // only Db names in the table `mysql`.`db` uses wildcards |
||
2296 | // as we are in the db specific rights display we want |
||
2297 | // all db names escaped, also from other sources |
||
2298 | $db_rights_row['Db'] = Util::escapeMysqlWildcards( |
||
2299 | $db_rights_row['Db'] |
||
2300 | ); |
||
2301 | } |
||
2302 | $db_rights[$db_rights_row[$dbOrTableName]] = $db_rights_row; |
||
2303 | } |
||
2304 | |||
2305 | $this->dbi->freeResult($db_rights_result); |
||
2306 | |||
2307 | if ($type == 'database') { |
||
2308 | $sql_query = 'SELECT * FROM `mysql`.`db`' |
||
2309 | . $user_host_condition . ' ORDER BY `Db` ASC'; |
||
2310 | } elseif ($type == 'table') { |
||
2311 | $sql_query = 'SELECT `Table_name`,' |
||
2312 | . ' `Table_priv`,' |
||
2313 | . ' IF(`Column_priv` = _latin1 \'\', 0, 1)' |
||
2314 | . ' AS \'Column_priv\'' |
||
2315 | . ' FROM `mysql`.`tables_priv`' |
||
2316 | . $user_host_condition |
||
2317 | . ' ORDER BY `Table_name` ASC;'; |
||
2318 | } else { |
||
2319 | $sql_query = "SELECT `Routine_name`, `Proc_priv`" |
||
2320 | . " FROM `mysql`.`procs_priv`" |
||
2321 | . $user_host_condition |
||
2322 | . " ORDER BY `Routine_name`"; |
||
2323 | } |
||
2324 | |||
2325 | $result = $this->dbi->query($sql_query); |
||
2326 | |||
2327 | while ($row = $this->dbi->fetchAssoc($result)) { |
||
2328 | if (isset($db_rights[$row[$dbOrTableName]])) { |
||
2329 | $db_rights[$row[$dbOrTableName]] |
||
2330 | = array_merge($db_rights[$row[$dbOrTableName]], $row); |
||
2331 | } else { |
||
2332 | $db_rights[$row[$dbOrTableName]] = $row; |
||
2333 | } |
||
2334 | if ($type == 'database') { |
||
2335 | // there are db specific rights for this user |
||
2336 | // so we can drop this db rights |
||
2337 | $db_rights[$row['Db']]['can_delete'] = true; |
||
2338 | } |
||
2339 | } |
||
2340 | $this->dbi->freeResult($result); |
||
2341 | return $db_rights; |
||
2342 | } |
||
2343 | |||
2344 | /** |
||
2345 | * Parses Proc_priv data |
||
2346 | * |
||
2347 | * @param string $privs Proc_priv |
||
2348 | * |
||
2349 | * @return array |
||
2350 | */ |
||
2351 | public function parseProcPriv($privs) |
||
2352 | { |
||
2353 | $result = [ |
||
2354 | 'Alter_routine_priv' => 'N', |
||
2355 | 'Execute_priv' => 'N', |
||
2356 | 'Grant_priv' => 'N', |
||
2357 | ]; |
||
2358 | foreach (explode(',', (string) $privs) as $priv) { |
||
2359 | if ($priv == 'Alter Routine') { |
||
2360 | $result['Alter_routine_priv'] = 'Y'; |
||
2361 | } else { |
||
2362 | $result[$priv . '_priv'] = 'Y'; |
||
2363 | } |
||
2364 | } |
||
2365 | return $result; |
||
2366 | } |
||
2367 | |||
2368 | /** |
||
2369 | * Get a HTML table for display user's tabel specific or database specific rights |
||
2370 | * |
||
2371 | * @param string $username username |
||
2372 | * @param string $hostname host name |
||
2373 | * @param string $type database, table or routine |
||
2374 | * @param string $dbname database name |
||
2375 | * |
||
2376 | * @return string |
||
2377 | */ |
||
2378 | public function getHtmlForAllTableSpecificRights( |
||
2379 | $username, |
||
2380 | $hostname, |
||
2381 | $type, |
||
2382 | $dbname = '' |
||
2383 | ) { |
||
2384 | $uiData = [ |
||
2385 | 'database' => [ |
||
2386 | 'form_id' => 'database_specific_priv', |
||
2387 | 'sub_menu_label' => __('Database'), |
||
2388 | 'legend' => __('Database-specific privileges'), |
||
2389 | 'type_label' => __('Database'), |
||
2390 | ], |
||
2391 | 'table' => [ |
||
2392 | 'form_id' => 'table_specific_priv', |
||
2393 | 'sub_menu_label' => __('Table'), |
||
2394 | 'legend' => __('Table-specific privileges'), |
||
2395 | 'type_label' => __('Table'), |
||
2396 | ], |
||
2397 | 'routine' => [ |
||
2398 | 'form_id' => 'routine_specific_priv', |
||
2399 | 'sub_menu_label' => __('Routine'), |
||
2400 | 'legend' => __('Routine-specific privileges'), |
||
2401 | 'type_label' => __('Routine'), |
||
2402 | ], |
||
2403 | ]; |
||
2404 | |||
2405 | /** |
||
2406 | * no db name given, so we want all privs for the given user |
||
2407 | * db name was given, so we want all user specific rights for this db |
||
2408 | */ |
||
2409 | $db_rights = $this->getUserSpecificRights($username, $hostname, $type, $dbname); |
||
2410 | ksort($db_rights); |
||
2411 | |||
2412 | $foundRows = []; |
||
2413 | $privileges = []; |
||
2414 | foreach ($db_rights as $row) { |
||
2415 | $onePrivilege = []; |
||
2416 | |||
2417 | $paramTableName = ''; |
||
2418 | $paramRoutineName = ''; |
||
2419 | |||
2420 | if ($type == 'database') { |
||
2421 | $name = $row['Db']; |
||
2422 | $onePrivilege['grant'] = $row['Grant_priv'] == 'Y'; |
||
2423 | $onePrivilege['table_privs'] = ! empty($row['Table_priv']) |
||
2424 | || ! empty($row['Column_priv']); |
||
2425 | $onePrivilege['privileges'] = implode(',', $this->extractPrivInfo($row, true)); |
||
2426 | |||
2427 | $paramDbName = $row['Db']; |
||
2428 | } elseif ($type == 'table') { |
||
2429 | $name = $row['Table_name']; |
||
2430 | $onePrivilege['grant'] = in_array( |
||
2431 | 'Grant', |
||
2432 | explode(',', $row['Table_priv']) |
||
2433 | ); |
||
2434 | $onePrivilege['column_privs'] = ! empty($row['Column_priv']); |
||
2435 | $onePrivilege['privileges'] = implode(',', $this->extractPrivInfo($row, true)); |
||
2436 | |||
2437 | $paramDbName = $dbname; |
||
2438 | $paramTableName = $row['Table_name']; |
||
2439 | } else { // routine |
||
2440 | $name = $row['Routine_name']; |
||
2441 | $onePrivilege['grant'] = in_array( |
||
2442 | 'Grant', |
||
2443 | explode(',', $row['Proc_priv']) |
||
2444 | ); |
||
2445 | |||
2446 | $privs = $this->parseProcPriv($row['Proc_priv']); |
||
2447 | $onePrivilege['privileges'] = implode( |
||
2448 | ',', |
||
2449 | $this->extractPrivInfo($privs, true) |
||
2450 | ); |
||
2451 | |||
2452 | $paramDbName = $dbname; |
||
2453 | $paramRoutineName = $row['Routine_name']; |
||
2454 | } |
||
2455 | |||
2456 | $foundRows[] = $name; |
||
2457 | $onePrivilege['name'] = $name; |
||
2458 | |||
2459 | $onePrivilege['edit_link'] = ''; |
||
2460 | if ($GLOBALS['is_grantuser']) { |
||
2461 | $onePrivilege['edit_link'] = $this->getUserLink( |
||
2462 | 'edit', |
||
2463 | $username, |
||
2464 | $hostname, |
||
2465 | $paramDbName, |
||
2466 | $paramTableName, |
||
2467 | $paramRoutineName |
||
2468 | ); |
||
2469 | } |
||
2470 | |||
2471 | $onePrivilege['revoke_link'] = ''; |
||
2472 | if ($type != 'database' || ! empty($row['can_delete'])) { |
||
2473 | $onePrivilege['revoke_link'] = $this->getUserLink( |
||
2474 | 'revoke', |
||
2475 | $username, |
||
2476 | $hostname, |
||
2477 | $paramDbName, |
||
2478 | $paramTableName, |
||
2479 | $paramRoutineName |
||
2480 | ); |
||
2481 | } |
||
2482 | |||
2483 | $privileges[] = $onePrivilege; |
||
2484 | } |
||
2485 | |||
2486 | $data = $uiData[$type]; |
||
2487 | $data['privileges'] = $privileges; |
||
2488 | $data['username'] = $username; |
||
2489 | $data['hostname'] = $hostname; |
||
2490 | $data['database'] = $dbname; |
||
2491 | $data['type'] = $type; |
||
2492 | |||
2493 | if ($type == 'database') { |
||
2494 | // we already have the list of databases from libraries/common.inc.php |
||
2495 | // via $pma = new PMA; |
||
2496 | $pred_db_array = $GLOBALS['dblist']->databases; |
||
2497 | $databases_to_skip = [ |
||
2498 | 'information_schema', |
||
2499 | 'performance_schema', |
||
2500 | ]; |
||
2501 | |||
2502 | $databases = []; |
||
2503 | if (! empty($pred_db_array)) { |
||
2504 | foreach ($pred_db_array as $current_db) { |
||
2505 | if (in_array($current_db, $databases_to_skip)) { |
||
2506 | continue; |
||
2507 | } |
||
2508 | $current_db_escaped = Util::escapeMysqlWildcards($current_db); |
||
2509 | // cannot use array_diff() once, outside of the loop, |
||
2510 | // because the list of databases has special characters |
||
2511 | // already escaped in $foundRows, |
||
2512 | // contrary to the output of SHOW DATABASES |
||
2513 | if (! in_array($current_db_escaped, $foundRows)) { |
||
2514 | $databases[] = $current_db; |
||
2515 | } |
||
2516 | } |
||
2517 | } |
||
2518 | $data['databases'] = $databases; |
||
2519 | } elseif ($type == 'table') { |
||
2520 | $result = @$this->dbi->tryQuery( |
||
2521 | "SHOW TABLES FROM " . Util::backquote($dbname), |
||
2522 | DatabaseInterface::CONNECT_USER, |
||
2523 | DatabaseInterface::QUERY_STORE |
||
2524 | ); |
||
2525 | |||
2526 | $tables = []; |
||
2527 | if ($result) { |
||
2528 | while ($row = $this->dbi->fetchRow($result)) { |
||
2529 | if (! in_array($row[0], $foundRows)) { |
||
2530 | $tables[] = $row[0]; |
||
2531 | } |
||
2532 | } |
||
2533 | $this->dbi->freeResult($result); |
||
2534 | } |
||
2535 | $data['tables'] = $tables; |
||
2536 | } else { // routine |
||
2537 | $routineData = $this->dbi->getRoutines($dbname); |
||
2538 | |||
2539 | $routines = []; |
||
2540 | foreach ($routineData as $routine) { |
||
2541 | if (! in_array($routine['name'], $foundRows)) { |
||
2542 | $routines[] = $routine['name']; |
||
2543 | } |
||
2544 | } |
||
2545 | $data['routines'] = $routines; |
||
2546 | } |
||
2547 | |||
2548 | return $this->template->render('server/privileges/privileges_summary', $data); |
||
2549 | } |
||
2550 | |||
2551 | /** |
||
2552 | * Get HTML for display the users overview |
||
2553 | * (if less than 50 users, display them immediately) |
||
2554 | * |
||
2555 | * @param array $result ran sql query |
||
2556 | * @param array $db_rights user's database rights array |
||
2557 | * @param string $pmaThemeImage a image source link |
||
2558 | * @param string $text_dir text directory |
||
2559 | * |
||
2560 | * @return string HTML snippet |
||
2561 | */ |
||
2562 | public function getUsersOverview($result, array $db_rights, $pmaThemeImage, $text_dir) |
||
2563 | { |
||
2564 | global $is_grantuser, $is_createuser; |
||
2565 | |||
2566 | $cfgRelation = $this->relation->getRelationsParam(); |
||
2567 | |||
2568 | while ($row = $this->dbi->fetchAssoc($result)) { |
||
2569 | $row['privs'] = $this->extractPrivInfo($row, true); |
||
2570 | $db_rights[$row['User']][$row['Host']] = $row; |
||
2571 | } |
||
2572 | $this->dbi->freeResult($result); |
||
2573 | |||
2574 | $user_group_count = 0; |
||
2575 | if ($cfgRelation['menuswork']) { |
||
2576 | $sql_query = 'SELECT * FROM ' . Util::backquote($cfgRelation['db']) |
||
2577 | . '.' . Util::backquote($cfgRelation['users']); |
||
2578 | $result = $this->relation->queryAsControlUser($sql_query, false); |
||
2579 | $group_assignment = []; |
||
2580 | if ($result) { |
||
2581 | while ($row = $this->dbi->fetchAssoc($result)) { |
||
2582 | $group_assignment[$row['username']] = $row['usergroup']; |
||
2583 | } |
||
2584 | } |
||
2585 | $this->dbi->freeResult($result); |
||
2586 | |||
2587 | $user_group_count = $this->getUserGroupCount(); |
||
2588 | } |
||
2589 | |||
2590 | $hosts = []; |
||
2591 | foreach ($db_rights as $user) { |
||
2592 | ksort($user); |
||
2593 | foreach ($user as $host) { |
||
2594 | $check_plugin_query = "SELECT * FROM `mysql`.`user` WHERE " |
||
2595 | . "`User` = '" . $host['User'] . "' AND `Host` = '" |
||
2596 | . $host['Host'] . "'"; |
||
2597 | $res = $this->dbi->fetchSingleRow($check_plugin_query); |
||
2598 | |||
2599 | $hasPassword = false; |
||
2600 | if ((isset($res['authentication_string']) |
||
2601 | && ! empty($res['authentication_string'])) |
||
2602 | || (isset($res['Password']) |
||
2603 | && ! empty($res['Password'])) |
||
2604 | ) { |
||
2605 | $hasPassword = true; |
||
2606 | } |
||
2607 | |||
2608 | $hosts[] = [ |
||
2609 | 'user' => $host['User'], |
||
2610 | 'host' => $host['Host'], |
||
2611 | 'has_password' => $hasPassword, |
||
2612 | 'has_select_priv' => isset($host['Select_priv']), |
||
2613 | 'privileges' => $host['privs'], |
||
2614 | 'group' => $group_assignment[$host['User']] ?? '', |
||
2615 | 'has_grant' => $host['Grant_priv'] == 'Y', |
||
2616 | ]; |
||
2617 | } |
||
2618 | } |
||
2619 | |||
2620 | return $this->template->render('server/privileges/users_overview', [ |
||
2621 | 'menus_work' => $cfgRelation['menuswork'], |
||
2622 | 'user_group_count' => $user_group_count, |
||
2623 | 'pma_theme_image' => $pmaThemeImage, |
||
2624 | 'text_dir' => $text_dir, |
||
2625 | 'initial' => $_GET['initial'] ?? '', |
||
2626 | 'hosts' => $hosts, |
||
2627 | 'is_grantuser' => $is_grantuser, |
||
2628 | 'is_createuser' => $is_createuser, |
||
2629 | ]); |
||
2630 | } |
||
2631 | |||
2632 | /** |
||
2633 | * Get HTML for Displays the initials |
||
2634 | * |
||
2635 | * @param array $array_initials array for all initials, even non A-Z |
||
2636 | * |
||
2637 | * @return string HTML snippet |
||
2638 | */ |
||
2639 | public function getHtmlForInitials(array $array_initials) |
||
2640 | { |
||
2641 | // initialize to false the letters A-Z |
||
2642 | for ($letter_counter = 1; $letter_counter < 27; $letter_counter++) { |
||
2643 | if (! isset($array_initials[mb_chr($letter_counter + 64)])) { |
||
2644 | $array_initials[mb_chr($letter_counter + 64)] = false; |
||
2645 | } |
||
2646 | } |
||
2647 | |||
2648 | $initials = $this->dbi->tryQuery( |
||
2649 | 'SELECT DISTINCT UPPER(LEFT(`User`,1)) FROM `user`' |
||
2650 | . ' ORDER BY UPPER(LEFT(`User`,1)) ASC', |
||
2651 | DatabaseInterface::CONNECT_USER, |
||
2652 | DatabaseInterface::QUERY_STORE |
||
2653 | ); |
||
2654 | if ($initials) { |
||
2655 | while (list($tmp_initial) = $this->dbi->fetchRow($initials)) { |
||
2656 | $array_initials[$tmp_initial] = true; |
||
2657 | } |
||
2658 | } |
||
2659 | |||
2660 | // Display the initials, which can be any characters, not |
||
2661 | // just letters. For letters A-Z, we add the non-used letters |
||
2662 | // as greyed out. |
||
2663 | |||
2664 | uksort($array_initials, "strnatcasecmp"); |
||
2665 | |||
2666 | return $this->template->render('server/privileges/initials_row', [ |
||
2667 | 'array_initials' => $array_initials, |
||
2668 | 'initial' => isset($_GET['initial']) ? $_GET['initial'] : null, |
||
2669 | ]); |
||
2670 | } |
||
2671 | |||
2672 | /** |
||
2673 | * Get the database rights array for Display user overview |
||
2674 | * |
||
2675 | * @return array database rights array |
||
2676 | */ |
||
2677 | public function getDbRightsForUserOverview() |
||
2678 | { |
||
2679 | // we also want users not in table `user` but in other table |
||
2680 | $tables = $this->dbi->fetchResult('SHOW TABLES FROM `mysql`;'); |
||
2681 | |||
2682 | $tablesSearchForUsers = [ |
||
2683 | 'user', |
||
2684 | 'db', |
||
2685 | 'tables_priv', |
||
2686 | 'columns_priv', |
||
2687 | 'procs_priv', |
||
2688 | ]; |
||
2689 | |||
2690 | $db_rights_sqls = []; |
||
2691 | foreach ($tablesSearchForUsers as $table_search_in) { |
||
2692 | if (in_array($table_search_in, $tables)) { |
||
2693 | $db_rights_sqls[] = 'SELECT DISTINCT `User`, `Host` FROM `mysql`.`' |
||
2694 | . $table_search_in . '` ' |
||
2695 | . (isset($_GET['initial']) |
||
2696 | ? $this->rangeOfUsers($_GET['initial']) |
||
2697 | : ''); |
||
2698 | } |
||
2699 | } |
||
2700 | $user_defaults = [ |
||
2701 | 'User' => '', |
||
2702 | 'Host' => '%', |
||
2703 | 'Password' => '?', |
||
2704 | 'Grant_priv' => 'N', |
||
2705 | 'privs' => ['USAGE'], |
||
2706 | ]; |
||
2707 | |||
2708 | // for the rights |
||
2709 | $db_rights = []; |
||
2710 | |||
2711 | $db_rights_sql = '(' . implode(') UNION (', $db_rights_sqls) . ')' |
||
2712 | . ' ORDER BY `User` ASC, `Host` ASC'; |
||
2713 | |||
2714 | $db_rights_result = $this->dbi->query($db_rights_sql); |
||
2715 | |||
2716 | while ($db_rights_row = $this->dbi->fetchAssoc($db_rights_result)) { |
||
2717 | $db_rights_row = array_merge($user_defaults, $db_rights_row); |
||
2718 | $db_rights[$db_rights_row['User']][$db_rights_row['Host']] |
||
2719 | = $db_rights_row; |
||
2720 | } |
||
2721 | $this->dbi->freeResult($db_rights_result); |
||
2722 | ksort($db_rights); |
||
2723 | |||
2724 | return $db_rights; |
||
2725 | } |
||
2726 | |||
2727 | /** |
||
2728 | * Delete user and get message and sql query for delete user in privileges |
||
2729 | * |
||
2730 | * @param array $queries queries |
||
2731 | * |
||
2732 | * @return array Message |
||
2733 | */ |
||
2734 | public function deleteUser(array $queries) |
||
2735 | { |
||
2736 | $sql_query = ''; |
||
2737 | if (empty($queries)) { |
||
2738 | $message = Message::error(__('No users selected for deleting!')); |
||
2739 | } else { |
||
2740 | if ($_POST['mode'] == 3) { |
||
2741 | $queries[] = '# ' . __('Reloading the privileges') . ' …'; |
||
2742 | $queries[] = 'FLUSH PRIVILEGES;'; |
||
2743 | } |
||
2744 | $drop_user_error = ''; |
||
2745 | foreach ($queries as $sql_query) { |
||
2746 | if ($sql_query[0] != '#') { |
||
2747 | if (! $this->dbi->tryQuery($sql_query)) { |
||
2748 | $drop_user_error .= $this->dbi->getError() . "\n"; |
||
2749 | } |
||
2750 | } |
||
2751 | } |
||
2752 | // tracking sets this, causing the deleted db to be shown in navi |
||
2753 | unset($GLOBALS['db']); |
||
2754 | |||
2755 | $sql_query = implode("\n", $queries); |
||
2756 | if (! empty($drop_user_error)) { |
||
2757 | $message = Message::rawError($drop_user_error); |
||
2758 | } else { |
||
2759 | $message = Message::success( |
||
2760 | __('The selected users have been deleted successfully.') |
||
2761 | ); |
||
2762 | } |
||
2763 | } |
||
2764 | return [ |
||
2765 | $sql_query, |
||
2766 | $message, |
||
2767 | ]; |
||
2768 | } |
||
2769 | |||
2770 | /** |
||
2771 | * Update the privileges and return the success or error message |
||
2772 | * |
||
2773 | * @param string $username username |
||
2774 | * @param string $hostname host name |
||
2775 | * @param string $tablename table name |
||
2776 | * @param string $dbname database name |
||
2777 | * @param string $itemType item type |
||
2778 | * |
||
2779 | * @return array success message or error message for update |
||
2780 | */ |
||
2781 | public function updatePrivileges($username, $hostname, $tablename, $dbname, $itemType) |
||
2782 | { |
||
2783 | $db_and_table = $this->wildcardEscapeForGrant($dbname, $tablename); |
||
2784 | |||
2785 | $sql_query0 = 'REVOKE ALL PRIVILEGES ON ' . $itemType . ' ' . $db_and_table |
||
2786 | . ' FROM \'' . $this->dbi->escapeString($username) |
||
2787 | . '\'@\'' . $this->dbi->escapeString($hostname) . '\';'; |
||
2788 | |||
2789 | if (! isset($_POST['Grant_priv']) || $_POST['Grant_priv'] != 'Y') { |
||
2790 | $sql_query1 = 'REVOKE GRANT OPTION ON ' . $itemType . ' ' . $db_and_table |
||
2791 | . ' FROM \'' . $this->dbi->escapeString($username) . '\'@\'' |
||
2792 | . $this->dbi->escapeString($hostname) . '\';'; |
||
2793 | } else { |
||
2794 | $sql_query1 = ''; |
||
2795 | } |
||
2796 | |||
2797 | // Should not do a GRANT USAGE for a table-specific privilege, it |
||
2798 | // causes problems later (cannot revoke it) |
||
2799 | if (! (strlen($tablename) > 0 |
||
2800 | && 'USAGE' == implode('', $this->extractPrivInfo())) |
||
2801 | ) { |
||
2802 | $sql_query2 = 'GRANT ' . implode(', ', $this->extractPrivInfo()) |
||
2803 | . ' ON ' . $itemType . ' ' . $db_and_table |
||
2804 | . ' TO \'' . $this->dbi->escapeString($username) . '\'@\'' |
||
2805 | . $this->dbi->escapeString($hostname) . '\''; |
||
2806 | |||
2807 | if (strlen($dbname) === 0) { |
||
2808 | // add REQUIRE clause |
||
2809 | $sql_query2 .= $this->getRequireClause(); |
||
2810 | } |
||
2811 | |||
2812 | if ((isset($_POST['Grant_priv']) && $_POST['Grant_priv'] == 'Y') |
||
2813 | || (strlen($dbname) === 0 |
||
2814 | && (isset($_POST['max_questions']) || isset($_POST['max_connections']) |
||
2815 | || isset($_POST['max_updates']) |
||
2816 | || isset($_POST['max_user_connections']))) |
||
2817 | ) { |
||
2818 | $sql_query2 .= $this->getWithClauseForAddUserAndUpdatePrivs(); |
||
2819 | } |
||
2820 | $sql_query2 .= ';'; |
||
2821 | } |
||
2822 | if (! $this->dbi->tryQuery($sql_query0)) { |
||
2823 | // This might fail when the executing user does not have |
||
2824 | // ALL PRIVILEGES himself. |
||
2825 | // See https://github.com/phpmyadmin/phpmyadmin/issues/9673 |
||
2826 | $sql_query0 = ''; |
||
2827 | } |
||
2828 | if (! empty($sql_query1) && ! $this->dbi->tryQuery($sql_query1)) { |
||
2829 | // this one may fail, too... |
||
2830 | $sql_query1 = ''; |
||
2831 | } |
||
2832 | if (! empty($sql_query2)) { |
||
2833 | $this->dbi->query($sql_query2); |
||
2834 | } else { |
||
2835 | $sql_query2 = ''; |
||
2836 | } |
||
2837 | $sql_query = $sql_query0 . ' ' . $sql_query1 . ' ' . $sql_query2; |
||
2838 | $message = Message::success(__('You have updated the privileges for %s.')); |
||
2839 | $message->addParam('\'' . $username . '\'@\'' . $hostname . '\''); |
||
2840 | |||
2841 | return [ |
||
2842 | $sql_query, |
||
2843 | $message, |
||
2844 | ]; |
||
2845 | } |
||
2846 | |||
2847 | /** |
||
2848 | * Get List of information: Changes / copies a user |
||
2849 | * |
||
2850 | * @return array |
||
2851 | */ |
||
2852 | public function getDataForChangeOrCopyUser() |
||
2853 | { |
||
2854 | $queries = null; |
||
2855 | $password = null; |
||
2856 | |||
2857 | if (isset($_POST['change_copy'])) { |
||
2858 | $user_host_condition = ' WHERE `User` = ' |
||
2859 | . "'" . $this->dbi->escapeString($_POST['old_username']) . "'" |
||
2860 | . ' AND `Host` = ' |
||
2861 | . "'" . $this->dbi->escapeString($_POST['old_hostname']) . "';"; |
||
2862 | $row = $this->dbi->fetchSingleRow( |
||
2863 | 'SELECT * FROM `mysql`.`user` ' . $user_host_condition |
||
2864 | ); |
||
2865 | if (! $row) { |
||
2866 | $response = Response::getInstance(); |
||
2867 | $response->addHTML( |
||
2868 | Message::notice(__('No user found.'))->getDisplay() |
||
2869 | ); |
||
2870 | unset($_POST['change_copy']); |
||
2871 | } else { |
||
2872 | foreach ($row as $key => $value) { |
||
2873 | $GLOBALS[$key] = $value; |
||
2874 | } |
||
2875 | $serverVersion = $this->dbi->getVersion(); |
||
2876 | // Recent MySQL versions have the field "Password" in mysql.user, |
||
2877 | // so the previous extract creates $row['Password'] but this script |
||
2878 | // uses $password |
||
2879 | if (! isset($row['password']) && isset($row['Password'])) { |
||
2880 | $row['password'] = $row['Password']; |
||
2881 | } |
||
2882 | if (Util::getServerType() == 'MySQL' |
||
2883 | && $serverVersion >= 50606 |
||
2884 | && $serverVersion < 50706 |
||
2885 | && ((isset($row['authentication_string']) |
||
2886 | && empty($row['password'])) |
||
2887 | || (isset($row['plugin']) |
||
2888 | && $row['plugin'] == 'sha256_password')) |
||
2889 | ) { |
||
2890 | $row['password'] = $row['authentication_string']; |
||
2891 | } |
||
2892 | |||
2893 | if (Util::getServerType() == 'MariaDB' |
||
2894 | && $serverVersion >= 50500 |
||
2895 | && isset($row['authentication_string']) |
||
2896 | && empty($row['password']) |
||
2897 | ) { |
||
2898 | $row['password'] = $row['authentication_string']; |
||
2899 | } |
||
2900 | |||
2901 | // Always use 'authentication_string' column |
||
2902 | // for MySQL 5.7.6+ since it does not have |
||
2903 | // the 'password' column at all |
||
2904 | if (in_array(Util::getServerType(), ['MySQL', 'Percona Server']) |
||
2905 | && $serverVersion >= 50706 |
||
2906 | && isset($row['authentication_string']) |
||
2907 | ) { |
||
2908 | $row['password'] = $row['authentication_string']; |
||
2909 | } |
||
2910 | $password = $row['password']; |
||
2911 | $queries = []; |
||
2912 | } |
||
2913 | } |
||
2914 | |||
2915 | return [ |
||
2916 | $queries, |
||
2917 | $password, |
||
2918 | ]; |
||
2919 | } |
||
2920 | |||
2921 | /** |
||
2922 | * Update Data for information: Deletes users |
||
2923 | * |
||
2924 | * @param array $queries queries array |
||
2925 | * |
||
2926 | * @return array |
||
2927 | */ |
||
2928 | public function getDataForDeleteUsers($queries) |
||
2929 | { |
||
2930 | if (isset($_POST['change_copy'])) { |
||
2931 | $selected_usr = [ |
||
2932 | $_POST['old_username'] . '&#27;' . $_POST['old_hostname'], |
||
2933 | ]; |
||
2934 | } else { |
||
2935 | $selected_usr = $_POST['selected_usr']; |
||
2936 | $queries = []; |
||
2937 | } |
||
2938 | |||
2939 | // this happens, was seen in https://reports.phpmyadmin.net/reports/view/17146 |
||
2940 | if (! is_array($selected_usr)) { |
||
2941 | return []; |
||
2942 | } |
||
2943 | |||
2944 | foreach ($selected_usr as $each_user) { |
||
2945 | list($this_user, $this_host) = explode('&#27;', $each_user); |
||
2946 | $queries[] = '# ' |
||
2947 | . sprintf( |
||
2948 | __('Deleting %s'), |
||
2949 | '\'' . $this_user . '\'@\'' . $this_host . '\'' |
||
2950 | ) |
||
2951 | . ' ...'; |
||
2952 | $queries[] = 'DROP USER \'' |
||
2953 | . $this->dbi->escapeString($this_user) |
||
2954 | . '\'@\'' . $this->dbi->escapeString($this_host) . '\';'; |
||
2955 | $this->relationCleanup->user($this_user); |
||
2956 | |||
2957 | if (isset($_POST['drop_users_db'])) { |
||
2958 | $queries[] = 'DROP DATABASE IF EXISTS ' |
||
2959 | . Util::backquote($this_user) . ';'; |
||
2960 | $GLOBALS['reload'] = true; |
||
2961 | } |
||
2962 | } |
||
2963 | return $queries; |
||
2964 | } |
||
2965 | |||
2966 | /** |
||
2967 | * update Message For Reload |
||
2968 | * |
||
2969 | * @return Message|null |
||
2970 | */ |
||
2971 | public function updateMessageForReload(): ?Message |
||
2972 | { |
||
2973 | $message = null; |
||
2974 | if (isset($_GET['flush_privileges'])) { |
||
2975 | $sql_query = 'FLUSH PRIVILEGES;'; |
||
2976 | $this->dbi->query($sql_query); |
||
2977 | $message = Message::success( |
||
2978 | __('The privileges were reloaded successfully.') |
||
2979 | ); |
||
2980 | } |
||
2981 | |||
2982 | if (isset($_GET['validate_username'])) { |
||
2983 | $message = Message::success(); |
||
2984 | } |
||
2985 | |||
2986 | return $message; |
||
2987 | } |
||
2988 | |||
2989 | /** |
||
2990 | * update Data For Queries from queries_for_display |
||
2991 | * |
||
2992 | * @param array $queries queries array |
||
2993 | * @param array|null $queries_for_display queries array for display |
||
2994 | * |
||
2995 | * @return array |
||
2996 | */ |
||
2997 | public function getDataForQueries(array $queries, $queries_for_display) |
||
2998 | { |
||
2999 | $tmp_count = 0; |
||
3000 | foreach ($queries as $sql_query) { |
||
3001 | if ($sql_query[0] != '#') { |
||
3002 | $this->dbi->query($sql_query); |
||
3003 | } |
||
3004 | // when there is a query containing a hidden password, take it |
||
3005 | // instead of the real query sent |
||
3006 | if (isset($queries_for_display[$tmp_count])) { |
||
3007 | $queries[$tmp_count] = $queries_for_display[$tmp_count]; |
||
3008 | } |
||
3009 | $tmp_count++; |
||
3010 | } |
||
3011 | |||
3012 | return $queries; |
||
3013 | } |
||
3014 | |||
3015 | /** |
||
3016 | * update Data for information: Adds a user |
||
3017 | * |
||
3018 | * @param string|array|null $dbname db name |
||
3019 | * @param string $username user name |
||
3020 | * @param string $hostname host name |
||
3021 | * @param string|null $password password |
||
3022 | * @param bool $is_menuwork is_menuwork set? |
||
3023 | * |
||
3024 | * @return array |
||
3025 | */ |
||
3026 | public function addUser( |
||
3027 | $dbname, |
||
3028 | $username, |
||
3029 | $hostname, |
||
3030 | ?string $password, |
||
3031 | $is_menuwork |
||
3032 | ) { |
||
3033 | $_add_user_error = false; |
||
3034 | $message = null; |
||
3035 | $queries = null; |
||
3036 | $queries_for_display = null; |
||
3037 | $sql_query = null; |
||
3038 | |||
3039 | if (! isset($_POST['adduser_submit']) && ! isset($_POST['change_copy'])) { |
||
3040 | return [ |
||
3041 | $message, |
||
3042 | $queries, |
||
3043 | $queries_for_display, |
||
3044 | $sql_query, |
||
3045 | $_add_user_error, |
||
3046 | ]; |
||
3047 | } |
||
3048 | |||
3049 | $sql_query = ''; |
||
3050 | if ($_POST['pred_username'] == 'any') { |
||
3051 | $username = ''; |
||
3052 | } |
||
3053 | switch ($_POST['pred_hostname']) { |
||
3054 | case 'any': |
||
3055 | $hostname = '%'; |
||
3056 | break; |
||
3057 | case 'localhost': |
||
3058 | $hostname = 'localhost'; |
||
3059 | break; |
||
3060 | case 'hosttable': |
||
3061 | $hostname = ''; |
||
3062 | break; |
||
3063 | case 'thishost': |
||
3064 | $_user_name = $this->dbi->fetchValue('SELECT USER()'); |
||
3065 | $hostname = mb_substr( |
||
3066 | $_user_name, |
||
3067 | mb_strrpos($_user_name, '@') + 1 |
||
3068 | ); |
||
3069 | unset($_user_name); |
||
3070 | break; |
||
3071 | } |
||
3072 | $sql = "SELECT '1' FROM `mysql`.`user`" |
||
3073 | . " WHERE `User` = '" . $this->dbi->escapeString($username) . "'" |
||
3074 | . " AND `Host` = '" . $this->dbi->escapeString($hostname) . "';"; |
||
3075 | if ($this->dbi->fetchValue($sql) == 1) { |
||
3076 | $message = Message::error(__('The user %s already exists!')); |
||
3077 | $message->addParam('[em]\'' . $username . '\'@\'' . $hostname . '\'[/em]'); |
||
3078 | $_GET['adduser'] = true; |
||
3079 | $_add_user_error = true; |
||
3080 | |||
3081 | return [ |
||
3082 | $message, |
||
3083 | $queries, |
||
3084 | $queries_for_display, |
||
3085 | $sql_query, |
||
3086 | $_add_user_error, |
||
3087 | ]; |
||
3088 | } |
||
3089 | |||
3090 | list( |
||
3091 | $create_user_real, |
||
3092 | $create_user_show, |
||
3093 | $real_sql_query, |
||
3094 | $sql_query, |
||
3095 | $password_set_real, |
||
3096 | $password_set_show, |
||
3097 | $alter_real_sql_query, |
||
3098 | $alter_sql_query |
||
3099 | ) = $this->getSqlQueriesForDisplayAndAddUser( |
||
3100 | $username, |
||
3101 | $hostname, |
||
3102 | (isset($password) ? $password : '') |
||
3103 | ); |
||
3104 | |||
3105 | if (empty($_POST['change_copy'])) { |
||
3106 | $_error = false; |
||
3107 | |||
3108 | if ($create_user_real !== null) { |
||
3109 | if (! $this->dbi->tryQuery($create_user_real)) { |
||
3110 | $_error = true; |
||
3111 | } |
||
3112 | if (isset($password_set_real, $_POST['authentication_plugin']) && ! empty($password_set_real)) { |
||
3113 | $this->setProperPasswordHashing( |
||
3114 | $_POST['authentication_plugin'] |
||
3115 | ); |
||
3116 | if ($this->dbi->tryQuery($password_set_real)) { |
||
3117 | $sql_query .= $password_set_show; |
||
3118 | } |
||
3119 | } |
||
3120 | $sql_query = $create_user_show . $sql_query; |
||
3121 | } |
||
3122 | |||
3123 | list($sql_query, $message) = $this->addUserAndCreateDatabase( |
||
3124 | $_error, |
||
3125 | $real_sql_query, |
||
3126 | $sql_query, |
||
3127 | $username, |
||
3128 | $hostname, |
||
3129 | $dbname, |
||
3130 | $alter_real_sql_query, |
||
3131 | $alter_sql_query |
||
3132 | ); |
||
3133 | if (! empty($_POST['userGroup']) && $is_menuwork) { |
||
3134 | $this->setUserGroup($GLOBALS['username'], $_POST['userGroup']); |
||
3135 | } |
||
3136 | |||
3137 | return [ |
||
3138 | $message, |
||
3139 | $queries, |
||
3140 | $queries_for_display, |
||
3141 | $sql_query, |
||
3142 | $_add_user_error, |
||
3143 | ]; |
||
3144 | } |
||
3145 | |||
3146 | // Copy the user group while copying a user |
||
3147 | $old_usergroup = |
||
3148 | isset($_POST['old_usergroup']) ? $_POST['old_usergroup'] : null; |
||
3149 | $this->setUserGroup($_POST['username'], $old_usergroup); |
||
3150 | |||
3151 | if ($create_user_real === null) { |
||
3152 | $queries[] = $create_user_real; |
||
3153 | } |
||
3154 | $queries[] = $real_sql_query; |
||
3155 | |||
3156 | if (isset($password_set_real, $_POST['authentication_plugin']) && ! empty($password_set_real)) { |
||
3157 | $this->setProperPasswordHashing( |
||
3158 | $_POST['authentication_plugin'] |
||
3159 | ); |
||
3160 | |||
3161 | $queries[] = $password_set_real; |
||
3162 | } |
||
3163 | // we put the query containing the hidden password in |
||
3164 | // $queries_for_display, at the same position occupied |
||
3165 | // by the real query in $queries |
||
3166 | $tmp_count = count($queries); |
||
3167 | if (isset($create_user_real)) { |
||
3168 | $queries_for_display[$tmp_count - 2] = $create_user_show; |
||
3169 | } |
||
3170 | if (isset($password_set_real) && ! empty($password_set_real)) { |
||
3171 | $queries_for_display[$tmp_count - 3] = $create_user_show; |
||
3172 | $queries_for_display[$tmp_count - 2] = $sql_query; |
||
3173 | $queries_for_display[$tmp_count - 1] = $password_set_show; |
||
3174 | } else { |
||
3175 | $queries_for_display[$tmp_count - 1] = $sql_query; |
||
3176 | } |
||
3177 | |||
3178 | return [ |
||
3179 | $message, |
||
3180 | $queries, |
||
3181 | $queries_for_display, |
||
3182 | $sql_query, |
||
3183 | $_add_user_error, |
||
3184 | ]; |
||
3185 | } |
||
3186 | |||
3187 | /** |
||
3188 | * Sets proper value of `old_passwords` according to |
||
3189 | * the authentication plugin selected |
||
3190 | * |
||
3191 | * @param string $auth_plugin authentication plugin selected |
||
3192 | * |
||
3193 | * @return void |
||
3194 | */ |
||
3195 | public function setProperPasswordHashing($auth_plugin) |
||
3196 | { |
||
3197 | // Set the hashing method used by PASSWORD() |
||
3198 | // to be of type depending upon $authentication_plugin |
||
3199 | if ($auth_plugin == 'sha256_password') { |
||
3200 | $this->dbi->tryQuery('SET `old_passwords` = 2;'); |
||
3201 | } elseif ($auth_plugin == 'mysql_old_password') { |
||
3202 | $this->dbi->tryQuery('SET `old_passwords` = 1;'); |
||
3203 | } else { |
||
3204 | $this->dbi->tryQuery('SET `old_passwords` = 0;'); |
||
3205 | } |
||
3206 | } |
||
3207 | |||
3208 | /** |
||
3209 | * Update DB information: DB, Table, isWildcard |
||
3210 | * |
||
3211 | * @return array |
||
3212 | */ |
||
3213 | public function getDataForDBInfo() |
||
3214 | { |
||
3215 | $username = null; |
||
3216 | $hostname = null; |
||
3217 | $dbname = null; |
||
3218 | $tablename = null; |
||
3219 | $routinename = null; |
||
3220 | $dbname_is_wildcard = null; |
||
3221 | |||
3222 | if (isset($_REQUEST['username'])) { |
||
3223 | $username = $_REQUEST['username']; |
||
3224 | } |
||
3225 | if (isset($_REQUEST['hostname'])) { |
||
3226 | $hostname = $_REQUEST['hostname']; |
||
3227 | } |
||
3228 | /** |
||
3229 | * Checks if a dropdown box has been used for selecting a database / table |
||
3230 | */ |
||
3231 | if (Core::isValid($_POST['pred_tablename'])) { |
||
3232 | $tablename = $_POST['pred_tablename']; |
||
3233 | } elseif (Core::isValid($_REQUEST['tablename'])) { |
||
3234 | $tablename = $_REQUEST['tablename']; |
||
3235 | } else { |
||
3236 | unset($tablename); |
||
3237 | } |
||
3238 | |||
3239 | if (Core::isValid($_POST['pred_routinename'])) { |
||
3240 | $routinename = $_POST['pred_routinename']; |
||
3241 | } elseif (Core::isValid($_REQUEST['routinename'])) { |
||
3242 | $routinename = $_REQUEST['routinename']; |
||
3243 | } else { |
||
3244 | unset($routinename); |
||
3245 | } |
||
3246 | |||
3247 | if (isset($_POST['pred_dbname'])) { |
||
3248 | $is_valid_pred_dbname = true; |
||
3249 | foreach ($_POST['pred_dbname'] as $key => $db_name) { |
||
3250 | if (! Core::isValid($db_name)) { |
||
3251 | $is_valid_pred_dbname = false; |
||
3252 | break; |
||
3253 | } |
||
3254 | } |
||
3255 | } |
||
3256 | |||
3257 | if (isset($_REQUEST['dbname'])) { |
||
3258 | $is_valid_dbname = true; |
||
3259 | if (is_array($_REQUEST['dbname'])) { |
||
3260 | foreach ($_REQUEST['dbname'] as $key => $db_name) { |
||
3261 | if (! Core::isValid($db_name)) { |
||
3262 | $is_valid_dbname = false; |
||
3263 | break; |
||
3264 | } |
||
3265 | } |
||
3266 | } else { |
||
3267 | if (! Core::isValid($_REQUEST['dbname'])) { |
||
3268 | $is_valid_dbname = false; |
||
3269 | } |
||
3270 | } |
||
3271 | } |
||
3272 | |||
3273 | if (isset($is_valid_pred_dbname) && $is_valid_pred_dbname) { |
||
3274 | $dbname = $_POST['pred_dbname']; |
||
3275 | // If dbname contains only one database. |
||
3276 | if (count($dbname) === 1) { |
||
3277 | $dbname = $dbname[0]; |
||
3278 | } |
||
3279 | } elseif (isset($is_valid_dbname) && $is_valid_dbname) { |
||
3280 | $dbname = $_REQUEST['dbname']; |
||
3281 | } else { |
||
3282 | unset($dbname, $tablename); |
||
3283 | } |
||
3284 | |||
3285 | if (isset($dbname)) { |
||
3286 | if (is_array($dbname)) { |
||
3287 | $db_and_table = $dbname; |
||
3288 | foreach ($db_and_table as $key => $db_name) { |
||
3289 | $db_and_table[$key] .= '.'; |
||
3290 | } |
||
3291 | } else { |
||
3292 | $unescaped_db = Util::unescapeMysqlWildcards($dbname); |
||
3293 | $db_and_table = Util::backquote($unescaped_db) . '.'; |
||
3294 | } |
||
3295 | if (isset($tablename)) { |
||
3296 | $db_and_table .= Util::backquote($tablename); |
||
3297 | } else { |
||
3298 | if (is_array($db_and_table)) { |
||
3299 | foreach ($db_and_table as $key => $db_name) { |
||
3300 | $db_and_table[$key] .= '*'; |
||
3301 | } |
||
3302 | } else { |
||
3303 | $db_and_table .= '*'; |
||
3304 | } |
||
3305 | } |
||
3306 | } else { |
||
3307 | $db_and_table = '*.*'; |
||
3308 | } |
||
3309 | |||
3310 | // check if given $dbname is a wildcard or not |
||
3311 | if (isset($dbname)) { |
||
3312 | //if (preg_match('/\\\\(?:_|%)/i', $dbname)) { |
||
3313 | if (! is_array($dbname) && preg_match('/(?<!\\\\)(?:_|%)/', $dbname)) { |
||
3314 | $dbname_is_wildcard = true; |
||
3315 | } else { |
||
3316 | $dbname_is_wildcard = false; |
||
3317 | } |
||
3318 | } |
||
3319 | |||
3320 | return [ |
||
3321 | $username, |
||
3322 | $hostname, |
||
3323 | isset($dbname) ? $dbname : null, |
||
3324 | isset($tablename) ? $tablename : null, |
||
3325 | isset($routinename) ? $routinename : null, |
||
3326 | $db_and_table, |
||
3327 | $dbname_is_wildcard, |
||
3328 | ]; |
||
3329 | } |
||
3330 | |||
3331 | /** |
||
3332 | * Get title and textarea for export user definition in Privileges |
||
3333 | * |
||
3334 | * @param string $username username |
||
3335 | * @param string $hostname host name |
||
3336 | * |
||
3337 | * @return array ($title, $export) |
||
3338 | */ |
||
3339 | public function getListForExportUserDefinition($username, $hostname) |
||
3340 | { |
||
3341 | $export = '<textarea class="export" cols="60" rows="15">'; |
||
3342 | |||
3343 | if (isset($_POST['selected_usr'])) { |
||
3344 | // export privileges for selected users |
||
3345 | $title = __('Privileges'); |
||
3346 | |||
3347 | //For removing duplicate entries of users |
||
3348 | $_POST['selected_usr'] = array_unique($_POST['selected_usr']); |
||
3349 | |||
3350 | foreach ($_POST['selected_usr'] as $export_user) { |
||
3351 | $export_username = mb_substr( |
||
3352 | $export_user, |
||
3353 | 0, |
||
3354 | mb_strpos($export_user, '&') |
||
3355 | ); |
||
3356 | $export_hostname = mb_substr( |
||
3357 | $export_user, |
||
3358 | mb_strrpos($export_user, ';') + 1 |
||
3359 | ); |
||
3360 | $export .= '# ' |
||
3361 | . sprintf( |
||
3362 | __('Privileges for %s'), |
||
3363 | '`' . htmlspecialchars($export_username) |
||
3364 | . '`@`' . htmlspecialchars($export_hostname) . '`' |
||
3365 | ) |
||
3366 | . "\n\n"; |
||
3367 | $export .= $this->getGrants($export_username, $export_hostname) . "\n"; |
||
3368 | } |
||
3369 | } else { |
||
3370 | // export privileges for a single user |
||
3371 | $title = __('User') . ' `' . htmlspecialchars($username) |
||
3372 | . '`@`' . htmlspecialchars($hostname) . '`'; |
||
3373 | $export .= $this->getGrants($username, $hostname); |
||
3374 | } |
||
3375 | // remove trailing whitespace |
||
3376 | $export = trim($export); |
||
3377 | |||
3378 | $export .= '</textarea>'; |
||
3379 | |||
3380 | return [ |
||
3381 | $title, |
||
3382 | $export, |
||
3383 | ]; |
||
3384 | } |
||
3385 | |||
3386 | /** |
||
3387 | * Get HTML for display Add userfieldset |
||
3388 | * |
||
3389 | * @param string $db the database |
||
3390 | * @param string $table the table name |
||
3391 | * |
||
3392 | * @return string html output |
||
3393 | */ |
||
3394 | public function getAddUserHtmlFieldset($db = '', $table = '') |
||
3395 | { |
||
3396 | if (! $GLOBALS['is_createuser']) { |
||
3397 | return ''; |
||
3398 | } |
||
3399 | $rel_params = []; |
||
3400 | $url_params = [ |
||
3401 | 'adduser' => 1, |
||
3402 | ]; |
||
3403 | if (! empty($db)) { |
||
3404 | $url_params['dbname'] |
||
3405 | = $rel_params['checkprivsdb'] |
||
3406 | = $db; |
||
3407 | } |
||
3408 | if (! empty($table)) { |
||
3409 | $url_params['tablename'] |
||
3410 | = $rel_params['checkprivstable'] |
||
3411 | = $table; |
||
3412 | } |
||
3413 | |||
3414 | return $this->template->render('server/privileges/add_user_fieldset', [ |
||
3415 | 'url_params' => $url_params, |
||
3416 | 'rel_params' => $rel_params, |
||
3417 | ]); |
||
3418 | } |
||
3419 | |||
3420 | /** |
||
3421 | * Get HTML snippet for display user overview page |
||
3422 | * |
||
3423 | * @param string $pmaThemeImage a image source link |
||
3424 | * @param string $text_dir text directory |
||
3425 | * |
||
3426 | * @return string |
||
3427 | */ |
||
3428 | public function getHtmlForUserOverview($pmaThemeImage, $text_dir) |
||
3592 | ]); |
||
3593 | } |
||
3594 | |||
3595 | /** |
||
3596 | * Get HTML snippet for display user properties |
||
3597 | * |
||
3598 | * @param boolean $dbname_is_wildcard whether database name is wildcard or not |
||
3599 | * @param string $url_dbname url database name that urlencode() string |
||
3600 | * @param string $username username |
||
3601 | * @param string $hostname host name |
||
3602 | * @param string|array $dbname database name |
||
3603 | * @param string $tablename table name |
||
3604 | * |
||
3605 | * @return string |
||
3606 | */ |
||
3607 | public function getHtmlForUserProperties( |
||
3710 | ]); |
||
3711 | } |
||
3712 | |||
3713 | /** |
||
3714 | * Get queries for Table privileges to change or copy user |
||
3715 | * |
||
3716 | * @param string $user_host_condition user host condition to |
||
3717 | * select relevant table privileges |
||
3718 | * @param array $queries queries array |
||
3719 | * @param string $username username |
||
3720 | * @param string $hostname host name |
||
3721 | * |
||
3722 | * @return array |
||
3723 | */ |
||
3724 | public function getTablePrivsQueriesForChangeOrCopyUser( |
||
3725 | $user_host_condition, |
||
3726 | array $queries, |
||
3727 | $username, |
||
3728 | $hostname |
||
3729 | ) { |
||
3730 | $res = $this->dbi->query( |
||
3731 | 'SELECT `Db`, `Table_name`, `Table_priv` FROM `mysql`.`tables_priv`' |
||
3732 | . $user_host_condition, |
||
3733 | DatabaseInterface::CONNECT_USER, |
||
3734 | DatabaseInterface::QUERY_STORE |
||
3735 | ); |
||
3736 | while ($row = $this->dbi->fetchAssoc($res)) { |
||
3737 | $res2 = $this->dbi->query( |
||
3738 | 'SELECT `Column_name`, `Column_priv`' |
||
3739 | . ' FROM `mysql`.`columns_priv`' |
||
3740 | . ' WHERE `User`' |
||
3741 | . ' = \'' . $this->dbi->escapeString($_POST['old_username']) . "'" |
||
3742 | . ' AND `Host`' |
||
3743 | . ' = \'' . $this->dbi->escapeString($_POST['old_username']) . '\'' |
||
3744 | . ' AND `Db`' |
||
3745 | . ' = \'' . $this->dbi->escapeString($row['Db']) . "'" |
||
3746 | . ' AND `Table_name`' |
||
3747 | . ' = \'' . $this->dbi->escapeString($row['Table_name']) . "'" |
||
3748 | . ';', |
||
3749 | DatabaseInterface::CONNECT_USER, |
||
3750 | DatabaseInterface::QUERY_STORE |
||
3751 | ); |
||
3752 | |||
3753 | $tmp_privs1 = $this->extractPrivInfo($row); |
||
3754 | $tmp_privs2 = [ |
||
3755 | 'Select' => [], |
||
3756 | 'Insert' => [], |
||
3757 | 'Update' => [], |
||
3758 | 'References' => [], |
||
3759 | ]; |
||
3760 | |||
3761 | while ($row2 = $this->dbi->fetchAssoc($res2)) { |
||
3762 | $tmp_array = explode(',', $row2['Column_priv']); |
||
3763 | if (in_array('Select', $tmp_array)) { |
||
3764 | $tmp_privs2['Select'][] = $row2['Column_name']; |
||
3765 | } |
||
3766 | if (in_array('Insert', $tmp_array)) { |
||
3767 | $tmp_privs2['Insert'][] = $row2['Column_name']; |
||
3768 | } |
||
3769 | if (in_array('Update', $tmp_array)) { |
||
3770 | $tmp_privs2['Update'][] = $row2['Column_name']; |
||
3771 | } |
||
3772 | if (in_array('References', $tmp_array)) { |
||
3773 | $tmp_privs2['References'][] = $row2['Column_name']; |
||
3774 | } |
||
3775 | } |
||
3776 | if (count($tmp_privs2['Select']) > 0 && ! in_array('SELECT', $tmp_privs1)) { |
||
3777 | $tmp_privs1[] = 'SELECT (`' . implode('`, `', $tmp_privs2['Select']) . '`)'; |
||
3778 | } |
||
3779 | if (count($tmp_privs2['Insert']) > 0 && ! in_array('INSERT', $tmp_privs1)) { |
||
3780 | $tmp_privs1[] = 'INSERT (`' . implode('`, `', $tmp_privs2['Insert']) . '`)'; |
||
3781 | } |
||
3782 | if (count($tmp_privs2['Update']) > 0 && ! in_array('UPDATE', $tmp_privs1)) { |
||
3783 | $tmp_privs1[] = 'UPDATE (`' . implode('`, `', $tmp_privs2['Update']) . '`)'; |
||
3784 | } |
||
3785 | if (count($tmp_privs2['References']) > 0 |
||
3786 | && ! in_array('REFERENCES', $tmp_privs1) |
||
3787 | ) { |
||
3788 | $tmp_privs1[] |
||
3789 | = 'REFERENCES (`' . implode('`, `', $tmp_privs2['References']) . '`)'; |
||
3790 | } |
||
3791 | |||
3792 | $queries[] = 'GRANT ' . implode(', ', $tmp_privs1) |
||
3793 | . ' ON ' . Util::backquote($row['Db']) . '.' |
||
3794 | . Util::backquote($row['Table_name']) |
||
3795 | . ' TO \'' . $this->dbi->escapeString($username) |
||
3796 | . '\'@\'' . $this->dbi->escapeString($hostname) . '\'' |
||
3797 | . (in_array('Grant', explode(',', $row['Table_priv'])) |
||
3798 | ? ' WITH GRANT OPTION;' |
||
3799 | : ';'); |
||
3800 | } |
||
3801 | return $queries; |
||
3802 | } |
||
3803 | |||
3804 | /** |
||
3805 | * Get queries for database specific privileges for change or copy user |
||
3806 | * |
||
3807 | * @param array $queries queries array with string |
||
3808 | * @param string $username username |
||
3809 | * @param string $hostname host name |
||
3810 | * |
||
3811 | * @return array |
||
3812 | */ |
||
3813 | public function getDbSpecificPrivsQueriesForChangeOrCopyUser( |
||
3814 | array $queries, |
||
3815 | $username, |
||
3816 | $hostname |
||
3817 | ) { |
||
3818 | $user_host_condition = ' WHERE `User`' |
||
3819 | . ' = \'' . $this->dbi->escapeString($_POST['old_username']) . "'" |
||
3820 | . ' AND `Host`' |
||
3821 | . ' = \'' . $this->dbi->escapeString($_POST['old_hostname']) . '\';'; |
||
3822 | |||
3823 | $res = $this->dbi->query( |
||
3824 | 'SELECT * FROM `mysql`.`db`' . $user_host_condition |
||
3825 | ); |
||
3826 | |||
3827 | while ($row = $this->dbi->fetchAssoc($res)) { |
||
3828 | $queries[] = 'GRANT ' . implode(', ', $this->extractPrivInfo($row)) |
||
3829 | . ' ON ' . Util::backquote($row['Db']) . '.*' |
||
3830 | . ' TO \'' . $this->dbi->escapeString($username) |
||
3831 | . '\'@\'' . $this->dbi->escapeString($hostname) . '\'' |
||
3832 | . ($row['Grant_priv'] == 'Y' ? ' WITH GRANT OPTION;' : ';'); |
||
3833 | } |
||
3834 | $this->dbi->freeResult($res); |
||
3835 | |||
3836 | $queries = $this->getTablePrivsQueriesForChangeOrCopyUser( |
||
3837 | $user_host_condition, |
||
3838 | $queries, |
||
3839 | $username, |
||
3840 | $hostname |
||
3841 | ); |
||
3842 | |||
3843 | return $queries; |
||
3844 | } |
||
3845 | |||
3846 | /** |
||
3847 | * Prepares queries for adding users and |
||
3848 | * also create database and return query and message |
||
3849 | * |
||
3850 | * @param boolean $_error whether user create or not |
||
3851 | * @param string $real_sql_query SQL query for add a user |
||
3852 | * @param string $sql_query SQL query to be displayed |
||
3853 | * @param string $username username |
||
3854 | * @param string $hostname host name |
||
3855 | * @param string $dbname database name |
||
3856 | * @param string $alter_real_sql_query SQL query for ALTER USER |
||
3857 | * @param string $alter_sql_query SQL query for ALTER USER to be displayed |
||
3858 | * |
||
3859 | * @return array, $message |
||
3860 | */ |
||
3861 | public function addUserAndCreateDatabase( |
||
3862 | $_error, |
||
3863 | $real_sql_query, |
||
3864 | $sql_query, |
||
3865 | $username, |
||
3866 | $hostname, |
||
3867 | $dbname, |
||
3868 | $alter_real_sql_query, |
||
3869 | $alter_sql_query |
||
3870 | ) { |
||
3871 | if ($_error || (! empty($real_sql_query) |
||
3872 | && ! $this->dbi->tryQuery($real_sql_query)) |
||
3873 | ) { |
||
3874 | $_POST['createdb-1'] = $_POST['createdb-2'] |
||
3875 | = $_POST['createdb-3'] = null; |
||
3876 | $message = Message::rawError($this->dbi->getError()); |
||
3877 | } elseif ($alter_real_sql_query !== '' && ! $this->dbi->tryQuery($alter_real_sql_query)) { |
||
3878 | $_POST['createdb-1'] = $_POST['createdb-2'] |
||
3879 | = $_POST['createdb-3'] = null; |
||
3880 | $message = Message::rawError($this->dbi->getError()); |
||
3881 | } else { |
||
3882 | $sql_query .= $alter_sql_query; |
||
3883 | $message = Message::success(__('You have added a new user.')); |
||
3884 | } |
||
3885 | |||
3886 | if (isset($_POST['createdb-1'])) { |
||
3887 | // Create database with same name and grant all privileges |
||
3888 | $q = 'CREATE DATABASE IF NOT EXISTS ' |
||
3889 | . Util::backquote( |
||
3890 | $this->dbi->escapeString($username) |
||
3891 | ) . ';'; |
||
3892 | $sql_query .= $q; |
||
3893 | if (! $this->dbi->tryQuery($q)) { |
||
3894 | $message = Message::rawError($this->dbi->getError()); |
||
3895 | } |
||
3896 | |||
3897 | /** |
||
3898 | * Reload the navigation |
||
3899 | */ |
||
3900 | $GLOBALS['reload'] = true; |
||
3901 | $GLOBALS['db'] = $username; |
||
3902 | |||
3903 | $q = 'GRANT ALL PRIVILEGES ON ' |
||
3904 | . Util::backquote( |
||
3905 | Util::escapeMysqlWildcards( |
||
3906 | $this->dbi->escapeString($username) |
||
3907 | ) |
||
3908 | ) . '.* TO \'' |
||
3909 | . $this->dbi->escapeString($username) |
||
3910 | . '\'@\'' . $this->dbi->escapeString($hostname) . '\';'; |
||
3911 | $sql_query .= $q; |
||
3912 | if (! $this->dbi->tryQuery($q)) { |
||
3913 | $message = Message::rawError($this->dbi->getError()); |
||
3914 | } |
||
3915 | } |
||
3916 | |||
3917 | if (isset($_POST['createdb-2'])) { |
||
3918 | // Grant all privileges on wildcard name (username\_%) |
||
3919 | $q = 'GRANT ALL PRIVILEGES ON ' |
||
3920 | . Util::backquote( |
||
3921 | Util::escapeMysqlWildcards( |
||
3922 | $this->dbi->escapeString($username) |
||
3923 | ) . '\_%' |
||
3924 | ) . '.* TO \'' |
||
3925 | . $this->dbi->escapeString($username) |
||
3926 | . '\'@\'' . $this->dbi->escapeString($hostname) . '\';'; |
||
3927 | $sql_query .= $q; |
||
3928 | if (! $this->dbi->tryQuery($q)) { |
||
3929 | $message = Message::rawError($this->dbi->getError()); |
||
3930 | } |
||
3931 | } |
||
3932 | |||
3933 | if (isset($_POST['createdb-3'])) { |
||
3934 | // Grant all privileges on the specified database to the new user |
||
3935 | $q = 'GRANT ALL PRIVILEGES ON ' |
||
3936 | . Util::backquote( |
||
3937 | $this->dbi->escapeString($dbname) |
||
3938 | ) . '.* TO \'' |
||
3939 | . $this->dbi->escapeString($username) |
||
3940 | . '\'@\'' . $this->dbi->escapeString($hostname) . '\';'; |
||
3941 | $sql_query .= $q; |
||
3942 | if (! $this->dbi->tryQuery($q)) { |
||
3943 | $message = Message::rawError($this->dbi->getError()); |
||
3944 | } |
||
3945 | } |
||
3946 | return [ |
||
3947 | $sql_query, |
||
3948 | $message, |
||
3949 | ]; |
||
3950 | } |
||
3951 | |||
3952 | /** |
||
3953 | * Get the hashed string for password |
||
3954 | * |
||
3955 | * @param string $password password |
||
3956 | * |
||
3957 | * @return string |
||
3958 | */ |
||
3959 | public function getHashedPassword($password) |
||
3960 | { |
||
3961 | $password = $this->dbi->escapeString($password); |
||
3962 | $result = $this->dbi->fetchSingleRow( |
||
3963 | "SELECT PASSWORD('" . $password . "') AS `password`;" |
||
3964 | ); |
||
3965 | |||
3966 | return $result['password']; |
||
3967 | } |
||
3968 | |||
3969 | /** |
||
3970 | * Check if MariaDB's 'simple_password_check' |
||
3971 | * OR 'cracklib_password_check' is ACTIVE |
||
3972 | * |
||
3973 | * @return boolean if atleast one of the plugins is ACTIVE |
||
3974 | */ |
||
3975 | public function checkIfMariaDBPwdCheckPluginActive() |
||
3976 | { |
||
3977 | $serverVersion = $this->dbi->getVersion(); |
||
3978 | if (! (Util::getServerType() == 'MariaDB' && $serverVersion >= 100002)) { |
||
3979 | return false; |
||
3980 | } |
||
3981 | |||
3982 | $result = $this->dbi->tryQuery( |
||
3983 | 'SHOW PLUGINS SONAME LIKE \'%_password_check%\'' |
||
3984 | ); |
||
3985 | |||
3986 | /* Plugins are not working, for example directory does not exists */ |
||
3987 | if ($result === false) { |
||
3988 | return false; |
||
3989 | } |
||
3990 | |||
3991 | while ($row = $this->dbi->fetchAssoc($result)) { |
||
3992 | if ($row['Status'] === 'ACTIVE') { |
||
3993 | return true; |
||
3994 | } |
||
3995 | } |
||
3996 | |||
3997 | return false; |
||
3998 | } |
||
3999 | |||
4000 | |||
4001 | /** |
||
4002 | * Get SQL queries for Display and Add user |
||
4003 | * |
||
4004 | * @param string $username username |
||
4005 | * @param string $hostname host name |
||
4006 | * @param string $password password |
||
4007 | * |
||
4008 | * @return array ($create_user_real, $create_user_show, $real_sql_query, $sql_query |
||
4009 | * $password_set_real, $password_set_show, $alter_real_sql_query, $alter_sql_query) |
||
4010 | */ |
||
4011 | public function getSqlQueriesForDisplayAndAddUser($username, $hostname, $password) |
||
4247 | ]; |
||
4248 | } |
||
4249 | |||
4250 | /** |
||
4251 | * Returns the type ('PROCEDURE' or 'FUNCTION') of the routine |
||
4252 | * |
||
4253 | * @param string $dbname database |
||
4254 | * @param string $routineName routine |
||
4255 | * |
||
4256 | * @return string type |
||
4257 | */ |
||
4258 | public function getRoutineType($dbname, $routineName) |
||
4259 | { |
||
4260 | $routineData = $this->dbi->getRoutines($dbname); |
||
4261 | |||
4262 | foreach ($routineData as $routine) { |
||
4263 | if ($routine['name'] === $routineName) { |
||
4264 | return $routine['type']; |
||
4265 | } |
||
4266 | } |
||
4267 | return ''; |
||
4268 | } |
||
4269 | |||
4270 | /** |
||
4271 | * @param string $username User name |
||
4272 | * @param string $hostname Host name |
||
4273 | * @param string $database Database name |
||
4274 | * @param string $routine Routine name |
||
4275 | * |
||
4276 | * @return array |
||
4277 | */ |
||
4278 | private function getRoutinePrivileges( |
||
4296 | } |
||
4297 | } |
||
4298 |
In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.