fix(firewall): serialize automatic brute-force blocking

Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
2026-07-30 23:05:32 -04:00
parent a5c10e9b9b
commit ad6443bff3
4 changed files with 125 additions and 9 deletions
+54
View File
@@ -15,6 +15,7 @@ class FirewallStore
{
protected const RULES_COLLECTION = 'firewall_rules';
protected const LOGS_COLLECTION = 'firewall_logs';
protected const BRUTE_FORCE_CLAIMS_COLLECTION = 'firewall_brute_force_claims';
public function __construct(
protected readonly DataStore $dataStore
@@ -343,6 +344,48 @@ class FirewallStore
]);
}
/**
* Atomically claim responsibility for responding to a tenant/IP brute-force incident.
*/
public function claimBruteForce(
string $tenantId,
string $ipAddress,
int $claimDurationSeconds
): bool {
if ($claimDurationSeconds < 1) {
throw new \InvalidArgumentException('Brute-force claim duration must be greater than zero.');
}
$now = new \DateTimeImmutable();
$claimId = hash('sha256', $tenantId."\0".$ipAddress);
$collection = $this->dataStore->selectCollection(self::BRUTE_FORCE_CLAIMS_COLLECTION);
$collection->deleteOne([
'_id' => $claimId,
'expiresAt' => ['$lte' => $now->format(\DateTimeInterface::ATOM)],
]);
try {
$collection->insertOne([
'_id' => $claimId,
'tenantId' => $tenantId,
'ipAddress' => $ipAddress,
'createdAt' => $now->format(\DateTimeInterface::ATOM),
'expiresAt' => $now
->modify("+{$claimDurationSeconds} seconds")
->format(\DateTimeInterface::ATOM),
]);
} catch (\MongoDB\Driver\Exception\BulkWriteException $error) {
if ($error->getCode() === 11000) {
return false;
}
throw $error;
}
return true;
}
/**
* Get blocked requests count for dashboard
*/
@@ -375,4 +418,15 @@ class FirewallStore
return $result->getDeletedCount();
}
public function cleanupExpiredBruteForceClaims(): int
{
$result = $this->dataStore
->selectCollection(self::BRUTE_FORCE_CLAIMS_COLLECTION)
->deleteMany([
'expiresAt' => ['$lte' => (new \DateTimeImmutable())->format(\DateTimeInterface::ATOM)],
]);
return $result->getDeletedCount();
}
}