function csrf_field(): string {
$token = htmlspecialchars($_SESSION['csrf_token'] ?? '', ENT_QUOTES, 'UTF-8');
return '';
}
function redirect(string $url): never {
header("Location: $url");
exit;
}
function is_logged_in(): bool {
return !empty($_SESSION['user_id']);
}
function require_login(): void {
if (!is_logged_in()) {
redirect('/index.php');
}
// Session-Fingerprint prüfen
$fp = sha1(($_SERVER['REMOTE_ADDR'] ?? '') . ($_SERVER['HTTP_USER_AGENT'] ?? ''));
if (($_SESSION['fingerprint'] ?? '') !== $fp) {
session_destroy();
redirect('/index.php');
}
// Session-Timeout prüfen
$timeout = (int)$_ENV['SESSION_LIFETIME'] * 60;
if (isset($_SESSION['last_activity']) && (time() - $_SESSION['last_activity']) > $timeout) {
session_destroy();
redirect('/index.php?timeout=1');
}
// ── MFA-Check ────────────────────────────────────────────
// Seiten die OHNE mfa_verified erreichbar sein müssen:
$current = $_SERVER['PHP_SELF'] ?? '';
$mfa_pages = [
'/auth/login.php',
'/auth/logout.php',
'/auth/mfa-verify.php',
'/auth/mfa-setup.php',
'/auth/mfa-email-send.php',
];
$is_mfa_page = false;
foreach ($mfa_pages as $p) {
if (str_ends_with($current, $p)) {
$is_mfa_page = true;
break;
}
}
if (!$is_mfa_page && empty($_SESSION['mfa_verified'])) {
redirect('/auth/mfa-verify.php');
}
// ── Ende MFA-Check ───────────────────────────────────────
$_SESSION['last_activity'] = time();
}
function has_role(string ...$roles): bool {
$userRoles = $_SESSION['roles'] ?? [];
foreach ($roles as $role) {
if (in_array($role, $userRoles, true)) return true;
}
return false;
}
function require_role(string ...$roles): void {
require_login();
if (!has_role(...$roles)) {
http_response_code(403);
die('Keine Berechtigung.');
}
}
// ── Modul-Berechtigungen ─────────────────────────────────────
// Granulare Berechtigungen für geschützte Module (Gefahrenzone)
// Verwaltet in: Benutzerverwaltung → Gefahrenzone
// Tabelle: user_permissions
//
// Verfügbare Permissions:
// intune – Intune/MDM-Dokumentation
// software – Software & Lizenzverwaltung
// zugaenge_credentials – Zugänge & Credentials (IT-Tresor)
// benutzerverwaltung – Benutzerverwaltung
// einstellungen – Systemeinstellungen
// export – Daten-Export
// firmenwechsel – Firmenwechsel/Verrechnungsprozess
// ------------------------------------------------------------
function has_permission(string $permission): bool {
if (!is_logged_in()) return false;
// Superadmin + Admin haben immer alle Berechtigungen
if (has_role('superadmin', 'admin')) return true;
try {
$stmt = db()->prepare("
SELECT COUNT(*)
FROM user_permissions
WHERE user_id = ?
AND permission = ?
");
$stmt->execute([$_SESSION['user_id'], $permission]);
return (bool)$stmt->fetchColumn();
} catch (Exception $e) {
error_log('has_permission Fehler: ' . $e->getMessage());
return false;
}
}
function require_permission(string $permission): void {
require_login();
if (!has_permission($permission)) {
http_response_code(403);
die('Keine Berechtigung.');
}
}
// ── Audit-Log ────────────────────────────────────────────────
function audit(string $aktion, string $tabelle = null, int $datensatzId = null, $altWert = null, $neuWert = null): void {
try {
db()->prepare("
INSERT INTO audit_log (tenant_id, user_id, aktion, tabelle, datensatz_id, alt_wert, neu_wert, ip_adresse)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
")->execute([
$_SESSION['tenant_id'] ?? 1,
$_SESSION['user_id'] ?? null,
$aktion,
$tabelle,
$datensatzId,
$altWert ? json_encode($altWert, JSON_UNESCAPED_UNICODE) : null,
$neuWert ? json_encode($neuWert, JSON_UNESCAPED_UNICODE) : null,
$_SERVER['REMOTE_ADDR'] ?? null,
]);
} catch (Exception $e) {
// Audit-Fehler niemals die App zum Absturz bringen
error_log('Audit-Log Fehler: ' . $e->getMessage());
}
}
// ── Settings / Listen aus DB laden ───────────────────────────
// Gibt den Rohwert als String zurück (z.B. für SMTP-Einstellungen)
function getSetting(string $key, string $default = ''): string {
try {
$tid = $_SESSION['tenant_id'] ?? 1;
$stmt = db()->prepare("SELECT wert FROM settings WHERE tenant_id = ? AND schluessel = ?");
$stmt->execute([$tid, $key]);
$val = $stmt->fetchColumn();
return ($val !== false && $val !== '') ? $val : $default;
} catch (Exception $e) {
return $default;
}
}
// Gibt mehrzeiligen Wert als gefilteres Array zurück (eine Zeile = ein Eintrag)
function getListe(string $key, string $default = ''): array {
$raw = getSetting($key, $default);
return array_values(array_filter(array_map('trim', explode("\n", $raw))));
}
Fatal error: Uncaught Error: Call to undefined function is_logged_in() in /var/www/leo-invent/public_html/index.php:3
Stack trace:
#0 {main}
thrown in /var/www/leo-invent/public_html/index.php on line 3