import { randomUUID } from "node:crypto"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; import request from "supertest"; import type { FastifyInstance } from "fastify"; import { PrismaClient } from "@prisma/client"; import argon2 from "argon2"; import { buildApp } from "../src/server"; const prisma = new PrismaClient(); let authEnabledApp: FastifyInstance; let authDisabledApp: FastifyInstance; function readCookieValue(setCookie: string[] | undefined, cookieName: string): string | null { if (!setCookie) return null; for (const raw of setCookie) { const firstPart = raw.split(";")[0] ?? ""; const [name, value] = firstPart.split("="); if (name?.trim() === cookieName && value) return value.trim(); } return null; } beforeAll(async () => { authEnabledApp = await buildApp({ AUTH_DISABLED: false, SEED_DEFAULT_BUDGET: false }); await authEnabledApp.ready(); authDisabledApp = await buildApp({ NODE_ENV: "production", AUTH_DISABLED: true, ALLOW_INSECURE_AUTH_FOR_DEV: true, SEED_DEFAULT_BUDGET: false, CORS_ORIGINS: "https://allowed.example.com", APP_ORIGIN: "https://allowed.example.com", }); await authDisabledApp.ready(); }); afterAll(async () => { if (authEnabledApp) await authEnabledApp.close(); if (authDisabledApp) await authDisabledApp.close(); await prisma.$disconnect(); }); describe("/admin/rollover access control", () => { it("returns 401 without a valid authenticated session when AUTH_DISABLED=false", async () => { const res = await request(authEnabledApp.server) .post("/admin/rollover") .send({ dryRun: true }); expect(res.status).toBe(401); expect(res.body.code).toBe("UNAUTHENTICATED"); }); it("returns 403 for authenticated users when AUTH_DISABLED=false", async () => { const agent = request.agent(authEnabledApp.server); const email = `rollover-auth-${Date.now()}@test.dev`; const password = "SupersAFE123!"; await prisma.user.create({ data: { email, passwordHash: await argon2.hash(password), emailVerified: true, }, }); try { const login = await agent.post("/auth/login").send({ email, password }); expect(login.status).toBe(200); const csrf = readCookieValue(login.headers["set-cookie"], "csrf"); expect(csrf).toBeTruthy(); const res = await agent .post("/admin/rollover") .set("x-csrf-token", csrf as string) .send({ dryRun: true }); expect(res.status).toBe(403); expect(res.body.ok).toBe(false); } finally { await prisma.user.deleteMany({ where: { email } }); } }); it("returns 403 for non-internal client IP when AUTH_DISABLED=true", async () => { const csrf = randomUUID().replace(/-/g, ""); const res = await request(authDisabledApp.server) .post("/admin/rollover") .set("x-user-id", `external-${Date.now()}`) .set("x-csrf-token", csrf) .set("Cookie", `csrf=${csrf}`) .set("x-forwarded-for", "8.8.8.8") .send({ dryRun: true }); expect(res.status).toBe(403); expect(res.body.ok).toBe(false); }); it("allows internal client IP when AUTH_DISABLED=true", async () => { const csrf = randomUUID().replace(/-/g, ""); const res = await request(authDisabledApp.server) .post("/admin/rollover") .set("x-user-id", `internal-${Date.now()}`) .set("x-csrf-token", csrf) .set("Cookie", `csrf=${csrf}`) .set("x-forwarded-for", "10.23.45.67") .send({ dryRun: true }); expect(res.status).toBe(200); expect(res.body.ok).toBe(true); expect(res.body.dryRun).toBe(true); expect(typeof res.body.processed).toBe("number"); }); });