admins.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  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. } elseif (
  147. $targetUsername ===
  148. normalizeAdminUsername($_SESSION['admin_username'] ?? "")
  149. ) {
  150. $message = "Sie können Ihr eigenes Admin-Konto nicht löschen.";
  151. $messageType = "error";
  152. } else {
  153. unset($adminAccounts[$targetUsername]);
  154. if (!saveAdminAccounts($adminAccounts)) {
  155. $message = "Admin konnte nicht gelöscht werden.";
  156. $messageType = "error";
  157. $adminAccounts = getAdminAccounts();
  158. } else {
  159. logAccess("Admin deleted admin account", [
  160. "username" => $targetUsername,
  161. ]);
  162. $message = "Admin wurde gelöscht.";
  163. $messageType = "success";
  164. }
  165. }
  166. }
  167. $adminAccounts = getAdminAccounts();
  168. }
  169. }
  170. $currentAdmin = isset($_SESSION['admin_username'])
  171. ? normalizeAdminUsername($_SESSION['admin_username'])
  172. : "";
  173. $changeUsername = normalizeAdminUsername($_GET['change'] ?? "");
  174. $selectedChangeUser = null;
  175. $editDescriptionUsername = normalizeAdminUsername(
  176. $_GET['edit_description'] ?? "",
  177. );
  178. $selectedDescriptionUser = null;
  179. if ($changeUsername !== "") {
  180. if (!isset($adminAccounts[$changeUsername])) {
  181. if ($message === "") {
  182. $message = "Ausgewählter Admin wurde nicht gefunden.";
  183. $messageType = "error";
  184. }
  185. } else {
  186. $selectedChangeUser = $changeUsername;
  187. }
  188. }
  189. if ($editDescriptionUsername !== "") {
  190. if (!isset($adminAccounts[$editDescriptionUsername])) {
  191. if ($message === "") {
  192. $message = "Ausgewählter Admin wurde nicht gefunden.";
  193. $messageType = "error";
  194. }
  195. } else {
  196. $selectedDescriptionUser = $editDescriptionUsername;
  197. }
  198. }
  199. ksort($adminAccounts);
  200. $bodyClass = "admin-page";
  201. include __DIR__ . "/../includes/header.php";
  202. ?>
  203. <div class="admin-header">
  204. <h2>Admins verwalten</h2>
  205. <div>
  206. <a href="index.php" class="btn btn-secondary">Zurück zum Dashboard</a>
  207. </div>
  208. </div>
  209. <?php if ($message !== ""): ?>
  210. <div class="alert alert-<?php echo $messageType; ?>">
  211. <?php echo htmlspecialchars($message); ?>
  212. </div>
  213. <?php endif; ?>
  214. <div class="panel">
  215. <p><strong>Eingeloggt als:</strong> <?php echo htmlspecialchars(
  216. $currentAdmin !== "" ? $currentAdmin : "Unbekannt",
  217. ); ?></p>
  218. </div>
  219. <div class="panel">
  220. <h3>Neuen Admin anlegen</h3>
  221. <form method="POST">
  222. <?php echo csrfField(); ?>
  223. <div class="form-group">
  224. <label for="username">Benutzername *</label>
  225. <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">
  226. </div>
  227. <div class="form-group">
  228. <label for="description">Beschreibung *</label>
  229. <input type="text" id="description" name="description" required maxlength="120" placeholder="z.B. Kassierer, Shop-Team">
  230. </div>
  231. <div class="form-group">
  232. <label for="email">E-Mail *</label>
  233. <input type="email" id="email" name="email" required maxlength="190" placeholder="z.B. max.mustermann@example.org">
  234. </div>
  235. <div class="form-group">
  236. <label for="password">Passwort (mind. 8 Zeichen) *</label>
  237. <input type="password" id="password" name="password" required minlength="8">
  238. </div>
  239. <div class="form-group">
  240. <label for="password_confirm">Passwort bestätigen *</label>
  241. <input type="password" id="password_confirm" name="password_confirm" required minlength="8">
  242. </div>
  243. <button type="submit" name="add_admin" class="btn">Admin anlegen</button>
  244. </form>
  245. </div>
  246. <div class="panel">
  247. <h3>Admin-Liste</h3>
  248. <div class="table-responsive">
  249. <table class="responsive-table">
  250. <thead>
  251. <tr>
  252. <th>Benutzername</th>
  253. <th>Beschreibung</th>
  254. <th>E-Mail</th>
  255. <th>Aktionen</th>
  256. </tr>
  257. </thead>
  258. <tbody>
  259. <?php foreach ($adminAccounts as $username => $account): ?>
  260. <tr>
  261. <td data-label="Benutzername">
  262. <strong><?php echo htmlspecialchars(
  263. $username,
  264. ); ?></strong>
  265. <?php if ($username === $currentAdmin): ?>
  266. <span class="status status-open status-self">Du</span>
  267. <?php endif; ?>
  268. </td>
  269. <td data-label="Beschreibung">
  270. <?php echo htmlspecialchars($account["description"]); ?>
  271. </td>
  272. <td data-label="E-Mail">
  273. <?php echo htmlspecialchars($account["email"]); ?>
  274. </td>
  275. <td data-label="Aktionen">
  276. <a href="admins.php?edit_description=<?php echo urlencode(
  277. $username,
  278. ); ?>" class="btn btn-small btn-secondary">Profil ändern</a>
  279. <a href="admins.php?change=<?php echo urlencode(
  280. $username,
  281. ); ?>" class="btn btn-small btn-secondary">Passwort ändern</a>
  282. <?php if ($username !== $currentAdmin): ?>
  283. <form method="POST" class="inline-form" onsubmit="return confirm('Admin wirklich löschen?');">
  284. <?php echo csrfField(); ?>
  285. <input type="hidden" name="target_username" value="<?php echo htmlspecialchars(
  286. $username,
  287. ); ?>">
  288. <button type="submit" name="delete_admin" class="btn btn-small">Löschen</button>
  289. </form>
  290. <?php endif; ?>
  291. </td>
  292. </tr>
  293. <?php endforeach; ?>
  294. </tbody>
  295. </table>
  296. </div>
  297. </div>
  298. <?php if ($selectedDescriptionUser !== null): ?>
  299. <div class="panel">
  300. <h3>Profil ändern: <?php echo htmlspecialchars(
  301. $selectedDescriptionUser,
  302. ); ?></h3>
  303. <form method="POST">
  304. <?php echo csrfField(); ?>
  305. <input type="hidden" name="target_username" value="<?php echo htmlspecialchars(
  306. $selectedDescriptionUser,
  307. ); ?>">
  308. <div class="form-group">
  309. <label for="description_edit">Beschreibung *</label>
  310. <input type="text" id="description_edit" name="description" maxlength="120" required value="<?php echo htmlspecialchars(
  311. $adminAccounts[$selectedDescriptionUser]["description"],
  312. ); ?>">
  313. </div>
  314. <div class="form-group">
  315. <label for="email_edit">E-Mail *</label>
  316. <input type="email" id="email_edit" name="email" maxlength="190" required value="<?php echo htmlspecialchars(
  317. $adminAccounts[$selectedDescriptionUser]["email"],
  318. ); ?>">
  319. </div>
  320. <button type="submit" name="update_description" class="btn">Profil speichern</button>
  321. <a href="admins.php" class="btn btn-secondary">Abbrechen</a>
  322. </form>
  323. </div>
  324. <?php endif; ?>
  325. <?php if ($selectedChangeUser !== null): ?>
  326. <div class="panel">
  327. <h3>Passwort ändern: <?php echo htmlspecialchars(
  328. $selectedChangeUser,
  329. ); ?></h3>
  330. <form method="POST">
  331. <?php echo csrfField(); ?>
  332. <input type="hidden" name="target_username" value="<?php echo htmlspecialchars(
  333. $selectedChangeUser,
  334. ); ?>">
  335. <div class="form-group">
  336. <label for="new_password">Neues Passwort (mind. 8 Zeichen) *</label>
  337. <input type="password" id="new_password" name="new_password" required minlength="8">
  338. </div>
  339. <div class="form-group">
  340. <label for="new_password_confirm">Neues Passwort bestätigen *</label>
  341. <input type="password" id="new_password_confirm" name="new_password_confirm" required minlength="8">
  342. </div>
  343. <button type="submit" name="change_password" class="btn">Passwort speichern</button>
  344. <a href="admins.php" class="btn btn-secondary">Abbrechen</a>
  345. </form>
  346. </div>
  347. <?php endif; ?>
  348. <?php include __DIR__ . "/../includes/footer.php"; ?>