105 lines
3.6 KiB
PHP
105 lines
3.6 KiB
PHP
<?php
|
|
|
|
use App\Libraries\RateLimiterService;
|
|
use CodeIgniter\Test\CIUnitTestCase;
|
|
|
|
/**
|
|
* @internal
|
|
*/
|
|
final class RateLimiterServiceSmokeTest extends CIUnitTestCase
|
|
{
|
|
private RateLimiterService $service;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
helper('utility');
|
|
$this->service = new RateLimiterService();
|
|
}
|
|
|
|
public function testIpBlockEscalationAndUnblockFlow(): void
|
|
{
|
|
$cfg = config('RateLimiter');
|
|
$fingerprint = 'smoke-ip-' . uniqid('', true);
|
|
|
|
// Initially no IP block.
|
|
$this->assertNull($this->service->getIpBlock($fingerprint));
|
|
|
|
// Record enough failures to trigger soft block.
|
|
$result = null;
|
|
for ($i = 0; $i < $cfg->ipBlock['violation_soft']; $i++) {
|
|
$result = $this->service->recordIpFailure($fingerprint);
|
|
}
|
|
|
|
$this->assertIsArray($result);
|
|
$this->assertSame('soft', $result['level']);
|
|
|
|
// Hitting again while blocked should eventually escalate to hard.
|
|
$this->service->checkIp($fingerprint, 'jwtApi'); // return soft, escalate to medium
|
|
$this->service->checkIp($fingerprint, 'jwtApi'); // return medium, escalate to hard
|
|
$blocked = $this->service->checkIp($fingerprint, 'jwtApi'); // return hard
|
|
|
|
$this->assertIsArray($blocked);
|
|
$this->assertSame('hard', $blocked['level']);
|
|
|
|
$this->service->unblockIp($fingerprint);
|
|
$this->assertNull($this->service->getIpBlock($fingerprint));
|
|
}
|
|
|
|
public function testUserFailureFlowAndManualUnblock(): void
|
|
{
|
|
$cfg = config('RateLimiter');
|
|
$identity = 'smoke-user-' . uniqid('', true) . '@example.com';
|
|
|
|
$this->assertNull($this->service->getUserBlock($identity));
|
|
|
|
$result = null;
|
|
for ($i = 0; $i < $cfg->authApi['violation_soft']; $i++) {
|
|
$result = $this->service->recordUserFailure($identity, 'authApi');
|
|
}
|
|
|
|
$this->assertIsArray($result);
|
|
$this->assertSame('soft', $result['level']);
|
|
|
|
// Re-hits while blocked escalate from soft -> medium -> hard.
|
|
$this->service->checkUser($identity); // return soft, escalate to medium
|
|
$this->service->checkUser($identity); // return medium, escalate to hard
|
|
$blocked = $this->service->checkUser($identity); // return hard
|
|
|
|
$this->assertIsArray($blocked);
|
|
$this->assertSame('hard', $blocked['level']);
|
|
|
|
$this->service->unblockUser($identity);
|
|
$this->assertNull($this->service->getUserBlock($identity));
|
|
}
|
|
|
|
public function testJwtUserThrottleToSoftBlock(): void
|
|
{
|
|
$cfg = config('RateLimiter');
|
|
$identity = 'smoke-throttle-' . uniqid('', true) . '@example.com';
|
|
|
|
// First "limit" requests should pass.
|
|
for ($i = 0; $i < $cfg->jwtApi['limit']; $i++) {
|
|
$this->assertNull($this->service->checkUserThrottle($identity, 'jwtApi'));
|
|
}
|
|
|
|
// Over-limit attempts should return throttle first, then soft block after enough violations.
|
|
$throttle = $this->service->checkUserThrottle($identity, 'jwtApi');
|
|
$this->assertIsArray($throttle);
|
|
$this->assertSame('throttle', $throttle['level']);
|
|
|
|
$blocked = null;
|
|
for ($i = 0; $i < $cfg->jwtApi['violation_soft']; $i++) {
|
|
$blocked = $this->service->checkUserThrottle($identity, 'jwtApi');
|
|
}
|
|
|
|
$this->assertIsArray($blocked);
|
|
$this->assertContains($blocked['level'], ['soft', 'medium', 'hard']);
|
|
|
|
$this->service->unblockUser($identity);
|
|
$this->assertNull($this->service->getUserBlock($identity));
|
|
}
|
|
}
|
|
|