From f27fcddd55f779e13d93b22c9ac0be938fd870f8 Mon Sep 17 00:00:00 2001 From: Martin Angers Date: Tue, 6 Jun 2023 14:31:33 -0400 Subject: [PATCH] Prevent clearing macos updates settings when applying/modifying a team without those settings (#12160) --- changes/issue-12058-fix-apply-teams | 1 + cmd/fleetctl/apply_test.go | 107 +++++- cmd/fleetctl/get_test.go | 9 +- .../expectedGetConfigAppConfigJson.json | 222 +++++------ .../expectedGetConfigAppConfigYaml.yml | 4 +- ...ectedGetConfigIncludeServerConfigJson.json | 346 +++++++++--------- ...pectedGetConfigIncludeServerConfigYaml.yml | 4 +- .../testdata/expectedGetTeamsJson.json | 4 +- .../testdata/expectedGetTeamsYaml.yml | 4 +- .../macosSetupExpectedAppConfigEmpty.yml | 4 +- .../macosSetupExpectedAppConfigSet.yml | 4 +- .../macosSetupExpectedTeam1And2Empty.yml | 8 +- .../macosSetupExpectedTeam1And2Set.yml | 8 +- .../testdata/macosSetupExpectedTeam1Empty.yml | 4 +- ee/server/service/teams.go | 15 +- orbit/pkg/update/nudge_test.go | 3 +- .../cached_mysql/cached_mysql_test.go | 9 +- server/datastore/mysql/schema.sql | 2 +- server/datastore/mysql/teams_test.go | 8 +- server/fleet/app.go | 23 +- server/fleet/app_test.go | 37 +- server/fleet/nudge.go | 4 +- server/service/appconfig.go | 17 +- server/service/appconfig_test.go | 9 +- server/service/integration_enterprise_test.go | 163 ++++++--- server/service/orbit.go | 8 +- 26 files changed, 590 insertions(+), 437 deletions(-) create mode 100644 changes/issue-12058-fix-apply-teams diff --git a/changes/issue-12058-fix-apply-teams b/changes/issue-12058-fix-apply-teams new file mode 100644 index 0000000000..5330a72e0d --- /dev/null +++ b/changes/issue-12058-fix-apply-teams @@ -0,0 +1 @@ +* Fixed a bug with applying team specs via `fleetctl apply` and updating a team via the `PATCH /api/latest/fleet/mdm/teams/{id}` endpoint so that the MDM updates settings (`minimum_version` and `deadline`) are not cleared if not provided in the payload. diff --git a/cmd/fleetctl/apply_test.go b/cmd/fleetctl/apply_test.go index c1fd52e72c..83e8db4d6c 100644 --- a/cmd/fleetctl/apply_test.go +++ b/cmd/fleetctl/apply_test.go @@ -143,7 +143,7 @@ func TestApplyTeamSpecs(t *testing.T) { agentOpts := json.RawMessage(`{"config":{"foo":"bar"},"overrides":{"platforms":{"darwin":{"foo":"override"}}}}`) ds.AppConfigFunc = func(ctx context.Context) (*fleet.AppConfig, error) { - return &fleet.AppConfig{AgentOptions: &agentOpts}, nil + return &fleet.AppConfig{AgentOptions: &agentOpts, MDM: fleet.MDM{EnabledAndConfigured: true}}, nil } ds.SaveTeamFunc = func(ctx context.Context, team *fleet.Team) (*fleet.Team, error) { @@ -157,6 +157,14 @@ func TestApplyTeamSpecs(t *testing.T) { return nil } + ds.BatchSetMDMAppleProfilesFunc = func(ctx context.Context, tmID *uint, profiles []*fleet.MDMAppleConfigProfile) error { + return nil + } + + ds.BulkSetPendingMDMAppleHostProfilesFunc = func(ctx context.Context, hostIDs, teamIDs, profileIDs []uint, hostUUIDs []string) error { + return nil + } + ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error { return nil } @@ -189,8 +197,8 @@ spec: newAgentOpts := json.RawMessage(`{"config":{"views":{"foo":"bar"}}}`) newMDMSettings := fleet.TeamMDM{ MacOSUpdates: fleet.MacOSUpdates{ - MinimumVersion: "12.3.1", - Deadline: "2011-03-01", + MinimumVersion: optjson.SetString("12.3.1"), + Deadline: optjson.SetString("2011-03-01"), }, } require.Equal(t, "[+] applied 2 teams\n", runAppForTest(t, []string{"apply", "-f", filename})) @@ -202,20 +210,37 @@ spec: assert.True(t, ds.ApplyEnrollSecretsFuncInvoked) ds.ApplyEnrollSecretsFuncInvoked = false - filename = writeTmpYml(t, ` + mobileCfgPath := writeTmpMobileconfig(t, "N1") + filename = writeTmpYml(t, fmt.Sprintf(` apiVersion: v1 kind: team spec: team: name: team1 -`) + mdm: + macos_settings: + custom_settings: + - %s +`, mobileCfgPath)) + + newMDMSettings = fleet.TeamMDM{ + MacOSUpdates: fleet.MacOSUpdates{ + MinimumVersion: optjson.SetString("12.3.1"), + Deadline: optjson.SetString("2011-03-01"), + }, + MacOSSettings: fleet.MacOSSettings{ + CustomSettings: []string{mobileCfgPath}, + }, + } require.Equal(t, "[+] applied 1 teams\n", runAppForTest(t, []string{"apply", "-f", filename})) + // enroll secret not provided, so left unchanged assert.Equal(t, []*fleet.EnrollSecret{{Secret: "AAA"}}, enrolledSecretsCalled[uint(42)]) assert.False(t, ds.ApplyEnrollSecretsFuncInvoked) // agent options not provided, so left unchanged assert.JSONEq(t, string(newAgentOpts), string(*teamsByName["team1"].Config.AgentOptions)) - assert.Equal(t, fleet.TeamMDM{}, teamsByName["team1"].Config.MDM) + // macos updates options not provided, left unchanged, and macos custom settings added + assert.Equal(t, newMDMSettings, teamsByName["team1"].Config.MDM) filename = writeTmpYml(t, ` apiVersion: v1 @@ -237,8 +262,11 @@ spec: newMDMSettings = fleet.TeamMDM{ MacOSUpdates: fleet.MacOSUpdates{ - MinimumVersion: "10.10.10", - Deadline: "1992-03-01", + MinimumVersion: optjson.SetString("10.10.10"), + Deadline: optjson.SetString("1992-03-01"), + }, + MacOSSettings: fleet.MacOSSettings{ // macos settings not provided, so not cleared + CustomSettings: []string{mobileCfgPath}, }, } newAgentOpts = json.RawMessage(`{"config":{"views":{"foo":"qux"}}}`) @@ -255,11 +283,66 @@ spec: team: agent_options: name: team1 + mdm: + macos_updates: + macos_settings: `) require.Equal(t, "[+] applied 1 teams\n", runAppForTest(t, []string{"apply", "-f", filename})) // agent options provided but empty, clears the value assert.Nil(t, teamsByName["team1"].Config.AgentOptions) + // macos settings and updates still the same (not cleared) because only the + // top-level key is provided. + assert.Equal(t, newMDMSettings, teamsByName["team1"].Config.MDM) + // enroll secret not cleared since not provided + assert.Equal(t, []*fleet.EnrollSecret{{Secret: "BBB"}}, enrolledSecretsCalled[uint(42)]) + + filename = writeTmpYml(t, ` +apiVersion: v1 +kind: team +spec: + team: + name: team1 + mdm: + macos_updates: + minimum_version: +`) + + // fails: minimum_version provided empty, but deadline not provided + _, err := runAppNoChecks([]string{"apply", "-f", filename}) + require.ErrorContains(t, err, "deadline is required when minimum_version is provided") + + filename = writeTmpYml(t, ` +apiVersion: v1 +kind: team +spec: + team: + name: team1 + mdm: + macos_updates: + minimum_version: + deadline: + macos_settings: + custom_settings: +`) + + newMDMSettings = fleet.TeamMDM{ + MacOSUpdates: fleet.MacOSUpdates{ + MinimumVersion: optjson.String{Set: true}, + Deadline: optjson.String{Set: true}, + }, + MacOSSettings: fleet.MacOSSettings{ + CustomSettings: []string{}, + }, + } + + require.Equal(t, "[+] applied 1 teams\n", runAppForTest(t, []string{"apply", "-f", filename})) + // agent options still cleared + assert.Nil(t, teamsByName["team1"].Config.AgentOptions) + // macos settings and updates are now cleared. + assert.Equal(t, newMDMSettings, teamsByName["team1"].Config.MDM) + // enroll secret not cleared since not provided + assert.Equal(t, []*fleet.EnrollSecret{{Secret: "BBB"}}, enrolledSecretsCalled[uint(42)]) } func writeTmpYml(t *testing.T, contents string) string { @@ -333,8 +416,8 @@ spec: AppleBMDefaultTeam: "team1", AppleBMTermsExpired: false, MacOSUpdates: fleet.MacOSUpdates{ - MinimumVersion: "12.1.1", - Deadline: "2011-02-01", + MinimumVersion: optjson.SetString("12.1.1"), + Deadline: optjson.SetString("2011-02-01"), }, } assert.Equal(t, "[+] applied fleet config\n", runAppForTest(t, []string{"apply", "-f", name})) @@ -822,8 +905,8 @@ spec: EnableDiskEncryption: false, }, MacOSUpdates: fleet.MacOSUpdates{ - MinimumVersion: "10.10.10", - Deadline: "1992-03-01", + MinimumVersion: optjson.SetString("10.10.10"), + Deadline: optjson.SetString("1992-03-01"), }, }, savedTeam.Config.MDM) assert.Equal(t, []*fleet.EnrollSecret{{Secret: "BBB"}}, teamEnrollSecrets) diff --git a/cmd/fleetctl/get_test.go b/cmd/fleetctl/get_test.go index 0ce34e4d48..9aae4cbaed 100644 --- a/cmd/fleetctl/get_test.go +++ b/cmd/fleetctl/get_test.go @@ -15,6 +15,7 @@ import ( "github.com/ghodss/yaml" + "github.com/fleetdm/fleet/v4/pkg/optjson" "github.com/fleetdm/fleet/v4/pkg/spec" "github.com/fleetdm/fleet/v4/server/config" "github.com/fleetdm/fleet/v4/server/fleet" @@ -157,8 +158,8 @@ func TestGetTeams(t *testing.T) { }, MDM: fleet.TeamMDM{ MacOSUpdates: fleet.MacOSUpdates{ - MinimumVersion: "12.3.1", - Deadline: "2021-12-14", + MinimumVersion: optjson.SetString("12.3.1"), + Deadline: optjson.SetString("2021-12-14"), }, }, }, @@ -1638,8 +1639,8 @@ func TestGetTeamsYAMLAndApply(t *testing.T) { }, MDM: fleet.TeamMDM{ MacOSUpdates: fleet.MacOSUpdates{ - MinimumVersion: "12.3.1", - Deadline: "2021-12-14", + MinimumVersion: optjson.SetString("12.3.1"), + Deadline: optjson.SetString("2021-12-14"), }, }, }, diff --git a/cmd/fleetctl/testdata/expectedGetConfigAppConfigJson.json b/cmd/fleetctl/testdata/expectedGetConfigAppConfigJson.json index 34f5abeeaf..fd2efa8d0f 100644 --- a/cmd/fleetctl/testdata/expectedGetConfigAppConfigJson.json +++ b/cmd/fleetctl/testdata/expectedGetConfigAppConfigJson.json @@ -1,113 +1,113 @@ { - "kind": "config", - "apiVersion": "v1", - "spec": { - "org_info": { - "org_name": "", - "org_logo_url": "", - "contact_url": "https://fleetdm.com/company/contact" - }, - "server_settings": { - "server_url": "", - "live_query_disabled": false, - "enable_analytics": false, - "deferred_save_host": false - }, - "smtp_settings": { - "enable_smtp": false, - "configured": false, - "sender_address": "", - "server": "", - "port": 0, - "authentication_type": "", - "user_name": "", - "password": "", - "enable_ssl_tls": false, - "authentication_method": "", - "domain": "", - "verify_ssl_certs": false, - "enable_start_tls": false - }, - "host_expiry_settings": { - "host_expiry_enabled": false, - "host_expiry_window": 0 - }, - "features": { - "enable_host_users": true, - "enable_software_inventory": false - }, - "sso_settings": { - "entity_id": "", - "issuer_uri": "", - "idp_image_url": "", - "metadata": "", - "metadata_url": "", - "idp_name": "", - "enable_jit_provisioning": false, - "enable_jit_role_sync": false, - "enable_sso": false, - "enable_sso_idp_login": false - }, - "fleet_desktop": { - "transparency_url": "https://fleetdm.com/transparency" - }, - "vulnerability_settings": { - "databases_path": "/some/path" - }, - "webhook_settings": { - "host_status_webhook": { - "enable_host_status_webhook": false, - "destination_url": "", - "host_percentage": 0, - "days_count": 0 - }, - "failing_policies_webhook": { - "enable_failing_policies_webhook": false, - "destination_url": "", - "policy_ids": null, - "host_batch_size": 0 - }, - "vulnerabilities_webhook": { - "enable_vulnerabilities_webhook": false, - "destination_url": "", - "host_batch_size": 0 - }, - "interval": "0s" - }, - "integrations": { - "jira": null, - "zendesk": null - }, - "mdm": { - "apple_bm_terms_expired": false, - "apple_bm_enabled_and_configured": false, - "enabled_and_configured": false, - "apple_bm_default_team": "", - "macos_updates": { - "minimum_version": "", - "deadline": "" - }, - "macos_migration": { - "enable": false, - "mode": "", - "webhook_url": "" - }, - "macos_settings": { - "custom_settings": null, - "enable_disk_encryption": false - }, - "macos_setup": { - "bootstrap_package": null, - "enable_end_user_authentication": false, - "macos_setup_assistant": null - }, - "end_user_authentication": { - "entity_id": "", - "issuer_uri": "", - "metadata": "", - "metadata_url": "", - "idp_name": "" - } - } - } + "kind": "config", + "apiVersion": "v1", + "spec": { + "org_info": { + "org_name": "", + "org_logo_url": "", + "contact_url": "https://fleetdm.com/company/contact" + }, + "server_settings": { + "server_url": "", + "live_query_disabled": false, + "enable_analytics": false, + "deferred_save_host": false + }, + "smtp_settings": { + "enable_smtp": false, + "configured": false, + "sender_address": "", + "server": "", + "port": 0, + "authentication_type": "", + "user_name": "", + "password": "", + "enable_ssl_tls": false, + "authentication_method": "", + "domain": "", + "verify_ssl_certs": false, + "enable_start_tls": false + }, + "host_expiry_settings": { + "host_expiry_enabled": false, + "host_expiry_window": 0 + }, + "features": { + "enable_host_users": true, + "enable_software_inventory": false + }, + "sso_settings": { + "entity_id": "", + "issuer_uri": "", + "idp_image_url": "", + "metadata": "", + "metadata_url": "", + "idp_name": "", + "enable_jit_provisioning": false, + "enable_jit_role_sync": false, + "enable_sso": false, + "enable_sso_idp_login": false + }, + "fleet_desktop": { + "transparency_url": "https://fleetdm.com/transparency" + }, + "vulnerability_settings": { + "databases_path": "/some/path" + }, + "webhook_settings": { + "host_status_webhook": { + "enable_host_status_webhook": false, + "destination_url": "", + "host_percentage": 0, + "days_count": 0 + }, + "failing_policies_webhook": { + "enable_failing_policies_webhook": false, + "destination_url": "", + "policy_ids": null, + "host_batch_size": 0 + }, + "vulnerabilities_webhook": { + "enable_vulnerabilities_webhook": false, + "destination_url": "", + "host_batch_size": 0 + }, + "interval": "0s" + }, + "integrations": { + "jira": null, + "zendesk": null + }, + "mdm": { + "apple_bm_terms_expired": false, + "apple_bm_enabled_and_configured": false, + "enabled_and_configured": false, + "apple_bm_default_team": "", + "macos_updates": { + "minimum_version": null, + "deadline": null + }, + "macos_migration": { + "enable": false, + "mode": "", + "webhook_url": "" + }, + "macos_settings": { + "custom_settings": null, + "enable_disk_encryption": false + }, + "macos_setup": { + "bootstrap_package": null, + "enable_end_user_authentication": false, + "macos_setup_assistant": null + }, + "end_user_authentication": { + "entity_id": "", + "issuer_uri": "", + "metadata": "", + "metadata_url": "", + "idp_name": "" + } + } + } } diff --git a/cmd/fleetctl/testdata/expectedGetConfigAppConfigYaml.yml b/cmd/fleetctl/testdata/expectedGetConfigAppConfigYaml.yml index 8d6a3598a0..194608b9be 100644 --- a/cmd/fleetctl/testdata/expectedGetConfigAppConfigYaml.yml +++ b/cmd/fleetctl/testdata/expectedGetConfigAppConfigYaml.yml @@ -23,8 +23,8 @@ spec: mode: "" webhook_url: "" macos_updates: - minimum_version: "" - deadline: "" + minimum_version: null + deadline: null macos_settings: custom_settings: enable_disk_encryption: false diff --git a/cmd/fleetctl/testdata/expectedGetConfigIncludeServerConfigJson.json b/cmd/fleetctl/testdata/expectedGetConfigIncludeServerConfigJson.json index 5a98007ce3..9409cc7728 100644 --- a/cmd/fleetctl/testdata/expectedGetConfigIncludeServerConfigJson.json +++ b/cmd/fleetctl/testdata/expectedGetConfigIncludeServerConfigJson.json @@ -1,175 +1,175 @@ { - "kind": "config", - "apiVersion": "v1", - "spec": { - "org_info": { - "org_name": "", - "org_logo_url": "", - "contact_url": "https://fleetdm.com/company/contact" - }, - "server_settings": { - "server_url": "", - "live_query_disabled": false, - "enable_analytics": false, - "deferred_save_host": false - }, - "smtp_settings": { - "enable_smtp": false, - "configured": false, - "sender_address": "", - "server": "", - "port": 0, - "authentication_type": "", - "user_name": "", - "password": "", - "enable_ssl_tls": false, - "authentication_method": "", - "domain": "", - "verify_ssl_certs": false, - "enable_start_tls": false - }, - "host_expiry_settings": { - "host_expiry_enabled": false, - "host_expiry_window": 0 - }, - "features": { - "enable_host_users": true, - "enable_software_inventory": false - }, - "mdm": { - "apple_bm_default_team": "", - "apple_bm_terms_expired": false, - "apple_bm_enabled_and_configured": false, - "enabled_and_configured": false, - "macos_updates": { - "minimum_version": "", - "deadline": "" - }, - "macos_migration": { - "enable": false, - "mode": "", - "webhook_url": "" - }, - "macos_settings": { - "custom_settings": null, - "enable_disk_encryption": false - }, - "macos_setup": { - "bootstrap_package": null, - "enable_end_user_authentication": false, - "macos_setup_assistant": null - }, - "end_user_authentication": { - "entity_id": "", - "issuer_uri": "", - "metadata": "", - "metadata_url": "", - "idp_name": "" - } - }, - "sso_settings": { - "enable_jit_provisioning": false, - "enable_jit_role_sync": false, - "entity_id": "", - "issuer_uri": "", - "idp_image_url": "", - "metadata": "", - "metadata_url": "", - "idp_name": "", - "enable_sso": false, - "enable_sso_idp_login": false - }, - "fleet_desktop": { - "transparency_url": "https://fleetdm.com/transparency" - }, - "vulnerability_settings": { - "databases_path": "/some/path" - }, - "webhook_settings": { - "host_status_webhook": { - "enable_host_status_webhook": false, - "destination_url": "", - "host_percentage": 0, - "days_count": 0 - }, - "failing_policies_webhook": { - "enable_failing_policies_webhook": false, - "destination_url": "", - "policy_ids": null, - "host_batch_size": 0 - }, - "vulnerabilities_webhook": { - "enable_vulnerabilities_webhook": false, - "destination_url": "", - "host_batch_size": 0 - }, - "interval": "0s" - }, - "integrations": { - "jira": null, - "zendesk": null - }, - "update_interval": { - "osquery_detail": "1h0m0s", - "osquery_policy": "1h0m0s" - }, - "vulnerabilities": { - "databases_path": "", - "periodicity": "0s", - "cpe_database_url": "", - "cpe_translations_url": "", - "cve_feed_prefix_url": "", - "current_instance_checks": "", - "disable_data_sync": false, - "recent_vulnerability_max_age": "0s", - "disable_win_os_vulnerabilities": false - }, - "license": { - "tier": "free", - "expiration": "0001-01-01T00:00:00Z" - }, - "logging": { - "debug": true, - "json": false, - "result": { - "plugin": "filesystem", - "config": { - "enable_log_compression": false, - "enable_log_rotation": false, - "result_log_file": "/dev/null", - "status_log_file": "/dev/null", - "audit_log_file": "/dev/null", - "max_size": 500, - "max_age": 0, - "max_backups": 0 - } - }, - "status": { - "plugin": "filesystem", - "config": { - "enable_log_compression": false, - "enable_log_rotation": false, - "result_log_file": "/dev/null", - "status_log_file": "/dev/null", - "audit_log_file": "/dev/null", - "max_size": 500, - "max_age": 0, - "max_backups": 0 - } - }, - "audit": { - "plugin": "filesystem", - "config": { - "enable_log_compression": false, - "enable_log_rotation": false, - "result_log_file": "/dev/null", - "status_log_file": "/dev/null", - "audit_log_file": "/dev/null", - "max_size": 500, - "max_age": 0, - "max_backups": 0 - } - } - } - } + "kind": "config", + "apiVersion": "v1", + "spec": { + "org_info": { + "org_name": "", + "org_logo_url": "", + "contact_url": "https://fleetdm.com/company/contact" + }, + "server_settings": { + "server_url": "", + "live_query_disabled": false, + "enable_analytics": false, + "deferred_save_host": false + }, + "smtp_settings": { + "enable_smtp": false, + "configured": false, + "sender_address": "", + "server": "", + "port": 0, + "authentication_type": "", + "user_name": "", + "password": "", + "enable_ssl_tls": false, + "authentication_method": "", + "domain": "", + "verify_ssl_certs": false, + "enable_start_tls": false + }, + "host_expiry_settings": { + "host_expiry_enabled": false, + "host_expiry_window": 0 + }, + "features": { + "enable_host_users": true, + "enable_software_inventory": false + }, + "mdm": { + "apple_bm_default_team": "", + "apple_bm_terms_expired": false, + "apple_bm_enabled_and_configured": false, + "enabled_and_configured": false, + "macos_updates": { + "minimum_version": null, + "deadline": null + }, + "macos_migration": { + "enable": false, + "mode": "", + "webhook_url": "" + }, + "macos_settings": { + "custom_settings": null, + "enable_disk_encryption": false + }, + "macos_setup": { + "bootstrap_package": null, + "enable_end_user_authentication": false, + "macos_setup_assistant": null + }, + "end_user_authentication": { + "entity_id": "", + "issuer_uri": "", + "metadata": "", + "metadata_url": "", + "idp_name": "" + } + }, + "sso_settings": { + "enable_jit_provisioning": false, + "enable_jit_role_sync": false, + "entity_id": "", + "issuer_uri": "", + "idp_image_url": "", + "metadata": "", + "metadata_url": "", + "idp_name": "", + "enable_sso": false, + "enable_sso_idp_login": false + }, + "fleet_desktop": { + "transparency_url": "https://fleetdm.com/transparency" + }, + "vulnerability_settings": { + "databases_path": "/some/path" + }, + "webhook_settings": { + "host_status_webhook": { + "enable_host_status_webhook": false, + "destination_url": "", + "host_percentage": 0, + "days_count": 0 + }, + "failing_policies_webhook": { + "enable_failing_policies_webhook": false, + "destination_url": "", + "policy_ids": null, + "host_batch_size": 0 + }, + "vulnerabilities_webhook": { + "enable_vulnerabilities_webhook": false, + "destination_url": "", + "host_batch_size": 0 + }, + "interval": "0s" + }, + "integrations": { + "jira": null, + "zendesk": null + }, + "update_interval": { + "osquery_detail": "1h0m0s", + "osquery_policy": "1h0m0s" + }, + "vulnerabilities": { + "databases_path": "", + "periodicity": "0s", + "cpe_database_url": "", + "cpe_translations_url": "", + "cve_feed_prefix_url": "", + "current_instance_checks": "", + "disable_data_sync": false, + "recent_vulnerability_max_age": "0s", + "disable_win_os_vulnerabilities": false + }, + "license": { + "tier": "free", + "expiration": "0001-01-01T00:00:00Z" + }, + "logging": { + "debug": true, + "json": false, + "result": { + "plugin": "filesystem", + "config": { + "enable_log_compression": false, + "enable_log_rotation": false, + "result_log_file": "/dev/null", + "status_log_file": "/dev/null", + "audit_log_file": "/dev/null", + "max_size": 500, + "max_age": 0, + "max_backups": 0 + } + }, + "status": { + "plugin": "filesystem", + "config": { + "enable_log_compression": false, + "enable_log_rotation": false, + "result_log_file": "/dev/null", + "status_log_file": "/dev/null", + "audit_log_file": "/dev/null", + "max_size": 500, + "max_age": 0, + "max_backups": 0 + } + }, + "audit": { + "plugin": "filesystem", + "config": { + "enable_log_compression": false, + "enable_log_rotation": false, + "result_log_file": "/dev/null", + "status_log_file": "/dev/null", + "audit_log_file": "/dev/null", + "max_size": 500, + "max_age": 0, + "max_backups": 0 + } + } + } + } } diff --git a/cmd/fleetctl/testdata/expectedGetConfigIncludeServerConfigYaml.yml b/cmd/fleetctl/testdata/expectedGetConfigIncludeServerConfigYaml.yml index 4f076a405f..7922ebaba4 100644 --- a/cmd/fleetctl/testdata/expectedGetConfigIncludeServerConfigYaml.yml +++ b/cmd/fleetctl/testdata/expectedGetConfigIncludeServerConfigYaml.yml @@ -23,8 +23,8 @@ spec: mode: "" webhook_url: "" macos_updates: - minimum_version: "" - deadline: "" + minimum_version: null + deadline: null macos_settings: custom_settings: enable_disk_encryption: false diff --git a/cmd/fleetctl/testdata/expectedGetTeamsJson.json b/cmd/fleetctl/testdata/expectedGetTeamsJson.json index 333ae91c32..19152a690c 100644 --- a/cmd/fleetctl/testdata/expectedGetTeamsJson.json +++ b/cmd/fleetctl/testdata/expectedGetTeamsJson.json @@ -25,8 +25,8 @@ }, "mdm": { "macos_updates": { - "minimum_version": "", - "deadline": "" + "minimum_version": null, + "deadline": null }, "macos_settings": { "custom_settings": null, diff --git a/cmd/fleetctl/testdata/expectedGetTeamsYaml.yml b/cmd/fleetctl/testdata/expectedGetTeamsYaml.yml index 42ddb13426..2b571ae8b5 100644 --- a/cmd/fleetctl/testdata/expectedGetTeamsYaml.yml +++ b/cmd/fleetctl/testdata/expectedGetTeamsYaml.yml @@ -8,8 +8,8 @@ spec: enable_software_inventory: true mdm: macos_updates: - minimum_version: "" - deadline: "" + minimum_version: null + deadline: null macos_settings: custom_settings: enable_disk_encryption: false diff --git a/cmd/fleetctl/testdata/macosSetupExpectedAppConfigEmpty.yml b/cmd/fleetctl/testdata/macosSetupExpectedAppConfigEmpty.yml index f62a91a144..41a6400829 100644 --- a/cmd/fleetctl/testdata/macosSetupExpectedAppConfigEmpty.yml +++ b/cmd/fleetctl/testdata/macosSetupExpectedAppConfigEmpty.yml @@ -30,8 +30,8 @@ spec: enable_end_user_authentication: false macos_setup_assistant: null macos_updates: - deadline: "" - minimum_version: "" + deadline: null + minimum_version: null end_user_authentication: idp_name: "" issuer_uri: "" diff --git a/cmd/fleetctl/testdata/macosSetupExpectedAppConfigSet.yml b/cmd/fleetctl/testdata/macosSetupExpectedAppConfigSet.yml index 8376f881f4..fa368bb20b 100644 --- a/cmd/fleetctl/testdata/macosSetupExpectedAppConfigSet.yml +++ b/cmd/fleetctl/testdata/macosSetupExpectedAppConfigSet.yml @@ -30,8 +30,8 @@ spec: enable_end_user_authentication: false macos_setup_assistant: %s macos_updates: - deadline: "" - minimum_version: "" + deadline: null + minimum_version: null end_user_authentication: idp_name: "" issuer_uri: "" diff --git a/cmd/fleetctl/testdata/macosSetupExpectedTeam1And2Empty.yml b/cmd/fleetctl/testdata/macosSetupExpectedTeam1And2Empty.yml index 5aa72cb8d8..346bbc2eb7 100644 --- a/cmd/fleetctl/testdata/macosSetupExpectedTeam1And2Empty.yml +++ b/cmd/fleetctl/testdata/macosSetupExpectedTeam1And2Empty.yml @@ -15,8 +15,8 @@ spec: enable_end_user_authentication: false macos_setup_assistant: null macos_updates: - deadline: "" - minimum_version: "" + deadline: null + minimum_version: null name: tm1 --- apiVersion: v1 @@ -34,6 +34,6 @@ spec: bootstrap_package: null macos_setup_assistant: null macos_updates: - deadline: "" - minimum_version: "" + deadline: null + minimum_version: null name: tm2 diff --git a/cmd/fleetctl/testdata/macosSetupExpectedTeam1And2Set.yml b/cmd/fleetctl/testdata/macosSetupExpectedTeam1And2Set.yml index 808b96abac..45f1733019 100644 --- a/cmd/fleetctl/testdata/macosSetupExpectedTeam1And2Set.yml +++ b/cmd/fleetctl/testdata/macosSetupExpectedTeam1And2Set.yml @@ -15,8 +15,8 @@ spec: enable_end_user_authentication: false macos_setup_assistant: %s macos_updates: - deadline: "" - minimum_version: "" + deadline: null + minimum_version: null name: tm1 --- apiVersion: v1 @@ -34,6 +34,6 @@ spec: bootstrap_package: %s macos_setup_assistant: %s macos_updates: - deadline: "" - minimum_version: "" + deadline: null + minimum_version: null name: tm2 diff --git a/cmd/fleetctl/testdata/macosSetupExpectedTeam1Empty.yml b/cmd/fleetctl/testdata/macosSetupExpectedTeam1Empty.yml index e807ec3396..21d9b9d8db 100644 --- a/cmd/fleetctl/testdata/macosSetupExpectedTeam1Empty.yml +++ b/cmd/fleetctl/testdata/macosSetupExpectedTeam1Empty.yml @@ -15,7 +15,7 @@ spec: enable_end_user_authentication: false macos_setup_assistant: null macos_updates: - deadline: "" - minimum_version: "" + deadline: null + minimum_version: null name: tm1 diff --git a/ee/server/service/teams.go b/ee/server/service/teams.go index 0efb715017..0b8c3f9f13 100644 --- a/ee/server/service/teams.go +++ b/ee/server/service/teams.go @@ -117,8 +117,11 @@ func (svc *Service) ModifyTeam(ctx context.Context, teamID uint, payload fleet.T 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.MacOSUpdates = *payload.MDM.MacOSUpdates + if payload.MDM.MacOSUpdates.MinimumVersion.Set || payload.MDM.MacOSUpdates.Deadline.Set { + macOSMinVersionUpdated = team.Config.MDM.MacOSUpdates.MinimumVersion.Value != payload.MDM.MacOSUpdates.MinimumVersion.Value || + team.Config.MDM.MacOSUpdates.Deadline.Value != payload.MDM.MacOSUpdates.Deadline.Value + team.Config.MDM.MacOSUpdates = *payload.MDM.MacOSUpdates + } } if payload.MDM.MacOSSettings != nil { @@ -187,8 +190,8 @@ func (svc *Service) ModifyTeam(ctx context.Context, teamID uint, payload fleet.T fleet.ActivityTypeEditedMacOSMinVersion{ TeamID: &team.ID, TeamName: &team.Name, - MinimumVersion: team.Config.MDM.MacOSUpdates.MinimumVersion, - Deadline: team.Config.MDM.MacOSUpdates.Deadline, + MinimumVersion: team.Config.MDM.MacOSUpdates.MinimumVersion.Value, + Deadline: team.Config.MDM.MacOSUpdates.Deadline.Value, }, ); err != nil { return nil, ctxerr.Wrap(ctx, err, "create activity for team macos min version edited") @@ -773,7 +776,9 @@ func (svc *Service) editTeamFromSpec( return err } team.Config.Features = features - team.Config.MDM.MacOSUpdates = spec.MDM.MacOSUpdates + if spec.MDM.MacOSUpdates.Deadline.Set || spec.MDM.MacOSUpdates.MinimumVersion.Set { + team.Config.MDM.MacOSUpdates = spec.MDM.MacOSUpdates + } oldMacOSDiskEncryption := team.Config.MDM.MacOSSettings.EnableDiskEncryption if err := svc.applyTeamMacOSSettings(ctx, spec, &team.Config.MDM.MacOSSettings); err != nil { diff --git a/orbit/pkg/update/nudge_test.go b/orbit/pkg/update/nudge_test.go index 16ac27d684..ece20f8b5c 100644 --- a/orbit/pkg/update/nudge_test.go +++ b/orbit/pkg/update/nudge_test.go @@ -8,6 +8,7 @@ import ( "time" "github.com/fleetdm/fleet/v4/orbit/pkg/constant" + "github.com/fleetdm/fleet/v4/pkg/optjson" "github.com/fleetdm/fleet/v4/server/fleet" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" @@ -57,7 +58,7 @@ func (s *nudgeTestSuite) TestNudgeConfigFetcherAddNudge() { require.Len(t, targets, 0) // set the config - cfg.NudgeConfig, err = fleet.NewNudgeConfig(fleet.MacOSUpdates{MinimumVersion: "11", Deadline: "2022-01-04"}) + cfg.NudgeConfig, err = fleet.NewNudgeConfig(fleet.MacOSUpdates{MinimumVersion: optjson.SetString("11"), Deadline: optjson.SetString("2022-01-04")}) require.NoError(t, err) // there's an error when the remote repo doesn't have the target yet diff --git a/server/datastore/cached_mysql/cached_mysql_test.go b/server/datastore/cached_mysql/cached_mysql_test.go index 94df696d70..0417987931 100644 --- a/server/datastore/cached_mysql/cached_mysql_test.go +++ b/server/datastore/cached_mysql/cached_mysql_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + "github.com/fleetdm/fleet/v4/pkg/optjson" "github.com/fleetdm/fleet/v4/server/fleet" "github.com/fleetdm/fleet/v4/server/mock" "github.com/fleetdm/fleet/v4/server/ptr" @@ -471,8 +472,8 @@ func TestCachedTeamMDMConfig(t *testing.T) { testMDMConfig := fleet.TeamMDM{ MacOSUpdates: fleet.MacOSUpdates{ - MinimumVersion: "10.10.10", - Deadline: "1992-03-01", + MinimumVersion: optjson.SetString("10.10.10"), + Deadline: optjson.SetString("1992-03-01"), }, } @@ -508,8 +509,8 @@ func TestCachedTeamMDMConfig(t *testing.T) { // saving a team updates config in cache updateMDMConfig := fleet.TeamMDM{ MacOSUpdates: fleet.MacOSUpdates{ - MinimumVersion: "13.13.13", - Deadline: "2022-03-01", + MinimumVersion: optjson.SetString("13.13.13"), + Deadline: optjson.SetString("2022-03-01"), }, } updateTeam := &fleet.Team{ diff --git a/server/datastore/mysql/schema.sql b/server/datastore/mysql/schema.sql index b15c6d9319..96c26edeb7 100644 --- a/server/datastore/mysql/schema.sql +++ b/server/datastore/mysql/schema.sql @@ -40,7 +40,7 @@ CREATE TABLE `app_config_json` ( UNIQUE KEY `id` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -INSERT INTO `app_config_json` VALUES (1,'{\"mdm\": {\"macos_setup\": {\"bootstrap_package\": null, \"macos_setup_assistant\": null, \"enable_end_user_authentication\": false}, \"macos_updates\": {\"deadline\": \"\", \"minimum_version\": \"\"}, \"macos_settings\": {\"custom_settings\": null, \"enable_disk_encryption\": false}, \"macos_migration\": {\"mode\": \"\", \"enable\": false, \"webhook_url\": \"\"}, \"apple_bm_default_team\": \"\", \"apple_bm_terms_expired\": false, \"enabled_and_configured\": false, \"end_user_authentication\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"issuer_uri\": \"\", \"metadata_url\": \"\"}, \"apple_bm_enabled_and_configured\": false}, \"features\": {\"enable_host_users\": true, \"enable_software_inventory\": false}, \"org_info\": {\"org_name\": \"\", \"contact_url\": \"\", \"org_logo_url\": \"\"}, \"integrations\": {\"jira\": null, \"zendesk\": null}, \"sso_settings\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"enable_sso\": false, \"issuer_uri\": \"\", \"metadata_url\": \"\", \"idp_image_url\": \"\", \"enable_jit_role_sync\": false, \"enable_sso_idp_login\": false, \"enable_jit_provisioning\": false}, \"agent_options\": {\"config\": {\"options\": {\"logger_plugin\": \"tls\", \"pack_delimiter\": \"/\", \"logger_tls_period\": 10, \"distributed_plugin\": \"tls\", \"disable_distributed\": false, \"logger_tls_endpoint\": \"/api/osquery/log\", \"distributed_interval\": 10, \"distributed_tls_max_attempts\": 3}, \"decorators\": {\"load\": [\"SELECT uuid AS host_uuid FROM system_info;\", \"SELECT hostname AS hostname FROM system_info;\"]}}, \"overrides\": {}}, \"fleet_desktop\": {\"transparency_url\": \"\"}, \"smtp_settings\": {\"port\": 587, \"domain\": \"\", \"server\": \"\", \"password\": \"\", \"user_name\": \"\", \"configured\": false, \"enable_smtp\": false, \"enable_ssl_tls\": true, \"sender_address\": \"\", \"enable_start_tls\": true, \"verify_ssl_certs\": true, \"authentication_type\": \"0\", \"authentication_method\": \"0\"}, \"server_settings\": {\"server_url\": \"\", \"enable_analytics\": false, \"deferred_save_host\": false, \"live_query_disabled\": false}, \"webhook_settings\": {\"interval\": \"0s\", \"host_status_webhook\": {\"days_count\": 0, \"destination_url\": \"\", \"host_percentage\": 0, \"enable_host_status_webhook\": false}, \"vulnerabilities_webhook\": {\"destination_url\": \"\", \"host_batch_size\": 0, \"enable_vulnerabilities_webhook\": false}, \"failing_policies_webhook\": {\"policy_ids\": null, \"destination_url\": \"\", \"host_batch_size\": 0, \"enable_failing_policies_webhook\": false}}, \"host_expiry_settings\": {\"host_expiry_window\": 0, \"host_expiry_enabled\": false}, \"vulnerability_settings\": {\"databases_path\": \"\"}}','2020-01-01 01:01:01','2020-01-01 01:01:01'); +INSERT INTO `app_config_json` VALUES (1,'{\"mdm\": {\"macos_setup\": {\"bootstrap_package\": null, \"macos_setup_assistant\": null, \"enable_end_user_authentication\": false}, \"macos_updates\": {\"deadline\": null, \"minimum_version\": null}, \"macos_settings\": {\"custom_settings\": null, \"enable_disk_encryption\": false}, \"macos_migration\": {\"mode\": \"\", \"enable\": false, \"webhook_url\": \"\"}, \"apple_bm_default_team\": \"\", \"apple_bm_terms_expired\": false, \"enabled_and_configured\": false, \"end_user_authentication\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"issuer_uri\": \"\", \"metadata_url\": \"\"}, \"apple_bm_enabled_and_configured\": false}, \"features\": {\"enable_host_users\": true, \"enable_software_inventory\": false}, \"org_info\": {\"org_name\": \"\", \"contact_url\": \"\", \"org_logo_url\": \"\"}, \"integrations\": {\"jira\": null, \"zendesk\": null}, \"sso_settings\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"enable_sso\": false, \"issuer_uri\": \"\", \"metadata_url\": \"\", \"idp_image_url\": \"\", \"enable_jit_role_sync\": false, \"enable_sso_idp_login\": false, \"enable_jit_provisioning\": false}, \"agent_options\": {\"config\": {\"options\": {\"logger_plugin\": \"tls\", \"pack_delimiter\": \"/\", \"logger_tls_period\": 10, \"distributed_plugin\": \"tls\", \"disable_distributed\": false, \"logger_tls_endpoint\": \"/api/osquery/log\", \"distributed_interval\": 10, \"distributed_tls_max_attempts\": 3}, \"decorators\": {\"load\": [\"SELECT uuid AS host_uuid FROM system_info;\", \"SELECT hostname AS hostname FROM system_info;\"]}}, \"overrides\": {}}, \"fleet_desktop\": {\"transparency_url\": \"\"}, \"smtp_settings\": {\"port\": 587, \"domain\": \"\", \"server\": \"\", \"password\": \"\", \"user_name\": \"\", \"configured\": false, \"enable_smtp\": false, \"enable_ssl_tls\": true, \"sender_address\": \"\", \"enable_start_tls\": true, \"verify_ssl_certs\": true, \"authentication_type\": \"0\", \"authentication_method\": \"0\"}, \"server_settings\": {\"server_url\": \"\", \"enable_analytics\": false, \"deferred_save_host\": false, \"live_query_disabled\": false}, \"webhook_settings\": {\"interval\": \"0s\", \"host_status_webhook\": {\"days_count\": 0, \"destination_url\": \"\", \"host_percentage\": 0, \"enable_host_status_webhook\": false}, \"vulnerabilities_webhook\": {\"destination_url\": \"\", \"host_batch_size\": 0, \"enable_vulnerabilities_webhook\": false}, \"failing_policies_webhook\": {\"policy_ids\": null, \"destination_url\": \"\", \"host_batch_size\": 0, \"enable_failing_policies_webhook\": false}}, \"host_expiry_settings\": {\"host_expiry_window\": 0, \"host_expiry_enabled\": false}, \"vulnerability_settings\": {\"databases_path\": \"\"}}','2020-01-01 01:01:01','2020-01-01 01:01:01'); /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `carve_blocks` ( diff --git a/server/datastore/mysql/teams_test.go b/server/datastore/mysql/teams_test.go index 03327d1190..69daa714f5 100644 --- a/server/datastore/mysql/teams_test.go +++ b/server/datastore/mysql/teams_test.go @@ -583,8 +583,8 @@ func testTeamsMDMConfig(t *testing.T, ds *Datastore) { Config: fleet.TeamConfig{ MDM: fleet.TeamMDM{ MacOSUpdates: fleet.MacOSUpdates{ - MinimumVersion: "10.15.0", - Deadline: "2025-10-01", + MinimumVersion: optjson.SetString("10.15.0"), + Deadline: optjson.SetString("2025-10-01"), }, MacOSSetup: fleet.MacOSSetup{ BootstrapPackage: optjson.SetString("bootstrap"), @@ -599,8 +599,8 @@ func testTeamsMDMConfig(t *testing.T, ds *Datastore) { assert.Equal(t, &fleet.TeamMDM{ MacOSUpdates: fleet.MacOSUpdates{ - MinimumVersion: "10.15.0", - Deadline: "2025-10-01", + MinimumVersion: optjson.SetString("10.15.0"), + Deadline: optjson.SetString("2025-10-01"), }, MacOSSetup: fleet.MacOSSetup{ BootstrapPackage: optjson.SetString("bootstrap"), diff --git a/server/fleet/app.go b/server/fleet/app.go index 512540b3c0..221292111e 100644 --- a/server/fleet/app.go +++ b/server/fleet/app.go @@ -159,32 +159,39 @@ var versionStringRegex = regexp.MustCompile(`^\d+(\.\d+)?(\.\d+)?$`) // MacOSUpdates is part of AppConfig and defines the macOS update settings. type MacOSUpdates struct { - // MinimumVerssion is the required minimum operating system version. - MinimumVersion string `json:"minimum_version"` + // MinimumVersion is the required minimum operating system version. + MinimumVersion optjson.String `json:"minimum_version"` // Deadline the required installation date for Nudge to enforce the required // operating system version. - Deadline string `json:"deadline"` + Deadline optjson.String `json:"deadline"` } func (m MacOSUpdates) Validate() error { // if no settings are provided it's okay to skip further validation - if m.MinimumVersion == "" && m.Deadline == "" { + if m.MinimumVersion.Value == "" && m.Deadline.Value == "" { + // if one is set and empty, the other must be set and empty too, otherwise + // it's as if only one was provided. + if m.MinimumVersion.Set && !m.Deadline.Set { + return errors.New("deadline is required when minimum_version is provided") + } else if !m.MinimumVersion.Set && m.Deadline.Set { + return errors.New("minimum_version is required when deadline is provided") + } return nil } - if m.MinimumVersion != "" && m.Deadline == "" { + if m.MinimumVersion.Value != "" && m.Deadline.Value == "" { return errors.New("deadline is required when minimum_version is provided") } - if m.Deadline != "" && m.MinimumVersion == "" { + if m.Deadline.Value != "" && m.MinimumVersion.Value == "" { return errors.New("minimum_version is required when deadline is provided") } - if !versionStringRegex.MatchString(m.MinimumVersion) { + if !versionStringRegex.MatchString(m.MinimumVersion.Value) { return errors.New(`minimum_version accepts version numbers only. (E.g., "13.0.1.") NOT "Ventura 13" or "13.0.1 (22A400)"`) } - if _, err := time.Parse("2006-01-02", m.Deadline); err != nil { + if _, err := time.Parse("2006-01-02", m.Deadline.Value); err != nil { return errors.New(`deadline accepts YYYY-MM-DD format only (E.g., "2023-06-01.")`) } diff --git a/server/fleet/app_test.go b/server/fleet/app_test.go index 07bf8b647f..99b3b18964 100644 --- a/server/fleet/app_test.go +++ b/server/fleet/app_test.go @@ -3,6 +3,7 @@ package fleet import ( "testing" + "github.com/fleetdm/fleet/v4/pkg/optjson" "github.com/stretchr/testify/require" ) @@ -16,22 +17,22 @@ func TestMacOSUpdatesValidate(t *testing.T) { { "with full version", MacOSUpdates{ - MinimumVersion: "10.15.0", - Deadline: "2020-01-01", + MinimumVersion: optjson.SetString("10.15.0"), + Deadline: optjson.SetString("2020-01-01"), }, }, { "without patch version", MacOSUpdates{ - MinimumVersion: "10.15", - Deadline: "2020-01-01", + MinimumVersion: optjson.SetString("10.15"), + Deadline: optjson.SetString("2020-01-01"), }, }, { "only major version", MacOSUpdates{ - MinimumVersion: "10", - Deadline: "2020-01-01", + MinimumVersion: optjson.SetString("10"), + Deadline: optjson.SetString("2020-01-01"), }, }, } @@ -51,22 +52,22 @@ func TestMacOSUpdatesValidate(t *testing.T) { { "version but no deadline", MacOSUpdates{ - MinimumVersion: "10.15.0", - Deadline: "", + MinimumVersion: optjson.SetString("10.15.0"), + Deadline: optjson.SetString(""), }, }, { "deadline with timestamp", MacOSUpdates{ - MinimumVersion: "10.15.0", - Deadline: "2020-01-01T00:00:00Z", + MinimumVersion: optjson.SetString("10.15.0"), + Deadline: optjson.SetString("2020-01-01T00:00:00Z"), }, }, { "incomplete date", MacOSUpdates{ - MinimumVersion: "10.15.0", - Deadline: "2020-01", + MinimumVersion: optjson.SetString("10.15.0"), + Deadline: optjson.SetString("2020-01"), }, }, } @@ -86,22 +87,22 @@ func TestMacOSUpdatesValidate(t *testing.T) { { "deadline but no version", MacOSUpdates{ - MinimumVersion: "", - Deadline: "2020-01-01", + MinimumVersion: optjson.SetString(""), + Deadline: optjson.SetString("2020-01-01"), }, }, { "version with build info", MacOSUpdates{ - MinimumVersion: "10.15.0 (19A583)", - Deadline: "2020-01-01", + MinimumVersion: optjson.SetString("10.15.0 (19A583)"), + Deadline: optjson.SetString("2020-01-01"), }, }, { "version with patch info", MacOSUpdates{ - MinimumVersion: "10.15.0-patch1", - Deadline: "2020-01-01", + MinimumVersion: optjson.SetString("10.15.0-patch1"), + Deadline: optjson.SetString("2020-01-01"), }, }, } diff --git a/server/fleet/nudge.go b/server/fleet/nudge.go index 91a9982040..4e824553cb 100644 --- a/server/fleet/nudge.go +++ b/server/fleet/nudge.go @@ -45,7 +45,7 @@ type nudgeUpdateElements struct { } func NewNudgeConfig(macOSUpdates MacOSUpdates) (*NudgeConfig, error) { - deadline, err := time.Parse("2006-01-02", macOSUpdates.Deadline) + deadline, err := time.Parse("2006-01-02", macOSUpdates.Deadline.Value) if err != nil { return nil, err } @@ -59,7 +59,7 @@ func NewNudgeConfig(macOSUpdates MacOSUpdates) (*NudgeConfig, error) { return &NudgeConfig{ OSVersionRequirements: []nudgeOSVersionRequirements{{ RequiredInstallationDate: localizedDeadline, - RequiredMinimumOSVersion: macOSUpdates.MinimumVersion, + RequiredMinimumOSVersion: macOSUpdates.MinimumVersion.Value, AboutUpdateURLs: []nudgeAboutUpdateURLs{{ Language: "en", AboutUpdateURL: "https://fleetdm.com/docs/using-fleet/mdm-macos-updates", diff --git a/server/service/appconfig.go b/server/service/appconfig.go index 17eec8515b..f75973f6b2 100644 --- a/server/service/appconfig.go +++ b/server/service/appconfig.go @@ -448,13 +448,14 @@ func (svc *Service) ModifyAppConfig(ctx context.Context, p []byte, applyOpts fle // if the macOS minimum version requirement changed, create the corresponding // activity - if oldAppConfig.MDM.MacOSUpdates != appConfig.MDM.MacOSUpdates { + if oldAppConfig.MDM.MacOSUpdates.MinimumVersion.Value != appConfig.MDM.MacOSUpdates.MinimumVersion.Value || + oldAppConfig.MDM.MacOSUpdates.Deadline.Value != appConfig.MDM.MacOSUpdates.Deadline.Value { if err := svc.ds.NewActivity( ctx, authz.UserFromContext(ctx), fleet.ActivityTypeEditedMacOSMinVersion{ - MinimumVersion: appConfig.MDM.MacOSUpdates.MinimumVersion, - Deadline: appConfig.MDM.MacOSUpdates.Deadline, + MinimumVersion: appConfig.MDM.MacOSUpdates.MinimumVersion.Value, + Deadline: appConfig.MDM.MacOSUpdates.Deadline.Value, }, ); err != nil { return nil, ctxerr.Wrap(ctx, err, "create activity for app config macos min version modification") @@ -563,9 +564,9 @@ func (svc *Service) validateMDM( } // MacOSUpdates - updatingVersion := mdm.MacOSUpdates.MinimumVersion != "" && + updatingVersion := mdm.MacOSUpdates.MinimumVersion.Value != "" && mdm.MacOSUpdates.MinimumVersion != oldMdm.MacOSUpdates.MinimumVersion - updatingDeadline := mdm.MacOSUpdates.Deadline != "" && + updatingDeadline := mdm.MacOSUpdates.Deadline.Value != "" && mdm.MacOSUpdates.Deadline != oldMdm.MacOSUpdates.Deadline if updatingVersion || updatingDeadline { @@ -573,9 +574,9 @@ func (svc *Service) validateMDM( invalid.Append("macos_updates.minimum_version", ErrMissingLicense.Error()) return } - if err := mdm.MacOSUpdates.Validate(); err != nil { - invalid.Append("macos_updates", err.Error()) - } + } + if err := mdm.MacOSUpdates.Validate(); err != nil { + invalid.Append("macos_updates", err.Error()) } // EndUserAuthentication diff --git a/server/service/appconfig_test.go b/server/service/appconfig_test.go index 171186550a..739fdfd325 100644 --- a/server/service/appconfig_test.go +++ b/server/service/appconfig_test.go @@ -778,7 +778,8 @@ func TestMDMAppleConfig(t *testing.T) { name: "nochange", licenseTier: "free", expectedMDM: fleet.MDM{ - MacOSSetup: fleet.MacOSSetup{BootstrapPackage: optjson.String{Set: true}, MacOSSetupAssistant: optjson.String{Set: true}}, + MacOSSetup: fleet.MacOSSetup{BootstrapPackage: optjson.String{Set: true}, MacOSSetupAssistant: optjson.String{Set: true}}, + MacOSUpdates: fleet.MacOSUpdates{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}}, }, }, { name: "newDefaultTeamNoLicense", @@ -804,6 +805,7 @@ func TestMDMAppleConfig(t *testing.T) { expectedMDM: fleet.MDM{ AppleBMDefaultTeam: "foobar", MacOSSetup: fleet.MacOSSetup{BootstrapPackage: optjson.String{Set: true}, MacOSSetupAssistant: optjson.String{Set: true}}, + MacOSUpdates: fleet.MacOSUpdates{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}}, }, }, { name: "foundEdit", @@ -814,6 +816,7 @@ func TestMDMAppleConfig(t *testing.T) { expectedMDM: fleet.MDM{ AppleBMDefaultTeam: "foobar", MacOSSetup: fleet.MacOSSetup{BootstrapPackage: optjson.String{Set: true}, MacOSSetupAssistant: optjson.String{Set: true}}, + MacOSUpdates: fleet.MacOSUpdates{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}}, }, }, { name: "ssoFree", @@ -830,6 +833,7 @@ func TestMDMAppleConfig(t *testing.T) { expectedMDM: fleet.MDM{ EndUserAuthentication: fleet.MDMEndUserAuthentication{SSOProviderSettings: fleet.SSOProviderSettings{EntityID: "foo"}}, MacOSSetup: fleet.MacOSSetup{BootstrapPackage: optjson.String{Set: true}, MacOSSetupAssistant: optjson.String{Set: true}}, + MacOSUpdates: fleet.MacOSUpdates{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}}, }, }, { name: "ssoAllFields", @@ -848,7 +852,8 @@ func TestMDMAppleConfig(t *testing.T) { MetadataURL: "http://isser.metadata.com", IDPName: "onelogin", }}, - MacOSSetup: fleet.MacOSSetup{BootstrapPackage: optjson.String{Set: true}, MacOSSetupAssistant: optjson.String{Set: true}}, + MacOSSetup: fleet.MacOSSetup{BootstrapPackage: optjson.String{Set: true}, MacOSSetupAssistant: optjson.String{Set: true}}, + MacOSUpdates: fleet.MacOSUpdates{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}}, }, }, { name: "ssoShortEntityID", diff --git a/server/service/integration_enterprise_test.go b/server/service/integration_enterprise_test.go index 44b2474c4f..b7c9478ef5 100644 --- a/server/service/integration_enterprise_test.go +++ b/server/service/integration_enterprise_test.go @@ -121,8 +121,8 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() { }, team.Config.Features) require.Equal(t, fleet.TeamMDM{ MacOSUpdates: fleet.MacOSUpdates{ - MinimumVersion: "10.15.0", - Deadline: "2021-01-01", + MinimumVersion: optjson.SetString("10.15.0"), + Deadline: optjson.SetString("2021-01-01"), }, MacOSSetup: fleet.MacOSSetup{ // because the MacOSSetup was marshalled to JSON to be saved in the DB, @@ -1448,80 +1448,125 @@ func (s *integrationEnterpriseTestSuite) TestMacOSUpdatesConfig() { team.ID = tmResp.Team.ID // modify the team's config - s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), fleet.TeamPayload{ - MDM: &fleet.TeamPayloadMDM{ - MacOSUpdates: &fleet.MacOSUpdates{ - MinimumVersion: "10.15.0", - Deadline: "2021-01-01", + s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{ + "mdm": map[string]any{ + "macos_updates": &fleet.MacOSUpdates{ + MinimumVersion: optjson.SetString("10.15.0"), + Deadline: optjson.SetString("2021-01-01"), }, }, }, 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) + require.Equal(t, "10.15.0", tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion.Value) + require.Equal(t, "2021-01-01", tmResp.Team.Config.MDM.MacOSUpdates.Deadline.Value) 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{ - MDM: &fleet.TeamPayloadMDM{ - MacOSUpdates: &fleet.MacOSUpdates{ - MinimumVersion: "10.15.0", - Deadline: "2025-10-01", + s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{ + "mdm": map[string]any{ + "macos_updates": &fleet.MacOSUpdates{ + MinimumVersion: optjson.SetString("10.15.0"), + Deadline: optjson.SetString("2025-10-01"), }, }, }, 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) + require.Equal(t, "10.15.0", tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion.Value) + require.Equal(t, "2025-10-01", tmResp.Team.Config.MDM.MacOSUpdates.Deadline.Value) 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) + // sending a nil MDM or MacOSUpdate config doesn't modify anything + s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{ + "mdm": nil, + }, http.StatusOK, &tmResp) + s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{ + "mdm": map[string]any{ + "macos_updates": nil, + }, + }, http.StatusOK, &tmResp) + require.Equal(t, "10.15.0", tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion.Value) + require.Equal(t, "2025-10-01", tmResp.Team.Config.MDM.MacOSUpdates.Deadline.Value) // 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.TeamPayloadMDM{MacOSUpdates: &fleet.MacOSUpdates{}}}, http.StatusOK, &tmResp) - require.Empty(t, tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion) - require.Empty(t, tmResp.Team.Config.MDM.MacOSUpdates.Deadline) + // sending macos settings but no macos_updates does not change the macos updates + s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{ + "mdm": map[string]any{ + "macos_settings": map[string]any{ + "custom_settings": nil, + }, + }, + }, http.StatusOK, &tmResp) + require.Equal(t, "10.15.0", tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion.Value) + require.Equal(t, "2025-10-01", tmResp.Team.Config.MDM.MacOSUpdates.Deadline.Value) + // no new activity is created + s.lastActivityMatches("", "", lastActivity) + + // sending empty MacOSUpdate fields empties both fields + s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{ + "mdm": map[string]any{ + "macos_updates": map[string]any{ + "minimum_version": "", + "deadline": nil, + }, + }, + }, http.StatusOK, &tmResp) + require.Empty(t, tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion.Value) + require.Empty(t, tmResp.Team.Config.MDM.MacOSUpdates.Deadline.Value) s.lastActivityMatches(fleet.ActivityTypeEditedMacOSMinVersion{}.ActivityName(), fmt.Sprintf(`{"team_id": %d, "team_name": %q, "minimum_version": "", "deadline": ""}`, team.ID, team.Name), 0) // error checks: // try to set an invalid deadline - s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), fleet.TeamPayload{ - MDM: &fleet.TeamPayloadMDM{ - MacOSUpdates: &fleet.MacOSUpdates{ - MinimumVersion: "10.15.0", - Deadline: "2021-01-01T00:00:00Z", + s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{ + "mdm": map[string]any{ + "macos_updates": map[string]any{ + "minimum_version": "10.15.0", + "deadline": "2021-01-01T00:00:00Z", }, }, }, http.StatusUnprocessableEntity, &tmResp) // try to set an invalid minimum version - s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), fleet.TeamPayload{ - MDM: &fleet.TeamPayloadMDM{ - MacOSUpdates: &fleet.MacOSUpdates{ - MinimumVersion: "10.15.0 (19A583)", - Deadline: "2021-01-01T00:00:00Z", + s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{ + "mdm": map[string]any{ + "macos_updates": map[string]any{ + "minimum_version": "10.15.0 (19A583)", + "deadline": "2021-01-01T00:00:00Z", }, }, }, http.StatusUnprocessableEntity, &tmResp) // try to set a deadline but not a minimum version - s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), fleet.TeamPayload{ - MDM: &fleet.TeamPayloadMDM{ - MacOSUpdates: &fleet.MacOSUpdates{ - Deadline: "2021-01-01T00:00:00Z", + s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{ + "mdm": map[string]any{ + "macos_updates": map[string]any{ + "deadline": "2021-01-01T00:00:00Z", + }, + }, + }, http.StatusUnprocessableEntity, &tmResp) + + // try to set an empty deadline but not a minimum version + s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{ + "mdm": map[string]any{ + "macos_updates": map[string]any{ + "deadline": "", }, }, }, http.StatusUnprocessableEntity, &tmResp) // try to set a minimum version but not a deadline - s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), fleet.TeamPayload{ - MDM: &fleet.TeamPayloadMDM{ - MacOSUpdates: &fleet.MacOSUpdates{ - MinimumVersion: "10.15.0 (19A583)", + s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{ + "mdm": map[string]any{ + "macos_updates": map[string]any{ + "minimum_version": "10.15.0 (19A583)", + }, + }, + }, http.StatusUnprocessableEntity, &tmResp) + + // try to set an empty minimum version but not a deadline + s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{ + "mdm": map[string]any{ + "macos_updates": map[string]any{ + "minimum_version": "", }, }, }, http.StatusUnprocessableEntity, &tmResp) @@ -1745,7 +1790,7 @@ func (s *integrationEnterpriseTestSuite) TestMDMMacOSUpdates() { // get the appconfig, nothing changed acResp = appConfigResponse{} s.DoJSON("GET", "/api/latest/fleet/config", nil, http.StatusOK, &acResp) - require.Equal(t, fleet.MacOSUpdates{}, acResp.MDM.MacOSUpdates) + require.Equal(t, fleet.MacOSUpdates{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}}, acResp.MDM.MacOSUpdates) // no activity got created activitiesResp = listActivitiesResponse{} @@ -1804,8 +1849,8 @@ func (s *integrationEnterpriseTestSuite) TestMDMMacOSUpdates() { } } }`), http.StatusOK, &acResp) - require.Equal(t, "12.3.1", acResp.MDM.MacOSUpdates.MinimumVersion) - require.Equal(t, "2022-01-01", acResp.MDM.MacOSUpdates.Deadline) + require.Equal(t, "12.3.1", acResp.MDM.MacOSUpdates.MinimumVersion.Value) + require.Equal(t, "2022-01-01", acResp.MDM.MacOSUpdates.Deadline.Value) // 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) @@ -1813,8 +1858,8 @@ func (s *integrationEnterpriseTestSuite) TestMDMMacOSUpdates() { // 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) + require.Equal(t, "12.3.1", acResp.MDM.MacOSUpdates.MinimumVersion.Value) + require.Equal(t, "2022-01-01", acResp.MDM.MacOSUpdates.Deadline.Value) // update the deadline acResp = appConfigResponse{} @@ -1826,8 +1871,8 @@ func (s *integrationEnterpriseTestSuite) TestMDMMacOSUpdates() { } } }`), http.StatusOK, &acResp) - require.Equal(t, "12.3.1", acResp.MDM.MacOSUpdates.MinimumVersion) - require.Equal(t, "2024-01-01", acResp.MDM.MacOSUpdates.Deadline) + require.Equal(t, "12.3.1", acResp.MDM.MacOSUpdates.MinimumVersion.Value) + require.Equal(t, "2024-01-01", acResp.MDM.MacOSUpdates.Deadline.Value) // 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) @@ -1835,6 +1880,8 @@ func (s *integrationEnterpriseTestSuite) TestMDMMacOSUpdates() { // 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) + require.Equal(t, "12.3.1", acResp.MDM.MacOSUpdates.MinimumVersion.Value) + require.Equal(t, "2024-01-01", acResp.MDM.MacOSUpdates.Deadline.Value) // no activity got created s.lastActivityMatches("", ``, lastActivity) @@ -1849,8 +1896,8 @@ func (s *integrationEnterpriseTestSuite) TestMDMMacOSUpdates() { } } }`), http.StatusOK, &acResp) - require.Empty(t, acResp.MDM.MacOSUpdates.MinimumVersion) - require.Empty(t, acResp.MDM.MacOSUpdates.Deadline) + require.Empty(t, acResp.MDM.MacOSUpdates.MinimumVersion.Value) + require.Empty(t, acResp.MDM.MacOSUpdates.Deadline.Value) // 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) @@ -1865,8 +1912,8 @@ func (s *integrationEnterpriseTestSuite) TestMDMMacOSUpdates() { } } }`), http.StatusOK, &acResp) - require.Empty(t, acResp.MDM.MacOSUpdates.MinimumVersion) - require.Empty(t, acResp.MDM.MacOSUpdates.Deadline) + require.Empty(t, acResp.MDM.MacOSUpdates.MinimumVersion.Value) + require.Empty(t, acResp.MDM.MacOSUpdates.Deadline.Value) // no activity got created s.lastActivityMatches("", ``, lastActivity) @@ -2570,7 +2617,7 @@ func (s *integrationEnterpriseTestSuite) TestOrbitConfigNudgeSettings() { resp = orbitGetConfigResponse{} s.DoJSON("POST", "/api/fleet/orbit/config", json.RawMessage(fmt.Sprintf(`{"orbit_node_key": %q}`, *h.OrbitNodeKey)), http.StatusOK, &resp) - wantCfg, err := fleet.NewNudgeConfig(fleet.MacOSUpdates{Deadline: "2022-01-04", MinimumVersion: "12.1.3"}) + wantCfg, err := fleet.NewNudgeConfig(fleet.MacOSUpdates{Deadline: optjson.SetString("2022-01-04"), MinimumVersion: optjson.SetString("12.1.3")}) require.NoError(t, err) require.Equal(t, wantCfg, resp.NudgeConfig) require.Equal(t, wantCfg.OSVersionRequirements[0].RequiredInstallationDate.String(), "2022-01-04 04:00:00 +0000 UTC") @@ -2598,15 +2645,15 @@ func (s *integrationEnterpriseTestSuite) TestOrbitConfigNudgeSettings() { s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), fleet.TeamPayload{ MDM: &fleet.TeamPayloadMDM{ MacOSUpdates: &fleet.MacOSUpdates{ - Deadline: "1992-01-01", - MinimumVersion: "13.1.1", + Deadline: optjson.SetString("1992-01-01"), + MinimumVersion: optjson.SetString("13.1.1"), }, }, }, http.StatusOK, &tmResp) resp = orbitGetConfigResponse{} s.DoJSON("POST", "/api/fleet/orbit/config", json.RawMessage(fmt.Sprintf(`{"orbit_node_key": %q}`, *h.OrbitNodeKey)), http.StatusOK, &resp) - wantCfg, err = fleet.NewNudgeConfig(fleet.MacOSUpdates{Deadline: "1992-01-01", MinimumVersion: "13.1.1"}) + wantCfg, err = fleet.NewNudgeConfig(fleet.MacOSUpdates{Deadline: optjson.SetString("1992-01-01"), MinimumVersion: optjson.SetString("13.1.1")}) require.NoError(t, err) require.Equal(t, wantCfg, resp.NudgeConfig) require.Equal(t, wantCfg.OSVersionRequirements[0].RequiredInstallationDate.String(), "1992-01-01 04:00:00 +0000 UTC") @@ -2615,7 +2662,7 @@ func (s *integrationEnterpriseTestSuite) TestOrbitConfigNudgeSettings() { h2 := createOrbitEnrolledHost(t, "darwin", "h2", s.ds) resp = orbitGetConfigResponse{} s.DoJSON("POST", "/api/fleet/orbit/config", json.RawMessage(fmt.Sprintf(`{"orbit_node_key": %q}`, *h2.OrbitNodeKey)), http.StatusOK, &resp) - wantCfg, err = fleet.NewNudgeConfig(fleet.MacOSUpdates{Deadline: "2022-01-04", MinimumVersion: "12.1.3"}) + wantCfg, err = fleet.NewNudgeConfig(fleet.MacOSUpdates{Deadline: optjson.SetString("2022-01-04"), MinimumVersion: optjson.SetString("12.1.3")}) require.NoError(t, err) require.Equal(t, wantCfg, resp.NudgeConfig) require.Equal(t, wantCfg.OSVersionRequirements[0].RequiredInstallationDate.String(), "2022-01-04 04:00:00 +0000 UTC") diff --git a/server/service/orbit.go b/server/service/orbit.go index 219949ac4b..9078548d40 100644 --- a/server/service/orbit.go +++ b/server/service/orbit.go @@ -225,8 +225,8 @@ func (svc *Service) GetOrbitConfig(ctx context.Context) (fleet.OrbitConfig, erro var nudgeConfig *fleet.NudgeConfig if mdmConfig != nil && - mdmConfig.MacOSUpdates.Deadline != "" && - mdmConfig.MacOSUpdates.MinimumVersion != "" { + mdmConfig.MacOSUpdates.Deadline.Value != "" && + mdmConfig.MacOSUpdates.MinimumVersion.Value != "" { nudgeConfig, err = fleet.NewNudgeConfig(mdmConfig.MacOSUpdates) if err != nil { return fleet.OrbitConfig{Notifications: notifs}, err @@ -250,8 +250,8 @@ func (svc *Service) GetOrbitConfig(ctx context.Context) (fleet.OrbitConfig, erro } var nudgeConfig *fleet.NudgeConfig - if config.MDM.MacOSUpdates.Deadline != "" && - config.MDM.MacOSUpdates.MinimumVersion != "" { + if config.MDM.MacOSUpdates.Deadline.Value != "" && + config.MDM.MacOSUpdates.MinimumVersion.Value != "" { nudgeConfig, err = fleet.NewNudgeConfig(config.MDM.MacOSUpdates) if err != nil { return fleet.OrbitConfig{Notifications: notifs}, err