From eec28f77b8af3ce14a4149db1e11e15bd8a417a0 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 5 Jun 2026 19:39:28 +0000 Subject: [PATCH] Erweiterte Features (1/2): Trusted-Proxy-IP, Bandbreitenlimits, 2FA - Schema: updater/migrations/0002 + database.sql (users.totp_*, voucher_templates qos_*, neue Tabelle api_keys) - Trusted-Proxy-IP: Auth::clientIp() wertet X-Forwarded-For nur hinter konfiguriertem trusted_proxy aus (korrektes Rate-Limit/Audit hinter Proxy) - Bandbreiten-/Datenlimits: UniFiController::createVoucher akzeptiert QoS (down/up kbit/s, Datenkontingent MB); Voucher-Profile speichern Limits, Voucher-Formular reicht sie via Template-Quick-Select durch - 2FA (TOTP, RFC 6238): includes/Totp.php (gegen RFC-Testvektoren verifiziert), zweistufiger Login, admin/security.php zum Aktivieren/Deaktivieren mit QR, Nav-Link + i18n --- admin/security.php | 137 ++++++++++++++++++ admin/templates.php | 33 ++++- database.sql | 19 +++ includes/Auth.php | 82 ++++++++++- includes/Totp.php | 83 +++++++++++ includes/UniFiController.php | 16 +- includes/admin_nav.php | 3 + index.php | 34 ++++- lang/de.php | 1 + lang/en.php | 1 + login.php | 38 ++++- updater/migrations/0002_extended_features.sql | 27 ++++ 12 files changed, 458 insertions(+), 16 deletions(-) create mode 100644 admin/security.php create mode 100644 includes/Totp.php create mode 100644 updater/migrations/0002_extended_features.sql diff --git a/admin/security.php b/admin/security.php new file mode 100644 index 0000000..afef7b1 --- /dev/null +++ b/admin/security.php @@ -0,0 +1,137 @@ +requireLogin(); + +$db = Database::getInstance(); +$user = $auth->getCurrentUser(); +$appTitle = $db->getSetting('app_title', 'UniFi Voucher System'); + +$error = ''; +$success = ''; +$hasPassword = !empty($user['password_hash']); +$totpEnabled = !empty($user['totp_enabled']); + +// 2FA aktivieren (Code bestaetigen) +if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['enable_totp'])) { + if (!$auth->validateCsrfToken($_POST['csrf_token'] ?? '')) { + $error = 'Ungültiges Sicherheits-Token'; + } else { + $secret = $_SESSION['totp_setup_secret'] ?? ''; + $code = trim($_POST['code'] ?? ''); + if ($secret === '') { + $error = 'Setup abgelaufen, bitte erneut starten.'; + } elseif (!Totp::verify($secret, $code)) { + $error = 'Code ungültig. Bitte erneut versuchen.'; + } else { + $auth->enableTotp($user['id'], $secret); + unset($_SESSION['totp_setup_secret']); + $totpEnabled = true; + $success = 'Zwei-Faktor-Authentifizierung wurde aktiviert.'; + } + } +} + +// 2FA deaktivieren +if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['disable_totp'])) { + if (!$auth->validateCsrfToken($_POST['csrf_token'] ?? '')) { + $error = 'Ungültiges Sicherheits-Token'; + } else { + $auth->disableTotp($user['id']); + $totpEnabled = false; + $success = 'Zwei-Faktor-Authentifizierung wurde deaktiviert.'; + } +} + +// Für die Setup-Ansicht ein Secret erzeugen (in Session halten bis bestätigt) +$setupSecret = ''; +$otpUri = ''; +if (!$totpEnabled && $hasPassword) { + $setupSecret = $_SESSION['totp_setup_secret'] ?? Totp::generateSecret(); + $_SESSION['totp_setup_secret'] = $setupSecret; + $otpUri = Totp::provisioningUri($setupSecret, $user['email'], $appTitle); +} +$csrf = $auth->getCsrfToken(); +?> + + + + + +Zwei-Faktor-Authentifizierung – <?= htmlspecialchars($appTitle) ?> + + + + + + +
+

🔐 Zwei-Faktor-Authentifizierung

+

Konto:

+ +
+
+ + +
● Nicht verfügbar
+

Ihr Konto meldet sich über Microsoft 365 an. 2FA wird dort in Ihrem Microsoft-Konto verwaltet.

+ +
● Aktiv
+

Bei jeder Anmeldung wird zusätzlich ein Code aus Ihrer Authenticator-App abgefragt.

+
+ + +
+ +
● Inaktiv
+
    +
  1. Authenticator-App öffnen (Google Authenticator, Authy, Microsoft Authenticator …)
  2. +
  3. QR-Code scannen oder Secret manuell eingeben
  4. +
  5. Den angezeigten 6-stelligen Code unten eingeben
  6. +
+
+
+
+ + + + +
+ + + + ← Zurück +
+ + diff --git a/admin/templates.php b/admin/templates.php index fe9512e..2fe218a 100644 --- a/admin/templates.php +++ b/admin/templates.php @@ -29,13 +29,17 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_template'])) { $expireMin = (int)($_POST['expire_minutes'] ?? 480); $description = trim($_POST['description'] ?? ''); + $qosDown = max(0, (int)($_POST['qos_rate_max_down'] ?? 0)) ?: null; + $qosUp = max(0, (int)($_POST['qos_rate_max_up'] ?? 0)) ?: null; + $qosQuota = max(0, (int)($_POST['qos_usage_quota'] ?? 0)) ?: null; + if (empty($name)) throw new Exception(__('error_name_req')); if ($maxUses < 1) $maxUses = 1; if ($expireMin < 1) $expireMin = 60; $db->execute( - "INSERT INTO voucher_templates (name, max_uses, expire_minutes, description, created_by) VALUES (?, ?, ?, ?, ?)", - [$name, $maxUses, $expireMin, $description, $_SESSION['user_id']] + "INSERT INTO voucher_templates (name, max_uses, expire_minutes, description, qos_rate_max_down, qos_rate_max_up, qos_usage_quota, created_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", + [$name, $maxUses, $expireMin, $description, $qosDown, $qosUp, $qosQuota, $_SESSION['user_id']] ); $success = __('templates_added'); } catch (Exception $e) { @@ -57,11 +61,15 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['edit_template'])) { $description = trim($_POST['description'] ?? ''); $isActive = isset($_POST['is_active']) ? 1 : 0; + $qosDown = max(0, (int)($_POST['qos_rate_max_down'] ?? 0)) ?: null; + $qosUp = max(0, (int)($_POST['qos_rate_max_up'] ?? 0)) ?: null; + $qosQuota = max(0, (int)($_POST['qos_usage_quota'] ?? 0)) ?: null; + if (empty($name)) throw new Exception(__('error_name_req')); $db->execute( - "UPDATE voucher_templates SET name=?, max_uses=?, expire_minutes=?, description=?, is_active=? WHERE id=?", - [$name, $maxUses, $expireMin, $description, $isActive, $id] + "UPDATE voucher_templates SET name=?, max_uses=?, expire_minutes=?, description=?, qos_rate_max_down=?, qos_rate_max_up=?, qos_usage_quota=?, is_active=? WHERE id=?", + [$name, $maxUses, $expireMin, $description, $qosDown, $qosUp, $qosQuota, $isActive, $id] ); $success = __('templates_updated'); } catch (Exception $e) { @@ -204,7 +212,7 @@ $adminBase = ''; -
+
+
+
+
+
@@ -276,6 +289,11 @@ $adminBase = '';
+
+
+
+
+
@@ -293,13 +311,16 @@ $adminBase = '';