Files
ThothII/backend/test/auth.test.ts
T

86 lines
3.1 KiB
TypeScript

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("upstream mode rejects legacy client identity headers without proxy principal fields", async () => {
const app = Fastify();
app.addHook("preHandler", authPreHandler("upstream"));
app.get("/me", async (req) => getPrincipal(req));
for (const headers of [
{ "x-authenticated-user": "mallory" },
{ "x-mock-user": "mallory" },
{ "x-authenticated-user": "mallory", "x-mock-user": "mallory" },
]) {
expect((await app.inject({ method: "GET", url: "/me", headers })).statusCode).toBe(401);
}
});
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 });
}
});