Files

91 lines
3.5 KiB
JavaScript
Raw Permalink Normal View History

// Admin account flow: approve → disable → enable (restore), plus guard rails.
import { mkdtempSync, rmSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { buildServer } from '../lib/web/server.js'
import { resolveConfig } from '../lib/config.js'
import { hashPassword } from '../lib/web/auth.js'
function assert(condition, message) {
if (!condition) throw new Error('ASSERT: ' + message)
}
const dataRoot = mkdtempSync(join(tmpdir(), 'dsh-smoke-admin-'))
const app = await buildServer(resolveConfig({ port: 0, dbPath: ':memory:', dataRoot }))
await app.listen({ port: 0 })
const base = `http://127.0.0.1:${app.server.address().port}`
await app.db.createUser({
id: 'admin',
username: 'admin',
passHash: await hashPassword('adminpass123'),
role: 'admin',
homeDir: join(dataRoot, 'users', 'admin', 'home'),
})
await app.db.createUser({
id: 'bob',
username: 'bob',
passHash: await hashPassword('bobpass123'),
role: 'pending',
homeDir: join(dataRoot, 'users', 'bob', 'home'),
})
async function json(path, { method = 'GET', body, cookie } = {}) {
const res = await fetch(base + path, {
method,
headers: {
...(body ? { 'content-type': 'application/json' } : {}),
...(cookie ? { cookie } : {}),
},
body: body ? JSON.stringify(body) : undefined,
})
const text = await res.text()
return { status: res.status, body: text ? JSON.parse(text) : null, setCookie: res.headers.get('set-cookie') }
}
try {
let r
// Pending user cannot log in.
r = await json('/api/auth/login', { method: 'POST', body: { username: 'bob', password: 'bobpass123' } })
assert(r.status === 403 && r.body.error === 'pending_review', 'pending user blocked from login')
// Admin login.
r = await json('/api/auth/login', { method: 'POST', body: { username: 'admin', password: 'adminpass123' } })
assert(r.status === 200, 'admin logs in')
const cookie = r.setCookie.split(';')[0]
// Approve bob.
r = await json('/api/admin/users/bob/approve', { method: 'POST', cookie })
assert(r.status === 200, 'approve succeeds')
// Bob can now log in.
r = await json('/api/auth/login', { method: 'POST', body: { username: 'bob', password: 'bobpass123' } })
assert(r.status === 200, 'approved user logs in')
// Disable bob.
r = await json('/api/admin/users/bob/disable', { method: 'POST', cookie })
assert(r.status === 200, 'disable succeeds')
// Bob blocked again (role disabled + sessions cleared).
r = await json('/api/auth/login', { method: 'POST', body: { username: 'bob', password: 'bobpass123' } })
assert(r.status === 403 && r.body.error === 'disabled', 'disabled user blocked from login')
// Enable (restore) bob.
r = await json('/api/admin/users/bob/enable', { method: 'POST', cookie })
assert(r.status === 200, 'enable succeeds')
// Bob can log in again.
r = await json('/api/auth/login', { method: 'POST', body: { username: 'bob', password: 'bobpass123' } })
assert(r.status === 200, 'restored user logs in')
// Guard rails.
r = await json('/api/admin/users/bob/enable', { method: 'POST', cookie })
assert(r.status === 409 && r.body.error === 'not_disabled', 'enable on non-disabled → 409')
r = await json('/api/admin/users/admin/disable', { method: 'POST', cookie })
assert(r.status === 409 && r.body.error === 'cannot_disable_admin', 'cannot disable admin')
console.log('OK: admin approve/disable/enable flow passed')
} finally {
await app.close()
rmSync(dataRoot, { recursive: true, force: true })
}