162 lines
5.5 KiB
TypeScript
162 lines
5.5 KiB
TypeScript
import { expect, test } from './fixtures';
|
|
import { login } from './helpers/auth';
|
|
import { UNKNOWN_UUID } from './helpers/constants';
|
|
import { uniqueName } from './helpers/entities';
|
|
|
|
test.describe('Users', () => {
|
|
test.describe.configure({ mode: 'serial' });
|
|
|
|
const username = uniqueName('user');
|
|
const password = 'spec-password-1';
|
|
let userId = '';
|
|
|
|
test.afterAll(async ({ admin }) => {
|
|
if (userId) {
|
|
await admin.del(`/api/users/${userId}`);
|
|
}
|
|
});
|
|
|
|
test('POST /api/users creates a user', async ({ admin }) => {
|
|
const response = await admin.post('/api/users', {
|
|
username: username.toUpperCase(),
|
|
password,
|
|
role: 'user',
|
|
});
|
|
|
|
expect(response.status).toBe(200);
|
|
// Usernames are stored lowercased.
|
|
expect(response.body).toMatchObject({ username, role: 'user' });
|
|
|
|
userId = response.body.id;
|
|
});
|
|
|
|
test('POST /api/users rejects duplicates and invalid bodies', async ({ admin }) => {
|
|
const duplicate = await admin.post('/api/users', { username, password, role: 'user' });
|
|
const shortPassword = await admin.post('/api/users', {
|
|
username: uniqueName('user'),
|
|
password: 'short',
|
|
role: 'user',
|
|
});
|
|
const badRole = await admin.post('/api/users', {
|
|
username: uniqueName('user'),
|
|
password,
|
|
role: 'superuser',
|
|
});
|
|
|
|
expect(duplicate.status).toBe(400);
|
|
expect(duplicate.body.error.message).toBe('User already exists');
|
|
expect(shortPassword.status).toBe(400);
|
|
expect(badRole.status).toBe(400);
|
|
});
|
|
|
|
test('POST /api/users requires an admin session', async ({ api, user, apiKey }) => {
|
|
const body = { username: uniqueName('user'), password, role: 'user' };
|
|
|
|
expect((await api.post('/api/users', body)).status).toBe(401);
|
|
expect((await user.post('/api/users', body)).status).toBe(401);
|
|
// API keys are blocked on /api/users even for admins.
|
|
expect((await apiKey.post('/api/users', body)).status).toBe(401);
|
|
});
|
|
|
|
test('GET /api/users/{userId} returns a user', async ({ admin, api, viewer }) => {
|
|
const response = await admin.get(`/api/users/${userId}`);
|
|
const self = await api
|
|
.bearer(await login(api, { username, password }))
|
|
.get(`/api/users/${userId}`);
|
|
const denied = await viewer.get(`/api/users/${userId}`);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body).toMatchObject({ id: userId, username, role: 'user' });
|
|
expect(response.body).not.toHaveProperty('password');
|
|
expect(self.status).toBe(200);
|
|
expect(denied.status).toBe(401);
|
|
});
|
|
|
|
test('POST /api/users/{userId} lets admins update role, username and password', async ({
|
|
admin,
|
|
api,
|
|
}) => {
|
|
const newPassword = 'spec-password-2';
|
|
const response = await admin.post(`/api/users/${userId}`, {
|
|
role: 'view-only',
|
|
password: newPassword,
|
|
});
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body).toMatchObject({ id: userId, username, role: 'view-only' });
|
|
|
|
// The new password works and the old one does not.
|
|
expect(await login(api, { username, password: newPassword })).toEqual(expect.any(String));
|
|
expect((await api.post('/api/auth/login', { username, password })).status).toBe(401);
|
|
|
|
await admin.post(`/api/users/${userId}`, { password });
|
|
});
|
|
|
|
test('POST /api/users/{userId} ignores role changes from non-admins', async ({ user, seed }) => {
|
|
const response = await user.post(`/api/users/${seed.user.id}`, { role: 'admin' });
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body.role).toBe('user');
|
|
});
|
|
|
|
test('POST /api/users/{userId} rejects unknown users and other users', async ({
|
|
admin,
|
|
user,
|
|
}) => {
|
|
const unknown = await admin.post(`/api/users/${UNKNOWN_UUID}`, { role: 'user' });
|
|
const denied = await user.post(`/api/users/${userId}`, { password: 'spec-password-3' });
|
|
|
|
expect(unknown.status).toBe(404);
|
|
expect(denied.status).toBe(401);
|
|
});
|
|
|
|
test('GET /api/users/{userId}/websites lists a user websites', async ({
|
|
admin,
|
|
user,
|
|
viewer,
|
|
seed,
|
|
}) => {
|
|
const response = await admin.get(`/api/users/${seed.user.id}/websites`);
|
|
const self = await user.get(`/api/users/${seed.user.id}/websites`, {
|
|
params: { includeTeams: 'true' },
|
|
});
|
|
const denied = await viewer.get(`/api/users/${seed.user.id}/websites`);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body.data.map((w: any) => w.id)).toContain(seed.website2.id);
|
|
expect(self.status).toBe(200);
|
|
expect(self.body.data.map((w: any) => w.id)).toContain(seed.website2.id);
|
|
expect(denied.status).toBe(401);
|
|
});
|
|
|
|
test('GET /api/users/{userId}/teams lists a user teams', async ({
|
|
admin,
|
|
user,
|
|
viewer,
|
|
seed,
|
|
}) => {
|
|
const response = await admin.get(`/api/users/${seed.user.id}/teams`);
|
|
const self = await user.get(`/api/users/${seed.user.id}/teams`);
|
|
const denied = await viewer.get(`/api/users/${seed.user.id}/teams`);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body.data.map((t: any) => t.id)).toContain(seed.team.id);
|
|
expect(self.status).toBe(200);
|
|
expect(denied.status).toBe(401);
|
|
});
|
|
|
|
test('DELETE /api/users/{userId} deletes a user', async ({ admin, user, seed }) => {
|
|
const denied = await user.del(`/api/users/${userId}`);
|
|
const self = await admin.del(`/api/users/${seed.admin.id}`);
|
|
const response = await admin.del(`/api/users/${userId}`);
|
|
|
|
expect(denied.status).toBe(401);
|
|
expect(self.status).toBe(400);
|
|
expect(self.body.error.message).toBe('You cannot delete yourself.');
|
|
expect(response.status).toBe(200);
|
|
expect(response.body).toEqual({ ok: true });
|
|
|
|
userId = '';
|
|
});
|
|
});
|