feat: introduce user management events
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
@@ -4,10 +4,12 @@ declare(strict_types=1);
|
||||
|
||||
namespace KTXC\Console\Tenant;
|
||||
|
||||
use KTXC\Context\TenantContext;
|
||||
use KTXC\Models\Tenant\DomainCollection;
|
||||
use KTXC\Models\Tenant\TenantConfiguration;
|
||||
use KTXC\Models\Tenant\TenantObject;
|
||||
use KTXC\Service\TenantService;
|
||||
use KTXC\Service\UserAccountsService;
|
||||
use KTXC\Stores\UserAccountsStore;
|
||||
use KTXC\Stores\UserRolesStore;
|
||||
use KTXF\Utile\UUID;
|
||||
@@ -35,6 +37,8 @@ class TenantCreateCommand extends Command
|
||||
private readonly TenantService $tenantService,
|
||||
private readonly UserRolesStore $rolesStore,
|
||||
private readonly UserAccountsStore $userStore,
|
||||
private readonly UserAccountsService $userService,
|
||||
private readonly TenantContext $tenantContext,
|
||||
private readonly LoggerInterface $logger
|
||||
) {
|
||||
parent::__construct();
|
||||
@@ -97,6 +101,10 @@ class TenantCreateCommand extends Command
|
||||
$io->error('Failed to create tenant.');
|
||||
return Command::FAILURE;
|
||||
}
|
||||
if (!$this->tenantContext->resolveIdentifier($identifier)) {
|
||||
throw new \RuntimeException("Failed to initialize tenant context for '{$identifier}'.");
|
||||
}
|
||||
$identifier = $this->tenantContext->requireIdentifier();
|
||||
|
||||
$this->logger->info('Tenant created via console', [
|
||||
'identifier' => $identifier,
|
||||
@@ -134,7 +142,7 @@ class TenantCreateCommand extends Command
|
||||
if ($this->userStore->fetchByIdentity($identifier, $adminIdentity)) {
|
||||
$io->warning("User '{$adminIdentity}' already exists in tenant '{$identifier}'; skipping admin user creation.");
|
||||
} else {
|
||||
$this->userStore->createUser($identifier, [
|
||||
$this->userService->createUser([
|
||||
'identity' => $adminIdentity,
|
||||
'label' => 'Administrator',
|
||||
'enabled' => true,
|
||||
|
||||
@@ -4,7 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace KTXC\Console\User;
|
||||
|
||||
use KTXC\Service\TenantService;
|
||||
use KTXC\Context\TenantContext;
|
||||
use KTXC\Service\UserAccountsService;
|
||||
use KTXC\Stores\UserAccountsStore;
|
||||
use KTXC\Stores\UserRolesStore;
|
||||
use Psr\Log\LoggerInterface;
|
||||
@@ -28,8 +29,9 @@ use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
class UserCreateCommand extends Command
|
||||
{
|
||||
public function __construct(
|
||||
private readonly TenantService $tenantService,
|
||||
private readonly TenantContext $tenantContext,
|
||||
private readonly UserAccountsStore $userStore,
|
||||
private readonly UserAccountsService $userService,
|
||||
private readonly UserRolesStore $rolesStore,
|
||||
private readonly LoggerInterface $logger
|
||||
) {
|
||||
@@ -59,11 +61,11 @@ class UserCreateCommand extends Command
|
||||
$io->title('Create User');
|
||||
|
||||
try {
|
||||
// Ensure the tenant exists
|
||||
if (!$this->tenantService->fetchById($tenant)) {
|
||||
if (!$this->tenantContext->resolveIdentifier($tenant)) {
|
||||
$io->error("Tenant '{$tenant}' not found.");
|
||||
return Command::FAILURE;
|
||||
}
|
||||
$tenant = $this->tenantContext->requireIdentifier();
|
||||
|
||||
// Ensure identity is unique within the tenant
|
||||
if ($this->userStore->fetchByIdentity($tenant, $identity)) {
|
||||
@@ -95,7 +97,7 @@ class UserCreateCommand extends Command
|
||||
$userData['uid'] = $input->getOption('uid');
|
||||
}
|
||||
|
||||
$user = $this->userStore->createUser($tenant, $userData);
|
||||
$user = $this->userService->createUser($userData);
|
||||
|
||||
$this->logger->info('User created via console', [
|
||||
'tenant' => $tenant,
|
||||
|
||||
@@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace KTXC\Console\User;
|
||||
|
||||
use KTXC\Context\TenantContext;
|
||||
use KTXC\Service\UserAccountsService;
|
||||
use KTXC\Stores\UserAccountsStore;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
@@ -26,7 +28,9 @@ use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
class UserDeleteCommand extends Command
|
||||
{
|
||||
public function __construct(
|
||||
private readonly TenantContext $tenantContext,
|
||||
private readonly UserAccountsStore $userStore,
|
||||
private readonly UserAccountsService $userService,
|
||||
private readonly LoggerInterface $logger
|
||||
) {
|
||||
parent::__construct();
|
||||
@@ -52,6 +56,12 @@ class UserDeleteCommand extends Command
|
||||
$io->title('Delete User');
|
||||
|
||||
try {
|
||||
if (!$this->tenantContext->resolveIdentifier($tenant)) {
|
||||
$io->error("Tenant '{$tenant}' not found.");
|
||||
return Command::FAILURE;
|
||||
}
|
||||
$tenant = $this->tenantContext->requireIdentifier();
|
||||
|
||||
$user = $this->userStore->fetchByIdentity($tenant, $identity);
|
||||
|
||||
if (!$user) {
|
||||
@@ -64,7 +74,7 @@ class UserDeleteCommand extends Command
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
if (!$this->userStore->deleteUser($tenant, $user['uid'])) {
|
||||
if (!$this->userService->deleteUser($user['uid'])) {
|
||||
$io->error("Failed to delete user '{$identity}'.");
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
@@ -6,14 +6,19 @@ use KTXC\Models\Identity\User;
|
||||
use KTXC\Context\IdentityContextInterface;
|
||||
use KTXC\Context\TenantContextInterface;
|
||||
use KTXC\Stores\UserAccountsStore;
|
||||
use KTXC\User\Event\UserCreatedEvent;
|
||||
use KTXC\User\Event\UserDeletingEvent;
|
||||
use KTXC\User\Event\UserUpdatedEvent;
|
||||
use KTXF\Event\EventDispatcherInterface;
|
||||
|
||||
class UserAccountsService
|
||||
{
|
||||
|
||||
public function __construct(
|
||||
private readonly TenantContextInterface $tenantContext,
|
||||
private readonly IdentityContextInterface $identityContext,
|
||||
private readonly UserAccountsStore $userStore
|
||||
private readonly IdentityContextInterface $identityContext,
|
||||
private readonly UserAccountsStore $userStore,
|
||||
private readonly EventDispatcherInterface $events,
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -65,17 +70,53 @@ class UserAccountsService
|
||||
|
||||
public function createUser(array $userData): array
|
||||
{
|
||||
return $this->userStore->createUser($this->tenantContext->identifier(), $userData);
|
||||
$tenantId = $this->tenantContext->requireIdentifier();
|
||||
$user = $this->userStore->createUser($tenantId, $userData);
|
||||
$this->events->dispatch(UserCreatedEvent::fromUser(
|
||||
$user,
|
||||
$tenantId,
|
||||
$this->identityContext->identifier(),
|
||||
));
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
public function updateUser(string $uid, array $updates): bool
|
||||
public function updateUser(string $userId, array $updates): bool
|
||||
{
|
||||
return $this->userStore->updateUser($this->tenantContext->identifier(), $uid, $updates);
|
||||
$tenantId = $this->tenantContext->requireIdentifier();
|
||||
if (!$this->userStore->updateUser($tenantId, $userId, $updates)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$user = $this->userStore->fetchByIdentifier($tenantId, $userId);
|
||||
if ($user === null) {
|
||||
throw new \RuntimeException("Updated user '{$userId}' could not be retrieved.");
|
||||
}
|
||||
|
||||
$this->events->dispatch(UserUpdatedEvent::fromUser(
|
||||
$user,
|
||||
$tenantId,
|
||||
$this->identityContext->identifier(),
|
||||
));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function deleteUser(string $uid): bool
|
||||
public function deleteUser(string $userId): bool
|
||||
{
|
||||
return $this->userStore->deleteUser($this->tenantContext->identifier(), $uid);
|
||||
$tenantId = $this->tenantContext->requireIdentifier();
|
||||
$user = $this->userStore->fetchByIdentifier($tenantId, $userId);
|
||||
if ($user === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->events->dispatch(UserDeletingEvent::fromUser(
|
||||
$user,
|
||||
$tenantId,
|
||||
$this->identityContext->identifier(),
|
||||
));
|
||||
|
||||
return $this->userStore->deleteUser($tenantId, $userId);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
@@ -126,10 +167,6 @@ class UserAccountsService
|
||||
return $this->userStore->storeSettings($this->tenantContext->identifier(), $this->identityContext->identifier(), $settings);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Helper Methods
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Check if a profile field is editable by the user
|
||||
*
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KTXC\User\Event;
|
||||
|
||||
final class UserCreatedEvent extends UserEvent
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KTXC\User\Event;
|
||||
|
||||
final class UserDeletingEvent extends UserEvent
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KTXC\User\Event;
|
||||
|
||||
use KTXF\Event\Event;
|
||||
|
||||
abstract class UserEvent extends Event
|
||||
{
|
||||
final public function __construct(
|
||||
private readonly string $userIdentifier,
|
||||
private readonly string $userIdentity,
|
||||
private readonly string $userLabel,
|
||||
private readonly bool $userEnabled,
|
||||
private readonly array $userRoles,
|
||||
private readonly string $tenantIdentifier,
|
||||
private readonly ?string $actorIdentifier = null,
|
||||
) {
|
||||
if ($userIdentifier === '') {
|
||||
throw new \InvalidArgumentException('User lifecycle events require a user ID.');
|
||||
}
|
||||
if ($userIdentity === '') {
|
||||
throw new \InvalidArgumentException('User lifecycle events require a user identity.');
|
||||
}
|
||||
if ($tenantIdentifier === '') {
|
||||
throw new \InvalidArgumentException('User lifecycle events require a tenant ID.');
|
||||
}
|
||||
parent::__construct(
|
||||
static::class,
|
||||
[
|
||||
'identifier' => $userIdentifier,
|
||||
'identity' => $userIdentity,
|
||||
'label' => $userLabel,
|
||||
'roles' => $userRoles,
|
||||
'enabled' => $userEnabled,
|
||||
],
|
||||
$tenantIdentifier,
|
||||
$actorIdentifier,
|
||||
);
|
||||
}
|
||||
|
||||
public static function fromUser(
|
||||
array $user,
|
||||
string $tenantIdentifier,
|
||||
?string $actorIdentifier = null,
|
||||
): static {
|
||||
return new static(
|
||||
(string) ($user['uid'] ?? ''),
|
||||
(string) ($user['identity'] ?? ''),
|
||||
(string) ($user['label'] ?? $user['identity'] ?? ''),
|
||||
(bool) ($user['enabled'] ?? true),
|
||||
array_values((array) ($user['roles'] ?? [])),
|
||||
$tenantIdentifier,
|
||||
$actorIdentifier,
|
||||
);
|
||||
}
|
||||
|
||||
public function userIdentifier(): string
|
||||
{
|
||||
return $this->userIdentifier;
|
||||
}
|
||||
|
||||
public function userIdentity(): string
|
||||
{
|
||||
return $this->userIdentity;
|
||||
}
|
||||
|
||||
public function userLabel(): string
|
||||
{
|
||||
return $this->userLabel;
|
||||
}
|
||||
|
||||
public function userRoles(): array
|
||||
{
|
||||
return $this->userRoles;
|
||||
}
|
||||
|
||||
public function userEnabled(): bool
|
||||
{
|
||||
return $this->userEnabled;
|
||||
}
|
||||
|
||||
public function actorIdentifier(): ?string
|
||||
{
|
||||
return $this->actorIdentifier;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KTXC\User\Event;
|
||||
|
||||
final class UserUpdatedEvent extends UserEvent
|
||||
{
|
||||
}
|
||||
Reference in New Issue
Block a user