fix(auth): harden unified diagnostic execution
This commit is contained in:
@@ -159,3 +159,19 @@ test.each([
|
||||
|
||||
await expect(validateWorkspace(workspace)).rejects.toThrow("invalid authentication diagnostics");
|
||||
});
|
||||
|
||||
test("rejects a workspace claimed activatable when authentication is not ready", async () => {
|
||||
server.use(http.post("/api/workspaces/validate", () => HttpResponse.json({
|
||||
workspace,
|
||||
contract: {},
|
||||
activatable: true,
|
||||
diagnostics: [],
|
||||
authentication: {
|
||||
ready: false,
|
||||
mode: "oidc",
|
||||
checks: [{ level: "error", code: "oidc_secret_missing", message: "Authentication is unavailable." }],
|
||||
},
|
||||
})));
|
||||
|
||||
await expect(validateWorkspace(workspace)).rejects.toThrow("invalid diagnostic result");
|
||||
});
|
||||
|
||||
@@ -291,7 +291,11 @@ function decodeWorkspaceDiagnostics(value: unknown): WorkspaceDiagnostics {
|
||||
...(diagnostic.field === undefined ? {} : { field: diagnostic.field }),
|
||||
};
|
||||
});
|
||||
return { activatable: source.activatable, diagnostics, authentication: decodeAuthentication(source.authentication) };
|
||||
const authentication = decodeAuthentication(source.authentication);
|
||||
if (source.activatable && !authentication.ready) {
|
||||
throw new Error("Workspace API returned an invalid diagnostic result");
|
||||
}
|
||||
return { activatable: source.activatable, diagnostics, authentication };
|
||||
}
|
||||
|
||||
function exactObject(value: unknown, keys: readonly string[]): Record<string, unknown> | undefined {
|
||||
|
||||
@@ -250,6 +250,33 @@ test("renders one authentication section with configured-group errors and no unm
|
||||
expect(within(section).queryByText(/unmapped/i)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("clears a previous authentication result as soon as validation is retried", async () => {
|
||||
const user = userEvent.setup();
|
||||
let calls = 0;
|
||||
let releaseRetry!: () => void;
|
||||
const retryStarted = new Promise<void>((resolve) => { releaseRetry = resolve; });
|
||||
server.use(http.post("/api/workspaces/validate", async () => {
|
||||
calls += 1;
|
||||
if (calls > 1) await retryStarted;
|
||||
return HttpResponse.json({
|
||||
workspace, contract: {}, activatable: true, diagnostics: [], authentication: readyAuthentication,
|
||||
});
|
||||
}));
|
||||
renderManager();
|
||||
await user.click(await screen.findByRole("button", { name: "PSD Clinical" }));
|
||||
const button = screen.getByRole("button", { name: "Validate workspace source" });
|
||||
|
||||
await user.click(button);
|
||||
expect(await screen.findByTestId("workspace-authentication")).toBeVisible();
|
||||
|
||||
await user.click(button);
|
||||
await waitFor(() => expect(calls).toBe(2));
|
||||
expect(screen.queryByTestId("workspace-authentication")).not.toBeInTheDocument();
|
||||
|
||||
releaseRetry();
|
||||
expect(await screen.findByTestId("workspace-authentication")).toBeVisible();
|
||||
});
|
||||
|
||||
test("never renders a hostile authentication field rejected by the API decoder", async () => {
|
||||
const user = userEvent.setup();
|
||||
const attacker = "attacker-field-SENTINEL";
|
||||
|
||||
@@ -194,6 +194,7 @@ export function WorkspaceManager({
|
||||
if (!guard) return;
|
||||
setBusyAction("validate");
|
||||
clearGlobalMessages();
|
||||
setAuthentication(undefined);
|
||||
setValidationNotice(undefined);
|
||||
setValidationDiagnostics([]);
|
||||
try {
|
||||
@@ -217,6 +218,7 @@ export function WorkspaceManager({
|
||||
if (!guard) return;
|
||||
setBusyAction("test");
|
||||
clearGlobalMessages();
|
||||
setAuthentication(undefined);
|
||||
setConnectionNotice(undefined);
|
||||
setConnectionDiagnostics([]);
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user