requireAdmin(); $db = Database::getInstance(); $mailer = new Mailer(); $appTitle = $db->getSetting('app_title', 'UniFi Voucher System'); $smtpEnabled = $db->getSetting('smtp_enabled', '0') === '1'; I18n::init(); $error = ''; $success = ''; // Send password reset link (POST statt GET: kein CSRF-Token in URLs/Referrern, // keine versehentliche Ausloesung durch Link-Prefetching) if ($_SERVER['REQUEST_METHOD']==='POST' && isset($_POST['send_reset'])) { if ($auth->validateCsrfToken($_POST['csrf_token'] ?? '')) { $targetUser = $db->fetchOne("SELECT * FROM users WHERE id=? AND is_active=1 AND password_hash IS NOT NULL", [(int)$_POST['send_reset']]); if ($targetUser) { try { $db->execute("DELETE FROM password_reset_tokens WHERE user_id=?", [$targetUser['id']]); $token = bin2hex(random_bytes(32)); $expiresAt = date('Y-m-d H:i:s', strtotime('+1 hour')); $db->execute("INSERT INTO password_reset_tokens (user_id, token, expires_at) VALUES (?,?,?)", [$targetUser['id'], $token, $expiresAt]); $systemUrl = rtrim($db->getSetting('system_url', ''), '/'); if (empty($systemUrl)) { $protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS']==='on' ? 'https' : 'http'; $scriptPath = dirname(dirname($_SERVER['SCRIPT_NAME'])); $systemUrl = $protocol . '://' . $_SERVER['HTTP_HOST'] . ($scriptPath==='/'?'':$scriptPath); } $resetUrl = $systemUrl . '/reset_password.php?token=' . $token; $mailer->sendRaw($targetUser['email'], $appTitle . ' – Passwort zurücksetzen', "Hallo {$targetUser['name']},\n\nEin Administrator hat für Sie einen Passwort-Reset-Link erstellt:\n\n{$resetUrl}\n\n(Gültig für 1 Stunde)\n\n{$appTitle}"); flashSet(__('reset_link_sent', ['email' => $targetUser['email']])); header('Location: users.php'); exit; } catch (Exception $e) { $error = 'Fehler beim Senden: ' . $e->getMessage(); } } else { $error = __('reset_link_failed'); } } else { $error = __('error_csrf'); } } // Edit user if ($_SERVER['REQUEST_METHOD']==='POST' && isset($_POST['edit_user'])) { if (!$auth->validateCsrfToken($_POST['csrf_token']??'')) { $error = __('error_csrf'); } else { try { $userId = (int)$_POST['user_id']; $isAdmin = isset($_POST['is_admin']) ? 1 : 0; $siteIds = $_POST['site_ids'] ?? []; // Lockout-Schutz: Der letzte Weg ins Admin-Panel darf nicht // versehentlich gekappt werden. if ($userId === (int)$_SESSION['user_id'] && !$isAdmin) { throw new Exception(__('error_self_demote')); } $oldUser = $db->fetchOne("SELECT * FROM users WHERE id=?", [$userId]); $oldSites= $db->fetchAll("SELECT s.name FROM sites s INNER JOIN user_site_access usa ON s.id=usa.site_id WHERE usa.user_id=?", [$userId]); $db->query("UPDATE users SET is_admin=? WHERE id=?", [$isAdmin, $userId]); $db->query("DELETE FROM user_site_access WHERE user_id=?", [$userId]); $newSites = []; if (!$isAdmin && !empty($siteIds)) { foreach ($siteIds as $siteId) { $db->execute("INSERT INTO user_site_access (user_id, site_id) VALUES (?,?)", [$userId, $siteId]); $site = $db->fetchOne("SELECT name FROM sites WHERE id=?", [$siteId]); if ($site) $newSites[] = $site['name']; } } $changes = []; if ($oldUser['is_admin'] != $isAdmin) { $changes[] = $isAdmin ? 'Sie wurden zum Administrator ernannt' : 'Ihre Administrator-Rechte wurden entfernt'; } $oldSiteNames = array_column($oldSites,'name'); $addedSites = array_diff($newSites, $oldSiteNames); $removedSites = array_diff($oldSiteNames, $newSites); if (!empty($addedSites)) $changes[] = 'Zugriff gewährt auf: ' . implode(', ', $addedSites); if (!empty($removedSites)) $changes[] = 'Zugriff entfernt von: ' . implode(', ', $removedSites); if ($isAdmin && !$oldUser['is_admin']) $changes[] = 'Sie haben nun Zugriff auf alle Sites'; if (!empty($changes)) $mailer->sendUserNotification($oldUser['email'], $oldUser['name'], $changes); $auth->writeAuditLog($_SESSION['user_id'], 'user_edit', 'user', $userId, implode('; ', $changes) ?: 'Keine Änderungen'); flashSet(__('users_updated') . (!empty($changes) ? ' '.__('users_notified') : '')); header('Location: users.php'); exit; } catch (Exception $e) { $error = $e->getMessage(); } } } // Add user if ($_SERVER['REQUEST_METHOD']==='POST' && isset($_POST['add_user'])) { if (!$auth->validateCsrfToken($_POST['csrf_token']??'')) { $error = __('error_csrf'); } else { try { $email = trim($_POST['email']); $name = trim($_POST['name']); $password= $_POST['password']; $isAdmin = isset($_POST['is_admin']) ? 1 : 0; $siteIds = $_POST['site_ids'] ?? []; if (empty($email)||empty($name)||empty($password)) throw new Exception(__('error_fill_all')); if (!filter_var($email, FILTER_VALIDATE_EMAIL)) throw new Exception(__('error_email_invalid')); if (strlen($password) < 8) throw new Exception(__('settings_pw_minlength')); if ($db->fetchOne("SELECT id FROM users WHERE email=?", [$email])) throw new Exception(__('error_email_exists')); $userId = $auth->registerUser($email, $name, $password, $isAdmin); if (!$userId) throw new Exception(__('error_user_create')); if (!$isAdmin && !empty($siteIds)) { foreach ($siteIds as $siteId) { $db->execute("INSERT INTO user_site_access (user_id, site_id) VALUES (?,?)", [$userId, $siteId]); } } $auth->writeAuditLog($_SESSION['user_id'], 'user_create', 'user', $userId, "Benutzer {$name} erstellt"); flashSet(__('users_added')); header('Location: users.php'); exit; } catch (Exception $e) { $error = $e->getMessage(); } } } // Delete user (POST + PRG) if ($_SERVER['REQUEST_METHOD']==='POST' && isset($_POST['delete_user'])) { if ($auth->validateCsrfToken($_POST['csrf_token'] ?? '')) { $deleteId = (int)$_POST['delete_user']; if ($deleteId === (int)$_SESSION['user_id']) { $error = __('error_self_delete'); } else { $db->query("DELETE FROM users WHERE id=?", [$deleteId]); $auth->writeAuditLog($_SESSION['user_id'], 'user_delete', 'user', $deleteId, 'Benutzer gelöscht'); flashSet(__('users_deleted')); header('Location: users.php'); exit; } } else { $error = __('error_csrf'); } } // Toggle user active (POST + PRG) if ($_SERVER['REQUEST_METHOD']==='POST' && isset($_POST['toggle_user'])) { if ($auth->validateCsrfToken($_POST['csrf_token'] ?? '')) { $toggleId = (int)$_POST['toggle_user']; if ($toggleId === (int)$_SESSION['user_id']) { $error = __('error_self_deactivate'); } else { $user = $db->fetchOne("SELECT is_active FROM users WHERE id=?", [$toggleId]); if ($user) { $newStatus = $user['is_active'] ? 0 : 1; $db->query("UPDATE users SET is_active=? WHERE id=?", [$newStatus, $toggleId]); flashSet(__('users_status_updated')); header('Location: users.php'); exit; } } } else { $error = __('error_csrf'); } } // 2FA eines Benutzers zurücksetzen (Admin-Hilfe bei verlorenem Authenticator) // POST + PRG wie die uebrigen state-aendernden Aktionen if ($_SERVER['REQUEST_METHOD']==='POST' && isset($_POST['reset_2fa'])) { if ($auth->validateCsrfToken($_POST['csrf_token'] ?? '')) { $auth->disableTotp((int)$_POST['reset_2fa']); flashSet('2FA des Benutzers wurde zurückgesetzt.'); header('Location: users.php'); exit; } else { $error = __('error_csrf'); } } if (empty($success) && empty($error) && ($flash = flashGet())) { $success = $flash['message']; } $users = $db->fetchAll("SELECT * FROM users ORDER BY name"); $sites = $db->fetchAll("SELECT * FROM sites WHERE is_active=1 ORDER BY name"); $userSiteAccess = []; foreach ($users as $user) { $userSiteAccess[$user['id']] = $db->fetchAll("SELECT s.id, s.name FROM sites s INNER JOIN user_site_access usa ON s.id=usa.site_id WHERE usa.user_id=?", [$user['id']]); } $currentPage = 'users'; ?>
= __('users_none_found') ?>
| = __('label_name') ?> | = __('label_email') ?> | = __('label_role') ?> | = __('label_status') ?> | = __('users_site_access') ?> | = __('users_last_login') ?> | = __('label_actions') ?> |
|---|---|---|---|---|---|---|
| = htmlspecialchars($user['name']) ?> = __('users_you') ?> | = htmlspecialchars($user['email']) ?> | = __('status_admin') ?> = __('status_user') ?> | = __('status_active') ?> = __('status_inactive') ?> | = __('users_all_sites') ?> = htmlspecialchars($s['name']) ?> = __('users_none') ?> | = date('d.m.Y H:i', strtotime($user['last_login'])) ?> = __('users_never') ?> |
|