feat(firewall): add scoped rule administration reads

Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
2026-08-03 22:05:00 -04:00
parent 4ee91a7918
commit 74696bbeb3
9 changed files with 486 additions and 0 deletions
+56
View File
@@ -11,6 +11,8 @@ use KTXF\Event\SecurityEvent;
final class FirewallRuleManager
{
public const QUERY_STATUSES = ['active', 'disabled', 'expired', 'all'];
public const MAX_QUERY_LIMIT = 100;
public const ORIGIN_MANUAL = 'manual';
public const ORIGIN_AUTOMATIC = 'automatic';
@@ -28,6 +30,60 @@ final class FirewallRuleManager
: $this->store->listRules($scope->tenantId, $activeOnly);
}
public function query(
FirewallRuleScope $scope,
string $status = 'active',
?string $type = null,
?string $action = null,
int $limit = 50,
int $offset = 0
): array {
if (!in_array($status, self::QUERY_STATUSES, true)) {
throw new \InvalidArgumentException('Invalid rule status filter.');
}
if ($type !== null && !in_array($type, [
FirewallRuleObject::TYPE_IP,
FirewallRuleObject::TYPE_IP_RANGE,
FirewallRuleObject::TYPE_DEVICE,
], true)) {
throw new \InvalidArgumentException('Invalid rule type filter.');
}
if ($action !== null && !in_array($action, [
FirewallRuleObject::ACTION_ALLOW,
FirewallRuleObject::ACTION_BLOCK,
], true)) {
throw new \InvalidArgumentException('Invalid rule action filter.');
}
if ($limit < 1 || $limit > self::MAX_QUERY_LIMIT || $offset < 0) {
throw new \InvalidArgumentException('Pagination requires limit 1-100 and offset 0 or greater.');
}
return $this->store->queryRules(
$scope->scope,
$scope->tenantId,
$status,
$type,
$action,
$limit,
$offset
);
}
public function fetch(FirewallRuleScope $scope, string $ruleId): ?FirewallRuleObject
{
return $this->ownedRule($scope, $ruleId);
}
/** @return array{precedence: string[], system: FirewallRuleObject[], tenant: FirewallRuleObject[]} */
public function effectivePolicy(string $tenantId): array
{
return [
'precedence' => ['system_block', 'tenant_allow', 'tenant_block', 'system_allow', 'default_allow'],
'system' => $this->store->listSystemRules(),
'tenant' => $this->store->listRules($tenantId),
];
}
public function blockIp(
FirewallRuleScope $scope,
string $ipAddress,
@@ -24,6 +24,23 @@ final class SystemFirewallRuleService
return $this->rules->list(FirewallRuleScope::system(), $activeOnly);
}
public function queryRules(
string $status = 'active',
?string $type = null,
?string $action = null,
int $limit = 50,
int $offset = 0
): array {
$this->requirePermission(self::PERMISSION_READ);
return $this->rules->query(FirewallRuleScope::system(), $status, $type, $action, $limit, $offset);
}
public function fetchRule(string $ruleId): ?FirewallRuleObject
{
$this->requirePermission(self::PERMISSION_READ);
return $this->rules->fetch(FirewallRuleScope::system(), $ruleId);
}
public function blockIp(string $ip, ?string $reason = null, ?int $durationSeconds = null): FirewallRuleObject
{
$this->requirePermission(self::PERMISSION_MANAGE);
@@ -26,6 +26,29 @@ final class TenantFirewallRuleService
return $this->rules->list($this->scope(), $activeOnly);
}
public function queryRules(
string $status = 'active',
?string $type = null,
?string $action = null,
int $limit = 50,
int $offset = 0
): array {
$this->requirePermission(self::PERMISSION_READ);
return $this->rules->query($this->scope(), $status, $type, $action, $limit, $offset);
}
public function fetchRule(string $ruleId): ?FirewallRuleObject
{
$this->requirePermission(self::PERMISSION_READ);
return $this->rules->fetch($this->scope(), $ruleId);
}
public function effectivePolicy(): array
{
$this->requirePermission(self::PERMISSION_READ);
return $this->rules->effectivePolicy($this->tenant->requireIdentifier());
}
public function blockIp(string $ip, ?string $reason = null, ?int $durationSeconds = null): FirewallRuleObject
{
$this->requirePermission(self::PERMISSION_MANAGE);