Skip to content

Commit

Permalink
allow transferring credentials from any project type to any other pro…
Browse files Browse the repository at this point in the history
…ject type
  • Loading branch information
despairblue committed Sep 9, 2024
1 parent 5f97c28 commit 625f9e2
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 176 deletions.
8 changes: 0 additions & 8 deletions packages/cli/src/credentials/credentials.service.ee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
277 changes: 109 additions & 168 deletions packages/cli/test/integration/credentials/credentials.api.ee.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'],
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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<ProjectRole>(['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> | Project,
// destination project
() => Promise<Project> | 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) {
Expand Down

0 comments on commit 625f9e2

Please sign in to comment.