diff --git a/server/fleet/hosts.go b/server/fleet/hosts.go index 5e1a792aee..58119cb43b 100644 --- a/server/fleet/hosts.go +++ b/server/fleet/hosts.go @@ -690,9 +690,17 @@ func (h *Host) IsDEPAssignedToFleet() bool { func (h *Host) IsEligibleForDEPMigration(isConnectedToFleetMDM bool) bool { return h.IsOsqueryEnrolled() && h.IsDEPAssignedToFleet() && + h.MDMInfo != nil && h.MDMInfo.HasJSONProfileAssigned() && h.MDMInfo.Enrolled && - !isConnectedToFleetMDM + // as a special case for migration with user interaction, we + // also check the information stored in host_mdm, and assume + // the host needs migration if it's not Fleet + // + // this is because we can't always rely on nano setting + // `nano_enrollment.active = 1` since sometimes Fleet won't get + // the checkout message from the host. + (!isConnectedToFleetMDM || h.MDMInfo.Name != WellKnownMDMFleet) } // NeedsDEPEnrollment returns true if the host should be DEP enrolled into diff --git a/server/service/integration_mdm_test.go b/server/service/integration_mdm_test.go index 1e310addaf..720397a494 100644 --- a/server/service/integration_mdm_test.go +++ b/server/service/integration_mdm_test.go @@ -5063,7 +5063,6 @@ func (s *integrationMDMTestSuite) TestMDMMigration() { s.DoJSON("POST", "/api/fleet/orbit/config", json.RawMessage(fmt.Sprintf(`{"orbit_node_key": %q}`, *host.OrbitNodeKey)), http.StatusOK, &orbitConfigResp) require.True(t, orbitConfigResp.Notifications.NeedsMDMMigration) require.False(t, orbitConfigResp.Notifications.RenewEnrollmentProfile) - cleanAssignmentStatus() // simulate that the device needs to be enrolled in fleet, DEP capable err = s.ds.SetOrUpdateMDMData( @@ -5109,6 +5108,16 @@ func (s *integrationMDMTestSuite) TestMDMMigration() { "", ) require.NoError(t, err) + mdmDevice := mdmtest.NewTestMDMClientAppleDirect(mdmtest.AppleEnrollInfo{ + SCEPChallenge: s.scepChallenge, + SCEPURL: s.server.URL + apple_mdm.SCEPPath, + MDMURL: s.server.URL + apple_mdm.MDMPath, + }, "MacBookPro16,1") + mdmDevice.SerialNumber = host.HardwareSerial + mdmDevice.UUID = host.UUID + err = mdmDevice.Enroll() + require.NoError(t, err) + require.NoError(t, err) getDesktopResp = fleetDesktopResponse{} res = s.DoRawNoAuth("GET", "/api/latest/fleet/device/"+token+"/desktop", nil, http.StatusOK) require.NoError(t, json.NewDecoder(res.Body).Decode(&getDesktopResp)) @@ -5127,6 +5136,51 @@ func (s *integrationMDMTestSuite) TestMDMMigration() { s.DoJSON("POST", "/api/fleet/orbit/config", json.RawMessage(fmt.Sprintf(`{"orbit_node_key": %q}`, *host.OrbitNodeKey)), http.StatusOK, &orbitConfigResp) require.False(t, orbitConfigResp.Notifications.NeedsMDMMigration) require.False(t, orbitConfigResp.Notifications.RenewEnrollmentProfile) + + // simulate a host that was reset to factory, but fleet still has an mdm record active + err = s.ds.SetOrUpdateMDMData( + ctx, + host.ID, + false, + true, + s.server.URL, + false, + fleet.WellKnownMDMSimpleMDM, + "", + ) + require.NoError(t, err) + getDesktopResp = fleetDesktopResponse{} + res = s.DoRawNoAuth("GET", "/api/latest/fleet/device/"+token+"/desktop", nil, http.StatusOK) + require.NoError(t, json.NewDecoder(res.Body).Decode(&getDesktopResp)) + require.NoError(t, res.Body.Close()) + require.NoError(t, getDesktopResp.Err) + require.Zero(t, *getDesktopResp.FailingPolicies) + require.True(t, getDesktopResp.Notifications.NeedsMDMMigration) + require.False(t, getDesktopResp.Notifications.RenewEnrollmentProfile) + require.Equal(t, acResp.OrgInfo.OrgLogoURL, getDesktopResp.Config.OrgInfo.OrgLogoURL) + require.Equal(t, acResp.OrgInfo.OrgLogoURLLightBackground, getDesktopResp.Config.OrgInfo.OrgLogoURLLightBackground) + require.Equal(t, acResp.OrgInfo.ContactURL, getDesktopResp.Config.OrgInfo.ContactURL) + require.Equal(t, acResp.OrgInfo.OrgName, getDesktopResp.Config.OrgInfo.OrgName) + require.Equal(t, acResp.MDM.MacOSMigration.Mode, getDesktopResp.Config.MDM.MacOSMigration.Mode) + + orbitConfigResp = orbitGetConfigResponse{} + s.DoJSON("POST", "/api/fleet/orbit/config", json.RawMessage(fmt.Sprintf(`{"orbit_node_key": %q}`, *host.OrbitNodeKey)), http.StatusOK, &orbitConfigResp) + require.True(t, orbitConfigResp.Notifications.NeedsMDMMigration) + require.False(t, orbitConfigResp.Notifications.RenewEnrollmentProfile) + + // clean up nano tables + mysql.ExecAdhocSQL(t, s.ds, func(q sqlx.ExtContext) error { + _, err := q.ExecContext(context.Background(), ` + DELETE FROM nano_enrollments WHERE id = ? + `, host.UUID) + return err + }) + mysql.ExecAdhocSQL(t, s.ds, func(q sqlx.ExtContext) error { + _, err := q.ExecContext(context.Background(), ` + DELETE FROM nano_devices WHERE id = ? + `, host.UUID) + return err + }) } token := "token_test_migration"