Initial Version
This commit is contained in:
251
core/lib/Controllers/UserAccountsController.php
Normal file
251
core/lib/Controllers/UserAccountsController.php
Normal file
@@ -0,0 +1,251 @@
|
||||
<?php
|
||||
|
||||
namespace KTXC\Controllers;
|
||||
|
||||
use KTXC\Http\Response\JsonResponse;
|
||||
use KTXC\Service\UserAccountsService;
|
||||
use KTXC\SessionIdentity;
|
||||
use KTXC\SessionTenant;
|
||||
use KTXF\Controller\ControllerAbstract;
|
||||
use KTXF\Routing\Attributes\AuthenticatedRoute;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* User Accounts Controller
|
||||
* Core administrative user management operations
|
||||
*/
|
||||
class UserAccountsController extends ControllerAbstract
|
||||
{
|
||||
public function __construct(
|
||||
private readonly SessionTenant $tenantIdentity,
|
||||
private readonly SessionIdentity $userIdentity,
|
||||
private readonly UserAccountsService $userService,
|
||||
private readonly LoggerInterface $logger
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Main versioned endpoint for user management
|
||||
*/
|
||||
#[AuthenticatedRoute('/user/accounts/v1', name: 'user.accounts.v1', methods: ['POST'])]
|
||||
public function index(int $version, string $transaction, string $operation, array $data = []): JsonResponse
|
||||
{
|
||||
try {
|
||||
// Check admin permission
|
||||
if (!$this->userIdentity->hasPermission('user.admin')) {
|
||||
return new JsonResponse([
|
||||
'status' => 'error',
|
||||
'data' => ['code' => 403, 'message' => 'Insufficient permissions']
|
||||
], JsonResponse::HTTP_FORBIDDEN);
|
||||
}
|
||||
|
||||
$result = $this->process($operation, $data);
|
||||
|
||||
return new JsonResponse([
|
||||
'version' => $version,
|
||||
'transaction' => $transaction,
|
||||
'operation' => $operation,
|
||||
'status' => 'success',
|
||||
'data' => $result,
|
||||
], JsonResponse::HTTP_OK);
|
||||
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
return new JsonResponse([
|
||||
'version' => $version,
|
||||
'transaction' => $transaction,
|
||||
'operation' => $operation,
|
||||
'status' => 'error',
|
||||
'data' => ['code' => 400, 'message' => $e->getMessage()]
|
||||
], JsonResponse::HTTP_BAD_REQUEST);
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
$this->logger->error('User manager operation failed', [
|
||||
'operation' => $operation,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
|
||||
return new JsonResponse([
|
||||
'version' => $version,
|
||||
'transaction' => $transaction,
|
||||
'operation' => $operation,
|
||||
'status' => 'error',
|
||||
'data' => ['code' => $e->getCode(), 'message' => $e->getMessage()]
|
||||
], JsonResponse::HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process operation
|
||||
*/
|
||||
private function process(string $operation, array $data): mixed
|
||||
{
|
||||
return match ($operation) {
|
||||
'user.list' => $this->userList($data),
|
||||
'user.fetch' => $this->userFetch($data),
|
||||
'user.create' => $this->userCreate($data),
|
||||
'user.update' => $this->userUpdate($data),
|
||||
'user.delete' => $this->userDelete($data),
|
||||
'user.provider.unlink' => $this->userProviderUnlink($data),
|
||||
default => throw new \InvalidArgumentException("Invalid operation: {$operation}"),
|
||||
};
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// User Operations
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* List all users for tenant
|
||||
*/
|
||||
private function userList(array $data): array
|
||||
{
|
||||
return $this->userService->listUsers($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch single user by UID
|
||||
*/
|
||||
private function userFetch(array $data): array
|
||||
{
|
||||
$uid = $data['uid'] ?? throw new \InvalidArgumentException('User ID required');
|
||||
|
||||
$user = $this->userService->fetchByIdentifier($uid);
|
||||
if (!$user) {
|
||||
throw new \InvalidArgumentException('User not found');
|
||||
}
|
||||
|
||||
// Get editable fields for profile
|
||||
$editableFields = $this->userService->getEditableFields($uid);
|
||||
$user['profile_editable'] = $editableFields;
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new user
|
||||
*/
|
||||
private function userCreate(array $data): array
|
||||
{
|
||||
if (!$this->userIdentity->hasPermission('user.create')) {
|
||||
throw new \InvalidArgumentException('Insufficient permissions to create users');
|
||||
}
|
||||
|
||||
$userData = [
|
||||
'identity' => $data['identity'] ?? throw new \InvalidArgumentException('Identity required'),
|
||||
'label' => $data['label'] ?? $data['identity'],
|
||||
'enabled' => $data['enabled'] ?? true,
|
||||
'roles' => $data['roles'] ?? [],
|
||||
'profile' => $data['profile'] ?? [],
|
||||
'settings' => [],
|
||||
'provider' => null,
|
||||
'provider_subject' => null,
|
||||
'provider_managed_fields' => []
|
||||
];
|
||||
|
||||
$this->logger->info('Creating user', [
|
||||
'tenant' => $this->tenantIdentity->identifier(),
|
||||
'identity' => $userData['identity'],
|
||||
'actor' => $this->userIdentity->identifier()
|
||||
]);
|
||||
|
||||
return $this->userService->createUser($userData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update existing user
|
||||
*/
|
||||
private function userUpdate(array $data): bool
|
||||
{
|
||||
if (!$this->userIdentity->hasPermission('user.update')) {
|
||||
throw new \InvalidArgumentException('Insufficient permissions to update users');
|
||||
}
|
||||
|
||||
$uid = $data['uid'] ?? throw new \InvalidArgumentException('User ID required');
|
||||
|
||||
// Build updates (exclude sensitive fields)
|
||||
$updates = [];
|
||||
$allowedFields = ['label', 'enabled', 'roles', 'profile'];
|
||||
|
||||
foreach ($allowedFields as $field) {
|
||||
if (isset($data[$field])) {
|
||||
$updates[$field] = $data[$field];
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($updates)) {
|
||||
throw new \InvalidArgumentException('No valid fields to update');
|
||||
}
|
||||
|
||||
// Special handling for profile updates (respect managed fields)
|
||||
if (isset($updates['profile'])) {
|
||||
$user = $this->userService->fetchByIdentifier($uid);
|
||||
$managedFields = $user['provider_managed_fields'] ?? [];
|
||||
|
||||
foreach ($managedFields as $field) {
|
||||
unset($updates['profile'][$field]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->logger->info('Updating user', [
|
||||
'tenant' => $this->tenantIdentity->identifier(),
|
||||
'uid' => $uid,
|
||||
'actor' => $this->userIdentity->identifier()
|
||||
]);
|
||||
|
||||
return $this->userService->updateUser($uid, $updates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete user
|
||||
*/
|
||||
private function userDelete(array $data): bool
|
||||
{
|
||||
if (!$this->userIdentity->hasPermission('user.delete')) {
|
||||
throw new \InvalidArgumentException('Insufficient permissions to delete users');
|
||||
}
|
||||
|
||||
$uid = $data['uid'] ?? throw new \InvalidArgumentException('User ID required');
|
||||
|
||||
// Prevent self-deletion
|
||||
if ($uid === $this->userIdentity->identifier()) {
|
||||
throw new \InvalidArgumentException('Cannot delete your own account');
|
||||
}
|
||||
|
||||
$this->logger->info('Deleting user', [
|
||||
'tenant' => $this->tenantIdentity->identifier(),
|
||||
'uid' => $uid,
|
||||
'actor' => $this->userIdentity->identifier()
|
||||
]);
|
||||
|
||||
return $this->userService->deleteUser($uid);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Security Operations
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Unlink external provider
|
||||
*/
|
||||
private function userProviderUnlink(array $data): bool
|
||||
{
|
||||
if (!$this->userIdentity->hasPermission('user.admin')) {
|
||||
throw new \InvalidArgumentException('Insufficient permissions');
|
||||
}
|
||||
|
||||
$uid = $data['uid'] ?? throw new \InvalidArgumentException('User ID required');
|
||||
|
||||
$updates = [
|
||||
'provider' => null,
|
||||
'provider_subject' => null,
|
||||
'provider_managed_fields' => []
|
||||
];
|
||||
|
||||
$this->logger->info('Unlinking provider', [
|
||||
'tenant' => $this->tenantIdentity->identifier(),
|
||||
'uid' => $uid,
|
||||
'actor' => $this->userIdentity->identifier()
|
||||
]);
|
||||
|
||||
return $this->userService->updateUser($uid, $updates);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user