refactor(arch): enforce gateway compliance and remove service-wrapping gateways

This commit is contained in:
2026-03-13 11:31:33 +01:00
parent 082fa4c9a5
commit 892da0048d
96 changed files with 1117 additions and 1060 deletions

View File

@@ -9,7 +9,6 @@ use MintyPHP\Repository\Access\RolePermissionRepository;
use MintyPHP\Repository\Access\RoleRepository;
use MintyPHP\Service\Access\AccessServicesFactory;
use MintyPHP\Service\Access\AuthorizationService;
use MintyPHP\Service\Access\PermissionGateway;
use MintyPHP\Service\Access\PermissionService;
use MintyPHP\Service\Access\RoleService;
use MintyPHP\Service\Access\UiAccessService;
@@ -19,7 +18,6 @@ final class AccessRegistrar implements ContainerRegistrar
{
public function register(AppContainer $container): void
{
$container->set(PermissionGateway::class, static fn (AppContainer $c): PermissionGateway => $c->get(AccessServicesFactory::class)->createPermissionGateway());
$container->set(AuthorizationService::class, static fn (AppContainer $c): AuthorizationService => $c->get(AccessServicesFactory::class)->createAuthorizationService());
$container->set(UiAccessService::class, static fn (AppContainer $c): UiAccessService => new UiAccessService(
$c->get(AuthorizationService::class)

View File

@@ -15,7 +15,7 @@ use MintyPHP\Http\SessionStoreInterface;
use MintyPHP\Repository\Search\SearchQueryRepository;
use MintyPHP\Repository\Stats\AdminStatsRepository;
use MintyPHP\Repository\Tenant\UserTenantRepository;
use MintyPHP\Service\Access\PermissionGateway;
use MintyPHP\Service\Access\PermissionService;
use MintyPHP\Service\AddressBook\AddressBookService;
use MintyPHP\Service\AddressBook\AddressBookServicesFactory;
use MintyPHP\Service\Audit\ApiAuditService;
@@ -49,7 +49,7 @@ final class AppServicesRegistrar implements ContainerRegistrar
$c->get(AdminStatsRepository::class)
));
$container->set(SearchDataService::class, static fn (AppContainer $c): SearchDataService => new SearchDataService(
$c->get(PermissionGateway::class),
$c->get(PermissionService::class),
$c->get(UserTenantRepository::class),
$c->get(SearchQueryRepository::class)
));

View File

@@ -12,7 +12,6 @@ use MintyPHP\Repository\Tenant\TenantRepository;
use MintyPHP\Service\Auth\ApiTokenEndpointService;
use MintyPHP\Service\Auth\ApiTokenService;
use MintyPHP\Service\Auth\AuthRepositoryFactory;
use MintyPHP\Service\Auth\AuthScopeGateway;
use MintyPHP\Service\Auth\AuthService;
use MintyPHP\Service\Auth\AuthServicesFactory;
use MintyPHP\Service\Auth\EmailVerificationService;
@@ -32,14 +31,13 @@ final class AuthRegistrar implements ContainerRegistrar
$container->set(ApiTokenEndpointService::class, static fn (AppContainer $c): ApiTokenEndpointService => new ApiTokenEndpointService(
$c->get(AuthRepositoryFactory::class)->createApiTokenRepository(),
$c->get(TenantRepository::class),
$c->get(AuthScopeGateway::class)
$c->get(\MintyPHP\Service\Tenant\TenantScopeService::class)
));
$container->set(PasswordResetService::class, static fn (AppContainer $c): PasswordResetService => $c->get(AuthServicesFactory::class)->createPasswordResetService());
$container->set(EmailVerificationService::class, static fn (AppContainer $c): EmailVerificationService => $c->get(AuthServicesFactory::class)->createEmailVerificationService());
$container->set(TenantSsoService::class, static fn (AppContainer $c): TenantSsoService => $c->get(AuthServicesFactory::class)->createTenantSsoService());
$container->set(MicrosoftOidcService::class, static fn (AppContainer $c): MicrosoftOidcService => $c->get(AuthServicesFactory::class)->createMicrosoftOidcService());
$container->set(SsoUserLinkService::class, static fn (AppContainer $c): SsoUserLinkService => $c->get(AuthServicesFactory::class)->createSsoUserLinkService());
$container->set(AuthScopeGateway::class, static fn (AppContainer $c): AuthScopeGateway => $c->get(AuthServicesFactory::class)->createAuthScopeGateway());
$container->set(TenantMicrosoftAuthRepository::class, static fn (AppContainer $c): TenantMicrosoftAuthRepository => $c->get(AuthRepositoryFactory::class)->createTenantMicrosoftAuthRepository());
$container->set(ApiTokenRepository::class, static fn (AppContainer $c): ApiTokenRepository => $c->get(AuthRepositoryFactory::class)->createApiTokenRepository());
$container->set(RememberTokenRepository::class, static fn (AppContainer $c): RememberTokenRepository => $c->get(AuthRepositoryFactory::class)->createRememberTokenRepository());

View File

@@ -6,7 +6,6 @@ use MintyPHP\App\AppContainer;
use MintyPHP\App\Container\ContainerRegistrar;
use MintyPHP\Repository\Org\DepartmentRepository;
use MintyPHP\Repository\Tenant\TenantRepository;
use MintyPHP\Service\Directory\DirectoryScopeGateway;
use MintyPHP\Service\Directory\DirectoryServicesFactory;
use MintyPHP\Service\Directory\DirectorySettingsGateway;
use MintyPHP\Service\Org\DepartmentService;
@@ -23,7 +22,6 @@ final class DirectoryRegistrar implements ContainerRegistrar
$container->set(TenantService::class, static fn (AppContainer $c): TenantService => $c->get(DirectoryServicesFactory::class)->createTenantService());
$container->set(TenantScopeService::class, static fn (AppContainer $c): TenantScopeService => $c->get(TenantServicesFactory::class)->createTenantScopeService());
$container->set(DepartmentService::class, static fn (AppContainer $c): DepartmentService => $c->get(DirectoryServicesFactory::class)->createDepartmentService());
$container->set(DirectoryScopeGateway::class, static fn (AppContainer $c): DirectoryScopeGateway => $c->get(DirectoryServicesFactory::class)->createDirectoryScopeGateway());
$container->set(DirectorySettingsGateway::class, static fn (AppContainer $c): DirectorySettingsGateway => $c->get(DirectoryServicesFactory::class)->createDirectorySettingsGateway());
$container->set(TenantAvatarService::class, static fn (AppContainer $c): TenantAvatarService => $c->get(TenantServicesFactory::class)->createTenantAvatarService());
$container->set(TenantFaviconService::class, static fn (AppContainer $c): TenantFaviconService => $c->get(TenantServicesFactory::class)->createTenantFaviconService());

View File

@@ -13,7 +13,7 @@ use MintyPHP\Service\Access\AccessGatewayFactory;
use MintyPHP\Service\Access\AccessPolicyFactory;
use MintyPHP\Service\Access\AccessRepositoryFactory;
use MintyPHP\Service\Access\AccessServicesFactory;
use MintyPHP\Service\Access\PermissionGateway;
use MintyPHP\Service\Access\PermissionService;
use MintyPHP\Service\AddressBook\AddressBookServicesFactory;
use MintyPHP\Service\Audit\AuditRepositoryFactory;
use MintyPHP\Service\Audit\AuditServicesFactory;
@@ -24,7 +24,6 @@ use MintyPHP\Service\Branding\BrandingServicesFactory;
use MintyPHP\Service\CustomField\CustomFieldServicesFactory;
use MintyPHP\Service\Directory\DirectoryGatewayFactory;
use MintyPHP\Service\Directory\DirectoryRepositoryFactory;
use MintyPHP\Service\Directory\DirectoryScopeGateway;
use MintyPHP\Service\Directory\DirectoryServicesFactory;
use MintyPHP\Service\Import\ImportRepositoryFactory;
use MintyPHP\Service\Import\ImportServicesFactory;
@@ -41,7 +40,6 @@ use MintyPHP\Service\Tenant\TenantScopeService;
use MintyPHP\Service\Tenant\TenantServicesFactory;
use MintyPHP\Service\User\UserGatewayFactory;
use MintyPHP\Service\User\UserRepositoryFactory;
use MintyPHP\Service\User\UserScopeGateway;
use MintyPHP\Service\User\UserServicesFactory;
final class ServiceFactoryRegistrar implements ContainerRegistrar
@@ -69,10 +67,10 @@ final class ServiceFactoryRegistrar implements ContainerRegistrar
));
$container->set(CustomFieldServicesFactory::class, static fn (AppContainer $c): CustomFieldServicesFactory => new CustomFieldServicesFactory(
$c->get(TenantRepository::class),
$c->get(UserScopeGateway::class)
$c->get(TenantScopeService::class)
));
$container->set(TenantServicesFactory::class, static fn (AppContainer $c): TenantServicesFactory => new TenantServicesFactory(
$c->get(PermissionGateway::class),
$c->get(PermissionService::class),
$c->get(TenantRepositoryFactory::class)
));
$container->set(ImportServicesFactory::class, static fn (AppContainer $c): ImportServicesFactory => new ImportServicesFactory(
@@ -81,6 +79,7 @@ final class ServiceFactoryRegistrar implements ContainerRegistrar
$c->get(SettingServicesFactory::class),
$c->get(DirectoryServicesFactory::class),
$c->get(ImportRepositoryFactory::class),
$c->get(TenantScopeService::class),
$c->get(SessionStoreInterface::class)
));
$container->set(SchedulerServicesFactory::class, static fn (AppContainer $c): SchedulerServicesFactory => new SchedulerServicesFactory(
@@ -92,7 +91,8 @@ final class ServiceFactoryRegistrar implements ContainerRegistrar
$container->set(AddressBookServicesFactory::class, static fn (AppContainer $c): AddressBookServicesFactory => new AddressBookServicesFactory(
$c->get(UserServicesFactory::class),
$c->get(DirectoryServicesFactory::class),
$c->get(CustomFieldServicesFactory::class)
$c->get(CustomFieldServicesFactory::class),
$c->get(TenantScopeService::class)
));
$container->set(DirectoryServicesFactory::class, static fn (AppContainer $c): DirectoryServicesFactory => new DirectoryServicesFactory(
$c->get(UserServicesFactory::class),
@@ -108,7 +108,7 @@ final class ServiceFactoryRegistrar implements ContainerRegistrar
$c->get(AccessRepositoryFactory::class),
$c->get(AccessGatewayFactory::class),
// Wrapped in a second closure to break the circular dependency:
// AccessServicesFactory <-> AccessPolicyFactory via PermissionGateway.
// AccessServicesFactory <-> AccessPolicyFactory via PermissionService.
static fn (): AccessPolicyFactory => $c->get(AccessPolicyFactory::class)
));
$container->set(AccessGatewayFactory::class, static fn (AppContainer $c): AccessGatewayFactory => new AccessGatewayFactory(
@@ -117,8 +117,8 @@ final class ServiceFactoryRegistrar implements ContainerRegistrar
$c->get(SessionStoreInterface::class)
));
$container->set(AccessPolicyFactory::class, static fn (AppContainer $c): AccessPolicyFactory => new AccessPolicyFactory(
$c->get(PermissionGateway::class),
$c->get(DirectoryScopeGateway::class)
$c->get(PermissionService::class),
$c->get(TenantScopeService::class)
));
$container->set(UserGatewayFactory::class, static fn (AppContainer $c): UserGatewayFactory => new UserGatewayFactory(
$c->get(AccessServicesFactory::class),
@@ -135,7 +135,6 @@ final class ServiceFactoryRegistrar implements ContainerRegistrar
$container->set(AuthGatewayFactory::class, static fn (AppContainer $c): AuthGatewayFactory => new AuthGatewayFactory(
$c->get(AuthRepositoryFactory::class),
$c->get(AccessServicesFactory::class),
$c->get(UserServicesFactory::class),
$c->get(SettingServicesFactory::class),
$c->get(TenantScopeService::class)
));

View File

@@ -26,7 +26,7 @@ use MintyPHP\Service\Settings\SettingsSessionGateway;
use MintyPHP\Service\Settings\SettingsSmtpGateway;
use MintyPHP\Service\Settings\SettingsSystemAuditGateway;
use MintyPHP\Service\Settings\SettingsUserLifecycleGateway;
use MintyPHP\Service\Settings\ThemeConfigService;
use MintyPHP\Service\Settings\ThemeConfigGateway;
use MintyPHP\Service\Tenant\TenantService;
final class SettingsRegistrar implements ContainerRegistrar
@@ -65,7 +65,7 @@ final class SettingsRegistrar implements ContainerRegistrar
$c->get(ApiTokenRepository::class),
$c->get(SystemAuditService::class)
));
$container->set(ThemeConfigService::class, static fn (AppContainer $c): ThemeConfigService => $c->get(SettingServicesFactory::class)->createThemeConfigService());
$container->set(ThemeConfigGateway::class, static fn (AppContainer $c): ThemeConfigGateway => $c->get(SettingServicesFactory::class)->createThemeConfigGateway());
// Branding services are registered here because they depend on settings gateways
// and are consumed alongside settings (e.g. in UserAccessPdfService and the admin UI).
$container->set(BrandingLogoService::class, static fn (AppContainer $c): BrandingLogoService => $c->get(BrandingServicesFactory::class)->createBrandingLogoService());

View File

@@ -24,7 +24,6 @@ use MintyPHP\Service\User\UserPasswordPolicyService;
use MintyPHP\Service\User\UserPasswordService;
use MintyPHP\Service\User\UserRepositoryFactory;
use MintyPHP\Service\User\UserSavedFilterService;
use MintyPHP\Service\User\UserScopeGateway;
use MintyPHP\Service\User\UserServicesFactory;
use MintyPHP\Service\User\UserTenantContextService;
@@ -37,7 +36,6 @@ final class UserRegistrar implements ContainerRegistrar
$container->set(UserPasswordService::class, static fn (AppContainer $c): UserPasswordService => $c->get(UserServicesFactory::class)->createUserPasswordService());
$container->set(UserAvatarService::class, static fn (AppContainer $c): UserAvatarService => $c->get(UserServicesFactory::class)->createUserAvatarService());
$container->set(UserDirectoryGateway::class, static fn (AppContainer $c): UserDirectoryGateway => $c->get(UserServicesFactory::class)->createUserDirectoryGateway());
$container->set(UserScopeGateway::class, static fn (AppContainer $c): UserScopeGateway => $c->get(UserServicesFactory::class)->createUserScopeGateway());
$container->set(UserAccessTemplateService::class, static fn (AppContainer $c): UserAccessTemplateService => new UserAccessTemplateService(
$c->get(TenantSsoService::class),
$c->get(UserDirectoryGateway::class),

View File

@@ -6,14 +6,14 @@ use MintyPHP\Repository\Access\RolePermissionRepository;
use MintyPHP\Repository\Access\UserRoleRepository;
use MintyPHP\Service\Access\AuthorizationService;
use MintyPHP\Service\Auth\ApiTokenService;
use MintyPHP\Service\Auth\AuthScopeGateway;
use MintyPHP\Service\Tenant\TenantScopeService;
use MintyPHP\Service\User\UserTenantContextService;
// Static facade for API authentication — state is set once per request during ApiBootstrap::init().
// Dependencies are injected as lazy resolvers so services are only instantiated when actually needed.
class ApiAuth
{
/** @var (callable(): AuthScopeGateway)|null */
/** @var (callable(): TenantScopeService)|null */
private static $authScopeGatewayResolver = null;
/** @var (callable(): ApiTokenService)|null */
@@ -239,9 +239,9 @@ class ApiAuth
}
}
private static function scopeGateway(): AuthScopeGateway
private static function scopeGateway(): TenantScopeService
{
return self::resolveDependency(self::$authScopeGatewayResolver, AuthScopeGateway::class, 'ApiAuth');
return self::resolveDependency(self::$authScopeGatewayResolver, TenantScopeService::class, 'ApiAuth');
}
private static function apiTokenService(): ApiTokenService

View File

@@ -8,7 +8,6 @@ use MintyPHP\Service\Audit\AuditServicesFactory;
class AccessGatewayFactory
{
private ?PermissionService $permissionService = null;
private ?PermissionGateway $permissionGateway = null;
public function __construct(
private readonly AccessRepositoryFactory $accessRepositoryFactory,
@@ -28,8 +27,4 @@ class AccessGatewayFactory
);
}
public function createPermissionGateway(): PermissionGateway
{
return $this->permissionGateway ??= new PermissionGateway($this->createPermissionService());
}
}

View File

@@ -2,7 +2,7 @@
namespace MintyPHP\Service\Access;
use MintyPHP\Service\Directory\DirectoryScopeGateway;
use MintyPHP\Service\Tenant\TenantScopeService;
class AccessPolicyFactory
{
@@ -16,52 +16,52 @@ class AccessPolicyFactory
private ?AuthorizationService $authorizationService = null;
public function __construct(
private readonly PermissionGateway $permissionGateway,
private readonly DirectoryScopeGateway $directoryScopeGateway
private readonly PermissionService $permissionService,
private readonly TenantScopeService $tenantScopeService
) {
}
public function createUserAuthorizationPolicy(): UserAuthorizationPolicy
{
return $this->userAuthorizationPolicy ??= new UserAuthorizationPolicy(
$this->permissionGateway,
$this->directoryScopeGateway
$this->permissionService,
$this->tenantScopeService
);
}
public function createRoleAuthorizationPolicy(): RoleAuthorizationPolicy
{
return $this->roleAuthorizationPolicy ??= new RoleAuthorizationPolicy($this->permissionGateway);
return $this->roleAuthorizationPolicy ??= new RoleAuthorizationPolicy($this->permissionService);
}
public function createPermissionAuthorizationPolicy(): PermissionAuthorizationPolicy
{
return $this->permissionAuthorizationPolicy ??= new PermissionAuthorizationPolicy($this->permissionGateway);
return $this->permissionAuthorizationPolicy ??= new PermissionAuthorizationPolicy($this->permissionService);
}
public function createOperationsAuthorizationPolicy(): OperationsAuthorizationPolicy
{
return $this->operationsAuthorizationPolicy ??= new OperationsAuthorizationPolicy($this->permissionGateway);
return $this->operationsAuthorizationPolicy ??= new OperationsAuthorizationPolicy($this->permissionService);
}
public function createSettingsAuthorizationPolicy(): SettingsAuthorizationPolicy
{
return $this->settingsAuthorizationPolicy ??= new SettingsAuthorizationPolicy($this->permissionGateway);
return $this->settingsAuthorizationPolicy ??= new SettingsAuthorizationPolicy($this->permissionService);
}
public function createTenantAuthorizationPolicy(): TenantAuthorizationPolicy
{
return $this->tenantAuthorizationPolicy ??= new TenantAuthorizationPolicy(
$this->permissionGateway,
$this->directoryScopeGateway
$this->permissionService,
$this->tenantScopeService
);
}
public function createDepartmentAuthorizationPolicy(): DepartmentAuthorizationPolicy
{
return $this->departmentAuthorizationPolicy ??= new DepartmentAuthorizationPolicy(
$this->permissionGateway,
$this->directoryScopeGateway
$this->permissionService,
$this->tenantScopeService
);
}

View File

@@ -39,11 +39,6 @@ class AccessServicesFactory
return $this->accessGatewayFactory->createPermissionService();
}
public function createPermissionGateway(): PermissionGateway
{
return $this->accessGatewayFactory->createPermissionGateway();
}
public function createUserAuthorizationPolicy(): UserAuthorizationPolicy
{
return $this->accessPolicyFactory()->createUserAuthorizationPolicy();

View File

@@ -2,7 +2,7 @@
namespace MintyPHP\Service\Access;
use MintyPHP\Service\Directory\DirectoryScopeGateway;
use MintyPHP\Service\Tenant\TenantScopeService;
class DepartmentAuthorizationPolicy implements AuthorizationPolicyInterface
{
@@ -13,8 +13,8 @@ class DepartmentAuthorizationPolicy implements AuthorizationPolicyInterface
public const ABILITY_ADMIN_DEPARTMENTS_DELETE = 'admin.departments.delete';
public function __construct(
private readonly PermissionGateway $permissionGateway,
private readonly DirectoryScopeGateway $scopeGateway
private readonly PermissionService $permissionService,
private readonly TenantScopeService $scopeGateway
) {
}
@@ -150,7 +150,7 @@ class DepartmentAuthorizationPolicy implements AuthorizationPolicyInterface
private function hasPermission(int $userId, string $permissionKey): bool
{
return $userId > 0 && $this->permissionGateway->userHas($userId, $permissionKey);
return $userId > 0 && $this->permissionService->userHas($userId, $permissionKey);
}
private function capabilitiesFromDecision(AuthorizationDecision $decision): array

View File

@@ -42,7 +42,7 @@ final class OperationsAuthorizationPolicy implements AuthorizationPolicyInterfac
public const ABILITY_API_TOKENS_SELF_MANAGE = 'ops.api.tokens.self_manage';
public function __construct(
private readonly PermissionGateway $permissionGateway
private readonly PermissionService $permissionService
) {
}
@@ -138,10 +138,10 @@ final class OperationsAuthorizationPolicy implements AuthorizationPolicyInterfac
private function authorizeUsersCreateCustomFields(int $actorUserId): AuthorizationDecision
{
if (!$this->permissionGateway->userHas($actorUserId, PermissionService::CUSTOM_FIELDS_EDIT_VALUES)) {
if (!$this->permissionService->userHas($actorUserId, PermissionService::CUSTOM_FIELDS_EDIT_VALUES)) {
return AuthorizationDecision::deny(403, 'forbidden');
}
if (!$this->permissionGateway->userHas($actorUserId, PermissionService::USERS_UPDATE_ASSIGNMENTS)) {
if (!$this->permissionService->userHas($actorUserId, PermissionService::USERS_UPDATE_ASSIGNMENTS)) {
return AuthorizationDecision::deny(403, 'forbidden');
}
return AuthorizationDecision::allow();
@@ -149,10 +149,10 @@ final class OperationsAuthorizationPolicy implements AuthorizationPolicyInterfac
private function authorizeApiTokensSelfManage(int $actorUserId): AuthorizationDecision
{
if ($this->permissionGateway->userHas($actorUserId, PermissionService::USERS_SELF_UPDATE)) {
if ($this->permissionService->userHas($actorUserId, PermissionService::USERS_SELF_UPDATE)) {
return AuthorizationDecision::allow();
}
if ($this->permissionGateway->userHas($actorUserId, PermissionService::API_TOKENS_MANAGE)) {
if ($this->permissionService->userHas($actorUserId, PermissionService::API_TOKENS_MANAGE)) {
return AuthorizationDecision::allow();
}
return AuthorizationDecision::deny(403, 'forbidden');
@@ -160,7 +160,7 @@ final class OperationsAuthorizationPolicy implements AuthorizationPolicyInterfac
private function allowIfHas(int $actorUserId, string $permissionKey): AuthorizationDecision
{
if (!$this->permissionGateway->userHas($actorUserId, $permissionKey)) {
if (!$this->permissionService->userHas($actorUserId, $permissionKey)) {
return AuthorizationDecision::deny(403, 'forbidden');
}
return AuthorizationDecision::allow();

View File

@@ -11,7 +11,7 @@ class PermissionAuthorizationPolicy implements AuthorizationPolicyInterface
public const ABILITY_ADMIN_PERMISSIONS_DELETE = 'admin.permissions.delete';
public function __construct(
private readonly PermissionGateway $permissionGateway
private readonly PermissionService $permissionService
) {
}
@@ -118,7 +118,7 @@ class PermissionAuthorizationPolicy implements AuthorizationPolicyInterface
private function hasPermission(int $userId, string $permissionKey): bool
{
return $userId > 0 && $this->permissionGateway->userHas($userId, $permissionKey);
return $userId > 0 && $this->permissionService->userHas($userId, $permissionKey);
}
private function capabilitiesFromDecision(AuthorizationDecision $decision): array

View File

@@ -1,55 +0,0 @@
<?php
namespace MintyPHP\Service\Access;
class PermissionGateway
{
public function __construct(private readonly PermissionService $permissionService)
{
}
public function userHas(int $userId, string $permissionKey): bool
{
return $this->permissionService->userHas($userId, $permissionKey);
}
public function getUserPermissions(int $userId, bool $refresh = false): array
{
return $this->permissionService->getUserPermissions($userId, $refresh);
}
public function getCachedPermissions(int $userId): array
{
return $this->permissionService->getCachedPermissions($userId);
}
public function clearUserCache(int $userId): void
{
$this->permissionService->clearUserCache($userId);
}
public function listPaged(array $options): array
{
return $this->permissionService->listPaged($options);
}
public function find(int $id): ?array
{
return $this->permissionService->find($id);
}
public function createFromAdmin(array $input): array
{
return $this->permissionService->createFromAdmin($input);
}
public function updateFromAdmin(int $id, array $input): array
{
return $this->permissionService->updateFromAdmin($id, $input);
}
public function deleteById(int $id): array
{
return $this->permissionService->deleteById($id);
}
}

View File

@@ -11,7 +11,7 @@ class RoleAuthorizationPolicy implements AuthorizationPolicyInterface
public const ABILITY_ADMIN_ROLES_DELETE = 'admin.roles.delete';
public function __construct(
private readonly PermissionGateway $permissionGateway
private readonly PermissionService $permissionService
) {
}
@@ -116,7 +116,7 @@ class RoleAuthorizationPolicy implements AuthorizationPolicyInterface
private function hasPermission(int $userId, string $permissionKey): bool
{
return $userId > 0 && $this->permissionGateway->userHas($userId, $permissionKey);
return $userId > 0 && $this->permissionService->userHas($userId, $permissionKey);
}
private function capabilitiesFromDecision(AuthorizationDecision $decision): array

View File

@@ -11,7 +11,7 @@ class SettingsAuthorizationPolicy implements AuthorizationPolicyInterface
public const ABILITY_ADMIN_SETTINGS_USER_LIFECYCLE_RUN = 'admin.settings.user_lifecycle.run';
public function __construct(
private readonly PermissionGateway $permissionGateway
private readonly PermissionService $permissionService
) {
}
@@ -70,6 +70,6 @@ class SettingsAuthorizationPolicy implements AuthorizationPolicyInterface
private function hasPermission(int $userId, string $permissionKey): bool
{
return $userId > 0 && $this->permissionGateway->userHas($userId, $permissionKey);
return $userId > 0 && $this->permissionService->userHas($userId, $permissionKey);
}
}

View File

@@ -2,7 +2,7 @@
namespace MintyPHP\Service\Access;
use MintyPHP\Service\Directory\DirectoryScopeGateway;
use MintyPHP\Service\Tenant\TenantScopeService;
class TenantAuthorizationPolicy implements AuthorizationPolicyInterface
{
@@ -16,8 +16,8 @@ class TenantAuthorizationPolicy implements AuthorizationPolicyInterface
public const ABILITY_ADMIN_TENANTS_MEDIA_UPDATE = 'admin.tenants.media.update';
public function __construct(
private readonly PermissionGateway $permissionGateway,
private readonly DirectoryScopeGateway $scopeGateway
private readonly PermissionService $permissionService,
private readonly TenantScopeService $scopeGateway
) {
}
@@ -223,7 +223,7 @@ class TenantAuthorizationPolicy implements AuthorizationPolicyInterface
private function hasPermission(int $userId, string $permissionKey): bool
{
return $userId > 0 && $this->permissionGateway->userHas($userId, $permissionKey);
return $userId > 0 && $this->permissionService->userHas($userId, $permissionKey);
}
private function capabilitiesFromDecision(AuthorizationDecision $decision): array

View File

@@ -2,7 +2,7 @@
namespace MintyPHP\Service\Access;
use MintyPHP\Service\Directory\DirectoryScopeGateway;
use MintyPHP\Service\Tenant\TenantScopeService;
class UserAuthorizationPolicy implements AuthorizationPolicyInterface
{
@@ -29,8 +29,8 @@ class UserAuthorizationPolicy implements AuthorizationPolicyInterface
public const ABILITY_API_USERS_SHOW_DELETE = 'api.users.show.delete';
public function __construct(
private readonly PermissionGateway $permissionGateway,
private readonly DirectoryScopeGateway $scopeGateway
private readonly PermissionService $permissionService,
private readonly TenantScopeService $scopeGateway
) {
}
@@ -357,7 +357,7 @@ class UserAuthorizationPolicy implements AuthorizationPolicyInterface
private function hasPermission(int $userId, string $permissionKey): bool
{
return $userId > 0 && $this->permissionGateway->userHas($userId, $permissionKey);
return $userId > 0 && $this->permissionService->userHas($userId, $permissionKey);
}
private function canEditUserForTarget(int $actorUserId, int $targetUserId): bool

View File

@@ -1,17 +0,0 @@
<?php
namespace MintyPHP\Service\AddressBook;
use MintyPHP\Service\User\UserAvatarService;
class AddressBookAvatarGateway
{
public function __construct(private readonly UserAvatarService $userAvatarService)
{
}
public function hasAvatar(string $userUuid): bool
{
return $this->userAvatarService->hasAvatar($userUuid);
}
}

View File

@@ -1,25 +0,0 @@
<?php
namespace MintyPHP\Service\AddressBook;
use MintyPHP\Repository\CustomField\TenantCustomFieldOptionRepository;
use MintyPHP\Service\CustomField\UserCustomFieldValueService;
class AddressBookCustomFieldGateway
{
public function __construct(
private readonly UserCustomFieldValueService $userCustomFieldValueService
) {
}
public function extractFilterSpec(array $query, int $currentUserId): array
{
return $this->userCustomFieldValueService->extractAddressBookFilterSpec($query, $currentUserId);
}
public function listOptionsByDefinitionIds(array $definitionIds, bool $activeOnly = true): array
{
return TenantCustomFieldOptionRepository::listByDefinitionIds($definitionIds, $activeOnly);
}
}

View File

@@ -1,74 +0,0 @@
<?php
namespace MintyPHP\Service\AddressBook;
use MintyPHP\Service\Access\RoleService;
use MintyPHP\Service\Directory\DirectoryScopeGateway;
use MintyPHP\Service\Org\DepartmentService;
use MintyPHP\Service\Tenant\TenantService;
class AddressBookDirectoryGateway
{
public function __construct(
private readonly TenantService $tenantService,
private readonly DepartmentService $departmentService,
private readonly RoleService $roleService,
private readonly DirectoryScopeGateway $scopeGateway
) {
}
public function listTenants(): array
{
return $this->tenantService()->list();
}
public function listDepartmentsByTenantIds(array $tenantIds): array
{
return $this->departmentService()->listByTenantIds($tenantIds);
}
public function listDepartments(): array
{
return $this->departmentService()->list();
}
public function listActiveRoles(): array
{
return $this->roleService()->listActive();
}
public function getUserTenantIds(int $userId): array
{
return $this->scopeGateway()->getUserTenantIds($userId);
}
public function isStrictScope(): bool
{
return $this->scopeGateway()->isStrict();
}
public function canAccessUser(int $viewerUserId, int $targetUserId): bool
{
return $this->scopeGateway()->canAccess('users', $targetUserId, $viewerUserId);
}
private function tenantService(): TenantService
{
return $this->tenantService;
}
private function departmentService(): DepartmentService
{
return $this->departmentService;
}
private function roleService(): RoleService
{
return $this->roleService;
}
private function scopeGateway(): DirectoryScopeGateway
{
return $this->scopeGateway;
}
}

View File

@@ -2,17 +2,27 @@
namespace MintyPHP\Service\AddressBook;
use MintyPHP\Repository\CustomField\TenantCustomFieldOptionRepository;
use MintyPHP\Service\Access\RoleService;
use MintyPHP\Service\CustomField\UserCustomFieldValueService;
use MintyPHP\Service\Org\DepartmentService;
use MintyPHP\Service\Tenant\TenantScopeService;
use MintyPHP\Service\Tenant\TenantService;
use MintyPHP\Service\User\UserAccountService;
use MintyPHP\Service\User\UserAssignmentService;
use MintyPHP\Service\User\UserAvatarService;
class AddressBookService
{
public function __construct(
private readonly UserAccountService $userAccountService,
private readonly UserAssignmentService $userAssignmentService,
private readonly AddressBookDirectoryGateway $directoryGateway,
private readonly AddressBookCustomFieldGateway $customFieldGateway,
private readonly AddressBookAvatarGateway $avatarGateway
private readonly TenantService $tenantService,
private readonly DepartmentService $departmentService,
private readonly RoleService $roleService,
private readonly TenantScopeService $scopeGateway,
private readonly UserCustomFieldValueService $customFieldValueService,
private readonly UserAvatarService $avatarService
) {
}
@@ -22,8 +32,8 @@ class AddressBookService
$activeRoles = $this->splitCommaValues($query['roles'] ?? '');
$activeDepartments = $this->splitCommaValues($query['departments'] ?? '');
$tenantIds = $this->normalizeIds($this->directoryGateway->getUserTenantIds($currentUserId));
$tenants = $this->directoryGateway->listTenants();
$tenantIds = $this->normalizeIds($this->scopeGateway->getUserTenantIds($currentUserId));
$tenants = $this->tenantService->list();
if ($tenantIds) {
$allowedTenantMap = array_fill_keys($tenantIds, true);
$tenants = array_values(array_filter(
@@ -33,7 +43,7 @@ class AddressBookService
return $tenantId > 0 && isset($allowedTenantMap[$tenantId]);
}
));
} elseif ($this->directoryGateway->isStrictScope()) {
} elseif ($this->scopeGateway->isStrict()) {
$tenants = [];
}
@@ -46,12 +56,12 @@ class AddressBookService
);
$departments = $tenantIds
? $this->directoryGateway->listDepartmentsByTenantIds($tenantIds)
: ($this->directoryGateway->isStrictScope() ? [] : $this->directoryGateway->listDepartments());
? $this->departmentService->listByTenantIds($tenantIds)
: ($this->scopeGateway->isStrict() ? [] : $this->departmentService->list());
$roles = $this->directoryGateway->listActiveRoles();
$roles = $this->roleService->listActive();
$customFieldFilterSpec = $this->customFieldGateway->extractFilterSpec($query, $currentUserId);
$customFieldFilterSpec = $this->customFieldValueService->extractAddressBookFilterSpec($query, $currentUserId);
$customFieldFilterDefinitions = $this->normalizeDefinitions(
$customFieldFilterSpec['definitions'] ?? []
);
@@ -68,7 +78,7 @@ class AddressBookService
}
$customFieldDefinitionIds = array_values(array_unique($customFieldDefinitionIds));
$customFieldOptions = $this->customFieldGateway->listOptionsByDefinitionIds(
$customFieldOptions = TenantCustomFieldOptionRepository::listByDefinitionIds(
$customFieldDefinitionIds,
true
);
@@ -151,7 +161,7 @@ class AddressBookService
$tenants = $query['tenants'] ?? '';
$departments = $query['departments'] ?? '';
$roles = $query['roles'] ?? '';
$customFieldFilterSpec = $this->customFieldGateway->extractFilterSpec($query, $currentUserId);
$customFieldFilterSpec = $this->customFieldValueService->extractAddressBookFilterSpec($query, $currentUserId);
$result = $this->userAccountService->listPaged([
'limit' => $limit,
@@ -195,7 +205,7 @@ class AddressBookService
'tenants' => $tenantList,
'departments' => $departmentList,
'roles' => $roleList,
'has_avatar' => $uuid !== '' && $this->avatarGateway->hasAvatar($uuid),
'has_avatar' => $uuid !== '' && $this->avatarService->hasAvatar($uuid),
];
}
@@ -225,12 +235,12 @@ class AddressBookService
// Users can always view their own profile; others require scope access.
if (
$currentUserId !== $targetUserId
&& !$this->directoryGateway->canAccessUser($currentUserId, $targetUserId)
&& !$this->scopeGateway->canAccess('users', $targetUserId, $currentUserId)
) {
return ['status' => 'forbidden'];
}
$viewerTenantIds = $this->normalizeIds($this->directoryGateway->getUserTenantIds($currentUserId));
$viewerTenantIds = $this->normalizeIds($this->scopeGateway->getUserTenantIds($currentUserId));
$assignments = $this->userAssignmentService->buildAssignmentsForUser($targetUserId);
$departmentMap = [];
@@ -294,7 +304,7 @@ class AddressBookService
$roleLabels = array_values($roleLabels);
$avatarUuid = (string) ($user['uuid'] ?? '');
$user['has_avatar'] = $avatarUuid !== '' && $this->avatarGateway->hasAvatar($avatarUuid);
$user['has_avatar'] = $avatarUuid !== '' && $this->avatarService->hasAvatar($avatarUuid);
$user['tenant_groups'] = $tenantGroups;
$user['role_labels'] = $roleLabels;

View File

@@ -4,19 +4,18 @@ namespace MintyPHP\Service\AddressBook;
use MintyPHP\Service\CustomField\CustomFieldServicesFactory;
use MintyPHP\Service\Directory\DirectoryServicesFactory;
use MintyPHP\Service\Tenant\TenantScopeService;
use MintyPHP\Service\User\UserServicesFactory;
class AddressBookServicesFactory
{
private ?AddressBookDirectoryGateway $addressBookDirectoryGateway = null;
private ?AddressBookCustomFieldGateway $addressBookCustomFieldGateway = null;
private ?AddressBookAvatarGateway $addressBookAvatarGateway = null;
private ?AddressBookService $addressBookService = null;
public function __construct(
private readonly UserServicesFactory $userServicesFactory,
private readonly DirectoryServicesFactory $directoryServicesFactory,
private readonly CustomFieldServicesFactory $customFieldServicesFactory
private readonly CustomFieldServicesFactory $customFieldServicesFactory,
private readonly TenantScopeService $tenantScopeService
) {
}
@@ -27,41 +26,16 @@ class AddressBookServicesFactory
}
$userFactory = $this->userServicesFactory;
$directoryFactory = $this->directoryServicesFactory;
return $this->addressBookService = new AddressBookService(
$userFactory->createUserAccountService(),
$userFactory->createUserAssignmentService(),
$this->createAddressBookDirectoryGateway(),
$this->createAddressBookCustomFieldGateway(),
$this->createAddressBookAvatarGateway()
);
}
public function createAddressBookDirectoryGateway(): AddressBookDirectoryGateway
{
if ($this->addressBookDirectoryGateway !== null) {
return $this->addressBookDirectoryGateway;
}
$directoryFactory = $this->directoryServicesFactory;
return $this->addressBookDirectoryGateway = new AddressBookDirectoryGateway(
$directoryFactory->createTenantService(),
$directoryFactory->createDepartmentService(),
$directoryFactory->createRoleService(),
$directoryFactory->createDirectoryScopeGateway()
);
}
public function createAddressBookCustomFieldGateway(): AddressBookCustomFieldGateway
{
return $this->addressBookCustomFieldGateway ??= new AddressBookCustomFieldGateway(
$this->customFieldServicesFactory->createUserCustomFieldValueService()
);
}
public function createAddressBookAvatarGateway(): AddressBookAvatarGateway
{
return $this->addressBookAvatarGateway ??= new AddressBookAvatarGateway(
$this->userServicesFactory->createUserAvatarService()
$this->tenantScopeService,
$this->customFieldServicesFactory->createUserCustomFieldValueService(),
$userFactory->createUserAvatarService()
);
}
}

View File

@@ -4,13 +4,14 @@ namespace MintyPHP\Service\Auth;
use MintyPHP\Repository\Auth\ApiTokenRepository;
use MintyPHP\Repository\Tenant\TenantRepository;
use MintyPHP\Service\Tenant\TenantScopeService;
class ApiTokenEndpointService
{
public function __construct(
private readonly ApiTokenRepository $apiTokenRepository,
private readonly TenantRepository $tenantRepository,
private readonly AuthScopeGateway $authScopeGateway
private readonly TenantScopeService $authScopeGateway
) {
}

View File

@@ -6,6 +6,7 @@ use MintyPHP\Http\RequestRuntimeInterface;
use MintyPHP\Repository\Auth\ApiTokenRepositoryInterface;
use MintyPHP\Repository\Support\DatabaseSessionRepository;
use MintyPHP\Repository\User\UserReadRepositoryInterface;
use MintyPHP\Service\Tenant\TenantScopeService;
class ApiTokenService
{
@@ -15,7 +16,7 @@ class ApiTokenService
private readonly UserReadRepositoryInterface $userReadRepository,
private readonly ApiTokenRepositoryInterface $apiTokenRepository,
private readonly AuthSettingsGateway $settingsGateway,
private readonly AuthScopeGateway $scopeGateway,
private readonly TenantScopeService $scopeGateway,
private readonly DatabaseSessionRepository $databaseSessionRepository,
private readonly RequestRuntimeInterface $requestRuntime
) {

View File

@@ -1,22 +0,0 @@
<?php
namespace MintyPHP\Service\Auth;
use MintyPHP\Service\User\UserAvatarService;
class AuthAvatarGateway
{
public function __construct(private readonly UserAvatarService $userAvatarService)
{
}
public function isValidUuid(string $uuid): bool
{
return $this->userAvatarService->isValidUuid($uuid);
}
public function saveBinary(string $uuid, string $binary, string $mime): void
{
$this->userAvatarService->saveBinary($uuid, $binary, $mime);
}
}

View File

@@ -3,7 +3,7 @@
namespace MintyPHP\Service\Auth;
use MintyPHP\Service\Access\AccessServicesFactory;
use MintyPHP\Service\Access\PermissionGateway;
use MintyPHP\Service\Access\PermissionService;
use MintyPHP\Service\Settings\SettingsApiPolicyGateway;
use MintyPHP\Service\Settings\SettingsAppGateway;
use MintyPHP\Service\Settings\SettingsDefaultsGateway;
@@ -11,7 +11,6 @@ use MintyPHP\Service\Settings\SettingServicesFactory;
use MintyPHP\Service\Settings\SettingsLoginPersistenceGateway;
use MintyPHP\Service\Settings\SettingsMicrosoftGateway;
use MintyPHP\Service\Tenant\TenantScopeService;
use MintyPHP\Service\User\UserServicesFactory;
class AuthGatewayFactory
{
@@ -20,21 +19,15 @@ class AuthGatewayFactory
private ?SettingsDefaultsGateway $settingsDefaultsGateway = null;
private ?SettingsAppGateway $settingsAppGateway = null;
private ?SettingsLoginPersistenceGateway $settingsLoginPersistenceGateway = null;
private ?PermissionGateway $permissionGateway = null;
private ?PermissionService $permissionService = null;
private ?AuthSettingsGateway $authSettingsGateway = null;
private ?AuthScopeGateway $authScopeGateway = null;
private ?AuthPermissionGateway $authPermissionGateway = null;
private ?AuthTenantGateway $authTenantGateway = null;
private ?AuthCryptoGateway $authCryptoGateway = null;
private ?AuthTenantSsoGateway $authTenantSsoGateway = null;
private ?AuthSavedFilterGateway $authSavedFilterGateway = null;
private ?AuthAvatarGateway $authAvatarGateway = null;
private ?AuthExternalIdentityGateway $authExternalIdentityGateway = null;
public function __construct(
private readonly AuthRepositoryFactory $authRepositoryFactory,
private readonly AccessServicesFactory $accessServicesFactory,
private readonly UserServicesFactory $userServicesFactory,
private readonly SettingServicesFactory $settingServicesFactory,
private readonly TenantScopeService $tenantScopeService
) {
@@ -51,14 +44,14 @@ class AuthGatewayFactory
);
}
public function createAuthScopeGateway(): AuthScopeGateway
public function getTenantScopeService(): TenantScopeService
{
return $this->authScopeGateway ??= new AuthScopeGateway($this->tenantScopeService);
return $this->tenantScopeService;
}
public function createAuthPermissionGateway(): AuthPermissionGateway
public function createPermissionService(): PermissionService
{
return $this->authPermissionGateway ??= new AuthPermissionGateway($this->createPermissionGateway());
return $this->permissionService ??= $this->accessServicesFactory->createPermissionService();
}
public function createAuthTenantGateway(): AuthTenantGateway
@@ -73,25 +66,6 @@ class AuthGatewayFactory
return $this->authCryptoGateway ??= new AuthCryptoGateway();
}
public function createAuthTenantSsoGateway(TenantSsoService $tenantSsoService): AuthTenantSsoGateway
{
return $this->authTenantSsoGateway ??= new AuthTenantSsoGateway($tenantSsoService);
}
public function createAuthSavedFilterGateway(): AuthSavedFilterGateway
{
return $this->authSavedFilterGateway ??= new AuthSavedFilterGateway(
$this->userServicesFactory->createUserSavedFilterService()
);
}
public function createAuthAvatarGateway(): AuthAvatarGateway
{
return $this->authAvatarGateway ??= new AuthAvatarGateway(
$this->userServicesFactory->createUserAvatarService()
);
}
public function createAuthExternalIdentityGateway(): AuthExternalIdentityGateway
{
return $this->authExternalIdentityGateway ??= new AuthExternalIdentityGateway(
@@ -99,11 +73,6 @@ class AuthGatewayFactory
);
}
private function createPermissionGateway(): PermissionGateway
{
return $this->permissionGateway ??= $this->accessServicesFactory->createPermissionGateway();
}
private function createSettingsMicrosoftGateway(): SettingsMicrosoftGateway
{
return $this->settingsMicrosoftGateway ??= $this->settingServicesFactory->createSettingsMicrosoftGateway();

View File

@@ -1,22 +0,0 @@
<?php
namespace MintyPHP\Service\Auth;
use MintyPHP\Service\Access\PermissionGateway;
class AuthPermissionGateway
{
public function __construct(private readonly PermissionGateway $permissionGateway)
{
}
public function warmUserPermissions(int $userId, bool $forceRefresh = true): array
{
return $this->permissionGateway->getUserPermissions($userId, $forceRefresh);
}
public function userHas(int $userId, string $permissionKey): bool
{
return $this->permissionGateway->userHas($userId, $permissionKey);
}
}

View File

@@ -1,17 +0,0 @@
<?php
namespace MintyPHP\Service\Auth;
use MintyPHP\Service\User\UserSavedFilterService;
class AuthSavedFilterGateway
{
public function __construct(private readonly UserSavedFilterService $userSavedFilterService)
{
}
public function listAddressBookFilters(int $userId): array
{
return $this->userSavedFilterService->listForAddressBook($userId);
}
}

View File

@@ -1,33 +0,0 @@
<?php
namespace MintyPHP\Service\Auth;
use MintyPHP\Service\Tenant\TenantScopeService;
class AuthScopeGateway
{
public function __construct(
private readonly TenantScopeService $tenantScopeService
) {
}
public function hasGlobalAccess(int $userId): bool
{
return $this->tenantScopeService->hasGlobalAccess($userId);
}
public function getUserTenantIds(int $userId): array
{
return $this->tenantScopeService->getUserTenantIds($userId);
}
public function canAccess(string $resourceType, int $resourceId, int $userId): bool
{
return $this->tenantScopeService->canAccess($resourceType, $resourceId, $userId);
}
public function resourceBelongsToTenant(string $resourceType, int $resourceId, int $tenantId): bool
{
return $this->tenantScopeService->resourceBelongsToTenant($resourceType, $resourceId, $tenantId);
}
}

View File

@@ -7,6 +7,7 @@ use MintyPHP\Http\SessionStoreInterface;
use MintyPHP\Repository\User\UserReadRepositoryInterface;
use MintyPHP\Repository\User\UserWriteRepositoryInterface;
use MintyPHP\Router;
use MintyPHP\Service\Access\PermissionService;
use MintyPHP\Service\Audit\SystemAuditService;
use MintyPHP\Service\User\UserAccountService;
use MintyPHP\Service\User\UserTenantContextService;
@@ -22,8 +23,8 @@ class AuthService
private readonly UserTenantContextService $userTenantContextService,
private readonly RememberMeService $rememberMeService,
private readonly EmailVerificationService $emailVerificationService,
private readonly AuthPermissionGateway $permissionGateway,
private readonly AuthTenantSsoGateway $tenantSsoGateway,
private readonly PermissionService $permissionService,
private readonly TenantSsoService $tenantSsoService,
private readonly AuthSessionTenantContextService $authSessionTenantContextService,
private readonly SystemAuditService $systemAuditService,
private readonly SessionStoreInterface $sessionStore
@@ -92,7 +93,7 @@ class AuthService
];
}
if ($userId > 0) {
$this->permissionGateway->warmUserPermissions($userId, true);
$this->permissionService->getUserPermissions($userId, true);
$this->loadTenantDataIntoSession($userId);
if ($this->noActiveTenant()) {
Auth::logout();
@@ -135,7 +136,7 @@ class AuthService
if ($tenantId <= 0) {
continue;
}
if ($this->tenantSsoGateway->isLocalPasswordLoginAllowed($tenantId)) {
if ($this->tenantSsoService->isLocalPasswordLoginAllowed($tenantId)) {
return true;
}
}
@@ -184,7 +185,7 @@ class AuthService
}
}
$this->permissionGateway->warmUserPermissions($userId, true);
$this->permissionService->getUserPermissions($userId, true);
$this->loadTenantDataIntoSession($userId);
if ($this->noActiveTenant()) {
Auth::logout();
@@ -336,7 +337,7 @@ class AuthService
}
$this->sessionStore->set('user', $user);
$this->permissionGateway->warmUserPermissions($userId, true);
$this->permissionService->getUserPermissions($userId, true);
$this->loadTenantDataIntoSession($userId);
$refreshed = true;
}

View File

@@ -42,7 +42,7 @@ class AuthServicesFactory
$this->userServicesFactory->createUserReadRepository(),
$this->authRepositoryFactory->createApiTokenRepository(),
$this->authGatewayFactory->createAuthSettingsGateway(),
$this->createAuthScopeGateway(),
$this->authGatewayFactory->getTenantScopeService(),
$this->databaseSessionRepository,
$this->requestRuntime
);
@@ -75,7 +75,7 @@ class AuthServicesFactory
$this->userServicesFactory->createUserReadRepository(),
$this->userServicesFactory->createUserWriteRepository(),
$this->authRepositoryFactory->createRememberTokenRepository(),
$this->authGatewayFactory->createAuthPermissionGateway(),
$this->authGatewayFactory->createPermissionService(),
$this->createAuthSessionTenantContextService(),
$this->sessionStore,
$this->cookieStore,
@@ -93,8 +93,8 @@ class AuthServicesFactory
$this->userServicesFactory->createUserTenantContextService(),
$this->createRememberMeService(),
$this->createEmailVerificationService(),
$this->authGatewayFactory->createAuthPermissionGateway(),
$this->createAuthTenantSsoGateway(),
$this->authGatewayFactory->createPermissionService(),
$this->createTenantSsoService(),
$this->createAuthSessionTenantContextService(),
$this->auditServicesFactory->createSystemAuditService(),
$this->sessionStore
@@ -109,8 +109,8 @@ class AuthServicesFactory
$this->userServicesFactory->createUserAssignmentService(),
$this->userServicesFactory->createUserTenantRepository(),
$this->authGatewayFactory->createAuthSettingsGateway(),
$this->createAuthTenantSsoGateway(),
$this->authGatewayFactory->createAuthAvatarGateway(),
$this->createTenantSsoService(),
$this->userServicesFactory->createUserAvatarService(),
$this->authGatewayFactory->createAuthExternalIdentityGateway()
);
}
@@ -140,16 +140,6 @@ class AuthServicesFactory
);
}
public function createAuthScopeGateway(): AuthScopeGateway
{
return $this->authGatewayFactory->createAuthScopeGateway();
}
private function createAuthTenantSsoGateway(): AuthTenantSsoGateway
{
return $this->authGatewayFactory->createAuthTenantSsoGateway($this->createTenantSsoService());
}
private function createOidcHttpGateway(): OidcHttpGateway
{
return new OidcHttpGateway();
@@ -159,7 +149,7 @@ class AuthServicesFactory
{
return $this->authSessionTenantContextService ??= new AuthSessionTenantContextService(
$this->userServicesFactory->createUserTenantContextService(),
$this->authGatewayFactory->createAuthSavedFilterGateway(),
$this->userServicesFactory->createUserSavedFilterService(),
$this->sessionStore
);
}

View File

@@ -3,13 +3,14 @@
namespace MintyPHP\Service\Auth;
use MintyPHP\Http\SessionStoreInterface;
use MintyPHP\Service\User\UserSavedFilterService;
use MintyPHP\Service\User\UserTenantContextService;
class AuthSessionTenantContextService
{
public function __construct(
private readonly UserTenantContextService $userTenantContextService,
private readonly AuthSavedFilterGateway $savedFilterGateway,
private readonly UserSavedFilterService $savedFilterService,
private readonly SessionStoreInterface $sessionStore
) {
}
@@ -23,7 +24,7 @@ class AuthSessionTenantContextService
$availableTenants = $this->userTenantContextService->getAvailableTenants($userId);
$this->sessionStore->set('available_tenants', $availableTenants);
$this->sessionStore->set('available_departments_by_tenant', $this->userTenantContextService->getAvailableDepartmentsByTenant($userId));
$this->sessionStore->set('address_book_saved_filters', $this->savedFilterGateway->listAddressBookFilters($userId));
$this->sessionStore->set('address_book_saved_filters', $this->savedFilterService->listForAddressBook($userId));
if (!$availableTenants) {
$this->sessionStore->set('no_active_tenant', true);

View File

@@ -1,30 +0,0 @@
<?php
namespace MintyPHP\Service\Auth;
class AuthTenantSsoGateway
{
public function __construct(private readonly TenantSsoService $tenantSsoService)
{
}
public function isLocalPasswordLoginAllowed(int $tenantId): bool
{
return $this->tenantSsoService->isLocalPasswordLoginAllowed($tenantId);
}
public function normalizeProfileSyncFields($fields): array
{
return $this->tenantSsoService->normalizeProfileSyncFields($fields);
}
public function defaultProfileSyncFields(): array
{
return $this->tenantSsoService->defaultProfileSyncFields();
}
public function microsoftProviderKey(): string
{
return $this->tenantSsoService->microsoftProviderKey();
}
}

View File

@@ -10,6 +10,7 @@ use MintyPHP\I18n;
use MintyPHP\Repository\Auth\RememberTokenRepositoryInterface;
use MintyPHP\Repository\User\UserReadRepositoryInterface;
use MintyPHP\Repository\User\UserWriteRepositoryInterface;
use MintyPHP\Service\Access\PermissionService;
use MintyPHP\Session;
class RememberMeService
@@ -21,7 +22,7 @@ class RememberMeService
private readonly UserReadRepositoryInterface $userReadRepository,
private readonly UserWriteRepositoryInterface $userWriteRepository,
private readonly RememberTokenRepositoryInterface $rememberTokenRepository,
private readonly AuthPermissionGateway $permissionGateway,
private readonly PermissionService $permissionService,
private readonly AuthSessionTenantContextService $authSessionTenantContextService,
private readonly SessionStoreInterface $sessionStore,
private readonly CookieStoreInterface $cookieStore,
@@ -101,7 +102,7 @@ class RememberMeService
}
$userId = (int) $user['id'];
if ($userId > 0) {
$this->permissionGateway->warmUserPermissions($userId, true);
$this->permissionService->getUserPermissions($userId, true);
$this->authSessionTenantContextService->hydrateForUser($userId);
if ((bool) $this->sessionStore->get('no_active_tenant', false)) {
// Enforce the same tenant policy as interactive login.

View File

@@ -6,6 +6,7 @@ use MintyPHP\Repository\Tenant\UserTenantRepositoryInterface;
use MintyPHP\Repository\User\UserReadRepositoryInterface;
use MintyPHP\Repository\User\UserWriteRepositoryInterface;
use MintyPHP\Service\User\UserAssignmentService;
use MintyPHP\Service\User\UserAvatarService;
class SsoUserLinkService
{
@@ -15,8 +16,8 @@ class SsoUserLinkService
private readonly UserAssignmentService $userAssignmentService,
private readonly UserTenantRepositoryInterface $userTenantRepository,
private readonly AuthSettingsGateway $settingsGateway,
private readonly AuthTenantSsoGateway $tenantSsoGateway,
private readonly AuthAvatarGateway $avatarGateway,
private readonly TenantSsoService $tenantSsoService,
private readonly UserAvatarService $avatarService,
private readonly AuthExternalIdentityGateway $externalIdentityGateway
) {
}
@@ -34,7 +35,7 @@ class SsoUserLinkService
return ['ok' => false, 'error' => 'identity_invalid'];
}
$providerKey = $this->tenantSsoGateway->microsoftProviderKey();
$providerKey = $this->tenantSsoService->microsoftProviderKey();
// Look up by OID first (stable Microsoft object ID), fall back to issuer+subject
// to handle tenants that migrated from an older OIDC setup without OID.
$identity = $this->externalIdentityGateway->findByProviderTidOid(
@@ -136,9 +137,9 @@ class SsoUserLinkService
return;
}
$syncFields = $this->tenantSsoGateway->normalizeProfileSyncFields($claims['sync_profile_fields'] ?? []);
$syncFields = $this->tenantSsoService->normalizeProfileSyncFields($claims['sync_profile_fields'] ?? []);
if (!$syncFields) {
$syncFields = $this->tenantSsoGateway->defaultProfileSyncFields();
$syncFields = $this->tenantSsoService->defaultProfileSyncFields();
}
if (!$syncFields) {
return;
@@ -202,12 +203,12 @@ class SsoUserLinkService
return;
}
$userUuid = (string) ($user['uuid'] ?? '');
if (!$this->avatarGateway->isValidUuid($userUuid)) {
if (!$this->avatarService->isValidUuid($userUuid)) {
return;
}
// Keep avatar current with Microsoft profile photo on each login.
$this->avatarGateway->saveBinary($userUuid, $binary, $avatarMime);
$this->avatarService->saveBinary($userUuid, $binary, $avatarMime);
}
private function ensureTenantAssignment(int $userId, int $tenantId): void
@@ -228,7 +229,7 @@ class SsoUserLinkService
string $email
): void {
try {
$providerKey = $this->tenantSsoGateway->microsoftProviderKey();
$providerKey = $this->tenantSsoService->microsoftProviderKey();
$this->externalIdentityGateway->createLink([
'user_id' => $userId,
'provider' => $providerKey,

View File

@@ -3,7 +3,7 @@
namespace MintyPHP\Service\CustomField;
use MintyPHP\Repository\Tenant\TenantRepository;
use MintyPHP\Service\User\UserScopeGateway;
use MintyPHP\Service\Tenant\TenantScopeService;
class CustomFieldServicesFactory
{
@@ -12,7 +12,7 @@ class CustomFieldServicesFactory
public function __construct(
private readonly TenantRepository $tenantRepository,
private readonly UserScopeGateway $userScopeGateway
private readonly TenantScopeService $userScopeGateway
) {
}

View File

@@ -7,12 +7,12 @@ use MintyPHP\Repository\CustomField\TenantCustomFieldOptionRepository;
use MintyPHP\Repository\CustomField\UserCustomFieldValueOptionRepository;
use MintyPHP\Repository\CustomField\UserCustomFieldValueRepository;
use MintyPHP\Repository\Support\RepoQuery;
use MintyPHP\Service\User\UserScopeGateway;
use MintyPHP\Service\Tenant\TenantScopeService;
class UserCustomFieldValueService
{
public function __construct(
private readonly UserScopeGateway $userScopeGateway
private readonly TenantScopeService $userScopeGateway
) {
}
@@ -526,7 +526,7 @@ class UserCustomFieldValueService
];
}
private function userScopeGateway(): UserScopeGateway
private function userScopeGateway(): TenantScopeService
{
return $this->userScopeGateway;
}

View File

@@ -8,8 +8,6 @@ use MintyPHP\Service\Tenant\TenantScopeService;
class DirectoryGatewayFactory
{
private ?DirectorySettingsGateway $directorySettingsGateway = null;
private ?DirectoryScopeGateway $directoryScopeGateway = null;
public function __construct(
private readonly SettingServicesFactory $settingServicesFactory,
private readonly TenantScopeService $tenantScopeService
@@ -23,8 +21,8 @@ class DirectoryGatewayFactory
);
}
public function createDirectoryScopeGateway(): DirectoryScopeGateway
public function getTenantScopeService(): TenantScopeService
{
return $this->directoryScopeGateway ??= new DirectoryScopeGateway($this->tenantScopeService);
return $this->tenantScopeService;
}
}

View File

@@ -1,58 +0,0 @@
<?php
namespace MintyPHP\Service\Directory;
use MintyPHP\Service\Tenant\TenantScopeService;
class DirectoryScopeGateway
{
public function __construct(
private readonly TenantScopeService $tenantScopeService
) {
}
public function hasGlobalAccess(int $userId): bool
{
return $this->tenantScopeService->hasGlobalAccess($userId);
}
public function getUserTenantIds(int $userId): array
{
return $this->tenantScopeService->getUserTenantIds($userId);
}
public function isStrict(): bool
{
return $this->tenantScopeService->isStrict();
}
public function canAccess(string $resourceType, int $resourceId, int $userId): bool
{
return $this->tenantScopeService->canAccess($resourceType, $resourceId, $userId);
}
public function resourceBelongsToTenant(string $resourceType, int $resourceId, int $tenantId): bool
{
return $this->tenantScopeService->resourceBelongsToTenant($resourceType, $resourceId, $tenantId);
}
public function filterTenantIdsForUser(array $tenantIds, int $userId): array
{
return $this->tenantScopeService->filterTenantIdsForUser($tenantIds, $userId);
}
// When a scoped admin updates tenant assignments, they can't see tenants outside their scope.
// This merges the requested IDs with any out-of-scope IDs from existing assignments so those
// aren't silently dropped when the admin saves.
public function mergeTenantIdsPreservingOutOfScope(
array $requestedTenantIds,
array $existingTenantIds,
array $allowedTenantIds
): array {
return $this->tenantScopeService->mergeTenantIdsPreservingOutOfScope(
$requestedTenantIds,
$existingTenantIds,
$allowedTenantIds
);
}
}

View File

@@ -8,6 +8,7 @@ use MintyPHP\Repository\Tenant\TenantRepositoryInterface;
use MintyPHP\Service\Access\RoleService;
use MintyPHP\Service\Audit\AuditServicesFactory;
use MintyPHP\Service\Org\DepartmentService;
use MintyPHP\Service\Tenant\TenantScopeService;
use MintyPHP\Service\Tenant\TenantService;
use MintyPHP\Service\User\UserServicesFactory;
@@ -41,7 +42,7 @@ class DirectoryServicesFactory
$this->userServicesFactory,
$this->createDepartmentRepository(),
$this->createDirectorySettingsGateway(),
$this->createDirectoryScopeGateway(),
$this->directoryGatewayFactory->getTenantScopeService(),
$this->auditServicesFactory->createSystemAuditService()
);
}
@@ -76,8 +77,8 @@ class DirectoryServicesFactory
return $this->directoryGatewayFactory->createDirectorySettingsGateway();
}
public function createDirectoryScopeGateway(): DirectoryScopeGateway
public function getTenantScopeService(): TenantScopeService
{
return $this->directoryGatewayFactory->createDirectoryScopeGateway();
return $this->directoryGatewayFactory->getTenantScopeService();
}
}

View File

@@ -3,12 +3,11 @@
namespace MintyPHP\Service\Import;
use MintyPHP\Http\SessionStoreInterface;
use MintyPHP\Service\Access\PermissionGateway;
use MintyPHP\Service\Access\PermissionService;
use MintyPHP\Service\Audit\ImportAuditService;
use MintyPHP\Service\Import\Profile\ImportProfileInterface;
use MintyPHP\Service\Settings\SettingsDefaultsGateway;
use MintyPHP\Service\User\UserScopeGateway;
use MintyPHP\Service\Tenant\TenantScopeService;
class ImportService
{
@@ -28,9 +27,9 @@ class ImportService
private readonly ImportAuditService $importAuditService,
private readonly ImportStateStoreService $importStateStoreService,
private readonly array $profiles,
private readonly PermissionGateway $permissionGateway,
private readonly PermissionService $permissionService,
private readonly SettingsDefaultsGateway $settingsDefaultsGateway,
private readonly UserScopeGateway $userScopeGateway,
private readonly TenantScopeService $userScopeGateway,
private readonly SessionStoreInterface $sessionStore
) {
}
@@ -490,7 +489,7 @@ class ImportService
private function canImportAssignments(int $userId): bool
{
return $this->permissionGateway->userHas($userId, PermissionService::USERS_IMPORT_ASSIGNMENTS);
return $this->permissionService->userHas($userId, PermissionService::USERS_IMPORT_ASSIGNMENTS);
}
/**

View File

@@ -5,7 +5,7 @@ namespace MintyPHP\Service\Import;
use MintyPHP\Http\SessionStoreInterface;
use MintyPHP\Repository\Audit\ImportAuditRunRepositoryInterface;
use MintyPHP\Service\Access\AccessServicesFactory;
use MintyPHP\Service\Access\PermissionGateway;
use MintyPHP\Service\Access\PermissionService;
use MintyPHP\Service\Audit\ImportAuditService;
use MintyPHP\Service\Directory\DirectoryServicesFactory;
use MintyPHP\Service\Import\Profile\DepartmentImportGateway;
@@ -14,6 +14,7 @@ use MintyPHP\Service\Import\Profile\UserImportGateway;
use MintyPHP\Service\Import\Profile\UserImportProfile;
use MintyPHP\Service\Settings\SettingsDefaultsGateway;
use MintyPHP\Service\Settings\SettingServicesFactory;
use MintyPHP\Service\Tenant\TenantScopeService;
use MintyPHP\Service\User\UserServicesFactory;
class ImportServicesFactory
@@ -22,7 +23,7 @@ class ImportServicesFactory
private ?ImportTempFileService $importTempFileService = null;
private ?ImportStateStoreService $importStateStoreService = null;
private ?ImportAuditService $importAuditService = null;
private ?PermissionGateway $permissionGateway = null;
private ?PermissionService $permissionService = null;
private ?SettingsDefaultsGateway $settingsDefaultsGateway = null;
private ?ImportService $importService = null;
@@ -32,6 +33,7 @@ class ImportServicesFactory
private readonly SettingServicesFactory $settingServicesFactory,
private readonly DirectoryServicesFactory $directoryServicesFactory,
private readonly ImportRepositoryFactory $importRepositoryFactory,
private readonly TenantScopeService $tenantScopeService,
private readonly SessionStoreInterface $sessionStore
) {
}
@@ -43,18 +45,22 @@ class ImportServicesFactory
}
$profiles = [
'users' => new UserImportProfile(new UserImportGateway(
$this->userServicesFactory->createUserReadRepository(),
$this->userServicesFactory->createUserAccountService(),
$this->directoryServicesFactory->createTenantRepository(),
$this->directoryServicesFactory->createRoleRepository(),
$this->directoryServicesFactory->createDepartmentRepository(),
)),
'departments' => new DepartmentImportProfile(new DepartmentImportGateway(
$this->directoryServicesFactory->createDepartmentRepository(),
$this->directoryServicesFactory->createTenantRepository(),
'users' => new UserImportProfile(
new UserImportGateway(
$this->userServicesFactory->createUserReadRepository(),
$this->directoryServicesFactory->createTenantRepository(),
$this->directoryServicesFactory->createRoleRepository(),
$this->directoryServicesFactory->createDepartmentRepository(),
),
$this->userServicesFactory->createUserAccountService()
),
'departments' => new DepartmentImportProfile(
new DepartmentImportGateway(
$this->directoryServicesFactory->createDepartmentRepository(),
$this->directoryServicesFactory->createTenantRepository()
),
$this->directoryServicesFactory->createDepartmentService()
)),
),
];
return $this->importService = new ImportService(
@@ -63,9 +69,9 @@ class ImportServicesFactory
$this->createImportAuditService(),
$this->createImportStateStoreService(),
$profiles,
$this->createPermissionGateway(),
$this->createPermissionService(),
$this->createSettingsDefaultsGateway(),
$this->userServicesFactory->createUserScopeGateway(),
$this->tenantScopeService,
$this->sessionStore
);
}
@@ -98,9 +104,9 @@ class ImportServicesFactory
return $this->importRepositoryFactory->createImportAuditRunRepository();
}
private function createPermissionGateway(): PermissionGateway
private function createPermissionService(): PermissionService
{
return $this->permissionGateway ??= $this->accessServicesFactory->createPermissionGateway();
return $this->permissionService ??= $this->accessServicesFactory->createPermissionService();
}
private function createSettingsDefaultsGateway(): SettingsDefaultsGateway

View File

@@ -4,58 +4,32 @@ namespace MintyPHP\Service\Import\Profile;
use MintyPHP\Repository\Org\DepartmentRepositoryInterface;
use MintyPHP\Repository\Tenant\TenantRepositoryInterface;
use MintyPHP\Service\Org\DepartmentService;
class DepartmentImportGateway
{
public function __construct(
private readonly DepartmentRepositoryInterface $departmentRepository,
private readonly TenantRepositoryInterface $tenantRepository,
private readonly DepartmentService $departmentService
private readonly TenantRepositoryInterface $tenantRepository
) {
}
public function findTenantByUuid(string $uuid): ?array
{
return $this->tenantRepository()->findByUuid($uuid);
return $this->tenantRepository->findByUuid($uuid);
}
public function findTenantById(int $id): ?array
{
return $this->tenantRepository()->find($id);
return $this->tenantRepository->find($id);
}
public function existsDepartmentCode(string $code): bool
{
return $this->departmentRepository()->existsByCode($code);
return $this->departmentRepository->existsByCode($code);
}
public function existsDepartmentByTenantAndDescription(int $tenantId, string $description): bool
{
return $this->departmentRepository()->existsByTenantAndDescription($tenantId, $description);
}
/**
* @param array<string, mixed> $input
* @return array<string, mixed>
*/
public function createDepartmentFromAdmin(array $input, int $currentUserId): array
{
return $this->departmentService()->createFromAdmin($input, $currentUserId);
}
private function departmentRepository(): DepartmentRepositoryInterface
{
return $this->departmentRepository;
}
private function tenantRepository(): TenantRepositoryInterface
{
return $this->tenantRepository;
}
private function departmentService(): DepartmentService
{
return $this->departmentService;
return $this->departmentRepository->existsByTenantAndDescription($tenantId, $description);
}
}

View File

@@ -2,10 +2,14 @@
namespace MintyPHP\Service\Import\Profile;
use MintyPHP\Service\Org\DepartmentService;
class DepartmentImportProfile implements ImportProfileInterface
{
public function __construct(private readonly DepartmentImportGateway $gateway)
{
public function __construct(
private readonly DepartmentImportGateway $gateway,
private readonly DepartmentService $departmentService
) {
}
public function key(): string
@@ -139,7 +143,7 @@ class DepartmentImportProfile implements ImportProfileInterface
return ['status' => 'skipped', 'code' => 'department_exists'];
}
$result = $this->gateway->createDepartmentFromAdmin([
$result = $this->departmentService->createFromAdmin([
'description' => (string) ($normalized['description'] ?? ''),
'tenant_id' => (int) ($normalized['tenant_id'] ?? 0),
'code' => (string) ($normalized['code'] ?? ''),

View File

@@ -6,13 +6,11 @@ use MintyPHP\Repository\Access\RoleRepositoryInterface;
use MintyPHP\Repository\Org\DepartmentRepositoryInterface;
use MintyPHP\Repository\Tenant\TenantRepositoryInterface;
use MintyPHP\Repository\User\UserReadRepositoryInterface;
use MintyPHP\Service\User\UserAccountService;
class UserImportGateway
{
public function __construct(
private readonly UserReadRepositoryInterface $userReadRepository,
private readonly UserAccountService $userAccountService,
private readonly TenantRepositoryInterface $tenantRepository,
private readonly RoleRepositoryInterface $roleRepository,
private readonly DepartmentRepositoryInterface $departmentRepository,
@@ -53,13 +51,4 @@ class UserImportGateway
{
return $this->departmentRepository->find($id);
}
/**
* @param array<string, mixed> $input
* @return array<string, mixed>
*/
public function createUserFromAdmin(array $input, int $currentUserId): array
{
return $this->userAccountService->createFromAdmin($input, $currentUserId);
}
}

View File

@@ -2,6 +2,8 @@
namespace MintyPHP\Service\Import\Profile;
use MintyPHP\Service\User\UserAccountService;
class UserImportProfile implements ImportProfileInterface
{
/**
@@ -9,8 +11,10 @@ class UserImportProfile implements ImportProfileInterface
*/
private ?array $allowedLocales = null;
public function __construct(private readonly UserImportGateway $gateway)
{
public function __construct(
private readonly UserImportGateway $gateway,
private readonly UserAccountService $userAccountService
) {
}
public function key(): string
@@ -203,7 +207,7 @@ class UserImportProfile implements ImportProfileInterface
$input['active'] = null;
}
$result = $this->gateway->createUserFromAdmin($input, (int) ($context['current_user_id'] ?? 0));
$result = $this->userAccountService->createFromAdmin($input, (int) ($context['current_user_id'] ?? 0));
if (!($result['ok'] ?? false)) {
$errors = $result['errors'] ?? [];
$message = is_array($errors) && isset($errors[0]) ? (string) $errors[0] : t('User can not be registered');

View File

@@ -4,8 +4,8 @@ namespace MintyPHP\Service\Org;
use MintyPHP\Repository\Org\DepartmentRepositoryInterface;
use MintyPHP\Service\Audit\SystemAuditService;
use MintyPHP\Service\Directory\DirectoryScopeGateway;
use MintyPHP\Service\Directory\DirectorySettingsGateway;
use MintyPHP\Service\Tenant\TenantScopeService;
use MintyPHP\Service\User\UserServicesFactory;
class DepartmentService
@@ -14,7 +14,7 @@ class DepartmentService
private readonly UserServicesFactory $userServicesFactory,
private readonly DepartmentRepositoryInterface $departmentRepository,
private readonly DirectorySettingsGateway $settingsGateway,
private readonly DirectoryScopeGateway $scopeGateway,
private readonly TenantScopeService $scopeGateway,
private readonly SystemAuditService $systemAuditService
) {
}

View File

@@ -4,14 +4,13 @@ namespace MintyPHP\Service\Search;
use MintyPHP\Repository\Search\SearchQueryRepository;
use MintyPHP\Repository\Tenant\UserTenantRepository;
use MintyPHP\Service\Access\PermissionGateway;
use MintyPHP\Service\Access\PermissionService;
use MintyPHP\Support\SearchConfig;
class SearchDataService
{
public function __construct(
private readonly PermissionGateway $permissionGateway,
private readonly PermissionService $permissionService,
private readonly UserTenantRepository $userTenantRepository,
private readonly SearchQueryRepository $searchQueryRepository
) {
@@ -37,7 +36,7 @@ class SearchDataService
}
$perm = (string) ($resource['permission'] ?? '');
if ($perm !== '' && !$this->permissionGateway->userHas($userId, $perm)) {
if ($perm !== '' && !$this->permissionService->userHas($userId, $perm)) {
continue;
}
@@ -68,7 +67,7 @@ class SearchDataService
}
$items = array_merge($items, SearchConfig::hotkeyResultItems($query));
if ($this->permissionGateway->userHas($userId, PermissionService::DOCS_VIEW)) {
if ($this->permissionService->userHas($userId, PermissionService::DOCS_VIEW)) {
$items = array_merge($items, SearchConfig::docsResultItems($query));
}
@@ -145,7 +144,7 @@ class SearchDataService
}
$perm = (string) ($resource['permission'] ?? '');
if ($perm !== '' && !$this->permissionGateway->userHas($userId, $perm)) {
if ($perm !== '' && !$this->permissionService->userHas($userId, $perm)) {
continue;
}
@@ -193,7 +192,7 @@ class SearchDataService
$results[] = $hotkeyResource;
}
if ($this->permissionGateway->userHas($userId, PermissionService::DOCS_VIEW)) {
if ($this->permissionService->userHas($userId, PermissionService::DOCS_VIEW)) {
$docsResource = SearchConfig::docsPreviewResource($query);
if ($docsResource !== null) {
$results[] = $docsResource;
@@ -210,7 +209,7 @@ class SearchDataService
{
if ($userId > 0) {
// Prime cached permissions for the current request.
$this->permissionGateway->getUserPermissions($userId);
$this->permissionService->getUserPermissions($userId);
}
$tenantIds = $userId > 0

View File

@@ -9,7 +9,7 @@ use MintyPHP\Repository\Tenant\TenantRepositoryInterface;
class SettingServicesFactory
{
private ?ThemeConfigService $themeConfigService = null;
private ?ThemeConfigGateway $themeConfigService = null;
private ?SettingCacheService $settingCacheService = null;
private ?SettingsCryptoGatewayInterface $settingsCryptoGateway = null;
private ?SettingsMetadataGateway $settingsMetadataGateway = null;
@@ -53,7 +53,7 @@ class SettingServicesFactory
{
return $this->settingsAppGateway ??= new SettingsAppGateway(
$this->createSettingsMetadataGateway(),
$this->createThemeConfigService(),
$this->createThemeConfigGateway(),
);
}
@@ -105,9 +105,9 @@ class SettingServicesFactory
return $this->settingCacheService ??= new SettingCacheService();
}
public function createThemeConfigService(): ThemeConfigService
public function createThemeConfigGateway(): ThemeConfigGateway
{
return $this->themeConfigService ??= new ThemeConfigService();
return $this->themeConfigService ??= new ThemeConfigGateway();
}
public function createSettingsCryptoGateway(): SettingsCryptoGatewayInterface

View File

@@ -8,7 +8,7 @@ class SettingsAppGateway
{
public function __construct(
private readonly SettingsMetadataGateway $settingsMetadataGateway,
private readonly ThemeConfigService $themeConfigService
private readonly ThemeConfigGateway $themeConfigService
) {
}

View File

@@ -2,7 +2,7 @@
namespace MintyPHP\Service\Settings;
class ThemeConfigService
class ThemeConfigGateway
{
private const DEFAULT_THEMES = [
'light' => 'Light',

View File

@@ -5,7 +5,6 @@ namespace MintyPHP\Service\Tenant;
use MintyPHP\Repository\Org\DepartmentRepositoryInterface;
use MintyPHP\Repository\Tenant\TenantRepositoryInterface;
use MintyPHP\Repository\Tenant\UserTenantRepositoryInterface;
use MintyPHP\Service\Access\PermissionGateway;
use MintyPHP\Service\Access\PermissionService;
class TenantScopeService
@@ -14,7 +13,7 @@ class TenantScopeService
private readonly TenantRepositoryInterface $tenantRepository,
private readonly DepartmentRepositoryInterface $departmentRepository,
private readonly UserTenantRepositoryInterface $userTenantRepository,
private readonly PermissionGateway $permissionGateway
private readonly PermissionService $permissionService
) {
}
@@ -167,6 +166,6 @@ class TenantScopeService
return false;
}
return $this->permissionGateway->userHas($userId, PermissionService::TENANT_SCOPE_GLOBAL);
return $this->permissionService->userHas($userId, PermissionService::TENANT_SCOPE_GLOBAL);
}
}

View File

@@ -5,7 +5,7 @@ namespace MintyPHP\Service\Tenant;
use MintyPHP\Repository\Org\DepartmentRepositoryInterface;
use MintyPHP\Repository\Tenant\TenantRepositoryInterface;
use MintyPHP\Repository\Tenant\UserTenantRepositoryInterface;
use MintyPHP\Service\Access\PermissionGateway;
use MintyPHP\Service\Access\PermissionService;
class TenantServicesFactory
{
@@ -14,7 +14,7 @@ class TenantServicesFactory
private ?TenantFaviconService $tenantFaviconService = null;
public function __construct(
private readonly PermissionGateway $permissionGateway,
private readonly PermissionService $permissionService,
private readonly TenantRepositoryFactory $tenantRepositoryFactory
) {
}
@@ -25,7 +25,7 @@ class TenantServicesFactory
$this->createTenantRepository(),
$this->createDepartmentRepository(),
$this->createUserTenantRepository(),
$this->createPermissionGateway()
$this->createPermissionService()
);
}
@@ -54,8 +54,8 @@ class TenantServicesFactory
return $this->tenantRepositoryFactory->createUserTenantRepository();
}
public function createPermissionGateway(): PermissionGateway
public function createPermissionService(): PermissionService
{
return $this->permissionGateway;
return $this->permissionService;
}
}

View File

@@ -8,6 +8,7 @@ use MintyPHP\Repository\User\UserListQueryRepositoryInterface;
use MintyPHP\Repository\User\UserReadRepositoryInterface;
use MintyPHP\Repository\User\UserWriteRepositoryInterface;
use MintyPHP\Service\Audit\SystemAuditService;
use MintyPHP\Service\Tenant\TenantScopeService;
class UserAccountService
{
@@ -18,7 +19,7 @@ class UserAccountService
private readonly UserAssignmentService $userAssignmentService,
private readonly UserPasswordService $userPasswordService,
private readonly UserSettingsGateway $settingsGateway,
private readonly UserScopeGateway $scopeGateway,
private readonly TenantScopeService $scopeGateway,
private readonly UserDirectoryGateway $directoryGateway,
private readonly SystemAuditService $systemAuditService,
private readonly DatabaseSessionRepository $databaseSessionRepository

View File

@@ -7,6 +7,7 @@ use MintyPHP\Repository\Org\UserDepartmentRepositoryInterface;
use MintyPHP\Repository\Support\DatabaseSessionRepository;
use MintyPHP\Repository\Tenant\UserTenantRepositoryInterface;
use MintyPHP\Repository\User\UserWriteRepositoryInterface;
use MintyPHP\Service\Access\PermissionService;
class UserAssignmentService
{
@@ -16,7 +17,7 @@ class UserAssignmentService
private readonly UserRoleRepositoryInterface $userRoleRepository,
private readonly UserDepartmentRepositoryInterface $userDepartmentRepository,
private readonly UserDirectoryGateway $directoryGateway,
private readonly UserPermissionGateway $permissionGateway,
private readonly PermissionService $permissionService,
private readonly DatabaseSessionRepository $databaseSessionRepository
) {
}
@@ -181,7 +182,7 @@ class UserAssignmentService
$ids = array_values(array_filter($ids, static fn ($id) => isset($validMap[$id])));
}
$result = $this->userRoleRepository->replaceForUser($userId, $ids);
$this->permissionGateway->clearUserCache($userId);
$this->permissionService->clearUserCache($userId);
if ($result && $bumpAuthz) {
$this->bumpAuthzVersion($userId);
}

View File

@@ -3,7 +3,7 @@
namespace MintyPHP\Service\User;
use MintyPHP\Service\Access\AccessServicesFactory;
use MintyPHP\Service\Access\PermissionGateway;
use MintyPHP\Service\Access\PermissionService;
use MintyPHP\Service\Directory\DirectoryRepositoryFactory;
use MintyPHP\Service\Settings\SettingsAppGateway;
use MintyPHP\Service\Settings\SettingsDefaultsGateway;
@@ -16,12 +16,9 @@ class UserGatewayFactory
private ?SettingsDefaultsGateway $settingsDefaultsGateway = null;
private ?SettingsAppGateway $settingsAppGateway = null;
private ?SettingsUserLifecycleGateway $settingsUserLifecycleGateway = null;
private ?PermissionGateway $permissionGateway = null;
private ?PermissionService $permissionService = null;
private ?UserSettingsGateway $userSettingsGateway = null;
private ?UserScopeGateway $userScopeGateway = null;
private ?UserDirectoryGateway $userDirectoryGateway = null;
private ?UserPermissionGateway $userPermissionGateway = null;
public function __construct(
private readonly AccessServicesFactory $accessServicesFactory,
private readonly SettingServicesFactory $settingServicesFactory,
@@ -39,9 +36,9 @@ class UserGatewayFactory
);
}
public function createUserScopeGateway(): UserScopeGateway
public function getTenantScopeService(): TenantScopeService
{
return $this->userScopeGateway ??= new UserScopeGateway($this->tenantScopeService);
return $this->tenantScopeService;
}
public function createUserDirectoryGateway(): UserDirectoryGateway
@@ -53,14 +50,9 @@ class UserGatewayFactory
);
}
public function createUserPermissionGateway(): UserPermissionGateway
public function createPermissionService(): PermissionService
{
return $this->userPermissionGateway ??= new UserPermissionGateway($this->createPermissionGateway());
}
private function createPermissionGateway(): PermissionGateway
{
return $this->permissionGateway ??= $this->accessServicesFactory->createPermissionGateway();
return $this->permissionService ??= $this->accessServicesFactory->createPermissionService();
}
private function createSettingsDefaultsGateway(): SettingsDefaultsGateway

View File

@@ -1,17 +0,0 @@
<?php
namespace MintyPHP\Service\User;
use MintyPHP\Service\Access\PermissionGateway;
class UserPermissionGateway
{
public function __construct(private readonly PermissionGateway $permissionGateway)
{
}
public function clearUserCache(int $userId): void
{
$this->permissionGateway->clearUserCache($userId);
}
}

View File

@@ -1,28 +0,0 @@
<?php
namespace MintyPHP\Service\User;
use MintyPHP\Service\Tenant\TenantScopeService;
class UserScopeGateway
{
public function __construct(
private readonly TenantScopeService $tenantScopeService
) {
}
public function hasGlobalAccess(int $userId): bool
{
return $this->tenantScopeService->hasGlobalAccess($userId);
}
public function canAccess(string $resourceType, int $resourceId, int $userId): bool
{
return $this->tenantScopeService->canAccess($resourceType, $resourceId, $userId);
}
public function getUserTenantIds(int $userId): array
{
return $this->tenantScopeService->getUserTenantIds($userId);
}
}

View File

@@ -43,7 +43,7 @@ class UserServicesFactory
$this->createUserAssignmentService(),
$this->createUserPasswordService(),
$this->createUserSettingsGateway(),
$this->createUserScopeGateway(),
$this->userGatewayFactory->getTenantScopeService(),
$this->createUserDirectoryGateway(),
$this->auditServicesFactory->createSystemAuditService(),
$this->databaseSessionRepository
@@ -58,7 +58,7 @@ class UserServicesFactory
$this->createUserRoleRepository(),
$this->createUserDepartmentRepository(),
$this->createUserDirectoryGateway(),
$this->createUserPermissionGateway(),
$this->userGatewayFactory->createPermissionService(),
$this->databaseSessionRepository
);
}
@@ -69,7 +69,7 @@ class UserServicesFactory
$this->createUserReadRepository(),
$this->createUserWriteRepository(),
$this->createUserTenantRepository(),
$this->createUserScopeGateway(),
$this->userGatewayFactory->getTenantScopeService(),
$this->createUserDirectoryGateway()
);
}
@@ -139,21 +139,11 @@ class UserServicesFactory
return $this->userGatewayFactory->createUserSettingsGateway();
}
public function createUserScopeGateway(): UserScopeGateway
{
return $this->userGatewayFactory->createUserScopeGateway();
}
public function createUserDirectoryGateway(): UserDirectoryGateway
{
return $this->userGatewayFactory->createUserDirectoryGateway();
}
public function createUserPermissionGateway(): UserPermissionGateway
{
return $this->userGatewayFactory->createUserPermissionGateway();
}
public function createUserLifecycleService(): UserLifecycleService
{
return $this->userLifecycleService ??= new UserLifecycleService(

View File

@@ -5,6 +5,7 @@ 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
{
@@ -12,7 +13,7 @@ class UserTenantContextService
private readonly UserReadRepositoryInterface $userReadRepository,
private readonly UserWriteRepositoryInterface $userWriteRepository,
private readonly UserTenantRepositoryInterface $userTenantRepository,
private readonly UserScopeGateway $scopeGateway,
private readonly TenantScopeService $scopeGateway,
private readonly UserDirectoryGateway $directoryGateway
) {
}

View File

@@ -32,7 +32,7 @@ function setAppContainer(\MintyPHP\App\AppContainer $container): void
$GLOBALS['minty_app_container'] = $container;
\MintyPHP\Http\ApiAuth::configure(
static fn (): \MintyPHP\Service\Auth\AuthScopeGateway => $container->get(\MintyPHP\Service\Auth\AuthScopeGateway::class),
static fn (): \MintyPHP\Service\Tenant\TenantScopeService => $container->get(\MintyPHP\Service\Tenant\TenantScopeService::class),
static fn (): \MintyPHP\Service\Auth\ApiTokenService => $container->get(\MintyPHP\Service\Auth\ApiTokenService::class),
static fn (): \MintyPHP\Repository\Access\UserRoleRepository => $container->get(\MintyPHP\Repository\Access\UserRoleRepository::class),
static fn (): \MintyPHP\Repository\Access\RolePermissionRepository => $container->get(\MintyPHP\Repository\Access\RolePermissionRepository::class),
@@ -192,14 +192,14 @@ function appSetting(string $key): ?string
*/
function appThemes(): array
{
if (!class_exists('MintyPHP\\Service\\Settings\\ThemeConfigService')) {
if (!class_exists('MintyPHP\\Service\\Settings\\ThemeConfigGateway')) {
return [
'light' => 'Light',
'dark' => 'Dark',
];
}
return app(\MintyPHP\Service\Settings\ThemeConfigService::class)->all();
return app(\MintyPHP\Service\Settings\ThemeConfigGateway::class)->all();
}
/**