From f30017f354ad22ac5f88351eed21da3155f5d031 Mon Sep 17 00:00:00 2001 From: Lucas Manuel Rodriguez Date: Fri, 30 Aug 2024 17:00:29 -0300 Subject: [PATCH] Fix upcoming activities for automatic installers (#21714) Small fix for #21428. This is to show the activity the right way (Because installations triggered by Fleet will have `host_software_installs` with `NULL` `user_id`.). --- server/datastore/mysql/activities.go | 12 +++++--- server/datastore/mysql/activities_test.go | 29 +++++++++++++++---- server/service/integration_enterprise_test.go | 10 +++++++ 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/server/datastore/mysql/activities.go b/server/datastore/mysql/activities.go index 7ea67b5504..09a71a6f22 100644 --- a/server/datastore/mysql/activities.go +++ b/server/datastore/mysql/activities.go @@ -316,10 +316,12 @@ func (ds *Datastore) ListHostUpcomingActivities(ctx context.Context, hostID uint // list pending software installs fmt.Sprintf(`SELECT hsi.execution_id as uuid, - u.name as name, - u.id as user_id, - u.gravatar_url as gravatar_url, - u.email as user_email, + -- policies with automatic installers generate a host_software_installs with (user_id=NULL,self_service=0), + -- thus the user_id for the upcoming activity needs to be the user that uploaded the software installer. + IF(hsi.user_id IS NULL AND NOT hsi.self_service, u2.name, u.name) AS name, + IF(hsi.user_id IS NULL AND NOT hsi.self_service, u2.id, u.id) as user_id, + IF(hsi.user_id IS NULL AND NOT hsi.self_service, u2.gravatar_url, u.gravatar_url) as gravatar_url, + IF(hsi.user_id IS NULL AND NOT hsi.self_service, u2.email, u.email) AS user_email, :installed_software_type as activity_type, hsi.created_at as created_at, JSON_OBJECT( @@ -339,6 +341,8 @@ func (ds *Datastore) ListHostUpcomingActivities(ctx context.Context, hostID uint software_titles st ON st.id = si.title_id LEFT OUTER JOIN users u ON u.id = hsi.user_id + LEFT OUTER JOIN + users u2 ON u2.id = si.user_id LEFT OUTER JOIN host_display_names hdn ON hdn.host_id = hsi.host_id WHERE diff --git a/server/datastore/mysql/activities_test.go b/server/datastore/mysql/activities_test.go index 4c5e4077d2..2bf04c06c7 100644 --- a/server/datastore/mysql/activities_test.go +++ b/server/datastore/mysql/activities_test.go @@ -494,7 +494,10 @@ func testListHostUpcomingActivities(t *testing.T, ds *Datastore) { InstallScriptExitCode: ptr.Int(0), }) require.NoError(t, err) - h1Foo, err := ds.InsertSoftwareInstallRequest(noUserCtx, h1.ID, sw1Meta.InstallerID, false) // no user for this one + + // No user for this one and not Self-service, means it was installed by Fleet thus the author was decided to be the admin + // that uploaded the installer. + h1Foo, err := ds.InsertSoftwareInstallRequest(noUserCtx, h1.ID, sw1Meta.InstallerID, false) require.NoError(t, err) // create a single pending request for h2, as well as a non-pending one @@ -509,6 +512,9 @@ func testListHostUpcomingActivities(t *testing.T, ds *Datastore) { // add a pending software install request for h2 h2Bar, err := ds.InsertSoftwareInstallRequest(ctx, h2.ID, sw2Meta.InstallerID, false) require.NoError(t, err) + // No user for this one and Self-service, means it was installed by the end user, so the user_id should be null/nil. + h2Foo, err := ds.InsertSoftwareInstallRequest(noUserCtx, h2.ID, sw1Meta.InstallerID, true) + require.NoError(t, err) // nothing for h3 @@ -517,6 +523,8 @@ func testListHostUpcomingActivities(t *testing.T, ds *Datastore) { endTime = SetOrderedCreatedAtTimestamps(t, ds, endTime, "host_software_installs", "execution_id", h1FooFailed, h1Bar) endTime = SetOrderedCreatedAtTimestamps(t, ds, endTime, "host_script_results", "execution_id", h1C, h1D, h1E) endTime = SetOrderedCreatedAtTimestamps(t, ds, endTime, "host_software_installs", "execution_id", h1FooInstalled, h1Foo) + endTime = SetOrderedCreatedAtTimestamps(t, ds, endTime, "host_software_installs", "execution_id", h1Foo) + endTime = SetOrderedCreatedAtTimestamps(t, ds, endTime, "host_software_installs", "execution_id", h2Foo) endTime = SetOrderedCreatedAtTimestamps(t, ds, endTime, "host_software_installs", "execution_id", h2Bar) endTime = SetOrderedCreatedAtTimestamps(t, ds, endTime, "host_script_results", "execution_id", h2A, h2F) SetOrderedCreatedAtTimestamps(t, ds, endTime, "host_vpp_software_installs", "command_uuid", vppCommand1, vppCommand2) @@ -529,7 +537,8 @@ func testListHostUpcomingActivities(t *testing.T, ds *Datastore) { h1E: false, h2A: true, h2F: true, - h1Foo: false, + h1Foo: true, + h2Foo: false, h1Bar: true, h2Bar: true, vppCommand1: true, @@ -544,6 +553,10 @@ func testListHostUpcomingActivities(t *testing.T, ds *Datastore) { h1Foo: "foo", h1Bar: "bar", h2Bar: "bar", + h2Foo: "foo", + } + execIDsWithUserAdminID := map[string]struct{}{ + h1Foo: {}, } cases := []struct { @@ -595,10 +608,10 @@ func testListHostUpcomingActivities(t *testing.T, ds *Datastore) { wantMeta: &fleet.PaginationMetadata{HasNextResults: false, HasPreviousResults: true, TotalResults: 8}, }, { - opts: fleet.ListOptions{PerPage: 3}, + opts: fleet.ListOptions{PerPage: 4}, hostID: h2.ID, - wantExecs: []string{h2Bar, h2A, vppCommand2}, - wantMeta: &fleet.PaginationMetadata{HasNextResults: false, HasPreviousResults: false, TotalResults: 3}, + wantExecs: []string{h2Foo, h2Bar, h2A, vppCommand2}, + wantMeta: &fleet.PaginationMetadata{HasNextResults: false, HasPreviousResults: false, TotalResults: 4}, }, { opts: fleet.ListOptions{}, @@ -639,7 +652,11 @@ func testListHostUpcomingActivities(t *testing.T, ds *Datastore) { case fleet.ActivityTypeInstalledSoftware{}.ActivityName(): require.Equal(t, wantExec, details["install_uuid"], "result %d", i) require.Equal(t, execIDsSoftwareTitle[wantExec], details["software_title"], "result %d", i) - wantUser = u2 + if _, ok := execIDsWithUserAdminID[details["install_uuid"].(string)]; ok { + wantUser = u + } else { + wantUser = u2 + } case fleet.ActivityInstalledAppStoreApp{}.ActivityName(): require.Equal(t, wantExec, details["command_uuid"], "result %d", i) diff --git a/server/service/integration_enterprise_test.go b/server/service/integration_enterprise_test.go index 43e7d2edbc..99904d6a04 100644 --- a/server/service/integration_enterprise_test.go +++ b/server/service/integration_enterprise_test.go @@ -13214,6 +13214,16 @@ func (s *integrationEnterpriseTestSuite) TestPolicyAutomationsSoftwareInstallers }, ), http.StatusOK, &distributedResp) + // Upcoming activities for host1Team1 should show the automatic installation of dummy_installer.pkg. + // Check the author should be the admin that uploaded the installer. + var listUpcomingAct listHostUpcomingActivitiesResponse + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/activities/upcoming", host1Team1.ID), nil, http.StatusOK, &listUpcomingAct) + require.Len(t, listUpcomingAct.Activities, 1) + require.NotNil(t, listUpcomingAct.Activities[0].ActorID) + require.Equal(t, globalAdmin.ID, *listUpcomingAct.Activities[0].ActorID) + require.Equal(t, globalAdmin.Name, *listUpcomingAct.Activities[0].ActorFullName) + require.Equal(t, globalAdmin.Email, *listUpcomingAct.Activities[0].ActorEmail) + // // Finally have orbit install the packages and check activities. //