mirror of
https://github.com/nextcloud/all-in-one.git
synced 2026-05-21 02:40:09 +00:00
Throttle login attempts to 5 failures per 5 minutes
AI-assistant: Copilot v1.0.7 (Claude Opus 4.6) Signed-off-by: Pablo Zmdl <pablo@nextcloud.com>
This commit is contained in:
@@ -29,6 +29,9 @@ function showPassword(id) {
|
||||
const xhr = e.target;
|
||||
if (xhr.status === 201) {
|
||||
window.location.replace(xhr.getResponseHeader('Location'));
|
||||
} else if ([422, 429].includes(xhr.status)) {
|
||||
disableSpinner()
|
||||
showError(xhr.response);
|
||||
} else if (xhr.status === 422) {
|
||||
disableSpinner()
|
||||
showError(xhr.response);
|
||||
|
||||
@@ -11,23 +11,52 @@ use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
|
||||
readonly class LoginController {
|
||||
private const int MAX_LOGIN_ATTEMPTS_PER_TTL = 5;
|
||||
private const int LOGIN_COUNTER_TTL = 300;
|
||||
private const string RATE_LIMIT_CACHE_KEY = 'login_failed_attempts';
|
||||
|
||||
public function __construct(
|
||||
private AuthManager $authManager,
|
||||
private DockerActionManager $dockerActionManager,
|
||||
) {
|
||||
}
|
||||
|
||||
private function getFailedLoginCount() : int {
|
||||
$count = apcu_fetch(self::RATE_LIMIT_CACHE_KEY);
|
||||
return $count !== false ? (int)$count : 0;
|
||||
}
|
||||
|
||||
private function incrementFailedLoginCount() : void {
|
||||
if (!apcu_exists(self::RATE_LIMIT_CACHE_KEY)) {
|
||||
apcu_store(self::RATE_LIMIT_CACHE_KEY, 1, self::LOGIN_COUNTER_TTL);
|
||||
} else {
|
||||
apcu_inc(self::RATE_LIMIT_CACHE_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
private function resetFailedLoginCount() : void {
|
||||
apcu_delete(self::RATE_LIMIT_CACHE_KEY);
|
||||
}
|
||||
|
||||
public function TryLogin(Request $request, Response $response, array $args) : Response {
|
||||
if (!$this->dockerActionManager->isLoginAllowed()) {
|
||||
$response->getBody()->write("The login is blocked since Nextcloud is running.");
|
||||
return $response->withHeader('Location', '.')->withStatus(422);
|
||||
}
|
||||
|
||||
if ($this->getFailedLoginCount() >= self::MAX_LOGIN_ATTEMPTS_PER_TTL) {
|
||||
$response->getBody()->write("Too many failed login attempts. Please try again in some minutes.");
|
||||
return $response->withHeader('Location', '.')->withStatus(429);
|
||||
}
|
||||
|
||||
$password = $request->getParsedBody()['password'] ?? '';
|
||||
if($this->authManager->CheckCredentials($password)) {
|
||||
$this->resetFailedLoginCount();
|
||||
$this->authManager->SetAuthState(true);
|
||||
return $response->withHeader('Location', '.')->withStatus(201);
|
||||
}
|
||||
|
||||
$this->incrementFailedLoginCount();
|
||||
$response->getBody()->write("The password is incorrect.");
|
||||
return $response->withHeader('Location', '.')->withStatus(422);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user