feat(auth): centralize ThothII permission enforcement

This commit is contained in:
2026-08-16 17:52:03 +02:00
parent 23ac75ce1d
commit 2e0489ce22
20 changed files with 330 additions and 93 deletions
+40
View File
@@ -74,10 +74,12 @@ function appFor(
registry: RegistryFake,
diagnose = vi.fn(async () => ({ activatable: true, diagnostics: [] })),
secretStore = testSecretStore(),
env: Record<string, string> = {},
) {
return buildApp(loadConfig({
THT_HARNESS_DIR: "/missing-harness",
THT_WORKSPACE_REGISTRY_ROOT: "/tmp/thoth-route-test-registry",
...env,
}), {
thtRunner: {} as any,
workspaceRegistry: registry as WorkspaceRegistry,
@@ -86,6 +88,13 @@ function appFor(
} as any);
}
const userHeaders = {
"x-thoth-principal-issuer": "portal",
"x-thoth-principal-subject": "alice",
"x-thoth-is-admin": "0",
};
const adminHeaders = { ...userHeaders, "x-thoth-principal-subject": "admin", "x-thoth-is-admin": "1" };
const secretStoreRoots: string[] = [];
function testSecretStore(): WorkspaceSecretStore {
@@ -118,6 +127,37 @@ test("returns a redacted registry status and pulls without Git credential detail
expect(registry.pull).toHaveBeenCalledTimes(1);
});
test("workspace mutations and secret writes require their catalog permissions", async () => {
const registry = registryFake();
const app = appFor(registry, undefined, testSecretStore(), { AUTH_MODE: "upstream" });
try {
const deniedPull = await app.inject({ method: "POST", url: "/workspace-registry/pull", headers: userHeaders });
const allowedPull = await app.inject({ method: "POST", url: "/workspace-registry/pull", headers: adminHeaders });
const deniedBootstrap = await app.inject({ method: "GET", url: "/workspace-registry/status", headers: userHeaders });
const allowedBootstrap = await app.inject({ method: "GET", url: "/workspace-registry/status", headers: adminHeaders });
const deniedSecret = await app.inject({
method: "PUT", url: "/workspaces/psd-clinical/secrets", headers: userHeaders,
payload: { values: { "dwh.password": "secret" } },
});
const allowedSecret = await app.inject({
method: "PUT", url: "/workspaces/psd-clinical/secrets", headers: adminHeaders,
payload: { values: { "dwh.password": "secret" } },
});
expect(deniedPull.statusCode).toBe(403);
expect(deniedPull.json()).toEqual({ code: "auth_forbidden", error: "This operation is not permitted" });
expect(allowedPull.statusCode).toBe(200);
expect(deniedBootstrap.statusCode).toBe(403);
expect(deniedBootstrap.json()).toEqual({ code: "auth_forbidden", error: "This operation is not permitted" });
expect(allowedBootstrap.statusCode).toBe(200);
expect(deniedSecret.statusCode).toBe(403);
expect(deniedSecret.json()).toEqual({ code: "auth_forbidden", error: "This operation is not permitted" });
expect(allowedSecret.statusCode).toBe(200);
} finally {
await app.close();
}
});
test.each([
["POST", "/workspaces/publish"],
["GET", "/workspaces/psd-clinical/export"],