$this->authorizeAdminTenantsView($context), self::ABILITY_ADMIN_TENANTS_CREATE => $this->authorizeAdminTenantsCreate($context), self::ABILITY_ADMIN_TENANTS_EDIT_CONTEXT => $this->authorizeAdminTenantsEditContext($context), self::ABILITY_ADMIN_TENANTS_EDIT_SUBMIT => $this->authorizeAdminTenantsEditSubmit($context), self::ABILITY_ADMIN_TENANTS_DELETE => $this->authorizeAdminTenantsDelete($context), self::ABILITY_ADMIN_TENANTS_CUSTOM_FIELDS_MANAGE => $this->authorizeAdminTenantCustomFieldsManage($context), self::ABILITY_ADMIN_TENANTS_AVATAR_VIEW => $this->authorizeAdminTenantAvatarView($context), self::ABILITY_ADMIN_TENANTS_MEDIA_UPDATE => $this->authorizeAdminTenantMediaUpdate($context), default => AuthorizationDecision::deny(500, 'authorization_ability_not_supported'), }; } private function authorizeAdminTenantsEditContext(array $context): AuthorizationDecision { $actorUserId = $this->actorUserId($context); $targetTenantId = $this->targetTenantId($context); if ($actorUserId <= 0 || $targetTenantId <= 0) { return AuthorizationDecision::deny(403, 'forbidden'); } if (!$this->scopeGateway->canAccess('tenants', $targetTenantId, $actorUserId)) { return AuthorizationDecision::deny(403, 'permission_denied'); } $canViewTenant = $this->hasPermission($actorUserId, PermissionService::TENANTS_VIEW); $canUpdateTenant = $this->hasPermission($actorUserId, PermissionService::TENANTS_UPDATE); $canViewPage = $canViewTenant || $canUpdateTenant; if (!$canViewPage) { return AuthorizationDecision::deny(403, 'forbidden'); } $capabilities = [ 'can_view_page' => $canViewPage, 'can_update_tenant' => $canUpdateTenant, 'can_manage_custom_fields' => $this->hasPermission($actorUserId, PermissionService::CUSTOM_FIELDS_MANAGE), 'can_manage_sso' => $this->hasPermission($actorUserId, PermissionService::TENANTS_SSO_MANAGE), 'can_delete_tenant' => $this->hasPermission($actorUserId, PermissionService::TENANTS_DELETE), ]; return AuthorizationDecision::allow(['capabilities' => $capabilities]); } private function authorizeAdminTenantsView(array $context): AuthorizationDecision { $actorUserId = $this->actorUserId($context); if (!$this->hasPermission($actorUserId, PermissionService::TENANTS_VIEW)) { return AuthorizationDecision::deny(403, 'forbidden'); } return AuthorizationDecision::allow([ 'capabilities' => [ 'can_view_page' => true, 'can_create_tenant' => $this->hasPermission($actorUserId, PermissionService::TENANTS_CREATE), 'can_manage_custom_fields' => $this->hasPermission($actorUserId, PermissionService::CUSTOM_FIELDS_MANAGE), 'can_manage_sso' => $this->hasPermission($actorUserId, PermissionService::TENANTS_SSO_MANAGE), 'allowed_tenant_ids' => $this->scopeGateway->getUserTenantIds($actorUserId), 'is_strict_scope' => $this->scopeGateway->isStrict(), 'has_global_access' => $this->scopeGateway->hasGlobalAccess($actorUserId), ], ]); } private function authorizeAdminTenantsCreate(array $context): AuthorizationDecision { $actorUserId = $this->actorUserId($context); if (!$this->hasPermission($actorUserId, PermissionService::TENANTS_CREATE)) { return AuthorizationDecision::deny(403, 'forbidden'); } return AuthorizationDecision::allow([ 'capabilities' => [ 'can_manage_custom_fields' => $this->hasPermission($actorUserId, PermissionService::CUSTOM_FIELDS_MANAGE), 'can_manage_sso' => $this->hasPermission($actorUserId, PermissionService::TENANTS_SSO_MANAGE), ], ]); } private function authorizeAdminTenantsEditSubmit(array $context): AuthorizationDecision { $contextDecision = $this->authorizeAdminTenantsEditContext($context); if (!$contextDecision->isAllowed()) { return $contextDecision; } $capabilities = $this->capabilitiesFromDecision($contextDecision); if (!($capabilities['can_update_tenant'] ?? false)) { return AuthorizationDecision::deny(403, 'forbidden'); } $input = is_array($context['input'] ?? null) ? $context['input'] : []; if ($this->containsSsoFields($input) && !($capabilities['can_manage_sso'] ?? false)) { return AuthorizationDecision::deny(403, 'forbidden'); } return AuthorizationDecision::allow(['capabilities' => $capabilities]); } private function authorizeAdminTenantsDelete(array $context): AuthorizationDecision { return $this->authorizeTenantWithPermission($context, PermissionService::TENANTS_DELETE); } private function authorizeAdminTenantCustomFieldsManage(array $context): AuthorizationDecision { return $this->authorizeTenantWithPermission($context, PermissionService::CUSTOM_FIELDS_MANAGE); } private function authorizeAdminTenantAvatarView(array $context): AuthorizationDecision { $actorUserId = $this->actorUserId($context); if (!$this->hasPermission($actorUserId, PermissionService::TENANTS_VIEW) && !$this->hasPermission($actorUserId, PermissionService::TENANTS_UPDATE)) { return AuthorizationDecision::deny(403, 'forbidden'); } return $this->authorizeTenantScopeOnly($context); } private function authorizeAdminTenantMediaUpdate(array $context): AuthorizationDecision { return $this->authorizeTenantWithPermission($context, PermissionService::TENANTS_UPDATE); } private function authorizeTenantWithPermission(array $context, string $permissionKey): AuthorizationDecision { $actorUserId = $this->actorUserId($context); if (!$this->hasPermission($actorUserId, $permissionKey)) { return AuthorizationDecision::deny(403, 'forbidden'); } return $this->authorizeTenantScopeOnly($context); } private function authorizeTenantScopeOnly(array $context): AuthorizationDecision { $actorUserId = $this->actorUserId($context); $targetTenantId = $this->targetTenantId($context); if ($actorUserId <= 0 || $targetTenantId <= 0) { return AuthorizationDecision::deny(403, 'forbidden'); } if (!$this->scopeGateway->canAccess('tenants', $targetTenantId, $actorUserId)) { return AuthorizationDecision::deny(403, 'permission_denied'); } return AuthorizationDecision::allow(); } private function containsSsoFields(array $input): bool { $ssoFields = [ 'microsoft_enabled', 'enforce_microsoft_login', 'sync_profile_on_login', 'sync_profile_fields', 'entra_tenant_id', 'allowed_domains', 'use_shared_app', 'client_id_override', 'client_secret_override', 'clear_client_secret_override', 'microsoft_auto_remember_mode', ]; foreach ($ssoFields as $field) { if (array_key_exists($field, $input)) { return true; } } return false; } private function targetTenantId(array $context): int { return (int) ($context['target_tenant_id'] ?? 0); } }