fix(firewall): preserve tenant ownership for automatic blocks
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
@@ -22,7 +22,7 @@ class FirewallServiceTest extends TestCase
|
||||
private TenantContextInterface&MockObject $tenantContext;
|
||||
private EventDispatcherInterface&MockObject $events;
|
||||
private FirewallService $service;
|
||||
private string $currentTenant;
|
||||
private ?string $currentTenant;
|
||||
private ?TenantConfiguration $currentConfiguration;
|
||||
|
||||
protected function setUp(): void
|
||||
@@ -33,7 +33,7 @@ class FirewallServiceTest extends TestCase
|
||||
$this->currentTenant = 'tenant-a';
|
||||
$this->currentConfiguration = null;
|
||||
$this->tenantContext->method('identifier')->willReturnCallback(
|
||||
fn(): string => $this->currentTenant
|
||||
fn(): ?string => $this->currentTenant
|
||||
);
|
||||
$this->tenantContext->method('configuration')->willReturnCallback(
|
||||
fn(): ?TenantConfiguration => $this->currentConfiguration
|
||||
@@ -238,6 +238,70 @@ class FirewallServiceTest extends TestCase
|
||||
$this->service->handleAuthFailure($event);
|
||||
}
|
||||
|
||||
#[TestDox('Automatic blocks retain the tenant carried by the authentication event')]
|
||||
public function testAutomaticBlockTenant(): void
|
||||
{
|
||||
$this->currentTenant = 'tenant-context';
|
||||
$this->store->expects($this->once())
|
||||
->method('countRecentFailures')
|
||||
->with('tenant-event', '203.0.113.10', 300)
|
||||
->willReturn(4);
|
||||
$this->store->expects($this->once())
|
||||
->method('findExactIpRule')
|
||||
->with(
|
||||
'tenant-event',
|
||||
'203.0.113.10',
|
||||
FirewallRuleObject::ACTION_BLOCK
|
||||
)
|
||||
->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-event'
|
||||
&& $rule->getExpiresAt() !== null;
|
||||
}))
|
||||
->willReturnArgument(0);
|
||||
|
||||
$publishedTenants = [];
|
||||
$this->events->expects($this->exactly(2))
|
||||
->method('dispatch')
|
||||
->willReturnCallback(static function (\KTXF\Event\Event $event) use (&$publishedTenants): void {
|
||||
$publishedTenants[] = $event->getTenantId();
|
||||
});
|
||||
|
||||
$event = \KTXF\Event\SecurityEvent::authFailure('203.0.113.10');
|
||||
$event->setTenantId('tenant-event');
|
||||
$this->service->handleAuthFailure($event);
|
||||
|
||||
self::assertSame(['tenant-event', 'tenant-event'], $publishedTenants);
|
||||
}
|
||||
|
||||
#[TestDox('Authentication events without a tenant use the current tenant')]
|
||||
public function testAutomaticBlockTenantFallback(): void
|
||||
{
|
||||
$this->store->expects($this->once())
|
||||
->method('countRecentFailures')
|
||||
->with('tenant-a', '203.0.113.10', 300)
|
||||
->willReturn(0);
|
||||
|
||||
$this->service->handleAuthFailure(
|
||||
\KTXF\Event\SecurityEvent::authFailure('203.0.113.10')
|
||||
);
|
||||
}
|
||||
|
||||
#[TestDox('Authentication failures are ignored when no tenant can be resolved')]
|
||||
public function testAutomaticBlockWithoutTenant(): void
|
||||
{
|
||||
$this->currentTenant = null;
|
||||
$this->store->expects($this->never())->method('countRecentFailures');
|
||||
$this->store->expects($this->never())->method('depositRule');
|
||||
|
||||
$this->service->handleAuthFailure(
|
||||
\KTXF\Event\SecurityEvent::authFailure('203.0.113.10')
|
||||
);
|
||||
}
|
||||
|
||||
private function rule(
|
||||
string $id,
|
||||
string $scope,
|
||||
|
||||
Reference in New Issue
Block a user