refactor(core): move ownership service to backend services

This commit is contained in:
Charlie Kolb 2026-10-12 08:39:08 +02:00
parent 01da88958d
commit 0275af7854
No known key found for this signature in database
97 changed files with 364 additions and 376 deletions

View File

@ -26,6 +26,7 @@ export {
} from './services/workflow-sharing.service';
export { FolderFinderService } from './services/folder-finder.service';
export { InstanceWriteAccessService } from './services/instance-write-access.service';
export { OwnershipService } from './services/ownership.service';
export { UrlService } from './services/url.service';
export {
classifyRestError,

View File

@ -1,8 +1,7 @@
import { Logger } from '@n8n/backend-common';
import type { EventService } from '@n8n/backend-services';
import { mockInstance } from '@n8n/backend-test-utils';
import {
Project,
CredentialsEntity,
SharedWorkflow,
User,
WorkflowEntity,
@ -14,17 +13,20 @@ import {
GLOBAL_OWNER_ROLE,
PROJECT_OWNER_ROLE,
} from '@n8n/db';
import type { SharedCredentials, SettingsRepository } from '@n8n/db';
import type { SharedCredentials } from '@n8n/db';
import { PROJECT_OWNER_ROLE_SLUG } from '@n8n/permissions';
import { v4 as uuid } from 'uuid';
import { mock } from 'vitest-mock-extended';
import { randomUUID } from 'node:crypto';
import { BadRequestError } from '@n8n/errors';
import { OwnershipService } from '@/services/ownership.service';
import { PasswordUtility } from '@/services/password.utility';
import { mockCredential, mockProject } from '@test/mock-objects';
import { CacheService } from '../cache/cache.service';
import { OwnershipService } from '../ownership.service';
import { CacheService } from '@n8n/backend-services';
const mockCredential = () => new CredentialsEntity();
const mockProject = () =>
Object.assign(new Project(), {
id: randomUUID(),
type: 'personal' as const,
name: 'Test project',
});
describe('OwnershipService', () => {
const userRepository = mockInstance(UserRepository);
@ -32,21 +34,13 @@ describe('OwnershipService', () => {
const projectRelationRepository = mockInstance(ProjectRelationRepository);
const projectRepository = mockInstance(ProjectRepository);
const cacheService = mockInstance(CacheService);
const passwordUtility = mockInstance(PasswordUtility);
const logger = mockInstance(Logger);
const eventService = mock<EventService>();
const settingsRepository = mock<SettingsRepository>();
const ownershipService = new OwnershipService(
cacheService,
eventService,
logger,
passwordUtility,
projectRelationRepository,
projectRepository,
sharedWorkflowRepository,
userRepository,
settingsRepository,
);
beforeEach(() => {
@ -108,9 +102,9 @@ describe('OwnershipService', () => {
test('should not use the repository if the owner was found in the cache', async () => {
// ARRANGE
const project = new Project();
project.id = uuid();
project.id = randomUUID();
const owner = new User();
owner.id = uuid();
owner.id = randomUUID();
owner.role = GLOBAL_OWNER_ROLE;
const projectRelation = new ProjectRelation();
projectRelation.role = PROJECT_OWNER_ROLE;
@ -274,7 +268,7 @@ describe('OwnershipService', () => {
describe('invalidateProjectOwnerCacheByUserId()', () => {
test('should delete cache entry for personal project', async () => {
const project = new Project();
project.id = uuid();
project.id = randomUUID();
projectRepository.getPersonalProjectForUser.mockResolvedValueOnce(project);
await ownershipService.invalidateProjectOwnerCacheByUserId('some-user-id');
@ -331,123 +325,4 @@ describe('OwnershipService', () => {
});
});
});
describe('setupOwner()', () => {
it('should throw a BadRequestError if the instance owner is already setup', async () => {
userRepository.hasClaimedInstanceOwner.mockResolvedValueOnce(true);
const execution = ownershipService.setupOwner(mock());
await expect(execution).rejects.toThrow(BadRequestError);
await expect(execution).rejects.toThrow('Instance owner already setup');
expect(userRepository.save).not.toHaveBeenCalled();
expect(eventService.emit).not.toHaveBeenCalled();
expect(logger.debug).toHaveBeenCalledWith(
'Request to claim instance ownership failed because instance owner already exists',
);
});
it('should throw a BadRequestError if the shell user is not found', async () => {
userRepository.hasClaimedInstanceOwner.mockResolvedValueOnce(false);
userRepository.findOne.mockResolvedValueOnce(null);
const execution = ownershipService.setupOwner(mock());
await expect(execution).rejects.toThrow(BadRequestError);
await expect(execution).rejects.toThrow('Instance owner shell user not found');
expect(userRepository.save).not.toHaveBeenCalled();
});
it('should setup the instance owner successfully', async () => {
const user = mock<User>({
id: 'userId',
role: GLOBAL_OWNER_ROLE,
authIdentities: [],
});
const payload = {
email: 'valid@email.com',
password: 'NewPassword123',
firstName: 'Jane',
lastName: 'Doe',
};
// not quite perfect as we hash the password.
const expected = { ...user, ...payload, id: 'newUserId' };
userRepository.hasClaimedInstanceOwner.mockResolvedValueOnce(false);
userRepository.findOne.mockResolvedValueOnce(user);
userRepository.save.mockResolvedValueOnce(expected);
const actual = await ownershipService.setupOwner(payload);
expect(userRepository.save).toHaveBeenCalledWith(user, { transaction: false });
expect(eventService.emit).toHaveBeenCalledWith('instance-owner-setup', {
userId: 'newUserId',
});
expect(actual.id).toEqual('newUserId');
});
it('should normalize email to lowercase', async () => {
const user = mock<User>({
id: 'userId',
role: GLOBAL_OWNER_ROLE,
authIdentities: [],
});
const payload = {
email: 'Admin@Example.COM',
password: 'NewPassword123',
firstName: 'Jane',
lastName: 'Doe',
};
userRepository.hasClaimedInstanceOwner.mockResolvedValueOnce(false);
userRepository.findOne.mockResolvedValueOnce(user);
userRepository.save.mockResolvedValueOnce(user);
await ownershipService.setupOwner(payload);
expect(user.email).toBe('admin@example.com');
});
it('should skip hasInstanceOwner check when override is true', async () => {
const user = mock<User>({
id: 'userId',
role: GLOBAL_OWNER_ROLE,
authIdentities: [],
});
userRepository.findOne.mockResolvedValueOnce(user);
userRepository.save.mockResolvedValueOnce(user);
await ownershipService.setupOwner(
{ email: 'a@b.com', password: 'hash', firstName: 'A', lastName: 'B' },
{ overwriteExisting: true },
);
expect(userRepository.hasClaimedInstanceOwner).not.toHaveBeenCalled();
});
it('should use pre-hashed password when preHashed is true', async () => {
const user = mock<User>({
id: 'userId',
role: GLOBAL_OWNER_ROLE,
authIdentities: [],
});
const preHashedPassword = '$2b$10$abcdefghijklmnopqrstuuABCDEFGHIJKLMNOPQRSTUVWXYZ01234';
userRepository.findOne.mockResolvedValueOnce(user);
userRepository.save.mockResolvedValueOnce(user);
await ownershipService.setupOwner(
{ email: 'a@b.com', password: preHashedPassword, firstName: 'A', lastName: 'B' },
{ overwriteExisting: true, passwordIsHashed: true },
);
expect(user.password).toBe(preHashedPassword);
expect(passwordUtility.hash).not.toHaveBeenCalled();
});
});
});

View File

@ -1,38 +1,27 @@
import { OwnerSetupRequestDto } from '@n8n/api-types';
import { Logger } from '@n8n/backend-common';
import { CacheService, EventService } from '@n8n/backend-services';
import type { ListQueryDb } from '@n8n/db';
import {
GLOBAL_OWNER_ROLE,
Project,
User,
ProjectRelationRepository,
ProjectRepository,
SharedWorkflowRepository,
UserRepository,
Role,
SettingsRepository,
Scope,
SharedWorkflowRepository,
User,
UserRepository,
} from '@n8n/db';
import { Service } from '@n8n/di';
import config from '@/config';
import { BadRequestError } from '@n8n/errors';
import { PasswordUtility } from './password.utility';
import { CacheService } from './cache/cache.service';
@Service()
export class OwnershipService {
constructor(
private cacheService: CacheService,
private eventService: EventService,
private logger: Logger,
private passwordUtility: PasswordUtility,
private projectRelationRepository: ProjectRelationRepository,
private projectRepository: ProjectRepository,
private sharedWorkflowRepository: SharedWorkflowRepository,
private userRepository: UserRepository,
private settingsRepository: SettingsRepository,
) {}
// To make use of the cache service we should store POJOs, these
@ -256,55 +245,4 @@ export class OwnershipService {
async hasInstanceOwner() {
return await this.userRepository.hasClaimedInstanceOwner();
}
async setupOwner(
payload: OwnerSetupRequestDto,
options?: { overwriteExisting?: boolean; passwordIsHashed?: boolean },
) {
const { email, firstName, lastName, password } = payload;
if (!options?.overwriteExisting && (await this.hasInstanceOwner())) {
this.logger.debug(
'Request to claim instance ownership failed because instance owner already exists',
);
throw new BadRequestError('Instance owner already setup');
}
let shellUser = await this.userRepository.findOne({
where: { role: { slug: GLOBAL_OWNER_ROLE.slug } },
relations: ['role'],
});
if (!shellUser) {
this.logger.error('Could not find shell user with global:owner role');
throw new BadRequestError('Instance owner shell user not found');
}
shellUser.email = email.toLowerCase();
shellUser.firstName = firstName;
shellUser.lastName = lastName;
shellUser.lastActiveAt = new Date();
shellUser.password = options?.passwordIsHashed
? password
: await this.passwordUtility.hash(password);
shellUser = await this.userRepository.save(shellUser, { transaction: false });
this.logger.info('Owner was set up successfully');
this.eventService.emit('instance-owner-setup', { userId: shellUser.id });
// The next block needs to be deleted and is temporary for now
// See packages/cli/src/config/schema.ts for more info
// We update the SettingsRepository so when we "startup" next time
// the config state is restored.
// #region Delete me
await this.settingsRepository.update(
{ key: 'userManagement.isInstanceOwnerSetUp' },
{ value: JSON.stringify(true) },
);
config.set('userManagement.isInstanceOwnerSetUp', true);
// #endregion
return shellUser;
}
}

View File

@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/unbound-method */
import type { Logger } from '@n8n/backend-common';
import type { EventService, WorkflowSharingService } from '@n8n/backend-services';
import type { EventService, WorkflowSharingService, OwnershipService } from '@n8n/backend-services';
import { mockLogger } from '@n8n/backend-test-utils';
import type { GlobalConfig, WorkflowsConfig } from '@n8n/config';
import type { Project, WorkflowEntity, WorkflowHistory, WorkflowRepository } from '@n8n/db';
@ -45,7 +45,6 @@ import type {
} from '@/scheduling/schedule-trigger-node/schedule-trigger-job-registrar';
import type { PolicyEnforcementService } from '@/policy/policy-enforcement.service';
import { PolicyViolationError } from '@/policy/policy-violation.error';
import type { OwnershipService } from '@/services/ownership.service';
import type { PollCursorService } from '@/workflows/triggers/poll-cursor.service';
import { TriggerExecutionContextFactory } from '@/workflows/triggers/trigger-execution-context.factory';
import type { WorkflowExecutionService } from '@/workflows/workflow-execution.service';

View File

@ -27,7 +27,7 @@ import { ExecutionAlreadyResumingError } from '@/errors/execution-already-resumi
import { ExecutionPersistence } from '@/executions/execution-persistence';
import { NodeTypes } from '@/node-types';
import type { MultiMainSetup } from '@/scaling/multi-main-setup.ee';
import type { OwnershipService } from '@/services/ownership.service';
import type { OwnershipService } from '@n8n/backend-services';
import type { WorkflowPublisherService } from '@/workflows/workflow-publisher.service';
import { WaitTracker } from '@/wait-tracker';
import type { WorkflowRunner } from '@/workflow-runner';

View File

@ -1,5 +1,5 @@
import { Logger, ModuleRegistry } from '@n8n/backend-common';
import { EventService, UrlService } from '@n8n/backend-services';
import { EventService, UrlService, OwnershipService } from '@n8n/backend-services';
import { mockInstance } from '@n8n/backend-test-utils';
import { ExecutionsConfig, GlobalConfig, type NodesConfig, WorkflowsConfig } from '@n8n/config';
import type { WorkflowEntity, Project, WorkflowHistory } from '@n8n/db';
@ -47,7 +47,6 @@ import { AgentsService } from '@/modules/agents/agents.service';
import { AgentsSettingsService } from '@/modules/agents/agents-settings.service';
import { DataTableProxyService } from '@/modules/data-table/data-table-proxy.service';
import { NodeTypes } from '@/node-types';
import { OwnershipService } from '@/services/ownership.service';
import { WorkflowStatisticsService } from '@/services/workflow-statistics.service';
import { Telemetry } from '@/telemetry';
import {

View File

@ -17,7 +17,7 @@ import type {
import { VariablesService } from '@/environments.ee/variables/variables.service.ee';
import { ExecutionPersistence } from '@/executions/execution-persistence';
import { NodeTypes } from '@/node-types';
import { OwnershipService } from '@/services/ownership.service';
import { OwnershipService } from '@n8n/backend-services';
import {
getLastExecutedNodeData,
getLastExecutedNodeRuns,

View File

@ -53,7 +53,7 @@ import {
} from '@/executions/pre-execution-checks';
import { ManualExecutionService } from '@/manual-execution.service';
import { EngineV2Dispatcher } from '@/services/engine-v2-dispatcher.service';
import { OwnershipService } from '@/services/ownership.service';
import { OwnershipService } from '@n8n/backend-services';
import { Telemetry } from '@/telemetry';
import * as WorkflowExecuteAdditionalData from '@/workflow-execute-additional-data';
import { EXECUTION_ENDED_WITHOUT_RESPONSE } from '@/webhooks/constants';

View File

@ -54,7 +54,7 @@ import { PollTriggerJobRegistrar } from '@/scheduling/poll-trigger-node/poll-tri
import type { ScheduleTriggerCollectionSession } from '@/scheduling/schedule-trigger-node/schedule-trigger-job-registrar';
import { ScheduleTriggerJobRegistrar } from '@/scheduling/schedule-trigger-node/schedule-trigger-job-registrar';
import { ActiveWorkflowsService } from '@/services/active-workflows.service';
import { OwnershipService } from '@/services/ownership.service';
import { OwnershipService } from '@n8n/backend-services';
import * as WebhookHelpers from '@/webhooks/webhook-helpers';
import { WebhookService } from '@/webhooks/webhook.service';
import * as WorkflowExecuteAdditionalData from '@/workflow-execute-additional-data';

View File

@ -17,7 +17,7 @@ import * as WorkflowExecuteAdditionalData from '@/workflow-execute-additional-da
import { WorkflowRunner } from '@/workflow-runner';
import { NodeTypes } from '../../node-types';
import { OwnershipService } from '../../services/ownership.service';
import { OwnershipService } from '@n8n/backend-services';
import { ChatExecutionManager } from '../chat-execution-manager';
import type { ChatMessage } from '../chat-service.types';

View File

@ -25,7 +25,7 @@ import { WorkflowRunner } from '../workflow-runner';
import type { ChatMessage } from './chat-service.types';
import { findResumeNode, redirectIfToolExecutor } from './utils';
import { NodeTypes } from '../node-types';
import { OwnershipService } from '../services/ownership.service';
import { OwnershipService } from '@n8n/backend-services';
import { stripToolSuffix } from '../utils';
@Service()

View File

@ -26,7 +26,7 @@ import { License } from '@/license';
import { LoadNodesAndCredentials } from '@/load-nodes-and-credentials';
import { CommunityPackagesService } from '@/modules/community-packages/community-packages.service';
import { PostHogClient } from '@/posthog';
import { OwnershipService } from '@/services/ownership.service';
import { OwnershipService } from '@n8n/backend-services';
import { ShutdownService } from '@/shutdown/shutdown.service';
import { TaskRunnerModule } from '@/task-runners/task-runner-module';
import { WorkflowRunner } from '@/workflow-runner';

View File

@ -28,7 +28,7 @@ import { License } from '@/license';
import { LoadNodesAndCredentials } from '@/load-nodes-and-credentials';
import { CommunityPackagesService } from '@/modules/community-packages/community-packages.service';
import { PostHogClient } from '@/posthog';
import { OwnershipService } from '@/services/ownership.service';
import { OwnershipService } from '@n8n/backend-services';
import { ShutdownService } from '@/shutdown/shutdown.service';
import { TaskRunnerModule } from '@/task-runners/task-runner-module';
import { WorkflowRunner } from '@/workflow-runner';

View File

@ -12,7 +12,7 @@ import { sep } from 'path';
import { z } from 'zod';
import { ActiveExecutions } from '@/active-executions';
import { OwnershipService } from '@/services/ownership.service';
import { OwnershipService } from '@n8n/backend-services';
import { findCliWorkflowStart } from '@/utils';
import { WorkflowRunner } from '@/workflow-runner';

View File

@ -1,4 +1,4 @@
import { EventService } from '@n8n/backend-services';
import { EventService, OwnershipService } from '@n8n/backend-services';
import { WorkflowRepository } from '@n8n/db';
import { Command } from '@n8n/decorators';
import { Container } from '@n8n/di';
@ -7,7 +7,6 @@ import { ExecutionBaseError, UnexpectedError, UserError } from 'n8n-workflow';
import { z } from 'zod';
import { ActiveExecutions } from '@/active-executions';
import { OwnershipService } from '@/services/ownership.service';
import { findCliWorkflowStart, isWorkflowIdValid } from '@/utils';
import { WorkflowRunner } from '@/workflow-runner';
import { getWorkflowProjectDetailsSafe } from '@/workflows/utils';

View File

@ -6,14 +6,14 @@ import { OwnerController } from '@/controllers/owner.controller';
import { BadRequestError } from '@n8n/errors';
import type { PostHogClient } from '@/posthog';
import type { BannerService } from '@/services/banner.service';
import type { OwnershipService } from '@/services/ownership.service';
import type { OwnerSetupService } from '@/services/owner-setup.service';
import type { UserService } from '@/services/user.service';
describe('OwnerController', () => {
const authService = mock<AuthService>();
const bannerService = mock<BannerService>();
const userService = mock<UserService>();
const ownershipService = mock<OwnershipService>();
const ownerSetupService = mock<OwnerSetupService>();
const postHogClient = mock<PostHogClient>();
const controller = new OwnerController(
@ -21,12 +21,12 @@ describe('OwnerController', () => {
bannerService,
userService,
postHogClient,
ownershipService,
ownerSetupService,
);
describe('setupOwner', () => {
it('should pass on errors from the service', async () => {
ownershipService.setupOwner.mockRejectedValueOnce(
ownerSetupService.setupOwner.mockRejectedValueOnce(
new BadRequestError('Instance owner already setup'),
);

View File

@ -6,8 +6,8 @@ import { Response } from 'express';
import { AuthService } from '@/auth/auth.service';
import { PostHogClient } from '@/posthog';
import { BannerService } from '@/services/banner.service';
import { OwnershipService } from '@/services/ownership.service';
import { UserService } from '@/services/user.service';
import { OwnerSetupService } from '@/services/owner-setup.service';
@RestController('/owner')
export class OwnerController {
@ -16,7 +16,7 @@ export class OwnerController {
private readonly bannerService: BannerService,
private readonly userService: UserService,
private readonly postHog: PostHogClient,
private readonly ownershipService: OwnershipService,
private readonly ownerSetupService: OwnerSetupService,
) {}
/**
@ -24,7 +24,7 @@ export class OwnerController {
*/
@Post('/setup', { skipAuth: true })
async setupOwner(req: AuthenticatedRequest, res: Response, @Body payload: OwnerSetupRequestDto) {
const owner = await this.ownershipService.setupOwner(payload);
const owner = await this.ownerSetupService.setupOwner(payload);
this.authService.issueCookie(res, owner, req.authInfo?.usedMfa ?? false, req.browserId);
return await this.userService.toPublic(owner, { posthog: this.postHog, withScopes: true });
}

View File

@ -9,13 +9,16 @@ import type {
import { mock } from 'vitest-mock-extended';
import type { CredentialConnectionStatusProxy } from '@/credentials/credential-connection-status-proxy';
import { type CredentialsFinderService, type RoleService } from '@n8n/backend-services';
import type {
CredentialsFinderService,
RoleService,
OwnershipService,
} from '@n8n/backend-services';
import type { CredentialsService } from '@/credentials/credentials.service';
import { EnterpriseCredentialsService } from '@/credentials/credentials.service.ee';
import type { ExternalSecretsConfig } from '@/modules/external-secrets.ee/external-secrets.config';
import type { SecretsProviderAccessCheckService } from '@/modules/external-secrets.ee/secret-provider-access-check.service.ee';
import type { PolicyEnforcementService } from '@/policy/policy-enforcement.service';
import type { OwnershipService } from '@/services/ownership.service';
import type { ProjectService } from '@/services/project.service.ee';
describe('EnterpriseCredentialsService', () => {

View File

@ -2,10 +2,11 @@ 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,
type RoleService,
type CredentialsFinderService,
import type {
EventService,
RoleService,
CredentialsFinderService,
OwnershipService,
} from '@n8n/backend-services';
import type {
CredentialsRepository,
@ -66,7 +67,6 @@ import {
} from '@/oauth/dcr-managed-fields';
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 { mockExistingCredential } from './credentials.test-data';

View File

@ -17,9 +17,8 @@ import { TransferCredentialError } from '@/errors/response-errors/transfer-crede
import { ExternalSecretsConfig } from '@/modules/external-secrets.ee/external-secrets.config';
import { SecretsProviderAccessCheckService } from '@/modules/external-secrets.ee/secret-provider-access-check.service.ee';
import { PolicyEnforcementService } from '@/policy/policy-enforcement.service';
import { OwnershipService } from '@/services/ownership.service';
import { OwnershipService, RoleService, CredentialsFinderService } from '@n8n/backend-services';
import { ProjectService } from '@/services/project.service.ee';
import { RoleService, CredentialsFinderService } from '@n8n/backend-services';
import { CredentialConnectionStatusProxy } from './credential-connection-status-proxy';
import { CredentialsService } from './credentials.service';

View File

@ -1,6 +1,11 @@
import type { CreateCredentialDto, CredentialConnectionStatus } from '@n8n/api-types';
import { Logger } from '@n8n/backend-common';
import { EventService, RoleService, CredentialsFinderService } from '@n8n/backend-services';
import {
EventService,
RoleService,
CredentialsFinderService,
OwnershipService,
} from '@n8n/backend-services';
import { Time } from '@n8n/constants';
import {
Project,
@ -74,7 +79,6 @@ import type { PolicyActor } from '@/policy/policy-enforcement-backend';
import { PolicyEnforcementService } from '@/policy/policy-enforcement.service';
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 { CredentialConnectionStatusProxy } from './credential-connection-status-proxy';

View File

@ -29,7 +29,7 @@ import { TestRunError } from '@/evaluation.ee/test-runner/errors.ee';
import type { License } from '@/license';
import { LoadNodesAndCredentials } from '@/load-nodes-and-credentials';
import type { Publisher } from '@/scaling/pubsub/publisher.service';
import type { OwnershipService } from '@/services/ownership.service';
import type { OwnershipService } from '@n8n/backend-services';
import type { Telemetry } from '@/telemetry';
import type { WorkflowRunner } from '@/workflow-runner';
import type { WorkflowHistoryService } from '@/workflows/workflow-history/workflow-history.service';

View File

@ -1,5 +1,5 @@
import { Logger } from '@n8n/backend-common';
import { EventService } from '@n8n/backend-services';
import { EventService, OwnershipService } from '@n8n/backend-services';
import { ExecutionsConfig } from '@n8n/config';
import type { EvaluationConfig, User } from '@n8n/db';
import {
@ -52,7 +52,6 @@ import {
} from '@/evaluation.ee/test-runner/utils.ee';
import { License } from '@/license';
import { Publisher } from '@/scaling/pubsub/publisher.service';
import { OwnershipService } from '@/services/ownership.service';
import { Telemetry } from '@/telemetry';
import { WorkflowRunner } from '@/workflow-runner';
import { getWorkflowProjectDetailsSafe } from '@/workflows/utils';

View File

@ -1,5 +1,5 @@
import type { LicenseState } from '@n8n/backend-common';
import { EventService } from '@n8n/backend-services';
import { EventService, type OwnershipService } from '@n8n/backend-services';
import { mockInstance } from '@n8n/backend-test-utils';
import { EMPTY_CANVAS_GROUPS_FLAG } from '@n8n/api-types';
import type { GlobalConfig } from '@n8n/config';
@ -41,7 +41,6 @@ import { OtelConfig } from '@/modules/otel/otel.config';
import type { PolicyRule } from '@/modules/type-availability-policies/policy-rule.types';
import type { NodeTypes } from '@/node-types';
import type { PostHogClient } from '@/posthog';
import type { OwnershipService } from '@/services/ownership.service';
import type { Telemetry } from '@/telemetry';
const flushPromises = async () => await new Promise((resolve) => setImmediate(resolve));

View File

@ -1,6 +1,6 @@
import { EMPTY_CANVAS_GROUPS_FLAG } from '@n8n/api-types';
import { LicenseState } from '@n8n/backend-common';
import { EventService } from '@n8n/backend-services';
import { EventService, OwnershipService } from '@n8n/backend-services';
import { GlobalConfig } from '@n8n/config';
import {
CredentialsRepository,
@ -59,7 +59,6 @@ import type {
} from '@/modules/type-availability-policies/policy-rule.types';
import { NodeTypes } from '@/node-types';
import { PostHogClient } from '@/posthog';
import { OwnershipService } from '@/services/ownership.service';
import { EventRelay } from './event-relay';
import { Telemetry } from '../../telemetry';

View File

@ -8,8 +8,7 @@ import { createRunExecutionData, NodeOperationError } from 'n8n-workflow';
import { mock } from 'vitest-mock-extended';
import { PolicyViolationError } from '@/policy/policy-violation.error';
import { OwnershipService } from '@/services/ownership.service';
import { UrlService } from '@n8n/backend-services';
import { OwnershipService, UrlService } from '@n8n/backend-services';
import { WorkflowExecutionService } from '@/workflows/workflow-execution.service';
import { executeErrorWorkflow } from '../execute-error-workflow';

View File

@ -1,6 +1,6 @@
import type { PushMessage } from '@n8n/api-types';
import { Logger } from '@n8n/backend-common';
import { EventService } from '@n8n/backend-services';
import { EventService, OwnershipService } from '@n8n/backend-services';
import { mockInstance } from '@n8n/backend-test-utils';
import { ExecutionsConfig } from '@n8n/config';
import type { Project, User } from '@n8n/db';
@ -42,7 +42,6 @@ import { ExecutionRedactionServiceProxy } from '@/executions/execution-redaction
import { ExternalHooks } from '@/external-hooks';
import { Push } from '@/push';
import { ExecutionMetadataService } from '@/services/execution-metadata.service';
import { OwnershipService } from '@/services/ownership.service';
import { WorkflowStatisticsService } from '@/services/workflow-statistics.service';
import { WorkflowHookContextService } from '@/workflow-hook-context.service';
import { WorkflowExecutionService } from '@/workflows/workflow-execution.service';

View File

@ -7,8 +7,7 @@ import { toErrorWorkflowContext } from 'n8n-workflow';
import type { IWorkflowErrorData } from '@/interfaces';
import { isPolicyRefusal } from '@/policy/policy-violation.error';
import { OwnershipService } from '@/services/ownership.service';
import { UrlService } from '@n8n/backend-services';
import { OwnershipService, UrlService } from '@n8n/backend-services';
/**
* Resolved lazily. A static import would close the cycle

View File

@ -1,4 +1,4 @@
import type { EventService } from '@n8n/backend-services';
import type { EventService, OwnershipService } from '@n8n/backend-services';
import { mockLogger } from '@n8n/backend-test-utils';
import type { ExecutionsConfig } from '@n8n/config';
import type { IExecutionResponse, Project } from '@n8n/db';
@ -9,7 +9,6 @@ import { ExecutionAlreadyResumingError } from '@/errors/execution-already-resumi
import { EnqueuedExecutionRecoveryService } from '@/executions/enqueued-execution-recovery.service';
import type { ExecutionCrashService } from '@/executions/execution-crash.service';
import type { ExecutionService } from '@/executions/execution.service';
import type { OwnershipService } from '@/services/ownership.service';
import type { WorkflowPublisherService } from '@/workflows/workflow-publisher.service';
import type { WorkflowRunner } from '@/workflow-runner';

View File

@ -34,11 +34,9 @@ import { ExecutionPersistence } from '@/executions/execution-persistence';
import { ExecutionRecoveryService } from '@/executions/execution-recovery.service';
import { ExternalHooks } from '@/external-hooks';
import { Push } from '@/push';
import { OwnershipService } from '@/services/ownership.service';
import { OwnershipService, type EventService, WorkflowSharingService } from '@n8n/backend-services';
import { WorkflowPublicationNotifier } from '@/workflows/publication/workflow-publication-notifier';
import { WorkflowPushNotifier } from '@/workflows/workflow-push-notifier.service';
import type { EventService } from '@n8n/backend-services';
import { WorkflowSharingService } from '@n8n/backend-services';
import { createExecution } from '@test-integration/db/executions';
import { IN_PROGRESS_EXECUTION_DATA, OOM_WORKFLOW } from './constants';

View File

@ -1,5 +1,5 @@
import { DeleteExecutionsDto } from '@n8n/api-types';
import type { EventService } from '@n8n/backend-services';
import type { EventService, OwnershipService } from '@n8n/backend-services';
import { mockInstance } from '@n8n/backend-test-utils';
import { GlobalConfig } from '@n8n/config';
import type {
@ -33,7 +33,6 @@ import type { ExecutionRequest } from '@/executions/execution.types';
import type { ExecutionStopService } from '@/scaling/execution-stop.service';
import { ScalingService } from '@/scaling/scaling.service';
import type { Job } from '@/scaling/scaling.types';
import type { OwnershipService } from '@/services/ownership.service';
import type { EngineDataPlaneProxyService } from '@/services/engine-data-plane-proxy.service';
import type { WaitTracker } from '@/wait-tracker';
import type { WorkflowRunner } from '@/workflow-runner';

View File

@ -1,5 +1,5 @@
import { Logger } from '@n8n/backend-common';
import { EventService } from '@n8n/backend-services';
import { EventService, OwnershipService } from '@n8n/backend-services';
import { ExecutionsConfig } from '@n8n/config';
import { Service } from '@n8n/di';
import { ErrorReporter } from 'n8n-core';
@ -9,7 +9,6 @@ import { strict as assert } from 'node:assert';
import { ExecutionAlreadyResumingError } from '@/errors/execution-already-resuming.error';
import { ExecutionCrashService } from '@/executions/execution-crash.service';
import { ExecutionService } from '@/executions/execution.service';
import { OwnershipService } from '@/services/ownership.service';
import { WorkflowPublisherService } from '@/workflows/workflow-publisher.service';
import { WorkflowRunner } from '@/workflow-runner';

View File

@ -24,7 +24,7 @@ import { getLifecycleHooksForRegularMain } from '@/execution-lifecycle/execution
import { ExecutionCrashService } from '@/executions/execution-crash.service';
import { ExecutionPersistence } from '@/executions/execution-persistence';
import { Push } from '@/push';
import { OwnershipService } from '@/services/ownership.service';
import { OwnershipService } from '@n8n/backend-services';
import { UserManagementMailer } from '@/user-management/email/user-management-mailer';
import { WorkflowPushNotifier } from '@/workflows/workflow-push-notifier.service';

View File

@ -1,7 +1,7 @@
import type { DeleteExecutionsDto } from '@n8n/api-types';
import { ExecutionRedactionQueryDtoSchema } from '@n8n/api-types';
import { Logger } from '@n8n/backend-common';
import { EventService } from '@n8n/backend-services';
import { EventService, OwnershipService } from '@n8n/backend-services';
import { GlobalConfig } from '@n8n/config';
import type {
CreateExecutionPayload,
@ -52,7 +52,6 @@ import { License } from '@/license';
import { NodeTypes } from '@/node-types';
import { ExecutionStopService } from '@/scaling/execution-stop.service';
import { EngineDataPlaneProxyService } from '@/services/engine-data-plane-proxy.service';
import { OwnershipService } from '@/services/ownership.service';
import { WaitTracker } from '@/wait-tracker';
import { WorkflowRunner } from '@/workflow-runner';
import { getWorkflowProjectDetailsSafe } from '@/workflows/utils';

View File

@ -14,9 +14,8 @@ import { mock } from 'vitest-mock-extended';
import type { Logger } from '@n8n/backend-common';
import type { CredentialsFinderService } from '@n8n/backend-services';
import type { CredentialsFinderService, OwnershipService } from '@n8n/backend-services';
import type { NodeTypes } from '@/node-types';
import type { OwnershipService } from '@/services/ownership.service';
import type { ProjectService } from '@/services/project.service.ee';
import { CredentialsPermissionChecker } from '../credentials-permission-checker';

View File

@ -11,8 +11,7 @@ import {
SubworkflowPolicyDenialError,
} from '@/errors/subworkflow-policy-denial.error';
import type { AccessService } from '@/services/access.service';
import { OwnershipService } from '@/services/ownership.service';
import type { UrlService } from '@n8n/backend-services';
import { OwnershipService, type UrlService } from '@n8n/backend-services';
import { SubworkflowPolicyChecker } from '../subworkflow-policy-checker';

View File

@ -7,9 +7,12 @@ import type { INode, INodeTypeDescription } from 'n8n-workflow';
import { getActiveCredentialTypes, UserError } from 'n8n-workflow';
import { isCredSharingEnabled } from '@/constants/credential-sharing';
import { CredentialsFinderService, type UnusableCredential } from '@n8n/backend-services';
import {
CredentialsFinderService,
type UnusableCredential,
OwnershipService,
} from '@n8n/backend-services';
import { NodeTypes } from '@/node-types';
import { OwnershipService } from '@/services/ownership.service';
import { ProjectService } from '@/services/project.service.ee';
class InvalidCredentialError extends UserError {

View File

@ -6,8 +6,7 @@ import { type INode, type IWorkflowBase, type WorkflowSettings } from 'n8n-workf
import { SubworkflowPolicyDenialError } from '@/errors/subworkflow-policy-denial.error';
import { AccessService } from '@/services/access.service';
import { OwnershipService } from '@/services/ownership.service';
import { UrlService } from '@n8n/backend-services';
import { OwnershipService, UrlService } from '@n8n/backend-services';
type Policy = WorkflowSettings.CallerPolicy;
type DenialPolicy = Exclude<Policy, 'any'>;

View File

@ -2,13 +2,13 @@ import type { Logger } from '@n8n/backend-common';
import type { InstanceSettingsLoaderConfig } from '@n8n/config';
import { mock } from 'vitest-mock-extended';
import type { OwnershipService } from '@/services/ownership.service';
import type { OwnerSetupService } from '@/services/owner-setup.service';
import { OwnerInstanceSettingsLoader } from '../loaders/owner.instance-settings-loader';
describe('OwnerInstanceSettingsLoader', () => {
const logger = mock<Logger>({ scoped: vi.fn().mockReturnThis() });
const ownershipService = mock<OwnershipService>();
const ownerSetupService = mock<OwnerSetupService>();
const validBcryptHash = '$2b$10$abcdefghijklmnopqrstuuABCDEFGHIJKLMNOPQRSTUVWXYZ01234';
@ -22,7 +22,7 @@ describe('OwnerInstanceSettingsLoader', () => {
...configOverrides,
} as InstanceSettingsLoaderConfig;
return new OwnerInstanceSettingsLoader(config, ownershipService, logger);
return new OwnerInstanceSettingsLoader(config, ownerSetupService, logger);
};
beforeEach(() => {
@ -37,7 +37,7 @@ describe('OwnerInstanceSettingsLoader', () => {
const result = await loader.run();
expect(result).toBe('skipped');
expect(ownershipService.setupOwner).not.toHaveBeenCalled();
expect(ownerSetupService.setupOwner).not.toHaveBeenCalled();
expect(logger.warn).not.toHaveBeenCalled();
});
@ -47,7 +47,7 @@ describe('OwnerInstanceSettingsLoader', () => {
const result = await loader.run();
expect(result).toBe('skipped');
expect(ownershipService.setupOwner).not.toHaveBeenCalled();
expect(ownerSetupService.setupOwner).not.toHaveBeenCalled();
});
it('should skip when ownerPasswordHash is set', async () => {
@ -56,7 +56,7 @@ describe('OwnerInstanceSettingsLoader', () => {
const result = await loader.run();
expect(result).toBe('skipped');
expect(ownershipService.setupOwner).not.toHaveBeenCalled();
expect(ownerSetupService.setupOwner).not.toHaveBeenCalled();
});
it('should skip when both email and password are set', async () => {
@ -68,7 +68,7 @@ describe('OwnerInstanceSettingsLoader', () => {
const result = await loader.run();
expect(result).toBe('skipped');
expect(ownershipService.setupOwner).not.toHaveBeenCalled();
expect(ownerSetupService.setupOwner).not.toHaveBeenCalled();
});
});
@ -116,7 +116,7 @@ describe('OwnerInstanceSettingsLoader', () => {
expect(logger.info).toHaveBeenCalledWith(
expect.stringContaining('N8N_INSTANCE_OWNER_MANAGED_BY_ENV is enabled'),
);
expect(ownershipService.setupOwner).toHaveBeenCalledWith(
expect(ownerSetupService.setupOwner).toHaveBeenCalledWith(
{
email: 'admin@example.com',
firstName: 'Jane',
@ -128,8 +128,6 @@ describe('OwnerInstanceSettingsLoader', () => {
});
it('should overwrite existing owner', async () => {
ownershipService.hasInstanceOwner.mockResolvedValue(true);
const loader = createLoader({
ownerManagedByEnv: true,
ownerEmail: 'new@example.com',
@ -141,7 +139,7 @@ describe('OwnerInstanceSettingsLoader', () => {
const result = await loader.run();
expect(result).toBe('created');
expect(ownershipService.setupOwner).toHaveBeenCalledWith(
expect(ownerSetupService.setupOwner).toHaveBeenCalledWith(
{
email: 'new@example.com',
firstName: 'New',

View File

@ -3,7 +3,7 @@ import { InstanceSettingsLoaderConfig } from '@n8n/config';
import { Service } from '@n8n/di';
import { z } from 'zod';
import { OwnershipService } from '@/services/ownership.service';
import { OwnerSetupService } from '@/services/owner-setup.service';
import { InstanceBootstrappingError } from '../instance-bootstrapping.error';
@ -45,7 +45,7 @@ const ownerEnvSchema = z
export class OwnerInstanceSettingsLoader {
constructor(
private readonly instanceSettingsLoaderConfig: InstanceSettingsLoaderConfig,
private readonly ownershipService: OwnershipService,
private readonly ownerSetupService: OwnerSetupService,
private logger: Logger,
) {
this.logger = this.logger.scoped('instance-settings-loader');
@ -67,7 +67,7 @@ export class OwnerInstanceSettingsLoader {
throw new InstanceBootstrappingError(result.error.issues[0].message);
}
await this.ownershipService.setupOwner(result.data.payload, result.data.options);
await this.ownerSetupService.setupOwner(result.data.payload, result.data.options);
return 'created';
}

View File

@ -15,9 +15,8 @@ import { NodeOperationError } from 'n8n-workflow';
import type { MockInstance } from 'vitest';
import { mock } from 'vitest-mock-extended';
import type { InstanceWriteAccessService } from '@n8n/backend-services';
import type { InstanceWriteAccessService, OwnershipService } from '@n8n/backend-services';
import * as checkAccess from '@/permissions.ee/check-access';
import type { OwnershipService } from '@/services/ownership.service';
import type { DataTableAggregateService } from '../data-table-aggregate.service';
import { DataTableProxyService } from '../data-table-proxy.service';

View File

@ -27,8 +27,7 @@ import {
import { ForbiddenError } from '@n8n/errors';
import { userHasScopes } from '@/permissions.ee/check-access';
import { InstanceWriteAccessService } from '@n8n/backend-services';
import { OwnershipService } from '@/services/ownership.service';
import { InstanceWriteAccessService, OwnershipService } from '@n8n/backend-services';
import { DataTableAggregateService } from './data-table-aggregate.service';
import { DataTableService } from './data-table.service';

View File

@ -20,7 +20,7 @@ import type { NodeTypes } from '@/node-types';
import type { PostHogClient } from '@/posthog';
import type { DataTableService } from '@/modules/data-table/data-table.service';
import type { WorkflowRunner } from '@/workflow-runner';
import type { OwnershipService } from '@/services/ownership.service';
import type { OwnershipService } from '@n8n/backend-services';
import type { WorkflowFinderService } from '@/workflows/workflow-finder.service';
import type { WorkflowStaticDataService } from '@/workflows/workflow-static-data.service';

View File

@ -49,7 +49,7 @@ import { LoadNodesAndCredentials } from '@/load-nodes-and-credentials';
import { DataTableService } from '@/modules/data-table/data-table.service';
import { NodeTypes } from '@/node-types';
import { PostHogClient } from '@/posthog';
import { OwnershipService } from '@/services/ownership.service';
import { OwnershipService } from '@n8n/backend-services';
import { WorkflowRunner } from '@/workflow-runner';
import { WorkflowFinderService } from '@/workflows/workflow-finder.service';
import { WorkflowStaticDataService } from '@/workflows/workflow-static-data.service';

View File

@ -1,5 +1,5 @@
import type { LicenseState, Logger } from '@n8n/backend-common';
import { EventService } from '@n8n/backend-services';
import { EventService, type OwnershipService } from '@n8n/backend-services';
import type {
NodeExecuteAfterContext,
NodeExecuteBeforeContext,
@ -11,7 +11,6 @@ import { Workflow } from 'n8n-workflow';
import type { INodeTypes, IRun, IRunExecutionData, WorkflowExecuteMode } from 'n8n-workflow';
import type { RelayEventMap } from '@/events/maps/relay.event-map';
import type { OwnershipService } from '@/services/ownership.service';
import type { ExecutionLevelTracer } from '../execution-level-tracer';
import { OtelLifecycleHandler, countInputItems, countOutputItems } from '../otel-lifecycle-handler';

View File

@ -1,5 +1,5 @@
import { LicenseState, Logger } from '@n8n/backend-common';
import { EventService } from '@n8n/backend-services';
import { EventService, OwnershipService } from '@n8n/backend-services';
import { OnLifecycleEvent, OnPubSubEvent } from '@n8n/decorators';
import type {
WorkflowExecuteBeforeContext,
@ -18,7 +18,6 @@ import type { CustomAttributes } from './execution-level-tracer.types';
import { OtelSettingsService } from './otel-settings.service';
import { OtelService } from './otel.service';
import { TraceContextService } from './tracing-context';
import { OwnershipService } from '../../services/ownership.service';
const isCustomTelemetryTag = (value: unknown): value is ICustomTelemetryTag =>
typeof value === 'object' &&

View File

@ -10,7 +10,7 @@ import { mock } from 'vitest-mock-extended';
import { PolicyEnforcementService } from '@/policy/policy-enforcement.service';
import { PolicyViolationError } from '@/policy/policy-violation.error';
import { OwnershipService } from '@/services/ownership.service';
import { OwnershipService } from '@n8n/backend-services';
import { PolicyCheckFailedError } from '../policy-check-failed.error';
import { PolicyDecisionService } from '../policy-decision.service';

View File

@ -3,7 +3,7 @@ import type { WorkflowExecuteBeforeContext } from '@n8n/decorators';
import { Service } from '@n8n/di';
import { PolicyEnforcementService } from '@/policy/policy-enforcement.service';
import { OwnershipService } from '@/services/ownership.service';
import { OwnershipService } from '@n8n/backend-services';
/**
* The execution backstop. Every way an execution starts — main, queue worker,

View File

@ -16,7 +16,7 @@ import type {
} from 'simple-git';
import { buildHttpsGitConfig, buildSshCommand } from '@/modules/promotions.ee/promotions-git.utils';
import { OwnershipService } from '@/services/ownership.service';
import { OwnershipService } from '@n8n/backend-services';
import {
SOURCE_CONTROL_DEFAULT_BRANCH,

View File

@ -1,7 +1,7 @@
import type { Project } from '@n8n/db';
import { mock } from 'vitest-mock-extended';
import type { OwnershipService } from '@/services/ownership.service';
import type { OwnershipService } from '@n8n/backend-services';
import { enforceWorkflowPublishPolicy } from '../enforce-workflow-publish';
import type { PolicyActor } from '../policy-enforcement-backend';

View File

@ -1,6 +1,6 @@
import type { PolicedWorkflow } from '@n8n/decorators';
import type { OwnershipService } from '@/services/ownership.service';
import type { OwnershipService } from '@n8n/backend-services';
import type { PolicyActor } from './policy-enforcement-backend';
import type { PolicyEnforcementService } from './policy-enforcement.service';

View File

@ -1,6 +1,6 @@
import { DynamicStructuredTool } from '@langchain/core/tools';
import type { Logger } from '@n8n/backend-common';
import type { EventService } from '@n8n/backend-services';
import { type EventService, OwnershipService } from '@n8n/backend-services';
import type { ExecutionsConfig } from '@n8n/config';
import type { IExecutionResponse, ExecutionRepository, Project } from '@n8n/db';
import { WorkflowPublishHistoryRepository } from '@n8n/db';
@ -49,7 +49,6 @@ import { ExternalHooks } from '@/external-hooks';
import type { ManualExecutionService } from '@/manual-execution.service';
import { DataTableProxyService } from '@/modules/data-table/data-table-proxy.service';
import type { NodeTypes } from '@/node-types';
import { OwnershipService } from '@/services/ownership.service';
import { WorkflowStatisticsService } from '@/services/workflow-statistics.service';
import * as WorkflowExecuteAdditionalData from '@/workflow-execute-additional-data';
import { WorkflowHookContextService } from '@/workflow-hook-context.service';

View File

@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/unbound-method */
/* eslint-disable @typescript-eslint/naming-convention -- item keys are pinned to the legacy ScheduleTrigger emit shape */
import type { Logger } from '@n8n/backend-common';
import type { EventService } from '@n8n/backend-services';
import type { EventService, OwnershipService } from '@n8n/backend-services';
import type { GlobalConfig } from '@n8n/config';
import type { ExecutionEntity, ExecutionRepository, Project } from '@n8n/db';
import { createDispatchReporter, type ClaimedTask } from '@n8n/scheduler';
@ -11,7 +11,6 @@ import { CRON_NODE_TYPE, SCHEDULE_TRIGGER_NODE_TYPE, UnexpectedError } from 'n8n
import { mock } from 'vitest-mock-extended';
import { DuplicateExecutionError } from '@/errors/duplicate-execution.error';
import type { OwnershipService } from '@/services/ownership.service';
import * as WorkflowExecuteAdditionalData from '@/workflow-execute-additional-data';
import type { TriggerExecutionContextFactory } from '@/workflows/triggers/trigger-execution-context.factory';
import type { WorkflowExecutionService } from '@/workflows/workflow-execution.service';

View File

@ -1,5 +1,5 @@
import { Logger } from '@n8n/backend-common';
import { EventService } from '@n8n/backend-services';
import { EventService, OwnershipService } from '@n8n/backend-services';
import { GlobalConfig } from '@n8n/config';
import { ExecutionRepository } from '@n8n/db';
import { Service } from '@n8n/di';
@ -8,7 +8,6 @@ import { ErrorReporter } from 'n8n-core';
import { CRON_NODE_TYPE, UnexpectedError } from 'n8n-workflow';
import { DuplicateExecutionError } from '@/errors/duplicate-execution.error';
import { OwnershipService } from '@/services/ownership.service';
import * as WorkflowExecuteAdditionalData from '@/workflow-execute-additional-data';
import { TriggerExecutionContextFactory } from '@/workflows/triggers/trigger-execution-context.factory';
import { getWorkflowProjectDetailsSafe } from '@/workflows/utils';

View File

@ -12,8 +12,7 @@ import { FeatureNotLicensedError } from '@/errors/feature-not-licensed.error';
import { BadRequestError } from '@n8n/errors';
import type { License } from '@/license';
import { AiGatewayService } from '@/services/ai-gateway.service';
import type { OwnershipService } from '@/services/ownership.service';
import type { UrlService } from '@n8n/backend-services';
import type { OwnershipService, UrlService } from '@n8n/backend-services';
const INSTANCE_BASE_URL = 'https://my-n8n.example.com';

View File

@ -3,6 +3,7 @@ import { tmpdir } from 'node:os';
import { join } from 'node:path';
import type { Mock } from 'vitest';
import type { LicenseState, Logger, ModuleRegistry } from '@n8n/backend-common';
import type { OwnershipService, UrlService } from '@n8n/backend-services';
import type { GlobalConfig, SecurityConfig } from '@n8n/config';
import type { WorkflowRepository } from '@n8n/db';
import { Container } from '@n8n/di';
@ -19,10 +20,8 @@ import { CommunityPackagesConfig } from '@/modules/community-packages/community-
import type { PushConfig } from '@/push/push.config';
import type { AiUsageService } from '@/services/ai-usage.service';
import { FrontendService, type PublicFrontendSettings } from '@/services/frontend.service';
import type { UrlService } from '@n8n/backend-services';
import type { WorkflowReviewPolicyService } from '@/services/workflow-review-policy.service';
import type { UserManagementMailer } from '@/user-management/email';
import type { OwnershipService } from '../ownership.service';
// Mock the workflow history helper functions to avoid DI container issues in tests
vi.mock('@/workflows/workflow-history/workflow-history-helper', () => ({

View File

@ -0,0 +1,132 @@
import { Logger } from '@n8n/backend-common';
import type { EventService, OwnershipService } from '@n8n/backend-services';
import { mockInstance } from '@n8n/backend-test-utils';
import { GLOBAL_OWNER_ROLE, type SettingsRepository, User, UserRepository } from '@n8n/db';
import { BadRequestError } from '@n8n/errors';
import { mock } from 'vitest-mock-extended';
import { OwnerSetupService } from '@/services/owner-setup.service';
import { PasswordUtility } from '@/services/password.utility';
describe('OwnerSetupService', () => {
const ownershipService = mock<OwnershipService>();
const eventService = mock<EventService>();
const logger = mockInstance(Logger);
const passwordUtility = mockInstance(PasswordUtility);
const userRepository = mockInstance(UserRepository);
const settingsRepository = mock<SettingsRepository>();
const ownerSetupService = new OwnerSetupService(
ownershipService,
eventService,
logger,
passwordUtility,
userRepository,
settingsRepository,
);
beforeEach(() => {
vi.clearAllMocks();
});
it('should throw a BadRequestError if the instance owner is already setup', async () => {
ownershipService.hasInstanceOwner.mockResolvedValueOnce(true);
const execution = ownerSetupService.setupOwner(mock());
await expect(execution).rejects.toThrow(BadRequestError);
await expect(execution).rejects.toThrow('Instance owner already setup');
expect(userRepository.save).not.toHaveBeenCalled();
expect(eventService.emit).not.toHaveBeenCalled();
expect(logger.debug).toHaveBeenCalledWith(
'Request to claim instance ownership failed because instance owner already exists',
);
});
it('should throw a BadRequestError if the shell user is not found', async () => {
ownershipService.hasInstanceOwner.mockResolvedValueOnce(false);
userRepository.findOne.mockResolvedValueOnce(null);
const execution = ownerSetupService.setupOwner(mock());
await expect(execution).rejects.toThrow(BadRequestError);
await expect(execution).rejects.toThrow('Instance owner shell user not found');
expect(userRepository.save).not.toHaveBeenCalled();
});
it('should setup the instance owner successfully', async () => {
const user = mock<User>({
id: 'userId',
role: GLOBAL_OWNER_ROLE,
authIdentities: [],
});
const payload = {
email: 'valid@email.com',
password: 'NewPassword123',
firstName: 'Jane',
lastName: 'Doe',
};
const expected = { ...user, ...payload, id: 'newUserId' };
ownershipService.hasInstanceOwner.mockResolvedValueOnce(false);
userRepository.findOne.mockResolvedValueOnce(user);
userRepository.save.mockResolvedValueOnce(expected);
const actual = await ownerSetupService.setupOwner(payload);
expect(userRepository.save).toHaveBeenCalledWith(user, { transaction: false });
expect(eventService.emit).toHaveBeenCalledWith('instance-owner-setup', {
userId: 'newUserId',
});
expect(settingsRepository.update).toHaveBeenCalledWith(
{ key: 'userManagement.isInstanceOwnerSetUp' },
{ value: JSON.stringify(true) },
);
expect(actual.id).toEqual('newUserId');
});
it('should normalize email to lowercase', async () => {
const user = mock<User>({ id: 'userId', role: GLOBAL_OWNER_ROLE, authIdentities: [] });
ownershipService.hasInstanceOwner.mockResolvedValueOnce(false);
userRepository.findOne.mockResolvedValueOnce(user);
userRepository.save.mockResolvedValueOnce(user);
await ownerSetupService.setupOwner({
email: 'Admin@Example.COM',
password: 'NewPassword123',
firstName: 'Jane',
lastName: 'Doe',
});
expect(user.email).toBe('admin@example.com');
});
it('should skip the owner check when overwriteExisting is true', async () => {
const user = mock<User>({ id: 'userId', role: GLOBAL_OWNER_ROLE, authIdentities: [] });
userRepository.findOne.mockResolvedValueOnce(user);
userRepository.save.mockResolvedValueOnce(user);
await ownerSetupService.setupOwner(
{ email: 'a@b.com', password: 'hash', firstName: 'A', lastName: 'B' },
{ overwriteExisting: true },
);
expect(ownershipService.hasInstanceOwner).not.toHaveBeenCalled();
});
it('should use a pre-hashed password when passwordIsHashed is true', async () => {
const user = mock<User>({ id: 'userId', role: GLOBAL_OWNER_ROLE, authIdentities: [] });
const preHashedPassword = '$2b$10$abcdefghijklmnopqrstuuABCDEFGHIJKLMNOPQRSTUVWXYZ01234';
userRepository.findOne.mockResolvedValueOnce(user);
userRepository.save.mockResolvedValueOnce(user);
await ownerSetupService.setupOwner(
{ email: 'a@b.com', password: preHashedPassword, firstName: 'A', lastName: 'B' },
{ overwriteExisting: true, passwordIsHashed: true },
);
expect(user.password).toBe(preHashedPassword);
expect(passwordUtility.hash).not.toHaveBeenCalled();
});
});

View File

@ -3,11 +3,13 @@ import { GLOBAL_OWNER_ROLE } from '@n8n/db';
import { Container } from '@n8n/di';
import { HooksService } from '@/services/hooks.service';
import { OwnershipService } from '@/services/ownership.service';
import { OwnershipService } from '@n8n/backend-services';
import { OwnerSetupService } from '@/services/owner-setup.service';
import { createUserShell } from '@test-integration/db/users';
let hookService: HooksService;
let ownershipService: OwnershipService;
let ownerSetupService: OwnerSetupService;
// See PAY-4247 - This test case can be deleted when the ticket is complete
describe('Ownership Service integration test', () => {
@ -21,6 +23,7 @@ describe('Ownership Service integration test', () => {
await testDb.init();
hookService = Container.get(HooksService);
ownershipService = Container.get(OwnershipService);
ownerSetupService = Container.get(OwnerSetupService);
});
afterAll(async () => {
@ -49,7 +52,7 @@ describe('Ownership Service integration test', () => {
it('should recognise ownership creation from api', async () => {
expect(await ownershipService.hasInstanceOwner()).toBeFalsy();
await ownershipService.setupOwner({
await ownerSetupService.setupOwner({
firstName: 'TEST',
lastName: 'LN',
password: 'PW',

View File

@ -1,6 +1,6 @@
import type { ProjectRelation } from '@n8n/api-types';
import type { Logger, ModuleRegistry } from '@n8n/backend-common';
import { type EventService, type RoleService } from '@n8n/backend-services';
import type { EventService, OwnershipService, RoleService } from '@n8n/backend-services';
import {
type FolderRepository,
type Project,
@ -21,7 +21,6 @@ import type { EntityManager } from '@n8n/typeorm';
import type { Mocked } from 'vitest';
import { mock } from 'vitest-mock-extended';
import type { OwnershipService } from '../ownership.service';
import { ProjectService } from '../project.service.ee';
import type { ICredentialConnectionStatusProvider } from '@/credentials/credential-connection-status-provider.interface';

View File

@ -1,5 +1,10 @@
import { InviteUsersRequestDto } from '@n8n/api-types';
import { type EventService, UrlService, type RoleService } from '@n8n/backend-services';
import {
type EventService,
type OwnershipService,
type RoleService,
UrlService,
} from '@n8n/backend-services';
import { mockInstance } from '@n8n/backend-test-utils';
import { GlobalConfig } from '@n8n/config';
import {
@ -32,7 +37,6 @@ import type { UserManagementMailer } from '@/user-management/email';
import { JwtService } from '../jwt.service';
import type { OwnershipTransferService } from '../ownership-transfer/ownership-transfer.service';
import type { OwnershipService } from '../ownership.service';
import type { ProjectService } from '../project.service.ee';
import type { PublicApiKeyService } from '../public-api-key.service';

View File

@ -1,4 +1,4 @@
import { EventService } from '@n8n/backend-services';
import { EventService, OwnershipService } from '@n8n/backend-services';
import type { Mock } from 'vitest';
import {
getPersonalProject,
@ -30,7 +30,6 @@ import {
} from 'n8n-workflow';
import { INSTANCE_ACTIVATED_SETTINGS_KEY } from '@/services/instance-activation.service';
import { OwnershipService } from '@/services/ownership.service';
import { UserService } from '@/services/user.service';
import { WorkflowStatisticsService } from '@/services/workflow-statistics.service';
import { createUser } from '@test-integration/db/users';

View File

@ -19,8 +19,7 @@ import { FeatureNotLicensedError } from '@/errors/feature-not-licensed.error';
import { BadRequestError } from '@n8n/errors';
import { License } from '@/license';
import { checkAiGatewayEligibility } from '@/services/ai-gateway-eligibility';
import { OwnershipService } from '@/services/ownership.service';
import { UrlService } from '@n8n/backend-services';
import { OwnershipService, UrlService } from '@n8n/backend-services';
interface GatewayTokenResponse {
token: string;

View File

@ -11,7 +11,7 @@ import { BinaryDataConfig, InstanceSettings, type Types } from 'n8n-core';
import type { ICredentialType, INodeTypeBaseDescription, INodeTypeDescription } from 'n8n-workflow';
import path from 'path';
import { UrlService } from '@n8n/backend-services';
import { UrlService, OwnershipService } from '@n8n/backend-services';
import { AiUsageService } from './ai-usage.service';
import { WorkflowReviewPolicyService } from './workflow-review-policy.service';
@ -28,7 +28,6 @@ import { MfaService } from '@/mfa/mfa.service';
import { CommunityPackagesConfig } from '@/modules/community-packages/community-packages.config';
import { isApiKeyAuthEnabled } from '@/public-api';
import { PushConfig } from '@/push/push.config';
import { OwnershipService } from '@/services/ownership.service';
import { getSamlLoginLabel, getCurrentAuthenticationMethod } from '@/sso.ee/sso-helpers';
import { UserManagementMailer } from '@/user-management/email';
import { resolveFrontendHealthEndpointPath } from '@/utils/health-endpoint.util';

View File

@ -0,0 +1,73 @@
import { OwnerSetupRequestDto } from '@n8n/api-types';
import { Logger } from '@n8n/backend-common';
import { EventService, OwnershipService } from '@n8n/backend-services';
import { GLOBAL_OWNER_ROLE, SettingsRepository, UserRepository } from '@n8n/db';
import { Service } from '@n8n/di';
import { BadRequestError } from '@n8n/errors';
import config from '@/config';
import { PasswordUtility } from './password.utility';
@Service()
export class OwnerSetupService {
constructor(
private readonly ownershipService: OwnershipService,
private readonly eventService: EventService,
private readonly logger: Logger,
private readonly passwordUtility: PasswordUtility,
private readonly userRepository: UserRepository,
private readonly settingsRepository: SettingsRepository,
) {}
async setupOwner(
payload: OwnerSetupRequestDto,
options?: { overwriteExisting?: boolean; passwordIsHashed?: boolean },
) {
const { email, firstName, lastName, password } = payload;
if (!options?.overwriteExisting && (await this.ownershipService.hasInstanceOwner())) {
this.logger.debug(
'Request to claim instance ownership failed because instance owner already exists',
);
throw new BadRequestError('Instance owner already setup');
}
let shellUser = await this.userRepository.findOne({
where: { role: { slug: GLOBAL_OWNER_ROLE.slug } },
relations: ['role'],
});
if (!shellUser) {
this.logger.error('Could not find shell user with global:owner role');
throw new BadRequestError('Instance owner shell user not found');
}
shellUser.email = email.toLowerCase();
shellUser.firstName = firstName;
shellUser.lastName = lastName;
shellUser.lastActiveAt = new Date();
shellUser.password = options?.passwordIsHashed
? password
: await this.passwordUtility.hash(password);
shellUser = await this.userRepository.save(shellUser, { transaction: false });
this.logger.info('Owner was set up successfully');
this.eventService.emit('instance-owner-setup', { userId: shellUser.id });
// The next block needs to be deleted and is temporary for now
// See packages/cli/src/config/schema.ts for more info
// We update the SettingsRepository so when we "startup" next time
// the config state is restored.
// #region Delete me
await this.settingsRepository.update(
{ key: 'userManagement.isInstanceOwnerSetUp' },
{ value: JSON.stringify(true) },
);
config.set('userManagement.isInstanceOwnerSetUp', true);
// #endregion
return shellUser;
}
}

View File

@ -1,6 +1,7 @@
import {
OwnershipTransferHandlerRegistry,
type ProjectOwnershipTransferHandler,
type OwnershipService,
} from '@n8n/backend-services';
import type {
CredentialsEntity,
@ -18,7 +19,6 @@ import type { CredentialsService } from '@/credentials/credentials.service';
import type { PolicyActor } from '@/policy/policy-enforcement-backend';
import type { PolicyEnforcementService } from '@/policy/policy-enforcement.service';
import type { FolderService } from '@/services/folder.service';
import type { OwnershipService } from '@/services/ownership.service';
import type { WorkflowService } from '@/workflows/workflow.service';
import { OwnershipTransferService } from '../ownership-transfer.service';

View File

@ -1,4 +1,4 @@
import { OwnershipTransferHandlerRegistry } from '@n8n/backend-services';
import { OwnershipTransferHandlerRegistry, OwnershipService } from '@n8n/backend-services';
import {
SharedCredentialsRepository,
SharedWorkflowRepository,
@ -11,7 +11,6 @@ import { CredentialsService } from '@/credentials/credentials.service';
import type { PolicyActor } from '@/policy/policy-enforcement-backend';
import { PolicyEnforcementService } from '@/policy/policy-enforcement.service';
import { FolderService } from '@/services/folder.service';
import { OwnershipService } from '@/services/ownership.service';
import { WorkflowService } from '@/workflows/workflow.service';
/**

View File

@ -1,6 +1,6 @@
import type { CreateProjectDto, ProjectType, UpdateProjectDto } from '@n8n/api-types';
import { LicenseState, Logger, ModuleRegistry } from '@n8n/backend-common';
import { EventService, RoleService } from '@n8n/backend-services';
import { EventService, RoleService, OwnershipService } from '@n8n/backend-services';
import {
type User,
FolderRepository,
@ -37,8 +37,6 @@ import { UserError } from 'n8n-workflow';
import { BadRequestError, ConflictError, ForbiddenError, NotFoundError } from '@n8n/errors';
import { UserManagementMailer } from '@/user-management/email';
import { OwnershipService } from './ownership.service';
const INSTANCE_ACCESS_ROLE_ERROR =
"This user has access through their instance role. Project roles can't change their access in this project.";
const INSTANCE_ACCESS_REMOVE_ERROR =

View File

@ -1,6 +1,6 @@
import type { RoleChangeRequestDto } from '@n8n/api-types';
import { Logger } from '@n8n/backend-common';
import { EventService, UrlService, RoleService } from '@n8n/backend-services';
import { EventService, UrlService, RoleService, OwnershipService } from '@n8n/backend-services';
import { GlobalConfig } from '@n8n/config';
import type { PublicUser } from '@n8n/db';
import {
@ -31,7 +31,6 @@ import { UserError } from 'n8n-workflow';
import { validate as uuidValidate } from 'uuid';
import { JwtService } from './jwt.service';
import { OwnershipService } from './ownership.service';
import { ProjectService } from './project.service.ee';
import { PublicApiKeyService } from './public-api-key.service';

View File

@ -1,5 +1,5 @@
import { Logger, TypedEmitter } from '@n8n/backend-common';
import { EventService, isBillableExecution } from '@n8n/backend-services';
import { EventService, isBillableExecution, OwnershipService } from '@n8n/backend-services';
import { DatabaseConfig } from '@n8n/config';
import type { CrashedExecution } from '@n8n/db';
import {
@ -23,7 +23,6 @@ import {
import { UserService } from '@/services/user.service';
import { INSTANCE_ACTIVATED_SETTINGS_KEY } from './instance-activation.service';
import { OwnershipService } from './ownership.service';
type CompletedRunOutcome = {
mode: WorkflowExecuteMode;

View File

@ -18,7 +18,7 @@ import {
import { ActiveExecutions } from '@/active-executions';
import { ExecutionAlreadyResumingError } from '@/errors/execution-already-resuming.error';
import { ExecutionPersistence } from '@/executions/execution-persistence';
import { OwnershipService } from '@/services/ownership.service';
import { OwnershipService } from '@n8n/backend-services';
import { WorkflowPublisherService } from '@/workflows/workflow-publisher.service';
import { WorkflowRunner } from '@/workflow-runner';

View File

@ -1,5 +1,5 @@
import { Logger } from '@n8n/backend-common';
import { EventService } from '@n8n/backend-services';
import { EventService, OwnershipService } from '@n8n/backend-services';
import { mockInstance } from '@n8n/backend-test-utils';
import { EngineConfig } from '@n8n/config';
import type { Project, User } from '@n8n/db';
@ -67,7 +67,6 @@ import { WebhookResponseRelay } from '@/scaling/webhook-response-relay';
import { EngineDataPlaneProxyService } from '@/services/engine-data-plane-proxy.service';
import { EngineV2Dispatcher } from '@/services/engine-v2-dispatcher.service';
import { EngineV2WebhookResponseRegistry } from '@/modules/engine-v2/webhook-response/webhook-response-registry.service';
import { OwnershipService } from '@/services/ownership.service';
import { OAuth2FlowProxy } from '@/services/oauth2-flow-proxy.service';
import type { ProtectedResource } from '@/services/protected-resource.registry';
import { ProtectedResourceRegistry } from '@/services/protected-resource.registry';

View File

@ -1,5 +1,5 @@
import { Logger } from '@n8n/backend-common';
import { EventService } from '@n8n/backend-services';
import { EventService, OwnershipService } from '@n8n/backend-services';
import { ExecutionsConfig, GlobalConfig } from '@n8n/config';
import type { Project } from '@n8n/db';
import { UserRepository } from '@n8n/db';
@ -81,7 +81,6 @@ import {
OAuthTokenVerifierProxy,
} from '@/services/oauth-token-verifier-proxy.service';
import { OAuth2FlowProxy } from '@/services/oauth2-flow-proxy.service';
import { OwnershipService } from '@/services/ownership.service';
import { ProtectedResourceRegistry } from '@/services/protected-resource.registry';
import type { WebhookRunOutcome } from '@/modules/engine-v2/webhook-response/webhook-outcome';
import type { WebhookResponseWait } from '@/modules/engine-v2/webhook-response/webhook-response-registry.service';

View File

@ -352,7 +352,7 @@ export async function executeWorkflow(
const executionId = await activeExecutions.add(runData);
const { OwnershipService } = await import('@/services/ownership.service.js');
const { OwnershipService } = await import('@n8n/backend-services');
const { projectId, projectName } = await getWorkflowProjectDetailsSafe(
Container.get(OwnershipService),
workflowData.id,
@ -423,7 +423,7 @@ export async function executeAgent(
// `webhooks/*`, `scaling/job-processor`). Resolve it from the workflow's
// owning project so the agent runs under the correct project scope.
if (!projectId && additionalData.workflowId) {
const { OwnershipService } = await import('@/services/ownership.service.js');
const { OwnershipService } = await import('@n8n/backend-services');
const ownershipService = Container.get(OwnershipService);
const project = await ownershipService.getWorkflowProjectCached(additionalData.workflowId);
projectId = project.id;
@ -883,7 +883,7 @@ export async function getBase({
// matching `getVariables`'s own pre-existing lookup below: an unresolvable owner
// project fails execution setup, it isn't silently tolerated.
if (!projectId && workflowId) {
const { OwnershipService } = await import('@/services/ownership.service.js');
const { OwnershipService } = await import('@n8n/backend-services');
const project = await Container.get(OwnershipService).getWorkflowProjectCached(workflowId);
projectId = project?.id;
}

View File

@ -37,7 +37,7 @@ import { VariablesService } from '@/environments.ee/variables/variables.service.
import { ExecutionPersistence } from '@/executions/execution-persistence';
import { NodeTypes } from '@/node-types';
import { OwnershipService } from './services/ownership.service';
import { OwnershipService } from '@n8n/backend-services';
export { dropInvalidWorkflowGroups, makeGetNodeTypeForGrouping };
export { getLastExecutedNodeData, getLastExecutedNodeRuns } from 'n8n-workflow';

View File

@ -4,7 +4,7 @@ import type { Project } from '@n8n/db';
import { WorkflowEntity } from '@n8n/db';
import { mock } from 'vitest-mock-extended';
import type { OwnershipService } from '@/services/ownership.service';
import type { OwnershipService } from '@n8n/backend-services';
import { dropRedactionPolicy, getWorkflowProjectDetailsSafe } from '@/workflows/utils';
describe('dropRedactionPolicy', () => {

View File

@ -1,5 +1,9 @@
import type { Logger } from '@n8n/backend-common';
import type { EventService, InstanceWriteAccessService } from '@n8n/backend-services';
import type {
EventService,
InstanceWriteAccessService,
OwnershipService,
} from '@n8n/backend-services';
import type { GlobalConfig, WorkflowsConfig } from '@n8n/config';
import type {
CreateExecutionPayload,
@ -36,7 +40,6 @@ import { ForbiddenError } from '@n8n/errors';
import type { ExecutionCrashService } from '@/executions/execution-crash.service';
import type { IWorkflowErrorData } from '@/interfaces';
import type { NodeTypes } from '@/node-types';
import type { OwnershipService } from '@/services/ownership.service';
import type { TestWebhooks } from '@/webhooks/test-webhooks';
import * as WorkflowExecuteAdditionalData from '@/workflow-execute-additional-data';
import type { WorkflowRunner } from '@/workflow-runner';

View File

@ -15,11 +15,10 @@ import { WorkflowActivationError } from 'n8n-workflow';
import { mock } from 'vitest-mock-extended';
import type { ActiveWorkflowManager } from '@/active-workflow-manager';
import type { CredentialsFinderService } from '@n8n/backend-services';
import type { CredentialsFinderService, OwnershipService } from '@n8n/backend-services';
import type { CredentialsService } from '@/credentials/credentials.service';
import type { CredentialsPermissionChecker } from '@/executions/pre-execution-checks/credentials-permission-checker';
import type { PolicyEnforcementService } from '@/policy/policy-enforcement.service';
import type { OwnershipService } from '@/services/ownership.service';
import type { ProjectService } from '@/services/project.service.ee';
import type { WorkflowFinderService } from '@/workflows/workflow-finder.service';
import type { WorkflowMutationHooksProxy } from '@/workflows/workflow-mutation-hooks-proxy.service';

View File

@ -1,5 +1,5 @@
import type { LicenseState } from '@n8n/backend-common';
import { type EventService, type RoleService } from '@n8n/backend-services';
import type { EventService, RoleService, OwnershipService } from '@n8n/backend-services';
import type { GlobalConfig, WorkflowsConfig } from '@n8n/config';
import type {
Transaction,
@ -46,7 +46,6 @@ import type { DurableJobProvisioner } from '@/scheduling/durable-job-provisioner
import type { PollTriggerJobRegistrar } from '@/scheduling/poll-trigger-node/poll-trigger-job-registrar';
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 { TagService } from '@/services/tag.service';
import type { WebhookService } from '@/webhooks/webhook.service';

View File

@ -18,7 +18,7 @@ import { TELEMETRY_EVENT } from '@n8n/telemetry';
import type { NodeTypes } from '@/node-types';
import type { PolicyEnforcementService } from '@/policy/policy-enforcement.service';
import { PolicyViolationError } from '@/policy/policy-violation.error';
import type { OwnershipService } from '@/services/ownership.service';
import type { OwnershipService } from '@n8n/backend-services';
import type { Telemetry } from '@/telemetry';
import { WorkflowPublicationApplier } from '@/workflows/publication/workflow-publication-applier';
import type { DurableJobProvisioner } from '@/scheduling/durable-job-provisioner';

View File

@ -20,7 +20,7 @@ import { PolicyEnforcementService } from '@/policy/policy-enforcement.service';
import { PolicyViolationError } from '@/policy/policy-violation.error';
import { DurableJobProvisioner } from '@/scheduling/durable-job-provisioner';
import { WorkflowScheduledJobOwner } from '@/scheduling/workflow-scheduled-job-owner';
import { OwnershipService } from '@/services/ownership.service';
import { OwnershipService } from '@n8n/backend-services';
import { Telemetry } from '@/telemetry';
import { formatNodeFailures } from '@/workflows/publication/format-node-failures';
import { healNodeIds } from '@/workflows/publication/heal-node-ids';

View File

@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/unbound-method */
import type { Logger } from '@n8n/backend-common';
import type { EventService } from '@n8n/backend-services';
import type { EventService, OwnershipService } from '@n8n/backend-services';
import type { GlobalConfig } from '@n8n/config';
import type { Project, WorkflowEntity } from '@n8n/db';
import type { IDeferredPromise } from '@n8n/utils/promise/deferred-promise';
@ -39,7 +39,6 @@ import type {
ScheduleTriggerCollectionSession,
ScheduleTriggerJobRegistrar,
} from '@/scheduling/schedule-trigger-node/schedule-trigger-job-registrar';
import type { OwnershipService } from '@/services/ownership.service';
import type { WorkflowPublisherService } from '@/workflows/workflow-publisher.service';
import * as WorkflowExecuteAdditionalData from '@/workflow-execute-additional-data';
import type { WorkflowExecutionService } from '@/workflows/workflow-execution.service';

View File

@ -1,5 +1,5 @@
import { Logger } from '@n8n/backend-common';
import { EventService } from '@n8n/backend-services';
import { EventService, OwnershipService } from '@n8n/backend-services';
import { GlobalConfig } from '@n8n/config';
import { Time } from '@n8n/constants';
import type { IWorkflowDb, PollerCursor, PollLeaseFence } from '@n8n/db';
@ -39,7 +39,6 @@ import { ExecutionService } from '@/executions/execution.service';
import { NodeTypes } from '@/node-types';
import type { ScheduleTriggerCollectionSession } from '@/scheduling/schedule-trigger-node/schedule-trigger-job-registrar';
import { ScheduleTriggerJobRegistrar } from '@/scheduling/schedule-trigger-node/schedule-trigger-job-registrar';
import { OwnershipService } from '@/services/ownership.service';
import * as WorkflowExecuteAdditionalData from '@/workflow-execute-additional-data';
import type { EngineV2ActiveTriggerEmit } from '@/workflows/triggers/engine-v2-active-triggers';
import { EngineV2ActiveTriggers } from '@/workflows/triggers/engine-v2-active-triggers';

View File

@ -5,7 +5,7 @@ import type { Scope } from '@n8n/permissions';
import { NodeApiError, NodeError, WorkflowActivationError } from 'n8n-workflow';
import type { WorkflowSettings } from 'n8n-workflow';
import type { OwnershipService } from '@/services/ownership.service';
import type { OwnershipService } from '@n8n/backend-services';
type RedactionMode = 'manual' | 'nonManual';

View File

@ -1,5 +1,5 @@
import { Logger } from '@n8n/backend-common';
import { EventService, InstanceWriteAccessService } from '@n8n/backend-services';
import { EventService, InstanceWriteAccessService, OwnershipService } from '@n8n/backend-services';
import { GlobalConfig, WorkflowsConfig } from '@n8n/config';
import type {
Project,
@ -49,7 +49,6 @@ import { FailedRunFactory } from '@/executions/failed-run-factory';
import { SubworkflowPolicyChecker } from '@/executions/pre-execution-checks';
import type { IWorkflowErrorData } from '@/interfaces';
import { NodeTypes } from '@/node-types';
import { OwnershipService } from '@/services/ownership.service';
import { TestWebhooks } from '@/webhooks/test-webhooks';
import * as WorkflowExecuteAdditionalData from '@/workflow-execute-additional-data';
import { WorkflowRunner } from '@/workflow-runner';

View File

@ -32,7 +32,7 @@ import {
import { ActiveWorkflowManager } from '@/active-workflow-manager';
import { isCredSharingEnabled } from '@/constants/credential-sharing';
import { CredentialsFinderService } from '@n8n/backend-services';
import { CredentialsFinderService, OwnershipService } from '@n8n/backend-services';
import { CredentialsService } from '@/credentials/credentials.service';
import { EnterpriseCredentialsService } from '@/credentials/credentials.service.ee';
import { CredentialsPermissionChecker } from '@/executions/pre-execution-checks/credentials-permission-checker';
@ -41,7 +41,6 @@ import { BadRequestError, NotFoundError } from '@n8n/errors';
import { TransferWorkflowError } from '@/errors/response-errors/transfer-workflow.error';
import { extractAgentCredentialIds } from '@/modules/agents/utils/extract-agent-credential-ids';
import { PolicyEnforcementService } from '@/policy/policy-enforcement.service';
import { OwnershipService } from '@/services/ownership.service';
import { ProjectService } from '@/services/project.service.ee';
import { WorkflowFinderService } from './workflow-finder.service';

View File

@ -1,7 +1,7 @@
import { UpdateWorkflowHistoryVersionDto } from '@n8n/api-types';
import type { WorkflowListPublicationStatus, WorkflowExecutionBlockCause } from '@n8n/api-types';
import { LicenseState, Logger } from '@n8n/backend-common';
import { EventService, RoleService } from '@n8n/backend-services';
import { EventService, RoleService, OwnershipService } from '@n8n/backend-services';
import { GlobalConfig } from '@n8n/config';
import type {
User,
@ -82,7 +82,6 @@ import { DurableJobProvisioner } from '@/scheduling/durable-job-provisioner';
import { PollTriggerJobRegistrar } from '@/scheduling/poll-trigger-node/poll-trigger-job-registrar';
import { ScheduleTriggerJobRegistrar } from '@/scheduling/schedule-trigger-node/schedule-trigger-job-registrar';
import { WorkflowScheduledJobOwner } from '@/scheduling/workflow-scheduled-job-owner';
import { OwnershipService } from '@/services/ownership.service';
import { ProjectService } from '@/services/project.service.ee';
import { TagService } from '@/services/tag.service';

View File

@ -12,7 +12,7 @@ import {
} from '@n8n/api-types';
import { Logger } from '@n8n/backend-common';
import { OutboundHttp, SsrfBlockedIpError } from '@n8n/backend-network';
import { EventService } from '@n8n/backend-services';
import { EventService, OwnershipService } from '@n8n/backend-services';
import { GlobalConfig } from '@n8n/config';
import {
AuthenticatedRequest,
@ -50,7 +50,6 @@ import { listQueryMiddleware } from '@/middlewares';
import { userHasScopes } from '@/permissions.ee/check-access';
import * as ResponseHelper from '@/response-helper';
import { NamingService } from '@/services/naming.service';
import { OwnershipService } from '@/services/ownership.service';
import { ProjectService } from '@/services/project.service.ee';
import { UserManagementMailer } from '@/user-management/email';
import * as utils from '@/utils';

View File

@ -27,7 +27,7 @@ import { ExternalHooks } from '@/external-hooks';
import { NodeTypes } from '@/node-types';
import { PolicyEnforcementService } from '@/policy/policy-enforcement.service';
import { Push } from '@/push';
import { OwnershipService } from '@/services/ownership.service';
import { OwnershipService } from '@n8n/backend-services';
import * as WebhookHelpers from '@/webhooks/webhook-helpers';
import { WebhookService } from '@/webhooks/webhook.service';
import * as AdditionalData from '@/workflow-execute-additional-data';

View File

@ -30,7 +30,7 @@ import { Push } from '@/push';
import { PubSubEventBus } from '@/scaling/pubsub/pubsub.eventbus';
import { DurableScheduler } from '@/scheduling/durable-scheduler';
import { Server } from '@/server';
import { OwnershipService } from '@/services/ownership.service';
import { OwnershipService } from '@n8n/backend-services';
import { ExecutionsPruningService } from '@/services/pruning/executions-pruning.service';
import { WorkflowHistoryCompactionService } from '@/services/pruning/workflow-history-compaction.service';
import { WorkflowStatisticsRollupService } from '@/services/workflow-statistics-rollup.service';

View File

@ -20,7 +20,7 @@ import { v4 as uuid } from 'uuid';
import { CredentialsPermissionChecker } from '@/executions/pre-execution-checks';
import { LoadNodesAndCredentials } from '@/load-nodes-and-credentials';
import { NodeTypes } from '@/node-types';
import { OwnershipService } from '@/services/ownership.service';
import { OwnershipService } from '@n8n/backend-services';
import { WorkflowValidationService } from '@/workflows/workflow-validation.service';
import { affixRoleToSaveCredential } from '@test-integration/db/credentials';
import { createOwner, createUser } from '@test-integration/db/users';

View File

@ -19,7 +19,7 @@ import { DateTime } from 'luxon';
import { PROJECT_ROOT, UnexpectedError } from 'n8n-workflow';
import { ActiveWorkflowManager } from '@/active-workflow-manager';
import { OwnershipService } from '@/services/ownership.service';
import { OwnershipService } from '@n8n/backend-services';
import {
createCredentials,
getCredentialSharings,

View File

@ -10,7 +10,7 @@ import { GLOBAL_OWNER_ROLE, UserRepository } from '@n8n/db';
import { Container } from '@n8n/di';
import validator from 'validator';
import { OwnershipService } from '@/services/ownership.service';
import { OwnershipService } from '@n8n/backend-services';
import { createUserShell } from './shared/db/users';
import * as utils from './shared/utils/';

View File

@ -25,7 +25,7 @@ import { ExecutionService } from '@/executions/execution.service';
import { ExternalHooks } from '@/external-hooks';
import { Push } from '@/push';
import { WorkflowScheduledJobOwner } from '@/scheduling/workflow-scheduled-job-owner';
import { OwnershipService } from '@/services/ownership.service';
import { OwnershipService } from '@n8n/backend-services';
import { Telemetry } from '@/telemetry';
import { WorkflowPublicationOutboxConsumer } from '@/workflows/publication/workflow-publication-outbox-consumer';
import { WorkflowService } from '@/workflows/workflow.service';

View File

@ -42,7 +42,7 @@ import { validateUser } from './shared/utils/users';
import { RESPONSE_ERROR_MESSAGES } from '@/constants';
import { ExecutionService } from '@/executions/execution.service';
import { ProvisioningService } from '@/modules/provisioning.ee/provisioning.service.ee';
import { OwnershipService } from '@/services/ownership.service';
import { OwnershipService } from '@n8n/backend-services';
import { CHANGE_ROLE_ERROR_MESSAGES } from '@/services/user.service';
import { Telemetry } from '@/telemetry';

View File

@ -22,7 +22,7 @@ import { ActiveWorkflowManager } from '@/active-workflow-manager';
import { ExecutionService } from '@/executions/execution.service';
import { ExternalHooks } from '@/external-hooks';
import { Push } from '@/push';
import { OwnershipService } from '@/services/ownership.service';
import { OwnershipService } from '@n8n/backend-services';
import { Telemetry } from '@/telemetry';
import { PublishedWorkflowTriggerDeactivator } from '@/workflows/publication/published-workflow-trigger-deactivator';
import { WorkflowPublicationLifecycleLock } from '@/workflows/publication/workflow-publication-lifecycle-lock';

View File

@ -1,4 +1,4 @@
import { EventService } from '@n8n/backend-services';
import { EventService, OwnershipService } from '@n8n/backend-services';
import {
createWorkflowHistory,
createWorkflowWithHistory,
@ -25,7 +25,6 @@ import { ActiveWorkflowManager } from '@/active-workflow-manager';
import { ExecutionService } from '@/executions/execution.service';
import { ExternalHooks } from '@/external-hooks';
import { Push } from '@/push';
import { OwnershipService } from '@/services/ownership.service';
import { WorkflowPublicationOutboxConsumer } from '@/workflows/publication/workflow-publication-outbox-consumer';
import { WorkflowPublicationApplier } from '@/workflows/publication/workflow-publication-applier';
import { WorkflowPublicationReconciler } from '@/workflows/publication/workflow-publication-reconciler.service';

View File

@ -33,9 +33,8 @@ import type { ExternalHooks } from '@/external-hooks';
import { MessageEventBus } from '@/eventbus/message-event-bus/message-event-bus';
import { NodeTypes } from '@/node-types';
import { PolicyEnforcementService } from '@/policy/policy-enforcement.service';
import { OwnershipService } from '@/services/ownership.service';
import { OwnershipService, RoleService } from '@n8n/backend-services';
import { ProjectService } from '@/services/project.service.ee';
import { RoleService } from '@n8n/backend-services';
import { Telemetry } from '@/telemetry';
import { WebhookService } from '@/webhooks/webhook.service';
import { WorkflowHookContextService } from '@/workflow-hook-context.service';