From dbfbdcc322e256c237235eff7ef2da0f32b5bba6 Mon Sep 17 00:00:00 2001 From: Jahziel Villasana-Espinoza Date: Wed, 4 Feb 2026 09:23:44 -0500 Subject: [PATCH] only refetch location data if iDevice is locked (#39241) **Related issue:** Resolves #39215 # Checklist for submitter If some of the following don't apply, delete the relevant line. - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files) for more information. ## Testing - [x] Added/updated automated tests - [x] Where appropriate, [automated tests simulate multiple hosts and test for host isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing) (updates to one hosts's records do not affect another) - [x] QA'd all new/changed functionality manually --- changes/39215-refetch | 1 + server/service/hosts.go | 7 ++- .../service/integration_mdm_commands_test.go | 58 +++++++++++++++++++ server/service/integration_mdm_test.go | 5 ++ 4 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 changes/39215-refetch diff --git a/changes/39215-refetch b/changes/39215-refetch new file mode 100644 index 0000000000..a94dcea0c7 --- /dev/null +++ b/changes/39215-refetch @@ -0,0 +1 @@ +- Updated refetch logic for iPhone and iPad to only fetch location data if the host is locked. diff --git a/server/service/hosts.go b/server/service/hosts.go index 3ff2d0f0ae..22951e2834 100644 --- a/server/service/hosts.go +++ b/server/service/hosts.go @@ -1480,7 +1480,12 @@ func (svc *Service) RefetchHost(ctx context.Context, id uint) error { return ctxerr.Wrap(ctx, err, "refetch host: get host DEP assignment") } - if adeData.IsDEPAssignedToFleet() { + lwStatus, err := svc.ds.GetHostLockWipeStatus(ctx, host) + if err != nil { + return ctxerr.Wrap(ctx, err, "refetch host: get host location data") + } + + if adeData.IsDEPAssignedToFleet() && lwStatus.IsLocked() { err = svc.mdmAppleCommander.DeviceLocation(ctx, []string{host.UUID}, cmdUUID) if err != nil { return ctxerr.Wrap(ctx, err, "refetch host: get location with MDM") diff --git a/server/service/integration_mdm_commands_test.go b/server/service/integration_mdm_commands_test.go index 6ed078ce40..b9c4a58e5f 100644 --- a/server/service/integration_mdm_commands_test.go +++ b/server/service/integration_mdm_commands_test.go @@ -2,6 +2,7 @@ package service import ( "context" + "crypto/x509" "encoding/json" "encoding/xml" "fmt" @@ -12,6 +13,7 @@ import ( "github.com/fleetdm/fleet/v4/pkg/mdm/mdmtest" "github.com/fleetdm/fleet/v4/server/fleet" "github.com/fleetdm/fleet/v4/server/mdm/nanodep/godep" + mdmtesting "github.com/fleetdm/fleet/v4/server/mdm/testing_utils" "github.com/fleetdm/fleet/v4/server/ptr" "github.com/google/uuid" "github.com/stretchr/testify/assert" @@ -319,6 +321,62 @@ func (s *integrationMDMTestSuite) TestLockUnlockWipeIOSIpadOS() { errMsg := extractServerErrorText(res.Body) require.Contains(t, errMsg, "Host cannot be wiped until it is unlocked.") + // Refetch the host, should update the location data + _ = s.Do("POST", fmt.Sprintf("/api/latest/fleet/hosts/%d/refetch", tc.host.ID), nil, http.StatusOK) + + testCerts := []*x509.Certificate{mdmtesting.NewTestMDMAppleCertTemplate()} + var hostResp getHostResponse + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d", tc.host.ID), nil, http.StatusOK, &hostResp) + assert.Equal(t, tc.host.ID, hostResp.Host.ID) + assert.True(t, hostResp.Host.RefetchRequested) + + // Check the MDM commands and send response + cmd, err = tc.mdmClient.Idle() + require.NoError(t, err) + require.NotNil(t, cmd) + + expectedSoftware := []fleet.HostSoftwareEntry{ + { + Software: fleet.Software{ + BundleIdentifier: "com.evernote.iPhone.Evernote", + Name: "Evernote", + Version: "10.98.0", + Source: "ios_apps", + }, + }, + } + require.Equal(t, "InstalledApplicationList", cmd.Command.RequestType) + cmd, err = tc.mdmClient.AcknowledgeInstalledApplicationList(tc.mdmClient.UUID, cmd.CommandUUID, + []fleet.Software{expectedSoftware[0].Software}) + require.NoError(t, err) + require.Equal(t, "CertificateList", cmd.Command.RequestType) + cmd, err = tc.mdmClient.AcknowledgeCertificateList(tc.mdmClient.UUID, cmd.CommandUUID, testCerts) + require.NoError(t, err) + require.Equal(t, "DeviceInformation", cmd.Command.RequestType) + _, err = tc.mdmClient.AcknowledgeDeviceInformation(tc.mdmClient.UUID, cmd.CommandUUID, tc.host.DisplayName(), "", "America/Los_Angeles") + require.NoError(t, err) + + cmd, err = tc.mdmClient.Idle() + require.NoError(t, err) + require.NotNil(t, cmd) + require.Equal(t, "DeviceLocation", cmd.Command.RequestType) + expectedLat, expectedLong = 10.10, 45.45 + _, err = tc.mdmClient.AcknowledgeDeviceLocation(getHostResp.Host.UUID, cmd.CommandUUID, expectedLat, expectedLong) + require.NoError(t, err) + + // Run device location handler + s.runWorker() + + // Get host data + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d", tc.host.ID), nil, http.StatusOK, &getHostResp) + require.NotNil(t, getHostResp.Host.MDM.DeviceStatus) + require.Equal(t, string(fleet.DeviceStatusLocked), *getHostResp.Host.MDM.DeviceStatus) + require.NotNil(t, getHostResp.Host.MDM.PendingAction) + require.Equal(t, "", *getHostResp.Host.MDM.PendingAction) + // Fleet should have the updated location data now + s.Assert().NotNil(getHostResp.Host.Geolocation) + s.Assert().Equal([]float64{expectedLat, expectedLong}, getHostResp.Host.Geolocation.Geometry.Coordinates) + // unlock the host unlockResp = unlockHostResponse{} s.DoJSON("POST", fmt.Sprintf("/api/latest/fleet/hosts/%d/unlock", tc.host.ID), nil, http.StatusOK, &unlockResp) diff --git a/server/service/integration_mdm_test.go b/server/service/integration_mdm_test.go index 569b0a0bbc..d250505512 100644 --- a/server/service/integration_mdm_test.go +++ b/server/service/integration_mdm_test.go @@ -12499,6 +12499,11 @@ func (s *integrationMDMTestSuite) TestRefetchIOSIPadOS() { host, mdmClient := s.createAppleMobileHostThenEnrollMDM("ios") require.NoError(t, s.ds.SetOrUpdateMDMData(context.Background(), host.ID, false, true, "https://foo.com", true, "", "", false)) + s.enableABM(t.Name()) + abmTok, err := s.ds.GetABMTokenByOrgName(t.Context(), t.Name()) + require.NoError(t, err) + s.Require().NoError(s.ds.UpsertMDMAppleHostDEPAssignments(t.Context(), []fleet.Host{*host}, abmTok.ID, nil)) + // Refetch host _ = s.Do("POST", fmt.Sprintf("/api/latest/fleet/hosts/%d/refetch", host.ID), nil, http.StatusOK) const commandsSentPerRefetch = 3