n8n/packages/cli/test/integration/public-api/users.test.ts

458 lines
11 KiB
TypeScript

import { testDb, mockInstance } from '@n8n/backend-test-utils';
import { FeatureNotLicensedError } from '@/errors/feature-not-licensed.error';
import { Telemetry } from '@/telemetry';
import { createRole } from '@test-integration/db/roles';
import {
createMember,
createMemberWithApiKey,
createOwnerWithApiKey,
getUserById,
} from '@test-integration/db/users';
import { setupTestServer } from '@test-integration/utils';
describe('Users in Public API', () => {
const testServer = setupTestServer({ endpointGroups: ['publicApi'] });
mockInstance(Telemetry);
beforeAll(async () => {
await testDb.init();
});
beforeEach(async () => {
await testDb.truncate(['User']);
});
describe('GET /users', () => {
it('if not authenticated, should reject', async () => {
/**
* Act
*/
const response = await testServer.publicApiAgentWithApiKey('').get('/users');
/**
* Assert
*/
expect(response.status).toBe(401);
});
it('should return users with roles', async () => {
/**
* Arrange
*/
const owner = await createOwnerWithApiKey();
const includeRole = true;
await createMember();
await createMember();
await createMember();
/**
* Act
*/
const response = await testServer
.publicApiAgentFor(owner)
.get('/users')
.query({ includeRole });
/**
* Assert
*/
expect(response.status).toBe(200);
const { data: users } = response.body;
expect(users).toHaveLength(4);
users.forEach((user: any) => {
expect(user).toHaveProperty('id');
expect(user).toHaveProperty('email');
expect(user).toHaveProperty('firstName');
expect(user).toHaveProperty('lastName');
expect(user).toHaveProperty('createdAt');
expect(user).toHaveProperty('updatedAt');
expect(user).toHaveProperty('isPending');
expect(user).toHaveProperty('role');
});
const members = users.filter((user: any) => user.role === 'global:member');
expect(members).toHaveLength(3);
const owners = users.filter((user: any) => user.role === 'global:owner');
expect(owners).toHaveLength(1);
});
});
describe('GET /users/:id', () => {
it('if not authenticated, should reject', async () => {
/**
* Arrange
*/
const member = await createMember();
/**
* Act
*/
const response = await testServer.publicApiAgentWithApiKey('').get(`/users/${member.id}`);
/**
* Assert
*/
expect(response.status).toBe(401);
});
it('should return a user with role', async () => {
/**
* Arrange
*/
const owner = await createOwnerWithApiKey();
const member = await createMember();
const includeRole = true;
/**
* Act
*/
const response = await testServer
.publicApiAgentFor(owner)
.get(`/users/${member.id}`)
.query({ includeRole });
/**
* Assert
*/
expect(response.status).toBe(200);
const returnedUser = response.body;
expect(returnedUser).toHaveProperty('id', member.id);
expect(returnedUser).toHaveProperty('email', member.email);
expect(returnedUser).toHaveProperty('firstName', member.firstName);
expect(returnedUser).toHaveProperty('lastName', member.lastName);
expect(returnedUser).toHaveProperty('createdAt');
expect(returnedUser).toHaveProperty('updatedAt');
expect(returnedUser).toHaveProperty('isPending', member.isPending);
expect(returnedUser).toHaveProperty('role', 'global:member');
});
});
describe('POST /users', () => {
it('if not authenticated, should reject', async () => {
/**
* Arrange
*/
const payload = { email: 'test@test.com', role: 'global:admin' };
/**
* Act
*/
const response = await testServer.publicApiAgentWithApiKey('').post('/users').send(payload);
/**
* Assert
*/
expect(response.status).toBe(401);
});
it('if missing scope, should reject', async () => {
/**
* Arrange
*/
testServer.license.enable('feat:advancedPermissions');
const member = await createMemberWithApiKey();
const payload = [{ email: 'test@test.com', role: 'global:admin' }];
/**
* Act
*/
const response = await testServer.publicApiAgentFor(member).post('/users').send(payload);
/**
* Assert
*/
expect(response.status).toBe(403);
expect(response.body).toHaveProperty('message', 'Forbidden');
});
it('should fail if role does not exist', async () => {
/**
* Arrange
*/
testServer.license.enable('feat:advancedPermissions');
const owner = await createOwnerWithApiKey();
const payload = [{ email: 'test@test.com', role: 'non-existing-role' }];
/**
* Act
*/
const response = await testServer.publicApiAgentFor(owner).post('/users').send(payload);
/**
* Assert
*/
expect(response.status).toBe(400);
expect(response.body).toHaveProperty('message', 'Role non-existing-role does not exist');
});
it('should create a user', async () => {
/**
* Arrange
*/
testServer.license.enable('feat:advancedPermissions');
const owner = await createOwnerWithApiKey();
const payload = [{ email: 'test@test.com', role: 'global:admin' }];
/**
* Act
*/
const response = await testServer.publicApiAgentFor(owner).post('/users').send(payload);
/**
* Assert
*/
expect(response.status).toBe(201);
expect(response.body).toHaveLength(1);
const [result] = response.body;
const { user: returnedUser, error } = result;
const payloadUser = payload[0];
expect(returnedUser).toHaveProperty('email', payload[0].email);
expect(typeof returnedUser.inviteAcceptUrl).toBe('string');
expect(typeof returnedUser.emailSent).toBe('boolean');
expect(error).toBe('');
const storedUser = await getUserById(returnedUser.id);
expect(returnedUser.id).toBe(storedUser.id);
expect(returnedUser.email).toBe(storedUser.email);
expect(returnedUser.email).toBe(payloadUser.email);
expect(storedUser.role.slug).toBe(payloadUser.role);
});
it('should create a user with an existing custom role', async () => {
/**
* Arrange
*/
testServer.license.enable('feat:advancedPermissions');
const owner = await createOwnerWithApiKey();
const customRole = 'custom:role';
await createRole({ slug: customRole, displayName: 'Custom role', roleType: 'global' });
const payload = [{ email: 'test@test.com', role: customRole }];
/**
* Act
*/
const response = await testServer.publicApiAgentFor(owner).post('/users').send(payload);
/**
* Assert
*/
expect(response.status).toBe(201);
});
});
describe('DELETE /users/:id', () => {
it('if not authenticated, should reject', async () => {
/**
* Arrange
*/
const member = await createMember();
/**
* Act
*/
const response = await testServer.publicApiAgentWithApiKey('').delete(`/users/${member.id}`);
/**
* Assert
*/
expect(response.status).toBe(401);
});
it('if missing scope, should reject', async () => {
/**
* Arrange
*/
testServer.license.enable('feat:advancedPermissions');
const member = await createMemberWithApiKey();
const secondMember = await createMember();
/**
* Act
*/
const response = await testServer
.publicApiAgentFor(member)
.delete(`/users/${secondMember.id}`);
/**
* Assert
*/
expect(response.status).toBe(403);
expect(response.body).toHaveProperty('message', 'Forbidden');
});
it('should delete a user', async () => {
/**
* Arrange
*/
testServer.license.enable('feat:advancedPermissions');
const owner = await createOwnerWithApiKey();
const member = await createMember();
/**
* Act
*/
const response = await testServer.publicApiAgentFor(owner).delete(`/users/${member.id}`);
/**
* Assert
*/
expect(response.status).toBe(204);
await expect(getUserById(member.id)).rejects.toThrow();
});
});
describe('PATCH /users/:id/role', () => {
it('if not authenticated, should reject', async () => {
/**
* Arrange
*/
const member = await createMember();
/**
* Act
*/
const response = await testServer
.publicApiAgentWithApiKey('')
.patch(`/users/${member.id}/role`);
/**
* Assert
*/
expect(response.status).toBe(401);
});
it('if not licensed, should reject', async () => {
/**
* Arrange
*/
const owner = await createOwnerWithApiKey();
const member = await createMember();
const payload = { newRoleName: 'global:admin' };
/**
* Act
*/
const response = await testServer
.publicApiAgentFor(owner)
.patch(`/users/${member.id}/role`)
.send(payload);
/**
* Assert
*/
expect(response.status).toBe(403);
expect(response.body).toHaveProperty(
'message',
new FeatureNotLicensedError('feat:advancedPermissions').message,
);
});
it('if missing scope, should reject', async () => {
/**
* Arrange
*/
testServer.license.enable('feat:advancedPermissions');
const member = await createMemberWithApiKey();
const secondMember = await createMember();
const payload = { newRoleName: 'global:admin' };
/**
* Act
*/
const response = await testServer
.publicApiAgentFor(member)
.patch(`/users/${secondMember.id}/role`)
.send(payload);
/**
* Assert
*/
expect(response.status).toBe(403);
expect(response.body).toHaveProperty('message', 'Forbidden');
});
it('should return a 400 on invalid payload', async () => {
/**
* Arrange
*/
testServer.license.enable('feat:advancedPermissions');
const owner = await createOwnerWithApiKey();
const member = await createMember();
const payload = { newRoleName: 'invalid' };
/**
* Act
*/
const response = await testServer
.publicApiAgentFor(owner)
.patch(`/users/${member.id}/role`)
.send(payload);
/**
* Assert
*/
expect(response.status).toBe(400);
});
it("should change a user's role", async () => {
/**
* Arrange
*/
testServer.license.enable('feat:advancedPermissions');
const owner = await createOwnerWithApiKey();
const member = await createMember();
const payload = { newRoleName: 'global:admin' };
/**
* Act
*/
const response = await testServer
.publicApiAgentFor(owner)
.patch(`/users/${member.id}/role`)
.send(payload);
/**
* Assert
*/
expect(response.status).toBe(204);
const storedUser = await getUserById(member.id);
expect(storedUser.role.slug).toBe(payload.newRoleName);
});
it('should change a user role to an existing custom role', async () => {
/**
* Arrange
*/
testServer.license.enable('feat:advancedPermissions');
const owner = await createOwnerWithApiKey();
const member = await createMember();
const customRole = 'custom:role';
await createRole({ slug: customRole, displayName: 'Custom role', roleType: 'global' });
const payload = { newRoleName: customRole };
/**
* Act
*/
const response = await testServer
.publicApiAgentFor(owner)
.patch(`/users/${member.id}/role`)
.send(payload);
/**
* Assert
*/
expect(response.status).toBe(204);
const storedUser = await getUserById(member.id);
expect(storedUser.role.slug).toBe(payload.newRoleName);
});
});
});