2026-03-04 15:56:58 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace MintyPHP\Service\Access;
|
|
|
|
|
|
2026-03-19 08:23:14 +01:00
|
|
|
use MintyPHP\App\Module\ModuleRegistry;
|
2026-03-13 11:31:33 +01:00
|
|
|
use MintyPHP\Service\Tenant\TenantScopeService;
|
2026-03-04 15:56:58 +01:00
|
|
|
|
|
|
|
|
class AccessPolicyFactory
|
|
|
|
|
{
|
|
|
|
|
private ?UserAuthorizationPolicy $userAuthorizationPolicy = null;
|
|
|
|
|
private ?RoleAuthorizationPolicy $roleAuthorizationPolicy = null;
|
|
|
|
|
private ?PermissionAuthorizationPolicy $permissionAuthorizationPolicy = null;
|
|
|
|
|
private ?OperationsAuthorizationPolicy $operationsAuthorizationPolicy = null;
|
|
|
|
|
private ?SettingsAuthorizationPolicy $settingsAuthorizationPolicy = null;
|
|
|
|
|
private ?TenantAuthorizationPolicy $tenantAuthorizationPolicy = null;
|
|
|
|
|
private ?DepartmentAuthorizationPolicy $departmentAuthorizationPolicy = null;
|
|
|
|
|
private ?AuthorizationService $authorizationService = null;
|
2026-03-25 10:02:03 +01:00
|
|
|
private \Closure $moduleClassResolver;
|
2026-03-04 15:56:58 +01:00
|
|
|
|
|
|
|
|
public function __construct(
|
2026-03-13 11:31:33 +01:00
|
|
|
private readonly PermissionService $permissionService,
|
2026-03-19 08:23:14 +01:00
|
|
|
private readonly TenantScopeService $tenantScopeService,
|
2026-03-25 10:02:03 +01:00
|
|
|
private readonly ?ModuleRegistry $moduleRegistry = null,
|
|
|
|
|
?callable $moduleClassResolver = null
|
2026-03-04 15:56:58 +01:00
|
|
|
) {
|
2026-03-25 10:02:03 +01:00
|
|
|
$this->moduleClassResolver = $moduleClassResolver !== null
|
|
|
|
|
? \Closure::fromCallable($moduleClassResolver)
|
|
|
|
|
: static fn (string $class): ?object => null;
|
2026-03-04 15:56:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function createUserAuthorizationPolicy(): UserAuthorizationPolicy
|
|
|
|
|
{
|
|
|
|
|
return $this->userAuthorizationPolicy ??= new UserAuthorizationPolicy(
|
2026-03-13 11:31:33 +01:00
|
|
|
$this->permissionService,
|
|
|
|
|
$this->tenantScopeService
|
2026-03-04 15:56:58 +01:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function createRoleAuthorizationPolicy(): RoleAuthorizationPolicy
|
|
|
|
|
{
|
2026-03-13 11:31:33 +01:00
|
|
|
return $this->roleAuthorizationPolicy ??= new RoleAuthorizationPolicy($this->permissionService);
|
2026-03-04 15:56:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function createPermissionAuthorizationPolicy(): PermissionAuthorizationPolicy
|
|
|
|
|
{
|
2026-03-13 11:31:33 +01:00
|
|
|
return $this->permissionAuthorizationPolicy ??= new PermissionAuthorizationPolicy($this->permissionService);
|
2026-03-04 15:56:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function createOperationsAuthorizationPolicy(): OperationsAuthorizationPolicy
|
|
|
|
|
{
|
2026-03-13 11:31:33 +01:00
|
|
|
return $this->operationsAuthorizationPolicy ??= new OperationsAuthorizationPolicy($this->permissionService);
|
2026-03-04 15:56:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function createSettingsAuthorizationPolicy(): SettingsAuthorizationPolicy
|
|
|
|
|
{
|
2026-03-13 11:31:33 +01:00
|
|
|
return $this->settingsAuthorizationPolicy ??= new SettingsAuthorizationPolicy($this->permissionService);
|
2026-03-04 15:56:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function createTenantAuthorizationPolicy(): TenantAuthorizationPolicy
|
|
|
|
|
{
|
|
|
|
|
return $this->tenantAuthorizationPolicy ??= new TenantAuthorizationPolicy(
|
2026-03-13 11:31:33 +01:00
|
|
|
$this->permissionService,
|
|
|
|
|
$this->tenantScopeService
|
2026-03-04 15:56:58 +01:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function createDepartmentAuthorizationPolicy(): DepartmentAuthorizationPolicy
|
|
|
|
|
{
|
|
|
|
|
return $this->departmentAuthorizationPolicy ??= new DepartmentAuthorizationPolicy(
|
2026-03-13 11:31:33 +01:00
|
|
|
$this->permissionService,
|
|
|
|
|
$this->tenantScopeService
|
2026-03-04 15:56:58 +01:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-06 00:44:52 +01:00
|
|
|
// Assembles all policies into a single AuthorizationService.
|
feat: extend module platform with UI slots, runtime components, CLI tooling and {{userId}} search support
Completes the generic module platform that enables modules to contribute
UI elements, runtime JS components, and search resources without any
core hardcoding.
New generic UI slot types:
- topbar.right_item: module-contributed topbar buttons
- layout.body_end_template: module-contributed dialog/overlay templates
- layout.head_style: module-contributed global CSS
- runtime.component: declarative JS component registration with phase ordering
New infrastructure:
- ModuleAutoloader: PSR-4 autoloading for module-local PHP classes
- ModuleRuntimePageBuilder: symlinks module pages into runtime directory
- ModuleRuntimeAssetPublisher: publishes module CSS/JS to web/modules/
- ModulePermissionSynchronizer: syncs module permissions to DB
- CLI scripts: module-runtime-sync, module-build, module-migrate,
module-permissions-sync, module-assets-sync
- {{userId}} placeholder in SearchDataService for user-scoped search queries
- Component runtime with phased initialization (early/default/late)
- AppContainer.protectExistingBindings() to prevent module→core overwrites
- Architecture tests: ModuleStructureContractTest, CoreTemplateIsolationTest,
FrontendComponentRuntimeContractTest, AppContainerIsolationContractTest
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:19:56 +01:00
|
|
|
// Core policies are registered first; module-contributed policies are appended.
|
|
|
|
|
// Each ability maps to exactly one policy via supports() — first match wins.
|
2026-03-04 15:56:58 +01:00
|
|
|
public function createAuthorizationService(): AuthorizationService
|
|
|
|
|
{
|
feat: extend module platform with UI slots, runtime components, CLI tooling and {{userId}} search support
Completes the generic module platform that enables modules to contribute
UI elements, runtime JS components, and search resources without any
core hardcoding.
New generic UI slot types:
- topbar.right_item: module-contributed topbar buttons
- layout.body_end_template: module-contributed dialog/overlay templates
- layout.head_style: module-contributed global CSS
- runtime.component: declarative JS component registration with phase ordering
New infrastructure:
- ModuleAutoloader: PSR-4 autoloading for module-local PHP classes
- ModuleRuntimePageBuilder: symlinks module pages into runtime directory
- ModuleRuntimeAssetPublisher: publishes module CSS/JS to web/modules/
- ModulePermissionSynchronizer: syncs module permissions to DB
- CLI scripts: module-runtime-sync, module-build, module-migrate,
module-permissions-sync, module-assets-sync
- {{userId}} placeholder in SearchDataService for user-scoped search queries
- Component runtime with phased initialization (early/default/late)
- AppContainer.protectExistingBindings() to prevent module→core overwrites
- Architecture tests: ModuleStructureContractTest, CoreTemplateIsolationTest,
FrontendComponentRuntimeContractTest, AppContainerIsolationContractTest
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:19:56 +01:00
|
|
|
if ($this->authorizationService !== null) {
|
|
|
|
|
return $this->authorizationService;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$policies = [
|
2026-03-04 15:56:58 +01:00
|
|
|
$this->createUserAuthorizationPolicy(),
|
|
|
|
|
$this->createRoleAuthorizationPolicy(),
|
|
|
|
|
$this->createPermissionAuthorizationPolicy(),
|
|
|
|
|
$this->createOperationsAuthorizationPolicy(),
|
|
|
|
|
$this->createSettingsAuthorizationPolicy(),
|
|
|
|
|
$this->createTenantAuthorizationPolicy(),
|
|
|
|
|
$this->createDepartmentAuthorizationPolicy(),
|
feat: extend module platform with UI slots, runtime components, CLI tooling and {{userId}} search support
Completes the generic module platform that enables modules to contribute
UI elements, runtime JS components, and search resources without any
core hardcoding.
New generic UI slot types:
- topbar.right_item: module-contributed topbar buttons
- layout.body_end_template: module-contributed dialog/overlay templates
- layout.head_style: module-contributed global CSS
- runtime.component: declarative JS component registration with phase ordering
New infrastructure:
- ModuleAutoloader: PSR-4 autoloading for module-local PHP classes
- ModuleRuntimePageBuilder: symlinks module pages into runtime directory
- ModuleRuntimeAssetPublisher: publishes module CSS/JS to web/modules/
- ModulePermissionSynchronizer: syncs module permissions to DB
- CLI scripts: module-runtime-sync, module-build, module-migrate,
module-permissions-sync, module-assets-sync
- {{userId}} placeholder in SearchDataService for user-scoped search queries
- Component runtime with phased initialization (early/default/late)
- AppContainer.protectExistingBindings() to prevent module→core overwrites
- Architecture tests: ModuleStructureContractTest, CoreTemplateIsolationTest,
FrontendComponentRuntimeContractTest, AppContainerIsolationContractTest
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:19:56 +01:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// Append module-contributed authorization policies.
|
2026-03-19 08:23:14 +01:00
|
|
|
if ($this->moduleRegistry !== null) {
|
|
|
|
|
try {
|
|
|
|
|
foreach ($this->moduleRegistry->getAuthorizationPolicies() as $policyClass) {
|
|
|
|
|
if (!is_string($policyClass) || trim($policyClass) === '') {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$policy = $this->instantiateModulePolicy($policyClass);
|
|
|
|
|
if ($policy instanceof AuthorizationPolicyInterface) {
|
|
|
|
|
$policies[] = $policy;
|
|
|
|
|
}
|
feat: extend module platform with UI slots, runtime components, CLI tooling and {{userId}} search support
Completes the generic module platform that enables modules to contribute
UI elements, runtime JS components, and search resources without any
core hardcoding.
New generic UI slot types:
- topbar.right_item: module-contributed topbar buttons
- layout.body_end_template: module-contributed dialog/overlay templates
- layout.head_style: module-contributed global CSS
- runtime.component: declarative JS component registration with phase ordering
New infrastructure:
- ModuleAutoloader: PSR-4 autoloading for module-local PHP classes
- ModuleRuntimePageBuilder: symlinks module pages into runtime directory
- ModuleRuntimeAssetPublisher: publishes module CSS/JS to web/modules/
- ModulePermissionSynchronizer: syncs module permissions to DB
- CLI scripts: module-runtime-sync, module-build, module-migrate,
module-permissions-sync, module-assets-sync
- {{userId}} placeholder in SearchDataService for user-scoped search queries
- Component runtime with phased initialization (early/default/late)
- AppContainer.protectExistingBindings() to prevent module→core overwrites
- Architecture tests: ModuleStructureContractTest, CoreTemplateIsolationTest,
FrontendComponentRuntimeContractTest, AppContainerIsolationContractTest
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:19:56 +01:00
|
|
|
}
|
2026-03-19 08:23:14 +01:00
|
|
|
} catch (\Throwable) {
|
|
|
|
|
// fail-open: no module registry available in this context
|
feat: extend module platform with UI slots, runtime components, CLI tooling and {{userId}} search support
Completes the generic module platform that enables modules to contribute
UI elements, runtime JS components, and search resources without any
core hardcoding.
New generic UI slot types:
- topbar.right_item: module-contributed topbar buttons
- layout.body_end_template: module-contributed dialog/overlay templates
- layout.head_style: module-contributed global CSS
- runtime.component: declarative JS component registration with phase ordering
New infrastructure:
- ModuleAutoloader: PSR-4 autoloading for module-local PHP classes
- ModuleRuntimePageBuilder: symlinks module pages into runtime directory
- ModuleRuntimeAssetPublisher: publishes module CSS/JS to web/modules/
- ModulePermissionSynchronizer: syncs module permissions to DB
- CLI scripts: module-runtime-sync, module-build, module-migrate,
module-permissions-sync, module-assets-sync
- {{userId}} placeholder in SearchDataService for user-scoped search queries
- Component runtime with phased initialization (early/default/late)
- AppContainer.protectExistingBindings() to prevent module→core overwrites
- Architecture tests: ModuleStructureContractTest, CoreTemplateIsolationTest,
FrontendComponentRuntimeContractTest, AppContainerIsolationContractTest
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:19:56 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $this->authorizationService = new AuthorizationService($policies);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function instantiateModulePolicy(string $policyClass): ?AuthorizationPolicyInterface
|
|
|
|
|
{
|
|
|
|
|
try {
|
2026-03-25 10:02:03 +01:00
|
|
|
$instance = ($this->moduleClassResolver)($policyClass);
|
|
|
|
|
return $instance instanceof AuthorizationPolicyInterface ? $instance : null;
|
feat: extend module platform with UI slots, runtime components, CLI tooling and {{userId}} search support
Completes the generic module platform that enables modules to contribute
UI elements, runtime JS components, and search resources without any
core hardcoding.
New generic UI slot types:
- topbar.right_item: module-contributed topbar buttons
- layout.body_end_template: module-contributed dialog/overlay templates
- layout.head_style: module-contributed global CSS
- runtime.component: declarative JS component registration with phase ordering
New infrastructure:
- ModuleAutoloader: PSR-4 autoloading for module-local PHP classes
- ModuleRuntimePageBuilder: symlinks module pages into runtime directory
- ModuleRuntimeAssetPublisher: publishes module CSS/JS to web/modules/
- ModulePermissionSynchronizer: syncs module permissions to DB
- CLI scripts: module-runtime-sync, module-build, module-migrate,
module-permissions-sync, module-assets-sync
- {{userId}} placeholder in SearchDataService for user-scoped search queries
- Component runtime with phased initialization (early/default/late)
- AppContainer.protectExistingBindings() to prevent module→core overwrites
- Architecture tests: ModuleStructureContractTest, CoreTemplateIsolationTest,
FrontendComponentRuntimeContractTest, AppContainerIsolationContractTest
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:19:56 +01:00
|
|
|
} catch (\Throwable) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2026-03-04 15:56:58 +01:00
|
|
|
}
|
|
|
|
|
}
|