From 669e944f50013efae9dadb9c3a5271043f475682 Mon Sep 17 00:00:00 2001 From: Konstantin Sykulev Date: Thu, 12 Dec 2024 13:33:19 -0600 Subject: [PATCH] Team policy endpoint now accepts `null` to unset a script or software installer (#24658) https://github.com/fleetdm/fleet/issues/23490 # 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/Committing-Changes.md#changes-files) for more information. - [x] Added/updated tests - [x] Manual QA for all new/changed functionality --------- Co-authored-by: Ian Littman --- .../23490-null-script-software-on-policies | 1 + .../ManagePoliciesPage/ManagePoliciesPage.tsx | 8 +- server/fleet/policies.go | 6 +- server/service/integration_core_test.go | 76 +++++++++-------- server/service/integration_enterprise_test.go | 84 ++++++++++++++----- server/service/team_policies.go | 12 +-- 6 files changed, 118 insertions(+), 69 deletions(-) create mode 100644 changes/23490-null-script-software-on-policies diff --git a/changes/23490-null-script-software-on-policies b/changes/23490-null-script-software-on-policies new file mode 100644 index 0000000000..cf2db860dc --- /dev/null +++ b/changes/23490-null-script-software-on-policies @@ -0,0 +1 @@ +* Allowed team policy endpoint (`PATCH /api/latest/fleet/teams/{team_id}/policies/{policy_id}`) to receive explicit `null` as a value for `script_id` or `software_title_id` to unset a script or software installer respectively. \ No newline at end of file diff --git a/frontend/pages/policies/ManagePoliciesPage/ManagePoliciesPage.tsx b/frontend/pages/policies/ManagePoliciesPage/ManagePoliciesPage.tsx index ec7764de62..dd0d122aae 100644 --- a/frontend/pages/policies/ManagePoliciesPage/ManagePoliciesPage.tsx +++ b/frontend/pages/policies/ManagePoliciesPage/ManagePoliciesPage.tsx @@ -560,9 +560,9 @@ const ManagePolicyPage = ({ responses.concat( changedPolicies.map((changedPolicy) => { return teamPoliciesAPI.update(changedPolicy.id, { - // "software_title_id": 0 will unset software install for the policy + // "software_title_id": null will unset software install for the policy // "software_title_id": X will set the value to the given integer (except 0). - software_title_id: changedPolicy.swIdToInstall || 0, + software_title_id: changedPolicy.swIdToInstall || null, team_id: teamIdForApi, }); }) @@ -613,9 +613,9 @@ const ManagePolicyPage = ({ responses.concat( changedPolicies.map((changedPolicy) => { return teamPoliciesAPI.update(changedPolicy.id, { - // "script_id": 0 will unset running a script for the policy (a script never has ID 0) + // "script_id": null will unset running a script for the policy // "script_id": X will sets script X to run when the policy fails - script_id: changedPolicy.scriptIdToRun || 0, + script_id: changedPolicy.scriptIdToRun || null, team_id: teamIdForApi, }); }) diff --git a/server/fleet/policies.go b/server/fleet/policies.go index 10c3c907d4..fdd4697e87 100644 --- a/server/fleet/policies.go +++ b/server/fleet/policies.go @@ -4,6 +4,8 @@ import ( "errors" "strings" "time" + + "github.com/fleetdm/fleet/v4/pkg/optjson" ) // PolicyPayload holds data for policy creation. @@ -162,12 +164,12 @@ type ModifyPolicyPayload struct { // Value 0 will unset the current installer from the policy. // // Only applies to team policies. - SoftwareTitleID *uint `json:"software_title_id" premium:"true"` + SoftwareTitleID optjson.Any[uint] `json:"software_title_id" premium:"true"` // ScriptID is the ID of the script that will be executed if the policy fails. // Value 0 will unset the current script from the policy. // // Only applies to team policies. - ScriptID *uint `json:"script_id" premium:"true"` + ScriptID optjson.Any[uint] `json:"script_id" premium:"true"` } // Verify verifies the policy payload is valid. diff --git a/server/service/integration_core_test.go b/server/service/integration_core_test.go index 4f13c1b54d..89d7a97c00 100644 --- a/server/service/integration_core_test.go +++ b/server/service/integration_core_test.go @@ -2404,16 +2404,18 @@ func (s *integrationTestSuite) TestGlobalPoliciesProprietary() { assert.Equal(t, "admin1@example.com", gpResp.Policy.AuthorEmail) assert.Equal(t, "darwin", gpResp.Policy.Platform) - mgpParams := modifyGlobalPolicyRequest{ - ModifyPolicyPayload: fleet.ModifyPolicyPayload{ - Name: ptr.String("TestQuery4"), - Query: ptr.String("select * from osquery_info;"), - Description: ptr.String("Some description updated"), - Resolution: ptr.String("some global resolution updated"), - }, - } - mgpResp := modifyGlobalPolicyResponse{} - s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/policies/%d", gpResp.Policy.ID), mgpParams, http.StatusOK, &mgpResp) + response := s.DoRaw("PATCH", fmt.Sprintf("/api/latest/fleet/policies/%d", gpResp.Policy.ID), []byte(`{ + "name": "TestQuery4", + "query": "select * from osquery_info;", + "description": "Some description updated", + "resolution": "some global resolution updated" + }`), http.StatusOK) + var mgpResp modifyGlobalPolicyResponse + responseBody, err := io.ReadAll(response.Body) + require.NoError(t, err) + err = json.Unmarshal(responseBody, &mgpResp) + require.NoError(t, err) + require.NotNil(t, gpResp.Policy) assert.Equal(t, "TestQuery4", mgpResp.Policy.Name) assert.Equal(t, "select * from osquery_info;", mgpResp.Policy.Query) @@ -2473,13 +2475,14 @@ func (s *integrationTestSuite) TestGlobalPoliciesProprietary() { s.DoJSON("GET", listHostsURL, nil, http.StatusOK, &listHostsResp) require.Len(t, listHostsResp.Hosts, 1) - mgpParams = modifyGlobalPolicyRequest{ - ModifyPolicyPayload: fleet.ModifyPolicyPayload{ - Query: ptr.String("select * from users;"), - }, - } - mgpResp = modifyGlobalPolicyResponse{} - s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/policies/%d", gpResp.Policy.ID), mgpParams, http.StatusOK, &mgpResp) + response = s.DoRaw("PATCH", fmt.Sprintf("/api/latest/fleet/policies/%d", gpResp.Policy.ID), []byte(`{ + "query": "select * from users;" + }`), http.StatusOK) + responseBody, err = io.ReadAll(response.Body) + require.NoError(t, err) + err = json.Unmarshal(responseBody, &mgpResp) + require.NoError(t, err) + require.NotNil(t, gpResp.Policy) assert.Equal(t, "TestQuery4", mgpResp.Policy.Name) assert.Equal(t, "select * from users;", mgpResp.Policy.Query) @@ -2538,13 +2541,14 @@ func (s *integrationTestSuite) TestGlobalPoliciesProprietary() { require.Len(t, listHostsResp.Hosts, 1) // Modify the platform for the policy, which should clear the policy stats - mgpParams = modifyGlobalPolicyRequest{ - ModifyPolicyPayload: fleet.ModifyPolicyPayload{ - Platform: ptr.String("linux"), - }, - } - mgpResp = modifyGlobalPolicyResponse{} - s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/policies/%d", gpResp.Policy.ID), mgpParams, http.StatusOK, &mgpResp) + response = s.DoRaw("PATCH", fmt.Sprintf("/api/latest/fleet/policies/%d", gpResp.Policy.ID), []byte(`{ + "platform": "linux" + }`), http.StatusOK) + responseBody, err = io.ReadAll(response.Body) + require.NoError(t, err) + err = json.Unmarshal(responseBody, &mgpResp) + require.NoError(t, err) + require.NotNil(t, gpResp.Policy) assert.Equal(t, "TestQuery4", mgpResp.Policy.Name) assert.Equal(t, "select * from users;", mgpResp.Policy.Query) @@ -2630,16 +2634,19 @@ func (s *integrationTestSuite) TestTeamPoliciesProprietary() { assert.Equal(t, "admin1@example.com", tpResp.Policy.AuthorEmail) tpNameNew := "TestPolicy4" - mtpParams := modifyTeamPolicyRequest{ - ModifyPolicyPayload: fleet.ModifyPolicyPayload{ - Name: ptr.String(tpNameNew), - Query: ptr.String("select * from osquery_info;"), - Description: ptr.String("Some description updated"), - Resolution: ptr.String("some team resolution updated"), - }, - } - mtpResp := modifyTeamPolicyResponse{} - s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/policies/%d", team1.ID, tpResp.Policy.ID), mtpParams, http.StatusOK, &mtpResp) + + response := s.DoRaw("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/policies/%d", team1.ID, tpResp.Policy.ID), []byte(fmt.Sprintf(`{ + "name": "%s", + "query": "select * from osquery_info;", + "description": "Some description updated", + "resolution": "some team resolution updated" + }`, tpNameNew)), http.StatusOK) + var mtpResp modifyGlobalPolicyResponse + responseBody, err := io.ReadAll(response.Body) + require.NoError(t, err) + err = json.Unmarshal(responseBody, &mtpResp) + require.NoError(t, err) + require.NotNil(t, mtpResp.Policy) assert.Equal(t, tpNameNew, mtpResp.Policy.Name) assert.Equal(t, "select * from osquery_info;", mtpResp.Policy.Query) @@ -12663,5 +12670,4 @@ func (s *integrationTestSuite) TestSecretVariables() { require.NoError(t, err) require.Len(t, secrets, 1) assert.Equal(t, "value", secrets[0].Value) - } diff --git a/server/service/integration_enterprise_test.go b/server/service/integration_enterprise_test.go index 06cf5c9c6d..7f3bbea59c 100644 --- a/server/service/integration_enterprise_test.go +++ b/server/service/integration_enterprise_test.go @@ -11696,14 +11696,14 @@ func (s *integrationEnterpriseTestSuite) TestBatchSetSoftwareInstallersWithPolic mtplr := modifyTeamPolicyResponse{} s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/policies/%d", team1.ID, policy1Team1.ID), modifyTeamPolicyRequest{ ModifyPolicyPayload: fleet.ModifyPolicyPayload{ - SoftwareTitleID: &rubyDebTitleID, + SoftwareTitleID: optjson.Any[uint]{Set: true, Valid: true, Value: rubyDebTitleID}, }, }, http.StatusOK, &mtplr) // Associate ruby.deb in team2 to policy2Team2. s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/policies/%d", team2.ID, policy2Team2.ID), modifyTeamPolicyRequest{ ModifyPolicyPayload: fleet.ModifyPolicyPayload{ - SoftwareTitleID: &rubyDebTitleID, + SoftwareTitleID: optjson.Any[uint]{Set: true, Valid: true, Value: rubyDebTitleID}, }, }, http.StatusOK, &mtplr) @@ -14211,28 +14211,28 @@ func (s *integrationEnterpriseTestSuite) TestPolicyAutomationsSoftwareInstallers mtplr := modifyTeamPolicyResponse{} s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/policies/%d", team1.ID, policy1Team1.ID), modifyTeamPolicyRequest{ ModifyPolicyPayload: fleet.ModifyPolicyPayload{ - SoftwareTitleID: ptr.Uint(999_999), + SoftwareTitleID: optjson.Any[uint]{Set: true, Valid: true, Value: 999_999}, }, }, http.StatusBadRequest, &mtplr) // Attempt to associate to a software title without associated installer. mtplr = modifyTeamPolicyResponse{} s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/policies/%d", team1.ID, policy1Team1.ID), modifyTeamPolicyRequest{ ModifyPolicyPayload: fleet.ModifyPolicyPayload{ - SoftwareTitleID: ptr.Uint(foobarAppTitleID), + SoftwareTitleID: optjson.Any[uint]{Set: true, Valid: true, Value: foobarAppTitleID}, }, }, http.StatusBadRequest, &mtplr) // Attempt to associate vppApp to policy1Team1 which should fail because we only allow associating software installers. mtplr = modifyTeamPolicyResponse{} s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/policies/%d", team1.ID, policy1Team1.ID), modifyTeamPolicyRequest{ ModifyPolicyPayload: fleet.ModifyPolicyPayload{ - SoftwareTitleID: &vppAppTitleID, + SoftwareTitleID: optjson.Any[uint]{Set: true, Valid: true, Value: vppAppTitleID}, }, }, http.StatusBadRequest, &mtplr) // Associate dummy_installer.pkg to policy1Team1. mtplr = modifyTeamPolicyResponse{} s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/policies/%d", team1.ID, policy1Team1.ID), modifyTeamPolicyRequest{ ModifyPolicyPayload: fleet.ModifyPolicyPayload{ - SoftwareTitleID: &dummyInstallerPkgTitleID, + SoftwareTitleID: optjson.Any[uint]{Set: true, Valid: true, Value: dummyInstallerPkgTitleID}, }, }, http.StatusOK, &mtplr) // Change name only (to test not setting a software_title_id). @@ -14249,13 +14249,33 @@ func (s *integrationEnterpriseTestSuite) TestPolicyAutomationsSoftwareInstallers mtplr = modifyTeamPolicyResponse{} s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/policies/%d", team1.ID, policy1Team1.ID), modifyTeamPolicyRequest{ ModifyPolicyPayload: fleet.ModifyPolicyPayload{ - SoftwareTitleID: ptr.Uint(0), + SoftwareTitleID: optjson.Any[uint]{Set: true, Valid: true, Value: 0}, }, }, http.StatusOK, &mtplr) policy1Team1, err = s.ds.Policy(ctx, policy1Team1.ID) require.NoError(t, err) require.Nil(t, policy1Team1.SoftwareInstallerID) + // re-add software installer to policy1Team1 + mtplr = modifyTeamPolicyResponse{} + s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/policies/%d", team1.ID, policy1Team1.ID), modifyTeamPolicyRequest{ + ModifyPolicyPayload: fleet.ModifyPolicyPayload{ + SoftwareTitleID: optjson.Any[uint]{Set: true, Valid: true, Value: dummyInstallerPkgTitleID}, + }, + }, http.StatusOK, &mtplr) + policy1Team1, err = s.ds.Policy(ctx, policy1Team1.ID) + require.NoError(t, err) + require.NotNil(t, policy1Team1.SoftwareInstallerID) + require.Equal(t, dummyInstallerPkgInstallerID, *policy1Team1.SoftwareInstallerID) + // Set to null to disable + mtplr = modifyTeamPolicyResponse{} + s.DoRaw("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/policies/%d", team1.ID, policy1Team1.ID), []byte(`{ + "software_title_id": null + }`), http.StatusOK) + policy1Team1, err = s.ds.Policy(ctx, policy1Team1.ID) + require.NoError(t, err) + require.Nil(t, policy1Team1.SoftwareInstallerID) + host1LastInstall, err := s.ds.GetHostLastInstallData(ctx, host1Team1.ID, dummyInstallerPkgInstallerID) require.NoError(t, err) require.Nil(t, host1LastInstall) @@ -14288,7 +14308,7 @@ func (s *integrationEnterpriseTestSuite) TestPolicyAutomationsSoftwareInstallers mtplr = modifyTeamPolicyResponse{} s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/policies/%d", team1.ID, policy1Team1.ID), modifyTeamPolicyRequest{ ModifyPolicyPayload: fleet.ModifyPolicyPayload{ - SoftwareTitleID: &dummyInstallerPkgTitleID, + SoftwareTitleID: optjson.Any[uint]{Set: true, Valid: true, Value: dummyInstallerPkgTitleID}, }, }, http.StatusOK, &mtplr) policy1Team1, err = s.ds.Policy(ctx, policy1Team1.ID) @@ -14337,7 +14357,7 @@ func (s *integrationEnterpriseTestSuite) TestPolicyAutomationsSoftwareInstallers mtplr = modifyTeamPolicyResponse{} s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/policies/%d", team1.ID, policy1Team1.ID), modifyTeamPolicyRequest{ ModifyPolicyPayload: fleet.ModifyPolicyPayload{ - SoftwareTitleID: &rubyDebTitleID, + SoftwareTitleID: optjson.Any[uint]{Set: true, Valid: true, Value: rubyDebTitleID}, }, }, http.StatusOK, &mtplr) @@ -14363,7 +14383,7 @@ func (s *integrationEnterpriseTestSuite) TestPolicyAutomationsSoftwareInstallers mtplr = modifyTeamPolicyResponse{} s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/policies/%d", team1.ID, policy1Team1.ID), modifyTeamPolicyRequest{ ModifyPolicyPayload: fleet.ModifyPolicyPayload{ - SoftwareTitleID: &dummyInstallerPkgTitleID, + SoftwareTitleID: optjson.Any[uint]{Set: true, Valid: true, Value: dummyInstallerPkgTitleID}, }, }, http.StatusOK, &mtplr) @@ -14371,7 +14391,7 @@ func (s *integrationEnterpriseTestSuite) TestPolicyAutomationsSoftwareInstallers mtplr = modifyTeamPolicyResponse{} s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/policies/%d", team1.ID, policy2Team1.ID), modifyTeamPolicyRequest{ ModifyPolicyPayload: fleet.ModifyPolicyPayload{ - SoftwareTitleID: &rubyDebTitleID, + SoftwareTitleID: optjson.Any[uint]{Set: true, Valid: true, Value: rubyDebTitleID}, }, }, http.StatusOK, &mtplr) @@ -14473,7 +14493,7 @@ func (s *integrationEnterpriseTestSuite) TestPolicyAutomationsSoftwareInstallers mtplr = modifyTeamPolicyResponse{} s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/policies/%d", team2.ID, policy4Team2.ID), modifyTeamPolicyRequest{ ModifyPolicyPayload: fleet.ModifyPolicyPayload{ - SoftwareTitleID: &fleetOsqueryMSITitleID, + SoftwareTitleID: optjson.Any[uint]{Set: true, Valid: true, Value: fleetOsqueryMSITitleID}, }, }, http.StatusOK, &mtplr) @@ -14533,7 +14553,7 @@ func (s *integrationEnterpriseTestSuite) TestPolicyAutomationsSoftwareInstallers mtplr = modifyTeamPolicyResponse{} s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/policies/%d", team2.ID, policy4Team2.ID), modifyTeamPolicyRequest{ ModifyPolicyPayload: fleet.ModifyPolicyPayload{ - SoftwareTitleID: ptr.Uint(0), + SoftwareTitleID: optjson.Any[uint]{Set: true, Valid: true, Value: 0}, }, }, http.StatusOK, &mtplr) @@ -14774,14 +14794,14 @@ func (s *integrationEnterpriseTestSuite) TestPolicyAutomationsScripts() { mtplr := modifyTeamPolicyResponse{} s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/policies/%d", team1.ID, policy1Team1.ID), modifyTeamPolicyRequest{ ModifyPolicyPayload: fleet.ModifyPolicyPayload{ - ScriptID: ptr.Uint(999_999), + ScriptID: optjson.Any[uint]{Set: true, Valid: true, Value: 999_999}, }, }, http.StatusBadRequest, &mtplr) // Associate first script to policy1Team1. mtplr = modifyTeamPolicyResponse{} s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/policies/%d", team1.ID, policy1Team1.ID), modifyTeamPolicyRequest{ ModifyPolicyPayload: fleet.ModifyPolicyPayload{ - ScriptID: &script.ID, + ScriptID: optjson.Any[uint]{Set: true, Valid: true, Value: script.ID}, }, }, http.StatusOK, &mtplr) // Change name only (to test not setting a script_id). @@ -14798,13 +14818,33 @@ func (s *integrationEnterpriseTestSuite) TestPolicyAutomationsScripts() { mtplr = modifyTeamPolicyResponse{} s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/policies/%d", team1.ID, policy1Team1.ID), modifyTeamPolicyRequest{ ModifyPolicyPayload: fleet.ModifyPolicyPayload{ - ScriptID: ptr.Uint(0), + ScriptID: optjson.Any[uint]{Set: true, Valid: true, Value: 0}, }, }, http.StatusOK, &mtplr) policy1Team1, err = s.ds.Policy(ctx, policy1Team1.ID) require.NoError(t, err) require.Nil(t, policy1Team1.ScriptID) + // re-add script to policy1Team1. + mtplr = modifyTeamPolicyResponse{} + s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/policies/%d", team1.ID, policy1Team1.ID), modifyTeamPolicyRequest{ + ModifyPolicyPayload: fleet.ModifyPolicyPayload{ + ScriptID: optjson.Any[uint]{Set: true, Valid: true, Value: script.ID}, + }, + }, http.StatusOK, &mtplr) + policy1Team1, err = s.ds.Policy(ctx, policy1Team1.ID) + require.NoError(t, err) + require.NotNil(t, policy1Team1.ScriptID) + require.Equal(t, script.ID, *policy1Team1.ScriptID) + // set to null to disable + mtplr = modifyTeamPolicyResponse{} + s.DoRaw("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/policies/%d", team1.ID, policy1Team1.ID), []byte(`{ + "script_id": null + }`), http.StatusOK) + policy1Team1, err = s.ds.Policy(ctx, policy1Team1.ID) + require.NoError(t, err) + require.Nil(t, policy1Team1.ScriptID) + // Add some results and stats that should be cleared after updating the script distributedResp := submitDistributedQueryResultsResponse{} s.DoJSONWithoutAuth("POST", "/api/osquery/distributed/write", genDistributedReqWithPolicyResults( @@ -14833,7 +14873,7 @@ func (s *integrationEnterpriseTestSuite) TestPolicyAutomationsScripts() { mtplr = modifyTeamPolicyResponse{} s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/policies/%d", team1.ID, policy1Team1.ID), modifyTeamPolicyRequest{ ModifyPolicyPayload: fleet.ModifyPolicyPayload{ - ScriptID: &script.ID, + ScriptID: optjson.Any[uint]{Set: true, Valid: true, Value: script.ID}, }, }, http.StatusOK, &mtplr) policy1Team1, err = s.ds.Policy(ctx, policy1Team1.ID) @@ -14881,7 +14921,7 @@ func (s *integrationEnterpriseTestSuite) TestPolicyAutomationsScripts() { mtplr = modifyTeamPolicyResponse{} s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/policies/%d", team1.ID, policy1Team1.ID), modifyTeamPolicyRequest{ ModifyPolicyPayload: fleet.ModifyPolicyPayload{ - ScriptID: &winScript.ID, + ScriptID: optjson.Any[uint]{Set: true, Valid: true, Value: winScript.ID}, }, }, http.StatusOK, &mtplr) @@ -14907,7 +14947,7 @@ func (s *integrationEnterpriseTestSuite) TestPolicyAutomationsScripts() { mtplr = modifyTeamPolicyResponse{} s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/policies/%d", team1.ID, policy1Team1.ID), modifyTeamPolicyRequest{ ModifyPolicyPayload: fleet.ModifyPolicyPayload{ - ScriptID: &script.ID, + ScriptID: optjson.Any[uint]{Set: true, Valid: true, Value: script.ID}, }, }, http.StatusOK, &mtplr) @@ -14915,7 +14955,7 @@ func (s *integrationEnterpriseTestSuite) TestPolicyAutomationsScripts() { mtplr = modifyTeamPolicyResponse{} s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/policies/%d", team1.ID, policy2Team1.ID), modifyTeamPolicyRequest{ ModifyPolicyPayload: fleet.ModifyPolicyPayload{ - ScriptID: &winScript.ID, + ScriptID: optjson.Any[uint]{Set: true, Valid: true, Value: winScript.ID}, }, }, http.StatusOK, &mtplr) @@ -15002,7 +15042,7 @@ func (s *integrationEnterpriseTestSuite) TestPolicyAutomationsScripts() { mtplr = modifyTeamPolicyResponse{} s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/policies/%d", team2.ID, policy4Team2.ID), modifyTeamPolicyRequest{ ModifyPolicyPayload: fleet.ModifyPolicyPayload{ - ScriptID: &psScript.ID, + ScriptID: optjson.Any[uint]{Set: true, Valid: true, Value: psScript.ID}, }, }, http.StatusOK, &mtplr) @@ -15024,7 +15064,7 @@ func (s *integrationEnterpriseTestSuite) TestPolicyAutomationsScripts() { mtplr = modifyTeamPolicyResponse{} s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/policies/%d", team2.ID, policy4Team2.ID), modifyTeamPolicyRequest{ ModifyPolicyPayload: fleet.ModifyPolicyPayload{ - ScriptID: ptr.Uint(0), + ScriptID: optjson.Any[uint]{Set: true, Valid: true, Value: 0}, }, }, http.StatusOK, &mtplr) diff --git a/server/service/team_policies.go b/server/service/team_policies.go index 12427dc528..a3d0320666 100644 --- a/server/service/team_policies.go +++ b/server/service/team_policies.go @@ -515,8 +515,8 @@ func (svc *Service) modifyPolicy(ctx context.Context, teamID *uint, id uint, p f policy.FailingHostCount = 0 policy.PassingHostCount = 0 } - if p.SoftwareTitleID != nil { - softwareInstallerID, err := svc.deduceSoftwareInstallerIDFromTitleID(ctx, teamID, p.SoftwareTitleID) + if p.SoftwareTitleID.Set { + softwareInstallerID, err := svc.deduceSoftwareInstallerIDFromTitleID(ctx, teamID, &p.SoftwareTitleID.Value) if err != nil { return nil, err } @@ -529,19 +529,19 @@ func (svc *Service) modifyPolicy(ctx context.Context, teamID *uint, id uint, p f } policy.SoftwareInstallerID = softwareInstallerID } - if p.ScriptID != nil { // indicates that script ID is changing, but might be to 0 to remove + if p.ScriptID.Set { // indicates that script ID is changing, but might be to 0 to remove // If the associated script is changed (or it's set and the policy didn't have an associated script) // then we clear the results of the policy so that automation can be triggered upon failure // (automation is currently triggered on the first failure or when it goes from passing to failure). - if *p.ScriptID != 0 && (policy.ScriptID == nil || *policy.ScriptID != *p.ScriptID) { + if p.ScriptID.Value != 0 && (policy.ScriptID == nil || *policy.ScriptID != p.ScriptID.Value) { removeAllMemberships = true removeStats = true } - if *p.ScriptID == 0 { + if p.ScriptID.Value == 0 { policy.ScriptID = nil } else { - policy.ScriptID = p.ScriptID + policy.ScriptID = &p.ScriptID.Value } }