Files
umami/tests/api/users.spec.ts

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 = '';
});
});