prepare('SELECT * FROM users WHERE id = ?'); $stmt->execute([$_SESSION['user_id']]); $user = $stmt->fetch(); return $cache = ($user ?: null); } function require_customer_login(): array { $user = current_user(); if (!$user) { flash('error', 'Bitte melden Sie sich an, um fortzufahren.'); redirect(base_path() . '/login.php'); } return $user; } // --------------------------------------------------------------- // Mitarbeiter-Auth // --------------------------------------------------------------- function staff_login(array $staff): void { session_regenerate_id(true); $_SESSION['staff_id'] = $staff['id']; $_SESSION['staff_last_activity'] = time(); $_SESSION['staff_login_at'] = time(); staff_touch_activity(get_db(), (int) $staff['id']); login_guard_success($staff, 'web'); } function staff_logout(): void { // Automatische Zeiterfassung: offene Arbeits-Sitzungen beim Logout sofort // abschließen, statt bis zum nächsten Stale-Sweep (Auto-Cron) zu warten. if (!empty($_SESSION['staff_id'])) { finalize_all_sessions_for_staff(get_db(), (int) $_SESSION['staff_id']); } unset($_SESSION['staff_id']); } function current_staff(): ?array { if (empty($_SESSION['staff_id'])) { return null; } static $cache = null; if ($cache !== null) { return $cache; } $stmt = get_db()->prepare('SELECT * FROM staff WHERE id = ? AND is_active = 1'); $stmt->execute([$_SESSION['staff_id']]); $staff = $stmt->fetch(); // „Das war ich nicht“ (Anmelde-Wächter): ältere Browser-Sitzungen dieses Kontos sind ungültig if ($staff && login_guard_session_revoked((int) $staff['id'])) { unset($_SESSION['staff_id']); return null; } return $cache = ($staff ?: null); } function require_staff_login(): array { $staff = current_staff(); if (!$staff) { // War die Sitzung gerade wegen Inaktivität abgelaufen (ein Hintergrund-Abruf hat sie schon beendet), den Grund nennen if (!empty($_SESSION['staff_timed_out'])) { unset($_SESSION['staff_timed_out']); flash('error', 'Sie wurden wegen Inaktivität abgemeldet. Bitte melden Sie sich erneut an.'); } else { flash('error', 'Bitte melden Sie sich an, um fortzufahren.'); } redirect(staff_base_path() . '/login.php'); } // Inaktivitäts-Timeout $timeout = (int)(get_setting('staff_session_timeout', '30') ?: 30); // Minuten $last = $_SESSION['staff_last_activity'] ?? 0; $userNavigation = staff_is_user_navigation(); if ($last && (time() - $last) > ($timeout * 60)) { staff_logout(); if (session_status() !== PHP_SESSION_ACTIVE) session_start(); $_SESSION['staff_timed_out'] = time(); // merken, damit die Anmeldeseite den Grund nennen kann (siehe oben) if (!staff_is_document_request()) { // Hintergrund-Abruf (Aktualisierung der Liste, Chat-Abfrage …): kurze Antwort statt Weiterleitung auf die Anmeldeseite. // Das Laden einer Seite dagegen führt immer zur Anmeldung, auch wenn der Browser es selbst ausgelöst hat (Tab neu geladen). http_response_code(401); header('Content-Type: application/json; charset=utf-8'); echo json_encode(['error' => 'Session abgelaufen', 'sessionExpired' => true]); exit; } unset($_SESSION['staff_timed_out']); $_SESSION['flash'] = [['type'=>'error','message'=>'Sie wurden wegen Inaktivität abgemeldet. Bitte melden Sie sich erneut an.']]; redirect('/staff/login.php'); } // Nur echte Bedienung hält die Sitzung am Leben: ein vom Nutzer ausgelöster Seitenaufruf oder der Ping der Seite bei Klicks und // Tastatureingaben (staff/session_ping.php). Hintergrund-Abfragen der offenen Seiten (Listen, Chat, Anwesenheit alle paar // Sekunden) zählen nicht, sonst würde eine offene Seite die Sitzung ewig verlängern und die Abmeldung nie greifen. if ($userNavigation) { $_SESSION['staff_last_activity'] = time(); staff_touch_activity(get_db(), (int) $staff['id']); } // "Eingebauter" Ersatz für echte Server-Cronjobs (siehe includes/pseudo_cron.php): // bei jeder authentifizierten Backend-Anfrage werden fällige Jobs im Hintergrund geprüft. pseudo_cron_schedule_background(); return $staff; } function require_admin(): array { $staff = require_staff_login(); if ($staff['role'] !== 'admin') { flash('error', 'Diese Funktion ist nur für Administratoren verfügbar.'); redirect(staff_base_path() . '/reports.php'); } return $staff; } /** * Wie require_admin(), lässt aber zusätzlich Agenten mit einer benutzerdefinierten Rolle * durch, die genau dieses Adminportal-Recht zugewiesen bekommen haben ("Co-Admin" für * einzelne Bereiche, siehe admin_section_permissions() / staff/roles.php). */ function require_admin_or_permission(string $permission): array { $staff = require_staff_login(); if ($staff['role'] !== 'admin' && !has_permission($staff, $permission)) { flash('error', 'Für diese Funktion fehlt die erforderliche Berechtigung.'); redirect(staff_base_path() . '/reports.php'); } return $staff; } /** Pfad-Helfer, damit Includes unabhängig vom Aufrufort funktionieren */ function base_path(): string { return ''; } function staff_base_path(): string { return '/staff'; }