Fix cert sign not showing under certain instances (#6908)

This commit is contained in:
Anthony Stirling
2026-07-07 22:45:56 +01:00
parent 01a1ef8c44
commit 5fba2720f0
2 changed files with 40 additions and 6 deletions
@@ -59,7 +59,7 @@ async function mockHardwareEndpoints(page: Page) {
}
test.describe("CertSign tool - certificate source model", () => {
test("renders, accepts a PDF, and exposes the Upload source", 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,10 +76,21 @@ test.describe("CertSign tool - certificate source model", () => {
await uploadFiles(page, SAMPLE_PDF);
await expect(page).toHaveURL(/\/cert-sign/);
// Source step always offers "Upload" (the former "Manual" mode).
// 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.getByRole("button", { name: /^upload$/i }).first(),
).toBeAttached({ timeout: 10_000 });
page.getByRole("button", { name: /pkcs12/i }).first(),
).toBeVisible({ timeout: 10_000 });
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,
);
});
test("does NOT offer 'This device' when not running as desktop", async ({
@@ -89,9 +100,10 @@ test.describe("CertSign tool - certificate source model", () => {
await page.waitForLoadState("domcontentloaded");
await uploadFiles(page, SAMPLE_PDF);
// No alternative sources: the source step is hidden, and hardware is never offered.
await expect(
page.getByRole("button", { name: /^upload$/i }).first(),
).toBeAttached({ timeout: 10_000 });
page.getByRole("button", { name: /pkcs12/i }).first(),
).toBeVisible({ timeout: 10_000 });
await expect(
page.getByRole("button", { name: /this device/i }),
).toHaveCount(0);
@@ -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