mirror of
https://github.com/Unleash/unleash.git
synced 2025-06-09 01:17:06 +02:00
In preparation for v6, this PR removes usage and references to `error.description` instead favoring `error.message` (as mentioned #4380) I found no references in the front end, so this might be (I believe it to be) all the required changes.
408 lines
13 KiB
TypeScript
408 lines
13 KiB
TypeScript
import {
|
|
type IUnleashTest,
|
|
setupAppWithCustomConfig,
|
|
} from '../../helpers/test-helper';
|
|
import dbInit, { type ITestDb } from '../../helpers/database-init';
|
|
import getLogger from '../../../fixtures/no-logger';
|
|
import {
|
|
USER_CREATED,
|
|
USER_DELETED,
|
|
USER_UPDATED,
|
|
} from '../../../../lib/types/events';
|
|
import type { IRole } from '../../../../lib/types/stores/access-store';
|
|
import type { IEventStore } from '../../../../lib/types/stores/event-store';
|
|
import type { IUserStore } from '../../../../lib/types/stores/user-store';
|
|
import { RoleName } from '../../../../lib/types/model';
|
|
import type { IRoleStore } from '../../../../lib/types/stores/role-store';
|
|
import { randomId } from '../../../../lib/util/random-id';
|
|
import { omitKeys } from '../../../../lib/util/omit-keys';
|
|
import type { ISessionStore } from '../../../../lib/types/stores/session-store';
|
|
import type { IUnleashStores } from '../../../../lib/types';
|
|
|
|
let stores: IUnleashStores;
|
|
let db: ITestDb;
|
|
let app: IUnleashTest;
|
|
|
|
let userStore: IUserStore;
|
|
let eventStore: IEventStore;
|
|
let roleStore: IRoleStore;
|
|
let sessionStore: ISessionStore;
|
|
let editorRole: IRole;
|
|
let adminRole: IRole;
|
|
|
|
beforeAll(async () => {
|
|
db = await dbInit('user_admin_api_serial', getLogger);
|
|
stores = db.stores;
|
|
app = await setupAppWithCustomConfig(stores, {
|
|
experimental: {
|
|
flags: {
|
|
strictSchemaValidation: true,
|
|
},
|
|
},
|
|
});
|
|
|
|
userStore = stores.userStore;
|
|
eventStore = stores.eventStore;
|
|
roleStore = stores.roleStore;
|
|
sessionStore = stores.sessionStore;
|
|
const roles = await roleStore.getRootRoles();
|
|
editorRole = roles.find((r) => r.name === RoleName.EDITOR)!!;
|
|
adminRole = roles.find((r) => r.name === RoleName.ADMIN)!!;
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await app.destroy();
|
|
await db.destroy();
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await userStore.deleteAll();
|
|
});
|
|
|
|
test('returns empty list of users', async () => {
|
|
return app.request
|
|
.get('/api/admin/user-admin')
|
|
.expect('Content-Type', /json/)
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body.users.length).toBe(0);
|
|
});
|
|
});
|
|
|
|
test('creates and returns all users', async () => {
|
|
const createUserRequests = [...Array(10).keys()].map((i) =>
|
|
app.request
|
|
.post('/api/admin/user-admin')
|
|
.send({
|
|
email: `some${i}@getunleash.ai`,
|
|
name: `Some Name ${i}`,
|
|
rootRole: editorRole.id,
|
|
})
|
|
.set('Content-Type', 'application/json'),
|
|
);
|
|
|
|
await Promise.all(createUserRequests);
|
|
|
|
return app.request
|
|
.get('/api/admin/user-admin')
|
|
.expect('Content-Type', /json/)
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body.users.length).toBe(10);
|
|
expect(res.body.users[2].rootRole).toBe(editorRole.id);
|
|
});
|
|
});
|
|
|
|
test('creates editor-user without password', async () => {
|
|
return app.request
|
|
.post('/api/admin/user-admin')
|
|
.send({
|
|
email: 'some@getunelash.ai',
|
|
name: 'Some Name',
|
|
rootRole: editorRole.id,
|
|
})
|
|
.set('Content-Type', 'application/json')
|
|
.expect(201)
|
|
.expect((res) => {
|
|
expect(res.body.email).toBe('some@getunelash.ai');
|
|
expect(res.body.rootRole).toBe(editorRole.id);
|
|
expect(res.body.id).toBeTruthy();
|
|
});
|
|
});
|
|
|
|
test('creates admin-user with password', async () => {
|
|
const { body } = await app.request
|
|
.post('/api/admin/user-admin')
|
|
.send({
|
|
email: 'some@getunelash.ai',
|
|
name: 'Some Name',
|
|
password: 'some-strange-pass-123-GH',
|
|
rootRole: adminRole.id,
|
|
})
|
|
.set('Content-Type', 'application/json')
|
|
.expect(201);
|
|
|
|
expect(body.rootRole).toBe(adminRole.id);
|
|
|
|
const user = await userStore.getByQuery({ id: body.id });
|
|
expect(user.email).toBe('some@getunelash.ai');
|
|
expect(user.name).toBe('Some Name');
|
|
|
|
const passwordHash = userStore.getPasswordHash(body.id);
|
|
expect(passwordHash).toBeTruthy();
|
|
|
|
const roles = await stores.accessStore.getRolesForUserId(body.id);
|
|
expect(roles.length).toBe(1);
|
|
expect(roles[0].name).toBe(RoleName.ADMIN);
|
|
});
|
|
|
|
test('requires known root role', async () => {
|
|
return app.request
|
|
.post('/api/admin/user-admin')
|
|
.send({
|
|
email: 'some@getunelash.ai',
|
|
name: 'Some Name',
|
|
rootRole: 'Unknown',
|
|
})
|
|
.set('Content-Type', 'application/json')
|
|
.expect(400);
|
|
});
|
|
|
|
test('should require username or email on create', async () => {
|
|
await app.request
|
|
.post('/api/admin/user-admin')
|
|
.send({ rootRole: adminRole.id })
|
|
.set('Content-Type', 'application/json')
|
|
.expect(400)
|
|
.expect((res) => {
|
|
expect(res.body.details[0].message).toEqual(
|
|
'You must specify username or email',
|
|
);
|
|
});
|
|
});
|
|
|
|
test('update user name', async () => {
|
|
const { body } = await app.request
|
|
.post('/api/admin/user-admin')
|
|
.send({
|
|
email: 'some@getunelash.ai',
|
|
name: 'Some Name',
|
|
rootRole: editorRole.id,
|
|
})
|
|
.set('Content-Type', 'application/json');
|
|
|
|
return app.request
|
|
.put(`/api/admin/user-admin/${body.id}`)
|
|
.send({
|
|
name: 'New name',
|
|
})
|
|
.set('Content-Type', 'application/json')
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body.email).toBe('some@getunelash.ai');
|
|
expect(res.body.name).toBe('New name');
|
|
expect(res.body.id).toBe(body.id);
|
|
});
|
|
});
|
|
|
|
test('should not require any fields on update', async () => {
|
|
const { body: created } = await app.request
|
|
.post('/api/admin/user-admin')
|
|
.send({ email: `${randomId()}@example.com`, rootRole: editorRole.id })
|
|
.set('Content-Type', 'application/json')
|
|
.expect(201);
|
|
|
|
const { body: updated } = await app.request
|
|
.put(`/api/admin/user-admin/${created.id}`)
|
|
.send({})
|
|
.set('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(updated).toEqual(
|
|
omitKeys(created, 'emailSent', 'inviteLink', 'rootRole'),
|
|
);
|
|
});
|
|
|
|
test('get a single user', async () => {
|
|
const { body } = await app.request
|
|
.post('/api/admin/user-admin')
|
|
.send({
|
|
email: 'some2@getunelash.ai',
|
|
name: 'Some Name 2',
|
|
rootRole: editorRole.id,
|
|
})
|
|
.set('Content-Type', 'application/json');
|
|
|
|
const { body: user } = await app.request
|
|
.get(`/api/admin/user-admin/${body.id}`)
|
|
.expect(200);
|
|
|
|
expect(user.email).toBe('some2@getunelash.ai');
|
|
expect(user.name).toBe('Some Name 2');
|
|
expect(user.id).toBe(body.id);
|
|
});
|
|
|
|
test('should delete user', async () => {
|
|
const user = await userStore.insert({ email: 'some@mail.com' });
|
|
|
|
await app.request.delete(`/api/admin/user-admin/${user.id}`).expect(200);
|
|
await app.request.get(`/api/admin/user-admin/${user.id}`).expect(404);
|
|
});
|
|
|
|
test('validator should require strong password', async () => {
|
|
return app.request
|
|
.post('/api/admin/user-admin/validate-password')
|
|
.send({ password: 'simple' })
|
|
.expect(400);
|
|
});
|
|
|
|
test('validator should accept strong password', async () => {
|
|
return app.request
|
|
.post('/api/admin/user-admin/validate-password')
|
|
.send({ password: 'simple123-_ASsad' })
|
|
.expect(200);
|
|
});
|
|
|
|
test('should change password', async () => {
|
|
const user = await userStore.insert({ email: 'some@mail.com' });
|
|
const spy = jest.spyOn(sessionStore, 'deleteSessionsForUser');
|
|
await app.request
|
|
.post(`/api/admin/user-admin/${user.id}/change-password`)
|
|
.send({ password: 'simple123-_ASsad' })
|
|
.expect(200);
|
|
expect(spy).toHaveBeenCalled();
|
|
});
|
|
|
|
test('should search for users', async () => {
|
|
await userStore.insert({ email: 'some@mail.com' });
|
|
await userStore.insert({ email: 'another@mail.com' });
|
|
await userStore.insert({ email: 'another2@mail.com' });
|
|
|
|
return app.request
|
|
.get('/api/admin/user-admin/search?q=another')
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body.length).toBe(2);
|
|
expect(res.body.some((u) => u.email === 'another@mail.com')).toBe(
|
|
true,
|
|
);
|
|
});
|
|
});
|
|
|
|
test('Creates a user and includes inviteLink and emailConfigured', async () => {
|
|
return app.request
|
|
.post('/api/admin/user-admin')
|
|
.send({
|
|
email: 'some@getunelash.ai',
|
|
name: 'Some Name',
|
|
rootRole: editorRole.id,
|
|
})
|
|
.set('Content-Type', 'application/json')
|
|
.expect(201)
|
|
.expect((res) => {
|
|
expect(res.body.email).toBe('some@getunelash.ai');
|
|
expect(res.body.rootRole).toBe(editorRole.id);
|
|
expect(res.body.inviteLink).toBeTruthy();
|
|
expect(res.body.emailSent).toBeFalsy();
|
|
expect(res.body.id).toBeTruthy();
|
|
});
|
|
});
|
|
|
|
test('Creates a user but does not send email if sendEmail is set to false', async () => {
|
|
const myAppConfig = await setupAppWithCustomConfig(stores, {
|
|
email: {
|
|
host: 'smtp.ethereal.email',
|
|
smtpuser: 'rafaela.pouros@ethereal.email',
|
|
smtppass: 'CuVPBSvUFBPuqXMFEe',
|
|
},
|
|
});
|
|
|
|
await myAppConfig.request
|
|
.post('/api/admin/user-admin')
|
|
.send({
|
|
email: 'some@getunelash.ai',
|
|
name: 'Some Name',
|
|
rootRole: editorRole.id,
|
|
sendEmail: false,
|
|
})
|
|
.set('Content-Type', 'application/json')
|
|
.expect(201)
|
|
.expect((res) => {
|
|
expect(res.body.emailSent).toBeFalsy();
|
|
});
|
|
await myAppConfig.request
|
|
.post('/api/admin/user-admin')
|
|
.send({
|
|
email: 'some2@getunelash.ai',
|
|
name: 'Some2 Name',
|
|
rootRole: editorRole.id,
|
|
})
|
|
.set('Content-Type', 'application/json')
|
|
.expect(201)
|
|
.expect((res) => {
|
|
expect(res.body.emailSent).toBeTruthy();
|
|
});
|
|
|
|
await myAppConfig.destroy();
|
|
});
|
|
|
|
test('generates USER_CREATED event', async () => {
|
|
const email = 'some@getunelash.ai';
|
|
const name = 'Some Name';
|
|
|
|
const { body } = await app.request
|
|
.post('/api/admin/user-admin')
|
|
.send({
|
|
email,
|
|
name,
|
|
password: 'some-strange-pass-123-GH',
|
|
rootRole: adminRole.id,
|
|
})
|
|
.set('Content-Type', 'application/json')
|
|
.expect(201);
|
|
|
|
const events = await eventStore.getEvents();
|
|
|
|
expect(events[0].type).toBe(USER_CREATED);
|
|
expect(events[0].data.email).toBe(email);
|
|
expect(events[0].data.name).toBe(name);
|
|
expect(events[0].data.id).toBe(body.id);
|
|
expect(events[0].data.password).toBeFalsy();
|
|
});
|
|
|
|
test('generates USER_DELETED event', async () => {
|
|
const user = await userStore.insert({ email: 'some@mail.com' });
|
|
await app.request.delete(`/api/admin/user-admin/${user.id}`).expect(200);
|
|
|
|
const events = await eventStore.getEvents();
|
|
expect(events[0].type).toBe(USER_DELETED);
|
|
expect(events[0].preData.id).toBe(user.id);
|
|
expect(events[0].preData.email).toBe(user.email);
|
|
});
|
|
|
|
test('generates USER_UPDATED event', async () => {
|
|
const { body } = await app.request
|
|
.post('/api/admin/user-admin')
|
|
.send({
|
|
email: 'some@getunelash.ai',
|
|
name: 'Some Name',
|
|
rootRole: editorRole.id,
|
|
})
|
|
.set('Content-Type', 'application/json');
|
|
|
|
await app.request
|
|
.put(`/api/admin/user-admin/${body.id}`)
|
|
.send({
|
|
name: 'New name',
|
|
})
|
|
.set('Content-Type', 'application/json');
|
|
|
|
const events = await eventStore.getEvents();
|
|
expect(events[0].type).toBe(USER_UPDATED);
|
|
expect(events[0].data.id).toBe(body.id);
|
|
expect(events[0].data.name).toBe('New name');
|
|
});
|
|
|
|
test('Anonymises name, username and email fields if anonymiseEventLog flag is set', async () => {
|
|
const anonymisedApp = await setupAppWithCustomConfig(
|
|
stores,
|
|
{ experimental: { flags: { anonymiseEventLog: true } } },
|
|
db.rawDatabase,
|
|
);
|
|
await anonymisedApp.request
|
|
.post('/api/admin/user-admin')
|
|
.send({
|
|
email: 'some@getunleash.ai',
|
|
name: 'Some Name',
|
|
rootRole: editorRole.id,
|
|
})
|
|
.set('Content-Type', 'application/json');
|
|
const response = await anonymisedApp.request.get(
|
|
'/api/admin/user-admin/access',
|
|
);
|
|
const body = response.body;
|
|
expect(body.users[0].email).toEqual('aeb83743e@unleash.run');
|
|
expect(body.users[0].name).toEqual('3a8b17647@unleash.run');
|
|
expect(body.users[0].username).toEqual(''); // Not set, so anonymise should return the empty string.
|
|
});
|