0, 'path' => '/', 'httponly' => true, 'secure' => $secure, 'samesite' => 'Lax', ]); session_name('OMSORG_FELIX_APP'); session_start(); function is_logged_in(): bool { if (empty($_SESSION['felix_logged_in']) || empty($_SESSION['felix_username'])) { return false; } $stmt = db()->prepare('SELECT active FROM users WHERE username = ?'); $stmt->execute([$_SESSION['felix_username']]); $row = $stmt->fetch(); return $row && (int)$row['active'] === 1; } function require_login(): void { if (!is_logged_in()) { header('Location: ../index.php'); exit; } if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST)) { verify_csrf(); } } function csrf_token(): string { if (empty($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); } return $_SESSION['csrf_token']; } function csrf_field(): string { return ''; } function verify_csrf(): void { $token = $_POST['csrf_token'] ?? ''; if (!hash_equals(csrf_token(), $token)) { http_response_code(403); exit('Ungültige Anfrage.'); } } function require_admin(): void { require_login(); if (!is_admin()) { header('Location: ../pages/dashboard.php'); exit; } } function is_admin(): bool { return isset($_SESSION['felix_role']) && $_SESSION['felix_role'] === 'admin'; } function current_user(): array|false { static $cache = null; static $cachedFor = null; $username = $_SESSION['felix_username'] ?? ''; if ($username === '') return false; if ($cache !== null && $cachedFor === $username) return $cache; $stmt = db()->prepare('SELECT * FROM users WHERE username = ?'); $stmt->execute([$username]); $cache = $stmt->fetch(); $cachedFor = $username; return $cache; } function current_username(): string { return $_SESSION['felix_username'] ?? ''; } function current_name(): string { return $_SESSION['felix_name'] ?? ''; } function e(string $s): string { return htmlspecialchars($s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); } // --- Login rate limiting (IP-based, persisted in DB) --- function login_recent_fails(string $ip, int $windowSec = 600): int { $cutoff = date('c', time() - $windowSec); $stmt = db()->prepare( 'SELECT COUNT(*) FROM login_attempts WHERE ip = ? AND attempted_at >= ?' ); $stmt->execute([$ip, $cutoff]); return (int) $stmt->fetchColumn(); } function login_record_fail(string $ip, string $username): void { db()->prepare( 'INSERT INTO login_attempts (ip, username, attempted_at) VALUES (?, ?, ?)' )->execute([$ip, $username, date('c')]); } function login_clear_fails(string $ip): void { db()->prepare('DELETE FROM login_attempts WHERE ip = ?')->execute([$ip]); }