Files
breadcrumb-the-shire/pages/admin/users/edit($id).php

294 lines
13 KiB
PHP

<?php
use MintyPHP\Buffer;
use MintyPHP\DB;
use MintyPHP\I18n;
use MintyPHP\Repository\Auth\PasswordResetRepository;
use MintyPHP\Repository\Auth\RememberTokenRepository;
use MintyPHP\Router;
use MintyPHP\Service\Access\AccessServicesFactory;
use MintyPHP\Service\Access\UserAuthorizationPolicy;
use MintyPHP\Service\Auth\AuthServicesFactory;
use MintyPHP\Service\CustomField\UserCustomFieldValueService;
use MintyPHP\Service\User\UserServicesFactory;
use MintyPHP\Support\Flash;
use MintyPHP\Support\Guard;
Guard::requireLogin();
$userServicesFactory = new UserServicesFactory();
$userAccountService = $userServicesFactory->createUserAccountService();
$userAssignmentService = $userServicesFactory->createUserAssignmentService();
$userTenantRepository = $userServicesFactory->createUserTenantRepository();
$userRoleRepository = $userServicesFactory->createUserRoleRepository();
$userDepartmentRepository = $userServicesFactory->createUserDepartmentRepository();
$userPasswordPolicyService = $userServicesFactory->createUserPasswordPolicyService();
$authServiceFactory = new AuthServicesFactory();
$accessServicesFactory = new AccessServicesFactory();
$authorizationService = $accessServicesFactory->createAuthorizationService();
$rolePermissionRepository = $accessServicesFactory->createRolePermissionRepository();
$authService = $authServiceFactory->createAuthService();
$apiTokenService = $authServiceFactory->createApiTokenService();
$passwordResetRepository = new PasswordResetRepository();
$rememberTokenRepository = new RememberTokenRepository();
$passwordMinLength = $userPasswordPolicyService->minLength();
$passwordHints = $userPasswordPolicyService->hints();
$currentUserId = (int) ($_SESSION['user']['id'] ?? 0);
$uuid = trim((string) ($id ?? ''));
$user = $uuid !== '' ? $userAccountService->findByUuid($uuid) : null;
if (!$user) {
Flash::error('User not found', 'admin/users', 'user_not_found');
Router::redirect('admin/users');
}
$userId = (int) ($user['id'] ?? 0);
$contextDecision = $authorizationService->authorize(UserAuthorizationPolicy::ABILITY_ADMIN_USERS_EDIT_CONTEXT, [
'actor_user_id' => $currentUserId,
'target_user_id' => $userId,
]);
if (!$contextDecision->isAllowed()) {
Router::redirect('error/forbidden');
return;
}
$capabilities = $contextDecision->attribute('capabilities', []);
if (!is_array($capabilities)) {
$capabilities = [];
}
$canViewUsers = (bool) ($capabilities['can_view_users'] ?? false);
$canViewPage = (bool) ($capabilities['can_view_page'] ?? false);
$canEditUser = (bool) ($capabilities['can_edit_user'] ?? false);
$canEditAssignments = (bool) ($capabilities['can_edit_assignments'] ?? false);
$canManageTenants = (bool) ($capabilities['can_manage_tenants'] ?? false);
$canEditCustomFieldValues = (bool) ($capabilities['can_edit_custom_field_values'] ?? false);
$canManageApiTokens = (bool) ($capabilities['can_manage_api_tokens'] ?? false);
$canViewAddressBook = (bool) ($capabilities['can_view_address_book'] ?? false);
$canViewUserMeta = (bool) ($capabilities['can_view_user_meta'] ?? false);
$canViewUserAudit = (bool) ($capabilities['can_view_user_audit'] ?? false);
$canAccessPdf = (bool) ($capabilities['can_access_pdf'] ?? false);
$canDeleteUser = (bool) ($capabilities['can_delete_user'] ?? false);
$canViewSecurityArtifacts = (bool) ($capabilities['can_view_security_artifacts'] ?? false);
$canViewPermissionsTable = (bool) ($capabilities['can_view_permissions_table'] ?? false);
$isOwnAccount = (bool) ($capabilities['is_own_account'] ?? false);
$allowedTenantIdsRaw = $capabilities['allowed_tenant_ids'] ?? null;
$allowedTenantIds = is_array($allowedTenantIdsRaw)
? array_values(array_unique(array_filter(array_map('intval', $allowedTenantIdsRaw), static fn (int $tenantId): bool => $tenantId > 0)))
: null;
if (!$canViewPage) {
Router::redirect('error/forbidden');
return;
}
if ($canViewUserMeta || $canViewUserAudit) {
$creatorId = (int) ($user['created_by'] ?? 0);
if ($creatorId > 0) {
$creator = $userAccountService->findById($creatorId);
if ($creator) {
$creatorName = trim(($creator['first_name'] ?? '') . ' ' . ($creator['last_name'] ?? ''));
$user['created_by_label'] = $creatorName !== '' ? $creatorName : ($creator['email'] ?? '');
$user['created_by_uuid'] = $creator['uuid'] ?? null;
}
}
$modifierId = (int) ($user['modified_by'] ?? 0);
if ($modifierId > 0) {
$modifier = $userAccountService->findById($modifierId);
if ($modifier) {
$modifierName = trim(($modifier['first_name'] ?? '') . ' ' . ($modifier['last_name'] ?? ''));
$user['modified_by_label'] = $modifierName !== '' ? $modifierName : ($modifier['email'] ?? '');
$user['modified_by_uuid'] = $modifier['uuid'] ?? null;
}
}
$activeChangedById = (int) ($user['active_changed_by'] ?? 0);
if ($activeChangedById > 0) {
$activeChanger = $userAccountService->findById($activeChangedById);
if ($activeChanger) {
$activeChangerName = trim(($activeChanger['first_name'] ?? '') . ' ' . ($activeChanger['last_name'] ?? ''));
$user['active_changed_by_label'] = $activeChangerName !== '' ? $activeChangerName : ($activeChanger['email'] ?? '');
$user['active_changed_by_uuid'] = $activeChanger['uuid'] ?? null;
}
}
}
$errors = [];
$form = $user;
$scopeGateway = directoryServicesFactory()->createDirectoryScopeGateway();
$strictTenantScope = $scopeGateway->isStrict();
$tenants = directoryServicesFactory()->createTenantService()->list();
if (is_array($allowedTenantIds)) {
$tenants = array_values(array_filter($tenants, static function (array $tenant) use ($allowedTenantIds): bool {
$tenantId = (int) ($tenant['id'] ?? 0);
return $tenantId > 0 && in_array($tenantId, $allowedTenantIds, true);
}));
} elseif (!$canManageTenants && $strictTenantScope) {
$tenants = [];
}
$selectedTenantIds = $userTenantRepository->listTenantIdsByUserId($userId);
if (is_array($allowedTenantIds)) {
$selectedTenantIds = array_values(array_intersect($selectedTenantIds, $allowedTenantIds));
}
$primaryTenantId = (int) ($user['primary_tenant_id'] ?? 0);
if (!$primaryTenantId && count($selectedTenantIds) === 1) {
$primaryTenantId = (int) $selectedTenantIds[0];
$user['primary_tenant_id'] = $primaryTenantId;
}
$roles = directoryServicesFactory()->createRoleService()->listActive();
$selectedRoleIds = $userRoleRepository->listRoleIdsByUserId($userId);
$permissionRows = $canViewPermissionsTable
? $rolePermissionRepository->listPermissionsWithRolesByRoleIds($selectedRoleIds)
: [];
$selectedDepartmentIds = $userDepartmentRepository->listDepartmentIdsByUserId($userId);
$departmentOptionsByTenant = directoryServicesFactory()->createDepartmentService()->groupActiveByTenantIds($selectedTenantIds);
$customFieldDefinitionsByTenant = UserCustomFieldValueService::buildDefinitionsByTenant($selectedTenantIds);
$customFieldDefinitionIds = [];
foreach ($customFieldDefinitionsByTenant as $tenantDefinitions) {
foreach ($tenantDefinitions as $definition) {
$definitionId = (int) ($definition['id'] ?? 0);
if ($definitionId > 0) {
$customFieldDefinitionIds[] = $definitionId;
}
}
}
$customFieldValueMap = UserCustomFieldValueService::buildUserValueMap($userId, $customFieldDefinitionIds);
$customFieldPostedValues = [
'custom_field_values' => [],
'custom_field_values_multi' => [],
];
$passwordResets = [];
$rememberTokens = [];
$apiTokens = [];
if ($canViewSecurityArtifacts) {
$passwordResets = $passwordResetRepository->listByUserId($userId, 25);
$rememberTokens = $rememberTokenRepository->listByUserId($userId, 25);
$apiTokens = $apiTokenService->listForUser($userId);
}
$canManageApiTokens = $canManageApiTokens && $canViewSecurityArtifacts;
$showApiTokens = $canViewSecurityArtifacts && (!empty($apiTokens) || $canManageApiTokens);
if (isset($_POST['email'])) {
$submitDecision = $authorizationService->authorize(UserAuthorizationPolicy::ABILITY_ADMIN_USERS_EDIT_SUBMIT, [
'actor_user_id' => $currentUserId,
'target_user_id' => $userId,
]);
if (!$submitDecision->isAllowed()) {
Router::redirect('error/forbidden');
return;
}
$submitCapabilities = $submitDecision->attribute('capabilities', []);
if (!is_array($submitCapabilities) || !isset($submitCapabilities['can_edit_user']) || !$submitCapabilities['can_edit_user']) {
Router::redirect('error/forbidden');
return;
}
$canEditUser = (bool) $submitCapabilities['can_edit_user'];
$canEditAssignments = (bool) ($submitCapabilities['can_edit_assignments'] ?? $canEditAssignments);
$canEditCustomFieldValues = (bool) ($submitCapabilities['can_edit_custom_field_values'] ?? $canEditCustomFieldValues);
$result = $userAccountService->updateFromAdmin($userId, $_POST, $currentUserId);
$form = $result['form'] ?? $form;
$errors = $result['errors'] ?? [];
$tenantIds = $userAssignmentService->normalizeIdInput($_POST['tenant_ids'] ?? []);
$existingTenantIds = $userTenantRepository->listTenantIdsByUserId($userId);
$tenantIdsForSync = $tenantIds;
if ($canEditAssignments && is_array($allowedTenantIds)) {
$tenantIds = array_values(array_intersect($tenantIds, $allowedTenantIds));
$tenantIdsForSync = $scopeGateway->mergeTenantIdsPreservingOutOfScope(
$tenantIds,
$existingTenantIds,
$allowedTenantIds
);
} elseif ($canEditAssignments && !$canManageTenants && $strictTenantScope) {
$tenantIds = [];
$tenantIdsForSync = $existingTenantIds;
}
if (!$canEditAssignments) {
$tenantIds = $userTenantRepository->listTenantIdsByUserId($userId);
if (is_array($allowedTenantIds)) {
$tenantIds = array_values(array_intersect($tenantIds, $allowedTenantIds));
}
$tenantIdsForSync = $tenantIds;
}
$selectedTenantIds = $tenantIds;
$primaryTenantId = (int) ($_POST['primary_tenant_id'] ?? 0);
$selectedRoleIds = $userAssignmentService->normalizeIdInput($_POST['role_ids'] ?? []);
$selectedDepartmentIds = $userAssignmentService->normalizeIdInput($_POST['department_ids'] ?? []);
$departmentOptionsByTenant = directoryServicesFactory()->createDepartmentService()->groupActiveByTenantIds($selectedTenantIds);
$customFieldDefinitionsByTenant = UserCustomFieldValueService::buildDefinitionsByTenant($selectedTenantIds);
$customFieldDefinitionIds = [];
foreach ($customFieldDefinitionsByTenant as $tenantDefinitions) {
foreach ($tenantDefinitions as $definition) {
$definitionId = (int) ($definition['id'] ?? 0);
if ($definitionId > 0) {
$customFieldDefinitionIds[] = $definitionId;
}
}
}
$customFieldValueMap = UserCustomFieldValueService::buildUserValueMap($userId, $customFieldDefinitionIds);
$customFieldPostedValues = [
'custom_field_values' => is_array($_POST['custom_field_values'] ?? null) ? $_POST['custom_field_values'] : [],
'custom_field_values_multi' => is_array($_POST['custom_field_values_multi'] ?? null) ? $_POST['custom_field_values_multi'] : [],
];
if ($result['ok'] ?? false) {
if ($canEditAssignments) {
$db = DB::handle();
$db->begin_transaction();
try {
$userAssignmentService->syncTenants($userId, $tenantIdsForSync, false);
$userAssignmentService->syncRoles($userId, $selectedRoleIds, false);
$userAssignmentService->syncDepartments($userId, $selectedDepartmentIds, false);
$userAssignmentService->bumpAuthzVersion($userId);
$db->commit();
} catch (\Throwable) {
$db->rollback();
$errors[] = t('User assignments can not be updated');
}
if ($currentUserId === $userId && !$errors) {
$authService->loadTenantDataIntoSession($userId);
}
}
$customFieldSyncResult = UserCustomFieldValueService::syncForUser(
$userId,
$selectedTenantIds,
$_POST,
$canEditCustomFieldValues
);
if (!($customFieldSyncResult['ok'] ?? false)) {
$errors = array_merge($errors, $customFieldSyncResult['errors'] ?? []);
$customFieldValueMap = UserCustomFieldValueService::buildUserValueMap($userId, $customFieldDefinitionIds);
}
if ($currentUserId === $userId && isset($form['theme'])) {
$themes = appThemes();
$themeValue = strtolower(trim((string) ($form['theme'] ?? '')));
$_SESSION['user']['theme'] = isset($themes[$themeValue]) ? $themeValue : appDefaultTheme();
}
if ($currentUserId === $userId && isset($form['locale'])) {
$_SESSION['user']['locale'] = $form['locale'];
I18n::$locale = $form['locale'];
}
if (!$errors) {
$action = (string) ($_POST['action'] ?? 'save');
if ($action === 'save_close') {
Flash::success('User updated', 'admin/users', 'user_updated');
Router::redirect('admin/users');
} else {
Flash::success('User updated', "admin/users/edit/{$uuid}", 'user_updated');
Router::redirect("admin/users/edit/{$uuid}");
}
}
}
}
Buffer::set('title', $isOwnAccount ? t('My account') : t('Edit user'));