/** * Desktop mode: one-shot desktop-login, Bearer-token shutdown, desktopMode in /api/me, * the desktop-session password change without oldPassword, and the single-user guard * closing the user-management and Project-member surfaces. */ import { describe, expect, it } from "vitest"; import { apiClient, createTestApp, loginAdmin } from "./helpers.js"; import type { ErrorBody, MeResponse } from "../src/api/types.js"; const TOKEN = "test-desktop-token"; function desktopApp() { return createTestApp({ config: { desktopToken: TOKEN } }); } describe("desktop-login", () => { it("redeems the token once: cookie session, redirect to /, second attempt 401", async () => { const t = await desktopApp(); try { const res = await t.app.request(`/api/auth/desktop-login?token=${TOKEN}`); expect(res.status).toBe(302); expect(res.headers.get("location")).toBe("/"); const cookie = res.headers.get("set-cookie"); expect(cookie).toContain("penguin_session="); const me = await t.app.request("/api/me", { headers: { cookie: cookie!.split(";")[0]! }, }); expect(me.status).toBe(200); const body = (await me.json()) as MeResponse; expect(body.user.userId).toBe("admin"); expect(body.desktopMode).toBe(true); const replay = await t.app.request(`/api/auth/desktop-login?token=${TOKEN}`); expect(replay.status).toBe(401); } finally { await t.cleanup(); } }); it("rejects a wrong or missing token without consuming the real one", async () => { const t = await desktopApp(); try { expect((await t.app.request("/api/auth/desktop-login?token=wrong")).status).toBe(401); expect((await t.app.request("/api/auth/desktop-login")).status).toBe(401); // The real token still works after failed attempts. expect((await t.app.request(`/api/auth/desktop-login?token=${TOKEN}`)).status).toBe(302); } finally { await t.cleanup(); } }); it("is 404 outside desktop mode, and /api/me reports desktopMode false", async () => { const t = await createTestApp(); try { expect((await t.app.request("/api/auth/desktop-login?token=x")).status).toBe(404); const admin = await loginAdmin(t.app); const me = await apiClient(t.app, admin.cookie).get("/api/me"); expect(((await me.json()) as MeResponse).desktopMode).toBe(false); } finally { await t.cleanup(); } }); }); describe("desktop shutdown endpoint", () => { it("accepts the Bearer token repeatedly and triggers the registered handler", async () => { const t = await desktopApp(); try { let requested = 0; t.deps.desktop!.onShutdownRequest(() => { requested += 1; }); const res = await t.app.request("/api/desktop/shutdown", { method: "POST", headers: { authorization: `Bearer ${TOKEN}` }, }); expect(res.status).toBe(202); // The route defers the trigger so the 202 can flush first. await new Promise((r) => setTimeout(r, 80)); expect(requested).toBe(1); // Unlike the login token, the shutdown credential is NOT one-shot. const again = await t.app.request("/api/desktop/shutdown", { method: "POST", headers: { authorization: `Bearer ${TOKEN}` }, }); expect(again.status).toBe(202); } finally { await t.cleanup(); } }); it("rejects wrong or missing tokens, and does not exist outside desktop mode", async () => { const t = await desktopApp(); try { const wrong = await t.app.request("/api/desktop/shutdown", { method: "POST", headers: { authorization: "Bearer nope" }, }); expect(wrong.status).toBe(401); const missing = await t.app.request("/api/desktop/shutdown", { method: "POST" }); expect(missing.status).toBe(401); } finally { await t.cleanup(); } const plain = await createTestApp(); try { // Outside desktop mode the route is not mounted; the request falls through to the // cookie auth middleware, which rejects the cookieless caller with 401. const res = await plain.app.request("/api/desktop/shutdown", { method: "POST", headers: { authorization: `Bearer ${TOKEN}` }, }); expect(res.status).toBe(401); } finally { await plain.cleanup(); } }); }); describe("desktop single-user mode", () => { async function expectSingleUser403(res: Response): Promise { expect(res.status).toBe(403); const body = (await res.json()) as ErrorBody; expect(body.error.code).toBe("desktop_single_user"); } it("rejects the whole admin-users surface with desktop_single_user", async () => { const t = await desktopApp(); try { // The seeded admin signed in through the regular login form: even a fully // authorized admin session gets the dedicated 403, not admin_required. const admin = await loginAdmin(t.app); const api = apiClient(t.app, admin.cookie); await expectSingleUser403(await api.get("/api/admin/users")); await expectSingleUser403( await api.post("/api/admin/users", { userId: "eve", password: "password-123" }), ); await expectSingleUser403( await api.post("/api/admin/users/admin/password", { password: "password-456" }), ); await expectSingleUser403( await t.app.request("/api/admin/users/eve", { method: "DELETE", headers: { cookie: admin.cookie }, }), ); // No user was created by the rejected POST. expect(t.deps.db.prepare("SELECT COUNT(*) AS n FROM users").get()?.n).toBe(1); } finally { await t.cleanup(); } }); it("rejects Project member management (reads and writes) with desktop_single_user", async () => { const t = await desktopApp(); try { const admin = await loginAdmin(t.app); const api = apiClient(t.app, admin.cookie); await expectSingleUser403(await api.get("/api/projects/default_project/members")); await expectSingleUser403( await api.post("/api/projects/default_project/members", { userId: "eve" }), ); await expectSingleUser403( await t.app.request("/api/projects/default_project/members/eve", { method: "DELETE", headers: { cookie: admin.cookie }, }), ); } finally { await t.cleanup(); } }); it("leaves both surfaces working on a normal multi-user server", async () => { const t = await createTestApp(); try { const admin = await loginAdmin(t.app); const api = apiClient(t.app, admin.cookie); const users = await api.get("/api/admin/users"); expect(users.status).toBe(200); const members = await api.get("/api/projects/default_project/members"); expect(members.status).toBe(200); } finally { await t.cleanup(); } }); }); describe("desktop-session password change", () => { async function desktopCookie(t: Awaited>): Promise { const res = await t.app.request(`/api/auth/desktop-login?token=${TOKEN}`); return res.headers.get("set-cookie")!.split(";")[0]!; } it("allows omitting oldPassword for a desktop session and clears the initial flag", async () => { const t = await desktopApp(); try { const cookie = await desktopCookie(t); const res = await apiClient(t.app, cookie).put("/api/me/password", { newPassword: "brand-new-password", }); expect(res.status).toBe(204); const me = (await (await apiClient(t.app, cookie).get("/api/me")).json()) as MeResponse; expect(me.user.passwordIsInitial).toBe(false); } finally { await t.cleanup(); } }); it("still validates oldPassword when it is provided by a desktop session", async () => { const t = await desktopApp(); try { const cookie = await desktopCookie(t); const res = await apiClient(t.app, cookie).put("/api/me/password", { oldPassword: "wrong-password", newPassword: "brand-new-password", }); expect(res.status).toBe(400); } finally { await t.cleanup(); } }); it("keeps requiring oldPassword for password-established sessions in desktop mode", async () => { const t = await desktopApp(); try { // Sign in via the regular login form against the same desktop-mode server. const admin = await loginAdmin(t.app); const res = await apiClient(t.app, admin.cookie).put("/api/me/password", { newPassword: "brand-new-password", }); expect(res.status).toBe(400); } finally { await t.cleanup(); } }); });