Merge: UI/UX-Feature-Branch integrieren + Security-Patches re-applien

Integriert die Feature-Branch (Dark Mode, i18n DE/EN, mobile Admin-Layout,
shared admin_nav, Toasts, Voucher-Templates, Bulk-Erstellung, konfigurierbare
Defaults, Password-Reset, Audit-Log-UI + Audit-Logging) mit der bestehenden
Security-/Updater-Arbeit.

Konfliktauflösung (6 Dateien: index.php + admin/*): Feature-Version als Basis,
darauf die Security-Patches re-appliziert:
- display_errors=0 + log_errors in allen neuen/gemergten Entry-Points
- Crypto::encrypt/decrypt an allen Site-Passwort-Pfaden (sites/index/vouchers/
  dashboard-Sync, inkl. doCreateVoucher + Bulk)
- CSRF-Prüfung für ALLE Voucher-Erstellungen (auch anonym/öffentlich), Token
  unbedingt im Formular; Session-Throttle gegen Spam
- Updater-Maintenance-Hook am Anfang von index.php wiederhergestellt

Auto-Merge verifiziert: Auth.php enthält Session-Timeout UND writeAuditLog;
login.php behält display_errors=0 + OAuth-state. Updater-Link in shared
admin_nav.php (i18n-Key nav_update DE/EN). Alle PHP-Dateien linten sauber.
This commit is contained in:
Claude 2026-06-05 19:10:18 +00:00
commit 526c43e8ee
No known key found for this signature in database
20 changed files with 5328 additions and 5521 deletions

View file

@ -38,6 +38,7 @@ class Auth {
$this->clearLoginAttempts($ip, $email);
$this->setUserSession($user);
$this->updateLastLogin($user['id']);
$this->writeAuditLog($user['id'], 'user_login', 'user', $user['id'], 'Login erfolgreich');
return true;
}
@ -45,6 +46,17 @@ class Auth {
return false;
}
public function writeAuditLog($userId, $action, $entityType = null, $entityId = null, $details = null) {
try {
$this->db->execute(
"INSERT INTO audit_log (user_id, action, entity_type, entity_id, details, ip_address) VALUES (?, ?, ?, ?, ?, ?)",
[$userId, $action, $entityType, $entityId !== null ? (string)$entityId : null, $details, $_SERVER['REMOTE_ADDR'] ?? '']
);
} catch (\Exception $e) {
// audit_log table may not exist on old installs
}
}
private function isRateLimited($ip, $email) {
try {
$count = $this->db->fetchOne(