instances added god may help
This commit is contained in:
@@ -10,37 +10,41 @@ class RateLimiterService
|
||||
{
|
||||
private const HASH_ALGO = 'sha256';
|
||||
|
||||
public static function hit(string $scope, string $subject, int $maxHits, int $windowSeconds, int $blockSeconds): array
|
||||
public function __construct(private readonly RateLimitRepository $rateLimitRepository)
|
||||
{
|
||||
return self::apply($scope, $subject, $maxHits, $windowSeconds, $blockSeconds, true);
|
||||
}
|
||||
|
||||
public static function registerFailure(string $scope, string $subject, int $maxHits, int $windowSeconds, int $blockSeconds): array
|
||||
public function hit(string $scope, string $subject, int $maxHits, int $windowSeconds, int $blockSeconds): array
|
||||
{
|
||||
return self::apply($scope, $subject, $maxHits, $windowSeconds, $blockSeconds, true);
|
||||
return $this->apply($scope, $subject, $maxHits, $windowSeconds, $blockSeconds, true);
|
||||
}
|
||||
|
||||
public static function isBlocked(string $scope, string $subject): array
|
||||
public function registerFailure(string $scope, string $subject, int $maxHits, int $windowSeconds, int $blockSeconds): array
|
||||
{
|
||||
$normalized = self::normalize($scope, $subject);
|
||||
return $this->apply($scope, $subject, $maxHits, $windowSeconds, $blockSeconds, true);
|
||||
}
|
||||
|
||||
public function isBlocked(string $scope, string $subject): array
|
||||
{
|
||||
$normalized = $this->normalize($scope, $subject);
|
||||
if ($normalized === null) {
|
||||
return ['allowed' => true, 'retry_after' => 0];
|
||||
}
|
||||
|
||||
try {
|
||||
$row = RateLimitRepository::findByScopeAndHash($normalized['scope'], $normalized['subject_hash']);
|
||||
$row = $this->rateLimitRepository->findByScopeAndHash($normalized['scope'], $normalized['subject_hash']);
|
||||
if (!is_array($row)) {
|
||||
return ['allowed' => true, 'retry_after' => 0];
|
||||
}
|
||||
|
||||
$blockedUntilTs = self::parseTimestamp((string) ($row['blocked_until'] ?? ''));
|
||||
$blockedUntilTs = $this->parseTimestamp((string) ($row['blocked_until'] ?? ''));
|
||||
if ($blockedUntilTs === null) {
|
||||
return ['allowed' => true, 'retry_after' => 0];
|
||||
}
|
||||
|
||||
$nowTs = time();
|
||||
if ($blockedUntilTs <= $nowTs) {
|
||||
RateLimitRepository::updateStateById(
|
||||
$this->rateLimitRepository->updateStateById(
|
||||
(int) ($row['id'] ?? 0),
|
||||
0,
|
||||
gmdate('Y-m-d H:i:s', $nowTs),
|
||||
@@ -59,21 +63,21 @@ class RateLimiterService
|
||||
}
|
||||
}
|
||||
|
||||
public static function reset(string $scope, string $subject): void
|
||||
public function reset(string $scope, string $subject): void
|
||||
{
|
||||
$normalized = self::normalize($scope, $subject);
|
||||
$normalized = $this->normalize($scope, $subject);
|
||||
if ($normalized === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
RateLimitRepository::deleteByScopeAndHash($normalized['scope'], $normalized['subject_hash']);
|
||||
$this->rateLimitRepository->deleteByScopeAndHash($normalized['scope'], $normalized['subject_hash']);
|
||||
} catch (\Throwable $exception) {
|
||||
// Ignore reset failures.
|
||||
}
|
||||
}
|
||||
|
||||
private static function apply(
|
||||
private function apply(
|
||||
string $scope,
|
||||
string $subject,
|
||||
int $maxHits,
|
||||
@@ -81,7 +85,7 @@ class RateLimiterService
|
||||
int $blockSeconds,
|
||||
bool $increment
|
||||
): array {
|
||||
$normalized = self::normalize($scope, $subject);
|
||||
$normalized = $this->normalize($scope, $subject);
|
||||
if ($normalized === null) {
|
||||
return ['allowed' => true, 'retry_after' => 0];
|
||||
}
|
||||
@@ -95,7 +99,7 @@ class RateLimiterService
|
||||
$nowSql = gmdate('Y-m-d H:i:s', $nowTs);
|
||||
|
||||
for ($attempt = 0; $attempt < 2; $attempt++) {
|
||||
$row = RateLimitRepository::findByScopeAndHash($normalized['scope'], $normalized['subject_hash']);
|
||||
$row = $this->rateLimitRepository->findByScopeAndHash($normalized['scope'], $normalized['subject_hash']);
|
||||
|
||||
if (!is_array($row)) {
|
||||
if (!$increment) {
|
||||
@@ -103,26 +107,15 @@ class RateLimiterService
|
||||
}
|
||||
|
||||
$hits = 1;
|
||||
$blockedUntilTs = null;
|
||||
if ($hits > $maxHits) {
|
||||
$blockedUntilTs = $nowTs + $blockSeconds;
|
||||
}
|
||||
|
||||
$created = RateLimitRepository::create(
|
||||
$created = $this->rateLimitRepository->create(
|
||||
$normalized['scope'],
|
||||
$normalized['subject_hash'],
|
||||
$hits,
|
||||
$nowSql,
|
||||
$blockedUntilTs !== null ? gmdate('Y-m-d H:i:s', $blockedUntilTs) : null
|
||||
null
|
||||
);
|
||||
|
||||
if ($created) {
|
||||
if ($blockedUntilTs !== null) {
|
||||
return [
|
||||
'allowed' => false,
|
||||
'retry_after' => max(1, $blockedUntilTs - $nowTs),
|
||||
];
|
||||
}
|
||||
return ['allowed' => true, 'retry_after' => 0];
|
||||
}
|
||||
|
||||
@@ -134,7 +127,7 @@ class RateLimiterService
|
||||
return ['allowed' => true, 'retry_after' => 0];
|
||||
}
|
||||
|
||||
$blockedUntilTs = self::parseTimestamp((string) ($row['blocked_until'] ?? ''));
|
||||
$blockedUntilTs = $this->parseTimestamp((string) ($row['blocked_until'] ?? ''));
|
||||
if ($blockedUntilTs !== null && $blockedUntilTs > $nowTs) {
|
||||
return [
|
||||
'allowed' => false,
|
||||
@@ -142,7 +135,7 @@ class RateLimiterService
|
||||
];
|
||||
}
|
||||
|
||||
$windowStartedTs = self::parseTimestamp((string) ($row['window_started_at'] ?? ''));
|
||||
$windowStartedTs = $this->parseTimestamp((string) ($row['window_started_at'] ?? ''));
|
||||
if ($windowStartedTs === null || ($windowStartedTs + $windowSeconds) <= $nowTs) {
|
||||
$windowStartedTs = $nowTs;
|
||||
$hits = 0;
|
||||
@@ -159,7 +152,7 @@ class RateLimiterService
|
||||
$newBlockedUntilTs = $nowTs + $blockSeconds;
|
||||
}
|
||||
|
||||
RateLimitRepository::updateStateById(
|
||||
$this->rateLimitRepository->updateStateById(
|
||||
$id,
|
||||
$hits,
|
||||
gmdate('Y-m-d H:i:s', $windowStartedTs),
|
||||
@@ -183,7 +176,7 @@ class RateLimiterService
|
||||
}
|
||||
}
|
||||
|
||||
private static function normalize(string $scope, string $subject): ?array
|
||||
private function normalize(string $scope, string $subject): ?array
|
||||
{
|
||||
$scope = strtolower(trim($scope));
|
||||
$subject = trim($subject);
|
||||
@@ -202,7 +195,7 @@ class RateLimiterService
|
||||
];
|
||||
}
|
||||
|
||||
private static function parseTimestamp(string $value): ?int
|
||||
private function parseTimestamp(string $value): ?int
|
||||
{
|
||||
$value = trim($value);
|
||||
if ($value === '') {
|
||||
|
||||
21
lib/Service/Security/SecurityServicesFactory.php
Normal file
21
lib/Service/Security/SecurityServicesFactory.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Service\Security;
|
||||
|
||||
use MintyPHP\Repository\Security\RateLimitRepository;
|
||||
|
||||
class SecurityServicesFactory
|
||||
{
|
||||
private ?RateLimitRepository $rateLimitRepository = null;
|
||||
private ?RateLimiterService $rateLimiterService = null;
|
||||
|
||||
public function createRateLimitRepository(): RateLimitRepository
|
||||
{
|
||||
return $this->rateLimitRepository ??= new RateLimitRepository();
|
||||
}
|
||||
|
||||
public function createRateLimiterService(): RateLimiterService
|
||||
{
|
||||
return $this->rateLimiterService ??= new RateLimiterService($this->createRateLimitRepository());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user