forked from fa/breadcrumb-the-shire
205 lines
6.4 KiB
PHP
205 lines
6.4 KiB
PHP
<?php
|
|
|
|
namespace MintyPHP\Service\User;
|
|
|
|
use MintyPHP\Repository\Tenant\UserTenantRepositoryInterface;
|
|
use MintyPHP\Repository\User\UserReadRepositoryInterface;
|
|
use MintyPHP\Repository\User\UserWriteRepositoryInterface;
|
|
|
|
class UserTenantContextService
|
|
{
|
|
public function __construct(
|
|
private readonly UserReadRepositoryInterface $userReadRepository,
|
|
private readonly UserWriteRepositoryInterface $userWriteRepository,
|
|
private readonly UserTenantRepositoryInterface $userTenantRepository,
|
|
private readonly UserScopeGateway $scopeGateway,
|
|
private readonly UserDirectoryGateway $directoryGateway
|
|
) {
|
|
}
|
|
|
|
/**
|
|
* Get the current tenant ID for a user (with fallback to primary tenant)
|
|
*/
|
|
public function getCurrentTenantId(int $userId): ?int
|
|
{
|
|
$user = $this->userReadRepository->find($userId);
|
|
if (!$user) {
|
|
return null;
|
|
}
|
|
|
|
$activeTenantIds = $this->scopeGateway->getUserTenantIds($userId);
|
|
if (!$activeTenantIds) {
|
|
return null;
|
|
}
|
|
|
|
$currentTenantId = (int) ($user['current_tenant_id'] ?? 0);
|
|
if ($currentTenantId > 0 && in_array($currentTenantId, $activeTenantIds, true)) {
|
|
return $currentTenantId;
|
|
}
|
|
|
|
$primaryTenantId = (int) ($user['primary_tenant_id'] ?? 0);
|
|
if ($primaryTenantId > 0 && in_array($primaryTenantId, $activeTenantIds, true)) {
|
|
return $primaryTenantId;
|
|
}
|
|
|
|
return $activeTenantIds[0] ?? null;
|
|
}
|
|
|
|
/**
|
|
* Get the current tenant data for a user
|
|
*/
|
|
public function getCurrentTenant(int $userId): ?array
|
|
{
|
|
$currentTenantId = $this->getCurrentTenantId($userId);
|
|
if (!$currentTenantId) {
|
|
return null;
|
|
}
|
|
|
|
return $this->directoryGateway->findTenant($currentTenantId);
|
|
}
|
|
|
|
/**
|
|
* Get all tenants the user has access to
|
|
*/
|
|
public function getAvailableTenants(int $userId): array
|
|
{
|
|
$tenantIds = $this->scopeGateway->getUserTenantIds($userId);
|
|
if (!$tenantIds) {
|
|
return [];
|
|
}
|
|
|
|
$tenants = [];
|
|
foreach ($tenantIds as $tenantId) {
|
|
$tenant = $this->directoryGateway->findTenant((int) $tenantId);
|
|
if ($tenant && (($tenant['status'] ?? 'active') === 'active')) {
|
|
$tenants[] = $tenant;
|
|
}
|
|
}
|
|
|
|
usort($tenants, static function ($a, $b) {
|
|
return strcmp((string) ($a['description'] ?? ''), (string) ($b['description'] ?? ''));
|
|
});
|
|
|
|
return $tenants;
|
|
}
|
|
|
|
/**
|
|
* Get only explicitly assigned active tenants for a user.
|
|
* This intentionally ignores global tenant-scope bypass permissions.
|
|
*/
|
|
public function getAssignedActiveTenants(int $userId): array
|
|
{
|
|
if ($userId <= 0) {
|
|
return [];
|
|
}
|
|
|
|
$tenantIds = array_values(array_unique(array_map(
|
|
'intval',
|
|
$this->userTenantRepository->listTenantIdsByUserId($userId)
|
|
)));
|
|
$tenantIds = array_values(array_filter($tenantIds, static fn ($id) => $id > 0));
|
|
if (!$tenantIds) {
|
|
return [];
|
|
}
|
|
|
|
$activeTenantIds = $this->directoryGateway->listActiveTenantIdsByIds($tenantIds);
|
|
if (!$activeTenantIds) {
|
|
return [];
|
|
}
|
|
|
|
$tenantsById = [];
|
|
foreach ($this->directoryGateway->listTenantsByIds($activeTenantIds) as $tenant) {
|
|
$tenantId = (int) ($tenant['id'] ?? 0);
|
|
if ($tenantId <= 0) {
|
|
continue;
|
|
}
|
|
$tenantsById[$tenantId] = $tenant;
|
|
}
|
|
|
|
$tenants = [];
|
|
foreach ($tenantIds as $tenantId) {
|
|
if (isset($tenantsById[$tenantId])) {
|
|
$tenants[] = $tenantsById[$tenantId];
|
|
}
|
|
}
|
|
|
|
usort($tenants, static function ($a, $b) {
|
|
return strcmp((string) ($a['description'] ?? ''), (string) ($b['description'] ?? ''));
|
|
});
|
|
|
|
return $tenants;
|
|
}
|
|
|
|
/**
|
|
* Get available departments grouped by tenant for a user.
|
|
* Returns an array of groups: ['tenant' => [...], 'departments' => [...]]
|
|
*/
|
|
public function getAvailableDepartmentsByTenant(int $userId): array
|
|
{
|
|
$tenants = $this->getAvailableTenants($userId);
|
|
if (!$tenants) {
|
|
return [];
|
|
}
|
|
|
|
$groups = [];
|
|
foreach ($tenants as $tenant) {
|
|
$tenantId = (int) ($tenant['id'] ?? 0);
|
|
if ($tenantId <= 0) {
|
|
continue;
|
|
}
|
|
$departments = $this->directoryGateway->listDepartmentsByTenantIds([$tenantId]);
|
|
usort($departments, static function ($a, $b) {
|
|
return strcmp((string) ($a['description'] ?? ''), (string) ($b['description'] ?? ''));
|
|
});
|
|
$departments = array_map(static function (array $department): array {
|
|
return [
|
|
'id' => (int) ($department['id'] ?? 0),
|
|
'uuid' => $department['uuid'] ?? '',
|
|
'description' => $department['description'] ?? '',
|
|
];
|
|
}, $departments);
|
|
|
|
$groups[] = [
|
|
'tenant' => [
|
|
'id' => (int) ($tenant['id'] ?? 0),
|
|
'uuid' => $tenant['uuid'] ?? '',
|
|
'description' => $tenant['description'] ?? '',
|
|
],
|
|
'departments' => $departments,
|
|
];
|
|
}
|
|
|
|
return $groups;
|
|
}
|
|
|
|
/**
|
|
* Set the current tenant for a user (with validation)
|
|
*/
|
|
public function setCurrentTenant(int $userId, int $tenantId): array
|
|
{
|
|
if ($tenantId <= 0) {
|
|
return ['ok' => false, 'error' => 'invalid_tenant'];
|
|
}
|
|
|
|
$userTenantIds = $this->userTenantRepository->listTenantIdsByUserId($userId);
|
|
if (!in_array($tenantId, $userTenantIds, true)) {
|
|
return ['ok' => false, 'error' => 'tenant_not_assigned'];
|
|
}
|
|
|
|
$tenant = $this->directoryGateway->findTenant($tenantId);
|
|
if (!$tenant) {
|
|
return ['ok' => false, 'error' => 'tenant_not_found'];
|
|
}
|
|
if (($tenant['status'] ?? 'active') !== 'active') {
|
|
return ['ok' => false, 'error' => 'tenant_inactive'];
|
|
}
|
|
|
|
$updated = $this->userWriteRepository->setCurrentTenant($userId, $tenantId);
|
|
if (!$updated) {
|
|
return ['ok' => false, 'error' => 'update_failed'];
|
|
}
|
|
|
|
return ['ok' => true, 'tenant' => $tenant];
|
|
}
|
|
}
|