Fix: filter out verified remove android certs in host details (#37539)

This commit is contained in:
Tim Lee
2025-12-19 16:35:58 -07:00
committed by GitHub
parent c1d13ed858
commit f97284cf13
5 changed files with 90 additions and 0 deletions
@@ -206,6 +206,18 @@ func (ds *Datastore) DeleteHostCertificateTemplates(ctx context.Context, hostCer
return nil
}
// DeleteHostCertificateTemplate deletes a single host_certificate_template record
// identified by host_uuid and certificate_template_id.
func (ds *Datastore) DeleteHostCertificateTemplate(ctx context.Context, hostUUID string, certificateTemplateID uint) error {
const stmt = `DELETE FROM host_certificate_templates WHERE host_uuid = ? AND certificate_template_id = ?`
if _, err := ds.writer(ctx).ExecContext(ctx, stmt, hostUUID, certificateTemplateID); err != nil {
return ctxerr.Wrap(ctx, err, "delete host_certificate_template")
}
return nil
}
func (ds *Datastore) UpsertCertificateStatus(
ctx context.Context,
hostUUID string,
@@ -24,6 +24,7 @@ func TestHostCertificateTemplates(t *testing.T) {
{"ListCertificateTemplatesForHosts", testListCertificateTemplatesForHosts},
{"GetCertificateTemplateForHostNoTeam", testGetCertificateTemplateForHostNoTeam},
{"BulkInsertAndDeleteHostCertificateTemplates", testBulkInsertAndDeleteHostCertificateTemplates},
{"DeleteHostCertificateTemplate", testDeleteHostCertificateTemplate},
{"UpsertHostCertificateTemplateStatus", testUpsertHostCertificateTemplateStatus},
{"CreatePendingCertificateTemplatesForExistingHosts", testCreatePendingCertificateTemplatesForExistingHosts},
{"CreatePendingCertificateTemplatesForNewHost", testCreatePendingCertificateTemplatesForNewHost},
@@ -475,6 +476,63 @@ func testBulkInsertAndDeleteHostCertificateTemplates(t *testing.T, ds *Datastore
})
}
func testDeleteHostCertificateTemplate(t *testing.T, ds *Datastore) {
ctx := t.Context()
t.Run("no error when deleting non-existent record", func(t *testing.T) {
defer TruncateTables(t, ds)
err := ds.DeleteHostCertificateTemplate(ctx, "non-existent-host", 999)
require.NoError(t, err)
})
t.Run("only deletes specified record", func(t *testing.T) {
defer TruncateTables(t, ds)
setup := createCertTemplateTestSetup(t, ctx, ds, "")
templateTwo, err := ds.CreateCertificateTemplate(ctx, &fleet.CertificateTemplate{
Name: "Cert2",
TeamID: setup.team.ID,
CertificateAuthorityID: setup.ca.ID,
SubjectName: "CN=Test Subject 2",
})
require.NoError(t, err)
// Insert multiple host certificate template records
hostCerts := []fleet.HostCertificateTemplate{
{HostUUID: "host-1", CertificateTemplateID: setup.template.ID, FleetChallenge: ptr.String("challenge-1"), Status: fleet.CertificateTemplateVerified, OperationType: fleet.MDMOperationTypeRemove, Name: setup.template.Name},
{HostUUID: "host-1", CertificateTemplateID: templateTwo.ID, FleetChallenge: ptr.String("challenge-2"), Status: fleet.CertificateTemplateVerified, OperationType: fleet.MDMOperationTypeInstall, Name: templateTwo.Name},
{HostUUID: "host-2", CertificateTemplateID: setup.template.ID, FleetChallenge: ptr.String("challenge-3"), Status: fleet.CertificateTemplateVerified, OperationType: fleet.MDMOperationTypeRemove, Name: setup.template.Name},
}
err = ds.BulkInsertHostCertificateTemplates(ctx, hostCerts)
require.NoError(t, err)
// Delete only host-1's first certificate
err = ds.DeleteHostCertificateTemplate(ctx, "host-1", setup.template.ID)
require.NoError(t, err)
// Verify only 2 records remain
var count int
err = ds.writer(ctx).GetContext(ctx, &count, "SELECT COUNT(*) FROM host_certificate_templates")
require.NoError(t, err)
require.Equal(t, 2, count)
// Verify the correct records remain
var remaining []struct {
HostUUID string `db:"host_uuid"`
CertificateTemplateID uint `db:"certificate_template_id"`
}
err = ds.writer(ctx).SelectContext(ctx, &remaining,
"SELECT host_uuid, certificate_template_id FROM host_certificate_templates ORDER BY host_uuid, certificate_template_id")
require.NoError(t, err)
require.Len(t, remaining, 2)
require.Equal(t, "host-1", remaining[0].HostUUID)
require.Equal(t, templateTwo.ID, remaining[0].CertificateTemplateID)
require.Equal(t, "host-2", remaining[1].HostUUID)
require.Equal(t, setup.template.ID, remaining[1].CertificateTemplateID)
})
}
func testUpsertHostCertificateTemplateStatus(t *testing.T, ds *Datastore) {
ctx := t.Context()
+3
View File
@@ -2576,6 +2576,9 @@ type Datastore interface {
// DeleteHostCertificateTemplates deletes specific host_certificate_templates records
// identified by (host_uuid, certificate_template_id) pairs.
DeleteHostCertificateTemplates(ctx context.Context, hostCertTemplates []HostCertificateTemplate) error
// DeleteHostCertificateTemplate deletes a single host_certificate_template record
// identified by host_uuid and certificate_template_id.
DeleteHostCertificateTemplate(ctx context.Context, hostUUID string, certificateTemplateID uint) error
// ListAndroidHostUUIDsWithPendingCertificateTemplates returns hosts that have
// certificate templates in 'pending' status ready for delivery.
+12
View File
@@ -1687,6 +1687,8 @@ type BulkInsertHostCertificateTemplatesFunc func(ctx context.Context, hostCertTe
type DeleteHostCertificateTemplatesFunc func(ctx context.Context, hostCertTemplates []fleet.HostCertificateTemplate) error
type DeleteHostCertificateTemplateFunc func(ctx context.Context, hostUUID string, certificateTemplateID uint) error
type ListAndroidHostUUIDsWithPendingCertificateTemplatesFunc func(ctx context.Context, offset int, limit int) ([]string, error)
type GetAndTransitionCertificateTemplatesToDeliveringFunc func(ctx context.Context, hostUUID string) (*fleet.HostCertificateTemplatesForDelivery, error)
@@ -4202,6 +4204,9 @@ type DataStore struct {
DeleteHostCertificateTemplatesFunc DeleteHostCertificateTemplatesFunc
DeleteHostCertificateTemplatesFuncInvoked bool
DeleteHostCertificateTemplateFunc DeleteHostCertificateTemplateFunc
DeleteHostCertificateTemplateFuncInvoked bool
ListAndroidHostUUIDsWithPendingCertificateTemplatesFunc ListAndroidHostUUIDsWithPendingCertificateTemplatesFunc
ListAndroidHostUUIDsWithPendingCertificateTemplatesFuncInvoked bool
@@ -10056,6 +10061,13 @@ func (s *DataStore) DeleteHostCertificateTemplates(ctx context.Context, hostCert
return s.DeleteHostCertificateTemplatesFunc(ctx, hostCertTemplates)
}
func (s *DataStore) DeleteHostCertificateTemplate(ctx context.Context, hostUUID string, certificateTemplateID uint) error {
s.mu.Lock()
s.DeleteHostCertificateTemplateFuncInvoked = true
s.mu.Unlock()
return s.DeleteHostCertificateTemplateFunc(ctx, hostUUID, certificateTemplateID)
}
func (s *DataStore) ListAndroidHostUUIDsWithPendingCertificateTemplates(ctx context.Context, offset int, limit int) ([]string, error) {
s.mu.Lock()
s.ListAndroidHostUUIDsWithPendingCertificateTemplatesFuncInvoked = true
+5
View File
@@ -592,5 +592,10 @@ func (svc *Service) UpdateCertificateStatus(
return nil
}
// If operation_type is "remove" and status is "verified", delete the host_certificate_template row
if opType == fleet.MDMOperationTypeRemove && status == fleet.MDMDeliveryVerified {
return svc.ds.DeleteHostCertificateTemplate(ctx, host.UUID, certificateTemplateID)
}
return svc.ds.UpsertCertificateStatus(ctx, host.UUID, certificateTemplateID, status, detail, opType)
}