refactor(support): consolidate remaining ID normalization via toIntIds()
Extends the toIntIds() rollout to service and repository layers: - Drops a third private duplicate (UserCustomFieldValueService::normalizeTenantIds) - Collapses the two-line intval+filter pattern inside UserAssignmentService, UserAuthorizationPolicy, SsoUserLinkService, DepartmentService, and UserCustomFieldValueService - Replaces inline patterns in 4 repositories (RolePermissionRepository, RoleAssignableRoleRepository, UserWriteRepository, DepartmentRepository, UserCustomFieldValueRepository, UserCustomFieldValueOptionRepository) - Simplifies the notifications sanitizeTenantIds trait body Tenant-area files deliberately untouched per parallel ongoing work on the tenant-logo refactor. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -28,8 +28,7 @@ class RoleAssignableRoleRepository implements RoleAssignableRoleRepositoryInterf
|
|||||||
|
|
||||||
public function listAssignableRoleIdsByRoleIds(array $roleIds): array
|
public function listAssignableRoleIdsByRoleIds(array $roleIds): array
|
||||||
{
|
{
|
||||||
$roleIds = array_values(array_unique(array_map('intval', $roleIds)));
|
$roleIds = toIntIds($roleIds);
|
||||||
$roleIds = array_values(array_filter($roleIds, static fn (int $id): bool => $id > 0));
|
|
||||||
if (!$roleIds) {
|
if (!$roleIds) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,8 +25,7 @@ class RolePermissionRepository implements RolePermissionRepositoryInterface
|
|||||||
|
|
||||||
public function countPermissionsByRoleIds(array $roleIds): array
|
public function countPermissionsByRoleIds(array $roleIds): array
|
||||||
{
|
{
|
||||||
$roleIds = array_values(array_unique(array_map('intval', $roleIds)));
|
$roleIds = toIntIds($roleIds);
|
||||||
$roleIds = array_values(array_filter($roleIds, static fn ($id) => $id > 0));
|
|
||||||
if (!$roleIds) {
|
if (!$roleIds) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,8 +17,7 @@ class UserCustomFieldValueOptionRepository implements UserCustomFieldValueOption
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$optionIds = array_values(array_unique(array_map('intval', $optionIds)));
|
$optionIds = toIntIds($optionIds);
|
||||||
$optionIds = array_values(array_filter($optionIds, static fn ($id) => $id > 0));
|
|
||||||
if (!$optionIds) {
|
if (!$optionIds) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -39,8 +38,7 @@ class UserCustomFieldValueOptionRepository implements UserCustomFieldValueOption
|
|||||||
|
|
||||||
public function listOptionIdsByValueIds(array $valueIds): array
|
public function listOptionIdsByValueIds(array $valueIds): array
|
||||||
{
|
{
|
||||||
$valueIds = array_values(array_unique(array_map('intval', $valueIds)));
|
$valueIds = toIntIds($valueIds);
|
||||||
$valueIds = array_values(array_filter($valueIds, static fn ($id) => $id > 0));
|
|
||||||
if (!$valueIds) {
|
if (!$valueIds) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@@ -69,7 +67,7 @@ class UserCustomFieldValueOptionRepository implements UserCustomFieldValueOption
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach ($map as &$ids) {
|
foreach ($map as &$ids) {
|
||||||
$ids = array_values(array_unique(array_map('intval', $ids)));
|
$ids = toIntIds($ids);
|
||||||
sort($ids, SORT_NUMERIC);
|
sort($ids, SORT_NUMERIC);
|
||||||
}
|
}
|
||||||
unset($ids);
|
unset($ids);
|
||||||
|
|||||||
@@ -27,8 +27,7 @@ class UserCustomFieldValueRepository implements UserCustomFieldValueRepositoryIn
|
|||||||
if ($userId <= 0) {
|
if ($userId <= 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
$definitionIds = array_values(array_unique(array_map('intval', $definitionIds)));
|
$definitionIds = toIntIds($definitionIds);
|
||||||
$definitionIds = array_values(array_filter($definitionIds, static fn ($id) => $id > 0));
|
|
||||||
if (!$definitionIds) {
|
if (!$definitionIds) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@@ -85,8 +84,7 @@ class UserCustomFieldValueRepository implements UserCustomFieldValueRepositoryIn
|
|||||||
if ($userId <= 0) {
|
if ($userId <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$definitionIds = array_values(array_unique(array_map('intval', $definitionIds)));
|
$definitionIds = toIntIds($definitionIds);
|
||||||
$definitionIds = array_values(array_filter($definitionIds, static fn ($id) => $id > 0));
|
|
||||||
if (!$definitionIds) {
|
if (!$definitionIds) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -103,8 +101,7 @@ class UserCustomFieldValueRepository implements UserCustomFieldValueRepositoryIn
|
|||||||
if ($userId <= 0) {
|
if ($userId <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$tenantIds = array_values(array_unique(array_map('intval', $tenantIds)));
|
$tenantIds = toIntIds($tenantIds);
|
||||||
$tenantIds = array_values(array_filter($tenantIds, static fn ($id) => $id > 0));
|
|
||||||
if (!$tenantIds) {
|
if (!$tenantIds) {
|
||||||
$result = DB::delete('delete from user_custom_field_values where user_id = ?', (string) $userId);
|
$result = DB::delete('delete from user_custom_field_values where user_id = ?', (string) $userId);
|
||||||
return $result !== false;
|
return $result !== false;
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ class DepartmentRepository implements DepartmentRepositoryInterface
|
|||||||
}
|
}
|
||||||
} elseif (array_key_exists('tenantIds', $options)) {
|
} elseif (array_key_exists('tenantIds', $options)) {
|
||||||
$tenantIds = $options['tenantIds'];
|
$tenantIds = $options['tenantIds'];
|
||||||
$tenantIds = is_array($tenantIds) ? array_values(array_unique(array_map('intval', $tenantIds))) : [];
|
$tenantIds = toIntIds($tenantIds);
|
||||||
if ($tenantIds) {
|
if ($tenantIds) {
|
||||||
$where[] = 'departments.tenant_id in (???)';
|
$where[] = 'departments.tenant_id in (???)';
|
||||||
$params[] = $tenantIds;
|
$params[] = $tenantIds;
|
||||||
|
|||||||
@@ -36,8 +36,7 @@ class UserWriteRepository implements UserWriteRepositoryInterface
|
|||||||
|
|
||||||
public function bumpAuthzVersionByUserIds(array $userIds): int
|
public function bumpAuthzVersionByUserIds(array $userIds): int
|
||||||
{
|
{
|
||||||
$userIds = array_values(array_unique(array_map('intval', $userIds)));
|
$userIds = toIntIds($userIds);
|
||||||
$userIds = array_values(array_filter($userIds, static fn ($id) => $id > 0));
|
|
||||||
if (!$userIds) {
|
if (!$userIds) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -409,9 +409,7 @@ class UserAuthorizationPolicy implements AuthorizationPolicyInterface
|
|||||||
'can_view_security_artifacts' => $isOwnAccount || $canViewUserAudit,
|
'can_view_security_artifacts' => $isOwnAccount || $canViewUserAudit,
|
||||||
'can_view_permissions_table' => $this->hasPermission($actorUserId, PermissionService::PERMISSIONS_VIEW),
|
'can_view_permissions_table' => $this->hasPermission($actorUserId, PermissionService::PERMISSIONS_VIEW),
|
||||||
'is_own_account' => $isOwnAccount,
|
'is_own_account' => $isOwnAccount,
|
||||||
'allowed_tenant_ids' => is_array($allowedTenantIds)
|
'allowed_tenant_ids' => is_array($allowedTenantIds) ? toIntIds($allowedTenantIds) : null,
|
||||||
? array_values(array_unique(array_map('intval', $allowedTenantIds)))
|
|
||||||
: null,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
return AuthorizationDecision::allow(['capabilities' => $capabilities]);
|
return AuthorizationDecision::allow(['capabilities' => $capabilities]);
|
||||||
|
|||||||
@@ -377,7 +377,7 @@ class SsoUserLinkService
|
|||||||
|
|
||||||
private function ensureTenantAssignment(int $userId, int $tenantId): void
|
private function ensureTenantAssignment(int $userId, int $tenantId): void
|
||||||
{
|
{
|
||||||
$tenantIds = array_values(array_unique(array_map('intval', $this->userTenantRepository->listTenantIdsByUserId($userId))));
|
$tenantIds = toIntIds($this->userTenantRepository->listTenantIdsByUserId($userId));
|
||||||
if (!in_array($tenantId, $tenantIds, true)) {
|
if (!in_array($tenantId, $tenantIds, true)) {
|
||||||
$tenantIds[] = $tenantId;
|
$tenantIds[] = $tenantId;
|
||||||
$this->userAssignmentService->syncTenants($userId, $tenantIds);
|
$this->userAssignmentService->syncTenants($userId, $tenantIds);
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class UserCustomFieldValueService
|
|||||||
if (!$canEdit) {
|
if (!$canEdit) {
|
||||||
return ['ok' => true, 'errors' => []];
|
return ['ok' => true, 'errors' => []];
|
||||||
}
|
}
|
||||||
$tenantIds = self::normalizeTenantIds($tenantIds);
|
$tenantIds = toIntIds($tenantIds);
|
||||||
if (!$tenantIds) {
|
if (!$tenantIds) {
|
||||||
return ['ok' => true, 'errors' => []];
|
return ['ok' => true, 'errors' => []];
|
||||||
}
|
}
|
||||||
@@ -38,7 +38,7 @@ class UserCustomFieldValueService
|
|||||||
|
|
||||||
public function buildDefinitionsByTenant(array $tenantIds, bool $includeInactive = false): array
|
public function buildDefinitionsByTenant(array $tenantIds, bool $includeInactive = false): array
|
||||||
{
|
{
|
||||||
$tenantIds = self::normalizeTenantIds($tenantIds);
|
$tenantIds = toIntIds($tenantIds);
|
||||||
if (!$tenantIds) {
|
if (!$tenantIds) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@@ -83,8 +83,7 @@ class UserCustomFieldValueService
|
|||||||
if ($userId <= 0) {
|
if ($userId <= 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
$definitionIds = array_values(array_unique(array_map('intval', $definitionIds)));
|
$definitionIds = toIntIds($definitionIds);
|
||||||
$definitionIds = array_values(array_filter($definitionIds, static fn ($id) => $id > 0));
|
|
||||||
if (!$definitionIds) {
|
if (!$definitionIds) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@@ -251,7 +250,7 @@ class UserCustomFieldValueService
|
|||||||
return ['ok' => false, 'errors' => [t('User not found')]];
|
return ['ok' => false, 'errors' => [t('User not found')]];
|
||||||
}
|
}
|
||||||
|
|
||||||
$tenantIds = self::normalizeTenantIds($tenantIds);
|
$tenantIds = toIntIds($tenantIds);
|
||||||
// Always clean values for tenants no longer assigned — happens even when !$canEdit,
|
// Always clean values for tenants no longer assigned — happens even when !$canEdit,
|
||||||
// because orphaned custom field data should not persist after tenant reassignment.
|
// because orphaned custom field data should not persist after tenant reassignment.
|
||||||
if (!$this->valueRepository->deleteByUserOutsideTenantIds($userId, $tenantIds)) {
|
if (!$this->valueRepository->deleteByUserOutsideTenantIds($userId, $tenantIds)) {
|
||||||
@@ -541,12 +540,6 @@ class UserCustomFieldValueService
|
|||||||
return $this->userScopeGateway;
|
return $this->userScopeGateway;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function normalizeTenantIds(array $tenantIds): array
|
|
||||||
{
|
|
||||||
$tenantIds = array_values(array_unique(array_map('intval', $tenantIds)));
|
|
||||||
return array_values(array_filter($tenantIds, static fn ($id) => $id > 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function normalizeBool($value): ?int
|
private static function normalizeBool($value): ?int
|
||||||
{
|
{
|
||||||
if (is_bool($value)) {
|
if (is_bool($value)) {
|
||||||
|
|||||||
@@ -43,8 +43,7 @@ class DepartmentService
|
|||||||
|
|
||||||
public function groupActiveByTenantIds(array $tenantIds): array
|
public function groupActiveByTenantIds(array $tenantIds): array
|
||||||
{
|
{
|
||||||
$tenantIds = array_values(array_unique(array_map('intval', $tenantIds)));
|
$tenantIds = toIntIds($tenantIds);
|
||||||
$tenantIds = array_values(array_filter($tenantIds, static fn ($id) => $id > 0));
|
|
||||||
if (!$tenantIds) {
|
if (!$tenantIds) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -176,8 +176,7 @@ class UserAssignmentService
|
|||||||
|
|
||||||
public function syncRoles(int $userId, array $roleIds, bool $bumpAuthz = true, int $actorUserId = 0): bool
|
public function syncRoles(int $userId, array $roleIds, bool $bumpAuthz = true, int $actorUserId = 0): bool
|
||||||
{
|
{
|
||||||
$ids = array_values(array_unique(array_map('intval', $roleIds)));
|
$ids = toIntIds($roleIds);
|
||||||
$ids = array_values(array_filter($ids, static fn ($id) => $id > 0));
|
|
||||||
$validIds = $this->directoryGateway->listActiveRoleIds();
|
$validIds = $this->directoryGateway->listActiveRoleIds();
|
||||||
if ($validIds) {
|
if ($validIds) {
|
||||||
$validMap = array_fill_keys($validIds, true);
|
$validMap = array_fill_keys($validIds, true);
|
||||||
@@ -201,16 +200,11 @@ class UserAssignmentService
|
|||||||
|
|
||||||
public function syncDepartments(int $userId, array $departmentIds, bool $bumpAuthz = true): bool
|
public function syncDepartments(int $userId, array $departmentIds, bool $bumpAuthz = true): bool
|
||||||
{
|
{
|
||||||
$ids = array_values(array_unique(array_map('intval', $departmentIds)));
|
$ids = toIntIds($departmentIds);
|
||||||
$ids = array_values(array_filter($ids, static fn ($id) => $id > 0));
|
|
||||||
// Important: use the user's assigned tenants directly (no permission bypass),
|
// Important: use the user's assigned tenants directly (no permission bypass),
|
||||||
// otherwise privileged users (e.g. admins) would incorrectly allow departments
|
// otherwise privileged users (e.g. admins) would incorrectly allow departments
|
||||||
// from tenants that were just removed from their assignments.
|
// from tenants that were just removed from their assignments.
|
||||||
$userTenantIds = array_values(array_unique(array_map(
|
$userTenantIds = toIntIds($this->userTenantRepository->listTenantIdsByUserId($userId));
|
||||||
'intval',
|
|
||||||
$this->userTenantRepository->listTenantIdsByUserId($userId)
|
|
||||||
)));
|
|
||||||
$userTenantIds = array_values(array_filter($userTenantIds, static fn ($id) => $id > 0));
|
|
||||||
if (!$userTenantIds) {
|
if (!$userTenantIds) {
|
||||||
$ids = [];
|
$ids = [];
|
||||||
} else {
|
} else {
|
||||||
@@ -304,14 +298,12 @@ class UserAssignmentService
|
|||||||
$collect($item);
|
$collect($item);
|
||||||
}
|
}
|
||||||
|
|
||||||
$ids = array_values(array_unique(array_map('intval', $flat)));
|
return toIntIds($flat);
|
||||||
return array_values(array_filter($ids, static fn ($id) => $id > 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function normalizeTenantIds(array $tenantIds): array
|
public function normalizeTenantIds(array $tenantIds): array
|
||||||
{
|
{
|
||||||
$ids = array_values(array_unique(array_map('intval', $tenantIds)));
|
$ids = toIntIds($tenantIds);
|
||||||
$ids = array_filter($ids, static fn ($id) => $id > 0);
|
|
||||||
$validIds = $this->directoryGateway->listTenantIds();
|
$validIds = $this->directoryGateway->listTenantIds();
|
||||||
if ($validIds) {
|
if ($validIds) {
|
||||||
$validMap = array_fill_keys($validIds, true);
|
$validMap = array_fill_keys($validIds, true);
|
||||||
|
|||||||
@@ -46,11 +46,6 @@ trait NotificationListenerTrait
|
|||||||
*/
|
*/
|
||||||
protected function sanitizeTenantIds(mixed $tenantIds): array
|
protected function sanitizeTenantIds(mixed $tenantIds): array
|
||||||
{
|
{
|
||||||
if (!is_array($tenantIds)) {
|
return is_array($tenantIds) ? toIntIds($tenantIds) : [];
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
$ids = array_values(array_unique(array_map('intval', $tenantIds)));
|
|
||||||
return array_values(array_filter($ids, static fn (int $tenantId): bool => $tenantId > 0));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user