import { afterEach, describe, expect, it } from 'vitest'; import { createServer } from '../src/app.js'; import { db } from '../src/db/knex.js'; import { truncateTables } from './truncate.js'; describe('admin route authorization', () => { /** @type {import('@hapi/hapi').Server | undefined} */ let server; afterEach(async () => { delete process.env.TRUST_PROXY; await server?.stop(); await truncateTables(); }); it('returns 401 for every admin route except login', async () => { server = await createServer(); const routes = server .table() .filter( (route) => route.path.startsWith('/api/admin/') && route.path !== '/api/admin/auth/login', ); expect(routes.length).toBeGreaterThan(10); for (const route of routes) { const response = await server.inject({ method: route.method, url: route.path.replace('{id}', '1'), payload: route.method === 'get' ? undefined : {}, }); expect(response.statusCode, `${route.method} ${route.path}`).toBe(401); } }); it('limits anonymous login attempts', async () => { server = await createServer(); for (let attempt = 0; attempt < 10; attempt += 1) { const response = await server.inject({ method: 'POST', url: '/api/admin/auth/login', payload: {}, }); expect(response.statusCode).toBe(400); } const blocked = await server.inject({ method: 'POST', url: '/api/admin/auth/login', payload: {}, }); expect(blocked.statusCode).toBe(429); }); it('uses the forwarded address when the proxy is trusted', async () => { process.env.TRUST_PROXY = 'true'; server = await createServer(); const response = await server.inject({ method: 'POST', url: '/api/admin/auth/login', headers: { 'x-forwarded-for': '203.0.113.10, 10.0.0.4' }, payload: { email: 'missing@example.com', password: 'wrong-password' }, }); expect(response.statusCode).toBe(401); const audit = await db('audit_log').where({ action: 'ADMIN_LOGIN_FAILED' }).first(); expect(audit.ip).toBe('203.0.113.10'); }); });