From 2958acc682f0f79600de1ce34ea7f571011f5ba9 Mon Sep 17 00:00:00 2001 From: dantecatalfamo Date: Wed, 22 Apr 2026 16:20:09 -0400 Subject: [PATCH] Cherry-pick: Don't resend pending certificates (#43820) **Related issue:** Resolves #37556 --- server/service/certificate_templates_test.go | 36 ++++++++++++++++++++ server/service/certificates.go | 10 ++++++ 2 files changed, 46 insertions(+) diff --git a/server/service/certificate_templates_test.go b/server/service/certificate_templates_test.go index b8086230ad8..781f59371c4 100644 --- a/server/service/certificate_templates_test.go +++ b/server/service/certificate_templates_test.go @@ -412,6 +412,18 @@ func TestResendHostCertificateTemplate(t *testing.T) { }, nil } + ds.GetCertificateTemplateByIdForHostFunc = func(ctx context.Context, id uint, hostUUID string) (*fleet.CertificateTemplateResponseForHost, error) { + return &fleet.CertificateTemplateResponseForHost{ + CertificateTemplateResponse: fleet.CertificateTemplateResponse{ + CertificateTemplateResponseSummary: fleet.CertificateTemplateResponseSummary{ + ID: id, + Name: templateName, + }, + }, + Status: fleet.CertificateTemplateDelivered, + }, nil + } + t.Run("succeeds and creates activity", func(t *testing.T) { ds.ResendHostCertificateTemplateFunc = func(ctx context.Context, hID uint, tID uint) error { require.Equal(t, hostID, hID) @@ -455,4 +467,28 @@ func TestResendHostCertificateTemplate(t *testing.T) { require.Contains(t, err.Error(), "db error") require.False(t, opts.ActivityMock.NewActivityFuncInvoked) }) + + t.Run("returns 400 when template is pending for host", func(t *testing.T) { + ds.GetCertificateTemplateByIdForHostFunc = func(ctx context.Context, id uint, hostUUID string) (*fleet.CertificateTemplateResponseForHost, error) { + return &fleet.CertificateTemplateResponseForHost{ + CertificateTemplateResponse: fleet.CertificateTemplateResponse{ + CertificateTemplateResponseSummary: fleet.CertificateTemplateResponseSummary{ + ID: id, + Name: templateName, + }, + }, + Status: fleet.CertificateTemplatePending, + }, nil + } + ds.ResendHostCertificateTemplateFuncInvoked = false + + err := svc.ResendHostCertificateTemplate(ctx, hostID, templateID) + require.Error(t, err) + + var umErr interface{ StatusCode() int } + require.ErrorAs(t, err, &umErr) + require.Equal(t, 400, umErr.StatusCode()) + require.False(t, ds.ResendHostCertificateTemplateFuncInvoked) + require.False(t, opts.ActivityMock.NewActivityFuncInvoked) + }) } diff --git a/server/service/certificates.go b/server/service/certificates.go index 2c14e1d5164..bba6d7ee1a2 100644 --- a/server/service/certificates.go +++ b/server/service/certificates.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "net/http" "regexp" "strings" "time" @@ -780,6 +781,15 @@ func (svc *Service) ResendHostCertificateTemplate(ctx context.Context, hostID ui return ctxerr.Wrap(ctx, err) } + template, err := svc.ds.GetCertificateTemplateByIdForHost(ctx, templateID, host.UUID) + if err != nil { + return ctxerr.Wrap(ctx, err, "checking host certificate template") + } + + if template.Status == fleet.CertificateTemplatePending { + return fleet.NewUserMessageError(errors.New("Couldn't resend pending certificate template."), http.StatusBadRequest) + } + if err := svc.ds.ResendHostCertificateTemplate(ctx, hostID, templateID); err != nil { return ctxerr.Wrap(ctx, err, "resending certificate template") }