feat: RBAC (#8922)
Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> Co-authored-by: Val <68596159+valya@users.noreply.github.com> Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <aditya@netroy.in> Co-authored-by: Valya Bullions <valya@n8n.io> Co-authored-by: Danny Martini <danny@n8n.io> Co-authored-by: Danny Martini <despair.blue@gmail.com> Co-authored-by: Iván Ovejero <ivov.src@gmail.com> Co-authored-by: Omar Ajoue <krynble@gmail.com> Co-authored-by: oleg <me@olegivaniv.com> Co-authored-by: Michael Kret <michael.k@radency.com> Co-authored-by: Michael Kret <88898367+michael-radency@users.noreply.github.com> Co-authored-by: Elias Meire <elias@meire.dev> Co-authored-by: Giulio Andreini <andreini@netseven.it> Co-authored-by: Giulio Andreini <g.andreini@gmail.com> Co-authored-by: Ayato Hayashi <go12limchangyong@gmail.com>
This commit is contained in:
@@ -7,120 +7,179 @@ import { mockInstance } from '../../shared/mocking';
|
||||
import { WorkflowEntity } from '@/databases/entities/WorkflowEntity';
|
||||
import { UserRepository } from '@/databases/repositories/user.repository';
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import { mockCredential, mockUser } from '../shared/mockObjects';
|
||||
import { Project } from '@/databases/entities/Project';
|
||||
import { ProjectRelationRepository } from '@/databases/repositories/projectRelation.repository';
|
||||
import { ProjectRelation } from '@/databases/entities/ProjectRelation';
|
||||
import { mockCredential, mockProject } from '../shared/mockObjects';
|
||||
|
||||
describe('OwnershipService', () => {
|
||||
const userRepository = mockInstance(UserRepository);
|
||||
const sharedWorkflowRepository = mockInstance(SharedWorkflowRepository);
|
||||
const ownershipService = new OwnershipService(mock(), userRepository, sharedWorkflowRepository);
|
||||
const projectRelationRepository = mockInstance(ProjectRelationRepository);
|
||||
const ownershipService = new OwnershipService(
|
||||
mock(),
|
||||
userRepository,
|
||||
mock(),
|
||||
projectRelationRepository,
|
||||
sharedWorkflowRepository,
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('getWorkflowOwner()', () => {
|
||||
test('should retrieve a workflow owner', async () => {
|
||||
const mockOwner = new User();
|
||||
const mockNonOwner = new User();
|
||||
describe('getWorkflowProjectCached()', () => {
|
||||
test('should retrieve a workflow owner project', async () => {
|
||||
const mockProject = new Project();
|
||||
|
||||
const sharedWorkflow = Object.assign(new SharedWorkflow(), {
|
||||
role: 'workflow:owner',
|
||||
user: mockOwner,
|
||||
project: mockProject,
|
||||
});
|
||||
|
||||
sharedWorkflowRepository.findOneOrFail.mockResolvedValueOnce(sharedWorkflow);
|
||||
|
||||
const returnedOwner = await ownershipService.getWorkflowOwnerCached('some-workflow-id');
|
||||
const returnedProject = await ownershipService.getWorkflowProjectCached('some-workflow-id');
|
||||
|
||||
expect(returnedOwner).toBe(mockOwner);
|
||||
expect(returnedOwner).not.toBe(mockNonOwner);
|
||||
expect(returnedProject).toBe(mockProject);
|
||||
});
|
||||
|
||||
test('should throw if no workflow owner found', async () => {
|
||||
test('should throw if no workflow owner project found', async () => {
|
||||
sharedWorkflowRepository.findOneOrFail.mockRejectedValue(new Error());
|
||||
|
||||
await expect(ownershipService.getWorkflowOwnerCached('some-workflow-id')).rejects.toThrow();
|
||||
await expect(ownershipService.getWorkflowProjectCached('some-workflow-id')).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getProjectOwnerCached()', () => {
|
||||
test('should retrieve a project owner', async () => {
|
||||
const mockProject = new Project();
|
||||
const mockOwner = new User();
|
||||
|
||||
const projectRelation = Object.assign(new ProjectRelation(), {
|
||||
role: 'project:personalOwner',
|
||||
project: mockProject,
|
||||
user: mockOwner,
|
||||
});
|
||||
|
||||
projectRelationRepository.getPersonalProjectOwners.mockResolvedValueOnce([projectRelation]);
|
||||
|
||||
const returnedOwner = await ownershipService.getProjectOwnerCached('some-project-id');
|
||||
|
||||
expect(returnedOwner).toBe(mockOwner);
|
||||
});
|
||||
|
||||
test('should not throw if no project owner found, should return null instead', async () => {
|
||||
projectRelationRepository.getPersonalProjectOwners.mockResolvedValueOnce([]);
|
||||
|
||||
const owner = await ownershipService.getProjectOwnerCached('some-project-id');
|
||||
|
||||
expect(owner).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getProjectOwnerCached()', () => {
|
||||
test('should retrieve a project owner', async () => {
|
||||
const mockProject = new Project();
|
||||
const mockOwner = new User();
|
||||
|
||||
const projectRelation = Object.assign(new ProjectRelation(), {
|
||||
role: 'project:personalOwner',
|
||||
project: mockProject,
|
||||
user: mockOwner,
|
||||
});
|
||||
|
||||
projectRelationRepository.getPersonalProjectOwners.mockResolvedValueOnce([projectRelation]);
|
||||
|
||||
const returnedOwner = await ownershipService.getProjectOwnerCached('some-project-id');
|
||||
|
||||
expect(returnedOwner).toBe(mockOwner);
|
||||
});
|
||||
|
||||
test('should not throw if no project owner found, should return null instead', async () => {
|
||||
projectRelationRepository.getPersonalProjectOwners.mockResolvedValueOnce([]);
|
||||
|
||||
const owner = await ownershipService.getProjectOwnerCached('some-project-id');
|
||||
|
||||
expect(owner).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('addOwnedByAndSharedWith()', () => {
|
||||
test('should add `ownedBy` and `sharedWith` to credential', async () => {
|
||||
const owner = mockUser();
|
||||
const editor = mockUser();
|
||||
const ownerProject = mockProject();
|
||||
const editorProject = mockProject();
|
||||
|
||||
const credential = mockCredential();
|
||||
|
||||
credential.shared = [
|
||||
{ role: 'credential:owner', user: owner },
|
||||
{ role: 'credential:editor', user: editor },
|
||||
{ role: 'credential:owner', project: ownerProject },
|
||||
{ role: 'credential:editor', project: editorProject },
|
||||
] as SharedCredentials[];
|
||||
|
||||
const { ownedBy, sharedWith } = ownershipService.addOwnedByAndSharedWith(credential);
|
||||
const { homeProject, sharedWithProjects } =
|
||||
ownershipService.addOwnedByAndSharedWith(credential);
|
||||
|
||||
expect(ownedBy).toStrictEqual({
|
||||
id: owner.id,
|
||||
email: owner.email,
|
||||
firstName: owner.firstName,
|
||||
lastName: owner.lastName,
|
||||
expect(homeProject).toMatchObject({
|
||||
id: ownerProject.id,
|
||||
name: ownerProject.name,
|
||||
type: ownerProject.type,
|
||||
});
|
||||
|
||||
expect(sharedWith).toStrictEqual([
|
||||
expect(sharedWithProjects).toMatchObject([
|
||||
{
|
||||
id: editor.id,
|
||||
email: editor.email,
|
||||
firstName: editor.firstName,
|
||||
lastName: editor.lastName,
|
||||
id: editorProject.id,
|
||||
name: editorProject.name,
|
||||
type: editorProject.type,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('should add `ownedBy` and `sharedWith` to workflow', async () => {
|
||||
const owner = mockUser();
|
||||
const editor = mockUser();
|
||||
const projectOwner = mockProject();
|
||||
const projectEditor = mockProject();
|
||||
|
||||
const workflow = new WorkflowEntity();
|
||||
|
||||
workflow.shared = [
|
||||
{ role: 'workflow:owner', user: owner },
|
||||
{ role: 'workflow:editor', user: editor },
|
||||
{ role: 'workflow:owner', project: projectOwner },
|
||||
{ role: 'workflow:editor', project: projectEditor },
|
||||
] as SharedWorkflow[];
|
||||
|
||||
const { ownedBy, sharedWith } = ownershipService.addOwnedByAndSharedWith(workflow);
|
||||
const { homeProject, sharedWithProjects } =
|
||||
ownershipService.addOwnedByAndSharedWith(workflow);
|
||||
|
||||
expect(ownedBy).toStrictEqual({
|
||||
id: owner.id,
|
||||
email: owner.email,
|
||||
firstName: owner.firstName,
|
||||
lastName: owner.lastName,
|
||||
expect(homeProject).toMatchObject({
|
||||
id: projectOwner.id,
|
||||
name: projectOwner.name,
|
||||
type: projectOwner.type,
|
||||
});
|
||||
|
||||
expect(sharedWith).toStrictEqual([
|
||||
expect(sharedWithProjects).toMatchObject([
|
||||
{
|
||||
id: editor.id,
|
||||
email: editor.email,
|
||||
firstName: editor.firstName,
|
||||
lastName: editor.lastName,
|
||||
id: projectEditor.id,
|
||||
name: projectEditor.name,
|
||||
type: projectEditor.type,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('should produce an empty sharedWith if no sharee', async () => {
|
||||
const owner = mockUser();
|
||||
|
||||
const credential = mockCredential();
|
||||
|
||||
credential.shared = [{ role: 'credential:owner', user: owner }] as SharedCredentials[];
|
||||
const project = mockProject();
|
||||
|
||||
const { ownedBy, sharedWith } = ownershipService.addOwnedByAndSharedWith(credential);
|
||||
credential.shared = [{ role: 'credential:owner', project }] as SharedCredentials[];
|
||||
|
||||
expect(ownedBy).toStrictEqual({
|
||||
id: owner.id,
|
||||
email: owner.email,
|
||||
firstName: owner.firstName,
|
||||
lastName: owner.lastName,
|
||||
const { homeProject, sharedWithProjects } =
|
||||
ownershipService.addOwnedByAndSharedWith(credential);
|
||||
|
||||
expect(homeProject).toMatchObject({
|
||||
id: project.id,
|
||||
name: project.name,
|
||||
type: project.type,
|
||||
});
|
||||
|
||||
expect(sharedWith).toHaveLength(0);
|
||||
expect(sharedWithProjects).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user