PrivilegesController.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. <?php
  2. declare(strict_types=1);
  3. namespace PhpMyAdmin\Controllers\Server;
  4. use PhpMyAdmin\CheckUserPrivileges;
  5. use PhpMyAdmin\Controllers\AbstractController;
  6. use PhpMyAdmin\Controllers\Database\PrivilegesController as DatabaseController;
  7. use PhpMyAdmin\Controllers\Table\PrivilegesController as TableController;
  8. use PhpMyAdmin\Core;
  9. use PhpMyAdmin\DatabaseInterface;
  10. use PhpMyAdmin\Html\Generator;
  11. use PhpMyAdmin\Message;
  12. use PhpMyAdmin\Relation;
  13. use PhpMyAdmin\RelationCleanup;
  14. use PhpMyAdmin\Response;
  15. use PhpMyAdmin\Server\Privileges;
  16. use PhpMyAdmin\Template;
  17. use PhpMyAdmin\Url;
  18. use PhpMyAdmin\Util;
  19. use function header;
  20. use function implode;
  21. use function is_array;
  22. use function ob_get_clean;
  23. use function ob_start;
  24. use function str_replace;
  25. use function urlencode;
  26. /**
  27. * Server privileges and users manipulations.
  28. */
  29. class PrivilegesController extends AbstractController
  30. {
  31. /** @var Relation */
  32. private $relation;
  33. /** @var DatabaseInterface */
  34. private $dbi;
  35. /**
  36. * @param Response $response
  37. * @param DatabaseInterface $dbi
  38. */
  39. public function __construct($response, Template $template, Relation $relation, $dbi)
  40. {
  41. parent::__construct($response, $template);
  42. $this->relation = $relation;
  43. $this->dbi = $dbi;
  44. }
  45. public function index(): void
  46. {
  47. global $db, $table, $err_url, $message, $text_dir, $post_patterns, $PMA_Theme;
  48. global $username, $hostname, $dbname, $tablename, $routinename, $db_and_table, $dbname_is_wildcard;
  49. global $queries, $password, $ret_message, $ret_queries, $queries_for_display, $sql_query, $_add_user_error;
  50. global $itemType, $tables, $num_tables, $total_num_tables, $sub_part;
  51. global $tooltip_truename, $tooltip_aliasname, $pos, $title, $export, $grants, $one_grant, $url_dbname;
  52. $checkUserPrivileges = new CheckUserPrivileges($this->dbi);
  53. $checkUserPrivileges->getPrivileges();
  54. $cfgRelation = $this->relation->getRelationsParam();
  55. $this->addScriptFiles(['server/privileges.js', 'vendor/zxcvbn.js']);
  56. $relationCleanup = new RelationCleanup($this->dbi, $this->relation);
  57. $serverPrivileges = new Privileges($this->template, $this->dbi, $this->relation, $relationCleanup);
  58. $databaseController = new DatabaseController(
  59. $this->response,
  60. $this->template,
  61. $db,
  62. $serverPrivileges,
  63. $this->dbi
  64. );
  65. $tableController = new TableController(
  66. $this->response,
  67. $this->template,
  68. $db,
  69. $table,
  70. $serverPrivileges,
  71. $this->dbi
  72. );
  73. if ((isset($_GET['viewing_mode'])
  74. && $_GET['viewing_mode'] === 'server')
  75. && $GLOBALS['cfgRelation']['menuswork']
  76. ) {
  77. $this->response->addHTML('<div class="container-fluid">');
  78. $this->render('server/privileges/subnav', [
  79. 'active' => 'privileges',
  80. 'is_super_user' => $this->dbi->isSuperUser(),
  81. ]);
  82. }
  83. /**
  84. * Sets globals from $_POST patterns, for privileges and max_* vars
  85. */
  86. $post_patterns = [
  87. '/_priv$/i',
  88. '/^max_/i',
  89. ];
  90. Core::setPostAsGlobal($post_patterns);
  91. $err_url = Url::getFromRoute('/');
  92. if ($this->dbi->isSuperUser()) {
  93. $this->dbi->selectDb('mysql');
  94. }
  95. $_add_user_error = false;
  96. /**
  97. * Get DB information: username, hostname, dbname,
  98. * tablename, db_and_table, dbname_is_wildcard
  99. */
  100. [
  101. $username,
  102. $hostname,
  103. $dbname,
  104. $tablename,
  105. $routinename,
  106. $db_and_table,
  107. $dbname_is_wildcard,
  108. ] = $serverPrivileges->getDataForDBInfo();
  109. /**
  110. * Checks if the user is allowed to do what they try to...
  111. */
  112. $isGrantUser = $this->dbi->isGrantUser();
  113. $isCreateUser = $this->dbi->isCreateUser();
  114. if (! $this->dbi->isSuperUser() && ! $isGrantUser && ! $isCreateUser) {
  115. $this->render('server/sub_page_header', [
  116. 'type' => 'privileges',
  117. 'is_image' => false,
  118. ]);
  119. $this->response->addHTML(
  120. Message::error(__('No Privileges'))
  121. ->getDisplay()
  122. );
  123. return;
  124. }
  125. if (! $isGrantUser && ! $isCreateUser) {
  126. $this->response->addHTML(Message::notice(
  127. __('You do not have the privileges to administrate the users!')
  128. )->getDisplay());
  129. }
  130. /**
  131. * Checks if the user is using "Change Login Information / Copy User" dialog
  132. * only to update the password
  133. */
  134. if (isset($_POST['change_copy']) && $username == $_POST['old_username']
  135. && $hostname == $_POST['old_hostname']
  136. ) {
  137. $this->response->addHTML(
  138. Message::error(
  139. __(
  140. "Username and hostname didn't change. "
  141. . 'If you only want to change the password, '
  142. . "'Change password' tab should be used."
  143. )
  144. )->getDisplay()
  145. );
  146. $this->response->setRequestStatus(false);
  147. return;
  148. }
  149. /**
  150. * Changes / copies a user, part I
  151. */
  152. [$queries, $password] = $serverPrivileges->getDataForChangeOrCopyUser();
  153. /**
  154. * Adds a user
  155. * (Changes / copies a user, part II)
  156. */
  157. [
  158. $ret_message,
  159. $ret_queries,
  160. $queries_for_display,
  161. $sql_query,
  162. $_add_user_error,
  163. ] = $serverPrivileges->addUser(
  164. $dbname ?? null,
  165. $username ?? null,
  166. $hostname ?? null,
  167. $password ?? null,
  168. (bool) $cfgRelation['menuswork']
  169. );
  170. //update the old variables
  171. if (isset($ret_queries)) {
  172. $queries = $ret_queries;
  173. unset($ret_queries);
  174. }
  175. if (isset($ret_message)) {
  176. $message = $ret_message;
  177. unset($ret_message);
  178. }
  179. /**
  180. * Changes / copies a user, part III
  181. */
  182. if (isset($_POST['change_copy'])) {
  183. $queries = $serverPrivileges->getDbSpecificPrivsQueriesForChangeOrCopyUser(
  184. $queries,
  185. $username,
  186. $hostname
  187. );
  188. }
  189. $itemType = '';
  190. if (! empty($routinename)) {
  191. $itemType = $serverPrivileges->getRoutineType($dbname, $routinename);
  192. }
  193. /**
  194. * Updates privileges
  195. */
  196. if (! empty($_POST['update_privs'])) {
  197. if (is_array($dbname)) {
  198. foreach ($dbname as $key => $db_name) {
  199. [$sql_query[$key], $message] = $serverPrivileges->updatePrivileges(
  200. ($username ?? ''),
  201. ($hostname ?? ''),
  202. ($tablename ?? ($routinename ?? '')),
  203. ($db_name ?? ''),
  204. $itemType
  205. );
  206. }
  207. $sql_query = implode("\n", $sql_query);
  208. } else {
  209. [$sql_query, $message] = $serverPrivileges->updatePrivileges(
  210. ($username ?? ''),
  211. ($hostname ?? ''),
  212. ($tablename ?? ($routinename ?? '')),
  213. ($dbname ?? ''),
  214. $itemType
  215. );
  216. }
  217. }
  218. /**
  219. * Assign users to user groups
  220. */
  221. if (! empty($_POST['changeUserGroup']) && $cfgRelation['menuswork']
  222. && $this->dbi->isSuperUser() && $this->dbi->isCreateUser()
  223. ) {
  224. $serverPrivileges->setUserGroup($username, $_POST['userGroup']);
  225. $message = Message::success();
  226. }
  227. /**
  228. * Revokes Privileges
  229. */
  230. if (isset($_POST['revokeall'])) {
  231. [$message, $sql_query] = $serverPrivileges->getMessageAndSqlQueryForPrivilegesRevoke(
  232. ($dbname ?? ''),
  233. ($tablename ?? ($routinename ?? '')),
  234. $username,
  235. $hostname,
  236. $itemType
  237. );
  238. }
  239. /**
  240. * Updates the password
  241. */
  242. if (isset($_POST['change_pw'])) {
  243. $message = $serverPrivileges->updatePassword(
  244. $err_url,
  245. $username,
  246. $hostname
  247. );
  248. }
  249. /**
  250. * Deletes users
  251. * (Changes / copies a user, part IV)
  252. */
  253. if (isset($_POST['delete'])
  254. || (isset($_POST['change_copy']) && $_POST['mode'] < 4)
  255. ) {
  256. $queries = $serverPrivileges->getDataForDeleteUsers($queries);
  257. if (empty($_POST['change_copy'])) {
  258. [$sql_query, $message] = $serverPrivileges->deleteUser($queries);
  259. }
  260. }
  261. /**
  262. * Changes / copies a user, part V
  263. */
  264. if (isset($_POST['change_copy'])) {
  265. $queries = $serverPrivileges->getDataForQueries($queries, $queries_for_display);
  266. $message = Message::success();
  267. $sql_query = implode("\n", $queries);
  268. }
  269. /**
  270. * Reloads the privilege tables into memory
  271. */
  272. $message_ret = $serverPrivileges->updateMessageForReload();
  273. if ($message_ret !== null) {
  274. $message = $message_ret;
  275. unset($message_ret);
  276. }
  277. /**
  278. * If we are in an Ajax request for Create User/Edit User/Revoke User/
  279. * Flush Privileges, show $message and return.
  280. */
  281. if ($this->response->isAjax()
  282. && empty($_REQUEST['ajax_page_request'])
  283. && ! isset($_GET['export'])
  284. && (! isset($_POST['submit_mult']) || $_POST['submit_mult'] !== 'export')
  285. && ((! isset($_GET['initial']) || $_GET['initial'] === null
  286. || $_GET['initial'] === '')
  287. || (isset($_POST['delete']) && $_POST['delete'] === __('Go')))
  288. && ! isset($_GET['showall'])
  289. && ! isset($_GET['edit_user_group_dialog'])
  290. ) {
  291. $extra_data = $serverPrivileges->getExtraDataForAjaxBehavior(
  292. ($password ?? ''),
  293. ($sql_query ?? ''),
  294. ($hostname ?? ''),
  295. ($username ?? '')
  296. );
  297. if (! empty($message) && $message instanceof Message) {
  298. $this->response->setRequestStatus($message->isSuccess());
  299. $this->response->addJSON('message', $message);
  300. $this->response->addJSON($extra_data);
  301. return;
  302. }
  303. }
  304. /**
  305. * Displays the links
  306. */
  307. if (isset($_GET['viewing_mode']) && $_GET['viewing_mode'] === 'db') {
  308. $db = $_REQUEST['db'] = $_GET['checkprivsdb'];
  309. // Gets the database structure
  310. $sub_part = '_structure';
  311. ob_start();
  312. [
  313. $tables,
  314. $num_tables,
  315. $total_num_tables,
  316. $sub_part,,,
  317. $tooltip_truename,
  318. $tooltip_aliasname,
  319. $pos,
  320. ] = Util::getDbInfo($db, $sub_part ?? '');
  321. $content = ob_get_clean();
  322. $this->response->addHTML($content . "\n");
  323. } elseif (! empty($GLOBALS['message'])) {
  324. $this->response->addHTML(Generator::getMessage($GLOBALS['message']));
  325. unset($GLOBALS['message']);
  326. }
  327. if (! empty($_GET['edit_user_group_dialog']) && $cfgRelation['menuswork']) {
  328. $dialog = $serverPrivileges->getHtmlToChooseUserGroup($username ?? null);
  329. if ($this->response->isAjax()) {
  330. $this->response->addJSON('message', $dialog);
  331. return;
  332. }
  333. $this->response->addHTML($dialog);
  334. }
  335. // export user definition
  336. if (isset($_GET['export'])
  337. || (isset($_POST['submit_mult']) && $_POST['submit_mult'] === 'export')
  338. ) {
  339. [$title, $export] = $serverPrivileges->getListForExportUserDefinition(
  340. $username ?? '',
  341. $hostname ?? ''
  342. );
  343. unset($username, $hostname, $grants, $one_grant);
  344. if ($this->response->isAjax()) {
  345. $this->response->addJSON('message', $export);
  346. $this->response->addJSON('title', $title);
  347. return;
  348. }
  349. $this->response->addHTML('<h2>' . $title . '</h2>' . $export);
  350. }
  351. // Show back the form if an error occurred
  352. if (isset($_GET['adduser']) || $_add_user_error === true) {
  353. // Add user
  354. $this->response->addHTML(
  355. $serverPrivileges->getHtmlForAddUser(($dbname ?? ''))
  356. );
  357. } elseif (isset($_GET['checkprivsdb'])) {
  358. if (isset($_GET['checkprivstable'])) {
  359. $this->response->addHTML($tableController->index([
  360. 'checkprivsdb' => $_GET['checkprivsdb'],
  361. 'checkprivstable' => $_GET['checkprivstable'],
  362. ]));
  363. } elseif ($this->response->isAjax() === true && empty($_REQUEST['ajax_page_request'])) {
  364. $message = Message::success(__('User has been added.'));
  365. $this->response->addJSON('message', $message);
  366. return;
  367. } else {
  368. $this->response->addHTML($databaseController->index([
  369. 'checkprivsdb' => $_GET['checkprivsdb'],
  370. ]));
  371. }
  372. } else {
  373. if (isset($dbname) && ! is_array($dbname)) {
  374. $url_dbname = urlencode(
  375. str_replace(
  376. [
  377. '\_',
  378. '\%',
  379. ],
  380. [
  381. '_',
  382. '%',
  383. ],
  384. $dbname
  385. )
  386. );
  387. }
  388. if (! isset($username)) {
  389. // No username is given --> display the overview
  390. $this->response->addHTML(
  391. $serverPrivileges->getHtmlForUserOverview($PMA_Theme->getImgPath(), $text_dir)
  392. );
  393. } elseif (! empty($routinename)) {
  394. $this->response->addHTML(
  395. $serverPrivileges->getHtmlForRoutineSpecificPrivileges(
  396. $username,
  397. $hostname ?? '',
  398. $dbname,
  399. $routinename,
  400. $url_dbname ?? ''
  401. )
  402. );
  403. } else {
  404. // A user was selected -> display the user's properties
  405. // In an Ajax request, prevent cached values from showing
  406. if ($this->response->isAjax()) {
  407. header('Cache-Control: no-cache');
  408. }
  409. $this->response->addHTML(
  410. $serverPrivileges->getHtmlForUserProperties(
  411. $dbname_is_wildcard,
  412. $url_dbname ?? '',
  413. $username,
  414. $hostname ?? '',
  415. $dbname ?? '',
  416. $tablename ?? ''
  417. )
  418. );
  419. }
  420. }
  421. if ((! isset($_GET['viewing_mode']) || $_GET['viewing_mode'] !== 'server')
  422. || ! $cfgRelation['menuswork']
  423. ) {
  424. return;
  425. }
  426. $this->response->addHTML('</div>');
  427. }
  428. }