Files
2026-11-05 21:21:44 +00:00

77 lines
4.4 KiB
PHP

<?php
/**
* „Von allen Geräten abmelden“: beendet alle Anmeldungen eines Mitarbeiters auf einmal.
* - Browser-Sitzungen (current_staff() prüft den Zeitpunkt „login_guard_revoke_<id>“, siehe includes/login_guard.php)
* - Anmeldungen der Apps (Android, Windows-Programm, Chrome-Erweiterung): ihre Tokens werden gelöscht, die Programme fragen danach neu nach der Anmeldung
* - Push: Browser-Abonnements und Android-Geräte, damit ein verlorenes Gerät keine Benachrichtigungen mehr bekommt
* - Ticket-Sperren des Mitarbeiters und laufende Zeiterfassung
* Das Passwort bleibt unverändert; wer es zusätzlich ändern will, tut das im Profil oder über „Das war ich nicht“.
*/
/**
* @param bool $keepCurrentSession true = die Sitzung, in der das gerade geschieht, bleibt angemeldet (nur bei eigener Abmeldung sinnvoll)
* @return array{at:int,apps:int,browsers:int,devices:int,locks:int}
*/
function staff_sign_out_everywhere(PDO $pdo, int $staffId, ?int $byStaffId = null, bool $keepCurrentSession = false): array
{
$st = $pdo->prepare('SELECT id, name, email FROM staff WHERE id = ?');
$st->execute([$staffId]);
$staff = $st->fetch();
$res = ['at' => 0, 'apps' => 0, 'browsers' => 0, 'devices' => 0, 'locks' => 0];
if (!$staff) return $res;
$count = function (string $table) use ($pdo, $staffId): int {
try {
$c = $pdo->prepare("SELECT COUNT(*) FROM {$table} WHERE staff_id = ?");
$c->execute([$staffId]);
return (int) $c->fetchColumn();
} catch (Throwable $e) {
return 0;
}
};
$res['apps'] = $count('mobile_api_tokens');
$res['browsers'] = $count('push_subscriptions');
$res['devices'] = $count('push_devices');
$at = time();
$res['at'] = $at;
set_setting('login_guard_revoke_' . $staffId, (string) $at);
foreach (['mobile_api_tokens', 'push_subscriptions', 'push_devices'] as $table) {
try { $pdo->prepare("DELETE FROM {$table} WHERE staff_id = ?")->execute([$staffId]); } catch (Throwable $e) { error_log('signout ' . $table . ': ' . $e->getMessage()); }
}
try {
$l = $pdo->prepare('UPDATE tickets SET locked_by = NULL, locked_at = NULL, updated_at = updated_at WHERE locked_by = ?');
$l->execute([$staffId]);
$res['locks'] = $l->rowCount();
} catch (Throwable $e) {
error_log('signout locks: ' . $e->getMessage());
}
if (function_exists('finalize_all_sessions_for_staff')) {
try { finalize_all_sessions_for_staff($pdo, $staffId); } catch (Throwable $e) { error_log('signout time: ' . $e->getMessage()); }
}
// Die eigene Sitzung bleibt, wenn gewünscht: Ihr Anmeldezeitpunkt liegt dann hinter dem Zeitpunkt des Widerrufs
if ($keepCurrentSession && $byStaffId === $staffId && PHP_SAPI !== 'cli' && session_status() === PHP_SESSION_ACTIVE) {
$_SESSION['staff_login_at'] = $at + 1;
}
$self = $byStaffId === null || $byStaffId === $staffId;
$detail = $self ? 'Von allen Geräten abgemeldet (eigene Veranlassung)' : 'Von allen Geräten abgemeldet durch Administrator #' . (int) $byStaffId;
try { login_guard_record($pdo, 'signout', $staffId, (string) $staff['email'], 'web', $detail); } catch (Throwable $e) { error_log('signout log: ' . $e->getMessage()); }
audit_log($byStaffId ?? $staffId, "Mitarbeiter #{$staffId} von allen Geräten abgemeldet ({$res['apps']} App-Anmeldungen, {$res['browsers']} Browser-Push, {$res['devices']} App-Geräte)");
if (!$self && trim((string) $staff['email']) !== '') {
send_mail((string) $staff['email'], 'Sie wurden von allen Geräten abgemeldet',
email_wrap('<h2 style="margin:0 0 14px;color:#0f172a;font-size:22px;line-height:1.3;font-weight:700;font-family:Segoe UI,Arial,sans-serif;">Von allen Geräten abgemeldet</h2><p style="margin:0;color:#334155">Ein Administrator hat Ihre Anmeldungen in DeskHub beendet. Bitte melden Sie sich neu an. Haben Sie das nicht erwartet, wenden Sie sich an Ihre Administration.</p>'));
}
return $res;
}
/** Aktive App-Anmeldungen eines Mitarbeiters (Android, Windows, Chrome) für die Anzeige. @return list<array<string,mixed>> */
function staff_app_logins(PDO $pdo, int $staffId): array
{
$s = $pdo->prepare('SELECT device_name, last_used_at, created_at FROM mobile_api_tokens WHERE staff_id = ? AND revoked_at IS NULL AND expires_at > ? ORDER BY COALESCE(last_used_at, created_at) DESC');
$s->execute([$staffId, db_now()]);
return $s->fetchAll();
}