diff --git a/OWNERS b/OWNERS index eb12fec83a2..db61f874338 100644 --- a/OWNERS +++ b/OWNERS @@ -28,6 +28,8 @@ packages/@n8n/config/ @n8n-io/catalysts packages/@n8n/backend-common/ @n8n-io/catalysts packages/@n8n/backend-network/ @n8n-io/catalysts packages/@n8n/backend-services/ @n8n-io/catalysts +packages/@n8n/backend-services/src/services/role.service.ts @n8n-io/iam +packages/@n8n/backend-services/src/services/role-cache.service.ts @n8n-io/iam packages/@n8n/backend-test-utils/ @n8n-io/catalysts packages/@n8n/di/ @n8n-io/catalysts packages/@n8n/errors/ @n8n-io/catalysts @@ -155,8 +157,6 @@ packages/cli/src/controllers/e2e.controller.ts @n8n-io/qa-dx # CLI services packages/cli/src/services/jwt.service.ts @n8n-io/iam packages/cli/src/services/user.service.ts @n8n-io/iam -packages/cli/src/services/role.service.ts @n8n-io/iam -packages/cli/src/services/role-cache.service.ts @n8n-io/iam packages/cli/src/services/password.utility.ts @n8n-io/iam packages/cli/src/services/public-api-key.service.ts @n8n-io/iam packages/cli/src/services/security-settings.service.ts @n8n-io/iam diff --git a/packages/@n8n/backend-services/package.json b/packages/@n8n/backend-services/package.json index 7f8ee7e6a18..26ec939e934 100644 --- a/packages/@n8n/backend-services/package.json +++ b/packages/@n8n/backend-services/package.json @@ -28,8 +28,10 @@ "@n8n/config": "workspace:*", "@n8n/constants": "workspace:*", "@n8n/decorators": "workspace:*", + "@n8n/db": "workspace:*", "@n8n/di": "workspace:*", "@n8n/errors": "workspace:*", + "@n8n/permissions": "workspace:*", "@n8n/utils": "workspace:*", "cache-manager": "catalog:", "express-openapi-validator": "5.5.3", diff --git a/packages/@n8n/backend-services/src/index.ts b/packages/@n8n/backend-services/src/index.ts index f85a6a73697..f98349e0a04 100644 --- a/packages/@n8n/backend-services/src/index.ts +++ b/packages/@n8n/backend-services/src/index.ts @@ -3,6 +3,12 @@ export { UncacheableValueError } from './errors/cache-errors/uncacheable-value.e export { CacheService } from './services/cache/cache.service'; export { RedisClientService } from './services/redis-client.service'; export type { RedisClientType } from './services/redis.types'; +export { RoleCacheService, type RoleLoader } from './services/role-cache.service'; +export { + RoleDeletionCheckProxy, + type RoleDeletionChecker, +} from './services/role-deletion-check-proxy.service'; +export { RoleService } from './services/role.service'; export { UrlService } from './services/url.service'; export { classifyRestError, diff --git a/packages/cli/src/services/__tests__/role-cache.service.test.ts b/packages/@n8n/backend-services/src/services/__tests__/role-cache.service.test.ts similarity index 95% rename from packages/cli/src/services/__tests__/role-cache.service.test.ts rename to packages/@n8n/backend-services/src/services/__tests__/role-cache.service.test.ts index be5026a848d..846de6be148 100644 --- a/packages/cli/src/services/__tests__/role-cache.service.test.ts +++ b/packages/@n8n/backend-services/src/services/__tests__/role-cache.service.test.ts @@ -1,13 +1,11 @@ import { Logger } from '@n8n/backend-common'; -import { mockInstance } from '@n8n/backend-test-utils'; import { RoleRepository } from '@n8n/db'; -import { Container } from '@n8n/di'; import { staticRolesWithScope } from '@n8n/permissions'; import type { MockedFunction } from 'vitest'; import { mock } from 'vitest-mock-extended'; -import type { CacheService } from '@n8n/backend-services'; -import { RoleCacheService } from '@/services/role-cache.service'; +import { type CacheService } from '../cache/cache.service'; +import { RoleCacheService } from '../role-cache.service'; // Mock static function vi.mock('@n8n/permissions', async () => ({ @@ -17,11 +15,11 @@ vi.mock('@n8n/permissions', async () => ({ describe('RoleCacheService', () => { const cacheService = mock(); - const logger = mockInstance(Logger); - const roleRepository = mockInstance(RoleRepository); + const logger = mock(); + const roleRepository = mock(); const staticRolesMock = staticRolesWithScope as MockedFunction; - const roleCacheService = new RoleCacheService(cacheService, logger); + const roleCacheService = new RoleCacheService(cacheService, logger, roleRepository); const mockRoleScopeMap = { project: { @@ -49,7 +47,6 @@ describe('RoleCacheService', () => { beforeEach(() => { vi.clearAllMocks(); - vi.spyOn(Container, 'get').mockReturnValue(roleRepository); }); describe('getRolesWithAllScopes', () => { @@ -186,7 +183,6 @@ describe('RoleCacheService', () => { await roleCacheService.refreshCache(); - expect(Container.get).toHaveBeenCalledWith(RoleRepository); expect(roleRepository.findAll).toHaveBeenCalledTimes(1); expect(cacheService.set).toHaveBeenCalledWith( 'roles:scope-map', diff --git a/packages/cli/src/services/__tests__/role-deletion-check-proxy.service.test.ts b/packages/@n8n/backend-services/src/services/__tests__/role-deletion-check-proxy.service.test.ts similarity index 94% rename from packages/cli/src/services/__tests__/role-deletion-check-proxy.service.test.ts rename to packages/@n8n/backend-services/src/services/__tests__/role-deletion-check-proxy.service.test.ts index 8dfdf1cf4d1..3e65df50c39 100644 --- a/packages/cli/src/services/__tests__/role-deletion-check-proxy.service.test.ts +++ b/packages/@n8n/backend-services/src/services/__tests__/role-deletion-check-proxy.service.test.ts @@ -3,7 +3,7 @@ import { mock } from 'vitest-mock-extended'; import { RoleDeletionCheckProxy, type RoleDeletionChecker, -} from '@/services/role-deletion-check-proxy.service'; +} from '../role-deletion-check-proxy.service'; describe('RoleDeletionCheckProxy', () => { it('reports no blockers when no provider is registered', async () => { diff --git a/packages/cli/src/services/__tests__/role.service.addScopes.test.ts b/packages/@n8n/backend-services/src/services/__tests__/role.service.addScopes.test.ts similarity index 79% rename from packages/cli/src/services/__tests__/role.service.addScopes.test.ts rename to packages/@n8n/backend-services/src/services/__tests__/role.service.addScopes.test.ts index d18e7803215..ad75775394f 100644 --- a/packages/cli/src/services/__tests__/role.service.addScopes.test.ts +++ b/packages/@n8n/backend-services/src/services/__tests__/role.service.addScopes.test.ts @@ -1,23 +1,21 @@ import type { LicenseState } from '@n8n/backend-common'; import { Logger } from '@n8n/backend-common'; -import { EventService } from '@n8n/backend-services'; -import { mockInstance } from '@n8n/backend-test-utils'; +import { EventService } from '../../events/event.service'; +import { RoleCacheService } from '../role-cache.service'; +import { RoleDeletionCheckProxy } from '../role-deletion-check-proxy.service'; +import { RoleService } from '../role.service'; import type { CredentialsEntity, ScopesField, User } from '@n8n/db'; import { RoleRepository, ScopeRepository } from '@n8n/db'; import { mock } from 'vitest-mock-extended'; -import { RoleCacheService } from '@/services/role-cache.service'; -import { RoleDeletionCheckProxy } from '@/services/role-deletion-check-proxy.service'; -import { RoleService } from '@/services/role.service'; - describe('RoleService.addScopes', () => { const licenseState = mock(); - const roleRepository = mockInstance(RoleRepository); - const scopeRepository = mockInstance(ScopeRepository); - const roleCacheService = mockInstance(RoleCacheService); - const logger = mockInstance(Logger); - const roleDeletionCheckProxy = mockInstance(RoleDeletionCheckProxy); - const eventService = mockInstance(EventService); + const roleRepository = mock(); + const scopeRepository = mock(); + const roleCacheService = mock(); + const logger = mock(); + const roleDeletionCheckProxy = mock(); + const eventService = mock(); const roleService = new RoleService( licenseState, diff --git a/packages/cli/src/services/__tests__/role.service.assignments.test.ts b/packages/@n8n/backend-services/src/services/__tests__/role.service.assignments.test.ts similarity index 91% rename from packages/cli/src/services/__tests__/role.service.assignments.test.ts rename to packages/@n8n/backend-services/src/services/__tests__/role.service.assignments.test.ts index ca1352db4bd..2e769f7d3e7 100644 --- a/packages/cli/src/services/__tests__/role.service.assignments.test.ts +++ b/packages/@n8n/backend-services/src/services/__tests__/role.service.assignments.test.ts @@ -1,23 +1,22 @@ import type { LicenseState } from '@n8n/backend-common'; import { Logger } from '@n8n/backend-common'; -import { EventService } from '@n8n/backend-services'; -import { mockInstance } from '@n8n/backend-test-utils'; +import { EventService } from '../../events/event.service'; +import { RoleCacheService } from '../role-cache.service'; +import { RoleDeletionCheckProxy } from '../role-deletion-check-proxy.service'; +import { RoleService } from '../role.service'; import { RoleRepository, ScopeRepository } from '@n8n/db'; import { mock } from 'vitest-mock-extended'; import { NotFoundError } from '@n8n/errors'; -import { RoleCacheService } from '@/services/role-cache.service'; -import { RoleDeletionCheckProxy } from '@/services/role-deletion-check-proxy.service'; -import { RoleService } from '@/services/role.service'; describe('RoleService.getRoleAssignments and getRoleProjectMembers', () => { const licenseState = mock(); - const roleRepository = mockInstance(RoleRepository); - const scopeRepository = mockInstance(ScopeRepository); - const roleCacheService = mockInstance(RoleCacheService); - const logger = mockInstance(Logger); - const roleDeletionCheckProxy = mockInstance(RoleDeletionCheckProxy); - const eventService = mockInstance(EventService); + const roleRepository = mock(); + const scopeRepository = mock(); + const roleCacheService = mock(); + const logger = mock(); + const roleDeletionCheckProxy = mock(); + const eventService = mock(); const roleService = new RoleService( licenseState, diff --git a/packages/cli/src/services/__tests__/role.service.customRoles.test.ts b/packages/@n8n/backend-services/src/services/__tests__/role.service.customRoles.test.ts similarity index 92% rename from packages/cli/src/services/__tests__/role.service.customRoles.test.ts rename to packages/@n8n/backend-services/src/services/__tests__/role.service.customRoles.test.ts index 1022125bc78..8ef84d13f12 100644 --- a/packages/cli/src/services/__tests__/role.service.customRoles.test.ts +++ b/packages/@n8n/backend-services/src/services/__tests__/role.service.customRoles.test.ts @@ -1,26 +1,25 @@ import type { CreateRoleDto, UpdateRoleDto } from '@n8n/api-types'; import type { LicenseState } from '@n8n/backend-common'; import { Logger } from '@n8n/backend-common'; -import { EventService } from '@n8n/backend-services'; -import { mockInstance } from '@n8n/backend-test-utils'; +import { EventService } from '../../events/event.service'; +import { RoleCacheService } from '../role-cache.service'; +import { RoleDeletionCheckProxy } from '../role-deletion-check-proxy.service'; +import { RoleService } from '../role.service'; import type { Role, Scope as DBScope } from '@n8n/db'; import { RoleRepository, ScopeRepository } from '@n8n/db'; import { MANDATORY_INSTANCE_SCOPES } from '@n8n/permissions'; import { mock } from 'vitest-mock-extended'; import { BadRequestError } from '@n8n/errors'; -import { RoleCacheService } from '@/services/role-cache.service'; -import { RoleDeletionCheckProxy } from '@/services/role-deletion-check-proxy.service'; -import { RoleService } from '@/services/role.service'; describe('RoleService custom role scope whitelist', () => { const licenseState = mock(); - const roleRepository = mockInstance(RoleRepository); - const scopeRepository = mockInstance(ScopeRepository); - const roleCacheService = mockInstance(RoleCacheService); - const logger = mockInstance(Logger); - const roleDeletionCheckProxy = mockInstance(RoleDeletionCheckProxy); - const eventService = mockInstance(EventService); + const roleRepository = mock(); + const scopeRepository = mock(); + const roleCacheService = mock(); + const logger = mock(); + const roleDeletionCheckProxy = mock(); + const eventService = mock(); const roleService = new RoleService( licenseState, diff --git a/packages/cli/src/services/__tests__/role.service.rolesWithScope.test.ts b/packages/@n8n/backend-services/src/services/__tests__/role.service.rolesWithScope.test.ts similarity index 81% rename from packages/cli/src/services/__tests__/role.service.rolesWithScope.test.ts rename to packages/@n8n/backend-services/src/services/__tests__/role.service.rolesWithScope.test.ts index 262d302c780..b93a64795dc 100644 --- a/packages/cli/src/services/__tests__/role.service.rolesWithScope.test.ts +++ b/packages/@n8n/backend-services/src/services/__tests__/role.service.rolesWithScope.test.ts @@ -1,22 +1,20 @@ import type { LicenseState } from '@n8n/backend-common'; import { Logger } from '@n8n/backend-common'; -import { EventService } from '@n8n/backend-services'; -import { mockInstance } from '@n8n/backend-test-utils'; +import { EventService } from '../../events/event.service'; +import { RoleCacheService } from '../role-cache.service'; +import { RoleDeletionCheckProxy } from '../role-deletion-check-proxy.service'; +import { RoleService } from '../role.service'; import { RoleRepository, ScopeRepository } from '@n8n/db'; import { mock } from 'vitest-mock-extended'; -import { RoleCacheService } from '@/services/role-cache.service'; -import { RoleDeletionCheckProxy } from '@/services/role-deletion-check-proxy.service'; -import { RoleService } from '@/services/role.service'; - describe('RoleService.rolesWithScope', () => { const licenseState = mock(); - const roleRepository = mockInstance(RoleRepository); - const scopeRepository = mockInstance(ScopeRepository); - const roleCacheService = mockInstance(RoleCacheService); - const logger = mockInstance(Logger); - const roleDeletionCheckProxy = mockInstance(RoleDeletionCheckProxy); - const eventService = mockInstance(EventService); + const roleRepository = mock(); + const scopeRepository = mock(); + const roleCacheService = mock(); + const logger = mock(); + const roleDeletionCheckProxy = mock(); + const eventService = mock(); const roleService = new RoleService( licenseState, @@ -39,11 +37,9 @@ describe('RoleService.rolesWithScope', () => { const result = await roleService.rolesWithScope('project', 'project:read'); - expect(roleCacheService.getRolesWithAllScopes).toHaveBeenCalledWith( - 'project', - ['project:read'], - undefined, - ); + expect(roleCacheService.getRolesWithAllScopes).toHaveBeenCalledWith('project', [ + 'project:read', + ]); expect(result).toEqual(mockRoles); }); @@ -54,11 +50,7 @@ describe('RoleService.rolesWithScope', () => { const result = await roleService.rolesWithScope('project', inputScopes); - expect(roleCacheService.getRolesWithAllScopes).toHaveBeenCalledWith( - 'project', - inputScopes, - undefined, - ); + expect(roleCacheService.getRolesWithAllScopes).toHaveBeenCalledWith('project', inputScopes); expect(result).toEqual(mockRoles); }); @@ -68,7 +60,7 @@ describe('RoleService.rolesWithScope', () => { const result = await roleService.rolesWithScope('project', []); - expect(roleCacheService.getRolesWithAllScopes).toHaveBeenCalledWith('project', [], undefined); + expect(roleCacheService.getRolesWithAllScopes).toHaveBeenCalledWith('project', []); expect(result).toEqual([]); }); }); @@ -110,11 +102,10 @@ describe('RoleService.rolesWithScope', () => { 'project:update', ]); - expect(roleCacheService.getRolesWithAllScopes).toHaveBeenCalledWith( - 'project', - ['project:read', 'project:update'], - undefined, - ); + expect(roleCacheService.getRolesWithAllScopes).toHaveBeenCalledWith('project', [ + 'project:read', + 'project:update', + ]); expect(result).toEqual(mockRoles); }); }); @@ -130,24 +121,15 @@ describe('RoleService.rolesWithScope', () => { await roleService.rolesWithScope('project', 'project:delete'); expect(roleCacheService.getRolesWithAllScopes).toHaveBeenCalledTimes(3); - expect(roleCacheService.getRolesWithAllScopes).toHaveBeenNthCalledWith( - 1, - 'credential', - ['credential:read'], - undefined, - ); - expect(roleCacheService.getRolesWithAllScopes).toHaveBeenNthCalledWith( - 2, - 'workflow', - ['workflow:execute'], - undefined, - ); - expect(roleCacheService.getRolesWithAllScopes).toHaveBeenNthCalledWith( - 3, - 'project', - ['project:delete'], - undefined, - ); + expect(roleCacheService.getRolesWithAllScopes).toHaveBeenNthCalledWith(1, 'credential', [ + 'credential:read', + ]); + expect(roleCacheService.getRolesWithAllScopes).toHaveBeenNthCalledWith(2, 'workflow', [ + 'workflow:execute', + ]); + expect(roleCacheService.getRolesWithAllScopes).toHaveBeenNthCalledWith(3, 'project', [ + 'project:delete', + ]); }); it('should handle mixed scope arrays', async () => { @@ -161,11 +143,7 @@ describe('RoleService.rolesWithScope', () => { const result = await roleService.rolesWithScope('project', mixedScopes); - expect(roleCacheService.getRolesWithAllScopes).toHaveBeenCalledWith( - 'project', - mixedScopes, - undefined, - ); + expect(roleCacheService.getRolesWithAllScopes).toHaveBeenCalledWith('project', mixedScopes); expect(result).toEqual(mockRoles); }); }); diff --git a/packages/cli/src/services/role-cache.service.ts b/packages/@n8n/backend-services/src/services/role-cache.service.ts similarity index 86% rename from packages/cli/src/services/role-cache.service.ts rename to packages/@n8n/backend-services/src/services/role-cache.service.ts index 1aabf3dc95d..f47b936f070 100644 --- a/packages/cli/src/services/role-cache.service.ts +++ b/packages/@n8n/backend-services/src/services/role-cache.service.ts @@ -1,11 +1,10 @@ import { Logger } from '@n8n/backend-common'; import { Time } from '@n8n/constants'; import { RoleRepository } from '@n8n/db'; -import type { EntityManager } from '@n8n/db'; -import { Container, Service } from '@n8n/di'; +import { Service } from '@n8n/di'; import { staticRolesWithScope, type Scope } from '@n8n/permissions'; -import { CacheService } from '@n8n/backend-services'; +import { CacheService } from './cache/cache.service'; type RoleInfo = { scopes: string[]; // array of scope slugs @@ -29,6 +28,8 @@ interface RoleScopeMap { }; } +export type RoleLoader = () => ReturnType; + @Service() export class RoleCacheService { private static readonly CACHE_KEY = 'roles:scope-map'; @@ -37,15 +38,15 @@ export class RoleCacheService { constructor( private readonly cacheService: CacheService, private readonly logger: Logger, + private readonly roleRepository: RoleRepository, ) {} /** * Get all roles from database and build scope map */ - private async buildRoleScopeMap(trx?: EntityManager): Promise { + private async buildRoleScopeMap(loadRoles?: RoleLoader): Promise { try { - const roleRepository = Container.get(RoleRepository); - const roles = await roleRepository.findAll(trx); + const roles = await (loadRoles?.() ?? this.roleRepository.findAll()); const roleScopeMap: RoleScopeMap = {}; for (const role of roles) { @@ -68,13 +69,13 @@ export class RoleCacheService { async getRolesWithAllScopes( namespace: 'global' | 'project' | 'credential' | 'workflow' | 'secretsProviderConnection', requiredScopes: Scope[], - em?: EntityManager, + loadRoles?: RoleLoader, ): Promise { if (requiredScopes.length === 0) return []; // Get cached role map with refresh function const roleScopeMap = await this.cacheService.get(RoleCacheService.CACHE_KEY, { - refreshFn: async () => await this.buildRoleScopeMap(em), + refreshFn: async () => await this.buildRoleScopeMap(loadRoles), fallbackValue: undefined, }); diff --git a/packages/cli/src/services/role-deletion-check-proxy.service.ts b/packages/@n8n/backend-services/src/services/role-deletion-check-proxy.service.ts similarity index 100% rename from packages/cli/src/services/role-deletion-check-proxy.service.ts rename to packages/@n8n/backend-services/src/services/role-deletion-check-proxy.service.ts diff --git a/packages/cli/src/services/role.service.ts b/packages/@n8n/backend-services/src/services/role.service.ts similarity index 96% rename from packages/cli/src/services/role.service.ts rename to packages/@n8n/backend-services/src/services/role.service.ts index 5cbfe62fc2d..a9c2d1f9cca 100644 --- a/packages/cli/src/services/role.service.ts +++ b/packages/@n8n/backend-services/src/services/role.service.ts @@ -5,7 +5,6 @@ import type { } from '@n8n/api-types'; import { CreateRoleDto } from '@n8n/api-types'; import { LicenseState, Logger } from '@n8n/backend-common'; -import { EventService } from '@n8n/backend-services'; import { CredentialsEntity, SharedCredentials, @@ -19,16 +18,10 @@ import { Scope as DBScope, ScopeRepository, GLOBAL_ADMIN_ROLE, + isUniqueConstraintError, } from '@n8n/db'; -import type { EntityManager } from '@n8n/db'; import { Service } from '@n8n/di'; -import type { - Scope, - Role as RoleDTO, - AssignableProjectRole, - AssignableGlobalRole, - RoleNamespace, -} from '@n8n/permissions'; +import type { Scope, Role as RoleDTO, RoleNamespace } from '@n8n/permissions'; import { combineScopes, CUSTOM_ROLE_SCOPE_WHITELIST, @@ -43,10 +36,16 @@ import { import { UnexpectedError, UserError } from 'n8n-workflow'; import { BadRequestError, NotFoundError } from '@n8n/errors'; -import { isUniqueConstraintError } from '@/response-helper'; - -import { RoleCacheService } from './role-cache.service'; +import { RoleCacheService, type RoleLoader } from './role-cache.service'; import { RoleDeletionCheckProxy } from './role-deletion-check-proxy.service'; +import { EventService } from '../events/event.service'; + +declare module '../events/event.service' { + interface EventMap { + 'custom-role-deleted': { userId: string; roleSlug: string }; + 'custom-role-updated': { userId: string; roleSlug: string; scopes: string[] }; + } +} @Service() export class RoleService { @@ -458,16 +457,18 @@ export class RoleService { async rolesWithScope( namespace: RoleNamespace, scopes: Scope | Scope[], - trx?: EntityManager, + loadRoles?: RoleLoader, ): Promise { if (!Array.isArray(scopes)) { scopes = [scopes]; } // Get database roles from cache - return await this.roleCacheService.getRolesWithAllScopes(namespace, scopes, trx); + return loadRoles + ? await this.roleCacheService.getRolesWithAllScopes(namespace, scopes, loadRoles) + : await this.roleCacheService.getRolesWithAllScopes(namespace, scopes); } - isRoleLicensed(role: AssignableProjectRole | AssignableGlobalRole) { + isRoleLicensed(role: string) { // TODO: move this info into FrontendSettings if (!isBuiltInRole(role)) { diff --git a/packages/cli/src/commands/__tests__/start.test.ts b/packages/cli/src/commands/__tests__/start.test.ts index 958fbbb36ec..2a0c4212849 100644 --- a/packages/cli/src/commands/__tests__/start.test.ts +++ b/packages/cli/src/commands/__tests__/start.test.ts @@ -33,7 +33,7 @@ import { NodeTypes } from '@/node-types'; import { PostHogClient } from '@/posthog'; import { PollJobProvider } from '@/scheduling/poll-trigger-node/poll-job-provider'; import { JwtService } from '@/services/jwt.service'; -import { RoleCacheService } from '@/services/role-cache.service'; +import { RoleCacheService } from '@n8n/backend-services'; import { ShutdownService } from '@/shutdown/shutdown.service'; import { TaskRunnerModule } from '@/task-runners/task-runner-module'; diff --git a/packages/cli/src/commands/start.ts b/packages/cli/src/commands/start.ts index 3ef81108531..10ed4f6bc99 100644 --- a/packages/cli/src/commands/start.ts +++ b/packages/cli/src/commands/start.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ -import { EventService, UrlService } from '@n8n/backend-services'; +import { EventService, UrlService, RoleCacheService } from '@n8n/backend-services'; import { HTML_NONCE_PLACEHOLDER, LICENSE_FEATURES } from '@n8n/constants'; import { AuthRolesService, @@ -42,7 +42,7 @@ import { Server } from '@/server'; import { JwtService } from '@/services/jwt.service'; import { ExecutionsPruningService } from '@/services/pruning/executions-pruning.service'; import { WorkflowHistoryCompactionService } from '@/services/pruning/workflow-history-compaction.service'; -import { RoleCacheService } from '@/services/role-cache.service'; + import { WorkflowStatisticsRollupService } from '@/services/workflow-statistics-rollup.service'; import { WaitTracker } from '@/wait-tracker'; diff --git a/packages/cli/src/controllers/__tests__/role.controller.test.ts b/packages/cli/src/controllers/__tests__/role.controller.test.ts index 9850397d231..d37fb3fb99e 100644 --- a/packages/cli/src/controllers/__tests__/role.controller.test.ts +++ b/packages/cli/src/controllers/__tests__/role.controller.test.ts @@ -1,11 +1,10 @@ -import type { EventService } from '@n8n/backend-services'; +import { type EventService, type RoleService } from '@n8n/backend-services'; import type { AuthenticatedRequest, Project } from '@n8n/db'; import type { Role } from '@n8n/permissions'; import { mock } from 'vitest-mock-extended'; import { NotFoundError } from '@n8n/errors'; import type { ProjectService } from '@/services/project.service.ee'; -import type { RoleService } from '@/services/role.service'; import { RoleController } from '../role.controller'; diff --git a/packages/cli/src/controllers/role.controller.ts b/packages/cli/src/controllers/role.controller.ts index d9a767559c0..da65d1e404c 100644 --- a/packages/cli/src/controllers/role.controller.ts +++ b/packages/cli/src/controllers/role.controller.ts @@ -13,7 +13,7 @@ import type { RoleMembersResponse, RoleProjectMembersResponse, } from '@n8n/api-types'; -import { EventService } from '@n8n/backend-services'; +import { EventService, RoleService } from '@n8n/backend-services'; import { LICENSE_FEATURES } from '@n8n/constants'; import { AuthenticatedRequest } from '@n8n/db'; import { @@ -33,7 +33,6 @@ import { Role as RoleDTO } from '@n8n/permissions'; import { NotFoundError } from '@n8n/errors'; import { ProjectService } from '@/services/project.service.ee'; import { assertCanManageRoleType, canReassignUsers } from '@/services/role-authorization'; -import { RoleService } from '@/services/role.service'; @RestController('/roles') export class RoleController { diff --git a/packages/cli/src/credentials/__tests__/credentials.service.ee.test.ts b/packages/cli/src/credentials/__tests__/credentials.service.ee.test.ts index 7dd2e75d3ac..4d41b67a068 100644 --- a/packages/cli/src/credentials/__tests__/credentials.service.ee.test.ts +++ b/packages/cli/src/credentials/__tests__/credentials.service.ee.test.ts @@ -16,7 +16,7 @@ import type { ExternalSecretsConfig } from '@/modules/external-secrets.ee/extern import type { SecretsProviderAccessCheckService } from '@/modules/external-secrets.ee/secret-provider-access-check.service.ee'; import type { OwnershipService } from '@/services/ownership.service'; import type { ProjectService } from '@/services/project.service.ee'; -import type { RoleService } from '@/services/role.service'; +import type { RoleService } from '@n8n/backend-services'; describe('EnterpriseCredentialsService', () => { const sharedCredentialsRepository = mock(); diff --git a/packages/cli/src/credentials/__tests__/credentials.service.test.ts b/packages/cli/src/credentials/__tests__/credentials.service.test.ts index fe0e033e6f7..014b9305572 100644 --- a/packages/cli/src/credentials/__tests__/credentials.service.test.ts +++ b/packages/cli/src/credentials/__tests__/credentials.service.test.ts @@ -2,7 +2,7 @@ import { CredentialDescriptionsService } from '@/credentials/credential-descript import type { PostHogClient } from '@/posthog'; import { CREDENTIAL_DESCRIPTION_MAX_LENGTH } from '@n8n/api-types'; import type { Logger } from '@n8n/backend-common'; -import type { EventService } from '@n8n/backend-services'; +import { type EventService, type RoleService } from '@n8n/backend-services'; import type { CredentialsRepository, ICredentialsDb, @@ -65,7 +65,6 @@ import * as checkAccess from '@/permissions.ee/check-access'; import type { CredentialsTester } from '@/services/credentials-tester.service'; import type { OwnershipService } from '@/services/ownership.service'; import type { ProjectService } from '@/services/project.service.ee'; -import type { RoleService } from '@/services/role.service'; import { mockExistingCredential } from './credentials.test-data'; diff --git a/packages/cli/src/credentials/credentials-finder.service.ts b/packages/cli/src/credentials/credentials-finder.service.ts index f79b3915584..a948d78f15f 100644 --- a/packages/cli/src/credentials/credentials-finder.service.ts +++ b/packages/cli/src/credentials/credentials-finder.service.ts @@ -11,7 +11,7 @@ import type { CredentialSharingRole, ProjectRole, Scope } from '@n8n/permissions import type { EntityManager, FindOptionsWhere } from '@n8n/typeorm'; import { In } from '@n8n/typeorm'; -import { RoleService } from '@/services/role.service'; +import { RoleService } from '@n8n/backend-services'; /** * The credential scopes an instance role can hold without being allowed to use a diff --git a/packages/cli/src/credentials/credentials.service.ee.ts b/packages/cli/src/credentials/credentials.service.ee.ts index 31442cd0525..48954b5ad17 100644 --- a/packages/cli/src/credentials/credentials.service.ee.ts +++ b/packages/cli/src/credentials/credentials.service.ee.ts @@ -18,7 +18,7 @@ import { ExternalSecretsConfig } from '@/modules/external-secrets.ee/external-se import { SecretsProviderAccessCheckService } from '@/modules/external-secrets.ee/secret-provider-access-check.service.ee'; import { OwnershipService } from '@/services/ownership.service'; import { ProjectService } from '@/services/project.service.ee'; -import { RoleService } from '@/services/role.service'; +import { RoleService } from '@n8n/backend-services'; import { CredentialConnectionStatusProxy } from './credential-connection-status-proxy'; import { CredentialsFinderService } from './credentials-finder.service'; diff --git a/packages/cli/src/credentials/credentials.service.ts b/packages/cli/src/credentials/credentials.service.ts index c36ba6e9fb9..252d349dba0 100644 --- a/packages/cli/src/credentials/credentials.service.ts +++ b/packages/cli/src/credentials/credentials.service.ts @@ -1,6 +1,6 @@ import type { CreateCredentialDto, CredentialConnectionStatus } from '@n8n/api-types'; import { Logger } from '@n8n/backend-common'; -import { EventService } from '@n8n/backend-services'; +import { EventService, RoleService } from '@n8n/backend-services'; import { Time } from '@n8n/constants'; import { Project, @@ -76,7 +76,6 @@ import type { CredentialRequest, ListQuery } from '@/requests'; import { CredentialsTester } from '@/services/credentials-tester.service'; import { OwnershipService } from '@/services/ownership.service'; import { ProjectService } from '@/services/project.service.ee'; -import { RoleService } from '@/services/role.service'; import { CredentialConnectionStatusProxy } from './credential-connection-status-proxy'; import { CredentialDescriptionsService } from './credential-descriptions.service'; diff --git a/packages/cli/src/executions/__tests__/execution-list.service.test.ts b/packages/cli/src/executions/__tests__/execution-list.service.test.ts index 5fff8dbace9..3f1892aa75b 100644 --- a/packages/cli/src/executions/__tests__/execution-list.service.test.ts +++ b/packages/cli/src/executions/__tests__/execution-list.service.test.ts @@ -3,7 +3,7 @@ import { Container } from '@n8n/di'; import { mock } from 'vitest-mock-extended'; import type { EngineDataPlaneProxyService } from '@/services/engine-data-plane-proxy.service'; -import type { RoleService } from '@/services/role.service'; +import type { RoleService } from '@n8n/backend-services'; import type { WorkflowSharingService } from '@/workflows/workflow-sharing.service'; import type { ExecutionCursor } from '../execution-cursor'; diff --git a/packages/cli/src/executions/execution-list.service.ts b/packages/cli/src/executions/execution-list.service.ts index d2fc9933be0..74d4100f2a8 100644 --- a/packages/cli/src/executions/execution-list.service.ts +++ b/packages/cli/src/executions/execution-list.service.ts @@ -3,7 +3,7 @@ import { Container, Service } from '@n8n/di'; import type { Scope } from '@n8n/permissions'; import { EngineDataPlaneProxyService } from '@/services/engine-data-plane-proxy.service'; -import { RoleService } from '@/services/role.service'; +import { RoleService } from '@n8n/backend-services'; import { WorkflowSharingService } from '@/workflows/workflow-sharing.service'; import { hasPosition, type ExecutionCursor } from './execution-cursor'; diff --git a/packages/cli/src/modules/agents/__tests__/agent-execution-update-broadcaster.test.ts b/packages/cli/src/modules/agents/__tests__/agent-execution-update-broadcaster.test.ts index 18ecd56f907..8e4ea918f55 100644 --- a/packages/cli/src/modules/agents/__tests__/agent-execution-update-broadcaster.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agent-execution-update-broadcaster.test.ts @@ -4,7 +4,7 @@ import type { InstanceSettings } from 'n8n-core'; import { mock } from 'vitest-mock-extended'; import type { Push } from '@/push'; -import type { RoleService } from '@/services/role.service'; +import type { RoleService } from '@n8n/backend-services'; import type { Publisher } from '@/scaling/pubsub/publisher.service'; import { AgentPushRecipientsService } from '../agent-push-recipients.service'; diff --git a/packages/cli/src/modules/agents/__tests__/agent-update-broadcaster.test.ts b/packages/cli/src/modules/agents/__tests__/agent-update-broadcaster.test.ts index 3e506130991..c29b9bd01c8 100644 --- a/packages/cli/src/modules/agents/__tests__/agent-update-broadcaster.test.ts +++ b/packages/cli/src/modules/agents/__tests__/agent-update-broadcaster.test.ts @@ -5,7 +5,7 @@ import { mock } from 'vitest-mock-extended'; import type { Push } from '@/push'; import type { Publisher } from '@/scaling/pubsub/publisher.service'; -import type { RoleService } from '@/services/role.service'; +import type { RoleService } from '@n8n/backend-services'; import { AgentPushRecipientsService } from '../agent-push-recipients.service'; import { AgentUpdateBroadcaster } from '../agent-update-broadcaster'; diff --git a/packages/cli/src/modules/agents/agent-push-recipients.service.ts b/packages/cli/src/modules/agents/agent-push-recipients.service.ts index ba97eac55e1..c25ab6b130d 100644 --- a/packages/cli/src/modules/agents/agent-push-recipients.service.ts +++ b/packages/cli/src/modules/agents/agent-push-recipients.service.ts @@ -1,7 +1,7 @@ import { UserRepository } from '@n8n/db'; import { Service } from '@n8n/di'; -import { RoleService } from '@/services/role.service'; +import { RoleService } from '@n8n/backend-services'; @Service() export class AgentPushRecipientsService { diff --git a/packages/cli/src/modules/data-table/__tests__/data-table.service.test.ts b/packages/cli/src/modules/data-table/__tests__/data-table.service.test.ts index 6132435fdcb..e847c82b344 100644 --- a/packages/cli/src/modules/data-table/__tests__/data-table.service.test.ts +++ b/packages/cli/src/modules/data-table/__tests__/data-table.service.test.ts @@ -1,6 +1,6 @@ import type { RenameDataTableColumnDto } from '@n8n/api-types'; import { Logger } from '@n8n/backend-common'; -import { EventService } from '@n8n/backend-services'; +import { EventService, RoleService } from '@n8n/backend-services'; import { mockInstance, testModules } from '@n8n/backend-test-utils'; import { ProjectRelationRepository, ProjectRepository, type User } from '@n8n/db'; import { In } from '@n8n/typeorm'; @@ -21,7 +21,6 @@ import { DataTableNotFoundError } from '../errors/data-table-not-found.error'; import { DataTableValidationError } from '../errors/data-table-validation.error'; import { ProjectNotFoundError, ProjectService } from '@/services/project.service.ee'; -import { RoleService } from '@/services/role.service'; describe('DataTableService', () => { let dataTableService: DataTableService; diff --git a/packages/cli/src/modules/data-table/data-table-aggregate.service.ts b/packages/cli/src/modules/data-table/data-table-aggregate.service.ts index c54fe2c1964..b6e02e875ed 100644 --- a/packages/cli/src/modules/data-table/data-table-aggregate.service.ts +++ b/packages/cli/src/modules/data-table/data-table-aggregate.service.ts @@ -4,7 +4,7 @@ import { ProjectRelationRepository, User } from '@n8n/db'; import { Service } from '@n8n/di'; import { hasGlobalScope } from '@n8n/permissions'; -import { RoleService } from '@/services/role.service'; +import { RoleService } from '@n8n/backend-services'; import { DataTableRepository } from './data-table.repository'; diff --git a/packages/cli/src/modules/data-table/data-table.service.ts b/packages/cli/src/modules/data-table/data-table.service.ts index 56caf40832c..4efe1b86fdb 100644 --- a/packages/cli/src/modules/data-table/data-table.service.ts +++ b/packages/cli/src/modules/data-table/data-table.service.ts @@ -11,7 +11,7 @@ import type { UpdateDataTableRowDto, } from '@n8n/api-types'; import { Logger } from '@n8n/backend-common'; -import { EventService } from '@n8n/backend-services'; +import { EventService, RoleService } from '@n8n/backend-services'; import { ProjectRelationRepository, ProjectRepository, type User } from '@n8n/db'; import { Service } from '@n8n/di'; import { hasGlobalScope, type Scope } from '@n8n/permissions'; @@ -48,7 +48,6 @@ import { DataTableValidationError } from './errors/data-table-validation.error'; import { normalizeRows } from './utils/sql-utils'; import { ProjectNotFoundError, ProjectService } from '@/services/project.service.ee'; -import { RoleService } from '@/services/role.service'; @Service() export class DataTableService { diff --git a/packages/cli/src/modules/dynamic-credentials.ee/services/__tests__/credential-connection-status.service.test.ts b/packages/cli/src/modules/dynamic-credentials.ee/services/__tests__/credential-connection-status.service.test.ts index 8cb5cdbc191..bf9705f1139 100644 --- a/packages/cli/src/modules/dynamic-credentials.ee/services/__tests__/credential-connection-status.service.test.ts +++ b/packages/cli/src/modules/dynamic-credentials.ee/services/__tests__/credential-connection-status.service.test.ts @@ -1,6 +1,7 @@ import type { Logger } from '@n8n/backend-common'; import type { ProjectRelationRepository, + RoleRepository, SharedCredentialsRepository, User, UserRepository, @@ -9,7 +10,7 @@ import type { EntityManager } from '@n8n/typeorm'; import type { Cipher } from 'n8n-core'; import { mock } from 'vitest-mock-extended'; -import type { RoleService } from '@/services/role.service'; +import type { RoleService } from '@n8n/backend-services'; import { SYSTEM_RESOLVER_ID } from '../../constants'; import { DynamicCredentialUserEntry } from '../../database/entities/dynamic-credential-user-entry'; @@ -37,6 +38,7 @@ describe('CredentialConnectionStatusService', () => { const cipher = mock(); const logger = mock(); const em = mock(); + const roleRepository = mock(); const service = new CredentialConnectionStatusService( repository, @@ -46,6 +48,7 @@ describe('CredentialConnectionStatusService', () => { projectRelationRepository, cipher, logger, + roleRepository, ); const CRED_ID = 'cred-1'; @@ -202,7 +205,7 @@ describe('CredentialConnectionStatusService', () => { expect(roleService.rolesWithScope).toHaveBeenCalledWith( 'credential', 'credential:connect', - em, + expect.any(Function), ); expect(sharedCredentialsRepository.findPairsWithCredentialAccess).toHaveBeenCalledWith( [{ credentialId: CRED_ID, userId: 'sharee-1' }], diff --git a/packages/cli/src/modules/dynamic-credentials.ee/services/credential-connection-status.service.ts b/packages/cli/src/modules/dynamic-credentials.ee/services/credential-connection-status.service.ts index 0654a0f1ecb..6b2b467641e 100644 --- a/packages/cli/src/modules/dynamic-credentials.ee/services/credential-connection-status.service.ts +++ b/packages/cli/src/modules/dynamic-credentials.ee/services/credential-connection-status.service.ts @@ -3,6 +3,7 @@ import { CredentialsEntity, In, ProjectRelationRepository, + RoleRepository, SharedCredentialsRepository, UserRepository, type User, @@ -18,7 +19,7 @@ import type { UserConnection, } from '@/credentials/credential-connection-status-provider.interface'; import { extractAccountIdentifierFromData } from '@/oauth/account-identifier'; -import { RoleService } from '@/services/role.service'; +import { RoleService } from '@n8n/backend-services'; import { SYSTEM_RESOLVER_ID } from '../constants'; import { DynamicCredentialUserEntry } from '../database/entities/dynamic-credential-user-entry'; @@ -54,6 +55,7 @@ export class CredentialConnectionStatusService implements ICredentialConnectionS private readonly projectRelationRepository: ProjectRelationRepository, private readonly cipher: Cipher, private readonly logger: Logger, + private readonly roleRepository: RoleRepository, ) {} async findMyConnections( @@ -214,7 +216,7 @@ export class CredentialConnectionStatusService implements ICredentialConnectionS const validCredRoles = await this.roleService.rolesWithScope( 'credential', CREDENTIAL_RETAIN_SCOPE, - em, + async () => await this.roleRepository.findAll(em), ); return await this.sharedCredentialsRepository.findPairsWithCredentialAccess( pairsToCheck, diff --git a/packages/cli/src/modules/external-secrets.ee/__tests__/secret-provider-access-check.service.ee.test.ts b/packages/cli/src/modules/external-secrets.ee/__tests__/secret-provider-access-check.service.ee.test.ts index 67086e15539..588a09972f5 100644 --- a/packages/cli/src/modules/external-secrets.ee/__tests__/secret-provider-access-check.service.ee.test.ts +++ b/packages/cli/src/modules/external-secrets.ee/__tests__/secret-provider-access-check.service.ee.test.ts @@ -8,7 +8,7 @@ import { mock } from 'vitest-mock-extended'; import { ForbiddenError, NotFoundError } from '@n8n/errors'; import type { ProjectService } from '@/services/project.service.ee'; -import type { RoleService } from '@/services/role.service'; +import type { RoleService } from '@n8n/backend-services'; import { SecretsProviderAccessCheckService } from '../secret-provider-access-check.service.ee'; diff --git a/packages/cli/src/modules/external-secrets.ee/external-secrets-settings.service.ee.ts b/packages/cli/src/modules/external-secrets.ee/external-secrets-settings.service.ee.ts index 97a5ac579ad..a8fa5b5fe3f 100644 --- a/packages/cli/src/modules/external-secrets.ee/external-secrets-settings.service.ee.ts +++ b/packages/cli/src/modules/external-secrets.ee/external-secrets-settings.service.ee.ts @@ -2,7 +2,7 @@ import { SettingsRepository } from '@n8n/db'; import { Service } from '@n8n/di'; import { EXTERNAL_SECRETS_SYSTEM_ROLES_ENABLED_SETTING } from '@n8n/permissions'; -import { RoleService } from '@/services/role.service'; +import { RoleService } from '@n8n/backend-services'; @Service() export class ExternalSecretsSettingsService { diff --git a/packages/cli/src/modules/external-secrets.ee/secret-provider-access-check.service.ee.ts b/packages/cli/src/modules/external-secrets.ee/secret-provider-access-check.service.ee.ts index 59f96075132..0ab0912b80d 100644 --- a/packages/cli/src/modules/external-secrets.ee/secret-provider-access-check.service.ee.ts +++ b/packages/cli/src/modules/external-secrets.ee/secret-provider-access-check.service.ee.ts @@ -9,7 +9,7 @@ import { combineScopes, getAuthPrincipalScopes, hasGlobalScope } from '@n8n/perm import { ForbiddenError, NotFoundError } from '@n8n/errors'; import { ProjectService } from '@/services/project.service.ee'; -import { RoleService } from '@/services/role.service'; +import { RoleService } from '@n8n/backend-services'; @Service() export class SecretsProviderAccessCheckService { diff --git a/packages/cli/src/modules/instance-ai/__tests__/instance-ai.adapter.node-usage.test.ts b/packages/cli/src/modules/instance-ai/__tests__/instance-ai.adapter.node-usage.test.ts index 65d323163da..e113db3ddc5 100644 --- a/packages/cli/src/modules/instance-ai/__tests__/instance-ai.adapter.node-usage.test.ts +++ b/packages/cli/src/modules/instance-ai/__tests__/instance-ai.adapter.node-usage.test.ts @@ -29,7 +29,7 @@ vi.mock('@n8n/ai-utilities', () => ({ import type { Logger } from '@n8n/backend-common'; import { INSTANCE_AI_NODE_USAGE_FLAG } from '@n8n/api-types'; import type { OutboundHttp } from '@n8n/backend-network'; -import type { EventService } from '@n8n/backend-services'; +import { type EventService, type RoleService } from '@n8n/backend-services'; import type { GlobalConfig } from '@n8n/config'; import type { AiBuilderTemporaryWorkflowRepository, @@ -63,7 +63,7 @@ import type { FolderService } from '@/services/folder.service'; import type { InstanceWriteAccessService } from '@/services/instance-write-access.service'; import { NodeResourceExplorerService } from '@/services/node-resource-explorer.service'; import type { ProjectService } from '@/services/project.service.ee'; -import type { RoleService } from '@/services/role.service'; + import type { TagService } from '@/services/tag.service'; import type { Telemetry } from '@/telemetry'; import type { WorkflowRunner } from '@/workflow-runner'; diff --git a/packages/cli/src/modules/instance-ai/__tests__/instance-ai.adapter.service.security.test.ts b/packages/cli/src/modules/instance-ai/__tests__/instance-ai.adapter.service.security.test.ts index 4d7fd33f1f0..bcf5f41fab7 100644 --- a/packages/cli/src/modules/instance-ai/__tests__/instance-ai.adapter.service.security.test.ts +++ b/packages/cli/src/modules/instance-ai/__tests__/instance-ai.adapter.service.security.test.ts @@ -27,7 +27,7 @@ vi.mock('@n8n/instance-ai', async () => { }); import type { Logger } from '@n8n/backend-common'; -import type { EventService } from '@n8n/backend-services'; +import { type EventService, type RoleService } from '@n8n/backend-services'; import type { GlobalConfig } from '@n8n/config'; import { GLOBAL_MEMBER_ROLE } from '@n8n/db'; import { Container } from '@n8n/di'; @@ -65,7 +65,7 @@ import type { DataTableService } from '@/modules/data-table/data-table.service'; import type { InstanceWriteAccessService } from '@/services/instance-write-access.service'; import type { NodeTypes } from '@/node-types'; import type { PolicyEnforcementService } from '@/policy/policy-enforcement.service'; -import type { RoleService } from '@/services/role.service'; + import type { OutboundHttp } from '@n8n/backend-network'; import type { AiGatewayService } from '@/services/ai-gateway.service'; import type { Telemetry } from '@/telemetry'; diff --git a/packages/cli/src/modules/instance-ai/__tests__/instance-ai.adapter.service.test.ts b/packages/cli/src/modules/instance-ai/__tests__/instance-ai.adapter.service.test.ts index 1831bcffa22..f99b68db188 100644 --- a/packages/cli/src/modules/instance-ai/__tests__/instance-ai.adapter.service.test.ts +++ b/packages/cli/src/modules/instance-ai/__tests__/instance-ai.adapter.service.test.ts @@ -1827,7 +1827,7 @@ import { } from '@n8n/errors'; import type { License } from '@/license'; import type { AiPreferenceService } from '@/services/ai-preference.service'; -import type { RoleService } from '@/services/role.service'; +import type { RoleService } from '@n8n/backend-services'; import type { OutboundHttp } from '@n8n/backend-network'; import { ModuleRegistry } from '@n8n/backend-common'; diff --git a/packages/cli/src/modules/instance-ai/instance-ai.adapter.service.ts b/packages/cli/src/modules/instance-ai/instance-ai.adapter.service.ts index f764ea90fe6..256163f5a93 100644 --- a/packages/cli/src/modules/instance-ai/instance-ai.adapter.service.ts +++ b/packages/cli/src/modules/instance-ai/instance-ai.adapter.service.ts @@ -26,7 +26,7 @@ import { import type { AiGatewayConfigDto } from '@n8n/api-types'; import { LicenseState, Logger, ModuleRegistry } from '@n8n/backend-common'; import { OutboundHttp } from '@n8n/backend-network'; -import { EventService } from '@n8n/backend-services'; +import { EventService, RoleService } from '@n8n/backend-services'; import { GlobalConfig } from '@n8n/config'; import { Time, TOOL_EXECUTOR_NODE_NAME } from '@n8n/constants'; import type { User, ExecutionSummaries, EvaluationConfig } from '@n8n/db'; @@ -192,7 +192,7 @@ import { FolderService } from '@/services/folder.service'; import { InstanceWriteAccessService } from '@/services/instance-write-access.service'; import { NodeResourceExplorerService } from '@/services/node-resource-explorer.service'; import { ProjectService } from '@/services/project.service.ee'; -import { RoleService } from '@/services/role.service'; + import { TagService } from '@/services/tag.service'; import { Telemetry } from '@/telemetry'; import { resolveBuiltinNodeDefinitionDirs } from '@/utils/node-definition-dirs'; diff --git a/packages/cli/src/modules/mcp/__tests__/get-workflow-details.parent-folder.integration.test.ts b/packages/cli/src/modules/mcp/__tests__/get-workflow-details.parent-folder.integration.test.ts index d5e90ee6ff7..c7014a8a6eb 100644 --- a/packages/cli/src/modules/mcp/__tests__/get-workflow-details.parent-folder.integration.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/get-workflow-details.parent-folder.integration.test.ts @@ -11,7 +11,7 @@ import { mock } from 'vitest-mock-extended'; import type { CredentialsService } from '@/credentials/credentials.service'; import { ProjectService } from '@/services/project.service.ee'; -import { RoleService } from '@/services/role.service'; +import { RoleService } from '@n8n/backend-services'; import { WorkflowFinderService } from '@/workflows/workflow-finder.service'; import { createFolder } from '@test-integration/db/folders'; import { createTag } from '@test-integration/db/tags'; diff --git a/packages/cli/src/modules/mcp/__tests__/get-workflow-details.tool.test.ts b/packages/cli/src/modules/mcp/__tests__/get-workflow-details.tool.test.ts index 567604be2b8..393f910aea9 100644 --- a/packages/cli/src/modules/mcp/__tests__/get-workflow-details.tool.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/get-workflow-details.tool.test.ts @@ -6,7 +6,7 @@ import { mock } from 'vitest-mock-extended'; import { CredentialsService } from '@/credentials/credentials.service'; import { ProjectService } from '@/services/project.service.ee'; -import { RoleService } from '@/services/role.service'; +import { RoleService } from '@n8n/backend-services'; import { Telemetry } from '@/telemetry'; import { WorkflowFinderService } from '@/workflows/workflow-finder.service'; diff --git a/packages/cli/src/modules/mcp/__tests__/install-community-node.registration.test.ts b/packages/cli/src/modules/mcp/__tests__/install-community-node.registration.test.ts index fd4125d6668..61fa1ee5d82 100644 --- a/packages/cli/src/modules/mcp/__tests__/install-community-node.registration.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/install-community-node.registration.test.ts @@ -1,5 +1,5 @@ import { LicenseState, ModuleRegistry } from '@n8n/backend-common'; -import { EventService, UrlService } from '@n8n/backend-services'; +import { EventService, UrlService, RoleService } from '@n8n/backend-services'; import { mockInstance, mockLogger } from '@n8n/backend-test-utils'; import { ExecutionsConfig, GlobalConfig, WorkflowsConfig } from '@n8n/config'; import { ExecutionRepository, ProjectRepository, SharedWorkflowRepository, User } from '@n8n/db'; @@ -21,7 +21,7 @@ import { FolderFinderService } from '@/services/folder-finder.service'; import { FolderService } from '@/services/folder.service'; import { NodeResourceExplorerService } from '@/services/node-resource-explorer.service'; import { ProjectService } from '@/services/project.service.ee'; -import { RoleService } from '@/services/role.service'; + import { TagService } from '@/services/tag.service'; import { Telemetry } from '@/telemetry'; import { WorkflowRunner } from '@/workflow-runner'; diff --git a/packages/cli/src/modules/mcp/__tests__/mcp-scopes.test.ts b/packages/cli/src/modules/mcp/__tests__/mcp-scopes.test.ts index 3a7e3144b30..1ba8e315d2d 100644 --- a/packages/cli/src/modules/mcp/__tests__/mcp-scopes.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/mcp-scopes.test.ts @@ -1,6 +1,6 @@ import { INSTANCE_ACTIVITY_CONTEXT_FLAG } from '@n8n/api-types'; import { LicenseState, ModuleRegistry } from '@n8n/backend-common'; -import { EventService, UrlService } from '@n8n/backend-services'; +import { EventService, UrlService, RoleService } from '@n8n/backend-services'; import { mockInstance, mockLogger } from '@n8n/backend-test-utils'; import { EndpointsConfig, ExecutionsConfig, GlobalConfig, WorkflowsConfig } from '@n8n/config'; import { @@ -49,7 +49,7 @@ import { FolderFinderService } from '@/services/folder-finder.service'; import { FolderService } from '@/services/folder.service'; import { NodeResourceExplorerService } from '@/services/node-resource-explorer.service'; import { ProjectService } from '@/services/project.service.ee'; -import { RoleService } from '@/services/role.service'; + import { TagService } from '@/services/tag.service'; import { Telemetry } from '@/telemetry'; import { WorkflowRunner } from '@/workflow-runner'; diff --git a/packages/cli/src/modules/mcp/__tests__/mcp.service.test.ts b/packages/cli/src/modules/mcp/__tests__/mcp.service.test.ts index c9757f7bdbe..f9f914b13c9 100644 --- a/packages/cli/src/modules/mcp/__tests__/mcp.service.test.ts +++ b/packages/cli/src/modules/mcp/__tests__/mcp.service.test.ts @@ -9,7 +9,7 @@ import { INSTANCE_ACTIVITY_CONTEXT_FLAG, } from '@n8n/api-types'; import { LicenseState, ModuleRegistry, type Logger } from '@n8n/backend-common'; -import { EventService, UrlService } from '@n8n/backend-services'; +import { EventService, UrlService, RoleService } from '@n8n/backend-services'; import { mockInstance, mockLogger } from '@n8n/backend-test-utils'; import { EndpointsConfig, ExecutionsConfig, GlobalConfig, WorkflowsConfig } from '@n8n/config'; import { @@ -42,7 +42,7 @@ import { FolderFinderService } from '@/services/folder-finder.service'; import { FolderService } from '@/services/folder.service'; import { NodeResourceExplorerService } from '@/services/node-resource-explorer.service'; import { ProjectService } from '@/services/project.service.ee'; -import { RoleService } from '@/services/role.service'; + import { TagService } from '@/services/tag.service'; import { Telemetry } from '@/telemetry'; import { WorkflowRunner } from '@/workflow-runner'; diff --git a/packages/cli/src/modules/mcp/mcp.service.ts b/packages/cli/src/modules/mcp/mcp.service.ts index 9180c974f51..6fd0320030a 100644 --- a/packages/cli/src/modules/mcp/mcp.service.ts +++ b/packages/cli/src/modules/mcp/mcp.service.ts @@ -7,7 +7,7 @@ import { INSTANCE_ACTIVITY_CONTEXT_FLAG, } from '@n8n/api-types'; import { LicenseState, Logger, ModuleRegistry } from '@n8n/backend-common'; -import { EventService, UrlService } from '@n8n/backend-services'; +import { EventService, UrlService, RoleService } from '@n8n/backend-services'; import { ExecutionsConfig, GlobalConfig, WorkflowsConfig } from '@n8n/config'; import { ExecutionRepository, ProjectRepository, SharedWorkflowRepository, User } from '@n8n/db'; import { Container, Service } from '@n8n/di'; @@ -40,7 +40,7 @@ import { FolderFinderService } from '@/services/folder-finder.service'; import { FolderService } from '@/services/folder.service'; import { NodeResourceExplorerService } from '@/services/node-resource-explorer.service'; import { ProjectService } from '@/services/project.service.ee'; -import { RoleService } from '@/services/role.service'; + import { TagService } from '@/services/tag.service'; import { Telemetry } from '@/telemetry'; import { WorkflowRunner } from '@/workflow-runner'; diff --git a/packages/cli/src/modules/mcp/tools/get-workflow-details.tool.ts b/packages/cli/src/modules/mcp/tools/get-workflow-details.tool.ts index 8c5dce45d77..06d20ed90ce 100644 --- a/packages/cli/src/modules/mcp/tools/get-workflow-details.tool.ts +++ b/packages/cli/src/modules/mcp/tools/get-workflow-details.tool.ts @@ -4,7 +4,7 @@ import z from 'zod'; import type { CredentialsService } from '@/credentials/credentials.service'; import type { ProjectService } from '@/services/project.service.ee'; -import type { RoleService } from '@/services/role.service'; +import type { RoleService } from '@n8n/backend-services'; import type { Telemetry } from '@/telemetry'; import type { WorkflowFinderService } from '@/workflows/workflow-finder.service'; diff --git a/packages/cli/src/modules/provisioning.ee/provisioning.module.ts b/packages/cli/src/modules/provisioning.ee/provisioning.module.ts index 14e51c430a3..9d71047d909 100644 --- a/packages/cli/src/modules/provisioning.ee/provisioning.module.ts +++ b/packages/cli/src/modules/provisioning.ee/provisioning.module.ts @@ -15,9 +15,7 @@ export class ProvisioningModule implements ModuleInterface { // Register the role-deletion checker so the core `RoleService` blocks // deleting a role still targeted by a mapping rule, without importing // this module. - const { RoleDeletionCheckProxy } = await import( - '@/services/role-deletion-check-proxy.service.js' - ); + const { RoleDeletionCheckProxy } = await import('@n8n/backend-services'); const { ProvisioningRoleDeletionChecker } = await import('./role-deletion-checker.ee.js'); Container.get(RoleDeletionCheckProxy).registerProvider( Container.get(ProvisioningRoleDeletionChecker), diff --git a/packages/cli/src/modules/provisioning.ee/role-deletion-checker.ee.ts b/packages/cli/src/modules/provisioning.ee/role-deletion-checker.ee.ts index 298fe22954c..3b459121e92 100644 --- a/packages/cli/src/modules/provisioning.ee/role-deletion-checker.ee.ts +++ b/packages/cli/src/modules/provisioning.ee/role-deletion-checker.ee.ts @@ -1,7 +1,7 @@ import { RoleMappingRuleRepository } from '@n8n/db'; import { Service } from '@n8n/di'; -import type { RoleDeletionChecker } from '@/services/role-deletion-check-proxy.service'; +import type { RoleDeletionChecker } from '@n8n/backend-services'; import { ProvisioningService } from './provisioning.service.ee'; diff --git a/packages/cli/src/modules/token-exchange/services/__tests__/identity-resolution.service.test.ts b/packages/cli/src/modules/token-exchange/services/__tests__/identity-resolution.service.test.ts index 06f90273823..b2153422d9e 100644 --- a/packages/cli/src/modules/token-exchange/services/__tests__/identity-resolution.service.test.ts +++ b/packages/cli/src/modules/token-exchange/services/__tests__/identity-resolution.service.test.ts @@ -1,5 +1,5 @@ import type { Logger } from '@n8n/backend-common'; -import type { EventService } from '@n8n/backend-services'; +import { type EventService, type RoleService } from '@n8n/backend-services'; import { GLOBAL_MEMBER_ROLE, type AuthIdentity, @@ -10,7 +10,6 @@ import { } from '@n8n/db'; import { mock } from 'vitest-mock-extended'; -import type { RoleService } from '@/services/role.service'; import type { UserService } from '@/services/user.service'; import type { TokenExchangeConfig } from '../../token-exchange.config'; diff --git a/packages/cli/src/modules/token-exchange/services/identity-resolution.service.ts b/packages/cli/src/modules/token-exchange/services/identity-resolution.service.ts index e92692e9e00..836bae6f649 100644 --- a/packages/cli/src/modules/token-exchange/services/identity-resolution.service.ts +++ b/packages/cli/src/modules/token-exchange/services/identity-resolution.service.ts @@ -1,5 +1,5 @@ import { Logger } from '@n8n/backend-common'; -import { EventService } from '@n8n/backend-services'; +import { EventService, RoleService } from '@n8n/backend-services'; import { AuthIdentity, AuthIdentityRepository, @@ -11,7 +11,6 @@ import { Service } from '@n8n/di'; import { GLOBAL_OWNER_ROLE_SLUG, isBuiltInRole } from '@n8n/permissions'; import { createHash } from 'node:crypto'; -import { RoleService } from '@/services/role.service'; import { UserService } from '@/services/user.service'; import { TokenExchangeConfig } from '../token-exchange.config'; diff --git a/packages/cli/src/modules/workflow-index/__tests__/workflow-dependency-query.service.test.ts b/packages/cli/src/modules/workflow-index/__tests__/workflow-dependency-query.service.test.ts index 20075ae28c8..9bda63f7d18 100644 --- a/packages/cli/src/modules/workflow-index/__tests__/workflow-dependency-query.service.test.ts +++ b/packages/cli/src/modules/workflow-index/__tests__/workflow-dependency-query.service.test.ts @@ -9,7 +9,7 @@ import type { import { chunkIds } from '@n8n/db'; import type { DataTableRepository } from '@/modules/data-table/data-table.repository'; import type { CredentialsFinderService } from '@/credentials/credentials-finder.service'; -import type { RoleService } from '@/services/role.service'; +import type { RoleService } from '@n8n/backend-services'; import type { WorkflowFinderService } from '@/workflows/workflow-finder.service'; import { mock } from 'vitest-mock-extended'; diff --git a/packages/cli/src/modules/workflow-index/workflow-dependency-query.service.ts b/packages/cli/src/modules/workflow-index/workflow-dependency-query.service.ts index 68be3be4a34..db22ece0c82 100644 --- a/packages/cli/src/modules/workflow-index/workflow-dependency-query.service.ts +++ b/packages/cli/src/modules/workflow-index/workflow-dependency-query.service.ts @@ -19,7 +19,7 @@ import { In, type FindManyOptions } from '@n8n/typeorm'; import { CredentialsFinderService } from '@/credentials/credentials-finder.service'; import { isCredSharingEnabled } from '@/constants/credential-sharing'; import { DataTableRepository } from '@/modules/data-table/data-table.repository'; -import { RoleService } from '@/services/role.service'; +import { RoleService } from '@n8n/backend-services'; import { WorkflowFinderService } from '@/workflows/workflow-finder.service'; import { AgentUsageProviderProxy } from './agent-usage-provider-proxy.service'; diff --git a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-authorization.access.service.test.ts b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-authorization.access.service.test.ts index 5bb27b3d4cc..55c5fb89d08 100644 --- a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-authorization.access.service.test.ts +++ b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-authorization.access.service.test.ts @@ -16,7 +16,7 @@ import { WorkflowReviewAuthorizationService } from '../workflow-review-authoriza import { NotFoundError } from '@n8n/errors'; import type { ProjectService } from '@/services/project.service.ee'; -import type { RoleService } from '@/services/role.service'; +import type { RoleService } from '@n8n/backend-services'; import type { WorkflowFinderService } from '@/workflows/workflow-finder.service'; const requestId = 'req-1'; diff --git a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-authorization.capabilities.service.test.ts b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-authorization.capabilities.service.test.ts index ed0a08f5496..bcb15bfee90 100644 --- a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-authorization.capabilities.service.test.ts +++ b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-authorization.capabilities.service.test.ts @@ -12,7 +12,7 @@ import type { import { mock } from 'vitest-mock-extended'; import type { ProjectService } from '@/services/project.service.ee'; -import type { RoleService } from '@/services/role.service'; +import type { RoleService } from '@n8n/backend-services'; import type { WorkflowFinderService } from '@/workflows/workflow-finder.service'; import { WorkflowReviewAuthorizationService } from '../workflow-review-authorization.service'; diff --git a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request-decision.service.test.ts b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request-decision.service.test.ts index ab826f20006..fddbf203d6c 100644 --- a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request-decision.service.test.ts +++ b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request-decision.service.test.ts @@ -1,6 +1,6 @@ import type { DecideWorkflowReviewRequestDto } from '@n8n/api-types'; import type { LicenseState, Logger } from '@n8n/backend-common'; -import type { EventService } from '@n8n/backend-services'; +import { type EventService, type RoleService } from '@n8n/backend-services'; import type { DbLockService, Project, @@ -33,7 +33,7 @@ import { WorkflowReviewStateNotifier } from '../workflow-review-state-notifier.s import type { CollaborationService } from '@/collaboration/collaboration.service'; import { BadRequestError, ConflictError, ForbiddenError, NotFoundError } from '@n8n/errors'; import type { ProjectService } from '@/services/project.service.ee'; -import type { RoleService } from '@/services/role.service'; + import type { WorkflowReviewPolicyService } from '@/services/workflow-review-policy.service'; import type { WorkflowFinderService } from '@/workflows/workflow-finder.service'; import type { WorkflowService } from '@/workflows/workflow.service'; diff --git a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request-submission.service.test.ts b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request-submission.service.test.ts index 3434f2254a1..dd478b504b8 100644 --- a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request-submission.service.test.ts +++ b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request-submission.service.test.ts @@ -3,7 +3,7 @@ import type { GetWorkflowReviewEligibleReviewersQueryDto, } from '@n8n/api-types'; import type { LicenseState, Logger } from '@n8n/backend-common'; -import type { EventService } from '@n8n/backend-services'; +import { type EventService, type RoleService } from '@n8n/backend-services'; import { DbLock, User } from '@n8n/db'; import type { AuthIdentity, @@ -27,7 +27,7 @@ import { mock } from 'vitest-mock-extended'; import type { CollaborationService } from '@/collaboration/collaboration.service'; import { BadRequestError, ConflictError, ForbiddenError, NotFoundError } from '@n8n/errors'; -import type { RoleService } from '@/services/role.service'; + import type { WorkflowReviewPolicyService } from '@/services/workflow-review-policy.service'; import type { WorkflowFinderService } from '@/workflows/workflow-finder.service'; import type { WorkflowHistoryService } from '@/workflows/workflow-history/workflow-history.service'; diff --git a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request-submission.version-update.service.test.ts b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request-submission.version-update.service.test.ts index 764232da13a..64435af6b1c 100644 --- a/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request-submission.version-update.service.test.ts +++ b/packages/cli/src/modules/workflow-reviews.ee/__tests__/workflow-review-request-submission.version-update.service.test.ts @@ -1,6 +1,6 @@ import type { UpdateWorkflowReviewRequestVersionDto } from '@n8n/api-types'; import type { LicenseState, Logger } from '@n8n/backend-common'; -import type { EventService } from '@n8n/backend-services'; +import { type EventService, type RoleService } from '@n8n/backend-services'; import { DbLock } from '@n8n/db'; import type { DbLockService, @@ -26,7 +26,7 @@ import { mock } from 'vitest-mock-extended'; import type { CollaborationService } from '@/collaboration/collaboration.service'; import { BadRequestError, ConflictError, ForbiddenError, NotFoundError } from '@n8n/errors'; -import type { RoleService } from '@/services/role.service'; + import type { WorkflowReviewPolicyService } from '@/services/workflow-review-policy.service'; import type { WorkflowFinderService } from '@/workflows/workflow-finder.service'; import type { WorkflowHistoryService } from '@/workflows/workflow-history/workflow-history.service'; diff --git a/packages/cli/src/modules/workflow-reviews.ee/workflow-review-authorization.service.ts b/packages/cli/src/modules/workflow-reviews.ee/workflow-review-authorization.service.ts index 5d24a348ace..789763efec7 100644 --- a/packages/cli/src/modules/workflow-reviews.ee/workflow-review-authorization.service.ts +++ b/packages/cli/src/modules/workflow-reviews.ee/workflow-review-authorization.service.ts @@ -21,7 +21,7 @@ import { import { NotFoundError } from '@n8n/errors'; import { ProjectService } from '@/services/project.service.ee'; -import { RoleService } from '@/services/role.service'; +import { RoleService } from '@n8n/backend-services'; import { WorkflowFinderService } from '@/workflows/workflow-finder.service'; import { resolveDecisionCapability } from './workflow-review-decision-policy'; diff --git a/packages/cli/src/modules/workflow-reviews.ee/workflow-review-request-submission.service.ts b/packages/cli/src/modules/workflow-reviews.ee/workflow-review-request-submission.service.ts index 6ae36fbba2f..e762416eae2 100644 --- a/packages/cli/src/modules/workflow-reviews.ee/workflow-review-request-submission.service.ts +++ b/packages/cli/src/modules/workflow-reviews.ee/workflow-review-request-submission.service.ts @@ -5,7 +5,7 @@ import type { WorkflowReviewEligibleReviewersList, WorkflowReviewRequestSummary, } from '@n8n/api-types'; -import { EventService } from '@n8n/backend-services'; +import { EventService, RoleService } from '@n8n/backend-services'; import { DbLock, DbLockService, @@ -24,7 +24,7 @@ import { import { Service } from '@n8n/di'; import { BadRequestError, ConflictError, NotFoundError } from '@n8n/errors'; -import { RoleService } from '@/services/role.service'; + import { WorkflowFinderService } from '@/workflows/workflow-finder.service'; import { WorkflowHistoryService } from '@/workflows/workflow-history/workflow-history.service'; diff --git a/packages/cli/src/permissions.ee/__tests__/check-access.test.ts b/packages/cli/src/permissions.ee/__tests__/check-access.test.ts index fced27d6675..97a50057772 100644 --- a/packages/cli/src/permissions.ee/__tests__/check-access.test.ts +++ b/packages/cli/src/permissions.ee/__tests__/check-access.test.ts @@ -1,19 +1,22 @@ import { GLOBAL_MEMBER_ROLE, ProjectRepository, + RoleRepository, SharedCredentialsRepository, SharedWorkflowRepository, CredentialsRepository, type User, + type Role, } from '@n8n/db'; import { Container } from '@n8n/di'; import { type Scope } from '@n8n/permissions'; +import type { EntityManager } from '@n8n/typeorm'; import type { Mock } from 'vitest'; import { mock } from 'vitest-mock-extended'; import { CredentialsFinderService } from '@/credentials/credentials-finder.service'; import { NotFoundError } from '@n8n/errors'; -import { RoleService } from '@/services/role.service'; +import { RoleService } from '@n8n/backend-services'; import { userHasScopes } from '../check-access'; @@ -27,6 +30,7 @@ describe('userHasScopes', () => { let hasGlobalConnectAccessMock: Mock; let roleServiceMock: Mock; let mockQueryBuilder: any; + const roleRepository = mock(); beforeAll(() => { findByWorkflowMock = vi.fn(); @@ -92,6 +96,7 @@ describe('userHasScopes', () => { rolesWithScope: roleServiceMock, }), ); + Container.set(RoleRepository, roleRepository); }); beforeEach(() => { @@ -169,6 +174,37 @@ describe('userHasScopes', () => { }); describe('RoleService integration', () => { + it('should load roles through the supplied transaction manager', async () => { + const credentialId = 'cred123'; + const entityManager = mock(); + const sharedCredentialsRepository = mock(); + entityManager.createQueryBuilder.mockReturnValue(mockQueryBuilder); + entityManager.getRepository.mockReturnValue(sharedCredentialsRepository); + sharedCredentialsRepository.findBy.mockResolvedValue([ + { + credentialsId: credentialId, + projectId: 'projectId', + role: 'credential:custom', + }, + ] as never); + roleRepository.findAll.mockResolvedValue([mock({ slug: 'credential:custom' })]); + roleServiceMock.mockImplementation( + async (_namespace, _scopes, loadRoles: () => Promise) => + (await loadRoles()).map(({ slug }) => slug), + ); + + const result = await userHasScopes( + { id: 'userId', scopes: [], role: GLOBAL_MEMBER_ROLE } as unknown as User, + ['credential:read'], + false, + { credentialId }, + entityManager, + ); + + expect(roleRepository.findAll).toHaveBeenCalledWith(entityManager); + expect(result).toBe(true); + }); + it('should use RoleService for credential role resolution', async () => { const credentialId = 'cred123'; const mockRoles = ['credential:owner', 'custom:credential-admin']; @@ -187,7 +223,7 @@ describe('userHasScopes', () => { const result = await userHasScopes(user, scopes, false, { credentialId }); - expect(roleServiceMock).toHaveBeenCalledWith('credential', scopes, undefined); + expect(roleServiceMock).toHaveBeenCalledWith('credential', scopes); expect(result).toBe(true); }); @@ -209,7 +245,7 @@ describe('userHasScopes', () => { const result = await userHasScopes(user, scopes, false, { workflowId }); - expect(roleServiceMock).toHaveBeenCalledWith('workflow', scopes, undefined); + expect(roleServiceMock).toHaveBeenCalledWith('workflow', scopes); expect(result).toBe(true); }); @@ -231,7 +267,7 @@ describe('userHasScopes', () => { const result = await userHasScopes(user, scopes, false, { credentialId }); - expect(roleServiceMock).toHaveBeenCalledWith('credential', scopes, undefined); + expect(roleServiceMock).toHaveBeenCalledWith('credential', scopes); expect(result).toBe(true); }); @@ -273,7 +309,7 @@ describe('userHasScopes', () => { await userHasScopes(user, ['credential:read'], false, { credentialId }); - expect(roleServiceMock).toHaveBeenCalledWith('credential', ['credential:read'], undefined); + expect(roleServiceMock).toHaveBeenCalledWith('credential', ['credential:read']); expect(roleServiceMock).not.toHaveBeenCalledWith('workflow', expect.anything()); }); @@ -292,7 +328,7 @@ describe('userHasScopes', () => { await userHasScopes(user, ['workflow:execute'], false, { workflowId }); - expect(roleServiceMock).toHaveBeenCalledWith('workflow', ['workflow:execute'], undefined); + expect(roleServiceMock).toHaveBeenCalledWith('workflow', ['workflow:execute']); expect(roleServiceMock).not.toHaveBeenCalledWith('credential', expect.anything()); }); @@ -396,7 +432,7 @@ describe('userHasScopes', () => { const result = await userHasScopes(user, scopes, false, { workflowId }); - expect(roleServiceMock).toHaveBeenCalledWith('workflow', scopes, undefined); + expect(roleServiceMock).toHaveBeenCalledWith('workflow', scopes); expect(result).toBe(true); }); }); diff --git a/packages/cli/src/permissions.ee/__tests__/project-scope.service.test.ts b/packages/cli/src/permissions.ee/__tests__/project-scope.service.test.ts index 56bdc01f996..8e235767b3f 100644 --- a/packages/cli/src/permissions.ee/__tests__/project-scope.service.test.ts +++ b/packages/cli/src/permissions.ee/__tests__/project-scope.service.test.ts @@ -1,7 +1,7 @@ import { mockInstance } from '@n8n/backend-test-utils'; import { ProjectRelationRepository, User } from '@n8n/db'; -import { RoleService } from '@/services/role.service'; +import { RoleService } from '@n8n/backend-services'; import { ProjectScopeService } from '../project-scope.service'; diff --git a/packages/cli/src/permissions.ee/check-access.ts b/packages/cli/src/permissions.ee/check-access.ts index 8fc59d17454..cc416a48d32 100644 --- a/packages/cli/src/permissions.ee/check-access.ts +++ b/packages/cli/src/permissions.ee/check-access.ts @@ -5,6 +5,7 @@ import { CredentialsRepository, Project, ProjectRepository, + RoleRepository, SharedCredentials, SharedCredentialsRepository, SharedWorkflow, @@ -16,7 +17,7 @@ import { UnexpectedError } from 'n8n-workflow'; import { CredentialsFinderService } from '@/credentials/credentials-finder.service'; import { NotFoundError } from '@n8n/errors'; -import { RoleService } from '@/services/role.service'; +import { RoleService } from '@n8n/backend-services'; const INSTANCE_CREDENTIAL_MANAGEMENT_SCOPES = new Set([ 'credential:read', @@ -114,6 +115,13 @@ export async function userHasScopes( // Then find at least one of the above qualifying projects having one of // those resource roles over the resource being checked. const roleService = Container.get(RoleService); + const loadRoles = entityManager + ? async () => await Container.get(RoleRepository).findAll(entityManager) + : undefined; + const rolesWithScope = async (namespace: 'credential' | 'workflow') => + loadRoles + ? await roleService.rolesWithScope(namespace, scopes, loadRoles) + : await roleService.rolesWithScope(namespace, scopes); if (credentialId) { const credentialRepo = entityManager @@ -125,7 +133,7 @@ export async function userHasScopes( throw new NotFoundError(`Credential with ID "${credentialId}" not found.`); } - const validRoles = await roleService.rolesWithScope('credential', scopes, entityManager); + const validRoles = await rolesWithScope('credential'); const hasValidRoles = credentials.some( (c) => userProjectIds.includes(c.projectId) && validRoles.includes(c.role), @@ -150,7 +158,7 @@ export async function userHasScopes( throw new NotFoundError(`Workflow with ID "${workflowId}" not found.`); } - const validRoles = await roleService.rolesWithScope('workflow', scopes, entityManager); + const validRoles = await rolesWithScope('workflow'); return workflows.some( (w) => userProjectIds.includes(w.projectId) && validRoles.includes(w.role), diff --git a/packages/cli/src/permissions.ee/project-scope.service.ts b/packages/cli/src/permissions.ee/project-scope.service.ts index be6d14f22b9..5bce375efe5 100644 --- a/packages/cli/src/permissions.ee/project-scope.service.ts +++ b/packages/cli/src/permissions.ee/project-scope.service.ts @@ -2,7 +2,7 @@ import { ProjectRelationRepository, type User } from '@n8n/db'; import { Service } from '@n8n/di'; import { hasGlobalScope, type Scope } from '@n8n/permissions'; -import { RoleService } from '@/services/role.service'; +import { RoleService } from '@n8n/backend-services'; /** * Resolves the project roles, or the project IDs, that restrict a scope-aware query. diff --git a/packages/cli/src/public-api/v1/controllers/__tests__/roles.public.controller.test.ts b/packages/cli/src/public-api/v1/controllers/__tests__/roles.public.controller.test.ts index d64b6232800..76b685ff0ce 100644 --- a/packages/cli/src/public-api/v1/controllers/__tests__/roles.public.controller.test.ts +++ b/packages/cli/src/public-api/v1/controllers/__tests__/roles.public.controller.test.ts @@ -1,11 +1,9 @@ import type { CreateRoleDto } from '@n8n/api-types'; -import type { EventService } from '@n8n/backend-services'; +import { type EventService, type RoleService } from '@n8n/backend-services'; import type { AuthenticatedRequest } from '@n8n/db'; import type { Role } from '@n8n/permissions'; import { mock } from 'vitest-mock-extended'; -import type { RoleService } from '@/services/role.service'; - import { RolesPublicController } from '../roles.public.controller'; describe('RolesPublicController', () => { diff --git a/packages/cli/src/public-api/v1/controllers/roles.public.controller.ts b/packages/cli/src/public-api/v1/controllers/roles.public.controller.ts index d02a08d399b..d219f89262b 100644 --- a/packages/cli/src/public-api/v1/controllers/roles.public.controller.ts +++ b/packages/cli/src/public-api/v1/controllers/roles.public.controller.ts @@ -8,7 +8,7 @@ import { UpdateRolePublicDto, roleSlugParamSchema, } from '@n8n/api-types'; -import { EventService } from '@n8n/backend-services'; +import { EventService, RoleService } from '@n8n/backend-services'; import { LICENSE_FEATURES } from '@n8n/constants'; import { AuthenticatedRequest } from '@n8n/db'; import { @@ -33,7 +33,6 @@ import type { Response } from 'express'; import { NotFoundError } from '@n8n/errors'; import { assertCanManageRoleType, canReassignUsers } from '@/services/role-authorization'; -import { RoleService } from '@/services/role.service'; type PublicRoleNamespace = Extract; const isPublicRole = (role: RoleDTO): role is RoleDTO & { roleType: PublicRoleNamespace } => diff --git a/packages/cli/src/services/__tests__/credentials-finder.service.test.ts b/packages/cli/src/services/__tests__/credentials-finder.service.test.ts index 2302850aeee..ae29227537e 100644 --- a/packages/cli/src/services/__tests__/credentials-finder.service.test.ts +++ b/packages/cli/src/services/__tests__/credentials-finder.service.test.ts @@ -24,7 +24,7 @@ import { mock } from 'vitest-mock-extended'; import { CredentialsFinderService } from '@/credentials/credentials-finder.service'; -import { RoleService } from '../role.service'; +import { RoleService } from '@n8n/backend-services'; describe('CredentialsFinderService', () => { const roleService = mockInstance(RoleService); diff --git a/packages/cli/src/services/__tests__/folder-finder.service.test.ts b/packages/cli/src/services/__tests__/folder-finder.service.test.ts index d605f3d2554..e84b7832f4d 100644 --- a/packages/cli/src/services/__tests__/folder-finder.service.test.ts +++ b/packages/cli/src/services/__tests__/folder-finder.service.test.ts @@ -1,7 +1,7 @@ import type { Folder, FolderRepository, User } from '@n8n/db'; import { mock } from 'vitest-mock-extended'; -import type { RoleService } from '@/services/role.service'; +import type { RoleService } from '@n8n/backend-services'; import { FolderFinderService } from '../folder-finder.service'; diff --git a/packages/cli/src/services/__tests__/project.service.ee.test.ts b/packages/cli/src/services/__tests__/project.service.ee.test.ts index 4be13850c66..6c424528687 100644 --- a/packages/cli/src/services/__tests__/project.service.ee.test.ts +++ b/packages/cli/src/services/__tests__/project.service.ee.test.ts @@ -1,9 +1,10 @@ import type { ProjectRelation } from '@n8n/api-types'; import type { Logger, ModuleRegistry } from '@n8n/backend-common'; -import type { EventService } from '@n8n/backend-services'; +import { type EventService, type RoleService } from '@n8n/backend-services'; import { type Project, type ProjectRepository, + type RoleRepository, type SharedCredentialsRepository, type SharedWorkflowRepository, type ProjectRelationRepository, @@ -21,7 +22,6 @@ import { mock } from 'vitest-mock-extended'; import type { OwnershipService } from '../ownership.service'; import { ProjectService } from '../project.service.ee'; -import type { RoleService } from '../role.service'; import type { ICredentialConnectionStatusProvider } from '@/credentials/credential-connection-status-provider.interface'; import { BadRequestError, ForbiddenError } from '@n8n/errors'; @@ -48,6 +48,7 @@ describe('ProjectService', () => { const eventService = mock(); const userManagementMailer = mock(); const userRepository = mock(); + const roleRepository = mock(); const user = mock({ id: 'actor-user', role: mock({ slug: 'global:owner' }) }); const projectService = new ProjectService( sharedWorkflowRepository, @@ -63,6 +64,7 @@ describe('ProjectService', () => { eventService, userManagementMailer, userRepository, + roleRepository, ); beforeEach(() => { diff --git a/packages/cli/src/services/__tests__/security-settings.service.test.ts b/packages/cli/src/services/__tests__/security-settings.service.test.ts index 81c1aa666fa..c13846f993f 100644 --- a/packages/cli/src/services/__tests__/security-settings.service.test.ts +++ b/packages/cli/src/services/__tests__/security-settings.service.test.ts @@ -1,4 +1,4 @@ -import { EventService } from '@n8n/backend-services'; +import { EventService, RoleService } from '@n8n/backend-services'; import { mockInstance } from '@n8n/backend-test-utils'; import { SettingsRepository, @@ -12,7 +12,7 @@ import { } from '@n8n/permissions'; import { InstanceRedactionEnforcementService } from '@/modules/redaction/instance-redaction-enforcement.service'; -import { RoleService } from '@/services/role.service'; + import { SecuritySettingsService } from '@/services/security-settings.service'; describe('SecuritySettingsService', () => { diff --git a/packages/cli/src/services/__tests__/user.service.test.ts b/packages/cli/src/services/__tests__/user.service.test.ts index d99076537c9..58ee537bd4d 100644 --- a/packages/cli/src/services/__tests__/user.service.test.ts +++ b/packages/cli/src/services/__tests__/user.service.test.ts @@ -1,5 +1,5 @@ import { InviteUsersRequestDto } from '@n8n/api-types'; -import { type EventService, UrlService } from '@n8n/backend-services'; +import { type EventService, UrlService, type RoleService } from '@n8n/backend-services'; import { mockInstance } from '@n8n/backend-test-utils'; import { GlobalConfig } from '@n8n/config'; import { @@ -32,7 +32,6 @@ import { JwtService } from '../jwt.service'; import type { OwnershipService } from '../ownership.service'; import type { ProjectService } from '../project.service.ee'; import type { PublicApiKeyService } from '../public-api-key.service'; -import type { RoleService } from '../role.service'; describe('UserService', () => { const globalConfig = mockInstance(GlobalConfig, { diff --git a/packages/cli/src/services/folder-finder.service.ts b/packages/cli/src/services/folder-finder.service.ts index 12bf45c09a4..0698cd7bce9 100644 --- a/packages/cli/src/services/folder-finder.service.ts +++ b/packages/cli/src/services/folder-finder.service.ts @@ -5,7 +5,7 @@ import { hasGlobalScope, type Scope } from '@n8n/permissions'; import type { FindOptionsWhere } from '@n8n/typeorm'; import { In } from '@n8n/typeorm'; -import { RoleService } from '@/services/role.service'; +import { RoleService } from '@n8n/backend-services'; /** * Resolves folders by id for a user, enforcing access through the folder's home diff --git a/packages/cli/src/services/project.service.ee.ts b/packages/cli/src/services/project.service.ee.ts index c52d0f4c1d4..6d5e5cde970 100644 --- a/packages/cli/src/services/project.service.ee.ts +++ b/packages/cli/src/services/project.service.ee.ts @@ -1,6 +1,6 @@ import type { CreateProjectDto, ProjectType, UpdateProjectDto } from '@n8n/api-types'; import { LicenseState, Logger, ModuleRegistry } from '@n8n/backend-common'; -import { EventService } from '@n8n/backend-services'; +import { EventService, RoleService } from '@n8n/backend-services'; import { type User, FolderRepository, @@ -8,6 +8,7 @@ import { ProjectRelation, ProjectRelationRepository, ProjectRepository, + RoleRepository, ProjectIdConflictError, SharedCredentialsRepository, SharedWorkflowRepository, @@ -37,7 +38,6 @@ import { BadRequestError, ConflictError, ForbiddenError, NotFoundError } from '@ import { UserManagementMailer } from '@/user-management/email'; import { OwnershipService } from './ownership.service'; -import { RoleService } from './role.service'; const INSTANCE_ACCESS_ROLE_ERROR = "This user has access through their instance role. Project roles can't change their access in this project."; @@ -100,6 +100,7 @@ export class ProjectService { private readonly eventService: EventService, private readonly userManagementMailer: UserManagementMailer, private readonly userRepository: UserRepository, + private readonly roleRepository: RoleRepository, ) {} private get workflowService() { @@ -911,10 +912,11 @@ export class ProjectService { }; if (!hasGlobalScope(user, scopes, { mode: 'allOf' })) { - // Use the same EntityManager as the project lookup (including when callers pass a - // transaction manager). Otherwise role resolution can open a second pooled connection - // while a transaction already holds a connection - const projectRoles = await this.roleService.rolesWithScope('project', scopes, em); + const projectRoles = await this.roleService.rolesWithScope( + 'project', + scopes, + async () => await this.roleRepository.findAll(em), + ); where = { ...where, diff --git a/packages/cli/src/services/security-settings.service.ts b/packages/cli/src/services/security-settings.service.ts index 76d92cc03f7..cc551b07a91 100644 --- a/packages/cli/src/services/security-settings.service.ts +++ b/packages/cli/src/services/security-settings.service.ts @@ -1,5 +1,5 @@ import type { RedactionFloor } from '@n8n/api-types'; -import { EventService } from '@n8n/backend-services'; +import { EventService, RoleService } from '@n8n/backend-services'; import { SettingsRepository, SharedCredentialsRepository, @@ -15,7 +15,6 @@ import type { DistributiveOmit } from '@n8n/utils/types'; import type { RelayEventMap, UserLike } from '@/events/maps/relay.event-map'; import { InstanceRedactionEnforcementService } from '@/modules/redaction/instance-redaction-enforcement.service'; -import { RoleService } from '@/services/role.service'; /** * The writable subset of the security policy shared by the internal controller diff --git a/packages/cli/src/services/user.service.ts b/packages/cli/src/services/user.service.ts index 21e7585154e..8aa51547dc6 100644 --- a/packages/cli/src/services/user.service.ts +++ b/packages/cli/src/services/user.service.ts @@ -1,6 +1,6 @@ import type { RoleChangeRequestDto } from '@n8n/api-types'; import { Logger } from '@n8n/backend-common'; -import { EventService, UrlService } from '@n8n/backend-services'; +import { EventService, UrlService, RoleService } from '@n8n/backend-services'; import { GlobalConfig } from '@n8n/config'; import type { PublicUser } from '@n8n/db'; import { @@ -34,7 +34,6 @@ import { JwtService } from './jwt.service'; import { OwnershipService } from './ownership.service'; import { ProjectService } from './project.service.ee'; import { PublicApiKeyService } from './public-api-key.service'; -import { RoleService } from './role.service'; import { RESPONSE_ERROR_MESSAGES } from '@/constants'; import { BadRequestError, ForbiddenError, InternalServerError, NotFoundError } from '@n8n/errors'; diff --git a/packages/cli/src/workflows/__tests__/workflow-finder.service.test.ts b/packages/cli/src/workflows/__tests__/workflow-finder.service.test.ts index 1048a45bddd..458b7bd8230 100644 --- a/packages/cli/src/workflows/__tests__/workflow-finder.service.test.ts +++ b/packages/cli/src/workflows/__tests__/workflow-finder.service.test.ts @@ -1,13 +1,15 @@ /* eslint-disable @typescript-eslint/unbound-method -- vi mocks */ import type { FolderRepository, + RoleRepository, SharedWorkflow, SharedWorkflowRepository, WorkflowRepository, } from '@n8n/db'; +import type { EntityManager } from '@n8n/typeorm'; import { mock } from 'vitest-mock-extended'; -import type { RoleService } from '@/services/role.service'; +import type { RoleService } from '@n8n/backend-services'; import { WorkflowFinderService } from '../workflow-finder.service'; @@ -22,16 +24,43 @@ function makeService(rows?: FolderRow[]) { const workflowRepository = mock(); const folderRepository = mock(); const roleService = mock(); + const roleRepository = mock(); const service = new WorkflowFinderService( sharedWorkflowRepository, folderRepository, roleService, workflowRepository, + roleRepository, ); - return { service, sharedWorkflowRepository, workflowRepository, folderRepository, roleService }; + return { + service, + sharedWorkflowRepository, + workflowRepository, + folderRepository, + roleService, + roleRepository, + }; } describe('WorkflowFinderService', () => { + it('loads roles through the supplied transaction manager', async () => { + const { service, roleService, roleRepository } = makeService(); + const em = mock(); + roleService.rolesWithScope.mockResolvedValue([]); + roleRepository.findAll.mockResolvedValue([]); + + await service.findWorkflowForUser( + 'workflow-1', + { id: 'user-1', role: { slug: 'global:member', scopes: [] } } as never, + ['workflow:read'], + { em }, + ); + + const loadRoles = roleService.rolesWithScope.mock.calls[0][2]; + await loadRoles?.(); + expect(roleRepository.findAll).toHaveBeenCalledWith(em); + }); + describe('findWorkflowIdsByFolder', () => { it('returns an empty map without querying when no folder ids are given', async () => { const { service, sharedWorkflowRepository } = makeService(); diff --git a/packages/cli/src/workflows/__tests__/workflow-sharing.service.test.ts b/packages/cli/src/workflows/__tests__/workflow-sharing.service.test.ts index 8048a2bca3b..b93d80f8b90 100644 --- a/packages/cli/src/workflows/__tests__/workflow-sharing.service.test.ts +++ b/packages/cli/src/workflows/__tests__/workflow-sharing.service.test.ts @@ -2,7 +2,7 @@ import type { Logger, LicenseState } from '@n8n/backend-common'; import type { ProjectRelationRepository, SharedWorkflowRepository, UserRepository } from '@n8n/db'; import { mock } from 'vitest-mock-extended'; -import type { RoleService } from '@/services/role.service'; +import type { RoleService } from '@n8n/backend-services'; import { WorkflowSharingService } from '../workflow-sharing.service'; diff --git a/packages/cli/src/workflows/__tests__/workflow.service.test.ts b/packages/cli/src/workflows/__tests__/workflow.service.test.ts index f83c9b83d73..caaa4df83d3 100644 --- a/packages/cli/src/workflows/__tests__/workflow.service.test.ts +++ b/packages/cli/src/workflows/__tests__/workflow.service.test.ts @@ -1,5 +1,5 @@ import type { LicenseState } from '@n8n/backend-common'; -import type { EventService } from '@n8n/backend-services'; +import { type EventService, type RoleService } from '@n8n/backend-services'; import type { GlobalConfig, WorkflowsConfig } from '@n8n/config'; import type { Project, @@ -45,7 +45,7 @@ import type { PollTriggerJobRegistrar } from '@/scheduling/poll-trigger-node/pol import type { ScheduleTriggerJobRegistrar } from '@/scheduling/schedule-trigger-node/schedule-trigger-job-registrar'; import type { WorkflowScheduledJobOwner } from '@/scheduling/workflow-scheduled-job-owner'; import type { OwnershipService } from '@/services/ownership.service'; -import type { RoleService } from '@/services/role.service'; + import type { TagService } from '@/services/tag.service'; import type { WebhookService } from '@/webhooks/webhook.service'; import * as WorkflowHelpers from '@/workflow-helpers'; diff --git a/packages/cli/src/workflows/workflow-finder.service.ts b/packages/cli/src/workflows/workflow-finder.service.ts index 0538624a9a8..254002b9196 100644 --- a/packages/cli/src/workflows/workflow-finder.service.ts +++ b/packages/cli/src/workflows/workflow-finder.service.ts @@ -1,12 +1,18 @@ import type { SharedWorkflow, User, WorkflowEntity, ListQuery } from '@n8n/db'; -import { SharedWorkflowRepository, FolderRepository, WorkflowRepository, chunkIds } from '@n8n/db'; +import { + SharedWorkflowRepository, + FolderRepository, + WorkflowRepository, + RoleRepository, + chunkIds, +} from '@n8n/db'; import { Service } from '@n8n/di'; import { hasGlobalScope, type Scope } from '@n8n/permissions'; import type { EntityManager, FindOptionsWhere } from '@n8n/typeorm'; import { In, IsNull } from '@n8n/typeorm'; import { userHasScopes } from '@/permissions.ee/check-access'; -import { RoleService } from '@/services/role.service'; +import { RoleService } from '@n8n/backend-services'; export type FindWorkflowsForUserOptions = { filters?: { @@ -31,6 +37,7 @@ export class WorkflowFinderService { private readonly folderRepository: FolderRepository, private readonly roleService: RoleService, private readonly workflowRepository: WorkflowRepository, + private readonly roleRepository: RoleRepository, ) {} async findWorkflowForUser( @@ -95,10 +102,15 @@ export class WorkflowFinderService { em?: EntityManager, ): Promise> { if (hasGlobalScope(user, scopes, { mode: 'allOf' })) return {}; + const loadRoles = em ? async () => await this.roleRepository.findAll(em) : undefined; + const rolesWithScope = async (namespace: 'project' | 'workflow') => + loadRoles + ? await this.roleService.rolesWithScope(namespace, scopes, loadRoles) + : await this.roleService.rolesWithScope(namespace, scopes); const [projectRoles, workflowRoles] = await Promise.all([ - this.roleService.rolesWithScope('project', scopes, em), - this.roleService.rolesWithScope('workflow', scopes, em), + rolesWithScope('project'), + rolesWithScope('workflow'), ]); return { diff --git a/packages/cli/src/workflows/workflow-sharing.service.ts b/packages/cli/src/workflows/workflow-sharing.service.ts index 0c0afef999a..603b551ea40 100644 --- a/packages/cli/src/workflows/workflow-sharing.service.ts +++ b/packages/cli/src/workflows/workflow-sharing.service.ts @@ -11,7 +11,7 @@ import { } from '@n8n/permissions'; import { In } from '@n8n/typeorm'; -import { RoleService } from '@/services/role.service'; +import { RoleService } from '@n8n/backend-services'; export type ShareWorkflowOptions = | { scopes: Scope[]; projectId?: string } diff --git a/packages/cli/src/workflows/workflow.service.ts b/packages/cli/src/workflows/workflow.service.ts index 3529d52e40c..43364fa08f8 100644 --- a/packages/cli/src/workflows/workflow.service.ts +++ b/packages/cli/src/workflows/workflow.service.ts @@ -1,7 +1,7 @@ import { UpdateWorkflowHistoryVersionDto } from '@n8n/api-types'; import type { WorkflowListPublicationStatus } from '@n8n/api-types'; import { LicenseState, Logger } from '@n8n/backend-common'; -import { EventService } from '@n8n/backend-services'; +import { EventService, RoleService } from '@n8n/backend-services'; import { GlobalConfig } from '@n8n/config'; import type { User, ListQueryDb, Project, WorkflowFolderUnionFull, WorkflowHistory } from '@n8n/db'; import { @@ -71,7 +71,7 @@ import { ScheduleTriggerJobRegistrar } from '@/scheduling/schedule-trigger-node/ import { WorkflowScheduledJobOwner } from '@/scheduling/workflow-scheduled-job-owner'; import { OwnershipService } from '@/services/ownership.service'; import { ProjectService } from '@/services/project.service.ee'; -import { RoleService } from '@/services/role.service'; + import { TagService } from '@/services/tag.service'; import { WEBHOOK_CONFLICT_MESSAGE } from '@/webhooks/constants'; import { WebhookService } from '@/webhooks/webhook.service'; diff --git a/packages/cli/test/integration/auth-roles-canvas-only.test.ts b/packages/cli/test/integration/auth-roles-canvas-only.test.ts index 23acf4f8c3f..7e296ee282f 100644 --- a/packages/cli/test/integration/auth-roles-canvas-only.test.ts +++ b/packages/cli/test/integration/auth-roles-canvas-only.test.ts @@ -4,7 +4,7 @@ import { AuthRolesService, RoleRepository, type User } from '@n8n/db'; import { Container } from '@n8n/di'; import { PROJECT_OWNER_ROLE_SLUG } from '@n8n/permissions'; -import { RoleCacheService } from '@/services/role-cache.service'; +import { RoleCacheService } from '@n8n/backend-services'; import { createMemberWithApiKey } from './shared/db/users'; import * as utils from './shared/utils/'; diff --git a/packages/cli/test/integration/controllers/role.controller.test.ts b/packages/cli/test/integration/controllers/role.controller.test.ts index 0b33cda5f83..42c4a5df0bf 100644 --- a/packages/cli/test/integration/controllers/role.controller.test.ts +++ b/packages/cli/test/integration/controllers/role.controller.test.ts @@ -2,7 +2,7 @@ import type { CreateRoleDto, UpdateRoleDto } from '@n8n/api-types'; import { mockInstance } from '@n8n/backend-test-utils'; import { type Role } from '@n8n/permissions'; -import { RoleService } from '@/services/role.service'; +import { RoleService } from '@n8n/backend-services'; import { cleanupRolesAndScopes } from '../shared/db/roles'; import { createMember, createOwner } from '../shared/db/users'; diff --git a/packages/cli/test/integration/credentials/credentials.api.ee.test.ts b/packages/cli/test/integration/credentials/credentials.api.ee.test.ts index c6ee4d89ad9..f132839a2e7 100644 --- a/packages/cli/test/integration/credentials/credentials.api.ee.test.ts +++ b/packages/cli/test/integration/credentials/credentials.api.ee.test.ts @@ -20,7 +20,7 @@ import config from '@/config'; import { CredentialsService } from '@/credentials/credentials.service'; import { CredentialsTester } from '@/services/credentials-tester.service'; import { ProjectService } from '@/services/project.service.ee'; -import { RoleCacheService } from '@/services/role-cache.service'; +import { RoleCacheService } from '@n8n/backend-services'; import { SecuritySettingsService } from '@/services/security-settings.service'; import { UserManagementMailer } from '@/user-management/email'; diff --git a/packages/cli/test/integration/database/repositories/credentials.repository.test.ts b/packages/cli/test/integration/database/repositories/credentials.repository.test.ts index 9151a0fe9ec..39d02269c8b 100644 --- a/packages/cli/test/integration/database/repositories/credentials.repository.test.ts +++ b/packages/cli/test/integration/database/repositories/credentials.repository.test.ts @@ -472,7 +472,7 @@ describe('CredentialsRepository', () => { const { CredentialsFinderService } = await import( '@/credentials/credentials-finder.service.js' ); - const { RoleService } = await import('@/services/role.service.js'); + const { RoleService } = await import('@n8n/backend-services'); const member = await createMember(); const teamProject = await createTeamProject('test-project'); @@ -566,7 +566,7 @@ describe('CredentialsRepository', () => { const { CredentialsFinderService } = await import( '@/credentials/credentials-finder.service.js' ); - const { RoleService } = await import('@/services/role.service.js'); + const { RoleService } = await import('@n8n/backend-services'); const member = await createMember(); const teamProject = await createTeamProject('test-project'); @@ -636,7 +636,7 @@ describe('CredentialsRepository', () => { const { CredentialsFinderService } = await import( '@/credentials/credentials-finder.service.js' ); - const { RoleService } = await import('@/services/role.service.js'); + const { RoleService } = await import('@n8n/backend-services'); const member = await createMember(); @@ -723,7 +723,7 @@ describe('CredentialsRepository', () => { const { CredentialsFinderService } = await import( '@/credentials/credentials-finder.service.js' ); - const { RoleService } = await import('@/services/role.service.js'); + const { RoleService } = await import('@n8n/backend-services'); // Create two separate users const userA = await createMember(); diff --git a/packages/cli/test/integration/database/repositories/workflow.repository.test.ts b/packages/cli/test/integration/database/repositories/workflow.repository.test.ts index 5b6421536db..816a54222a0 100644 --- a/packages/cli/test/integration/database/repositories/workflow.repository.test.ts +++ b/packages/cli/test/integration/database/repositories/workflow.repository.test.ts @@ -1132,7 +1132,7 @@ describe('WorkflowRepository', () => { const { createMember } = await import('../../shared/db/users.js'); const { createTeamProject, linkUserToProject } = await import('@n8n/backend-test-utils'); const { WorkflowSharingService } = await import('@/workflows/workflow-sharing.service.js'); - const { RoleService } = await import('@/services/role.service.js'); + const { RoleService } = await import('@n8n/backend-services'); const member = await createMember(); const teamProject = await createTeamProject('test-project'); @@ -1270,7 +1270,7 @@ describe('WorkflowRepository', () => { const { createMember } = await import('../../shared/db/users.js'); const { createTeamProject, linkUserToProject } = await import('@n8n/backend-test-utils'); const { WorkflowSharingService } = await import('@/workflows/workflow-sharing.service.js'); - const { RoleService } = await import('@/services/role.service.js'); + const { RoleService } = await import('@n8n/backend-services'); const member = await createMember(); @@ -1352,7 +1352,7 @@ describe('WorkflowRepository', () => { const { createMember } = await import('../../shared/db/users.js'); const { createTeamProject, linkUserToProject } = await import('@n8n/backend-test-utils'); const { WorkflowSharingService } = await import('@/workflows/workflow-sharing.service.js'); - const { RoleService } = await import('@/services/role.service.js'); + const { RoleService } = await import('@n8n/backend-services'); // Create two separate users const userA = await createMember(); diff --git a/packages/cli/test/integration/public-api/credentials.test.ts b/packages/cli/test/integration/public-api/credentials.test.ts index 3982baaf56a..18c75f4c0d5 100644 --- a/packages/cli/test/integration/public-api/credentials.test.ts +++ b/packages/cli/test/integration/public-api/credentials.test.ts @@ -26,7 +26,7 @@ import { mock } from 'vitest-mock-extended'; import { CredentialsService } from '@/credentials/credentials.service'; import { ExternalSecretsConfig } from '@/modules/external-secrets.ee/external-secrets.config'; -import { RoleCacheService } from '@/services/role-cache.service'; +import { RoleCacheService } from '@n8n/backend-services'; import { LoadNodesAndCredentials } from '@/load-nodes-and-credentials'; import { PostHogClient } from '@/posthog'; import { CredentialsTester } from '@/services/credentials-tester.service'; diff --git a/packages/cli/test/integration/services/role.service.test.ts b/packages/cli/test/integration/services/role.service.test.ts index bdccfae7796..5a7b361780e 100644 --- a/packages/cli/test/integration/services/role.service.test.ts +++ b/packages/cli/test/integration/services/role.service.test.ts @@ -14,8 +14,7 @@ import { BadRequestError, NotFoundError } from '@n8n/errors'; import { License } from '@/license'; import { ProvisioningRoleDeletionChecker } from '@/modules/provisioning.ee/role-deletion-checker.ee'; import { ProjectService } from '@/services/project.service.ee'; -import { RoleDeletionCheckProxy } from '@/services/role-deletion-check-proxy.service'; -import { RoleService } from '@/services/role.service'; +import { RoleDeletionCheckProxy, RoleService } from '@n8n/backend-services'; import { createRole, diff --git a/packages/cli/test/integration/shared/db/roles.ts b/packages/cli/test/integration/shared/db/roles.ts index 0d4ae800800..e3efc21271d 100644 --- a/packages/cli/test/integration/shared/db/roles.ts +++ b/packages/cli/test/integration/shared/db/roles.ts @@ -2,7 +2,7 @@ import { Role, RoleRepository, Scope, ScopeRepository } from '@n8n/db'; import { Container } from '@n8n/di'; import type { Scope as ScopeType } from '@n8n/permissions'; -import { RoleCacheService } from '@/services/role-cache.service'; +import { RoleCacheService } from '@n8n/backend-services'; /** * Creates a test role with given parameters diff --git a/packages/cli/test/integration/workflows/workflow.service.test.ts b/packages/cli/test/integration/workflows/workflow.service.test.ts index cee67a2f2cc..e1f9d6f10f8 100644 --- a/packages/cli/test/integration/workflows/workflow.service.test.ts +++ b/packages/cli/test/integration/workflows/workflow.service.test.ts @@ -32,7 +32,7 @@ import { NodeTypes } from '@/node-types'; import { PolicyEnforcementService } from '@/policy/policy-enforcement.service'; import { OwnershipService } from '@/services/ownership.service'; import { ProjectService } from '@/services/project.service.ee'; -import { RoleService } from '@/services/role.service'; +import { RoleService } from '@n8n/backend-services'; import { Telemetry } from '@/telemetry'; import { WebhookService } from '@/webhooks/webhook.service'; import { WorkflowHookContextService } from '@/workflow-hook-context.service'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8d6198c40e0..46ee28b081e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1813,6 +1813,9 @@ importers: '@n8n/constants': specifier: workspace:* version: link:../constants + '@n8n/db': + specifier: workspace:* + version: link:../db '@n8n/decorators': specifier: workspace:* version: link:../decorators @@ -1822,6 +1825,9 @@ importers: '@n8n/errors': specifier: workspace:* version: link:../errors + '@n8n/permissions': + specifier: workspace:* + version: link:../permissions '@n8n/utils': specifier: workspace:* version: link:../utils