import { describe, expect, it, beforeAll, afterAll } from "vitest"; import { buildApp } from "../src/app.js"; import { loadConfig } from "../src/config.js"; import { hasPermission, permissionForRequest } from "../src/lib/permissions.js"; describe("permissions helpers", () => { it("hasPermission respects admin ⊃ write ⊃ read", () => { expect(hasPermission(["cfdm:domains:write"], "cfdm:domains:read")).toBe( true, ); expect(hasPermission(["cfdm:domains:admin"], "cfdm:domains:write")).toBe( true, ); expect(hasPermission(["cfdm:domains:read"], "cfdm:domains:write")).toBe( false, ); }); it("permissionForRequest maps domains and settings", () => { expect(permissionForRequest("GET", "/api/v1/domains")).toBe( "cfdm:domains:read", ); expect(permissionForRequest("POST", "/api/v1/domains")).toBe( "cfdm:domains:write", ); expect(permissionForRequest("GET", "/api/v1/settings")).toBe( "cfdm:settings:admin", ); expect(permissionForRequest("POST", "/api/v1/sync/foo")).toBe( "cfdm:domains:write", ); }); }); describe("auth plugin (AUTH_REQUIRED)", () => { const secret = "test-secret-at-least-8"; const issuer = "https://auth.shnt.top"; beforeAll(() => { process.env.AUTH_REQUIRED = "true"; process.env.AUTH_JWT_SECRET = secret; process.env.AUTH_ISSUER = issuer; process.env.AUTH_PORTAL_URL = "http://localhost:5175"; }); afterAll(() => { delete process.env.AUTH_REQUIRED; delete process.env.AUTH_JWT_SECRET; delete process.env.AUTH_ISSUER; delete process.env.AUTH_PORTAL_URL; }); it("GET /api/v1/auth/config exposes portal settings", async () => { const app = await buildApp({ config: { ...loadConfig(), authRequired: true, jwtSecret: secret, authIssuer: issuer, authPortalUrl: "http://localhost:5175", staticDir: null, }, memory: true, }); const res = await app.inject({ method: "GET", url: "/api/v1/auth/config" }); expect(res.statusCode).toBe(200); expect(res.json()).toMatchObject({ required: true, portal_url: "http://localhost:5175", }); await app.close(); }); it("401 without token; 403 without cfdm app; 200 with rights", async () => { const app = await buildApp({ config: { ...loadConfig(), authRequired: true, jwtSecret: secret, authIssuer: issuer, authPortalUrl: "http://localhost:5175", staticDir: null, }, memory: true, }); const noAuth = await app.inject({ method: "GET", url: "/api/v1/domains" }); expect(noAuth.statusCode).toBe(401); const tokenNoApp = app.jwt.sign( { sub: "u1", email: "a@b.c", name: "A", apps: ["vps"], permissions: ["cfdm:domains:read"], iss: issuer, }, { expiresIn: "1h" }, ); const forbiddenApp = await app.inject({ method: "GET", url: "/api/v1/domains", headers: { authorization: `Bearer ${tokenNoApp}` }, }); expect(forbiddenApp.statusCode).toBe(403); const okToken = app.jwt.sign( { sub: "u2", email: "r@b.c", name: "R", apps: ["cfdm"], permissions: ["cfdm:domains:read"], iss: issuer, }, { expiresIn: "1h" }, ); const okRead = await app.inject({ method: "GET", url: "/api/v1/domains", headers: { authorization: `Bearer ${okToken}` }, }); expect(okRead.statusCode).toBe(200); const denyWrite = await app.inject({ method: "POST", url: "/api/v1/domains", headers: { authorization: `Bearer ${okToken}` }, payload: { name: "x" }, }); expect(denyWrite.statusCode).toBe(403); const loginBlocked = await app.inject({ method: "POST", url: "/api/v1/auth/login", payload: { username: "admin", password: "admin" }, }); expect(loginBlocked.statusCode).toBe(403); await app.close(); }); });