This commit is contained in:
2025-12-04 22:33:05 +01:00
parent 316175e158
commit 9dee06cdd6
145 changed files with 16865 additions and 88 deletions

View File

@@ -0,0 +1,138 @@
<?php
// inc/auth_helpers.php robustes Session-Handling (2025-09-05)
declare(strict_types=1);
/** ===== Session ===== */
function auth_start_session(array $cfg): void {
if (session_status() === PHP_SESSION_NONE) {
$name = $cfg['auth']['session_name'] ?? 'et_session';
session_name($name);
// Defaults
$cookiePath = $cfg['auth']['cookie_path'] ?? '/';
$cookieDomain = $cfg['auth']['cookie_domain'] ?? '';
$cookieSecure = $cfg['auth']['cookie_secure'] ?? true;
$cookieHttpOnly = $cfg['auth']['cookie_httponly'] ?? true;
$cookieSameSite = $cfg['auth']['cookie_samesite'] ?? 'Lax';
// PHP 7.3+ array API
session_set_cookie_params([
'lifetime' => 0,
'path' => $cookiePath,
'domain' => $cookieDomain,
'secure' => $cookieSecure,
'httponly' => $cookieHttpOnly,
'samesite' => $cookieSameSite,
]);
session_start();
}
}
/** ===== Auth Core ===== */
function auth_login(PDO $pdoCustomers, array $cfg, string $email, string $password): array {
auth_start_session($cfg);
$sql = "SELECT cu.id, cu.customer_id, cu.email, cu.password_hash, cu.role,
c.slug AS customer_slug, c.plan, c.status
FROM customer_users cu
JOIN customers c ON c.id = cu.customer_id
WHERE cu.email = :email AND cu.is_active = 1
LIMIT 1";
$st = $pdoCustomers->prepare($sql);
$st->execute([':email' => $email]);
$u = $st->fetch(PDO::FETCH_ASSOC);
if (!$u || !password_verify($password, $u['password_hash'])) {
return ['ok' => false, 'error' => 'invalid_credentials'];
}
if (($u['status'] ?? 'active') !== 'active') {
return ['ok' => false, 'error' => 'customer_inactive'];
}
// neue Session-ID, alte wird invalidiert
session_regenerate_id(true);
$_SESSION['user'] = [
'id' => (int)$u['id'],
'email' => $u['email'],
'role' => $u['role'],
'customer_id' => (int)$u['customer_id'],
'customer_slug' => $u['customer_slug'],
'plan' => $u['plan'],
];
return ['ok' => true, 'user' => $_SESSION['user']];
}
function auth_logout(array $cfg): void {
auth_start_session($cfg);
// Sessiondaten löschen
$_SESSION = [];
// Cookie-Parameter aus der aktiven Session
$params = session_get_cookie_params();
$name = session_name();
// Kandidaten für Domain/Path, um "falsch" gesetzte Cookies sicher zu treffen
$host = $_SERVER['HTTP_HOST'] ?? '';
$cfgDomain = $cfg['auth']['cookie_domain'] ?? '';
$paths = array_values(array_unique([$params['path'] ?? '/', '/', '']));
$domains = array_values(array_unique([
$params['domain'] ?? '',
$cfgDomain,
$host,
ltrim($host, '.'),
(strpos($host, '.') !== false ? '.' . ltrim($host, '.') : $host),
]));
// Alle Varianten invalidieren (secure/httponly wie gesetzt)
foreach ($paths as $p) {
foreach ($domains as $d) {
if ($d === null) continue;
setcookie($name, '', time() - 3600, $p, $d, $params['secure'] ?? true, $params['httponly'] ?? true);
}
// zusätzlich: ohne Domain (trifft Host-spezifische Cookies)
setcookie($name, '', time() - 3600, $p, '', $params['secure'] ?? true, $params['httponly'] ?? true);
}
// Session beenden
session_destroy();
session_write_close();
// In Staging aggressiv: Browser bitten, Cookies zu löschen (nicht jeder Browser respektiert das sofort)
if (($cfg['env'] ?? 'prod') === 'staging') {
header('Clear-Site-Data: "cookies"', false);
}
}
function auth_require(array $cfg): void {
auth_start_session($cfg);
if (empty($_SESSION['user'])) {
http_response_code(401);
header('Content-Type: application/json; charset=utf-8');
echo json_encode(['ok' => false, 'error' => 'unauthorized']);
exit;
}
}
function require_role(array $cfg, array $roles): void {
auth_start_session($cfg);
$r = $_SESSION['user']['role'] ?? null;
if (!$r || !in_array($r, $roles, true)) {
http_response_code(403);
header('Content-Type: application/json; charset=utf-8');
echo json_encode(['ok' => false, 'error' => 'forbidden']);
exit;
}
}
function current_user(array $cfg): ?array {
auth_start_session($cfg);
return $_SESSION['user'] ?? null;
}
function current_customer_id(array $cfg): ?int {
$u = current_user($cfg);
return $u['customer_id'] ?? null;
}