import { after, before, describe, it } from "node:test"; import assert from "node:assert/strict"; import { createTestDb, type TestDb } from "../helpers/sqlite.js"; import { createTestServer, type TestServer } from "../helpers/koa.js"; import { hashPassword } from "../../src/util/hash.js"; import type { ClientUser } from "../../src/db/types/User.js"; const PASSWORD = "user-visibility-password"; function basic(email: string): string { return `Basic ${Buffer.from(`${email}:${PASSWORD}`).toString("base64")}`; } describe("API user email visibility", () => { let testDb: TestDb; let server: TestServer; let admin: ClientUser; let normal: ClientUser; let coResident: ClientUser; let abodeAdmin: ClientUser; let aid: string; before(async () => { testDb = await createTestDb(); server = await createTestServer(testDb.db); const password = await hashPassword(PASSWORD); admin = await testDb.db.createUser({ email: "global-admin@test.example", name: "Global Admin", password, flags: { admin: true }, }); normal = await testDb.db.createUser({ email: "normal@test.example", name: "Normal", password, flags: {}, }); coResident = await testDb.db.createUser({ email: "co-resident@test.example", name: "Co-resident", password, flags: {}, }); abodeAdmin = await testDb.db.createUser({ email: "abode-admin@test.example", name: "Abode Admin", password, flags: {}, }); const abode = await testDb.db.createAbode( { name: "Shared abode" }, { uid: admin.uid }, ); aid = abode.aid; for (const user of [normal, coResident]) { await testDb.db.createResident( { uid: user.uid, aid, flags: {} }, { uid: admin.uid }, ); } await testDb.db.createResident( { uid: abodeAdmin.uid, aid, flags: { admin: true } }, { uid: admin.uid }, ); }); after(async () => { await server.close(); testDb.close(); }); it("shows a normal caller only their own email in GET /users", async () => { const response = await fetch(`${server.url}/users`, { headers: { Authorization: basic(normal.email) }, }); assert.equal(response.status, 200); const users = (await response.json()) as ClientUser[]; assert.equal( users.find((user) => user.uid === normal.uid)?.email, normal.email, ); assert.ok(!("email" in users.find((user) => user.uid === coResident.uid)!)); }); it("hides another user's email in GET /users/:uid", async () => { const response = await fetch(`${server.url}/users/${coResident.uid}`, { headers: { Authorization: basic(normal.email) }, }); assert.equal(response.status, 200); assert.ok(!("email" in ((await response.json()) as object))); }); it("allows global admins to see user emails", async () => { const response = await fetch(`${server.url}/users`, { headers: { Authorization: basic(admin.email) }, }); assert.equal(response.status, 200); const users = (await response.json()) as ClientUser[]; assert.equal( users.find((user) => user.uid === coResident.uid)?.email, coResident.email, ); }); it("makes lookup by email global-admin-only", async () => { const denied = await fetch( `${server.url}/users/by-email?email=${encodeURIComponent(coResident.email)}`, { headers: { Authorization: basic(normal.email) } }, ); assert.equal(denied.status, 401); const allowed = await fetch( `${server.url}/users/by-email?email=${encodeURIComponent(coResident.email)}`, { headers: { Authorization: basic(admin.email) } }, ); assert.equal(allowed.status, 200); assert.equal( ((await allowed.json()) as ClientUser).email, coResident.email, ); }); it("shows co-resident emails to an abode admin", async () => { const response = await fetch(`${server.url}/abodes/${aid}/users`, { headers: { Authorization: basic(abodeAdmin.email) }, }); assert.equal(response.status, 200); const users = (await response.json()) as ClientUser[]; assert.equal( users.find((user) => user.uid === coResident.uid)?.email, coResident.email, ); }); it("hides co-resident emails from a non-admin resident", async () => { const response = await fetch(`${server.url}/abodes/${aid}/users`, { headers: { Authorization: basic(normal.email) }, }); assert.equal(response.status, 200); const users = (await response.json()) as ClientUser[]; assert.equal( users.find((user) => user.uid === normal.uid)?.email, normal.email, ); assert.ok(!("email" in users.find((user) => user.uid === coResident.uid)!)); }); });