refactor(firewall): separate enforcement from rule management
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
@@ -8,6 +8,8 @@ use KTXC\Context\TenantContextInterface;
|
||||
use KTXC\Models\Firewall\FirewallRuleObject;
|
||||
use KTXC\Models\Tenant\TenantConfiguration;
|
||||
use KTXC\Service\FirewallService;
|
||||
use KTXC\Service\FirewallRuleCache;
|
||||
use KTXC\Service\FirewallRuleManager;
|
||||
use KTXC\Stores\FirewallStore;
|
||||
use KTXF\Event\EventDispatcherInterface;
|
||||
use PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations;
|
||||
@@ -38,7 +40,15 @@ class FirewallServiceTest extends TestCase
|
||||
$this->tenantContext->method('configuration')->willReturnCallback(
|
||||
fn(): ?TenantConfiguration => $this->currentConfiguration
|
||||
);
|
||||
$this->service = new FirewallService($this->store, $this->tenantContext, $this->events);
|
||||
$cache = new FirewallRuleCache($this->store);
|
||||
$manager = new FirewallRuleManager($this->store, $cache, $this->events);
|
||||
$this->service = new FirewallService(
|
||||
$this->store,
|
||||
$this->tenantContext,
|
||||
$this->events,
|
||||
$manager,
|
||||
$cache
|
||||
);
|
||||
}
|
||||
|
||||
#[TestDox('System blocks cannot be overridden by tenant allows')]
|
||||
@@ -114,85 +124,6 @@ class FirewallServiceTest extends TestCase
|
||||
self::assertSame('tenant-b', $this->service->analyze('203.0.113.10')->ruleId);
|
||||
}
|
||||
|
||||
#[TestDox('New IP blocks are explicitly tenant-scoped')]
|
||||
public function testIpBlockScope(): void
|
||||
{
|
||||
$this->store->method('findExactIpRule')->willReturn(null);
|
||||
$this->store->expects($this->once())
|
||||
->method('depositRule')
|
||||
->with(self::callback(static function (FirewallRuleObject $rule): bool {
|
||||
return $rule->getScope() === FirewallRuleObject::SCOPE_TENANT
|
||||
&& $rule->getTenantId() === 'tenant-a';
|
||||
}))
|
||||
->willReturnArgument(0);
|
||||
|
||||
$rule = $this->service->blockIp('203.0.113.10');
|
||||
|
||||
self::assertSame(FirewallRuleObject::SCOPE_TENANT, $rule->getScope());
|
||||
self::assertSame('tenant-a', $rule->getTenantId());
|
||||
}
|
||||
|
||||
#[TestDox('Malformed IP addresses are rejected before persistence')]
|
||||
public function testIpValidation(): void
|
||||
{
|
||||
$this->store->expects($this->never())->method('depositRule');
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Invalid IP address');
|
||||
|
||||
$this->service->blockIp('999.2.3.4');
|
||||
}
|
||||
|
||||
#[TestDox('Valid IPv6 addresses can be blocked')]
|
||||
public function testIpv6Validation(): void
|
||||
{
|
||||
$this->store->method('findExactIpRule')->willReturn(null);
|
||||
$this->store->expects($this->once())
|
||||
->method('depositRule')
|
||||
->with(self::callback(static fn(FirewallRuleObject $rule): bool => $rule->getValue() === '2001:db8::1'))
|
||||
->willReturnArgument(0);
|
||||
|
||||
self::assertSame('2001:db8::1', $this->service->blockIp(' 2001:db8::1 ')->getValue());
|
||||
}
|
||||
|
||||
#[TestDox('Malformed CIDR ranges are rejected before persistence')]
|
||||
public function testCidrValidation(): void
|
||||
{
|
||||
$this->store->expects($this->never())->method('depositRule');
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Invalid CIDR range');
|
||||
|
||||
$this->service->blockIpRange('2001:db8::/129');
|
||||
}
|
||||
|
||||
#[TestDox('Valid IPv4 and IPv6 CIDR ranges are accepted')]
|
||||
public function testValidCidrs(): void
|
||||
{
|
||||
$this->store->expects($this->exactly(2))->method('depositRule')->willReturnArgument(0);
|
||||
|
||||
self::assertSame('192.0.2.0/24', $this->service->blockIpRange('192.0.2.0/24')->getValue());
|
||||
self::assertSame('2001:db8::/32', $this->service->blockIpRange('2001:db8::/32')->getValue());
|
||||
}
|
||||
|
||||
#[TestDox('Temporary rules require a positive duration')]
|
||||
public function testDurationValidation(): void
|
||||
{
|
||||
$this->store->expects($this->never())->method('depositRule');
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('greater than zero');
|
||||
|
||||
$this->service->blockIp('203.0.113.10', durationSeconds: 0);
|
||||
}
|
||||
|
||||
#[TestDox('Device fingerprints must be non-empty and bounded')]
|
||||
public function testFingerprintValidation(): void
|
||||
{
|
||||
$this->store->expects($this->never())->method('depositRule');
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Device fingerprint');
|
||||
|
||||
$this->service->blockDevice(' ');
|
||||
}
|
||||
|
||||
#[TestDox('Typed tenant firewall settings drive brute-force thresholds')]
|
||||
public function testFirewallConfiguration(): void
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user