diff --git a/packages/cli/src/Interfaces.ts b/packages/cli/src/Interfaces.ts index 08efcd3f66..6c2da8d4e2 100644 --- a/packages/cli/src/Interfaces.ts +++ b/packages/cli/src/Interfaces.ts @@ -31,7 +31,6 @@ import type { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner'; import type { WorkflowExecute } from 'n8n-core'; import type PCancelable from 'p-cancelable'; -import type { FindOperator } from 'typeorm'; import type { ChildProcess } from 'child_process'; @@ -606,8 +605,6 @@ export interface IWorkflowStatisticsDataLoaded { dataLoaded: boolean; } -export type WhereClause = Record }>; - // ---------------------------------- // community nodes // ---------------------------------- diff --git a/packages/cli/src/UserManagement/UserManagementHelper.ts b/packages/cli/src/UserManagement/UserManagementHelper.ts index 548719fdc0..4178dff009 100644 --- a/packages/cli/src/UserManagement/UserManagementHelper.ts +++ b/packages/cli/src/UserManagement/UserManagementHelper.ts @@ -1,9 +1,4 @@ -import { In } from 'typeorm'; import { Container } from 'typedi'; -import type { Scope } from '@n8n/permissions'; - -import type { WhereClause } from '@/Interfaces'; -import type { User } from '@db/entities/User'; import { License } from '@/License'; export function isSharingEnabled(): boolean { @@ -29,32 +24,3 @@ export function rightDiff( return acc; }, []); } - -/** - * Build a `where` clause for a TypeORM entity search, - * checking for member access if the user is not an owner. - */ -export function whereClause({ - user, - entityType, - globalScope, - entityId = '', - roles = [], -}: { - user: User; - entityType: 'workflow' | 'credentials'; - globalScope: Scope; - entityId?: string; - roles?: string[]; -}): WhereClause { - const where: WhereClause = entityId ? { [entityType]: { id: entityId } } : {}; - - if (!user.hasGlobalScope(globalScope)) { - where.user = { id: user.id }; - if (roles?.length) { - where.role = { name: In(roles) }; - } - } - - return where; -} diff --git a/packages/cli/src/WorkflowHelpers.ts b/packages/cli/src/WorkflowHelpers.ts index 84b06a492f..870a154043 100644 --- a/packages/cli/src/WorkflowHelpers.ts +++ b/packages/cli/src/WorkflowHelpers.ts @@ -418,7 +418,7 @@ export async function replaceInvalidCredentials(workflow: WorkflowEntity): Promi /** * Get the IDs of the workflows that have been shared with the user. - * Returns all IDs if user has the 'workflow:read' scope (see `whereClause`) + * Returns all IDs if user has the 'workflow:read' scope. */ export async function getSharedWorkflowIds(user: User, roles?: RoleNames[]): Promise { const where: FindOptionsWhere = {}; diff --git a/packages/cli/src/commands/user-management/reset.ts b/packages/cli/src/commands/user-management/reset.ts index d04b561fdc..caa46ecc90 100644 --- a/packages/cli/src/commands/user-management/reset.ts +++ b/packages/cli/src/commands/user-management/reset.ts @@ -1,5 +1,4 @@ import { Container } from 'typedi'; -import { Not } from 'typeorm'; import type { CredentialsEntity } from '@db/entities/CredentialsEntity'; import { User } from '@db/entities/User'; import { CredentialsRepository } from '@db/repositories/credentials.repository'; @@ -25,20 +24,16 @@ export class Reset extends BaseCommand { async run(): Promise { const owner = await this.getInstanceOwner(); - const ownerWorkflowRole = await Container.get(RoleService).findWorkflowOwnerRole(); - const ownerCredentialRole = await Container.get(RoleService).findCredentialOwnerRole(); + const workflowOwnerRole = await Container.get(RoleService).findWorkflowOwnerRole(); + const credentialOwnerRole = await Container.get(RoleService).findCredentialOwnerRole(); - await Container.get(SharedWorkflowRepository).update( - { userId: Not(owner.id), roleId: ownerWorkflowRole.id }, - { user: owner }, + await Container.get(SharedWorkflowRepository).makeOwnerOfAllWorkflows(owner, workflowOwnerRole); + await Container.get(SharedCredentialsRepository).makeOwnerOfAllCredentials( + owner, + credentialOwnerRole, ); - await Container.get(SharedCredentialsRepository).update( - { userId: Not(owner.id), roleId: ownerCredentialRole.id }, - { user: owner }, - ); - - await Container.get(UserRepository).delete({ id: Not(owner.id) }); + await Container.get(UserRepository).deleteAllExcept(owner); await Container.get(UserRepository).save(Object.assign(owner, defaultUserProps)); const danglingCredentials: CredentialsEntity[] = await Container.get(CredentialsRepository) @@ -50,7 +45,7 @@ export class Reset extends BaseCommand { Container.get(SharedCredentialsRepository).create({ credentials, user: owner, - role: ownerCredentialRole, + role: credentialOwnerRole, }), ); await Container.get(SharedCredentialsRepository).save(newSharedCredentials); diff --git a/packages/cli/src/controllers/debug.controller.ts b/packages/cli/src/controllers/debug.controller.ts index 78173ba599..10c45a0618 100644 --- a/packages/cli/src/controllers/debug.controller.ts +++ b/packages/cli/src/controllers/debug.controller.ts @@ -2,8 +2,6 @@ import { Get, RestController } from '@/decorators'; import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner'; import { MultiMainSetup } from '@/services/orchestration/main/MultiMainSetup.ee'; import { WorkflowRepository } from '@/databases/repositories/workflow.repository'; -import { In } from 'typeorm'; -import { WebhookEntity } from '@/databases/entities/WebhookEntity'; @RestController('/debug') export class DebugController { @@ -17,16 +15,11 @@ export class DebugController { async getMultiMainSetupDetails() { const leaderKey = await this.multiMainSetup.fetchLeaderKey(); - const triggersAndPollers = await this.workflowRepository.find({ - select: ['id', 'name'], - where: { id: In(this.activeWorkflowRunner.allActiveInMemory()) }, - }); + const triggersAndPollers = await this.workflowRepository.findIn( + this.activeWorkflowRunner.allActiveInMemory(), + ); - const webhooks = (await this.workflowRepository - .createQueryBuilder('workflow') - .select('DISTINCT workflow.id, workflow.name') - .innerJoin(WebhookEntity, 'webhook_entity', 'workflow.id = webhook_entity.workflowId') - .execute()) as Array<{ id: string; name: string }>; + const webhooks = await this.workflowRepository.findWebhookBasedActiveWorkflows(); const activationErrors = await this.activeWorkflowRunner.getAllWorkflowActivationErrors(); diff --git a/packages/cli/src/controllers/workflowStatistics.controller.ts b/packages/cli/src/controllers/workflowStatistics.controller.ts index b1f5d0e68e..a5ad163823 100644 --- a/packages/cli/src/controllers/workflowStatistics.controller.ts +++ b/packages/cli/src/controllers/workflowStatistics.controller.ts @@ -5,7 +5,6 @@ import { StatisticsNames } from '@db/entities/WorkflowStatistics'; import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository'; import { WorkflowStatisticsRepository } from '@db/repositories/workflowStatistics.repository'; import { ExecutionRequest } from '@/requests'; -import { whereClause } from '@/UserManagement/UserManagementHelper'; import type { IWorkflowStatisticsDataLoaded } from '@/Interfaces'; import { Logger } from '@/Logger'; import { NotFoundError } from '@/errors/response-errors/not-found.error'; @@ -33,17 +32,10 @@ export class WorkflowStatisticsController { async hasWorkflowAccess(req: ExecutionRequest.Get, res: Response, next: NextFunction) { const { user } = req; const workflowId = req.params.id; - const allowed = await this.sharedWorkflowRepository.exist({ - relations: ['workflow'], - where: whereClause({ - user, - globalScope: 'workflow:read', - entityType: 'workflow', - entityId: workflowId, - }), - }); - if (allowed) { + const hasAccess = await this.sharedWorkflowRepository.hasAccess(workflowId, user); + + if (hasAccess) { next(); } else { this.logger.verbose('User attempted to read a workflow without permissions', { diff --git a/packages/cli/src/credentials/credentials.service.ts b/packages/cli/src/credentials/credentials.service.ts index f75f1eccd6..1d720c6cd9 100644 --- a/packages/cli/src/credentials/credentials.service.ts +++ b/packages/cli/src/credentials/credentials.service.ts @@ -8,8 +8,7 @@ import type { } from 'n8n-workflow'; import { CREDENTIAL_EMPTY_VALUE, deepCopy, NodeHelpers } from 'n8n-workflow'; import { Container } from 'typedi'; -import type { FindManyOptions, FindOptionsWhere } from 'typeorm'; -import { In, Like } from 'typeorm'; +import type { FindOptionsWhere } from 'typeorm'; import type { Scope } from '@n8n/permissions'; @@ -42,97 +41,35 @@ export class CredentialsService { }); } - private static toFindManyOptions(listQueryOptions?: ListQuery.Options) { - const findManyOptions: FindManyOptions = {}; - - type Select = Array; - - const defaultRelations = ['shared', 'shared.role', 'shared.user']; - const defaultSelect: Select = ['id', 'name', 'type', 'nodesAccess', 'createdAt', 'updatedAt']; - - if (!listQueryOptions) return { select: defaultSelect, relations: defaultRelations }; - - const { filter, select, take, skip } = listQueryOptions; - - if (typeof filter?.name === 'string' && filter?.name !== '') { - filter.name = Like(`%${filter.name}%`); - } - - if (typeof filter?.type === 'string' && filter?.type !== '') { - filter.type = Like(`%${filter.type}%`); - } - - if (filter) findManyOptions.where = filter; - if (select) findManyOptions.select = select; - if (take) findManyOptions.take = take; - if (skip) findManyOptions.skip = skip; - - if (take && select && !select?.id) { - findManyOptions.select = { ...findManyOptions.select, id: true }; // pagination requires id - } - - if (!findManyOptions.select) { - findManyOptions.select = defaultSelect; - findManyOptions.relations = defaultRelations; - } - - return findManyOptions; - } - static async getMany( user: User, options: { listQueryOptions?: ListQuery.Options; onlyOwn?: boolean } = {}, ) { - const findManyOptions = this.toFindManyOptions(options.listQueryOptions); - const returnAll = user.hasGlobalScope('credential:list') && !options.onlyOwn; const isDefaultSelect = !options.listQueryOptions?.select; if (returnAll) { - const credentials = await Container.get(CredentialsRepository).find(findManyOptions); + const credentials = await Container.get(CredentialsRepository).findMany( + options.listQueryOptions, + ); return isDefaultSelect ? credentials.map((c) => Container.get(OwnershipService).addOwnedByAndSharedWith(c)) : credentials; } - const ids = await this.getAccessibleCredentials(user.id); + const ids = await Container.get(SharedCredentialsRepository).getAccessibleCredentials(user.id); - const credentials = await Container.get(CredentialsRepository).find({ - ...findManyOptions, - where: { ...findManyOptions.where, id: In(ids) }, // only accessible credentials - }); + const credentials = await Container.get(CredentialsRepository).findMany( + options.listQueryOptions, + ids, // only accessible credentials + ); return isDefaultSelect ? credentials.map((c) => Container.get(OwnershipService).addOwnedByAndSharedWith(c)) : credentials; } - /** - * Get the IDs of all credentials owned by or shared with a user. - */ - private static async getAccessibleCredentials(userId: string) { - const sharings = await Container.get(SharedCredentialsRepository).find({ - relations: ['role'], - where: { - userId, - role: { name: In(['owner', 'user']), scope: 'credential' }, - }, - }); - - return sharings.map((s) => s.credentialsId); - } - - static async getManyByIds(ids: string[], { withSharings } = { withSharings: false }) { - const options: FindManyOptions = { where: { id: In(ids) } }; - - if (withSharings) { - options.relations = ['shared', 'shared.user', 'shared.role']; - } - - return Container.get(CredentialsRepository).find(options); - } - /** * Retrieve the sharing that matches a user and a credential. */ diff --git a/packages/cli/src/databases/repositories/credentials.repository.ts b/packages/cli/src/databases/repositories/credentials.repository.ts index 5687bd934e..f596f79ae5 100644 --- a/packages/cli/src/databases/repositories/credentials.repository.ts +++ b/packages/cli/src/databases/repositories/credentials.repository.ts @@ -1,16 +1,9 @@ import { Service } from 'typedi'; -import { - DataSource, - In, - Not, - Repository, - type DeleteResult, - type EntityManager, - type FindOptionsWhere, - Like, -} from 'typeorm'; +import { DataSource, In, Not, Repository, Like } from 'typeorm'; +import type { FindManyOptions, DeleteResult, EntityManager, FindOptionsWhere } from 'typeorm'; import { CredentialsEntity } from '../entities/CredentialsEntity'; import { SharedCredentials } from '../entities/SharedCredentials'; +import type { ListQuery } from '@/requests'; @Service() export class CredentialsRepository extends Repository { @@ -36,4 +29,61 @@ export class CredentialsRepository extends Repository { where: { name: Like(`${credentialName}%`) }, }); } + + async findMany(listQueryOptions?: ListQuery.Options, credentialIds?: string[]) { + const findManyOptions = this.toFindManyOptions(listQueryOptions); + + if (credentialIds) { + findManyOptions.where = { ...findManyOptions.where, id: In(credentialIds) }; + } + + return this.find(findManyOptions); + } + + private toFindManyOptions(listQueryOptions?: ListQuery.Options) { + const findManyOptions: FindManyOptions = {}; + + type Select = Array; + + const defaultRelations = ['shared', 'shared.role', 'shared.user']; + const defaultSelect: Select = ['id', 'name', 'type', 'nodesAccess', 'createdAt', 'updatedAt']; + + if (!listQueryOptions) return { select: defaultSelect, relations: defaultRelations }; + + const { filter, select, take, skip } = listQueryOptions; + + if (typeof filter?.name === 'string' && filter?.name !== '') { + filter.name = Like(`%${filter.name}%`); + } + + if (typeof filter?.type === 'string' && filter?.type !== '') { + filter.type = Like(`%${filter.type}%`); + } + + if (filter) findManyOptions.where = filter; + if (select) findManyOptions.select = select; + if (take) findManyOptions.take = take; + if (skip) findManyOptions.skip = skip; + + if (take && select && !select?.id) { + findManyOptions.select = { ...findManyOptions.select, id: true }; // pagination requires id + } + + if (!findManyOptions.select) { + findManyOptions.select = defaultSelect; + findManyOptions.relations = defaultRelations; + } + + return findManyOptions; + } + + async getManyByIds(ids: string[], { withSharings } = { withSharings: false }) { + const findManyOptions: FindManyOptions = { where: { id: In(ids) } }; + + if (withSharings) { + findManyOptions.relations = ['shared', 'shared.user', 'shared.role']; + } + + return this.find(findManyOptions); + } } diff --git a/packages/cli/src/databases/repositories/execution.repository.ts b/packages/cli/src/databases/repositories/execution.repository.ts index 67b3d0de4b..54a9f2ece6 100644 --- a/packages/cli/src/databases/repositories/execution.repository.ts +++ b/packages/cli/src/databases/repositories/execution.repository.ts @@ -187,17 +187,17 @@ export class ExecutionRepository extends Repository { where?: FindOptionsWhere; }, ): Promise { - const whereClause: FindOneOptions = { + const findOptions: FindOneOptions = { where: { id, ...options?.where, }, }; if (options?.includeData) { - whereClause.relations = ['executionData']; + findOptions.relations = ['executionData']; } - const execution = await this.findOne(whereClause); + const execution = await this.findOne(findOptions); if (!execution) { return undefined; diff --git a/packages/cli/src/databases/repositories/sharedCredentials.repository.ts b/packages/cli/src/databases/repositories/sharedCredentials.repository.ts index dabaf1833b..660c569136 100644 --- a/packages/cli/src/databases/repositories/sharedCredentials.repository.ts +++ b/packages/cli/src/databases/repositories/sharedCredentials.repository.ts @@ -1,7 +1,8 @@ import { Service } from 'typedi'; -import { DataSource, In, Repository } from 'typeorm'; +import { DataSource, In, Not, Repository } from 'typeorm'; import { SharedCredentials } from '../entities/SharedCredentials'; import type { User } from '../entities/User'; +import type { Role } from '../entities/Role'; @Service() export class SharedCredentialsRepository extends Repository { @@ -30,4 +31,23 @@ export class SharedCredentialsRepository extends Repository { }, }); } + + async makeOwnerOfAllCredentials(user: User, role: Role) { + return this.update({ userId: Not(user.id), roleId: role.id }, { user }); + } + + /** + * Get the IDs of all credentials owned by or shared with a user. + */ + async getAccessibleCredentials(userId: string) { + const sharings = await this.find({ + relations: ['role'], + where: { + userId, + role: { name: In(['owner', 'user']), scope: 'credential' }, + }, + }); + + return sharings.map((s) => s.credentialsId); + } } diff --git a/packages/cli/src/databases/repositories/sharedWorkflow.repository.ts b/packages/cli/src/databases/repositories/sharedWorkflow.repository.ts index 5a54975ecf..d371e2995a 100644 --- a/packages/cli/src/databases/repositories/sharedWorkflow.repository.ts +++ b/packages/cli/src/databases/repositories/sharedWorkflow.repository.ts @@ -1,7 +1,9 @@ import { Service } from 'typedi'; -import { DataSource, type FindOptionsWhere, Repository, In } from 'typeorm'; +import { DataSource, type FindOptionsWhere, Repository, In, Not } from 'typeorm'; import { SharedWorkflow } from '../entities/SharedWorkflow'; import { type User } from '../entities/User'; +import type { Scope } from '@n8n/permissions'; +import type { Role } from '../entities/Role'; @Service() export class SharedWorkflowRepository extends Repository { @@ -41,4 +43,33 @@ export class SharedWorkflowRepository extends Repository { }, }); } + + async findSharing( + workflowId: string, + user: User, + scope: Scope, + { roles, extraRelations }: { roles?: string[]; extraRelations?: string[] } = {}, + ) { + const where: FindOptionsWhere = { + workflow: { id: workflowId }, + }; + + if (!user.hasGlobalScope(scope)) { + where.user = { id: user.id }; + } + + if (roles) { + where.role = { name: In(roles) }; + } + + const relations = ['workflow', 'role']; + + if (extraRelations) relations.push(...extraRelations); + + return this.findOne({ relations, where }); + } + + async makeOwnerOfAllWorkflows(user: User, role: Role) { + return this.update({ userId: Not(user.id), roleId: role.id }, { user }); + } } diff --git a/packages/cli/src/databases/repositories/user.repository.ts b/packages/cli/src/databases/repositories/user.repository.ts index 1d47f31ea5..cb6543e5c8 100644 --- a/packages/cli/src/databases/repositories/user.repository.ts +++ b/packages/cli/src/databases/repositories/user.repository.ts @@ -1,5 +1,5 @@ import { Service } from 'typedi'; -import { DataSource, In, Repository } from 'typeorm'; +import { DataSource, In, Not, Repository } from 'typeorm'; import { User } from '../entities/User'; @Service() @@ -14,4 +14,8 @@ export class UserRepository extends Repository { relations: ['globalRole'], }); } + + async deleteAllExcept(user: User) { + await this.delete({ id: Not(user.id) }); + } } diff --git a/packages/cli/src/databases/repositories/workflow.repository.ts b/packages/cli/src/databases/repositories/workflow.repository.ts index 7a6951da19..49e9169c32 100644 --- a/packages/cli/src/databases/repositories/workflow.repository.ts +++ b/packages/cli/src/databases/repositories/workflow.repository.ts @@ -17,6 +17,7 @@ import { isStringArray } from '@/utils'; import config from '@/config'; import { WorkflowEntity } from '../entities/WorkflowEntity'; import { SharedWorkflow } from '../entities/SharedWorkflow'; +import { WebhookEntity } from '../entities/WebhookEntity'; @Service() export class WorkflowRepository extends Repository { @@ -183,4 +184,18 @@ export class WorkflowRepository extends Repository { where: { name: Like(`${workflowName}%`) }, }); } + + async findIn(workflowIds: string[]) { + return this.find({ + select: ['id', 'name'], + where: { id: In(workflowIds) }, + }); + } + + async findWebhookBasedActiveWorkflows() { + return this.createQueryBuilder('workflow') + .select('DISTINCT workflow.id, workflow.name') + .innerJoin(WebhookEntity, 'webhook_entity', 'workflow.id = webhook_entity.workflowId') + .execute() as Promise>; + } } diff --git a/packages/cli/src/workflows/workflow.service.ee.ts b/packages/cli/src/workflows/workflow.service.ee.ts index f665bf95fd..8ce3a39da5 100644 --- a/packages/cli/src/workflows/workflow.service.ee.ts +++ b/packages/cli/src/workflows/workflow.service.ee.ts @@ -18,6 +18,7 @@ import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.reposi import { BadRequestError } from '@/errors/response-errors/bad-request.error'; import { NotFoundError } from '@/errors/response-errors/not-found.error'; import { WorkflowRepository } from '@/databases/repositories/workflow.repository'; +import { CredentialsRepository } from '@/databases/repositories/credentials.repository'; @Service() export class EnterpriseWorkflowService { @@ -27,6 +28,7 @@ export class EnterpriseWorkflowService { private readonly roleService: RoleService, private readonly sharedWorkflowRepository: SharedWorkflowRepository, private readonly workflowRepository: WorkflowRepository, + private readonly credentialsRepository: CredentialsRepository, ) {} async isOwned( @@ -111,7 +113,7 @@ export class EnterpriseWorkflowService { credentialIdsUsedByWorkflow.add(credential.id); }); }); - const workflowCredentials = await CredentialsService.getManyByIds( + const workflowCredentials = await this.credentialsRepository.getManyByIds( Array.from(credentialIdsUsedByWorkflow), { withSharings: true }, ); diff --git a/packages/cli/src/workflows/workflow.service.ts b/packages/cli/src/workflows/workflow.service.ts index 96cd9715d8..7844316839 100644 --- a/packages/cli/src/workflows/workflow.service.ts +++ b/packages/cli/src/workflows/workflow.service.ts @@ -20,7 +20,6 @@ import { NodeTypes } from '@/NodeTypes'; import { WorkflowRunner } from '@/WorkflowRunner'; import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData'; import { TestWebhooks } from '@/TestWebhooks'; -import { whereClause } from '@/UserManagement/UserManagementHelper'; import { InternalHooks } from '@/InternalHooks'; import { WorkflowRepository } from '@db/repositories/workflow.repository'; import { OwnershipService } from '@/services/ownership.service'; @@ -137,16 +136,12 @@ export class WorkflowService { forceSave?: boolean, roles?: string[], ): Promise { - const shared = await this.sharedWorkflowRepository.findOne({ - relations: ['workflow', 'role'], - where: whereClause({ - user, - globalScope: 'workflow:update', - entityType: 'workflow', - entityId: workflowId, - roles, - }), - }); + const shared = await this.sharedWorkflowRepository.findSharing( + workflowId, + user, + 'workflow:update', + { roles }, + ); if (!shared) { this.logger.verbose('User attempted to update a workflow without permissions', { @@ -403,16 +398,12 @@ export class WorkflowService { async delete(user: User, workflowId: string): Promise { await this.externalHooks.run('workflow.delete', [workflowId]); - const sharedWorkflow = await this.sharedWorkflowRepository.findOne({ - relations: ['workflow', 'role'], - where: whereClause({ - user, - globalScope: 'workflow:delete', - entityType: 'workflow', - entityId: workflowId, - roles: ['owner'], - }), - }); + const sharedWorkflow = await this.sharedWorkflowRepository.findSharing( + workflowId, + user, + 'workflow:delete', + { roles: ['owner'] }, + ); if (!sharedWorkflow) { return; diff --git a/packages/cli/src/workflows/workflows.controller.ts b/packages/cli/src/workflows/workflows.controller.ts index 3ed338189c..b2ec5929f9 100644 --- a/packages/cli/src/workflows/workflows.controller.ts +++ b/packages/cli/src/workflows/workflows.controller.ts @@ -16,7 +16,6 @@ import type { ListQuery, WorkflowRequest } from '@/requests'; import { isBelowOnboardingThreshold } from '@/WorkflowHelpers'; import { EEWorkflowController } from './workflows.controller.ee'; import { WorkflowService } from './workflow.service'; -import { whereClause } from '@/UserManagement/UserManagementHelper'; import { Container } from 'typedi'; import { InternalHooks } from '@/InternalHooks'; import { RoleService } from '@/services/role.service'; @@ -196,22 +195,14 @@ workflowsController.get( ResponseHelper.send(async (req: WorkflowRequest.Get) => { const { id: workflowId } = req.params; - let relations = ['workflow', 'workflow.tags', 'role']; + const extraRelations = config.getEnv('workflowTagsDisabled') ? [] : ['workflow.tags']; - if (config.getEnv('workflowTagsDisabled')) { - relations = relations.filter((relation) => relation !== 'workflow.tags'); - } - - const shared = await Container.get(SharedWorkflowRepository).findOne({ - relations, - where: whereClause({ - user: req.user, - entityType: 'workflow', - globalScope: 'workflow:read', - entityId: workflowId, - roles: ['owner'], - }), - }); + const shared = await Container.get(SharedWorkflowRepository).findSharing( + workflowId, + req.user, + 'workflow:read', + { extraRelations }, + ); if (!shared) { Container.get(Logger).verbose('User attempted to access a workflow without permissions', { diff --git a/packages/cli/test/integration/debug.controller.test.ts b/packages/cli/test/integration/debug.controller.test.ts index 8336702c68..7b5fb31413 100644 --- a/packages/cli/test/integration/debug.controller.test.ts +++ b/packages/cli/test/integration/debug.controller.test.ts @@ -31,19 +31,11 @@ describe('DebugController', () => { const instanceId = 'main-71JdWtq306epIFki'; const leaderKey = 'some-leader-key'; - const createQueryBuilder = { - select: () => createQueryBuilder, - innerJoin: () => createQueryBuilder, - execute: () => webhooks, - }; - - workflowRepository.find.mockResolvedValue(triggersAndPollers); + workflowRepository.findIn.mockResolvedValue(triggersAndPollers); + workflowRepository.findWebhookBasedActiveWorkflows.mockResolvedValue(webhooks); activeWorkflowRunner.allActiveInMemory.mockReturnValue([workflowId]); activeWorkflowRunner.getAllWorkflowActivationErrors.mockResolvedValue(activationErrors); - jest - .spyOn(workflowRepository, 'createQueryBuilder') - .mockImplementation(() => createQueryBuilder); jest.spyOn(MultiMainSetup.prototype, 'instanceId', 'get').mockReturnValue(instanceId); jest.spyOn(MultiMainSetup.prototype, 'fetchLeaderKey').mockResolvedValue(leaderKey); jest.spyOn(MultiMainSetup.prototype, 'isLeader', 'get').mockReturnValue(true);