Record activity when the macos minimum version requirement is edited (#9594)

This commit is contained in:
Martin Angers 2023-01-31 17:36:18 -05:00 committed by GitHub
parent 68673cfa6a
commit c805ea2154
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 229 additions and 51 deletions

View file

@ -0,0 +1 @@
* Added an activity `edited_macos_min_version` when the required minimum macOS version is updated.

View file

@ -43,7 +43,7 @@ This activity contains the following fields:
```json
{
"pack_id": 123,
"pack_id": 123,
"pack_name": "foo"
}
```
@ -60,7 +60,7 @@ This activity contains the following fields:
```json
{
"pack_id": 123,
"pack_id": 123,
"pack_name": "foo"
}
```
@ -98,7 +98,7 @@ This activity contains the following fields:
```json
{
"policy_id": 123,
"policy_id": 123,
"policy_name": "foo"
}
```
@ -115,7 +115,7 @@ This activity contains the following fields:
```json
{
"policy_id": 123,
"policy_id": 123,
"policy_name": "foo"
}
```
@ -132,7 +132,7 @@ This activity contains the following fields:
```json
{
"policy_id": 123,
"policy_id": 123,
"policy_name": "foo"
}
```
@ -188,7 +188,7 @@ This activity contains the following fields:
```json
{
"query_id": 123,
"query_id": 123,
"query_name": "foo"
}
```
@ -205,7 +205,7 @@ This activity contains the following fields:
```json
{
"query_id": 123,
"query_id": 123,
"query_name": "foo"
}
```
@ -275,7 +275,7 @@ This activity contains the following fields:
```json
{
"team_id": 123,
"team_id": 123,
"team_name": "foo"
}
```
@ -292,7 +292,7 @@ This activity contains the following fields:
```json
{
"team_id": 123,
"team_id": 123,
"team_name": "foo"
}
```
@ -311,7 +311,7 @@ This activity contains a field "teams" where each item contains the team details
{
"teams": [
{
"id": 123,
"id": 123,
"name": "foo"
}
]
@ -331,7 +331,7 @@ This activity contains the following fields:
```json
{
"team_id": 123,
"team_id": 123,
"team_name": "foo",
"global": false
}
@ -350,7 +350,7 @@ This activity contains the following fields:
```json
{
"targets_count": 5000,
"targets_count": 5000,
"query_sql": "SELECT * from osquery_info;",
"query_name": "foo"
}
@ -530,6 +530,7 @@ Generated when a host is enrolled in Fleet's MDM.
This activity contains the following fields:
- "host_serial": Serial number of the host.
- "host_display_name": Display name of the host.
- "installed_from_dep": Whether the host was enrolled via DEP.
#### Example
@ -537,6 +538,7 @@ This activity contains the following fields:
```json
{
"host_serial": "C08VQ2AXHT96",
"host_display_name": "MacBookPro16,1 (C08VQ2AXHT96)",
"installed_from_dep": true
}
```
@ -547,6 +549,7 @@ Generated when a host is unenrolled from Fleet's MDM.
This activity contains the following fields:
- "host_serial": Serial number of the host.
- "host_display_name": Display name of the host.
- "installed_from_dep": Whether the host was enrolled via DEP.
#### Example
@ -554,10 +557,32 @@ This activity contains the following fields:
```json
{
"host_serial": "C08VQ2AXHT96",
"host_display_name": "MacBookPro16,1 (C08VQ2AXHT96)",
"installed_from_dep": true
}
```
### Type `edited_macos_min_version`
Generated when the minimum required macOS version is modified.
This activity contains the following fields:
- "team_id": The ID of the team that the minimum macOS version applies to, null if it applies to devices that are not in a team.
- "team_name": The name of the team that the minimum macOS version applies to, null if it applies to devices that are not in a team.
- "minimum_version": The minimum macOS version required, empty if the requirement was removed.
- "deadline": The deadline by which the minimum version requirement must be applied, empty if the requirement was removed.
#### Example
```json
{
"team_id": 3,
"team_name": "Workstations",
"minimum_version": "13.0.1",
"deadline": "2023-06-01"
}
```
<meta name="pageOrderInSection" value="1400">

View file

@ -25,7 +25,7 @@ SELECT 1 FROM disk_encryption WHERE user_uuid IS NOT "" AND filevault_status = '
## disk_encryption_linux
- Platforms: linux, ubuntu, debian, rhel, centos, sles, kali, gentoo, amzn, pop, arch, linuxmint, void
- Platforms: linux, ubuntu, debian, rhel, centos, sles, kali, gentoo, amzn, pop, arch, linuxmint, void, nixos
- Query:
@ -45,7 +45,7 @@ SELECT 1 FROM bitlocker_info WHERE drive_letter = 'C:' AND protection_status = 1
## disk_space_unix
- Platforms: linux, ubuntu, debian, rhel, centos, sles, kali, gentoo, amzn, pop, arch, linuxmint, void, darwin
- Platforms: linux, ubuntu, debian, rhel, centos, sles, kali, gentoo, amzn, pop, arch, linuxmint, void, nixos, darwin
- Query:
@ -166,7 +166,7 @@ select version, errors, warnings from munki_info;
## network_interface_unix
- Platforms: linux, ubuntu, debian, rhel, centos, sles, kali, gentoo, amzn, pop, arch, linuxmint, void, darwin
- Platforms: linux, ubuntu, debian, rhel, centos, sles, kali, gentoo, amzn, pop, arch, linuxmint, void, nixos, darwin
- Query:
@ -230,7 +230,7 @@ SELECT version FROM orbit_info
## os_unix_like
- Platforms: linux, ubuntu, debian, rhel, centos, sles, kali, gentoo, amzn, pop, arch, linuxmint, void, darwin
- Platforms: linux, ubuntu, debian, rhel, centos, sles, kali, gentoo, amzn, pop, arch, linuxmint, void, nixos, darwin
- Query:
@ -328,7 +328,7 @@ SELECT *,
## software_linux
- Platforms: linux, ubuntu, debian, rhel, centos, sles, kali, gentoo, amzn, pop, arch, linuxmint, void
- Platforms: linux, ubuntu, debian, rhel, centos, sles, kali, gentoo, amzn, pop, arch, linuxmint, void, nixos
- Query:

View file

@ -103,10 +103,12 @@ func (svc *Service) ModifyTeam(ctx context.Context, teamID uint, payload fleet.T
team.Config.WebhookSettings = *payload.WebhookSettings
}
var macOSMinVersionUpdated bool
if payload.MDM != nil {
if err := payload.MDM.MacOSUpdates.Validate(); err != nil {
return nil, fleet.NewInvalidArgumentError("macos_updates", err.Error())
}
macOSMinVersionUpdated = team.Config.MDM.MacOSUpdates != payload.MDM.MacOSUpdates
team.Config.MDM = *payload.MDM
}
@ -144,7 +146,25 @@ func (svc *Service) ModifyTeam(ctx context.Context, teamID uint, payload fleet.T
}
}
return svc.ds.SaveTeam(ctx, team)
team, err = svc.ds.SaveTeam(ctx, team)
if err != nil {
return nil, err
}
if macOSMinVersionUpdated {
if err := svc.ds.NewActivity(
ctx,
authz.UserFromContext(ctx),
fleet.ActivityTypeEditedMacOSMinVersion{
TeamID: &team.ID,
TeamName: &team.Name,
MinimumVersion: team.Config.MDM.MacOSUpdates.MinimumVersion,
Deadline: team.Config.MDM.MacOSUpdates.Deadline,
},
); err != nil {
return nil, ctxerr.Wrap(ctx, err, "create activity for team macos min version edited")
}
}
return team, err
}
func (svc *Service) ModifyTeamAgentOptions(ctx context.Context, teamID uint, teamOptions json.RawMessage, applyOptions fleet.ApplySpecOptions) (*fleet.Team, error) {
@ -496,6 +516,9 @@ func (svc *Service) ApplyTeamSpecs(ctx context.Context, specs []*fleet.TeamSpec,
}
if len(details) > 0 {
// TODO(mna): we don't create separate activities for e.g. edited agent
// options when applying team specs, so I didn't create explicit activities
// for min macos version either. Not sure if that's what we want.
if err := svc.ds.NewActivity(
ctx,
authz.UserFromContext(ctx),

View file

@ -45,6 +45,8 @@ var ActivityDetailsList = []ActivityDetails{
ActivityTypeMDMEnrolled{},
ActivityTypeMDMUnenrolled{},
ActivityTypeEditedMacOSMinVersion{},
}
type ActivityDetails interface {
@ -68,7 +70,7 @@ func (a ActivityTypeCreatedPack) Documentation() (activity string, details strin
`This activity contains the following fields:
- "pack_id": the id of the created pack.
- "pack_name": the name of the created pack.`, `{
"pack_id": 123,
"pack_id": 123,
"pack_name": "foo"
}`
}
@ -87,7 +89,7 @@ func (a ActivityTypeEditedPack) Documentation() (activity string, details string
`This activity contains the following fields:
- "pack_id": the id of the edited pack.
- "pack_name": the name of the edited pack.`, `{
"pack_id": 123,
"pack_id": 123,
"pack_name": "foo"
}`
}
@ -133,7 +135,7 @@ func (a ActivityTypeCreatedPolicy) Documentation() (activity string, details str
`This activity contains the following fields:
- "policy_id": the ID of the created policy.
- "policy_name": the name of the created policy.`, `{
"policy_id": 123,
"policy_id": 123,
"policy_name": "foo"
}`
}
@ -152,7 +154,7 @@ func (a ActivityTypeEditedPolicy) Documentation() (activity string, details stri
`This activity contains the following fields:
- "policy_id": the ID of the edited policy.
- "policy_name": the name of the edited policy.`, `{
"policy_id": 123,
"policy_id": 123,
"policy_name": "foo"
}`
}
@ -171,7 +173,7 @@ func (a ActivityTypeDeletedPolicy) Documentation() (activity string, details str
`This activity contains the following fields:
- "policy_id": the ID of the deleted policy.
- "policy_name": the name of the deleted policy.`, `{
"policy_id": 123,
"policy_id": 123,
"policy_name": "foo"
}`
}
@ -230,7 +232,7 @@ func (a ActivityTypeCreatedSavedQuery) Documentation() (activity string, details
`This activity contains the following fields:
- "query_id": the ID of the created query.
- "query_name": the name of the created query.`, `{
"query_id": 123,
"query_id": 123,
"query_name": "foo"
}`
}
@ -249,7 +251,7 @@ func (a ActivityTypeEditedSavedQuery) Documentation() (activity string, details
`This activity contains the following fields:
- "query_id": the ID of the query being edited.
- "query_name": the name of the query being edited.`, `{
"query_id": 123,
"query_id": 123,
"query_name": "foo"
}`
}
@ -324,7 +326,7 @@ func (a ActivityTypeCreatedTeam) Documentation() (activity string, details strin
`This activity contains the following fields:
- "team_id": unique ID of the created team.
- "team_name": the name of the created team.`, `{
"team_id": 123,
"team_id": 123,
"team_name": "foo"
}`
}
@ -343,7 +345,7 @@ func (a ActivityTypeDeletedTeam) Documentation() (activity string, details strin
`This activity contains the following fields:
- "team_id": unique ID of the deleted team.
- "team_name": the name of the deleted team.`, `{
"team_id": 123,
"team_id": 123,
"team_name": "foo"
}`
}
@ -368,7 +370,7 @@ func (a ActivityTypeAppliedSpecTeam) Documentation() (activity string, details s
- "name": Name of the team.`, `{
"teams": [
{
"id": 123,
"id": 123,
"name": "foo"
}
]
@ -391,7 +393,7 @@ func (a ActivityTypeEditedAgentOptions) Documentation() (activity string, detail
- "global": "true" if the user updated the global agent options, "false" if the agent options of a team were updated.
- "team_id": unique ID of the team for which the agent options were updated (null if global is true).
- "team_name": the name of the team for which the agent options were updated (null if global is true).`, `{
"team_id": 123,
"team_id": 123,
"team_name": "foo",
"global": false
}`
@ -413,7 +415,7 @@ func (a ActivityTypeLiveQuery) Documentation() (activity string, details string,
- "targets_count": Number of hosts where the live query was targeted to run.
- "query_sql": The SQL query to run on hosts.
- "query_name": Name of the query (this field is not set if this was not a saved query).`, `{
"targets_count": 5000,
"targets_count": 5000,
"query_sql": "SELECT * from osquery_info;",
"query_name": "foo"
}`
@ -442,6 +444,11 @@ type Activity struct {
Streamed *bool `json:"-" db:"streamed"`
}
// AuthzType implement AuthzTyper to be able to verify access to activities
func (*Activity) AuthzType() string {
return "activity"
}
type ActivityTypeUserLoggedIn struct {
PublicIP string `json:"public_ip"`
}
@ -677,7 +684,27 @@ func (a ActivityTypeMDMUnenrolled) Documentation() (activity string, details str
}`
}
// AuthzType implement AuthzTyper to be able to verify access to activities
func (*Activity) AuthzType() string {
return "activity"
type ActivityTypeEditedMacOSMinVersion struct {
TeamID *uint `json:"team_id"`
TeamName *string `json:"team_name"`
MinimumVersion string `json:"minimum_version"`
Deadline string `json:"deadline"`
}
func (a ActivityTypeEditedMacOSMinVersion) ActivityName() string {
return "edited_macos_min_version"
}
func (a ActivityTypeEditedMacOSMinVersion) Documentation() (activity string, details string, detailsExample string) {
return `Generated when the minimum required macOS version is modified.`,
`This activity contains the following fields:
- "team_id": The ID of the team that the minimum macOS version applies to, null if it applies to devices that are not in a team.
- "team_name": The name of the team that the minimum macOS version applies to, null if it applies to devices that are not in a team.
- "minimum_version": The minimum macOS version required, empty if the requirement was removed.
- "deadline": The deadline by which the minimum version requirement must be applied, empty if the requirement was removed.`, `{
"team_id": 3,
"team_name": "Workstations",
"minimum_version": "13.0.1",
"deadline": "2023-06-01"
}`
}

View file

@ -429,7 +429,22 @@ func (svc *Service) ModifyAppConfig(ctx context.Context, p []byte, applyOpts fle
Global: true,
},
); err != nil {
return nil, ctxerr.Wrap(ctx, err, "create activity for app config modification")
return nil, ctxerr.Wrap(ctx, err, "create activity for app config agent options modification")
}
}
// if the macOS minimum version requirement changed, create the corresponding
// activity
if oldAppConfig.MDM.MacOSUpdates != appConfig.MDM.MacOSUpdates {
if err := svc.ds.NewActivity(
ctx,
authz.UserFromContext(ctx),
fleet.ActivityTypeEditedMacOSMinVersion{
MinimumVersion: appConfig.MDM.MacOSUpdates.MinimumVersion,
Deadline: appConfig.MDM.MacOSUpdates.Deadline,
},
); err != nil {
return nil, ctxerr.Wrap(ctx, err, "create activity for app config macos min version modification")
}
}

View file

@ -115,12 +115,7 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
require.Equal(t, mdm, team.Config.MDM)
// an activity was created for team spec applied
var listActivities listActivitiesResponse
s.DoJSON("GET", "/api/latest/fleet/activities", nil, http.StatusOK, &listActivities, "order_key", "id", "order_direction", "desc")
require.True(t, len(listActivities.Activities) > 0)
assert.Equal(t, fleet.ActivityTypeAppliedSpecTeam{}.ActivityName(), listActivities.Activities[0].Type)
require.NotNil(t, listActivities.Activities[0].Details)
assert.JSONEq(t, fmt.Sprintf(`{"teams": [{"id": %d, "name": %q}]}`, team.ID, team.Name), string(*listActivities.Activities[0].Details))
s.lastActivityMatches(fleet.ActivityTypeAppliedSpecTeam{}.ActivityName(), fmt.Sprintf(`{"teams": [{"id": %d, "name": %q}]}`, team.ID, team.Name), 0)
// dry-run with invalid agent options
agentOpts = json.RawMessage(`{"config": {"nope": 1}}`)
@ -231,11 +226,7 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
require.Equal(t, appConfig.Features, team.Config.Features)
// an activity was created for the newly created team via the applied spec
s.DoJSON("GET", "/api/latest/fleet/activities", nil, http.StatusOK, &listActivities, "order_key", "id", "order_direction", "desc")
require.True(t, len(listActivities.Activities) > 0)
assert.Equal(t, fleet.ActivityTypeAppliedSpecTeam{}.ActivityName(), listActivities.Activities[0].Type)
require.NotNil(t, listActivities.Activities[0].Details)
assert.JSONEq(t, fmt.Sprintf(`{"teams": [{"id": %d, "name": %q}]}`, team.ID, team.Name), string(*listActivities.Activities[0].Details))
s.lastActivityMatches(fleet.ActivityTypeAppliedSpecTeam{}.ActivityName(), fmt.Sprintf(`{"teams": [{"id": %d, "name": %q}]}`, team.ID, team.Name), 0)
// updates
teamSpecs = applyTeamSpecsRequest{Specs: []*fleet.TeamSpec{{
@ -680,12 +671,7 @@ func (s *integrationEnterpriseTestSuite) TestTeamEndpoints() {
require.Contains(t, string(*tmResp.Team.Config.AgentOptions), `"aws_debug": true`) // left unchanged
// list activities, it should have created one for edited_agent_options
var listActivities listActivitiesResponse
s.DoJSON("GET", "/api/latest/fleet/activities", nil, http.StatusOK, &listActivities, "order_key", "id", "order_direction", "desc")
require.True(t, len(listActivities.Activities) > 0)
assert.Equal(t, fleet.ActivityTypeEditedAgentOptions{}.ActivityName(), listActivities.Activities[0].Type)
require.NotNil(t, listActivities.Activities[0].Details)
assert.JSONEq(t, fmt.Sprintf(`{"global": false, "team_id": %d, "team_name": %q}`, tm1ID, team.Name), string(*listActivities.Activities[0].Details))
s.lastActivityMatches(fleet.ActivityTypeEditedAgentOptions{}.ActivityName(), fmt.Sprintf(`{"global": false, "team_id": %d, "team_name": %q}`, tm1ID, team.Name), 0)
// modify team agent options - unknown team
tmResp.Team = nil
@ -1262,6 +1248,7 @@ func (s *integrationEnterpriseTestSuite) TestMacOSUpdatesConfig() {
}, http.StatusOK, &tmResp)
require.Equal(t, "10.15.0", tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion)
require.Equal(t, "2021-01-01", tmResp.Team.Config.MDM.MacOSUpdates.Deadline)
s.lastActivityMatches(fleet.ActivityTypeEditedMacOSMinVersion{}.ActivityName(), fmt.Sprintf(`{"team_id": %d, "team_name": %q, "minimum_version": "10.15.0", "deadline": "2021-01-01"}`, team.ID, team.Name), 0)
// only update the deadline
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), fleet.TeamPayload{
@ -1274,16 +1261,20 @@ func (s *integrationEnterpriseTestSuite) TestMacOSUpdatesConfig() {
}, http.StatusOK, &tmResp)
require.Equal(t, "10.15.0", tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion)
require.Equal(t, "2025-10-01", tmResp.Team.Config.MDM.MacOSUpdates.Deadline)
lastActivity := s.lastActivityMatches(fleet.ActivityTypeEditedMacOSMinVersion{}.ActivityName(), fmt.Sprintf(`{"team_id": %d, "team_name": %q, "minimum_version": "10.15.0", "deadline": "2025-10-01"}`, team.ID, team.Name), 0)
// sending a nil MacOSUpdate config doesn't modify anything
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), fleet.TeamPayload{MDM: nil}, http.StatusOK, &tmResp)
require.Equal(t, "10.15.0", tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion)
require.Equal(t, "2025-10-01", tmResp.Team.Config.MDM.MacOSUpdates.Deadline)
// no new activity is created
s.lastActivityMatches("", "", lastActivity)
// sending an empty MacOSUpdate empties both fields
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), fleet.TeamPayload{MDM: &fleet.TeamMDM{MacOSUpdates: fleet.MacOSUpdates{}}}, http.StatusOK, &tmResp)
require.Empty(t, tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion)
require.Empty(t, tmResp.Team.Config.MDM.MacOSUpdates.Deadline)
s.lastActivityMatches(fleet.ActivityTypeEditedMacOSMinVersion{}.ActivityName(), fmt.Sprintf(`{"team_id": %d, "team_name": %q, "minimum_version": "", "deadline": ""}`, team.ID, team.Name), 0)
// error checks:
@ -1527,6 +1518,14 @@ func (s *integrationEnterpriseTestSuite) TestDefaultAppleBMTeam() {
func (s *integrationEnterpriseTestSuite) TestMDMMacOSUpdates() {
t := s.T()
// keep the last activity, to detect newly created ones
var activitiesResp listActivitiesResponse
s.DoJSON("GET", "/api/latest/fleet/activities", nil, http.StatusOK, &activitiesResp, "order_key", "a.id", "order_direction", "desc")
var lastActivity uint
if len(activitiesResp.Activities) > 0 {
lastActivity = activitiesResp.Activities[0].ID
}
checkInvalidConfig := func(config string) {
// try to set an invalid config
acResp := appConfigResponse{}
@ -1536,6 +1535,14 @@ func (s *integrationEnterpriseTestSuite) TestMDMMacOSUpdates() {
acResp = appConfigResponse{}
s.DoJSON("GET", "/api/latest/fleet/config", nil, http.StatusOK, &acResp)
require.Equal(t, fleet.MacOSUpdates{}, acResp.MDM.MacOSUpdates)
// no activity got created
activitiesResp = listActivitiesResponse{}
s.DoJSON("GET", "/api/latest/fleet/activities", nil, http.StatusOK, &activitiesResp, "order_key", "a.id", "order_direction", "desc")
require.Condition(t, func() bool {
return (lastActivity == 0 && len(activitiesResp.Activities) == 0) ||
(len(activitiesResp.Activities) > 0 && activitiesResp.Activities[0].ID == lastActivity)
})
}
// missing minimum_version
@ -1589,11 +1596,69 @@ func (s *integrationEnterpriseTestSuite) TestMDMMacOSUpdates() {
require.Equal(t, "12.3.1", acResp.MDM.MacOSUpdates.MinimumVersion)
require.Equal(t, "2022-01-01", acResp.MDM.MacOSUpdates.Deadline)
// edited macos min version activity got created
s.lastActivityMatches(fleet.ActivityTypeEditedMacOSMinVersion{}.ActivityName(), `{"deadline":"2022-01-01", "minimum_version":"12.3.1", "team_id": null, "team_name": null}`, 0)
// get the appconfig
acResp = appConfigResponse{}
s.DoJSON("GET", "/api/latest/fleet/config", nil, http.StatusOK, &acResp)
require.Equal(t, "12.3.1", acResp.MDM.MacOSUpdates.MinimumVersion)
require.Equal(t, "2022-01-01", acResp.MDM.MacOSUpdates.Deadline)
// update the deadline
acResp = appConfigResponse{}
s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{
"mdm": {
"macos_updates": {
"minimum_version": "12.3.1",
"deadline": "2024-01-01"
}
}
}`), http.StatusOK, &acResp)
require.Equal(t, "12.3.1", acResp.MDM.MacOSUpdates.MinimumVersion)
require.Equal(t, "2024-01-01", acResp.MDM.MacOSUpdates.Deadline)
// another edited macos min version activity got created
lastActivity = s.lastActivityMatches(fleet.ActivityTypeEditedMacOSMinVersion{}.ActivityName(), `{"deadline":"2024-01-01", "minimum_version":"12.3.1", "team_id": null, "team_name": null}`, 0)
// update something unrelated - the transparency url
acResp = appConfigResponse{}
s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{"fleet_desktop":{"transparency_url": "customURL"}}`), http.StatusOK, &acResp)
// no activity got created
s.lastActivityMatches("", ``, lastActivity)
// clear the macos requirement
acResp = appConfigResponse{}
s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{
"mdm": {
"macos_updates": {
"minimum_version": "",
"deadline": ""
}
}
}`), http.StatusOK, &acResp)
require.Empty(t, acResp.MDM.MacOSUpdates.MinimumVersion)
require.Empty(t, acResp.MDM.MacOSUpdates.Deadline)
// edited macos min version activity got created with empty requirement
lastActivity = s.lastActivityMatches(fleet.ActivityTypeEditedMacOSMinVersion{}.ActivityName(), `{"deadline":"", "minimum_version":"", "team_id": null, "team_name": null}`, 0)
// update again with empty macos requirement
acResp = appConfigResponse{}
s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{
"mdm": {
"macos_updates": {
"minimum_version": "",
"deadline": ""
}
}
}`), http.StatusOK, &acResp)
require.Empty(t, acResp.MDM.MacOSUpdates.MinimumVersion)
require.Empty(t, acResp.MDM.MacOSUpdates.Deadline)
// no activity got created
s.lastActivityMatches("", ``, lastActivity)
}
func (s *integrationEnterpriseTestSuite) TestSSOJITProvisioning() {
@ -2192,6 +2257,28 @@ func (s *integrationEnterpriseTestSuite) TestOrbitConfigNudgeSettings() {
require.Equal(t, wantCfg.OSVersionRequirements[0].RequiredInstallationDate.String(), "2022-01-04 04:00:00 +0000 UTC")
}
// gets the latest activity and checks that it matches any provided properties.
// empty string or 0 id means do not check that property. It returns the ID of that
// latest activity.
func (s *integrationEnterpriseTestSuite) lastActivityMatches(name, details string, id uint) uint {
var listActivities listActivitiesResponse
s.DoJSON("GET", "/api/latest/fleet/activities", nil, http.StatusOK, &listActivities, "order_key", "a.id", "order_direction", "desc", "per_page", "1")
require.True(s.T(), len(listActivities.Activities) > 0)
act := listActivities.Activities[0]
if name != "" {
assert.Equal(s.T(), name, act.Type)
}
if details != "" {
require.NotNil(s.T(), act.Details)
assert.JSONEq(s.T(), details, string(*act.Details))
}
if id > 0 {
assert.Equal(s.T(), id, act.ID)
}
return act.ID
}
// allEqual compares all fields of a struct.
// If a field is a pointer on one side but not on the other, then it follows that pointer. This is useful for optional
// arguments.