Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expand credential sharing tests for User Management #3931

Merged
merged 5 commits into from
Aug 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion packages/cli/src/credentials/credentials.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,9 @@ export interface CredentialWithSharings extends ICredentialsDb {
sharedWith?: UserSharingsDetails[];
}

type UserSharingsDetails = { id: string; email: string; firstName: string; lastName: string };
export type UserSharingsDetails = {
id: string;
email: string;
firstName: string;
lastName: string;
};
104 changes: 104 additions & 0 deletions packages/cli/test/integration/credentials.ee.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { In } from 'typeorm';

import config from '../../config';
import { Db } from '../../src';
import type { UserSharingsDetails } from '../../src/credentials/credentials.types';
import type { CredentialsEntity } from '../../src/databases/entities/CredentialsEntity';
import type { Role } from '../../src/databases/entities/Role';
import { randomCredentialPayload } from './shared/random';
import * as testDb from './shared/testDb';
Expand Down Expand Up @@ -93,6 +95,101 @@ test('router should switch based on flag', async () => {
expect(eeGetStatus).toBe(200);
});

// ----------------------------------------
// GET /credentials - fetch all credentials
// ----------------------------------------

test('GET /credentials should return all creds for owner', async () => {
const owner = await testDb.createUser({ globalRole: globalOwnerRole });
const [member1, member2, member3] = await testDb.createManyUsers(3, {
globalRole: globalMemberRole,
});

const savedCredential = await saveCredential(randomCredentialPayload(), { user: owner });
await saveCredential(randomCredentialPayload(), { user: member1 });

const sharedWith = [member1, member2, member3];
await testDb.shareCredentialWithUsers(savedCredential, sharedWith);

const response = await authAgent(owner).get('/credentials');

expect(response.statusCode).toBe(200);
expect(response.body.data.length).toBe(2); // owner retrieved owner cred and member cred

const [ownerCredential, memberCredential] = response.body.data;

validateMainCredentialData(ownerCredential);
validateMainCredentialData(memberCredential);

expect(ownerCredential.ownedBy).toMatchObject({
id: owner.id,
email: owner.email,
firstName: owner.firstName,
lastName: owner.lastName,
});

expect(Array.isArray(ownerCredential.sharedWith)).toBe(true);
expect(ownerCredential.sharedWith.length).toBe(3);

ownerCredential.sharedWith.forEach((sharee: UserSharingsDetails, idx: number) => {
expect(sharee).toMatchObject({
id: sharedWith[idx].id,
email: sharedWith[idx].email,
firstName: sharedWith[idx].firstName,
lastName: sharedWith[idx].lastName,
});
});

expect(memberCredential.ownedBy).toMatchObject({
id: member1.id,
email: member1.email,
firstName: member1.firstName,
lastName: member1.lastName,
});

expect(Array.isArray(memberCredential.sharedWith)).toBe(true);
expect(memberCredential.sharedWith.length).toBe(0);
});

test('GET /credentials should return only relevant creds for member', async () => {
const [member1, member2] = await testDb.createManyUsers(2, {
globalRole: globalMemberRole,
});

await saveCredential(randomCredentialPayload(), { user: member2 });
const savedMemberCredential = await saveCredential(randomCredentialPayload(), { user: member1 });

await testDb.shareCredentialWithUsers(savedMemberCredential, [member2]);

const response = await authAgent(member1).get('/credentials');

expect(response.statusCode).toBe(200);
expect(response.body.data.length).toBe(1); // member retrieved only member cred

const [member1Credential] = response.body.data;

validateMainCredentialData(member1Credential);

expect(member1Credential.ownedBy).toMatchObject({
id: member1.id,
email: member1.email,
firstName: member1.firstName,
lastName: member1.lastName,
});

expect(Array.isArray(member1Credential.sharedWith)).toBe(true);
expect(member1Credential.sharedWith.length).toBe(1);

const [sharee] = member1Credential.sharedWith;

expect(sharee).toMatchObject({
id: member2.id,
email: member2.email,
firstName: member2.firstName,
lastName: member2.lastName,
});
});

// ----------------------------------------
// indempotent share/unshare
// ----------------------------------------
Expand Down Expand Up @@ -278,3 +375,10 @@ test('PUT /credentials/:id/share should unshare the credential', async () => {
expect(sharedCredentials.length).toBe(1);
expect(sharedCredentials[0].userId).toBe(owner.id);
});

function validateMainCredentialData(ownerCredential: CredentialsEntity) {
expect(typeof ownerCredential.name).toBe('string');
expect(typeof ownerCredential.type).toBe('string');
expect(typeof ownerCredential.nodesAccess[0].nodeType).toBe('string');
expect(ownerCredential.data).toBeUndefined();
}
105 changes: 2 additions & 103 deletions packages/cli/test/integration/credentials.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import express from 'express';
import { UserSettings } from 'n8n-core';

import config from '../../config';
import { Db } from '../../src';
import { RESPONSE_ERROR_MESSAGES } from '../../src/constants';
import { randomCredentialPayload, randomName, randomString } from './shared/random';
Expand All @@ -9,6 +10,7 @@ import * as utils from './shared/utils';

import type { AuthAgent, SaveCredentialFunction } from './shared/types';
import type { Role } from '../../src/databases/entities/Role';
import type { CredentialsEntity } from '../../src/databases/entities/CredentialsEntity';

jest.mock('../../src/telemetry');

Expand Down Expand Up @@ -380,109 +382,6 @@ test('PATCH /credentials/:id should fail with missing encryption key', async ()
mock.mockRestore();
});

test.skip('GET /credentials should retrieve all creds for owner', async () => {
const owner = await testDb.createUser({ globalRole: globalOwnerRole });
const firstMember = await testDb.createUser({ globalRole: globalMemberRole });
const secondMember = await testDb.createUser({ globalRole: globalMemberRole });

const { id } = await saveCredential(randomCredentialPayload(), { user: owner });
await saveCredential(randomCredentialPayload(), { user: firstMember });

await authAgent(owner).post(`/credentials/${id}/share`).send({ shareeId: firstMember.id });
await authAgent(owner).post(`/credentials/${id}/share`).send({ shareeId: secondMember.id });

const response = await authAgent(owner).get('/credentials');

expect(response.statusCode).toBe(200);
expect(response.body.data.length).toBe(2); // owner retrieved owner cred and member cred

const [ownerCredential, memberCredential] = response.body.data;

expect(typeof ownerCredential.name).toBe('string');
expect(typeof ownerCredential.type).toBe('string');
expect(typeof ownerCredential.nodesAccess[0].nodeType).toBe('string');
expect(ownerCredential.encryptedData).toBeUndefined();

expect(ownerCredential.ownedBy.id).toBe(owner.id);
expect(ownerCredential.ownedBy.email).toBe(owner.email);
expect(ownerCredential.ownedBy.firstName).toBe(owner.firstName);
expect(ownerCredential.ownedBy.lastName).toBe(owner.lastName);

const [firstSharee, secondSharee] = ownerCredential.sharedWith;

expect(firstSharee).toMatchObject({
id: firstMember.id,
email: firstMember.email,
firstName: firstMember.firstName,
lastName: firstMember.lastName,
});

expect(secondSharee).toMatchObject({
id: secondMember.id,
email: secondMember.email,
firstName: secondMember.firstName,
lastName: secondMember.lastName,
});

expect(typeof memberCredential.name).toBe('string');
expect(typeof memberCredential.type).toBe('string');
expect(typeof memberCredential.nodesAccess[0].nodeType).toBe('string');
expect(memberCredential.encryptedData).toBeUndefined();

expect(memberCredential.ownedBy.id).toBe(firstMember.id);
expect(memberCredential.ownedBy.email).toBe(firstMember.email);
expect(memberCredential.ownedBy.firstName).toBe(firstMember.firstName);
expect(memberCredential.ownedBy.lastName).toBe(firstMember.lastName);

expect(memberCredential.sharedWith).toBeUndefined();
});

// @TODO: Test for member request

test.skip('GET /credentials should retrieve member creds for member', async () => {
const owner = await testDb.createUser({ globalRole: globalOwnerRole });
const member = await testDb.createUser({ globalRole: globalMemberRole });

await saveCredential(randomCredentialPayload(), { user: owner });

for (let i = 0; i < 3; i++) {
await saveCredential(randomCredentialPayload(), { user: member });
}

const response = await authAgent(member).get('/credentials');

expect(response.statusCode).toBe(200);
expect(response.body.data.length).toBe(3); // member retrieved only member creds

for (const memberCredential of response.body.data) {
expect(typeof memberCredential.name).toBe('string');
expect(typeof memberCredential.type).toBe('string');
expect(typeof memberCredential.nodesAccess[0].nodeType).toBe('string');
expect(memberCredential.encryptedData).toBeUndefined();

expect(memberCredential.ownedBy.id).toBe(member.id);
expect(memberCredential.ownedBy.email).toBe(member.email);
expect(memberCredential.ownedBy.firstName).toBe(member.firstName);
expect(memberCredential.ownedBy.lastName).toBe(member.lastName);

expect(memberCredential.sharedWith).toBeUndefined();
}
});

test('GET /credentials should not retrieve owner creds for member', async () => {
const owner = await testDb.createUser({ globalRole: globalOwnerRole });
const member = await testDb.createUser({ globalRole: globalMemberRole });

for (let i = 0; i < 3; i++) {
await saveCredential(randomCredentialPayload(), { user: owner });
}

const response = await authAgent(member).get('/credentials');

expect(response.statusCode).toBe(200);
expect(response.body.data.length).toBe(0); // member did not retrieve owner's creds
});

test('GET /credentials/:id should retrieve owned cred for owner', async () => {
const ownerShell = await testDb.createUserShell(globalOwnerRole);
const authOwnerAgent = authAgent(ownerShell);
Expand Down