From 911feb74b209c8e11c0cdbafb6f7dcbb620a878b Mon Sep 17 00:00:00 2001 From: Scott Gress Date: Thu, 16 Oct 2025 20:38:57 -0500 Subject: [PATCH] Fix issue preventing "Install Fleet's agent (fleetd) manually" from saving (#34418) **Related issue:** Resolves #34410 # Checklist for submitter ## Testing - [X] Added/updated automated tests There was already a test for this but it was mocking the wrong thing; now it mocks the return from the "get bootstrap metadata" method. - [X] QA'd all new/changed functionality manually - "Install Fleet's agent (fleetd) manually" still disabled if there's no bootstrap or if software/scripts exists in setup experience - Verified that if there's just a bootstrap and no software or scripts, "Install Fleet's agent (fleetd) manually" can be checked and saved on No Team - Verified the same on a tesm. For unreleased bug fixes in a release candidate, one of: - [X] Confirmed that the fix is not expected to adversely impact load test results --- ee/server/service/mdm.go | 9 ++++++++- ee/server/service/teams.go | 9 ++++++++- ee/server/service/teams_test.go | 35 ++++++++++++++++++++++++++++++++- 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/ee/server/service/mdm.go b/ee/server/service/mdm.go index fdeeec3a8a..7992d2f52b 100644 --- a/ee/server/service/mdm.go +++ b/ee/server/service/mdm.go @@ -225,7 +225,14 @@ func (svc *Service) updateAppConfigMDMAppleSetup(ctx context.Context, payload fl if payload.ManualAgentInstall != nil { if ac.MDM.MacOSSetup.ManualAgentInstall.Value != *payload.ManualAgentInstall { - if *payload.ManualAgentInstall && (!ac.MDM.MacOSSetup.BootstrapPackage.Set || ac.MDM.MacOSSetup.BootstrapPackage.Value == "") { + // Try to load the bootstrap package to verify it exists. + _, err := svc.GetMDMAppleBootstrapPackageMetadata(ctx, 0, false) + // If we got an error other than not found, return it. + if err != nil && !fleet.IsNotFound(err) { + return ctxerr.Wrap(ctx, err, "checking bootstrap package") + } + // Otherwise if we got a not found error, we can't enable manual agent install. + if *payload.ManualAgentInstall && err != nil { return fleet.NewUserMessageError(errors.New("Couldn’t enable manual_agent_install. To use this option, first specify a bootstrap_package."), http.StatusUnprocessableEntity) } sec, err := svc.ds.GetSetupExperienceCount(ctx, string(fleet.MacOSPlatform), nil) diff --git a/ee/server/service/teams.go b/ee/server/service/teams.go index c60434aec4..042302e557 100644 --- a/ee/server/service/teams.go +++ b/ee/server/service/teams.go @@ -1790,7 +1790,14 @@ func (svc *Service) updateTeamMDMAppleSetup(ctx context.Context, tm *fleet.Team, if payload.ManualAgentInstall != nil { if tm.Config.MDM.MacOSSetup.ManualAgentInstall.Value != *payload.ManualAgentInstall { - if *payload.ManualAgentInstall && (!tm.Config.MDM.MacOSSetup.BootstrapPackage.Set || tm.Config.MDM.MacOSSetup.BootstrapPackage.Value == "") { + // Try to load the bootstrap package to verify it exists. + _, err := svc.GetMDMAppleBootstrapPackageMetadata(ctx, tm.ID, false) + // If we got an error other than not found, return it. + if err != nil && !fleet.IsNotFound(err) { + return ctxerr.Wrap(ctx, err, "checking bootstrap package") + } + // Otherwise if we got a not found error, we can't enable manual agent install. + if *payload.ManualAgentInstall && err != nil { return fleet.NewUserMessageError(errors.New("Couldn’t enable manual_agent_install. To use this option, first specify a bootstrap_package."), http.StatusUnprocessableEntity) } sec, err := svc.ds.GetSetupExperienceCount(ctx, string(fleet.MacOSPlatform), &tm.ID) diff --git a/ee/server/service/teams_test.go b/ee/server/service/teams_test.go index af69d91b96..7eaf74eb30 100644 --- a/ee/server/service/teams_test.go +++ b/ee/server/service/teams_test.go @@ -6,10 +6,12 @@ import ( "testing" "github.com/fleetdm/fleet/v4/pkg/optjson" + "github.com/fleetdm/fleet/v4/server/authz" "github.com/fleetdm/fleet/v4/server/config" "github.com/fleetdm/fleet/v4/server/fleet" "github.com/fleetdm/fleet/v4/server/mock" "github.com/fleetdm/fleet/v4/server/ptr" + "github.com/fleetdm/fleet/v4/server/test" "github.com/stretchr/testify/require" ) @@ -179,6 +181,18 @@ func TestObfuscateSecrets(t *testing.T) { }) } +type bootstrapNotFoundError struct { + msg string +} + +func (e *bootstrapNotFoundError) Error() string { + return e.msg +} + +func (e *bootstrapNotFoundError) IsNotFound() bool { + return true +} + func TestUpdateTeamMDMAppleSetupManualAgent(t *testing.T) { cases := []struct { Name string @@ -246,6 +260,9 @@ func TestUpdateTeamMDMAppleSetupManualAgent(t *testing.T) { return &fleet.Team{}, nil } + authorizer, err := authz.NewAuthorizer() + require.NoError(t, err) + svc := &Service{ ds: ds, config: config.FleetConfig{ @@ -253,12 +270,28 @@ func TestUpdateTeamMDMAppleSetupManualAgent(t *testing.T) { PrivateKey: "something", }, }, + authz: authorizer, } - ctx := context.Background() + // Add admin user to context + adminUser := &fleet.User{ + ID: 2, + GlobalRole: ptr.String(fleet.RoleAdmin), + Email: "useradmin@example.com", + } + ctx := test.UserContext(context.Background(), adminUser) for _, tc := range cases { t.Run(tc.Name, func(t *testing.T) { + ds.GetMDMAppleBootstrapPackageMetaFunc = func(ctx context.Context, teamID uint) (*fleet.MDMAppleBootstrapPackage, error) { + if tc.MacOSSetup.BootstrapPackage.Value == "" { + return nil, &bootstrapNotFoundError{msg: "bootstrap package not found"} + } + return &fleet.MDMAppleBootstrapPackage{ + Name: tc.MacOSSetup.BootstrapPackage.Value, + }, nil + } + ds.GetSetupExperienceCountFunc = func(ctx context.Context, platform string, teamID *uint) (*fleet.SetupExperienceCount, error) { return &tc.Count, nil }