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: = htmlspecialchars($user['email']) ?>
+
+
= htmlspecialchars($error) ?>
+
= htmlspecialchars($success) ?>
+
+
+
● 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
+
+ - Authenticator-App öffnen (Google Authenticator, Authy, Microsoft Authenticator …)
+ - QR-Code scannen oder Secret manuell eingeben
+ - Den angezeigten 6-stelligen Code unten eingeben
+
+
+
= htmlspecialchars($setupSecret) ?>
+
+
+
+
+
← 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 = '';
|