moduleClassResolver = $moduleClassResolver !== null ? \Closure::fromCallable($moduleClassResolver) : static fn (string $class): ?object => null; } public function createUserAuthorizationPolicy(): UserAuthorizationPolicy { return $this->userAuthorizationPolicy ??= new UserAuthorizationPolicy( $this->permissionService, $this->tenantScopeService ); } public function createRoleAuthorizationPolicy(): RoleAuthorizationPolicy { return $this->roleAuthorizationPolicy ??= new RoleAuthorizationPolicy($this->permissionService); } public function createPermissionAuthorizationPolicy(): PermissionAuthorizationPolicy { return $this->permissionAuthorizationPolicy ??= new PermissionAuthorizationPolicy($this->permissionService); } public function createOperationsAuthorizationPolicy(): OperationsAuthorizationPolicy { return $this->operationsAuthorizationPolicy ??= new OperationsAuthorizationPolicy($this->permissionService); } public function createSettingsAuthorizationPolicy(): SettingsAuthorizationPolicy { return $this->settingsAuthorizationPolicy ??= new SettingsAuthorizationPolicy($this->permissionService); } public function createTenantAuthorizationPolicy(): TenantAuthorizationPolicy { return $this->tenantAuthorizationPolicy ??= new TenantAuthorizationPolicy( $this->permissionService, $this->tenantScopeService ); } public function createDepartmentAuthorizationPolicy(): DepartmentAuthorizationPolicy { return $this->departmentAuthorizationPolicy ??= new DepartmentAuthorizationPolicy( $this->permissionService, $this->tenantScopeService ); } // Assembles all policies into a single AuthorizationService. // Core policies are registered first; module-contributed policies are appended. // Each ability maps to exactly one policy via supports() — first match wins. public function createAuthorizationService(): AuthorizationService { if ($this->authorizationService !== null) { return $this->authorizationService; } $policies = [ $this->createUserAuthorizationPolicy(), $this->createRoleAuthorizationPolicy(), $this->createPermissionAuthorizationPolicy(), $this->createOperationsAuthorizationPolicy(), $this->createSettingsAuthorizationPolicy(), $this->createTenantAuthorizationPolicy(), $this->createDepartmentAuthorizationPolicy(), ]; // Append module-contributed authorization policies. if ($this->moduleRegistry !== null) { try { foreach ($this->moduleRegistry->getAuthorizationPolicies() as $policyClass) { if (trim($policyClass) === '') { continue; } $policy = $this->instantiateModulePolicy($policyClass); if ($policy instanceof AuthorizationPolicyInterface) { $policies[] = $policy; } } } catch (\Throwable) { // fail-open: no module registry available in this context } } return $this->authorizationService = new AuthorizationService($policies); } private function instantiateModulePolicy(string $policyClass): ?AuthorizationPolicyInterface { try { $instance = ($this->moduleClassResolver)($policyClass); return $instance instanceof AuthorizationPolicyInterface ? $instance : null; } catch (\Throwable) { return null; } } }