Initial commit: OMSORG website + Mitarbeiter-App
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/db.php';
|
||||
|
||||
$secure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
|
||||
|| (($_SERVER['SERVER_PORT'] ?? null) == 443);
|
||||
session_set_cookie_params([
|
||||
'lifetime' => 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 '<input type="hidden" name="csrf_token" value="' . csrf_token() . '">';
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
Reference in New Issue
Block a user