Fixed error message when deleting a certificate authority (#41635)

<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #41532

# Checklist for submitter

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.

## Testing

- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Improved error messaging when deleting a certificate authority that is
referenced by certificate templates. Users now receive a clear,
user-friendly message instead of a generic database error.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Victor Lyuboslavsky
2026-03-13 13:25:35 -05:00
committed by GitHub
parent 0a00d20969
commit fe1e4d295b
4 changed files with 35 additions and 1 deletions
+1
View File
@@ -0,0 +1 @@
* Fixed error message when deleting a certificate authority (that is referenced by a certificate template) to show a helpful message instead of a raw database error.
@@ -1,6 +1,7 @@
import React, { useContext, useState } from "react";
import { ICertificateAuthorityPartial } from "interfaces/certificates";
import { getErrorReason } from "interfaces/errors";
import certificatesAPI from "services/entities/certificates";
import { NotificationContext } from "context/notification";
@@ -33,9 +34,11 @@ const DeleteCertificateAuthorityModal = ({
onExit();
} catch (e) {
setIsUpdating(false);
const status = (e as { status?: number })?.status;
const reason = status === 409 ? getErrorReason(e) : "";
renderFlash(
"error",
"Couldn't delete certificate authority. Please try again."
reason || "Couldn't delete certificate authority. Please try again."
);
}
};
@@ -394,6 +394,11 @@ func (ds *Datastore) DeleteCertificateAuthority(ctx context.Context, certificate
stmt = "DELETE FROM certificate_authorities WHERE id = ?"
result, err := ds.writer(ctx).ExecContext(ctx, stmt, certificateAuthorityID)
if err != nil {
if isMySQLForeignKey(err) {
return nil, fleet.ConflictError{
Message: "Couldn't delete. This certificate authority is used in a certificate. Please remove the certificate first.",
}
}
return nil, ctxerr.Wrap(ctx, err, fmt.Sprintf("deleting certificate authority with id %d", certificateAuthorityID))
}
@@ -378,6 +378,31 @@ func testDeleteCertificateAuthority(t *testing.T, ds *Datastore) {
_, err = ds.DeleteCertificateAuthority(ctx, ca.ID)
require.Error(t, err)
require.Contains(t, err.Error(), "not found")
// Test that deleting a CA referenced by a certificate template returns a conflict error
ca, err = ds.NewCertificateAuthority(ctx, &fleet.CertificateAuthority{
Type: string(fleet.CATypeCustomSCEPProxy),
Name: ptr.String("Referenced CA"),
URL: ptr.String("https://localhost"),
})
require.NoError(t, err)
require.NotNil(t, ca)
team, err := ds.NewTeam(ctx, &fleet.Team{Name: "cert-test-team"})
require.NoError(t, err)
// Insert a certificate template that references the CA
_, err = ds.writer(ctx).ExecContext(ctx,
`INSERT INTO certificate_templates (team_id, certificate_authority_id, name, subject_name) VALUES (?, ?, ?, ?)`,
team.ID, ca.ID, "test-cert", "CN=test",
)
require.NoError(t, err)
_, err = ds.DeleteCertificateAuthority(ctx, ca.ID)
require.Error(t, err)
var conflictErr fleet.ConflictError
require.ErrorAs(t, err, &conflictErr)
require.Contains(t, conflictErr.Error(), "certificate authority is used in a certificate")
}
func testUpdateCertificateAuthorityByID(t *testing.T, ds *Datastore) {