From f099db37e7fd042cc55355d4de203b0053d5feec Mon Sep 17 00:00:00 2001 From: Nico <32375741+nulmete@users.noreply.github.com> Date: Fri, 6 Feb 2026 09:15:30 -0300 Subject: [PATCH] Fix: IdP host vitals not automatically populated for Android hosts (#39254) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Related issue:** Resolves #38554 # Checklist for submitter - [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 There's already an integration test for `MaybeAssociateHostWithScimUser` which is the function call I added as a fix. See https://github.com/fleetdm/fleet/blob/b25c9522e4eb63a44e254904cf986440a7c30a95/server/datastore/mysql/hosts_test.go#L12242 - [x] QA'd all new/changed functionality manually Enrolled physical Android device and verified that **Full name (IdP)** and **Groups (IdP)** are populated. Screenshot 2026-02-03 at 3 31 16 PM --- .../38554-populate-idp-host-vitals-android | 1 + server/mdm/android/service/pubsub.go | 3 + server/mdm/android/service/pubsub_test.go | 55 ++++++++++++++++++- 3 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 changes/38554-populate-idp-host-vitals-android diff --git a/changes/38554-populate-idp-host-vitals-android b/changes/38554-populate-idp-host-vitals-android new file mode 100644 index 0000000000..d4b125bfc4 --- /dev/null +++ b/changes/38554-populate-idp-host-vitals-android @@ -0,0 +1 @@ +- Fixed Android enrollment to associate hosts with SCIM users, populating full name, groups, and department in host vitals. diff --git a/server/mdm/android/service/pubsub.go b/server/mdm/android/service/pubsub.go index 405fdaa0f7..ee2ea9e4fe 100644 --- a/server/mdm/android/service/pubsub.go +++ b/server/mdm/android/service/pubsub.go @@ -549,6 +549,9 @@ func (svc *Service) addNewHost(ctx context.Context, device *androidmanagement.De if err != nil { return ctxerr.Wrap(ctx, err, "associating host with idp account") } + if err := svc.fleetDS.MaybeAssociateHostWithScimUser(ctx, fleetHost.Host.ID); err != nil { + return ctxerr.Wrap(ctx, err, "associating android host with scim user") + } } // Create pending certificate templates for this newly enrolled host. diff --git a/server/mdm/android/service/pubsub_test.go b/server/mdm/android/service/pubsub_test.go index aba97a8e4f..8db78f7de6 100644 --- a/server/mdm/android/service/pubsub_test.go +++ b/server/mdm/android/service/pubsub_test.go @@ -182,6 +182,9 @@ func TestPubSubEnrollment(t *testing.T) { mockDS.AssociateHostMDMIdPAccountFunc = func(ctx context.Context, hostUUID, accountUUID string) error { return nil } + mockDS.MaybeAssociateHostWithScimUserFunc = func(ctx context.Context, hostID uint) error { + return nil + } enrollmentToken := enrollmentTokenRequest{ EnrollSecret: "global", @@ -193,11 +196,61 @@ func TestPubSubEnrollment(t *testing.T) { Name: createAndroidDeviceId("test-android"), EnrollmentTokenData: string(enrollTokenData), }) - err = svc.ProcessPubSubPush(context.Background(), "value", enrollmentMessage) + err = svc.ProcessPubSubPush(t.Context(), "value", enrollmentMessage) require.NoError(t, err) require.True(t, mockDS.AssociateHostMDMIdPAccountFuncInvoked) require.True(t, mockDS.NewAndroidHostFuncInvoked) + require.True(t, mockDS.MaybeAssociateHostWithScimUserFuncInvoked) + }) + + t.Run("associates scim user with correct host ID after idp association", func(t *testing.T) { + mockDS.AppConfigFunc = func(ctx context.Context) (*fleet.AppConfig, error) { + return &fleet.AppConfig{ + MDM: fleet.MDM{ + AndroidEnabledAndConfigured: true, + }, + }, nil + } + + expectedHostID := uint(42) + mockDS.NewAndroidHostFunc = func(ctx context.Context, host *fleet.AndroidHost) (*fleet.AndroidHost, error) { + return &fleet.AndroidHost{Host: &fleet.Host{ID: expectedHostID}}, nil + } + + var capturedIdpHostUUID, capturedIdpAcctUUID string + mockDS.AssociateHostMDMIdPAccountFunc = func(ctx context.Context, hostUUID, accountUUID string) error { + capturedIdpHostUUID = hostUUID + capturedIdpAcctUUID = accountUUID + return nil + } + + var capturedScimHostID uint + mockDS.MaybeAssociateHostWithScimUserFunc = func(ctx context.Context, hostID uint) error { + capturedScimHostID = hostID + return nil + } + + idpUUID := "test-idp-uuid" + enrollmentToken := enrollmentTokenRequest{ + EnrollSecret: "global", + IdpUUID: idpUUID, + } + enrollTokenData, err := json.Marshal(enrollmentToken) + require.NoError(t, err) + enrollmentMessage := createEnrollmentMessage(t, androidmanagement.Device{ + Name: createAndroidDeviceId("test-android-scim"), + EnrollmentTokenData: string(enrollTokenData), + }) + err = svc.ProcessPubSubPush(t.Context(), "value", enrollmentMessage) + require.NoError(t, err) + + require.True(t, mockDS.AssociateHostMDMIdPAccountFuncInvoked) + require.NotEmpty(t, capturedIdpHostUUID) + require.Equal(t, idpUUID, capturedIdpAcctUUID) + + require.True(t, mockDS.MaybeAssociateHostWithScimUserFuncInvoked) + require.Equal(t, expectedHostID, capturedScimHostID) }) }) }