1
0
Files
breadcrumb-the-shire/lib/Service/User/UserTenantContextService.php

207 lines
6.6 KiB
PHP

<?php
namespace MintyPHP\Service\User;
use MintyPHP\Repository\Tenant\UserTenantRepositoryInterface;
use MintyPHP\Repository\User\UserReadRepositoryInterface;
use MintyPHP\Repository\User\UserWriteRepositoryInterface;
use MintyPHP\Service\Tenant\TenantScopeService;
class UserTenantContextService
{
public function __construct(
private readonly UserReadRepositoryInterface $userReadRepository,
private readonly UserWriteRepositoryInterface $userWriteRepository,
private readonly UserTenantRepositoryInterface $userTenantRepository,
private readonly TenantScopeService $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;
}
// Priority: last-used tenant → primary tenant → first available.
$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];
}
}