Files
deskhub/DeskHub-Webseite/tests/login_unlock.php
T
2026-11-05 21:21:55 +00:00

83 lines
5.2 KiB
PHP

<?php
declare(strict_types=1);
/** Prüft login_guard_is_locked() und login_guard_unlock(): Web-, App- und 2FA-Zähler, andere Konten und die IP-Sperre bleiben unberührt. */
putenv('DESKHUB_TESTING=1');
putenv('DESKHUB_SQLITE_PATH=:memory:');
require_once dirname(__DIR__) . '/includes/bootstrap.php';
$failures = [];
$total = 0;
$check = function (bool $ok, string $what) use (&$failures, &$total): void {
$total++;
if (!$ok) $failures[] = $what;
};
$pdo = get_db();
$now = date('Y-m-d H:i:s');
$dept = (int) $pdo->query('SELECT id FROM departments ORDER BY id LIMIT 1')->fetchColumn();
$pdo->exec('DELETE FROM staff');
$ins = $pdo->prepare('INSERT INTO staff (name, email, password_hash, role, department_id, is_active, created_at) VALUES (?,?,?,?,?,1,?)');
$ins->execute(['Eva', 'Eva@Test.de', 'x', 'agent', $dept, $now]);
$eva = (int) $pdo->lastInsertId();
$ins->execute(['Max', 'max@test.de', 'x', 'agent', $dept, $now]);
$max = (int) $pdo->lastInsertId();
$log = function (string $bucket, string $id, int $n, ?string $at = null) use ($pdo, $now): void {
for ($i = 0; $i < $n; $i++) $pdo->prepare('INSERT INTO rate_limit_log (bucket, identifier, created_at) VALUES (?,?,?)')->execute([$bucket, $id, $at ?? $now]);
};
$count = fn(string $where) => (int) $pdo->query("SELECT COUNT(*) FROM rate_limit_log WHERE {$where}")->fetchColumn();
$max5 = login_guard_max_attempts();
$check($max5 === 5 && login_guard_lock_minutes() === 15, 'Standard: 5 Versuche, 15 Minuten');
$check(!login_guard_is_locked($pdo, 'eva@test.de'), 'ohne Versuche nicht gesperrt');
$log('staff_login_email', 'eva@test.de', 4);
$check(!login_guard_is_locked($pdo, 'eva@test.de'), 'vier Versuche: noch nicht gesperrt');
$log('mobile_staff_login_email', 'eva@test.de', 1);
$check(login_guard_is_locked($pdo, 'EVA@test.de '), 'Web und App zusammen erreichen die Grenze (Schreibweise egal)');
$check(!login_guard_is_locked($pdo, 'max@test.de') && !login_guard_is_locked($pdo, ''), 'andere Konten und leere Adresse: nicht gesperrt');
$pdo->exec("DELETE FROM rate_limit_log");
$log('staff_login_email', 'eva@test.de', 6, date('Y-m-d H:i:s', time() - 3600));
$check(!login_guard_is_locked($pdo, 'eva@test.de'), 'alte Versuche außerhalb des Zeitfensters zählen nicht');
$pdo->exec("DELETE FROM rate_limit_log");
$log('staff_login_email', 'eva@test.de', 5);
$log('mobile_staff_login_email', 'eva@test.de', 2);
$log('staff_totp_attempt', (string) $eva, 3);
$log('staff_login_email', 'max@test.de', 5);
$log('staff_totp_attempt', (string) $max, 2);
$log('staff_login_attempt', '203.0.113.5', 9);
$n = login_guard_unlock($pdo, $eva, $max);
$check($n === 10, 'gelöschte Zähler: Web 5 + App 2 + 2FA 3: ' . $n);
$check(!login_guard_is_locked($pdo, 'eva@test.de'), 'danach nicht mehr gesperrt');
$check($count("identifier = 'max@test.de'") === 5 && $count("bucket = 'staff_totp_attempt' AND identifier = '{$max}'") === 2, 'Max bleibt unberührt');
$check($count("bucket = 'staff_login_attempt'") === 9, 'die IP-Sperre bleibt bestehen');
$ev = $pdo->query("SELECT detail FROM login_events WHERE event = 'unlocked' AND staff_id = {$eva}")->fetchColumn();
$check(str_contains((string) $ev, 'Administrator'), 'Protokoll nennt den Administrator');
$check(login_guard_unlock($pdo, 999999) === 0, 'unbekannter Mitarbeiter: nichts passiert');
$check(login_guard_unlock($pdo, $eva, null, 'Sperre aufgehoben: Test') === 0, 'zweites Mal: nichts mehr zu löschen, kein Fehler');
$ev2 = $pdo->query("SELECT detail FROM login_events WHERE event = 'unlocked' AND staff_id = {$eva} ORDER BY id DESC LIMIT 1")->fetchColumn();
$check($ev2 === 'Sperre aufgehoben: Test', 'eigener Grund im Protokoll');
$check(isset(login_guard_event_labels()['unlocked']), 'Ereignisart ist beschriftet');
// Bekanntes Gerät
$check(!login_guard_known_device($pdo, $eva), 'ohne Cookie: kein bekanntes Gerät');
$cookie = bin2hex(random_bytes(16));
$_COOKIE[LOGIN_GUARD_COOKIE] = $cookie;
$check(!login_guard_known_device($pdo, $eva), 'Cookie, aber noch nicht gemerkt: unbekannt');
$pdo->prepare('INSERT INTO login_devices (staff_id, device_hash, label, channel, first_ip, last_ip, place, first_seen, last_seen) VALUES (?,?,?,?,?,?,?,?,?)')
->execute([$eva, hash('sha256', $eva . '|' . $cookie), 'Chrome auf Windows', 'web', '1.1.1.1', '1.1.1.1', 'Netz', $now, $now]);
$check(login_guard_known_device($pdo, $eva) && login_guard_known_device_for_email('eva@test.de'), 'gemerktes Gerät wird erkannt (auch über die E-Mail-Adresse)');
$check(!login_guard_known_device($pdo, $max) && !login_guard_known_device_for_email('max@test.de'), 'für ein anderes Konto gilt das Gerät nicht');
$_COOKIE[LOGIN_GUARD_COOKIE] = 'kein-gueltiges-cookie';
$check(!login_guard_known_device($pdo, $eva), 'ungültiges Cookie: unbekannt');
$_COOKIE[LOGIN_GUARD_COOKIE] = $cookie;
$check(!login_guard_known_device_for_email('niemand@test.de') && !login_guard_known_device_for_email(''), 'unbekannte oder leere Adresse: unbekannt');
$pdo->exec("UPDATE staff SET is_active = 0 WHERE id = {$eva}");
$check(!login_guard_known_device_for_email('eva@test.de'), 'deaktiviertes Konto: unbekannt');
if ($failures) {
fwrite(STDERR, "Fehlgeschlagen:\n - " . implode("\n - ", $failures) . "\n");
exit(1);
}
echo "Login unlock checks passed ({$total} Prüfungen).\n";