diff --git a/packages/cli/src/WorkflowHelpers.ts b/packages/cli/src/WorkflowHelpers.ts index 870a154043..010dee308a 100644 --- a/packages/cli/src/WorkflowHelpers.ts +++ b/packages/cli/src/WorkflowHelpers.ts @@ -420,18 +420,13 @@ 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. */ -export async function getSharedWorkflowIds(user: User, roles?: RoleNames[]): Promise { +export async function getSharedWorkflowIds(user: User, roleNames?: RoleNames[]): Promise { const where: FindOptionsWhere = {}; if (!user.hasGlobalScope('workflow:read')) { where.userId = user.id; } - if (roles?.length) { - const roleIds = await Container.get(RoleRepository) - .find({ - select: ['id'], - where: { name: In(roles), scope: 'workflow' }, - }) - .then((role) => role.map(({ id }) => id)); + if (roleNames?.length) { + const roleIds = await Container.get(RoleRepository).getIdsInScopeWorkflowByNames(roleNames); where.roleId = In(roleIds); } diff --git a/packages/cli/src/controllers/users.controller.ts b/packages/cli/src/controllers/users.controller.ts index 8ca0321b0f..cd47a7fde6 100644 --- a/packages/cli/src/controllers/users.controller.ts +++ b/packages/cli/src/controllers/users.controller.ts @@ -1,4 +1,3 @@ -import { In } from 'typeorm'; import { User } from '@db/entities/User'; import { SharedCredentials } from '@db/entities/SharedCredentials'; import { SharedWorkflow } from '@db/entities/SharedWorkflow'; @@ -214,10 +213,11 @@ export class UsersController { // Prevents issues with unique key constraints since user being assigned // workflows and credentials might be a sharee - await transactionManager.delete(SharedWorkflow, { - user: transferee, - workflowId: In(sharedWorkflowIds), - }); + await this.sharedWorkflowRepository.deleteByIds( + transactionManager, + sharedWorkflowIds, + transferee, + ); // Transfer ownership of owned workflows await transactionManager.update( @@ -239,10 +239,11 @@ export class UsersController { // Prevents issues with unique key constraints since user being assigned // workflows and credentials might be a sharee - await transactionManager.delete(SharedCredentials, { - user: transferee, - credentialsId: In(sharedCredentialIds), - }); + await this.sharedCredentialsRepository.deleteByIds( + transactionManager, + sharedCredentialIds, + transferee, + ); // Transfer ownership of owned credentials await transactionManager.update( diff --git a/packages/cli/src/credentials/credentials.controller.ee.ts b/packages/cli/src/credentials/credentials.controller.ee.ts index 49131babbe..8524996efd 100644 --- a/packages/cli/src/credentials/credentials.controller.ee.ts +++ b/packages/cli/src/credentials/credentials.controller.ee.ts @@ -10,7 +10,6 @@ import { EECredentialsService as EECredentials } from './credentials.service.ee' import { OwnershipService } from '@/services/ownership.service'; import { Container } from 'typedi'; import { InternalHooks } from '@/InternalHooks'; -import type { CredentialsEntity } from '@db/entities/CredentialsEntity'; import { BadRequestError } from '@/errors/response-errors/bad-request.error'; import { NotFoundError } from '@/errors/response-errors/not-found.error'; import { UnauthorizedError } from '@/errors/response-errors/unauthorized.error'; @@ -38,10 +37,10 @@ EECredentialsController.get( const { id: credentialId } = req.params; const includeDecryptedData = req.query.includeData === 'true'; - let credential = (await EECredentials.get( - { id: credentialId }, - { relations: ['shared', 'shared.role', 'shared.user'] }, - )) as CredentialsEntity; + let credential = await Container.get(CredentialsRepository).findOne({ + where: { id: credentialId }, + relations: ['shared', 'shared.role', 'shared.user'], + }); if (!credential) { throw new NotFoundError( diff --git a/packages/cli/src/databases/repositories/role.repository.ts b/packages/cli/src/databases/repositories/role.repository.ts index cda3726aa3..9694ec1033 100644 --- a/packages/cli/src/databases/repositories/role.repository.ts +++ b/packages/cli/src/databases/repositories/role.repository.ts @@ -1,5 +1,5 @@ import { Service } from 'typedi'; -import { DataSource, Repository } from 'typeorm'; +import { DataSource, In, Repository } from 'typeorm'; import type { RoleNames, RoleScopes } from '../entities/Role'; import { Role } from '../entities/Role'; import { User } from '../entities/User'; @@ -32,4 +32,11 @@ export class RoleRepository extends Repository { return acc; }, {}); } + + async getIdsInScopeWorkflowByNames(roleNames: RoleNames[]) { + return this.find({ + select: ['id'], + where: { name: In(roleNames), scope: 'workflow' }, + }).then((role) => role.map(({ id }) => id)); + } } diff --git a/packages/cli/src/databases/repositories/sharedCredentials.repository.ts b/packages/cli/src/databases/repositories/sharedCredentials.repository.ts index 92918f237a..8250a61357 100644 --- a/packages/cli/src/databases/repositories/sharedCredentials.repository.ts +++ b/packages/cli/src/databases/repositories/sharedCredentials.repository.ts @@ -1,5 +1,5 @@ import { Service } from 'typedi'; -import type { FindOptionsWhere } from 'typeorm'; +import type { EntityManager, FindOptionsWhere } from 'typeorm'; import { DataSource, In, Not, Repository } from 'typeorm'; import { SharedCredentials } from '../entities/SharedCredentials'; import type { User } from '../entities/User'; @@ -60,4 +60,11 @@ export class SharedCredentialsRepository extends Repository { return this.find({ where }); } + + async deleteByIds(transaction: EntityManager, sharedCredentialsIds: string[], user?: User) { + return transaction.delete(SharedCredentials, { + user, + credentialsId: In(sharedCredentialsIds), + }); + } } diff --git a/packages/cli/src/databases/repositories/sharedWorkflow.repository.ts b/packages/cli/src/databases/repositories/sharedWorkflow.repository.ts index 2229b6b84d..8e026cfa00 100644 --- a/packages/cli/src/databases/repositories/sharedWorkflow.repository.ts +++ b/packages/cli/src/databases/repositories/sharedWorkflow.repository.ts @@ -1,6 +1,6 @@ import { Service } from 'typedi'; import { DataSource, Repository, In, Not } from 'typeorm'; -import type { EntityManager, FindOptionsWhere } from 'typeorm'; +import type { EntityManager, FindOptionsSelect, FindOptionsWhere } from 'typeorm'; import { SharedWorkflow } from '../entities/SharedWorkflow'; import { type User } from '../entities/User'; import type { Scope } from '@n8n/permissions'; @@ -125,4 +125,20 @@ export class SharedWorkflowRepository extends Repository { return transaction.save(newSharedWorkflows); } + + async findWithFields(workflowIds: string[], { fields }: { fields: string[] }) { + return this.find({ + where: { + workflowId: In(workflowIds), + }, + select: fields as FindOptionsSelect, + }); + } + + async deleteByIds(transaction: EntityManager, sharedWorkflowIds: string[], user?: User) { + return transaction.delete(SharedWorkflow, { + user, + workflowId: In(sharedWorkflowIds), + }); + } } diff --git a/packages/cli/src/databases/repositories/workflow.repository.ts b/packages/cli/src/databases/repositories/workflow.repository.ts index 5410967779..79cd6461c0 100644 --- a/packages/cli/src/databases/repositories/workflow.repository.ts +++ b/packages/cli/src/databases/repositories/workflow.repository.ts @@ -54,8 +54,14 @@ export class WorkflowRepository extends Repository { }); } - async findByIds(workflowIds: string[]) { - return this.find({ where: { id: In(workflowIds) } }); + async findByIds(workflowIds: string[], { fields }: { fields?: string[] } = {}) { + const options: FindManyOptions = { + where: { id: In(workflowIds) }, + }; + + if (fields?.length) options.select = fields as FindOptionsSelect; + + return this.find(options); } async getActiveTriggerCount() { diff --git a/packages/cli/src/environments/sourceControl/sourceControlImport.service.ee.ts b/packages/cli/src/environments/sourceControl/sourceControlImport.service.ee.ts index 059419273d..f4743a3d67 100644 --- a/packages/cli/src/environments/sourceControl/sourceControlImport.service.ee.ts +++ b/packages/cli/src/environments/sourceControl/sourceControlImport.service.ee.ts @@ -225,18 +225,13 @@ export class SourceControlImportService { const ownerWorkflowRole = await this.getWorkflowOwnerRole(); const workflowRunner = this.activeWorkflowRunner; const candidateIds = candidates.map((c) => c.id); - const existingWorkflows = await Container.get(WorkflowRepository).find({ - where: { - id: In(candidateIds), - }, - select: ['id', 'name', 'versionId', 'active'], - }); - const allSharedWorkflows = await Container.get(SharedWorkflowRepository).find({ - where: { - workflowId: In(candidateIds), - }, - select: ['workflowId', 'roleId', 'userId'], + const existingWorkflows = await Container.get(WorkflowRepository).findByIds(candidateIds, { + fields: ['id', 'name', 'versionId', 'active'], }); + const allSharedWorkflows = await Container.get(SharedWorkflowRepository).findWithFields( + candidateIds, + { fields: ['workflowId', 'roleId', 'userId'] }, + ); const cachedOwnerIds = new Map(); const importWorkflowsResult = await Promise.all( candidates.map(async (candidate) => { diff --git a/packages/cli/src/workflows/workflow.service.ee.ts b/packages/cli/src/workflows/workflow.service.ee.ts index 7a86c7973a..aba0422491 100644 --- a/packages/cli/src/workflows/workflow.service.ee.ts +++ b/packages/cli/src/workflows/workflow.service.ee.ts @@ -15,7 +15,6 @@ import { NotFoundError } from '@/errors/response-errors/not-found.error'; import { WorkflowRepository } from '@/databases/repositories/workflow.repository'; import { CredentialsRepository } from '@/databases/repositories/credentials.repository'; import { RoleService } from '@/services/role.service'; -import type { EntityManager } from 'typeorm'; import { UserRepository } from '@/databases/repositories/user.repository'; @Service() @@ -46,13 +45,6 @@ export class EnterpriseWorkflowService { return { ownsWorkflow: true, workflow }; } - async share(transaction: EntityManager, workflow: WorkflowEntity, shareWithIds: string[]) { - const users = await this.userRepository.getByIds(transaction, shareWithIds); - const role = await this.roleService.findWorkflowEditorRole(); - - await this.sharedWorkflowRepository.share(transaction, workflow, users, role.id); - } - addOwnerAndSharings(workflow: WorkflowWithSharingsAndCredentials): void { workflow.ownedBy = null; workflow.sharedWith = []; diff --git a/packages/cli/src/workflows/workflows.controller.ts b/packages/cli/src/workflows/workflows.controller.ts index 9e7ee4cb5d..756328c03b 100644 --- a/packages/cli/src/workflows/workflows.controller.ts +++ b/packages/cli/src/workflows/workflows.controller.ts @@ -35,6 +35,7 @@ import { WorkflowRepository } from '@/databases/repositories/workflow.repository import type { RoleNames } from '@/databases/entities/Role'; import { UnauthorizedError } from '@/errors/response-errors/unauthorized.error'; import { CredentialsService } from '../credentials/credentials.service'; +import { UserRepository } from '@/databases/repositories/user.repository'; export const workflowsController = express.Router(); @@ -428,7 +429,10 @@ workflowsController.put( ); if (newShareeIds.length) { - await Container.get(EnterpriseWorkflowService).share(trx, workflow!, newShareeIds); + const users = await Container.get(UserRepository).getByIds(trx, newShareeIds); + const role = await Container.get(RoleService).findWorkflowEditorRole(); + + await Container.get(SharedWorkflowRepository).share(trx, workflow!, users, role.id); } });