admins.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. <?php
  2. require_once __DIR__ . "/../config.php";
  3. require_once __DIR__ . "/../includes/functions.php";
  4. // Check admin login
  5. if (!isset($_SESSION["admin_logged_in"]) || !$_SESSION["admin_logged_in"]) {
  6. header("Location: login.php");
  7. exit();
  8. }
  9. $pageTitle = "Admins verwalten";
  10. $message = "";
  11. $messageType = "";
  12. function isValidAdminPasswordInput($password)
  13. {
  14. return is_string($password) && strlen($password) >= 8;
  15. }
  16. $adminAccounts = getAdminAccounts();
  17. function isValidAdminEmailInput($email)
  18. {
  19. return isValidAdminEmail($email);
  20. }
  21. if ($_SERVER["REQUEST_METHOD"] === "POST") {
  22. // Validate CSRF token
  23. if (!validateCsrfToken($_POST["csrf_token"] ?? "")) {
  24. $message = "Ungültiges Token. Bitte versuchen Sie es erneut.";
  25. $messageType = "error";
  26. } else {
  27. if (isset($_POST["add_admin"])) {
  28. $username = normalizeAdminUsername($_POST["username"] ?? "");
  29. $description = normalizeAdminDescription(
  30. $_POST["description"] ?? "",
  31. );
  32. $email = normalizeAdminEmail($_POST["email"] ?? "");
  33. $password = $_POST["password"] ?? "";
  34. $passwordConfirm = $_POST["password_confirm"] ?? "";
  35. if (!isValidAdminUsername($username)) {
  36. $message =
  37. "Ungültiger Benutzername. Erlaubt: 3-50 Zeichen (Buchstaben, Zahlen, Punkt, Unterstrich, Bindestrich).";
  38. $messageType = "error";
  39. } elseif (isset($adminAccounts[$username])) {
  40. $message = "Dieser Benutzername existiert bereits.";
  41. $messageType = "error";
  42. } elseif (!isValidAdminDescription($description)) {
  43. $message = "Beschreibung ist erforderlich (max. 120 Zeichen).";
  44. $messageType = "error";
  45. } elseif (!isValidAdminEmailInput($email)) {
  46. $message = "Gültige E-Mail ist erforderlich.";
  47. $messageType = "error";
  48. } elseif (!isValidAdminPasswordInput($password)) {
  49. $message = "Passwort muss mindestens 8 Zeichen lang sein.";
  50. $messageType = "error";
  51. } elseif ($password !== $passwordConfirm) {
  52. $message = "Passwort und Bestätigung stimmen nicht überein.";
  53. $messageType = "error";
  54. } else {
  55. $adminAccounts[$username] = [
  56. "password_hash" => password_hash(
  57. $password,
  58. PASSWORD_BCRYPT,
  59. ),
  60. "description" => $description,
  61. "email" => $email,
  62. ];
  63. if (saveAdminAccounts($adminAccounts)) {
  64. logAccess("Admin added admin account", [
  65. "username" => $username,
  66. "description" => $description,
  67. ]);
  68. $message = "Admin wurde erfolgreich angelegt.";
  69. $messageType = "success";
  70. } else {
  71. $message = "Admin konnte nicht gespeichert werden.";
  72. $messageType = "error";
  73. }
  74. }
  75. }
  76. if (isset($_POST["update_description"])) {
  77. $targetUsername = normalizeAdminUsername(
  78. $_POST["target_username"] ?? "",
  79. );
  80. $description = normalizeAdminDescription(
  81. $_POST["description"] ?? "",
  82. );
  83. $email = normalizeAdminEmail($_POST["email"] ?? "");
  84. if (!isset($adminAccounts[$targetUsername])) {
  85. $message = "Admin nicht gefunden.";
  86. $messageType = "error";
  87. } elseif (!isValidAdminDescription($description)) {
  88. $message = "Beschreibung ist erforderlich (max. 120 Zeichen).";
  89. $messageType = "error";
  90. } elseif (!isValidAdminEmailInput($email)) {
  91. $message = "Gültige E-Mail ist erforderlich.";
  92. $messageType = "error";
  93. } else {
  94. $adminAccounts[$targetUsername]["description"] = $description;
  95. $adminAccounts[$targetUsername]["email"] = $email;
  96. if (saveAdminAccounts($adminAccounts)) {
  97. logAccess("Admin updated admin description", [
  98. "username" => $targetUsername,
  99. ]);
  100. $message = "Beschreibung und E-Mail wurden aktualisiert.";
  101. $messageType = "success";
  102. } else {
  103. $message = "Änderungen konnten nicht gespeichert werden.";
  104. $messageType = "error";
  105. }
  106. }
  107. }
  108. if (isset($_POST["change_password"])) {
  109. $targetUsername = normalizeAdminUsername(
  110. $_POST["target_username"] ?? "",
  111. );
  112. $newPassword = $_POST["new_password"] ?? "";
  113. $newPasswordConfirm = $_POST["new_password_confirm"] ?? "";
  114. if (!isset($adminAccounts[$targetUsername])) {
  115. $message = "Admin nicht gefunden.";
  116. $messageType = "error";
  117. } elseif (!isValidAdminPasswordInput($newPassword)) {
  118. $message = "Passwort muss mindestens 8 Zeichen lang sein.";
  119. $messageType = "error";
  120. } elseif ($newPassword !== $newPasswordConfirm) {
  121. $message = "Passwort und Bestätigung stimmen nicht überein.";
  122. $messageType = "error";
  123. } else {
  124. $adminAccounts[$targetUsername][
  125. "password_hash"
  126. ] = password_hash($newPassword, PASSWORD_BCRYPT);
  127. if (saveAdminAccounts($adminAccounts)) {
  128. logAccess("Admin changed admin password", [
  129. "username" => $targetUsername,
  130. ]);
  131. $message = "Passwort wurde aktualisiert.";
  132. $messageType = "success";
  133. } else {
  134. $message = "Passwort konnte nicht gespeichert werden.";
  135. $messageType = "error";
  136. }
  137. }
  138. }
  139. if (isset($_POST["delete_admin"])) {
  140. $targetUsername = normalizeAdminUsername(
  141. $_POST["target_username"] ?? "",
  142. );
  143. if (!isset($adminAccounts[$targetUsername])) {
  144. $message = "Admin nicht gefunden.";
  145. $messageType = "error";
  146. } else {
  147. unset($adminAccounts[$targetUsername]);
  148. if (!saveAdminAccounts($adminAccounts)) {
  149. $message = "Admin konnte nicht gelöscht werden.";
  150. $messageType = "error";
  151. $adminAccounts = getAdminAccounts();
  152. } else {
  153. logAccess("Admin deleted admin account", [
  154. "username" => $targetUsername,
  155. ]);
  156. if (
  157. isset($_SESSION["admin_username"]) &&
  158. $_SESSION["admin_username"] === $targetUsername
  159. ) {
  160. $_SESSION["admin_logged_in"] = false;
  161. unset($_SESSION["admin_username"]);
  162. session_destroy();
  163. header("Location: login.php");
  164. exit();
  165. }
  166. $message = "Admin wurde gelöscht.";
  167. $messageType = "success";
  168. }
  169. }
  170. }
  171. $adminAccounts = getAdminAccounts();
  172. }
  173. }
  174. $currentAdmin = isset($_SESSION["admin_username"])
  175. ? normalizeAdminUsername($_SESSION["admin_username"])
  176. : "";
  177. $changeUsername = normalizeAdminUsername($_GET["change"] ?? "");
  178. $selectedChangeUser = null;
  179. $editDescriptionUsername = normalizeAdminUsername(
  180. $_GET["edit_description"] ?? "",
  181. );
  182. $selectedDescriptionUser = null;
  183. if ($changeUsername !== "") {
  184. if (!isset($adminAccounts[$changeUsername])) {
  185. if ($message === "") {
  186. $message = "Ausgewählter Admin wurde nicht gefunden.";
  187. $messageType = "error";
  188. }
  189. } else {
  190. $selectedChangeUser = $changeUsername;
  191. }
  192. }
  193. if ($editDescriptionUsername !== "") {
  194. if (!isset($adminAccounts[$editDescriptionUsername])) {
  195. if ($message === "") {
  196. $message = "Ausgewählter Admin wurde nicht gefunden.";
  197. $messageType = "error";
  198. }
  199. } else {
  200. $selectedDescriptionUser = $editDescriptionUsername;
  201. }
  202. }
  203. ksort($adminAccounts);
  204. $bodyClass = "admin-page";
  205. include __DIR__ . "/../includes/header.php";
  206. ?>
  207. <div class="admin-header">
  208. <h2>Admins verwalten</h2>
  209. <div>
  210. <a href="index.php" class="btn btn-secondary">Zurück zum Dashboard</a>
  211. </div>
  212. </div>
  213. <?php if ($message !== ""): ?>
  214. <div class="alert alert-<?php echo $messageType; ?>">
  215. <?php echo htmlspecialchars($message); ?>
  216. </div>
  217. <?php endif; ?>
  218. <div class="panel">
  219. <p><strong>Eingeloggt als:</strong> <?php echo htmlspecialchars(
  220. $currentAdmin !== "" ? $currentAdmin : "Unbekannt",
  221. ); ?></p>
  222. </div>
  223. <div class="panel">
  224. <h3>Neuen Admin anlegen</h3>
  225. <form method="POST">
  226. <?php echo csrfField(); ?>
  227. <div class="form-group">
  228. <label for="username">Benutzername *</label>
  229. <input type="text" id="username" name="username" required maxlength="50" pattern="[A-Za-z0-9][A-Za-z0-9._-]{2,49}" placeholder="z.B. max.mustermann">
  230. </div>
  231. <div class="form-group">
  232. <label for="description">Beschreibung *</label>
  233. <input type="text" id="description" name="description" required maxlength="120" placeholder="z.B. Kassierer, Shop-Team">
  234. </div>
  235. <div class="form-group">
  236. <label for="email">E-Mail *</label>
  237. <input type="email" id="email" name="email" required maxlength="190" placeholder="z.B. max.mustermann@example.org">
  238. </div>
  239. <div class="form-group">
  240. <label for="password">Passwort (mind. 8 Zeichen) *</label>
  241. <input type="password" id="password" name="password" required minlength="8">
  242. </div>
  243. <div class="form-group">
  244. <label for="password_confirm">Passwort bestätigen *</label>
  245. <input type="password" id="password_confirm" name="password_confirm" required minlength="8">
  246. </div>
  247. <button type="submit" name="add_admin" class="btn">Admin anlegen</button>
  248. </form>
  249. </div>
  250. <div class="panel">
  251. <h3>Admin-Liste</h3>
  252. <div class="table-responsive">
  253. <table class="responsive-table">
  254. <thead>
  255. <tr>
  256. <th>Benutzername</th>
  257. <th>Beschreibung</th>
  258. <th>E-Mail</th>
  259. <th>Aktionen</th>
  260. </tr>
  261. </thead>
  262. <tbody>
  263. <?php foreach ($adminAccounts as $username => $account): ?>
  264. <tr>
  265. <td data-label="Benutzername">
  266. <strong><?php echo htmlspecialchars(
  267. $username,
  268. ); ?></strong>
  269. <?php if ($username === $currentAdmin): ?>
  270. <span class="status status-open status-self">Du</span>
  271. <?php endif; ?>
  272. </td>
  273. <td data-label="Beschreibung">
  274. <?php echo htmlspecialchars($account["description"]); ?>
  275. </td>
  276. <td data-label="E-Mail">
  277. <?php echo htmlspecialchars($account["email"]); ?>
  278. </td>
  279. <td data-label="Aktionen">
  280. <a href="admins.php?edit_description=<?php echo urlencode(
  281. $username,
  282. ); ?>" class="btn btn-small btn-secondary">Profil ändern</a>
  283. <a href="admins.php?change=<?php echo urlencode(
  284. $username,
  285. ); ?>" class="btn btn-small btn-secondary">Passwort ändern</a>
  286. <form method="POST" class="inline-form" onsubmit="return confirm('Admin wirklich löschen?');">
  287. <input type="hidden" name="target_username" value="<?php echo htmlspecialchars(
  288. $username,
  289. ); ?>">
  290. <button type="submit" name="delete_admin" class="btn btn-small">Löschen</button>
  291. </form>
  292. </td>
  293. </tr>
  294. <?php endforeach; ?>
  295. </tbody>
  296. </table>
  297. </div>
  298. </div>
  299. <?php if ($selectedDescriptionUser !== null): ?>
  300. <div class="panel">
  301. <h3>Profil ändern: <?php echo htmlspecialchars(
  302. $selectedDescriptionUser,
  303. ); ?></h3>
  304. <form method="POST">
  305. <?php echo csrfField(); ?>
  306. <input type="hidden" name="target_username" value="<?php echo htmlspecialchars(
  307. $selectedDescriptionUser,
  308. ); ?>">
  309. <div class="form-group">
  310. <label for="description_edit">Beschreibung *</label>
  311. <input type="text" id="description_edit" name="description" maxlength="120" required value="<?php echo htmlspecialchars(
  312. $adminAccounts[$selectedDescriptionUser]["description"],
  313. ); ?>">
  314. </div>
  315. <div class="form-group">
  316. <label for="email_edit">E-Mail *</label>
  317. <input type="email" id="email_edit" name="email" maxlength="190" required value="<?php echo htmlspecialchars(
  318. $adminAccounts[$selectedDescriptionUser]["email"],
  319. ); ?>">
  320. </div>
  321. <button type="submit" name="update_description" class="btn">Profil speichern</button>
  322. <a href="admins.php" class="btn btn-secondary">Abbrechen</a>
  323. </form>
  324. </div>
  325. <?php endif; ?>
  326. <?php if ($selectedChangeUser !== null): ?>
  327. <div class="panel">
  328. <h3>Passwort ändern: <?php echo htmlspecialchars(
  329. $selectedChangeUser,
  330. ); ?></h3>
  331. <form method="POST">
  332. <?php echo csrfField(); ?>
  333. <input type="hidden" name="target_username" value="<?php echo htmlspecialchars(
  334. $selectedChangeUser,
  335. ); ?>">
  336. <div class="form-group">
  337. <label for="new_password">Neues Passwort (mind. 8 Zeichen) *</label>
  338. <input type="password" id="new_password" name="new_password" required minlength="8">
  339. </div>
  340. <div class="form-group">
  341. <label for="new_password_confirm">Neues Passwort bestätigen *</label>
  342. <input type="password" id="new_password_confirm" name="new_password_confirm" required minlength="8">
  343. </div>
  344. <button type="submit" name="change_password" class="btn">Passwort speichern</button>
  345. <a href="admins.php" class="btn btn-secondary">Abbrechen</a>
  346. </form>
  347. </div>
  348. <?php endif; ?>
  349. <?php include __DIR__ . "/../includes/footer.php"; ?>