feat(firewall): add audited configuration management
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KTXC\Service;
|
||||
|
||||
use KTXC\Models\Tenant\TenantConfiguration;
|
||||
use KTXF\Event\EventDispatcherInterface;
|
||||
use KTXF\Event\SecurityEvent;
|
||||
|
||||
final class FirewallSettingsService
|
||||
{
|
||||
public function __construct(
|
||||
private readonly TenantService $tenants,
|
||||
private readonly EventDispatcherInterface $events,
|
||||
) {
|
||||
}
|
||||
|
||||
public function update(
|
||||
string $tenantId,
|
||||
bool $enabled,
|
||||
int $maxAuthFailures,
|
||||
int $authFailureWindow,
|
||||
int $autoBlockDuration,
|
||||
string $reason,
|
||||
?string $actorId
|
||||
): ?array {
|
||||
$reason = trim($reason);
|
||||
if ($reason === '' || strlen($reason) > 1000) {
|
||||
throw new \InvalidArgumentException('A change reason containing 1-1000 bytes is required.');
|
||||
}
|
||||
self::bounded($maxAuthFailures, 1, 1000, 'Maximum authentication failures');
|
||||
self::bounded($authFailureWindow, 1, 86400, 'Authentication failure window');
|
||||
self::bounded($autoBlockDuration, 1, 31536000, 'Automatic block duration');
|
||||
|
||||
$tenant = $this->tenants->fetchById($tenantId);
|
||||
if ($tenant === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$previous = $tenant->getConfiguration()->firewall()->jsonSerialize();
|
||||
$current = [
|
||||
'enabled' => $enabled,
|
||||
'maxAuthFailures' => $maxAuthFailures,
|
||||
'authFailureWindow' => $authFailureWindow,
|
||||
'autoBlockDuration' => $autoBlockDuration,
|
||||
];
|
||||
$configuration = (new TenantConfiguration())->jsonDeserialize([
|
||||
...$tenant->getConfiguration()->jsonSerialize(),
|
||||
'firewall' => $current,
|
||||
]);
|
||||
$tenant->setConfiguration($configuration);
|
||||
$this->tenants->deposit($tenant);
|
||||
|
||||
$event = new SecurityEvent(SecurityEvent::FIREWALL_SETTINGS_UPDATED, [
|
||||
'changeReason' => $reason,
|
||||
'changeOrigin' => FirewallRuleManager::ORIGIN_MANUAL,
|
||||
'previous' => $previous,
|
||||
'current' => $current,
|
||||
]);
|
||||
$event->setTenantId($tenantId)->setIdentityId($actorId);
|
||||
$this->events->dispatch($event);
|
||||
|
||||
return $current;
|
||||
}
|
||||
|
||||
private static function bounded(int $value, int $minimum, int $maximum, string $label): void
|
||||
{
|
||||
if ($value < $minimum || $value > $maximum) {
|
||||
throw new \InvalidArgumentException(
|
||||
"{$label} must be between {$minimum} and {$maximum}."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user