From b0f47725fdaa4b76ef8a654645ac75354637af10 Mon Sep 17 00:00:00 2001 From: Sarah Gillespie <73313222+gillespi314@users.noreply.github.com> Date: Thu, 24 Jul 2025 10:28:50 -0500 Subject: [PATCH] Add activity details for BYOD MDM enrollments (#31191) --- docs/Contributing/reference/audit-logs.md | 2 ++ frontend/interfaces/activity.ts | 3 +-- .../GlobalActivityItem.tests.tsx | 2 +- .../GlobalActivityItem/GlobalActivityItem.tsx | 2 +- server/fleet/activities.go | 15 +++++++++----- server/service/apple_mdm.go | 7 +++++-- server/service/apple_mdm_test.go | 6 ++++-- server/service/integration_mdm_dep_test.go | 4 ++-- server/service/integration_mdm_test.go | 20 ++++++++----------- server/service/microsoft_mdm.go | 2 +- 10 files changed, 35 insertions(+), 28 deletions(-) diff --git a/docs/Contributing/reference/audit-logs.md b/docs/Contributing/reference/audit-logs.md index ac030ac1fc..4e2de03bf9 100644 --- a/docs/Contributing/reference/audit-logs.md +++ b/docs/Contributing/reference/audit-logs.md @@ -549,6 +549,7 @@ This activity contains the following fields: - "host_display_name": Display name of the host. - "installed_from_dep": Whether the host was enrolled via DEP (Apple enrollments only, always false for Microsoft). - "mdm_platform": Used to distinguish between Apple and Microsoft enrollments. Can be "apple", "microsoft" or not present. If missing, this value is treated as "apple" for backwards compatibility. +- "enrollment_id": The unique identifier for MDM BYOD enrollments; null for other enrollments. #### Example @@ -558,6 +559,7 @@ This activity contains the following fields: "host_display_name": "MacBookPro16,1 (C08VQ2AXHT96)", "installed_from_dep": true, "mdm_platform": "apple" + "enrollment_id": null } ``` diff --git a/frontend/interfaces/activity.ts b/frontend/interfaces/activity.ts index 50d851c8c3..aa889b91fc 100644 --- a/frontend/interfaces/activity.ts +++ b/frontend/interfaces/activity.ts @@ -182,7 +182,7 @@ export interface IActivityDetails { deadline_days?: number; deadline?: string; email?: string; - enrollment_id?: string; + enrollment_id?: string | null; // unique identifier for MDM BYOD enrollments; null for other enrollments global?: boolean; grace_period_days?: number; host_display_name?: string; @@ -202,7 +202,6 @@ export interface IActivityDetails { name?: string; pack_id?: number; pack_name?: string; - personal_host?: boolean; platform?: Platform; // software platform policy_id?: number; policy_name?: string; diff --git a/frontend/pages/DashboardPage/cards/ActivityFeed/GlobalActivityItem/GlobalActivityItem.tests.tsx b/frontend/pages/DashboardPage/cards/ActivityFeed/GlobalActivityItem/GlobalActivityItem.tests.tsx index d2510c41c3..8cd14195d0 100644 --- a/frontend/pages/DashboardPage/cards/ActivityFeed/GlobalActivityItem/GlobalActivityItem.tests.tsx +++ b/frontend/pages/DashboardPage/cards/ActivityFeed/GlobalActivityItem/GlobalActivityItem.tests.tsx @@ -1027,7 +1027,7 @@ describe("Activity Feed", () => { type: ActivityType.MdmEnrolled, details: { host_display_name: "Test Host", - personal_host: true, + enrollment_id: "test-enrollment-id", mdm_platform: "apple", }, }); diff --git a/frontend/pages/DashboardPage/cards/ActivityFeed/GlobalActivityItem/GlobalActivityItem.tsx b/frontend/pages/DashboardPage/cards/ActivityFeed/GlobalActivityItem/GlobalActivityItem.tsx index 63fc289a7a..4396903a24 100644 --- a/frontend/pages/DashboardPage/cards/ActivityFeed/GlobalActivityItem/GlobalActivityItem.tsx +++ b/frontend/pages/DashboardPage/cards/ActivityFeed/GlobalActivityItem/GlobalActivityItem.tsx @@ -274,7 +274,7 @@ const TAGGED_TEMPLATES = { // note: if mdm_platform is missing, we assume this is Apple MDM for backwards // compatibility let enrollmentTypeText = ""; - if (activity.details?.personal_host) { + if (activity.details?.enrollment_id) { enrollmentTypeText = "personal"; } else if (activity.details?.installed_from_dep) { enrollmentTypeText = "automatic"; diff --git a/server/fleet/activities.go b/server/fleet/activities.go index 8153843d80..704bdc6dd2 100644 --- a/server/fleet/activities.go +++ b/server/fleet/activities.go @@ -895,10 +895,12 @@ func (a ActivityTypeFleetEnrolled) Documentation() (activity string, details str } type ActivityTypeMDMEnrolled struct { - HostSerial string `json:"host_serial"` - HostDisplayName string `json:"host_display_name"` - InstalledFromDEP bool `json:"installed_from_dep"` - MDMPlatform string `json:"mdm_platform"` + HostSerial *string `json:"host_serial"` + HostDisplayName string `json:"host_display_name"` + InstalledFromDEP bool `json:"installed_from_dep"` + MDMPlatform string `json:"mdm_platform"` + // EnrollmentID is the unique identifier for the MDM BYOD enrollments. It is nil for other enrollments. + EnrollmentID *string `json:"enrollment_id"` } func (a ActivityTypeMDMEnrolled) ActivityName() string { @@ -911,14 +913,17 @@ func (a ActivityTypeMDMEnrolled) Documentation() (activity string, details strin - "host_serial": Serial number of the host (Apple enrollments only, always empty for Microsoft). - "host_display_name": Display name of the host. - "installed_from_dep": Whether the host was enrolled via DEP (Apple enrollments only, always false for Microsoft). -- "mdm_platform": Used to distinguish between Apple and Microsoft enrollments. Can be "apple", "microsoft" or not present. If missing, this value is treated as "apple" for backwards compatibility.`, `{ +- "mdm_platform": Used to distinguish between Apple and Microsoft enrollments. Can be "apple", "microsoft" or not present. If missing, this value is treated as "apple" for backwards compatibility. +- "enrollment_id": The unique identifier for MDM BYOD enrollments; null for other enrollments.`, `{ "host_serial": "C08VQ2AXHT96", "host_display_name": "MacBookPro16,1 (C08VQ2AXHT96)", "installed_from_dep": true, "mdm_platform": "apple" + "enrollment_id": null }` } +// TODO(BMAA): Should we add enrollment_id for BYOD unenrollments? type ActivityTypeMDMUnenrolled struct { HostSerial string `json:"host_serial"` HostDisplayName string `json:"host_display_name"` diff --git a/server/service/apple_mdm.go b/server/service/apple_mdm.go index d2ae43e7d5..7a94c9d972 100644 --- a/server/service/apple_mdm.go +++ b/server/service/apple_mdm.go @@ -3408,12 +3408,15 @@ func (svc *MDMAppleCheckinAndCommandService) Authenticate(r *mdm.Request, m *mdm if err != nil { return ctxerr.Wrap(r.Context, err, "getting checkin info in Authenticate message") } - // TODO BMAA Update for activity changes mdmEnrolledActivity := &fleet.ActivityTypeMDMEnrolled{ HostDisplayName: updatedInfo.DisplayName, InstalledFromDEP: updatedInfo.DEPAssignedToFleet, MDMPlatform: fleet.MDMPlatformApple, - HostSerial: updatedInfo.HardwareSerial, + } + if r.Type == mdm.UserEnrollmentDevice { + mdmEnrolledActivity.EnrollmentID = ptr.String(m.EnrollmentID) + } else { + mdmEnrolledActivity.HostSerial = ptr.String(updatedInfo.HardwareSerial) } return newActivity( r.Context, nil, mdmEnrolledActivity, svc.ds, svc.logger, diff --git a/server/service/apple_mdm_test.go b/server/service/apple_mdm_test.go index dac4241321..1570cd9062 100644 --- a/server/service/apple_mdm_test.go +++ b/server/service/apple_mdm_test.go @@ -1175,7 +1175,8 @@ func TestMDMAuthenticateManualEnrollment(t *testing.T) { require.Nil(t, user) require.Equal(t, "mdm_enrolled", activity.ActivityName()) require.NotNil(t, a.HostSerial) - require.Equal(t, serial, a.HostSerial) + require.Equal(t, serial, *a.HostSerial) + require.Nil(t, a.EnrollmentID) require.Equal(t, a.HostDisplayName, fmt.Sprintf("%s (%s)", model, serial)) require.False(t, a.InstalledFromDEP) require.Equal(t, fleet.MDMPlatformApple, a.MDMPlatform) @@ -1242,7 +1243,8 @@ func TestMDMAuthenticateADE(t *testing.T) { require.Nil(t, user) require.Equal(t, "mdm_enrolled", activity.ActivityName()) require.NotNil(t, a.HostSerial) - require.Equal(t, serial, a.HostSerial) + require.Equal(t, serial, *a.HostSerial) + require.Nil(t, a.EnrollmentID) require.Equal(t, a.HostDisplayName, fmt.Sprintf("%s (%s)", model, serial)) require.True(t, a.InstalledFromDEP) require.Equal(t, fleet.MDMPlatformApple, a.MDMPlatform) diff --git a/server/service/integration_mdm_dep_test.go b/server/service/integration_mdm_dep_test.go index fd4fc61702..59ebbdbb98 100644 --- a/server/service/integration_mdm_dep_test.go +++ b/server/service/integration_mdm_dep_test.go @@ -1062,7 +1062,7 @@ func (s *integrationMDMTestSuite) TestDEPProfileAssignment() { require.JSONEq( t, fmt.Sprintf( - `{"host_serial": "%s", "host_display_name": "%s (%s)", "installed_from_dep": true, "mdm_platform": "apple"}`, + `{"host_serial": "%s", "enrollment_id": null, "host_display_name": "%s (%s)", "installed_from_dep": true, "mdm_platform": "apple"}`, devices[0].SerialNumber, devices[0].Model, devices[0].SerialNumber, ), string(*activity.Details), @@ -1294,7 +1294,7 @@ func (s *integrationMDMTestSuite) TestDEPProfileAssignment() { s.lastActivityMatches( "mdm_enrolled", fmt.Sprintf( - `{"host_serial": "%s", "host_display_name": "%s (%s)", "installed_from_dep": true, "mdm_platform": "apple"}`, + `{"host_serial": "%s", "enrollment_id": null, "host_display_name": "%s (%s)", "installed_from_dep": true, "mdm_platform": "apple"}`, mdmDevice.SerialNumber, mdmDevice.Model, mdmDevice.SerialNumber, ), 0, diff --git a/server/service/integration_mdm_test.go b/server/service/integration_mdm_test.go index fc3fc00730..f33b420ac1 100644 --- a/server/service/integration_mdm_test.go +++ b/server/service/integration_mdm_test.go @@ -1278,14 +1278,12 @@ func (s *integrationMDMTestSuite) TestAppleMDMDeviceEnrollment() { err := mdmDeviceA.Enroll() require.NoError(t, err) s.lastActivityOfTypeMatches(fleet.ActivityTypeMDMEnrolled{}.ActivityName(), - fmt.Sprintf(`{"host_serial": "%s", "host_display_name": "%s (%s)", "installed_from_dep": false, "mdm_platform": "apple"}`, mdmDeviceA.SerialNumber, mdmDeviceA.Model, mdmDeviceA.SerialNumber), 0) - + fmt.Sprintf(`{"host_serial": "%s", "enrollment_id": null, "host_display_name": "%s (%s)", "installed_from_dep": false, "mdm_platform": "apple"}`, mdmDeviceA.SerialNumber, mdmDeviceA.Model, mdmDeviceA.SerialNumber), 0) mdmDeviceB := mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo, "MacBookPro16,1") err = mdmDeviceB.Enroll() require.NoError(t, err) s.lastActivityOfTypeMatches(fleet.ActivityTypeMDMEnrolled{}.ActivityName(), - fmt.Sprintf(`{"host_serial": "%s", "host_display_name": "%s (%s)", "installed_from_dep": false, "mdm_platform": "apple"}`, mdmDeviceB.SerialNumber, mdmDeviceB.Model, mdmDeviceB.SerialNumber), 0) - + fmt.Sprintf(`{"host_serial": "%s", "enrollment_id": null, "host_display_name": "%s (%s)", "installed_from_dep": false, "mdm_platform": "apple"}`, mdmDeviceB.SerialNumber, mdmDeviceB.Model, mdmDeviceB.SerialNumber), 0) // Find the ID of Fleet's MDM solution var mdmID uint mysql.ExecAdhocSQL(t, s.ds, func(q sqlx.ExtContext) error { @@ -7368,7 +7366,8 @@ func (s *integrationMDMTestSuite) TestValidRequestSecurityTokenRequestWithDevice "mdm_platform": "microsoft", "host_serial": "%s", "installed_from_dep": false, - "host_display_name": "%s" + "host_display_name": "%s", + "enrollment_id": null }`, windowsHost.HardwareSerial, windowsHost.DisplayName()), 0) @@ -7418,7 +7417,8 @@ func (s *integrationMDMTestSuite) TestValidRequestSecurityTokenRequestWithAzureT "mdm_platform": "microsoft", "host_serial": "", "installed_from_dep": false, - "host_display_name": "DESKTOP-0C89RC0" + "host_display_name": "DESKTOP-0C89RC0", + "enrollment_id": null }`, 0) @@ -13873,10 +13873,8 @@ func (s *integrationMDMTestSuite) TestAppleMDMAccountDrivenUserEnrollment() { require.NoError(t, iPhoneMdmDevice.Enroll()) assert.Equal(t, iPhoneMdmDevice.EnrollInfo.AssignedManagedAppleID, "sso_user@example.com") - // TODO BMAA Update for activity changes s.lastActivityOfTypeMatches(fleet.ActivityTypeMDMEnrolled{}.ActivityName(), - fmt.Sprintf(`{"host_serial": "%s", "host_display_name": "%s (%s)", "installed_from_dep": false, "mdm_platform": "apple"}`, iPhoneMdmDevice.EnrollmentID(), iPhoneMdmDevice.Model, iPhoneMdmDevice.EnrollmentID()), 0) - + fmt.Sprintf(`{"host_serial": null, "enrollment_id": "%s", "host_display_name": "%s (%s)", "installed_from_dep": false, "mdm_platform": "apple"}`, iPhoneMdmDevice.EnrollmentID(), iPhoneMdmDevice.Model, iPhoneMdmDevice.EnrollmentID()), 0) linkedIDPAccount, err := s.ds.GetMDMIdPAccountByHostUUID(context.Background(), iPhoneMdmDevice.EnrollmentID()) require.NoError(t, err) require.NotNil(t, linkedIDPAccount) @@ -13898,10 +13896,8 @@ func (s *integrationMDMTestSuite) TestAppleMDMAccountDrivenUserEnrollment() { require.NoError(t, iPadMdmDevice.Enroll()) assert.Equal(t, iPadMdmDevice.EnrollInfo.AssignedManagedAppleID, "sso_user2@example.com") - // TODO BMAA Update for activity changes s.lastActivityOfTypeMatches(fleet.ActivityTypeMDMEnrolled{}.ActivityName(), - fmt.Sprintf(`{"host_serial": "%s", "host_display_name": "%s (%s)", "installed_from_dep": false, "mdm_platform": "apple"}`, iPadMdmDevice.EnrollmentID(), iPadMdmDevice.Model, iPadMdmDevice.EnrollmentID()), 0) - + fmt.Sprintf(`{"host_serial": null, "enrollment_id": "%s", "host_display_name": "%s (%s)", "installed_from_dep": false, "mdm_platform": "apple"}`, iPadMdmDevice.EnrollmentID(), iPadMdmDevice.Model, iPadMdmDevice.EnrollmentID()), 0) linkedIDPAccount, err = s.ds.GetMDMIdPAccountByHostUUID(context.Background(), iPadMdmDevice.EnrollmentID()) require.NoError(t, err) require.NotNil(t, linkedIDPAccount) diff --git a/server/service/microsoft_mdm.go b/server/service/microsoft_mdm.go index 60fd0889d3..e1351f107c 100644 --- a/server/service/microsoft_mdm.go +++ b/server/service/microsoft_mdm.go @@ -1848,7 +1848,7 @@ func (svc *Service) storeWindowsMDMEnrolledDevice(ctx context.Context, userID st ctx, nil, &fleet.ActivityTypeMDMEnrolled{ HostDisplayName: displayName, MDMPlatform: fleet.MDMPlatformMicrosoft, - HostSerial: serial, + HostSerial: &serial, }) if err != nil { // only logging, the device is enrolled at this point, and we