scope === FirewallRuleObject::SCOPE_SYSTEM ? $this->store->listSystemRules($activeOnly) : $this->store->listRules($scope->tenantId, $activeOnly); } public function blockIp( FirewallRuleScope $scope, string $ipAddress, ?string $reason, ?string $createdBy, ?int $durationSeconds = null, string $origin = self::ORIGIN_MANUAL ): FirewallRuleObject { $ipAddress = FirewallRuleValidator::ipAddress($ipAddress); FirewallRuleValidator::duration($durationSeconds); $existing = $this->store->findExactIpRule( $scope->tenantId, $ipAddress, FirewallRuleObject::ACTION_BLOCK, $scope->scope ); if ($existing) { return $existing; } $rule = $this->create( $scope, FirewallRuleObject::TYPE_IP, FirewallRuleObject::ACTION_BLOCK, $ipAddress, $reason ?? 'Blocked by administrator', $createdBy, $durationSeconds, $origin ); $this->publishIpEvent(SecurityEvent::IP_BLOCKED, $scope, $ipAddress, $reason); return $rule; } public function allowIp( FirewallRuleScope $scope, string $ipAddress, ?string $reason, ?string $createdBy, string $origin = self::ORIGIN_MANUAL ): FirewallRuleObject { $ipAddress = FirewallRuleValidator::ipAddress($ipAddress); $rule = $this->create( $scope, FirewallRuleObject::TYPE_IP, FirewallRuleObject::ACTION_ALLOW, $ipAddress, $reason ?? 'Allowed by administrator', $createdBy, null, $origin ); $this->publishIpEvent(SecurityEvent::IP_ALLOWED, $scope, $ipAddress, $reason); return $rule; } public function blockIpRange( FirewallRuleScope $scope, string $cidr, ?string $reason, ?string $createdBy, string $origin = self::ORIGIN_MANUAL ): FirewallRuleObject { return $this->create( $scope, FirewallRuleObject::TYPE_IP_RANGE, FirewallRuleObject::ACTION_BLOCK, FirewallRuleValidator::cidr($cidr), $reason ?? 'Range blocked by administrator', $createdBy, null, $origin ); } public function blockDevice( FirewallRuleScope $scope, string $fingerprint, ?string $reason, ?string $createdBy, ?int $durationSeconds = null, string $origin = self::ORIGIN_MANUAL ): FirewallRuleObject { FirewallRuleValidator::duration($durationSeconds); $fingerprint = FirewallRuleValidator::deviceFingerprint($fingerprint); $rule = $this->create( $scope, FirewallRuleObject::TYPE_DEVICE, FirewallRuleObject::ACTION_BLOCK, $fingerprint, $reason ?? 'Device blocked by administrator', $createdBy, $durationSeconds, $origin ); $event = new SecurityEvent(SecurityEvent::DEVICE_BLOCKED, ['device' => $fingerprint, 'reason' => $reason]); $event->setDeviceFingerprint($fingerprint)->setReason($reason)->setTenantId($scope->tenantId); $this->events->dispatch($event); return $rule; } public function disable(FirewallRuleScope $scope, string $ruleId, ?string $actorId = null): bool { $rule = $this->ownedRule($scope, $ruleId); if (!$rule) { return false; } $rule->setEnabled(false); $this->store->depositRule($rule); $this->cache->invalidate(); $this->publishLifecycleEvent(SecurityEvent::FIREWALL_RULE_DISABLED, $rule, $actorId); return true; } public function remove(FirewallRuleScope $scope, string $ruleId, ?string $actorId = null): bool { $rule = $this->ownedRule($scope, $ruleId); if (!$rule) { return false; } $this->store->destroyRule($rule); $this->cache->invalidate(); $this->publishLifecycleEvent(SecurityEvent::FIREWALL_RULE_REMOVED, $rule, $actorId); return true; } private function create( FirewallRuleScope $scope, string $type, string $action, string $value, string $reason, ?string $createdBy, ?int $durationSeconds = null, string $origin = self::ORIGIN_MANUAL ): FirewallRuleObject { if (!in_array($origin, [self::ORIGIN_MANUAL, self::ORIGIN_AUTOMATIC], true)) { throw new \InvalidArgumentException("Invalid firewall rule origin: {$origin}"); } $rule = (new FirewallRuleObject()) ->setScope($scope->scope) ->setTenantId($scope->tenantId) ->setType($type) ->setAction($action) ->setValue($value) ->setReason($reason) ->setCreatedBy($createdBy) ->setCreatedAt(new \DateTimeImmutable()) ->setMetadata(['origin' => $origin]) ->setEnabled(true); if ($durationSeconds !== null) { $rule->setExpiresAt((new \DateTimeImmutable())->modify("+{$durationSeconds} seconds")); } $this->store->depositRule($rule); $this->cache->invalidate(); $this->publishLifecycleEvent(SecurityEvent::FIREWALL_RULE_CREATED, $rule); return $rule; } private function ownedRule(FirewallRuleScope $scope, string $ruleId): ?FirewallRuleObject { $rule = $this->store->fetchRule($ruleId); return $rule && $scope->owns($rule) ? $rule : null; } private function publishIpEvent( string $name, FirewallRuleScope $scope, string $ipAddress, ?string $reason ): void { $event = new SecurityEvent($name, ['ip' => $ipAddress, 'reason' => $reason]); $event->setIpAddress($ipAddress)->setReason($reason)->setTenantId($scope->tenantId); $this->events->dispatch($event); } private function publishLifecycleEvent( string $name, FirewallRuleObject $rule, ?string $actorId = null ): void { $event = new SecurityEvent($name, [ 'ruleId' => $rule->getId(), 'ruleScope' => $rule->getScope(), 'ruleType' => $rule->getType(), 'ruleAction' => $rule->getAction(), 'ruleValue' => $rule->getValue(), 'reason' => $rule->getReason(), 'origin' => $rule->getMetadata()['origin'] ?? self::ORIGIN_MANUAL, 'expiresAt' => $rule->getExpiresAt()?->format(\DateTimeInterface::ATOM), ]); $event->setTenantId($rule->getTenantId()) ->setIdentityId($actorId ?? $rule->getCreatedBy()); $this->events->dispatch($event); } }