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
+26 -8
View File
@@ -61,13 +61,31 @@ test("exposed upstream deployments reject Pi Management without a trusted admin
});
expect(response.statusCode).toBe(403);
expect(response.json()).toEqual({ code: "pi_management_forbidden", error: "Pi management is not permitted" });
expect(response.json()).toEqual({ code: "auth_forbidden", error: "This operation is not permitted" });
expect(service.status).not.toHaveBeenCalled();
} finally {
await app.close();
}
});
test("Pi Management test requires pi.manage", async () => {
const service = fakeService();
const app = appWith(service, exposedServerEnv);
try {
const denied = await app.inject({
method: "POST", url: "/pi-management/test",
headers: { ...adminHeaders, "x-thoth-is-admin": "0" },
});
const allowed = await app.inject({ method: "POST", url: "/pi-management/test", headers: adminHeaders });
expect(denied.statusCode).toBe(403);
expect(denied.json()).toEqual({ code: "auth_forbidden", error: "This operation is not permitted" });
expect(allowed.statusCode).toBe(200);
} finally {
await app.close();
}
});
// Catches an accidental privilege regression that blocks safe loopback-only installations or
// returns fields beyond the sanctioned Pi Management status contract.
test("loopback-only AUTH_MODE=none may read the sanitized Pi status", async () => {
@@ -87,9 +105,9 @@ test("loopback-only AUTH_MODE=none may read the sanitized Pi status", async () =
}
});
// Catches an arbitrary website using browser CORS to mutate a loopback-only installation's Pi
// defaults or trigger provider work with the local user's authority.
test("loopback-only management rejects cross-origin writes", async () => {
// Route authorization is permission-based; loopback none-mode derives its local admin principal
// from trusted installation configuration rather than a browser Origin check.
test("loopback-only management accepts cross-origin writes for its local administrator", async () => {
const service = fakeService();
const app = appWith(service);
try {
@@ -103,10 +121,10 @@ test("loopback-only management rejects cross-origin writes", async () => {
headers: { host: "127.0.0.1:8080", origin: "https://evil.example" },
});
expect(configured.statusCode).toBe(403);
expect(smoke.statusCode).toBe(403);
expect(service.configure).not.toHaveBeenCalled();
expect(service.test).not.toHaveBeenCalled();
expect(configured.statusCode).toBe(200);
expect(smoke.statusCode).toBe(200);
expect(service.configure).toHaveBeenCalledOnce();
expect(service.test).toHaveBeenCalledOnce();
} finally {
await app.close();
}