fix(firewall): enforce system rules independently of tenant context
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
@@ -45,7 +45,7 @@ class FirewallStoreTest extends TestCase
|
||||
}
|
||||
}
|
||||
|
||||
#[TestDox('Applicable rules contain system rules and only the requested tenant rules')]
|
||||
#[TestDox('System and tenant rule sets remain independently scoped')]
|
||||
public function testApplicableScopes(): void
|
||||
{
|
||||
$tenantA = $this->rule('tenant-a', FirewallRuleObject::SCOPE_TENANT, 'tenant-a');
|
||||
@@ -60,7 +60,10 @@ class FirewallStoreTest extends TestCase
|
||||
$this->store->depositRule($rule);
|
||||
}
|
||||
|
||||
$rules = $this->store->listApplicableRules('tenant-a');
|
||||
$rules = array_merge(
|
||||
$this->store->listSystemRules(),
|
||||
$this->store->listRules('tenant-a')
|
||||
);
|
||||
$reasons = array_map(static fn(FirewallRuleObject $rule): ?string => $rule->getReason(), $rules);
|
||||
sort($reasons);
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ class FirewallRuleManagerTest extends TestCase
|
||||
public function testCacheInvalidation(): void
|
||||
{
|
||||
$this->store->expects($this->exactly(2))
|
||||
->method('listApplicableRules')
|
||||
->method('listRules')
|
||||
->with('tenant-a')
|
||||
->willReturnOnConsecutiveCalls([], []);
|
||||
$cache = new FirewallRuleCache($this->store);
|
||||
@@ -102,8 +102,8 @@ class FirewallRuleManagerTest extends TestCase
|
||||
$this->store->method('findExactIpRule')->willReturn(null);
|
||||
$this->store->method('depositRule')->willReturnArgument(0);
|
||||
|
||||
self::assertSame([], $cache->applicable('tenant-a'));
|
||||
self::assertSame([], $cache->tenant('tenant-a'));
|
||||
$manager->blockIp(FirewallRuleScope::tenant('tenant-a'), '203.0.113.10', null, 'admin');
|
||||
self::assertSame([], $cache->applicable('tenant-a'));
|
||||
self::assertSame([], $cache->tenant('tenant-a'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,10 +67,8 @@ class FirewallServiceTest extends TestCase
|
||||
'tenant-a'
|
||||
);
|
||||
|
||||
$this->store->expects($this->once())
|
||||
->method('listApplicableRules')
|
||||
->with('tenant-a')
|
||||
->willReturn([$tenantAllow, $systemBlock]);
|
||||
$this->store->expects($this->once())->method('listSystemRules')->willReturn([$systemBlock]);
|
||||
$this->store->expects($this->once())->method('listRules')->with('tenant-a')->willReturn([$tenantAllow]);
|
||||
$this->events->expects($this->once())->method('dispatch');
|
||||
|
||||
$result = $this->service->analyze('203.0.113.10');
|
||||
@@ -95,7 +93,8 @@ class FirewallServiceTest extends TestCase
|
||||
'tenant-a'
|
||||
);
|
||||
|
||||
$this->store->method('listApplicableRules')->willReturn([$systemAllow, $tenantBlock]);
|
||||
$this->store->method('listSystemRules')->willReturn([$systemAllow]);
|
||||
$this->store->method('listRules')->with('tenant-a')->willReturn([$tenantBlock]);
|
||||
$this->events->expects($this->once())->method('dispatch');
|
||||
|
||||
$result = $this->service->analyze('203.0.113.10');
|
||||
@@ -107,8 +106,9 @@ class FirewallServiceTest extends TestCase
|
||||
#[TestDox('Rule caches are isolated by tenant')]
|
||||
public function testTenantCacheIsolation(): void
|
||||
{
|
||||
$this->store->expects($this->once())->method('listSystemRules')->willReturn([]);
|
||||
$this->store->expects($this->exactly(2))
|
||||
->method('listApplicableRules')
|
||||
->method('listRules')
|
||||
->willReturnCallback(static fn(string $tenantId): array => [
|
||||
(new FirewallRuleObject())
|
||||
->setId($tenantId)
|
||||
@@ -124,6 +124,79 @@ class FirewallServiceTest extends TestCase
|
||||
self::assertSame('tenant-b', $this->service->analyze('203.0.113.10')->ruleId);
|
||||
}
|
||||
|
||||
#[TestDox('System blocks apply when no tenant is resolved')]
|
||||
public function testSystemBlockWithoutTenant(): void
|
||||
{
|
||||
$this->currentTenant = null;
|
||||
$this->store->method('listSystemRules')->willReturn([
|
||||
$this->rule(
|
||||
'system-block',
|
||||
FirewallRuleObject::SCOPE_SYSTEM,
|
||||
FirewallRuleObject::ACTION_BLOCK,
|
||||
null
|
||||
),
|
||||
]);
|
||||
$this->store->expects($this->never())->method('listRules');
|
||||
|
||||
self::assertSame('system-block', $this->service->analyze('203.0.113.10')->ruleId);
|
||||
}
|
||||
|
||||
#[TestDox('System allows can match when no tenant is resolved')]
|
||||
public function testSystemAllowWithoutTenant(): void
|
||||
{
|
||||
$this->currentTenant = null;
|
||||
$this->store->method('listSystemRules')->willReturn([
|
||||
$this->rule(
|
||||
'system-allow',
|
||||
FirewallRuleObject::SCOPE_SYSTEM,
|
||||
FirewallRuleObject::ACTION_ALLOW,
|
||||
null
|
||||
),
|
||||
]);
|
||||
|
||||
$result = $this->service->analyze('203.0.113.10');
|
||||
|
||||
self::assertTrue($result->isAllowed());
|
||||
self::assertSame('system-allow', $result->ruleId);
|
||||
}
|
||||
|
||||
#[TestDox('System blocks remain active when the tenant firewall is disabled')]
|
||||
public function testSystemBlockWithDisabledTenant(): void
|
||||
{
|
||||
$this->disableTenantFirewall();
|
||||
$this->store->method('listSystemRules')->willReturn([
|
||||
$this->rule(
|
||||
'system-block',
|
||||
FirewallRuleObject::SCOPE_SYSTEM,
|
||||
FirewallRuleObject::ACTION_BLOCK,
|
||||
null
|
||||
),
|
||||
]);
|
||||
$this->store->expects($this->never())->method('listRules');
|
||||
|
||||
self::assertSame('system-block', $this->service->analyze('203.0.113.10')->ruleId);
|
||||
}
|
||||
|
||||
#[TestDox('Tenant rules are ignored when the tenant firewall is disabled')]
|
||||
public function testTenantRulesDisabled(): void
|
||||
{
|
||||
$this->disableTenantFirewall();
|
||||
$this->store->method('listSystemRules')->willReturn([]);
|
||||
$this->store->expects($this->never())->method('listRules');
|
||||
|
||||
self::assertTrue($this->service->analyze('203.0.113.10')->isAllowed());
|
||||
}
|
||||
|
||||
#[TestDox('Requests without a tenant and without a system match are allowed')]
|
||||
public function testNoTenantDefault(): void
|
||||
{
|
||||
$this->currentTenant = null;
|
||||
$this->store->method('listSystemRules')->willReturn([]);
|
||||
$this->store->expects($this->never())->method('listRules');
|
||||
|
||||
self::assertTrue($this->service->analyze('203.0.113.10')->isAllowed());
|
||||
}
|
||||
|
||||
#[TestDox('Typed tenant firewall settings drive brute-force thresholds')]
|
||||
public function testFirewallConfiguration(): void
|
||||
{
|
||||
@@ -248,4 +321,11 @@ class FirewallServiceTest extends TestCase
|
||||
->setValue('203.0.113.10')
|
||||
->setReason($id);
|
||||
}
|
||||
|
||||
private function disableTenantFirewall(): void
|
||||
{
|
||||
$this->currentConfiguration = (new TenantConfiguration())->jsonDeserialize([
|
||||
'firewall' => ['enabled' => false],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user