move_detect/cloud/src/Auth/AuthController.php

50 lines
1.9 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Auth;
use App\Http\{Request, Response};
use App\Security\Password;
use App\Repository\{UserRepository, AuditRepository};
final class AuthController
{
public function __construct(
private UserRepository $users,
private AuditRepository $audit,
private int $maxAttempts = 5,
private int $lockSeconds = 900,
) {}
public function login(Request $req, SessionStore $sess, int $now): Response
{
$username = (string)$req->post('username');
$password = (string)$req->post('password');
$u = $this->users->byUsername($username);
if ($u === null) return Response::json(['error' => 'invalid_credentials'], 401);
if ((int)($u['locked_until'] ?? 0) > $now) {
$this->audit->append($username, 'login_locked', 'auth', [], []);
return Response::json(['error' => 'locked'], 423);
}
if (!Password::verify($password, (string)$u['password_hash'])) {
$attempts = (int)($u['failed_attempts'] ?? 0) + 1;
$lock = $attempts >= $this->maxAttempts ? $now + $this->lockSeconds : 0;
$this->users->recordFailure((int)$u['id'], $lock);
$this->audit->append($username, 'login_failed', 'auth', [], ['attempts' => $attempts]);
return Response::json(['error' => 'invalid_credentials'], 401);
}
$this->users->clearFailures((int)$u['id']);
$sess->set('user_id', (int)$u['id']);
$sess->set('username', $u['username']);
$sess->set('role', $u['role']);
$sess->set('site_scope', $u['site_scope'] ?? null);
$this->audit->append($username, 'login_ok', 'auth', [], []);
return Response::json(['ok' => true], 200);
}
public function logout(SessionStore $sess): Response
{
$sess->clear();
return Response::json(['ok' => true], 200);
}
}