From f703a678174180ec629ac2107ba956b14d943194 Mon Sep 17 00:00:00 2001 From: Anthony Stirling <77850077+Frooodle@users.noreply.github.com> Date: Tue, 7 Jul 2026 22:39:57 +0100 Subject: [PATCH] Fix cert sign not showing under certain instances (#6908) --- .../tests/stubbed/cert-sign-wizard.spec.ts | 21 ++++++++++++------ frontend/editor/src/core/tools/CertSign.tsx | 22 +++++++++++++++++++ 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/frontend/editor/src/core/tests/stubbed/cert-sign-wizard.spec.ts b/frontend/editor/src/core/tests/stubbed/cert-sign-wizard.spec.ts index 9b66fec1d9..e2d2e8b154 100644 --- a/frontend/editor/src/core/tests/stubbed/cert-sign-wizard.spec.ts +++ b/frontend/editor/src/core/tests/stubbed/cert-sign-wizard.spec.ts @@ -59,7 +59,7 @@ async function mockHardwareEndpoints(page: Page) { } test.describe("CertSign tool - certificate source model", () => { - test("renders, accepts a PDF, and defaults to upload when no other sources exist", async ({ + test("skips the redundant source step and goes straight to certificate format when Upload is the only source", async ({ page, }) => { await page.route("**/api/v1/security/cert-sign", (route) => @@ -76,12 +76,19 @@ test.describe("CertSign tool - certificate source model", () => { await uploadFiles(page, SAMPLE_PDF); await expect(page).toHaveURL(/\/cert-sign/); - // With no server/hardware sources, the picker collapses to a hint and the - // flow proceeds in the default MANUAL (upload) mode — no lone Upload CTA. + // With no server cert or hardware token there is nothing to choose, so the + // whole "Certificate source" step is hidden and the format picker shows directly. await expect( - page.getByText(/no other certificate sources are available/i).first(), + page.getByRole("button", { name: /pkcs12/i }).first(), ).toBeVisible({ timeout: 10_000 }); - await expect(page.getByRole("button", { name: /^upload$/i })).toHaveCount( + await expect(page.getByText(/certificate source/i)).toHaveCount(0); + await expect( + page.getByText(/no other certificate sources are available/i), + ).toHaveCount(0); + await expect( + page.getByRole("button", { name: /this device/i }), + ).toHaveCount(0); + await expect(page.getByRole("button", { name: /^server$/i })).toHaveCount( 0, ); }); @@ -93,9 +100,9 @@ test.describe("CertSign tool - certificate source model", () => { await page.waitForLoadState("domcontentloaded"); await uploadFiles(page, SAMPLE_PDF); - // No alternative sources: the picker is a hint, and hardware is never offered. + // No alternative sources: the source step is hidden, and hardware is never offered. await expect( - page.getByText(/no other certificate sources are available/i).first(), + page.getByRole("button", { name: /pkcs12/i }).first(), ).toBeVisible({ timeout: 10_000 }); await expect( page.getByRole("button", { name: /this device/i }), diff --git a/frontend/editor/src/core/tools/CertSign.tsx b/frontend/editor/src/core/tools/CertSign.tsx index dd0668a491..4f5173b174 100644 --- a/frontend/editor/src/core/tools/CertSign.tsx +++ b/frontend/editor/src/core/tools/CertSign.tsx @@ -1,5 +1,7 @@ +import { useEffect } from "react"; import { useTranslation } from "react-i18next"; import { createToolFlow } from "@app/components/tools/shared/createToolFlow"; +import { useAppConfig } from "@app/contexts/AppConfigContext"; import CertificateTypeSettings from "@app/components/tools/certSign/CertificateTypeSettings"; import CertificateFormatSettings from "@app/components/tools/certSign/CertificateFormatSettings"; import CertificateFilesSettings from "@app/components/tools/certSign/CertificateFilesSettings"; @@ -23,6 +25,25 @@ const CertSign = (props: BaseToolProps) => { props, ); + const { config } = useAppConfig(); + // "Upload" is always available; the source chooser is only meaningful when a + // server certificate or a hardware token gives the user an actual alternative. + const hasCertSourceChoice = + (config?.serverCertificateEnabled ?? false) || + (config?.hardwareSigningAvailable ?? false); + + // With Upload as the only source, keep signMode on MANUAL even if a saved + // automation set AUTO/DEVICE, so the hidden source step can't strand the flow. + useEffect(() => { + if (!hasCertSourceChoice && base.params.parameters.signMode !== "MANUAL") { + base.params.updateParameter("signMode", "MANUAL"); + } + }, [ + hasCertSourceChoice, + base.params.parameters.signMode, + base.params.updateParameter, + ]); + const certTypeTips = useCertificateTypeTips(); const appearanceTips = useSignatureAppearanceTips(); const signModeTips = useSignModeTips(); @@ -63,6 +84,7 @@ const CertSign = (props: BaseToolProps) => { steps: [ { title: t("certSign.source.stepTitle", "Certificate source"), + isVisible: hasCertSourceChoice, isCollapsed: base.settingsCollapsed, onCollapsedClick: base.settingsCollapsed ? base.handleSettingsReset