abc5bfcccc
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
110 lines
4.0 KiB
PHP
110 lines
4.0 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace KTXT\Unit\Service;
|
|
|
|
use KTXC\Models\Firewall\FirewallRuleObject;
|
|
use KTXC\Service\FirewallRuleCache;
|
|
use KTXC\Service\FirewallRuleManager;
|
|
use KTXC\Service\FirewallRuleScope;
|
|
use KTXC\Stores\FirewallStore;
|
|
use KTXF\Event\EventDispatcherInterface;
|
|
use PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations;
|
|
use PHPUnit\Framework\Attributes\TestDox;
|
|
use PHPUnit\Framework\MockObject\MockObject;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
#[AllowMockObjectsWithoutExpectations]
|
|
class FirewallRuleManagerTest extends TestCase
|
|
{
|
|
private FirewallStore&MockObject $store;
|
|
private EventDispatcherInterface&MockObject $events;
|
|
private FirewallRuleManager $manager;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
$this->store = $this->createMock(FirewallStore::class);
|
|
$this->events = $this->createMock(EventDispatcherInterface::class);
|
|
$this->manager = new FirewallRuleManager(
|
|
$this->store,
|
|
new FirewallRuleCache($this->store),
|
|
$this->events
|
|
);
|
|
}
|
|
|
|
#[TestDox('The shared manager creates rules with the supplied scope and owner')]
|
|
public function testScopeCreation(): void
|
|
{
|
|
$this->store->method('findExactIpRule')->willReturn(null);
|
|
$this->store->expects($this->exactly(2))
|
|
->method('depositRule')
|
|
->willReturnArgument(0);
|
|
|
|
$tenant = $this->manager->blockIp(
|
|
FirewallRuleScope::tenant('tenant-a'), '203.0.113.10', null, 'admin-a'
|
|
);
|
|
$system = $this->manager->blockIp(
|
|
FirewallRuleScope::system(), '203.0.113.11', null, 'system-admin'
|
|
);
|
|
|
|
self::assertSame('tenant-a', $tenant->getTenantId());
|
|
self::assertSame(FirewallRuleObject::SCOPE_TENANT, $tenant->getScope());
|
|
self::assertNull($system->getTenantId());
|
|
self::assertSame(FirewallRuleObject::SCOPE_SYSTEM, $system->getScope());
|
|
}
|
|
|
|
#[TestDox('Malformed rule values are rejected before persistence')]
|
|
public function testValidation(): void
|
|
{
|
|
$this->store->expects($this->never())->method('depositRule');
|
|
$this->expectException(\InvalidArgumentException::class);
|
|
|
|
$this->manager->blockIpRange(
|
|
FirewallRuleScope::tenant('tenant-a'), '2001:db8::/129', null, 'admin-a'
|
|
);
|
|
}
|
|
|
|
#[TestDox('Temporary rules require a positive duration')]
|
|
public function testDuration(): void
|
|
{
|
|
$this->store->expects($this->never())->method('depositRule');
|
|
$this->expectException(\InvalidArgumentException::class);
|
|
|
|
$this->manager->blockIp(
|
|
FirewallRuleScope::system(), '203.0.113.10', null, 'admin', 0
|
|
);
|
|
}
|
|
|
|
#[TestDox('Rule lifecycle operations cannot cross scope ownership')]
|
|
public function testOwnership(): void
|
|
{
|
|
$tenantRule = (new FirewallRuleObject())
|
|
->setId('tenant-rule')
|
|
->setScope(FirewallRuleObject::SCOPE_TENANT)
|
|
->setTenantId('tenant-a');
|
|
$this->store->method('fetchRule')->willReturn($tenantRule);
|
|
$this->store->expects($this->never())->method('destroyRule');
|
|
|
|
self::assertFalse($this->manager->remove(FirewallRuleScope::system(), 'tenant-rule'));
|
|
self::assertFalse($this->manager->remove(FirewallRuleScope::tenant('tenant-b'), 'tenant-rule'));
|
|
}
|
|
|
|
#[TestDox('Rule mutations invalidate the shared enforcement cache')]
|
|
public function testCacheInvalidation(): void
|
|
{
|
|
$this->store->expects($this->exactly(2))
|
|
->method('listRules')
|
|
->with('tenant-a')
|
|
->willReturnOnConsecutiveCalls([], []);
|
|
$cache = new FirewallRuleCache($this->store);
|
|
$manager = new FirewallRuleManager($this->store, $cache, $this->events);
|
|
$this->store->method('findExactIpRule')->willReturn(null);
|
|
$this->store->method('depositRule')->willReturnArgument(0);
|
|
|
|
self::assertSame([], $cache->tenant('tenant-a'));
|
|
$manager->blockIp(FirewallRuleScope::tenant('tenant-a'), '203.0.113.10', null, 'admin');
|
|
self::assertSame([], $cache->tenant('tenant-a'));
|
|
}
|
|
}
|