feat(auth): define strict installation authentication config
This commit is contained in:
@@ -1,6 +1,29 @@
|
||||
import { expect, test } from "vitest";
|
||||
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { stringify } from "yaml";
|
||||
import { loadConfig } from "../src/config.js";
|
||||
|
||||
function authFile(value: unknown): { directory: string; file: string } {
|
||||
const directory = mkdtempSync(join(tmpdir(), "thothii-app-auth-config-"));
|
||||
const file = join(directory, "auth.yaml");
|
||||
writeFileSync(file, stringify(value), "utf8");
|
||||
return { directory, file };
|
||||
}
|
||||
|
||||
function oidcAuthConfig(): Record<string, unknown> {
|
||||
return {
|
||||
version: 1, mode: "oidc", publicUrl: "https://thothii.example.org",
|
||||
oidc: {
|
||||
issuer: "https://authentik.example.org/application/o/thothii/", clientId: "thothii",
|
||||
clientSecretRef: "THT_OIDC_CLIENT_SECRET", scopes: ["openid"], groupsClaim: "groups",
|
||||
},
|
||||
groupCatalog: { driver: "authentik", baseUrl: "https://authentik.example.org", apiTokenRef: "THT_AUTHENTIK_API_TOKEN" },
|
||||
authorization: { groupRoles: { "TOT Users": ["user"], "TOT Admin": ["admin"] } },
|
||||
};
|
||||
}
|
||||
|
||||
test("loadConfig accepts container listening and runtime paths", () => {
|
||||
expect(loadConfig({
|
||||
HOST: "0.0.0.0",
|
||||
@@ -41,10 +64,53 @@ test("loadConfig keeps local development defaults", () => {
|
||||
internalEmbeddingUrl: "http://embedding:11434",
|
||||
internalEmbeddingModel: "qwen3-embedding:0.6b",
|
||||
internalEmbeddingDimensions: 1024,
|
||||
authMode: "none",
|
||||
authConfigFile: "/run/thothii-auth/auth.yaml",
|
||||
authStateRoot: "/data/auth",
|
||||
});
|
||||
expect(loadConfig({}).dataRoot).toBeUndefined();
|
||||
});
|
||||
|
||||
test("loadConfig makes an existing auth.yaml authoritative and rejects AUTH_MODE split-brain", () => {
|
||||
const { directory, file } = authFile(oidcAuthConfig());
|
||||
try {
|
||||
const config = loadConfig({ THT_AUTH_CONFIG_FILE: file, THT_AUTH_STATE_ROOT: "/state/auth" });
|
||||
expect(config.authMode).toBe("oidc");
|
||||
expect(config.authStateRoot).toBe("/state/auth");
|
||||
expect(config.authentication?.current().sourcePath).toBe(file);
|
||||
expect(() => loadConfig({ THT_AUTH_CONFIG_FILE: file, AUTH_MODE: "upstream" }))
|
||||
.toThrow("authentication configuration and AUTH_MODE cannot both be set");
|
||||
} finally {
|
||||
rmSync(directory, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test("loadConfig rejects an auth config path that exists but is not a regular file", () => {
|
||||
const directory = mkdtempSync(join(tmpdir(), "thothii-app-auth-config-directory-"));
|
||||
try {
|
||||
expect(() => loadConfig({ THT_AUTH_CONFIG_FILE: directory }))
|
||||
.toThrow("authentication configuration is invalid");
|
||||
} finally {
|
||||
rmSync(directory, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test("public exposure accepts configured OIDC and the upstream migration mode only", () => {
|
||||
const { directory, file } = authFile(oidcAuthConfig());
|
||||
try {
|
||||
expect(loadConfig({
|
||||
THOTH_PUBLIC_EXPOSURE: "true", THT_AUTH_CONFIG_FILE: file, THT_SESSION_STORAGE: "postgres",
|
||||
THT_SESSION_DB_HOST: "db.internal", THT_SESSION_DB_NAME: "thoth", THT_SESSION_RUNTIME_USER: "thoth_sessions_app",
|
||||
THT_SESSION_RUNTIME_PASSWORD_FILE: "/run/secrets/session_runtime_password", THT_SESSION_DB_SSLMODE: "verify-full",
|
||||
THT_SESSION_DB_SSLROOTCERT: "/run/secrets/session_ca.pem",
|
||||
}).authMode).toBe("oidc");
|
||||
expect(() => loadConfig({ THOTH_PUBLIC_EXPOSURE: "true", AUTH_MODE: "mock" }))
|
||||
.toThrow("public exposure requires AUTH_MODE=upstream or configured OIDC");
|
||||
} finally {
|
||||
rmSync(directory, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test("loadConfig accepts only the allowed internal semantic runtime hosts", () => {
|
||||
expect(loadConfig({
|
||||
THT_INTERNAL_QDRANT_URL: "http://localhost:6333",
|
||||
|
||||
Reference in New Issue
Block a user