89 lines
3.9 KiB
JavaScript
89 lines
3.9 KiB
JavaScript
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import express from 'express';
|
|
import session from 'express-session';
|
|
import { isTestDbConfigured, setupTestDb } from '../helpers/setup-db.js';
|
|
import usersRouter from '../../src/routes/users.js';
|
|
import authRouter from '../../src/routes/auth.js';
|
|
import { requireAuth } from '../../src/middleware/auth.js';
|
|
import { hashPassword } from '../../src/auth/passwords.js';
|
|
|
|
async function app(pool) {
|
|
process.env.DATABASE_URL = process.env.TEST_DATABASE_URL;
|
|
process.env.AUTH_ENABLED = 'true';
|
|
const ConnectPg = (await import('connect-pg-simple')).default(session);
|
|
const a = express();
|
|
a.use(express.json());
|
|
a.use(session({
|
|
store: new ConnectPg({ pool, tableName: 'sessions' }),
|
|
secret: 'test', name: 'dragonflight.sid',
|
|
cookie: { httpOnly: true, sameSite: 'lax', secure: false, maxAge: 8 * 3600 * 1000 },
|
|
rolling: false, resave: false, saveUninitialized: false,
|
|
}));
|
|
a.use('/api/v1/auth', authRouter);
|
|
a.use('/api/v1/auth/users', requireAuth, usersRouter);
|
|
return new Promise(r => {
|
|
const srv = a.listen(0, '127.0.0.1', () => {
|
|
r({ baseUrl: 'http://127.0.0.1:' + srv.address().port, close: () => new Promise(rs => srv.close(rs)) });
|
|
});
|
|
});
|
|
}
|
|
|
|
async function login(baseUrl, username, password) {
|
|
const r = await fetch(baseUrl + '/api/v1/auth/login', {
|
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ username, password }),
|
|
});
|
|
assert.equal(r.status, 200, 'login failed: ' + JSON.stringify(await r.json()));
|
|
return (r.headers.get('set-cookie') || '').split(';')[0];
|
|
}
|
|
|
|
test('users: list + create + delete + admin reset password', { skip: !isTestDbConfigured() && 'TEST_DATABASE_URL not set' }, async () => {
|
|
const pool = await setupTestDb();
|
|
await pool.query(`INSERT INTO users (username, password_hash) VALUES ('admin', $1)`, [await hashPassword('admin-passphrase!!')]);
|
|
const { baseUrl, close } = await app(pool);
|
|
try {
|
|
const cookie = await login(baseUrl, 'admin', 'admin-passphrase!!');
|
|
|
|
// List
|
|
const list = await fetch(baseUrl + '/api/v1/auth/users', { headers: { cookie } });
|
|
assert.equal(list.status, 200);
|
|
const users0 = await list.json();
|
|
assert.ok(users0.find(u => u.username === 'admin'));
|
|
|
|
// Create
|
|
const created = await fetch(baseUrl + '/api/v1/auth/users', {
|
|
method: 'POST', headers: { 'Content-Type': 'application/json', cookie },
|
|
body: JSON.stringify({ username: 'bob', password: 'bob-passphrase!', display_name: 'Bob' }),
|
|
});
|
|
assert.equal(created.status, 201);
|
|
const bob = await created.json();
|
|
assert.equal(bob.username, 'bob');
|
|
|
|
// Admin reset password
|
|
const reset = await fetch(baseUrl + '/api/v1/auth/users/' + bob.id + '/password', {
|
|
method: 'POST', headers: { 'Content-Type': 'application/json', cookie },
|
|
body: JSON.stringify({ new_password: 'a-fresh-passphrase' }),
|
|
});
|
|
assert.equal(reset.status, 204);
|
|
|
|
// Delete
|
|
const del = await fetch(baseUrl + '/api/v1/auth/users/' + bob.id, {
|
|
method: 'DELETE', headers: { cookie },
|
|
});
|
|
assert.equal(del.status, 204);
|
|
} finally { await close(); await pool.end(); }
|
|
});
|
|
|
|
test('users: cannot delete the last real user', { skip: !isTestDbConfigured() && 'TEST_DATABASE_URL not set' }, async () => {
|
|
const pool = await setupTestDb();
|
|
await pool.query(`INSERT INTO users (id, username, password_hash) VALUES (uuid_generate_v4(), 'solo', $1)`, [await hashPassword('only-user-here-12')]);
|
|
const { baseUrl, close } = await app(pool);
|
|
try {
|
|
const cookie = await login(baseUrl, 'solo', 'only-user-here-12');
|
|
const me = await (await fetch(baseUrl + '/api/v1/auth/me', { headers: { cookie } })).json();
|
|
const r = await fetch(baseUrl + '/api/v1/auth/users/' + me.id, { method: 'DELETE', headers: { cookie } });
|
|
assert.equal(r.status, 409);
|
|
assert.equal((await r.json()).error, 'cannot delete last user');
|
|
} finally { await close(); await pool.end(); }
|
|
});
|