refactor(security): add typed brute-force detection event

Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
2026-08-05 23:31:13 -04:00
parent a8e29d0305
commit e223ae7543
7 changed files with 165 additions and 24 deletions
@@ -0,0 +1,91 @@
<?php
declare(strict_types=1);
namespace KTXC\Security\Event;
use KTXF\Event\Event;
final class BruteForceDetectedEvent extends Event implements SecurityRequestEventInterface
{
private readonly string $reason;
public function __construct(
private readonly string $ipAddress,
private readonly int $failureCount,
private readonly int $windowSeconds,
?string $tenantId = null,
) {
if ($ipAddress === '') {
throw new \InvalidArgumentException('Brute-force detection requires an IP address.');
}
if ($failureCount < 1) {
throw new \InvalidArgumentException('Brute-force detection requires at least one failure.');
}
if ($windowSeconds < 1) {
throw new \InvalidArgumentException('Brute-force detection requires a positive window.');
}
$this->reason = sprintf(
'%d failed attempts in %d seconds',
$failureCount,
$windowSeconds,
);
parent::__construct(
self::class,
['failureCount' => $failureCount, 'windowSeconds' => $windowSeconds],
$tenantId,
);
}
public function getIpAddress(): string
{
return $this->ipAddress;
}
public function getFailureCount(): int
{
return $this->failureCount;
}
public function getWindowSeconds(): int
{
return $this->windowSeconds;
}
public function getDeviceFingerprint(): ?string
{
return null;
}
public function getUserAgent(): ?string
{
return null;
}
public function getRequestPath(): ?string
{
return null;
}
public function getRequestMethod(): ?string
{
return null;
}
public function getUserId(): ?string
{
return null;
}
public function getReason(): string
{
return $this->reason;
}
public function getSeverity(): int
{
return SecurityEvent::SEVERITY_CRITICAL;
}
}