fix(workspace): isolate maintenance authentication surface
This commit is contained in:
@@ -176,7 +176,11 @@ function positiveDimension(value: string | undefined, fallback: number): number
|
||||
return parsed;
|
||||
}
|
||||
|
||||
export function loadConfig(env: Record<string, string | undefined>): AppConfig {
|
||||
export function loadConfig(
|
||||
env: Record<string, string | undefined>,
|
||||
options: { surface?: "application" | "workspace-maintenance" } = {},
|
||||
): AppConfig {
|
||||
const applicationSurface = options.surface !== "workspace-maintenance";
|
||||
const defaultAuthConfigFile = "/run/thothii-auth/auth.yaml";
|
||||
const authConfigFile = absoluteAuthPath(env.THT_AUTH_CONFIG_FILE ?? defaultAuthConfigFile, "file");
|
||||
const authStateRoot = absoluteAuthPath(env.THT_AUTH_STATE_ROOT ?? "/data/auth", "state root");
|
||||
@@ -211,13 +215,13 @@ export function loadConfig(env: Record<string, string | undefined>): AppConfig {
|
||||
throw new Error(`unsupported AUTH_MODE=${requestedMode}; use none, mock, or upstream`);
|
||||
}
|
||||
const nodeEnvironment = env.NODE_ENV ?? process.env.NODE_ENV;
|
||||
if ((requestedMode === "none" || requestedMode === "mock")
|
||||
if (applicationSurface && (requestedMode === "none" || requestedMode === "mock")
|
||||
&& nodeEnvironment !== "development" && nodeEnvironment !== "test") {
|
||||
throw new Error("production requires auth.yaml or AUTH_MODE=upstream");
|
||||
}
|
||||
authMode = requestedMode as "none" | "mock" | "upstream";
|
||||
}
|
||||
const publicExposure = env.THOTH_PUBLIC_EXPOSURE === "true";
|
||||
const publicExposure = applicationSurface && env.THOTH_PUBLIC_EXPOSURE === "true";
|
||||
if (publicExposure && authMode !== "oidc" && authMode !== "upstream") {
|
||||
throw new Error("public exposure requires AUTH_MODE=upstream or configured OIDC behind a trusted proxy");
|
||||
}
|
||||
|
||||
@@ -212,7 +212,7 @@ async function readSessionInventory(dataRoot: string, workspaceId: string): Prom
|
||||
}
|
||||
|
||||
function createProductionService(): WorkspacePreprocessingService {
|
||||
const config = loadConfig(process.env);
|
||||
const config = loadConfig(process.env, { surface: "workspace-maintenance" });
|
||||
const registry = new WorkspaceRegistry(config.workspaceRegistry);
|
||||
const workspaceSecretStore = new WorkspaceSecretStore({
|
||||
root: config.workspaceSecretStoreRoot,
|
||||
|
||||
@@ -97,6 +97,17 @@ test("loadConfig allows none and mock only outside production when auth.yaml is
|
||||
.toThrow("production requires auth.yaml or AUTH_MODE=upstream");
|
||||
});
|
||||
|
||||
test("workspace maintenance loads production configuration without an authentication surface", () => {
|
||||
expect(loadConfig(
|
||||
{ NODE_ENV: "production", THOTH_PUBLIC_EXPOSURE: "true" },
|
||||
{ surface: "workspace-maintenance" },
|
||||
)).toMatchObject({
|
||||
authMode: "none",
|
||||
authentication: undefined,
|
||||
publicExposure: false,
|
||||
});
|
||||
});
|
||||
|
||||
test("local Compose profiles explicitly select the development auth environment", () => {
|
||||
for (const profile of ["../../deploy/compose.local.yaml", "../../docker-compose.dev.yml"]) {
|
||||
expect(readFileSync(new URL(profile, import.meta.url), "utf8")).toMatch(/NODE_ENV:\s*development/);
|
||||
|
||||
Reference in New Issue
Block a user