import { test, expect } from "vitest"; import Fastify from "fastify"; import { authPreHandler, getPrincipal } from "../src/auth/auth.js"; import { chmodSync, mkdtempSync, rmSync, statSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { expandLocalHome, localPrincipal } from "../src/auth/principal.js"; test("local mode resolves a stable local principal", async () => { const app = Fastify(); app.addHook("preHandler", authPreHandler("none")); app.get("/me", async (req) => getPrincipal(req)); expect((await app.inject({ method: "GET", url: "/me" })).json()).toMatchObject({ issuer: "local", subject: expect.any(String), isAdmin: false, }); }); test("mock mode makes a principal from the test header", async () => { const app = Fastify(); app.addHook("preHandler", authPreHandler("mock")); app.get("/me", async (req) => getPrincipal(req)); const res = await app.inject({ method: "GET", url: "/me", headers: { "x-mock-user": "alice" }, }); expect(res.json()).toEqual({ issuer: "mock", subject: "alice", displayName: "alice", isAdmin: false }); }); test("upstream mode accepts only normalized proxy principal headers", async () => { const app = Fastify(); app.addHook("preHandler", authPreHandler("upstream")); app.get("/me", async (req) => getPrincipal(req)); expect((await app.inject({ method: "GET", url: "/me" })).statusCode).toBe(401); const authenticated = await app.inject({ method: "GET", url: "/me", headers: { "x-thoth-principal-issuer": "portal", "x-thoth-principal-subject": "42", "x-thoth-principal-display-name": "Alice", "x-thoth-is-admin": "1", "x-authenticated-user": "must-not-be-used", }, }); expect(authenticated.json()).toEqual({ issuer: "portal", subject: "42", displayName: "Alice", isAdmin: true, }); }); test("local identity expands tilde homes and restores private POSIX permissions", () => { expect(expandLocalHome("~/thoth-test", "/home/tester")).toBe("/home/tester/thoth-test"); expect(expandLocalHome("~", "/home/tester")).toBe("/home/tester"); const home = mkdtempSync(join(tmpdir(), "thoth-principal-")); chmodSync(home, 0o755); const previous = process.env.THT_HOME; process.env.THT_HOME = home; try { localPrincipal(); if (process.platform !== "win32") { expect(statSync(home).mode & 0o777).toBe(0o700); expect(statSync(join(home, "identity.json")).mode & 0o777).toBe(0o600); } } finally { if (previous === undefined) delete process.env.THT_HOME; else process.env.THT_HOME = previous; rmSync(home, { recursive: true, force: true }); } });