From 625f9e272cb51dad039255d9b6c43da8d02bd122 Mon Sep 17 00:00:00 2001 From: Danny Martini Date: Mon, 9 Sep 2024 18:36:08 +0200 Subject: [PATCH] allow transferring credentials from any project type to any other project type --- .../src/credentials/credentials.service.ee.ts | 8 - .../credentials/credentials.api.ee.test.ts | 277 +++++++----------- 2 files changed, 109 insertions(+), 176 deletions(-) diff --git a/packages/cli/src/credentials/credentials.service.ee.ts b/packages/cli/src/credentials/credentials.service.ee.ts index 3f42d3bff8c3f..ebe9230955f7b 100644 --- a/packages/cli/src/credentials/credentials.service.ee.ts +++ b/packages/cli/src/credentials/credentials.service.ee.ts @@ -155,14 +155,6 @@ export class EnterpriseCredentialsService { "You can't transfer a credential into the project that's already owning it.", ); } - if (sourceProject.type !== 'team' && sourceProject.type !== 'personal') { - throw new TransferCredentialError( - 'You can only transfer credentials out of personal or team projects.', - ); - } - if (destinationProject.type !== 'team') { - throw new TransferCredentialError('You can only transfer credentials into team projects.'); - } await this.sharedCredentialsRepository.manager.transaction(async (trx) => { // 6. transfer the credential diff --git a/packages/cli/test/integration/credentials/credentials.api.ee.test.ts b/packages/cli/test/integration/credentials/credentials.api.ee.test.ts index 1540fe46e4645..d2966ec0c7fe1 100644 --- a/packages/cli/test/integration/credentials/credentials.api.ee.test.ts +++ b/packages/cli/test/integration/credentials/credentials.api.ee.test.ts @@ -31,6 +31,7 @@ import type { SuperAgentTest } from '../shared/types'; import { mockInstance } from '../../shared/mocking'; import { createTeamProject, linkUserToProject } from '../shared/db/projects'; import { createWorkflow, shareWorkflowWithUsers } from '@test-integration/db/workflows'; +import type { ProjectRole } from '@/databases/entities/project-relation'; const testServer = utils.setupTestServer({ endpointGroups: ['credentials'], @@ -1119,18 +1120,6 @@ describe('PUT /:credentialId/transfer', () => { .expect(400); }); - test('cannot transfer into a personal project', async () => { - const credential = await saveCredential(randomCredentialPayload(), { - user: member, - }); - - await testServer - .authAgentFor(member) - .put(`/credentials/${credential.id}/transfer`) - .send({ destinationProjectId: memberPersonalProject.id }) - .expect(400); - }); - test('cannot transfer somebody elses credential', async () => { const destinationProject = await createTeamProject('Destination Project', member); @@ -1159,187 +1148,139 @@ describe('PUT /:credentialId/transfer', () => { .expect(404); }); - test('project:editors cannot transfer credentials', async () => { - // - // ARRANGE - // - const sourceProject = await createTeamProject('Source Project'); - await linkUserToProject(member, sourceProject, 'project:editor'); - - const credential = await saveCredential(randomCredentialPayload(), { - project: sourceProject, - }); - - const destinationProject = await createTeamProject('Destination Project', member); - - // - // ACT & ASSERT - // - await testServer - .authAgentFor(member) - .put(`/credentials/${credential.id}/transfer`) - .send({ destinationProjectId: destinationProject.id }) - .expect(403); - }); - - test('transferring from a personal project to a team project severs all sharings', async () => { - // - // ARRANGE - // - const credential = await saveCredential(randomCredentialPayload(), { user: member }); - - // these sharings should be deleted by the transfer - await shareCredentialWithUsers(credential, [anotherMember, owner]); - - const destinationProject = await createTeamProject('Destination Project', member); - - // - // ACT - // - const response = await testServer - .authAgentFor(member) - .put(`/credentials/${credential.id}/transfer`) - .send({ destinationProjectId: destinationProject.id }) - .expect(200); - - // - // ASSERT - // - expect(response.body).toEqual({}); - - const allSharings = await getCredentialSharings(credential); - expect(allSharings).toHaveLength(1); - expect(allSharings[0]).toMatchObject({ - projectId: destinationProject.id, - credentialsId: credential.id, - role: 'credential:owner', - }); - }); - - test('can transfer from team to another team project', async () => { - // - // ARRANGE - // - const sourceProject = await createTeamProject('Team Project 1', member); - const credential = await saveCredential(randomCredentialPayload(), { - project: sourceProject, - }); - - const destinationProject = await createTeamProject('Team Project 2', member); - - // - // ACT - // - const response = await testServer - .authAgentFor(member) - .put(`/credentials/${credential.id}/transfer`) - .send({ destinationProjectId: destinationProject.id }) - .expect(200); - - // - // ASSERT - // - expect(response.body).toEqual({}); - - const allSharings = await getCredentialSharings(credential); - expect(allSharings).toHaveLength(1); - expect(allSharings[0]).toMatchObject({ - projectId: destinationProject.id, - credentialsId: credential.id, - role: 'credential:owner', - }); - }); - - test.each([ - ['owners', () => owner], - ['admins', () => admin], - ])( - '%s can always transfer from any personal or team project into any team project', - async (_name, actor) => { + test.each(['project:editor', 'project:viewer'])( + '%ss cannot transfer credentials', + async (projectRole) => { // // ARRANGE // - const sourceProject = await createTeamProject('Source Project', member); - const teamCredential = await saveCredential(randomCredentialPayload(), { + const sourceProject = await createTeamProject('Source Project'); + await linkUserToProject(member, sourceProject, projectRole); + + const credential = await saveCredential(randomCredentialPayload(), { project: sourceProject, }); - const personalCredential = await saveCredential(randomCredentialPayload(), { user: member }); - const destinationProject = await createTeamProject('Destination Project', member); // - // ACT + // ACT & ASSERT // - const response1 = await testServer - .authAgentFor(actor()) - .put(`/credentials/${teamCredential.id}/transfer`) + await testServer + .authAgentFor(member) + .put(`/credentials/${credential.id}/transfer`) .send({ destinationProjectId: destinationProject.id }) - .expect(200); - const response2 = await testServer - .authAgentFor(actor()) - .put(`/credentials/${personalCredential.id}/transfer`) + .expect(403); + }, + ); + + test.each< + [ + // user role + 'owners' | 'admins', + // source project type + 'team' | 'personal', + // destination project type + 'team' | 'personal', + // actor + () => User, + // source project + () => Promise | Project, + // destination project + () => Promise | Project, + ] + >([ + // owner + [ + 'owners', + 'team', + 'team', + () => owner, + async () => await createTeamProject('Source Project'), + async () => await createTeamProject('Destination Project'), + ], + [ + 'owners', + 'team', + 'personal', + () => owner, + async () => await createTeamProject('Source Project'), + () => memberPersonalProject, + ], + [ + 'owners', + 'personal', + 'team', + () => owner, + () => memberPersonalProject, + async () => await createTeamProject('Destination Project'), + ], + + // admin + [ + 'admins', + 'team', + 'team', + () => admin, + async () => await createTeamProject('Source Project'), + async () => await createTeamProject('Destination Project'), + ], + [ + 'admins', + 'team', + 'personal', + () => admin, + async () => await createTeamProject('Source Project'), + () => memberPersonalProject, + ], + [ + 'admins', + 'personal', + 'team', + () => admin, + () => memberPersonalProject, + async () => await createTeamProject('Destination Project'), + ], + ])( + '%s can always transfer from a %s project to a %s project', + async ( + _roleName, + _sourceProjectName, + _destinationProjectName, + getUser, + getSourceProject, + getDestinationProject, + ) => { + // ARRANGE + const user = getUser(); + const sourceProject = await getSourceProject(); + const destinationProject = await getDestinationProject(); + + const credential = await saveCredential(randomCredentialPayload(), { + project: sourceProject, + }); + + // ACT + const response = await testServer + .authAgentFor(user) + .put(`/credentials/${credential.id}/transfer`) .send({ destinationProjectId: destinationProject.id }) .expect(200); - // // ASSERT - // - expect(response1.body).toEqual({}); - expect(response2.body).toEqual({}); - - { - const allSharings = await getCredentialSharings(teamCredential); - expect(allSharings).toHaveLength(1); - expect(allSharings[0]).toMatchObject({ - projectId: destinationProject.id, - credentialsId: teamCredential.id, - role: 'credential:owner', - }); - } + expect(response.body).toEqual({}); { - const allSharings = await getCredentialSharings(personalCredential); + const allSharings = await getCredentialSharings(credential); expect(allSharings).toHaveLength(1); expect(allSharings[0]).toMatchObject({ projectId: destinationProject.id, - credentialsId: personalCredential.id, + credentialsId: credential.id, role: 'credential:owner', }); } }, ); - - test.each([ - ['owners', () => owner], - ['admins', () => admin], - ])('%s cannot transfer into personal projects', async (_name, actor) => { - // - // ARRANGE - // - const sourceProject = await createTeamProject('Source Project', member); - const teamCredential = await saveCredential(randomCredentialPayload(), { - project: sourceProject, - }); - - const personalCredential = await saveCredential(randomCredentialPayload(), { user: member }); - - const destinationProject = anotherMemberPersonalProject; - - // - // ACT & ASSERT - // - await testServer - .authAgentFor(actor()) - .put(`/credentials/${teamCredential.id}/transfer`) - .send({ destinationProjectId: destinationProject.id }) - .expect(400); - await testServer - .authAgentFor(actor()) - .put(`/credentials/${personalCredential.id}/transfer`) - .send({ destinationProjectId: destinationProject.id }) - .expect(400); - }); }); function validateMainCredentialData(credential: ListQuery.Credentials.WithOwnedByAndSharedWith) {