UserController.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. <?php
  2. namespace App\Controllers;
  3. use App\Exceptions\UnauthorizedException;
  4. use Slim\Exception\NotFoundException;
  5. use Slim\Http\Request;
  6. use Slim\Http\Response;
  7. class UserController extends Controller
  8. {
  9. const PER_PAGE = 15;
  10. /**
  11. * @param Request $request
  12. * @param Response $response
  13. * @param $args
  14. * @return Response
  15. */
  16. public function index(Request $request, Response $response, $args): Response
  17. {
  18. $page = isset($args['page']) ? (int)$args['page'] : 0;
  19. $page = max(0, --$page);
  20. $users = $this->database->query('SELECT * FROM `users` LIMIT ? OFFSET ?', [self::PER_PAGE, $page * self::PER_PAGE])->fetchAll();
  21. $pages = $this->database->query('SELECT COUNT(*) AS `count` FROM `users`')->fetch()->count / self::PER_PAGE;
  22. return $this->view->render($response,
  23. 'user/index.twig',
  24. [
  25. 'users' => $users,
  26. 'next' => $page < floor($pages),
  27. 'previous' => $page >= 1,
  28. 'current_page' => ++$page,
  29. ]
  30. );
  31. }
  32. /**
  33. * @param Request $request
  34. * @param Response $response
  35. * @return Response
  36. */
  37. public function create(Request $request, Response $response): Response
  38. {
  39. return $this->view->render($response, 'user/create.twig');
  40. }
  41. /**
  42. * @param Request $request
  43. * @param Response $response
  44. * @return Response
  45. */
  46. public function store(Request $request, Response $response): Response
  47. {
  48. if ($request->getParam('email') === null) {
  49. $this->session->alert(lang('email_required'), 'danger');
  50. return redirect($response, 'user.create');
  51. }
  52. if ($this->database->query('SELECT COUNT(*) AS `count` FROM `users` WHERE `email` = ?', $request->getParam('email'))->fetch()->count > 0) {
  53. $this->session->alert(lang('email_taken'), 'danger');
  54. return redirect($response, 'user.create');
  55. }
  56. if ($request->getParam('username') === null) {
  57. $this->session->alert(lang('username_required'), 'danger');
  58. return redirect($response, 'user.create');
  59. }
  60. if ($request->getParam('password') === null) {
  61. $this->session->alert(lang('password_required'), 'danger');
  62. return redirect($response, 'user.create');
  63. }
  64. if ($this->database->query('SELECT COUNT(*) AS `count` FROM `users` WHERE `username` = ?', $request->getParam('username'))->fetch()->count > 0) {
  65. $this->session->alert(lang('username_taken'), 'danger');
  66. return redirect($response, 'user.create');
  67. }
  68. do {
  69. $userCode = humanRandomString(5);
  70. } while ($this->database->query('SELECT COUNT(*) AS `count` FROM `users` WHERE `user_code` = ?', $userCode)->fetch()->count > 0);
  71. $token = $this->generateNewToken();
  72. $this->database->query('INSERT INTO `users`(`email`, `username`, `password`, `is_admin`, `active`, `user_code`, `token`) VALUES (?, ?, ?, ?, ?, ?, ?)', [
  73. $request->getParam('email'),
  74. $request->getParam('username'),
  75. password_hash($request->getParam('password'), PASSWORD_DEFAULT),
  76. $request->getParam('is_admin') !== null ? 1 : 0,
  77. $request->getParam('is_active') !== null ? 1 : 0,
  78. $userCode,
  79. $token,
  80. ]);
  81. $this->session->alert(lang('user_created', [$request->getParam('username')]), 'success');
  82. $this->logger->info('User ' . $this->session->get('username') . ' created a new user.', [array_diff_key($request->getParams(), array_flip(['password']))]);
  83. return redirect($response, 'user.index');
  84. }
  85. /**
  86. * @param Request $request
  87. * @param Response $response
  88. * @param $args
  89. * @return Response
  90. * @throws NotFoundException
  91. */
  92. public function edit(Request $request, Response $response, $args): Response
  93. {
  94. $user = $this->database->query('SELECT * FROM `users` WHERE `id` = ? LIMIT 1', $args['id'])->fetch();
  95. if (!$user) {
  96. throw new NotFoundException($request, $response);
  97. }
  98. return $this->view->render($response, 'user/edit.twig', [
  99. 'profile' => false,
  100. 'user' => $user,
  101. ]);
  102. }
  103. /**
  104. * @param Request $request
  105. * @param Response $response
  106. * @param $args
  107. * @return Response
  108. * @throws NotFoundException
  109. */
  110. public function update(Request $request, Response $response, $args): Response
  111. {
  112. $user = $this->database->query('SELECT * FROM `users` WHERE `id` = ? LIMIT 1', $args['id'])->fetch();
  113. if (!$user) {
  114. throw new NotFoundException($request, $response);
  115. }
  116. if ($request->getParam('email') === null) {
  117. $this->session->alert(lang('email_required'), 'danger');
  118. return redirect($response, 'user.edit', ['id' => $args['id']]);
  119. }
  120. if ($this->database->query('SELECT COUNT(*) AS `count` FROM `users` WHERE `email` = ? AND `email` <> ?', [$request->getParam('email'), $user->email])->fetch()->count > 0) {
  121. $this->session->alert(lang('email_taken'), 'danger');
  122. return redirect($response, 'user.edit', ['id' => $args['id']]);
  123. }
  124. if ($request->getParam('username') === null) {
  125. $this->session->alert(lang('username_required'), 'danger');
  126. return redirect($response, 'user.edit', ['id' => $args['id']]);
  127. }
  128. if ($this->database->query('SELECT COUNT(*) AS `count` FROM `users` WHERE `username` = ? AND `username` <> ?', [$request->getParam('username'), $user->username])->fetch()->count > 0) {
  129. $this->session->alert(lang('username_taken'), 'danger');
  130. return redirect($response, 'user.edit', ['id' => $args['id']]);
  131. }
  132. if ($user->id === $this->session->get('user_id') && $request->getParam('is_admin') === null) {
  133. $this->session->alert(lang('cannot_demote'), 'danger');
  134. return redirect($response, 'user.edit', ['id' => $args['id']]);
  135. }
  136. if ($request->getParam('password') !== null && !empty($request->getParam('password'))) {
  137. $this->database->query('UPDATE `users` SET `email`=?, `username`=?, `password`=?, `is_admin`=?, `active`=? WHERE `id` = ?', [
  138. $request->getParam('email'),
  139. $request->getParam('username'),
  140. password_hash($request->getParam('password'), PASSWORD_DEFAULT),
  141. $request->getParam('is_admin') !== null ? 1 : 0,
  142. $request->getParam('is_active') !== null ? 1 : 0,
  143. $user->id,
  144. ]);
  145. } else {
  146. $this->database->query('UPDATE `users` SET `email`=?, `username`=?, `is_admin`=?, `active`=? WHERE `id` = ?', [
  147. $request->getParam('email'),
  148. $request->getParam('username'),
  149. $request->getParam('is_admin') !== null ? 1 : 0,
  150. $request->getParam('is_active') !== null ? 1 : 0,
  151. $user->id,
  152. ]);
  153. }
  154. $this->session->alert(lang('user_updated', [$request->getParam('username')]), 'success');
  155. $this->logger->info('User ' . $this->session->get('username') . " updated $user->id.", [
  156. array_diff_key((array)$user, array_flip(['password'])),
  157. array_diff_key($request->getParams(), array_flip(['password'])),
  158. ]);
  159. return redirect($response, 'user.index');
  160. }
  161. /**
  162. * @param Request $request
  163. * @param Response $response
  164. * @param $args
  165. * @return Response
  166. * @throws NotFoundException
  167. */
  168. public function delete(Request $request, Response $response, $args): Response
  169. {
  170. $user = $this->database->query('SELECT * FROM `users` WHERE `id` = ? LIMIT 1', $args['id'])->fetch();
  171. if (!$user) {
  172. throw new NotFoundException($request, $response);
  173. }
  174. if ($user->id === $this->session->get('user_id')) {
  175. $this->session->alert(lang('cannot_delete'), 'danger');
  176. return redirect($response, 'user.index');
  177. }
  178. $this->database->query('DELETE FROM `users` WHERE `id` = ?', $user->id);
  179. $this->session->alert(lang('user_deleted'), 'success');
  180. $this->logger->info('User ' . $this->session->get('username') . " deleted $user->id.");
  181. return redirect($response, 'user.index');
  182. }
  183. /**
  184. * @param Request $request
  185. * @param Response $response
  186. * @return Response
  187. * @throws NotFoundException
  188. * @throws UnauthorizedException
  189. */
  190. public function profile(Request $request, Response $response): Response
  191. {
  192. $user = $this->database->query('SELECT * FROM `users` WHERE `id` = ? LIMIT 1', $this->session->get('user_id'))->fetch();
  193. if (!$user) {
  194. throw new NotFoundException($request, $response);
  195. }
  196. if ($user->id !== $this->session->get('user_id') && !$this->session->get('admin', false)) {
  197. throw new UnauthorizedException();
  198. }
  199. return $this->view->render($response, 'user/edit.twig', [
  200. 'profile' => true,
  201. 'user' => $user,
  202. ]);
  203. }
  204. /**
  205. * @param Request $request
  206. * @param Response $response
  207. * @param $args
  208. * @return Response
  209. * @throws NotFoundException
  210. * @throws UnauthorizedException
  211. */
  212. public function profileEdit(Request $request, Response $response, $args): Response
  213. {
  214. $user = $this->database->query('SELECT * FROM `users` WHERE `id` = ? LIMIT 1', $args['id'])->fetch();
  215. if (!$user) {
  216. throw new NotFoundException($request, $response);
  217. }
  218. if ($user->id !== $this->session->get('user_id') && !$this->session->get('admin', false)) {
  219. throw new UnauthorizedException();
  220. }
  221. if ($request->getParam('email') === null) {
  222. $this->session->alert(lang('email_required'), 'danger');
  223. return redirect($response, 'profile');
  224. }
  225. if ($this->database->query('SELECT COUNT(*) AS `count` FROM `users` WHERE `email` = ? AND `email` <> ?', [$request->getParam('email'), $user->email])->fetch()->count > 0) {
  226. $this->session->alert(lang('email_taken'), 'danger');
  227. return redirect($response, 'profile');
  228. }
  229. if ($request->getParam('password') !== null && !empty($request->getParam('password'))) {
  230. $this->database->query('UPDATE `users` SET `email`=?, `password`=? WHERE `id` = ?', [
  231. $request->getParam('email'),
  232. password_hash($request->getParam('password'), PASSWORD_DEFAULT),
  233. $user->id,
  234. ]);
  235. } else {
  236. $this->database->query('UPDATE `users` SET `email`=? WHERE `id` = ?', [
  237. $request->getParam('email'),
  238. $user->id,
  239. ]);
  240. }
  241. $this->session->alert(lang('profile_updated'), 'success');
  242. $this->logger->info('User ' . $this->session->get('username') . " updated profile of $user->id.");
  243. return redirect($response, 'profile');
  244. }
  245. /**
  246. * @param Request $request
  247. * @param Response $response
  248. * @param $args
  249. * @return Response
  250. * @throws NotFoundException
  251. * @throws UnauthorizedException
  252. */
  253. public function refreshToken(Request $request, Response $response, $args): Response
  254. {
  255. $user = $this->database->query('SELECT * FROM `users` WHERE `id` = ? LIMIT 1', $args['id'])->fetch();
  256. if (!$user) {
  257. throw new NotFoundException($request, $response);
  258. }
  259. if ($user->id !== $this->session->get('user_id') && !$this->session->get('admin', false)) {
  260. throw new UnauthorizedException();
  261. }
  262. $token = $this->generateNewToken();
  263. $this->database->query('UPDATE `users` SET `token`=? WHERE `id` = ?', [
  264. $token,
  265. $user->id,
  266. ]);
  267. $this->logger->info('User ' . $this->session->get('username') . " refreshed token of user $user->id.");
  268. $response->getBody()->write($token);
  269. return $response;
  270. }
  271. /**
  272. * @param Request $request
  273. * @param Response $response
  274. * @param $args
  275. * @return Response
  276. * @throws NotFoundException
  277. * @throws UnauthorizedException
  278. */
  279. public function getShareXconfigFile(Request $request, Response $response, $args): Response
  280. {
  281. $user = $this->database->query('SELECT * FROM `users` WHERE `id` = ? LIMIT 1', $args['id'])->fetch();
  282. if (!$user) {
  283. throw new NotFoundException($request, $response);
  284. }
  285. if ($user->id !== $this->session->get('user_id') && !$this->session->get('admin', false)) {
  286. throw new UnauthorizedException();
  287. }
  288. if ($user->token === null || $user->token === '') {
  289. $this->session->alert('You don\'t have a personal upload token. (Click the update token button and try again)', 'danger');
  290. return $response->withRedirect($request->getHeaderLine('HTTP_REFERER'));
  291. }
  292. $json = [
  293. 'DestinationType' => 'ImageUploader, TextUploader, FileUploader',
  294. 'RequestURL' => route('upload'),
  295. 'FileFormName' => 'upload',
  296. 'Arguments' => [
  297. 'file' => '$filename$',
  298. 'text' => '$input$',
  299. 'token' => $user->token,
  300. ],
  301. 'URL' => '$json:url$',
  302. 'ThumbnailURL' => '$json:url$/raw',
  303. 'DeletionURL' => '$json:url$/delete/' . $user->token,
  304. ];
  305. return $response
  306. ->withHeader('Content-Disposition', 'attachment;filename="' . $user->username . '-ShareX.sxcu"')
  307. ->withJson($json, 200, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
  308. }
  309. /**
  310. * @param Request $request
  311. * @param Response $response
  312. * @param $args
  313. * @return Response
  314. * @throws NotFoundException
  315. * @throws UnauthorizedException
  316. */
  317. public function getUploaderScriptFile(Request $request, Response $response, $args): Response
  318. {
  319. $user = $this->database->query('SELECT * FROM `users` WHERE `id` = ? LIMIT 1', $args['id'])->fetch();
  320. if (!$user) {
  321. throw new NotFoundException($request, $response);
  322. }
  323. if ($user->id !== $this->session->get('user_id') && !$this->session->get('admin', false)) {
  324. throw new UnauthorizedException();
  325. }
  326. if ($user->token === null || $user->token === '') {
  327. $this->session->alert('You don\'t have a personal upload token. (Click the update token button and try again)', 'danger');
  328. return $response->withRedirect($request->getHeaderLine('HTTP_REFERER'));
  329. }
  330. return $this->view->render($response->withHeader('Content-Disposition', 'attachment;filename="xbackbone_uploader_' . $user->username . '.sh"'),
  331. 'scripts/xbackbone_uploader.sh.twig',
  332. [
  333. 'username' => $user->username,
  334. 'upload_url' => route('upload'),
  335. 'token' => $user->token,
  336. ]
  337. );
  338. }
  339. /**
  340. * @return string
  341. */
  342. protected function generateNewToken(): string
  343. {
  344. do {
  345. $token = 'token_' . md5(uniqid('', true));
  346. } while ($this->database->query('SELECT COUNT(*) AS `count` FROM `users` WHERE `token` = ?', $token)->fetch()->count > 0);
  347. return $token;
  348. }
  349. }