Update DEP enrollment flow to apply minimum macOS version check when specified (#40720)

This commit is contained in:
Sarah Gillespie
2026-03-12 16:54:46 -05:00
committed by GitHub
parent d2332c5b2e
commit 85af52667d
19 changed files with 720 additions and 230 deletions
+1
View File
@@ -0,0 +1 @@
- Updated DEP enrollment flow to apply minimum macOS version check when specified.
+36 -26
View File
@@ -27,6 +27,7 @@ import (
apple_mdm "github.com/fleetdm/fleet/v4/server/mdm/apple"
nanodep_client "github.com/fleetdm/fleet/v4/server/mdm/nanodep/client"
"github.com/fleetdm/fleet/v4/server/mdm/nanodep/tokenpki"
mdmtest "github.com/fleetdm/fleet/v4/server/mdm/testing_utils"
"github.com/fleetdm/fleet/v4/server/mock"
mdmmock "github.com/fleetdm/fleet/v4/server/mock/mdm"
nanodep_mock "github.com/fleetdm/fleet/v4/server/mock/nanodep"
@@ -206,6 +207,9 @@ func TestApplyTeamSpecs(t *testing.T) {
license := &fleet.LicenseInfo{Tier: fleet.TierPremium, Expiration: time.Now().Add(24 * time.Hour)}
_, ds := testing_utils.RunServerWithMockedDS(t, &service.TestServerOpts{License: license})
// Mock Apple GDMF API (required for validating OS update minimum version settings)
mdmtest.StartNewAppleGDMFTestServer(t)
teamsByName := map[string]*fleet.Team{
"team1": {
ID: 42,
@@ -321,7 +325,7 @@ spec:
- secret: AAA
mdm:
macos_updates:
minimum_version: 12.3.1
minimum_version: 14.6.1
deadline: 2011-03-01
update_new_hosts: true
`)
@@ -329,7 +333,7 @@ spec:
newAgentOpts := json.RawMessage(`{"config":{"views":{"foo":"bar"}}}`)
newMDMSettings := fleet.TeamMDM{
MacOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("12.3.1"),
MinimumVersion: optjson.SetString("14.6.1"),
Deadline: optjson.SetString("2011-03-01"),
UpdateNewHosts: optjson.SetBool(true),
},
@@ -368,7 +372,7 @@ spec:
require.Equal(t, "[+] applied 1 fleet\n", RunAppForTest(t, []string{"apply", "-f", filename}))
newMDMSettings = fleet.TeamMDM{
MacOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("12.3.1"),
MinimumVersion: optjson.SetString("14.6.1"),
Deadline: optjson.SetString("2011-03-01"),
UpdateNewHosts: optjson.SetBool(true),
},
@@ -398,7 +402,7 @@ spec:
newMDMSettings = fleet.TeamMDM{
MacOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("12.3.1"),
MinimumVersion: optjson.SetString("14.6.1"),
Deadline: optjson.SetString("2011-03-01"),
UpdateNewHosts: optjson.SetBool(true),
},
@@ -436,13 +440,13 @@ spec:
name: team1
mdm:
macos_updates:
minimum_version: 10.10.10
minimum_version: 14.6.1
deadline: 1992-03-01
ios_updates:
minimum_version: 11.11.11
minimum_version: 17.6.1
deadline: 1993-04-02
ipados_updates:
minimum_version: 12.12.12
minimum_version: 17.6.1
deadline: 1994-05-03
secrets:
- secret: BBB
@@ -450,15 +454,15 @@ spec:
newMDMSettings = fleet.TeamMDM{
MacOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("10.10.10"),
MinimumVersion: optjson.SetString("14.6.1"),
Deadline: optjson.SetString("1992-03-01"),
},
IOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("11.11.11"),
MinimumVersion: optjson.SetString("17.6.1"),
Deadline: optjson.SetString("1993-04-02"),
},
IPadOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("12.12.12"),
MinimumVersion: optjson.SetString("17.6.1"),
Deadline: optjson.SetString("1994-05-03"),
},
WindowsUpdates: fleet.WindowsUpdates{
@@ -690,6 +694,9 @@ func TestApplyAppConfig(t *testing.T) {
license := &fleet.LicenseInfo{Tier: fleet.TierPremium, Expiration: time.Now().Add(24 * time.Hour)}
_, ds := testing_utils.RunServerWithMockedDS(t, &service.TestServerOpts{License: license})
// Mock Apple GDMF API (required for validating OS update minimum version settings)
mdmtest.StartNewAppleGDMFTestServer(t)
ds.ListUsersFunc = func(ctx context.Context, opt fleet.UserListOptions) ([]*fleet.User, error) {
return userRoleSpecList, nil
}
@@ -776,7 +783,7 @@ spec:
mdm:
apple_bm_default_team: "team1"
macos_updates:
minimum_version: 12.1.1
minimum_version: 14.6.1
deadline: 2011-02-01
windows_updates:
deadline_days: 5
@@ -787,7 +794,7 @@ spec:
DeprecatedAppleBMDefaultTeam: "team1",
AppleBMTermsExpired: false,
MacOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("12.1.1"),
MinimumVersion: optjson.SetString("14.6.1"),
Deadline: optjson.SetString("2011-02-01"),
},
MacOSSetup: fleet.MacOSSetup{
@@ -870,7 +877,7 @@ spec:
DeprecatedAppleBMDefaultTeam: "team1",
AppleBMTermsExpired: false,
MacOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("12.1.1"),
MinimumVersion: optjson.SetString("14.6.1"),
Deadline: optjson.SetString("2011-02-01"),
},
MacOSSetup: fleet.MacOSSetup{
@@ -1343,6 +1350,9 @@ func TestApplyAsGitOps(t *testing.T) {
// Mock Apple DEP API
depStorage := SetupMockDEPStorageAndMockDEPServer(t)
// Mock Apple GDMF API (required for validating OS update minimum version settings)
mdmtest.StartNewAppleGDMFTestServer(t)
config.SetTestMDMConfig(t, &fleetCfg, testCertPEM, testKeyPEM, "../../../server/service/testdata")
_, ds := testing_utils.RunServerWithMockedDS(t, &service.TestServerOpts{
@@ -1541,7 +1551,7 @@ kind: config
spec:
mdm:
macos_updates:
minimum_version: 10.10.10
minimum_version: 14.6.1
deadline: 2020-02-02
windows_updates:
deadline_days: 1
@@ -1570,7 +1580,7 @@ spec:
LockEndUserInfo: optjson.SetBool(false),
},
MacOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("10.10.10"),
MinimumVersion: optjson.SetString("14.6.1"),
Deadline: optjson.SetString("2020-02-02"),
},
WindowsUpdates: fleet.WindowsUpdates{
@@ -1614,7 +1624,7 @@ spec:
LockEndUserInfo: optjson.SetBool(false),
},
MacOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("10.10.10"),
MinimumVersion: optjson.SetString("14.6.1"),
Deadline: optjson.SetString("2020-02-02"),
},
WindowsUpdates: fleet.WindowsUpdates{
@@ -1641,7 +1651,7 @@ spec:
mdm:
enable_disk_encryption: false
macos_updates:
minimum_version: 10.10.10
minimum_version: 14.6.1
deadline: 1992-03-01
windows_updates:
deadline_days: 0
@@ -1665,7 +1675,7 @@ spec:
CustomSettings: []fleet.MDMProfileSpec{{Path: mobileConfigPath}},
},
MacOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("10.10.10"),
MinimumVersion: optjson.SetString("14.6.1"),
Deadline: optjson.SetString("1992-03-01"),
},
MacOSSetup: fleet.MacOSSetup{
@@ -1708,7 +1718,7 @@ spec:
CustomSettings: []fleet.MDMProfileSpec{{Path: mobileConfigPath}},
},
MacOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("10.10.10"),
MinimumVersion: optjson.SetString("14.6.1"),
Deadline: optjson.SetString("1992-03-01"),
},
WindowsUpdates: fleet.WindowsUpdates{
@@ -1746,7 +1756,7 @@ spec:
CustomSettings: []fleet.MDMProfileSpec{{Path: mobileConfigPath}},
},
MacOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("10.10.10"),
MinimumVersion: optjson.SetString("14.6.1"),
Deadline: optjson.SetString("1992-03-01"),
},
WindowsUpdates: fleet.WindowsUpdates{
@@ -3457,7 +3467,7 @@ spec:
minimum_version: "12.2"
deadline: "1892-01-01T00:00:00Z"
`,
wantErr: `422 Validation Failed: deadline accepts YYYY-MM-DD format only (E.g., "2023-06-01.")`,
wantErr: fmt.Sprintf(`422 Validation Failed: %s`, fleet.AppleOSVersionDeadlineInvalidMessage),
},
{
desc: "macos_updates.deadline with invalid date",
@@ -3472,7 +3482,7 @@ spec:
minimum_version: "12.2"
deadline: "18-01-01"
`,
wantErr: `422 Validation Failed: deadline accepts YYYY-MM-DD format only (E.g., "2023-06-01.")`,
wantErr: fmt.Sprintf(`422 Validation Failed: %s`, fleet.AppleOSVersionDeadlineInvalidMessage),
},
{
desc: "macos_updates.deadline with incomplete date",
@@ -3487,7 +3497,7 @@ spec:
minimum_version: "12.2"
deadline: "2022-01"
`,
wantErr: `422 Validation Failed: deadline accepts YYYY-MM-DD format only (E.g., "2023-06-01.")`,
wantErr: fmt.Sprintf(`422 Validation Failed: %s`, fleet.AppleOSVersionDeadlineInvalidMessage),
},
{
desc: "windows_updates.deadline_days but grace period empty",
@@ -3782,7 +3792,7 @@ spec:
minimum_version: "12.2"
deadline: "1892-01-01T00:00:00Z"
`,
wantErr: `422 Validation Failed: deadline accepts YYYY-MM-DD format only (E.g., "2023-06-01.")`,
wantErr: fmt.Sprintf(`422 Validation Failed: %s`, fleet.AppleOSVersionDeadlineInvalidMessage),
},
{
desc: "app config macos_updates.deadline with invalid date",
@@ -3795,7 +3805,7 @@ spec:
minimum_version: "12.2"
deadline: "18-01-01"
`,
wantErr: `422 Validation Failed: deadline accepts YYYY-MM-DD format only (E.g., "2023-06-01.")`,
wantErr: fmt.Sprintf(`422 Validation Failed: %s`, fleet.AppleOSVersionDeadlineInvalidMessage),
},
{
desc: "app config macos_updates.deadline with incomplete date",
@@ -3808,7 +3818,7 @@ spec:
minimum_version: "12.2"
deadline: "2022-01"
`,
wantErr: `422 Validation Failed: deadline accepts YYYY-MM-DD format only (E.g., "2023-06-01.")`,
wantErr: fmt.Sprintf(`422 Validation Failed: %s`, fleet.AppleOSVersionDeadlineInvalidMessage),
},
{
desc: "app config windows_updates.deadline_days but grace period empty",
+40 -26
View File
@@ -23,6 +23,7 @@ import (
"github.com/fleetdm/fleet/v4/server/mdm"
apple_mdm "github.com/fleetdm/fleet/v4/server/mdm/apple"
"github.com/fleetdm/fleet/v4/server/mdm/nanodep/tokenpki"
mdmtest "github.com/fleetdm/fleet/v4/server/mdm/testing_utils"
mdmtesting "github.com/fleetdm/fleet/v4/server/mdm/testing_utils"
"github.com/fleetdm/fleet/v4/server/mock"
digicert_mock "github.com/fleetdm/fleet/v4/server/mock/digicert"
@@ -335,6 +336,9 @@ func TestGitOpsBasicGlobalPremium(t *testing.T) {
},
)
// Mock Apple GDMF API (required for validating OS update minimum version settings)
mdmtest.StartNewAppleGDMFTestServer(t)
ds.BatchSetMDMProfilesFunc = func(
ctx context.Context, tmID *uint, macProfiles []*fleet.MDMAppleConfigProfile, winProfiles []*fleet.MDMWindowsConfigProfile,
macDecls []*fleet.MDMAppleDeclaration, androidProfiles []*fleet.MDMAndroidConfigProfile, vars []fleet.MDMProfileIdentifierFleetVariables,
@@ -486,14 +490,14 @@ func TestGitOpsBasicGlobalPremium(t *testing.T) {
controls:
macos_updates:
deadline: "2024-03-03"
minimum_version: "18.0"
minimum_version: "14.6.1"
ios_updates:
deadline: "2022-02-02"
minimum_version: "17.6"
update_new_hosts: true
ipados_updates:
deadline: "2023-03-03"
minimum_version: "18.0"
minimum_version: "17.6"
update_new_hosts: false
enable_disk_encryption: true
windows_require_bitlocker_pin: true
@@ -565,7 +569,7 @@ software:
// Check MDM settings
require.True(t, savedAppConfig.MDM.EnableDiskEncryption.Value)
require.True(t, savedAppConfig.MDM.RequireBitLockerPIN.Value)
require.Equal(t, "18.0", savedAppConfig.MDM.MacOSUpdates.MinimumVersion.Value)
require.Equal(t, "14.6.1", savedAppConfig.MDM.MacOSUpdates.MinimumVersion.Value)
require.Equal(t, "2024-03-03", savedAppConfig.MDM.MacOSUpdates.Deadline.Value)
// To keep things backwards compatible if MinimumVersion & Deadline are set, then UpdateNewHosts should be set to true
require.Equal(t, optjson.SetBool(true), savedAppConfig.MDM.MacOSUpdates.UpdateNewHosts)
@@ -573,7 +577,7 @@ software:
require.Equal(t, "17.6", savedAppConfig.MDM.IOSUpdates.MinimumVersion.Value)
require.Equal(t, optjson.Bool{}, savedAppConfig.MDM.IOSUpdates.UpdateNewHosts)
require.Equal(t, "2023-03-03", savedAppConfig.MDM.IPadOSUpdates.Deadline.Value)
require.Equal(t, "18.0", savedAppConfig.MDM.IPadOSUpdates.MinimumVersion.Value)
require.Equal(t, "17.6", savedAppConfig.MDM.IPadOSUpdates.MinimumVersion.Value)
require.Equal(t, optjson.Bool{}, savedAppConfig.MDM.IPadOSUpdates.UpdateNewHosts)
// Check certificate authorities
@@ -673,6 +677,9 @@ func TestGitOpsBasicTeam(t *testing.T) {
},
)
// Mock Apple GDMF API (required for validating OS update minimum version settings)
mdmtest.StartNewAppleGDMFTestServer(t)
const secret = "TestSecret"
ds.SetTeamVPPAppsFunc = func(ctx context.Context, teamID *uint, adamIDs []fleet.VPPAppTeam, _ map[string]uint) (bool, error) {
@@ -842,11 +849,11 @@ func TestGitOpsBasicTeam(t *testing.T) {
controls:
macos_updates:
deadline: "2025-10-10"
minimum_version: "18.0"
minimum_version: "14.6.1"
update_new_hosts: false
ios_updates:
deadline: "2024-10-10"
minimum_version: "18.0"
minimum_version: "17.6"
ipados_updates:
deadline: "2025-11-11"
minimum_version: "17.6"
@@ -911,7 +918,7 @@ software:
assert.True(t, savedTeam.Config.Features.EnableSoftwareInventory)
assert.Equal(t, "2025-10-10", savedTeam.Config.MDM.MacOSUpdates.Deadline.Value)
assert.Equal(t, "18.0", savedTeam.Config.MDM.MacOSUpdates.MinimumVersion.Value)
assert.Equal(t, "14.6.1", savedTeam.Config.MDM.MacOSUpdates.MinimumVersion.Value)
// Ensure the default value (if deadline and minimum_version are set) can be overriden with explicit setting
assert.Equal(t, optjson.SetBool(false), savedTeam.Config.MDM.MacOSUpdates.UpdateNewHosts)
@@ -2093,6 +2100,10 @@ func TestGitOpsBasicGlobalAndNoTeam(t *testing.T) {
KeyValueStore: testing_utils.NewMemKeyValueStore(),
},
)
// Mock Apple GDMF API (required for validating OS update minimum version settings)
mdmtest.StartNewAppleGDMFTestServer(t)
// Mock appConfig
savedAppConfig := &fleet.AppConfig{}
ds.AppConfigFunc = func(ctx context.Context) (*fleet.AppConfig, error) {
@@ -2347,7 +2358,7 @@ software:
controls:
ipados_updates:
deadline: "2023-03-03"
minimum_version: "18.0"
minimum_version: "17.6.1"
policies:
name: No team
software:
@@ -5707,6 +5718,9 @@ func TestGitOpsAppleOSUpdates(t *testing.T) {
},
)
// Mock Apple GDMF API (required for validating OS update minimum version settings)
mdmtest.StartNewAppleGDMFTestServer(t)
const localTeamName = "Team1"
var savedTeam *fleet.Team
baseTeam := &fleet.Team{
@@ -5924,11 +5938,11 @@ software:
t.Run("macos_updates", func(t *testing.T) {
t.Run("same values do not trigger BulkSetPendingMDMHostProfiles", func(t *testing.T) {
bulkSetPendingCalls = 0
savedTeam = existingTeamWithMacOSUpdates("2024-03-03", "14.0")
savedTeam = existingTeamWithMacOSUpdates("2024-03-03", "14.6.1")
teamFile, err := os.CreateTemp(t.TempDir(), "*.yml")
require.NoError(t, err)
_, err = teamFile.WriteString(macOSYAML("2024-03-03", "14.0"))
_, err = teamFile.WriteString(macOSYAML("2024-03-03", "14.6.1"))
require.NoError(t, err)
_ = RunAppForTest(t, []string{"gitops", "-f", teamFile.Name()})
@@ -5938,11 +5952,11 @@ software:
t.Run("changed deadline triggers BulkSetPendingMDMHostProfiles", func(t *testing.T) {
bulkSetPendingCalls = 0
savedTeam = existingTeamWithMacOSUpdates("2024-03-03", "14.0")
savedTeam = existingTeamWithMacOSUpdates("2024-03-03", "14.6.1")
teamFile, err := os.CreateTemp(t.TempDir(), "*.yml")
require.NoError(t, err)
_, err = teamFile.WriteString(macOSYAML("2024-04-04", "14.0"))
_, err = teamFile.WriteString(macOSYAML("2024-04-04", "14.6.1"))
require.NoError(t, err)
_ = RunAppForTest(t, []string{"gitops", "-f", teamFile.Name()})
@@ -5952,11 +5966,11 @@ software:
t.Run("changed minimum_version triggers BulkSetPendingMDMHostProfiles", func(t *testing.T) {
bulkSetPendingCalls = 0
savedTeam = existingTeamWithMacOSUpdates("2024-03-03", "14.0")
savedTeam = existingTeamWithMacOSUpdates("2024-03-03", "14.6.1")
teamFile, err := os.CreateTemp(t.TempDir(), "*.yml")
require.NoError(t, err)
_, err = teamFile.WriteString(macOSYAML("2024-03-03", "15.0"))
_, err = teamFile.WriteString(macOSYAML("2024-03-03", "13.6.9"))
require.NoError(t, err)
_ = RunAppForTest(t, []string{"gitops", "-f", teamFile.Name()})
@@ -5968,11 +5982,11 @@ software:
t.Run("ios_updates", func(t *testing.T) {
t.Run("same values do not trigger BulkSetPendingMDMHostProfiles", func(t *testing.T) {
bulkSetPendingCalls = 0
savedTeam = existingTeamWithIOSUpdates("2024-03-03", "17.0")
savedTeam = existingTeamWithIOSUpdates("2024-03-03", "17.6.1")
teamFile, err := os.CreateTemp(t.TempDir(), "*.yml")
require.NoError(t, err)
_, err = teamFile.WriteString(iOSYAML("2024-03-03", "17.0"))
_, err = teamFile.WriteString(iOSYAML("2024-03-03", "17.6.1"))
require.NoError(t, err)
_ = RunAppForTest(t, []string{"gitops", "-f", teamFile.Name()})
@@ -5982,11 +5996,11 @@ software:
t.Run("changed deadline triggers BulkSetPendingMDMHostProfiles", func(t *testing.T) {
bulkSetPendingCalls = 0
savedTeam = existingTeamWithIOSUpdates("2024-03-03", "17.0")
savedTeam = existingTeamWithIOSUpdates("2024-03-03", "17.6.1")
teamFile, err := os.CreateTemp(t.TempDir(), "*.yml")
require.NoError(t, err)
_, err = teamFile.WriteString(iOSYAML("2024-04-04", "17.0"))
_, err = teamFile.WriteString(iOSYAML("2024-04-04", "17.6.1"))
require.NoError(t, err)
_ = RunAppForTest(t, []string{"gitops", "-f", teamFile.Name()})
@@ -5996,11 +6010,11 @@ software:
t.Run("changed minimum_version triggers BulkSetPendingMDMHostProfiles", func(t *testing.T) {
bulkSetPendingCalls = 0
savedTeam = existingTeamWithIOSUpdates("2024-03-03", "17.0")
savedTeam = existingTeamWithIOSUpdates("2024-03-03", "17.6.1")
teamFile, err := os.CreateTemp(t.TempDir(), "*.yml")
require.NoError(t, err)
_, err = teamFile.WriteString(iOSYAML("2024-03-03", "18.0"))
_, err = teamFile.WriteString(iOSYAML("2024-03-03", "17.6"))
require.NoError(t, err)
_ = RunAppForTest(t, []string{"gitops", "-f", teamFile.Name()})
@@ -6012,11 +6026,11 @@ software:
t.Run("ipados_updates", func(t *testing.T) {
t.Run("same values do not trigger BulkSetPendingMDMHostProfiles", func(t *testing.T) {
bulkSetPendingCalls = 0
savedTeam = existingTeamWithIPadOSUpdates("2024-03-03", "17.0")
savedTeam = existingTeamWithIPadOSUpdates("2024-03-03", "17.6.1")
teamFile, err := os.CreateTemp(t.TempDir(), "*.yml")
require.NoError(t, err)
_, err = teamFile.WriteString(iPadOSYAML("2024-03-03", "17.0"))
_, err = teamFile.WriteString(iPadOSYAML("2024-03-03", "17.6.1"))
require.NoError(t, err)
_ = RunAppForTest(t, []string{"gitops", "-f", teamFile.Name()})
@@ -6026,11 +6040,11 @@ software:
t.Run("changed deadline triggers BulkSetPendingMDMHostProfiles", func(t *testing.T) {
bulkSetPendingCalls = 0
savedTeam = existingTeamWithIPadOSUpdates("2024-03-03", "17.0")
savedTeam = existingTeamWithIPadOSUpdates("2024-03-03", "17.6.1")
teamFile, err := os.CreateTemp(t.TempDir(), "*.yml")
require.NoError(t, err)
_, err = teamFile.WriteString(iPadOSYAML("2024-04-04", "17.0"))
_, err = teamFile.WriteString(iPadOSYAML("2024-04-04", "17.6.1"))
require.NoError(t, err)
_ = RunAppForTest(t, []string{"gitops", "-f", teamFile.Name()})
@@ -6040,11 +6054,11 @@ software:
t.Run("changed minimum_version triggers BulkSetPendingMDMHostProfiles", func(t *testing.T) {
bulkSetPendingCalls = 0
savedTeam = existingTeamWithIPadOSUpdates("2024-03-03", "17.0")
savedTeam = existingTeamWithIPadOSUpdates("2024-03-03", "17.6.1")
teamFile, err := os.CreateTemp(t.TempDir(), "*.yml")
require.NoError(t, err)
_, err = teamFile.WriteString(iPadOSYAML("2024-03-03", "18.0"))
_, err = teamFile.WriteString(iPadOSYAML("2024-03-03", "17.6"))
require.NoError(t, err)
_ = RunAppForTest(t, []string{"gitops", "-f", teamFile.Name()})
+16
View File
@@ -19,6 +19,7 @@ import (
"github.com/fleetdm/fleet/v4/server/contexts/logging"
"github.com/fleetdm/fleet/v4/server/contexts/viewer"
"github.com/fleetdm/fleet/v4/server/fleet"
apple_mdm "github.com/fleetdm/fleet/v4/server/mdm/apple"
"github.com/fleetdm/fleet/v4/server/ptr"
"github.com/fleetdm/fleet/v4/server/worker"
)
@@ -212,6 +213,19 @@ func (svc *Service) ModifyTeam(ctx context.Context, teamID uint, payload fleet.T
}
}
// Always check whether specified versions are supported by Apple (even if they weren't updated)
// Note that we're validating against the full, non-public asset set of OS versions here because
// in our DEP flow the minimum version just acts as the threshold for whether or not to update
// the host to the latest, public version. We don't need to install the specified version on the
// host during DEP so it doesn't need to be in the public asset set.
if errs := apple_mdm.ValidateMDMSettingsAppleSupportedOSVersion(team.Config.MDM, false); len(errs) > 0 {
invalid := &fleet.InvalidArgumentError{}
for k, v := range errs {
invalid.Append(k, v.Error())
}
return nil, invalid
}
if payload.MDM.WindowsUpdates != nil {
if err := payload.MDM.WindowsUpdates.Validate(); err != nil {
return nil, fleet.NewInvalidArgumentError("windows_updates", err.Error())
@@ -1131,6 +1145,8 @@ func (svc *Service) ApplyTeamSpecs(ctx context.Context, specs []*fleet.TeamSpec,
if len(secrets) > fleet.MaxEnrollSecretsCount {
return nil, ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("secrets", "too many secrets"), "validate secrets")
}
// TODO: should we be we validating the other Apple platforms? if so, we should also include
// ValidateMDMSettingsAppleSupportedOSVersion for each platform
if err := spec.MDM.MacOSUpdates.Validate(); err != nil {
return nil, ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("macos_updates", err.Error()))
}
@@ -1,6 +1,8 @@
import React, { useContext, useState } from "react";
import { isEmpty } from "lodash";
import { AxiosResponse } from "axios";
import { IApiError } from "interfaces/errors";
import { APP_CONTEXT_NO_TEAM_ID } from "interfaces/team";
import { NotificationContext } from "context/notification";
import configAPI from "services/entities/config";
@@ -15,6 +17,7 @@ import validatePresence from "components/forms/validators/validate_presence";
import CustomLink from "components/CustomLink";
import { AppContext } from "context/app";
import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper";
import { getErrorMessage } from "./helpers";
const baseClass = "apple-os-target-form";
@@ -164,8 +167,8 @@ const AppleOSTargetForm = ({
? await configAPI.update(updateData)
: await teamsAPI.update(updateData, currentTeamId);
renderFlash("success", "Successfully updated.");
} catch {
renderFlash("error", "Couldnt update. Please try again.");
} catch (err) {
renderFlash("error", getErrorMessage(err as AxiosResponse<IApiError>));
} finally {
currentTeamId === APP_CONTEXT_NO_TEAM_ID
? refetchAppConfig()
@@ -226,7 +229,7 @@ const AppleOSTargetForm = ({
value={updateNewHosts}
className={`${baseClass}__checkbox`}
labelTooltipContent={
"Hosts that automatically enroll (ADE) are updated to Apple's latest version during macOS Setup Assistant."
"During automated enrollment (ADE), hosts below the minimum version are updated to the latest version. If a minimum version isn't set, all hosts are updated to the latest version."
}
>
Update new hosts to latest
@@ -0,0 +1,28 @@
import React from "react";
import { AxiosResponse } from "axios";
import { IApiError } from "interfaces/errors";
// eslint-disable-next-line import/prefer-default-export
export const getErrorMessage = (err: AxiosResponse<IApiError>) => {
const apiReason = err?.data?.errors?.[0]?.reason?.toLowerCase?.();
if (apiReason?.includes("version isn't supported by apple")) {
return (
<>
Couldn&apos;t update. The <b>Minimum version</b> isn&apos;t supported by
Apple.
</>
);
}
if (apiReason?.includes("deadline isn't a valid date")) {
return (
<>
Couldn&apos;t update. The <b>Deadline</b> isn&apos;t a valid date.
</>
);
}
return "Couldnt update. Please try again.";
};
+1 -1
View File
@@ -371,7 +371,7 @@ func (m AppleOSUpdateSettings) Validate() error {
}
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.")`)
return errors.New(AppleOSVersionDeadlineInvalidMessage)
}
return nil
+2
View File
@@ -25,6 +25,8 @@ var (
AndroidMDMNotConfiguredMessage = "Android MDM isn't turned on. For more information about setting up MDM, please visit https://fleetdm.com/learn-more-about/how-to-connect-android-enterprise"
AppleMDMNotConfiguredMessage = "macOS MDM isn't turned on. Visit https://fleetdm.com/docs/using-fleet to learn how to turn on MDM."
AppleABMDefaultTeamDeprecatedMessage = "mdm.apple_bm_default_team has been deprecated. Please use the new mdm.apple_business_manager key documented here: https://fleetdm.com/learn-more-about/apple-business-manager-gitops"
AppleOSVersionUnsupportedMessage = "The minimum version isn't supported by Apple."
AppleOSVersionDeadlineInvalidMessage = "The deadline isn't a valid date."
CantTurnOffMDMForWindowsHostsMessage = "Can't turn off MDM for Windows hosts."
CantTurnOffMDMForPersonalHostsMessage = "Couldn't turn off MDM. This command isn't available for personal hosts."
CantWipePersonalHostsMessage = "Couldn't wipe. This command isn't available for personal hosts."
+54 -2
View File
@@ -19,6 +19,7 @@ import (
"github.com/fleetdm/fleet/v4/server/contexts/ctxerr"
"github.com/fleetdm/fleet/v4/server/fleet"
"github.com/fleetdm/fleet/v4/server/logging"
"github.com/fleetdm/fleet/v4/server/mdm/apple/gdmf"
"github.com/fleetdm/fleet/v4/server/mdm/apple/mobileconfig"
"github.com/fleetdm/fleet/v4/server/mdm/internal/commonmdm"
"github.com/fleetdm/fleet/v4/server/mdm/nanodep/godep"
@@ -1397,7 +1398,8 @@ func (pb *ProfileBimap) add(wantedProfile, currentProfile *fleet.MDMAppleProfile
type NewActivityFunc = fleet.NewActivityFunc
func IOSiPadOSRefetch(ctx context.Context, ds fleet.Datastore, commander *MDMAppleCommander, logger *slog.Logger,
newActivityFn NewActivityFunc) error {
newActivityFn NewActivityFunc,
) error {
appCfg, err := ds.AppConfig(ctx)
if err != nil {
return ctxerr.Wrap(ctx, err, "fetching app config")
@@ -1511,7 +1513,8 @@ func IOSiPadOSRefetch(ctx context.Context, ds fleet.Datastore, commander *MDMApp
// turnOffMDMIfAPNSFailed checks if the error is an APNSDeliveryError and turns off MDM for the failed devices.
// Returns a boolean value to indicate whether or not MDM was turned off.
func turnOffMDMIfAPNSFailed(ctx context.Context, ds fleet.Datastore, err error, logger *slog.Logger, newActivityFn NewActivityFunc) (bool,
error) {
error,
) {
var e *APNSDeliveryError
if !errors.As(err, &e) {
return false, nil
@@ -1606,6 +1609,55 @@ func IOSiPadOSRevive(ctx context.Context, ds fleet.Datastore, commander *MDMAppl
return nil
}
func ValidateMDMSettingsAppleSupportedOSVersion[T fleet.MDM | fleet.TeamMDM](settings T, excludeNonPublicAssetSets bool) map[string]error {
var macOSUpdates, iOSUpdates, iPadOSUpdates fleet.AppleOSUpdateSettings
if m, ok := any(settings).(fleet.MDM); ok {
macOSUpdates = m.MacOSUpdates
iOSUpdates = m.IOSUpdates
iPadOSUpdates = m.IPadOSUpdates
} else if t, ok := any(settings).(fleet.TeamMDM); ok {
macOSUpdates = t.MacOSUpdates
iOSUpdates = t.IOSUpdates
iPadOSUpdates = t.IPadOSUpdates
} else {
return nil
}
if macOSUpdates.MinimumVersion.Value == "" && iOSUpdates.MinimumVersion.Value == "" && iPadOSUpdates.MinimumVersion.Value == "" {
return nil
}
am, err := gdmf.GetAssetMetadata()
if err != nil {
return map[string]error{"mdm": fmt.Errorf("fetching Apple asset metadata: %w", err)}
} else if am == nil {
// this should never happen, but just in case, return an error indicating that the metadata is not available instead of panicking with a nil pointer dereference
return map[string]error{"mdm": errors.New("Apple asset metadata is not available")}
}
errs := make(map[string]error, 3)
if macOSUpdates.MinimumVersion.Value != "" {
if ok := am.IsSupportedMacOSVersion(macOSUpdates.MinimumVersion.Value, excludeNonPublicAssetSets); !ok {
errs["mdm.macos_updates.minimum_version"] = errors.New(fleet.AppleOSVersionUnsupportedMessage)
}
}
if iOSUpdates.MinimumVersion.Value != "" {
// NOTE: iPod generally falls in the category of iOS in Fleet, but we're only validating against iPhone here
// because we assume Apple will eventually remove iPod versions from the Apple Software Lookup Service
// and we want to avoid breaking workflows for users in that event
if ok := am.IsSupportedIOSVersion(iOSUpdates.MinimumVersion.Value, "iphone", excludeNonPublicAssetSets); !ok {
errs["mdm.ios_updates.minimum_version"] = errors.New(fleet.AppleOSVersionUnsupportedMessage)
}
}
if iPadOSUpdates.MinimumVersion.Value != "" {
if ok := am.IsSupportedIOSVersion(iPadOSUpdates.MinimumVersion.Value, "ipad", excludeNonPublicAssetSets); !ok {
errs["mdm.ipados_updates.minimum_version"] = errors.New(fleet.AppleOSVersionUnsupportedMessage)
}
}
return errs
}
// RecoveryLockCommander defines the interface for sending recovery lock commands.
// This interface is implemented by MDMAppleCommander and allows for testing.
type RecoveryLockCommander interface {
+166
View File
@@ -4,14 +4,18 @@ import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"log/slog"
"net/http"
"net/http/httptest"
"os"
"regexp"
"testing"
"time"
"github.com/fleetdm/fleet/v4/pkg/optjson"
"github.com/fleetdm/fleet/v4/server/dev_mode"
"github.com/fleetdm/fleet/v4/server/fleet"
"github.com/fleetdm/fleet/v4/server/mdm/apple/mobileconfig"
"github.com/fleetdm/fleet/v4/server/mdm/nanodep/client"
@@ -270,6 +274,168 @@ func TestGenerateEnrollmentProfileMobileconfig(t *testing.T) {
}
}
func TestValidateMDMSettingsAppleSupportedOSVersion(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
// load the test data from the file
b, err := os.ReadFile("./gdmf/testdata/gdmf.json")
require.NoError(t, err)
_, err = w.Write(b)
require.NoError(t, err)
}))
t.Cleanup(srv.Close)
dev_mode.SetOverride("FLEET_DEV_GDMF_URL", srv.URL, t)
// selected versions of from testdata/gdmf.json that we'll use in out tests
expectSupportedMacOSPublic := []string{"14.6.1", "13.6.9", "12.7.6", "11.7.10"}
expectSupportedMacOSNonPublic := []string{"14.5", "14.6", "13.6.8", "13.6.7", "12.7.5"}
expectSupportedIOSPublic := "17.6.1"
expectSupportedIOSNonPublic := "17.5.1"
// lastIPodSupportedVersion : = "15.8.3"
// helper function to initialize app config MDM settings with known good versions (tests will modify as needed)
mockAppConfigMDM := func() fleet.MDM {
return fleet.MDM{
MacOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString(expectSupportedMacOSPublic[0]),
},
IOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString(expectSupportedIOSPublic),
},
IPadOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString(expectSupportedIOSPublic),
},
}
}
// helper function to initialize team MDM settings with known good versions (tests will modify as needed)
mockTeamMDM := func() fleet.TeamMDM {
return fleet.TeamMDM{
MacOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString(expectSupportedMacOSPublic[0]),
},
IOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString(expectSupportedIOSPublic),
},
IPadOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString(expectSupportedIOSPublic),
},
}
}
// helper function to check if the error matches expectations for a given platform and log appropriately
checkErr := func(platform string, wantErr string, gotErrs map[string]error, msg string) {
key := fmt.Sprintf("mdm.%s_updates.minimum_version", platform)
if wantErr == "" {
assert.Empty(t, gotErrs, msg+": expected no error for platform %s but got: %v", platform, gotErrs)
} else {
assert.Len(t, gotErrs, 1, msg+": expected error for platform %s but got no errors", platform)
assert.Contains(t, gotErrs, key, msg+": expected error for platform %s but got no error", platform)
assert.ErrorContains(t, gotErrs[key], wantErr, msg+": expected error for platform %s but got: %v", platform, gotErrs[key])
}
}
t.Run("macos", func(t *testing.T) {
t.Run("app config mdm settings", func(t *testing.T) {
ac := mockAppConfigMDM()
for _, v := range expectSupportedMacOSPublic {
ac.MacOSUpdates.MinimumVersion = optjson.SetString(v)
checkErr("macos", "", ValidateMDMSettingsAppleSupportedOSVersion(ac, false), "expect public macOS version to be supported when including non-public asset sets")
checkErr("macos", "", ValidateMDMSettingsAppleSupportedOSVersion(ac, true), "expect public macOS version to be supported when excluding non-public asset sets")
}
for _, v := range expectSupportedMacOSNonPublic {
ac.MacOSUpdates.MinimumVersion = optjson.SetString(v)
checkErr("macos", "", ValidateMDMSettingsAppleSupportedOSVersion(ac, false), "expect non-public macOS version to be supported when including non-public asset sets")
checkErr("macos", fleet.AppleOSVersionUnsupportedMessage, ValidateMDMSettingsAppleSupportedOSVersion(ac, true), "expect non-public macOS version to return error when excluding non-public asset sets")
}
ac.MacOSUpdates.MinimumVersion = optjson.SetString("11.7.9") // not supported in either asset set, so we expect an error in both cases
checkErr("macos", fleet.AppleOSVersionUnsupportedMessage, ValidateMDMSettingsAppleSupportedOSVersion(ac, false), "expect unsupported macOS version to return error when including non-public asset sets")
checkErr("macos", fleet.AppleOSVersionUnsupportedMessage, ValidateMDMSettingsAppleSupportedOSVersion(ac, true), "expect unsupported macOS version to return error when excluding non-public asset sets")
})
t.Run("team mdm settings", func(t *testing.T) {
tm := mockTeamMDM()
for _, v := range expectSupportedMacOSPublic {
tm.MacOSUpdates.MinimumVersion = optjson.SetString(v)
checkErr("macos", "", ValidateMDMSettingsAppleSupportedOSVersion(tm, false), "expect public macOS version to be supported when including non-public asset sets")
checkErr("macos", "", ValidateMDMSettingsAppleSupportedOSVersion(tm, true), "expect public macOS version to be supported when excluding non-public asset sets")
}
for _, v := range expectSupportedMacOSNonPublic {
tm.MacOSUpdates.MinimumVersion = optjson.SetString(v)
checkErr("macos", "", ValidateMDMSettingsAppleSupportedOSVersion(tm, false), "expect non-public macOS version to be supported when including non-public asset sets")
checkErr("macos", fleet.AppleOSVersionUnsupportedMessage, ValidateMDMSettingsAppleSupportedOSVersion(tm, true), "expect non-public macOS version to return error when excluding non-public asset sets")
}
tm.MacOSUpdates.MinimumVersion = optjson.SetString("11.7.9") // not supported in either asset set, so we expect an error in both cases
checkErr("macos", fleet.AppleOSVersionUnsupportedMessage, ValidateMDMSettingsAppleSupportedOSVersion(tm, false), "expect unsupported macOS version to return error when including non-public asset sets")
checkErr("macos", fleet.AppleOSVersionUnsupportedMessage, ValidateMDMSettingsAppleSupportedOSVersion(tm, true), "expect unsupported macOS version to return error when excluding non-public asset sets")
})
})
t.Run("ios", func(t *testing.T) {
t.Run("app config mdm settings", func(t *testing.T) {
ac := mockAppConfigMDM()
ac.IOSUpdates.MinimumVersion = optjson.SetString(expectSupportedIOSPublic)
checkErr("ios", "", ValidateMDMSettingsAppleSupportedOSVersion(ac, false), "expect public iOS version to be supported when including non-public asset sets")
checkErr("ios", "", ValidateMDMSettingsAppleSupportedOSVersion(ac, true), "expect public iOS version to be supported when excluding non-public asset sets")
ac.IOSUpdates.MinimumVersion = optjson.SetString(expectSupportedIOSNonPublic)
checkErr("ios", "", ValidateMDMSettingsAppleSupportedOSVersion(ac, false), "expect non-public iOS version to be supported when including non-public asset sets")
checkErr("ios", fleet.AppleOSVersionUnsupportedMessage, ValidateMDMSettingsAppleSupportedOSVersion(ac, true), "expect non-public iOS version to return error when excluding non-public asset sets")
ac.IOSUpdates.MinimumVersion = optjson.SetString("5.3.9") // only supported for Apple Watch, so we expect an error
checkErr("ios", fleet.AppleOSVersionUnsupportedMessage, ValidateMDMSettingsAppleSupportedOSVersion(ac, false), "expect unsupported iOS version to return error when including non-public asset sets")
checkErr("ios", fleet.AppleOSVersionUnsupportedMessage, ValidateMDMSettingsAppleSupportedOSVersion(ac, true), "expect unsupported iOS version to return error when excluding non-public asset sets")
})
t.Run("team mdm settings", func(t *testing.T) {
tm := mockTeamMDM()
tm.IOSUpdates.MinimumVersion = optjson.SetString(expectSupportedIOSPublic)
checkErr("ios", "", ValidateMDMSettingsAppleSupportedOSVersion(tm, false), "expect public iOS version to be supported when including non-public asset sets")
checkErr("ios", "", ValidateMDMSettingsAppleSupportedOSVersion(tm, true), "expect public iOS version to be supported when excluding non-public asset sets")
tm.IOSUpdates.MinimumVersion = optjson.SetString(expectSupportedIOSNonPublic)
checkErr("ios", "", ValidateMDMSettingsAppleSupportedOSVersion(tm, false), "expect non-public iOS version to be supported when including non-public asset sets")
checkErr("ios", fleet.AppleOSVersionUnsupportedMessage, ValidateMDMSettingsAppleSupportedOSVersion(tm, true), "expect non-public iOS version to return error when excluding non-public asset sets")
tm.IOSUpdates.MinimumVersion = optjson.SetString("5.3.9") // only supported for Apple Watch, so we expect an error
checkErr("ios", fleet.AppleOSVersionUnsupportedMessage, ValidateMDMSettingsAppleSupportedOSVersion(tm, false), "expect unsupported iOS version to return error when including non-public asset sets")
checkErr("ios", fleet.AppleOSVersionUnsupportedMessage, ValidateMDMSettingsAppleSupportedOSVersion(tm, true), "expect unsupported iOS version to return error when excluding non-public asset sets")
})
})
t.Run("ipados", func(t *testing.T) {
t.Run("app config mdm settings", func(t *testing.T) {
ac := mockAppConfigMDM()
ac.IPadOSUpdates.MinimumVersion = optjson.SetString(expectSupportedIOSPublic)
checkErr("ipados", "", ValidateMDMSettingsAppleSupportedOSVersion(ac, false), "expect public iPadOS version to be supported when including non-public asset sets")
checkErr("ipados", "", ValidateMDMSettingsAppleSupportedOSVersion(ac, true), "expect public iPadOS version to be supported when excluding non-public asset sets")
ac.IPadOSUpdates.MinimumVersion = optjson.SetString(expectSupportedIOSNonPublic)
checkErr("ipados", "", ValidateMDMSettingsAppleSupportedOSVersion(ac, false), "expect non-public iPadOS version to be supported when including non-public asset sets")
checkErr("ipados", fleet.AppleOSVersionUnsupportedMessage, ValidateMDMSettingsAppleSupportedOSVersion(ac, true), "expect non-public iPadOS version to return error when excluding non-public asset sets")
ac.IPadOSUpdates.MinimumVersion = optjson.SetString("5.3.9") // only supported for Apple Watch, so we expect an error
checkErr("ipados", fleet.AppleOSVersionUnsupportedMessage, ValidateMDMSettingsAppleSupportedOSVersion(ac, false), "expect unsupported iPadOS version to return error when including non-public asset sets")
checkErr("ipados", fleet.AppleOSVersionUnsupportedMessage, ValidateMDMSettingsAppleSupportedOSVersion(ac, true), "expect unsupported iPadOS version to return error when excluding non-public asset sets")
})
t.Run("team mdm settings", func(t *testing.T) {
tm := mockTeamMDM()
tm.IPadOSUpdates.MinimumVersion = optjson.SetString(expectSupportedIOSPublic)
checkErr("ipados", "", ValidateMDMSettingsAppleSupportedOSVersion(tm, false), "expect public iPadOS version to be supported when including non-public asset sets")
checkErr("ipados", "", ValidateMDMSettingsAppleSupportedOSVersion(tm, true), "expect public iPadOS version to be supported when excluding non-public asset sets")
tm.IPadOSUpdates.MinimumVersion = optjson.SetString(expectSupportedIOSNonPublic)
checkErr("ipados", "", ValidateMDMSettingsAppleSupportedOSVersion(tm, false), "expect non-public iPadOS version to be supported when including non-public asset sets")
checkErr("ipados", fleet.AppleOSVersionUnsupportedMessage, ValidateMDMSettingsAppleSupportedOSVersion(tm, true), "expect non-public iPadOS version to return error when excluding non-public asset sets")
tm.IPadOSUpdates.MinimumVersion = optjson.SetString("5.3.9") // only supported for Apple Watch, so we expect an error
checkErr("ipados", fleet.AppleOSVersionUnsupportedMessage, ValidateMDMSettingsAppleSupportedOSVersion(tm, false), "expect unsupported iPadOS version to return error when including non-public asset sets")
checkErr("ipados", fleet.AppleOSVersionUnsupportedMessage, ValidateMDMSettingsAppleSupportedOSVersion(tm, true), "expect unsupported iPadOS version to return error when excluding non-public asset sets")
})
})
}
type notFoundError struct{}
func (e notFoundError) IsNotFound() bool { return true }
+54 -9
View File
@@ -58,34 +58,79 @@ type AssetSets struct {
// XROS []Asset `json:"xrOS"` // Fleet doesn't support xrOS yet
}
// APIResponse represents the response from the Apple Software Lookup Service[1][2].
// AssetMetadata represents the response from the Apple Software Lookup Service[1][2].
// [1]: http://gdmf.apple.com/v2/pmv
// [2]: https://support.apple.com/guide/deployment/use-mdm-to-deploy-software-updates-depafd2fad80/web
type APIResponse struct {
type AssetMetadata struct {
PublicAssetSets AssetSets `json:"PublicAssetSets"`
AssetSets AssetSets `json:"AssetSets"`
// PublicRapidSecurityResponses interface{} `json:"PublicRapidSecurityResponses"` // Fleet doesn't support PublicRapidSecurityResponses yet
}
// IsSupportedMacOSVersion checks if the given macOS version is supported by Apple. The
// excludeNonPublicAssetSets parameter controls whether to check against the full asset set or just
// the public asset set, which is relevant for DEP enrollment where only public versions are valid.
func (a AssetMetadata) IsSupportedMacOSVersion(version string, excludeNonPublicAssetSets bool) bool {
as := a.AssetSets.MacOS
if excludeNonPublicAssetSets {
as = a.PublicAssetSets.MacOS
}
for _, s := range as {
if s.ProductVersion == version {
return true // version is supported
}
}
return false // version is not supported
}
// IsSupportedIOSVersion checks if the given iOS version is supported by Apple for the given device
// prefix (e.g. "iPhone", "iPad"). If devicePrefix is empty, it checks if the version is supported
// for any iOS device (which includes things like iPod, Apple Watch, and Apple TV). The
// excludeNonPublicAssetSets parameter controls whether to check against the full asset set or just
// the public asset set, which is relevant for DEP enrollment where only public versions are valid.
func (a AssetMetadata) IsSupportedIOSVersion(version string, devicePrefix string, excludeNonPublicAssetSets bool) bool {
as := a.AssetSets.IOS
if excludeNonPublicAssetSets {
as = a.PublicAssetSets.IOS
}
for _, s := range as {
if s.ProductVersion == version {
if devicePrefix == "" {
return true // version is supported for iOS with any device prefix
}
for _, d := range s.SupportedDevices {
if strings.HasPrefix(strings.ToLower(d), strings.ToLower(devicePrefix)) {
return true // version is supported for device with the given prefix
}
}
}
}
return false // version is not supported
}
// GetLatestOSVersion returns the latest OS version for the given device. The device is matched
// against the Apple Software Update Lookup Service[1][2] to find the latest version. If no matching
// asset is found, an error is returned.
// against the Apple Software Update Lookup Service[1][2] to find the latest version in the
// PublicAssetSets. If no matching asset is found, an error is returned.
// [1]: http://gdmf.apple.com/v2/pmv
// [2]: https://support.apple.com/guide/deployment/use-mdm-to-deploy-software-updates-depafd2fad80/web
func GetLatestOSVersion(device fleet.MDMAppleMachineInfo) (*Asset, error) {
r, err := GetAssetMetadata()
am, err := GetAssetMetadata()
if err != nil {
return nil, fmt.Errorf("retrieving asset metadata: %w", err)
}
assetSet := r.PublicAssetSets.MacOS // default to public asset set; note that if the device is not macOS, iPhone, iPad, or iPod we'll fail to match the supported device and return an error below
assetSet := am.PublicAssetSets.MacOS // default to public asset set; note that if the device is not macOS, iPhone, iPad, or iPod we'll fail to match the supported device and return an error below
if strings.HasPrefix(device.Product, "iPhone") ||
strings.HasPrefix(device.Product, "iPod") ||
strings.HasPrefix(device.Product, "iPad") ||
strings.HasPrefix(device.SoftwareUpdateDeviceID, "iPhone") ||
strings.HasPrefix(device.SoftwareUpdateDeviceID, "iPod") ||
strings.HasPrefix(device.SoftwareUpdateDeviceID, "iPad") {
assetSet = r.PublicAssetSets.IOS
assetSet = am.PublicAssetSets.IOS
}
latestIdx := -1
for i, s := range assetSet {
@@ -129,7 +174,7 @@ func createClient() *http.Client {
// GetAssetMetadata retrieves the asset metadata from the Apple Software Lookup Service[1][2].
// [1]: http://gdmf.apple.com/v2/pmv
// [2]: https://support.apple.com/guide/deployment/use-mdm-to-deploy-software-updates-depafd2fad80/web
func GetAssetMetadata() (*APIResponse, error) {
func GetAssetMetadata() (*AssetMetadata, error) {
baseURL := getBaseURL()
reqURL, err := url.Parse(baseURL)
if err != nil {
@@ -151,7 +196,7 @@ func GetAssetMetadata() (*APIResponse, error) {
if err != nil {
return nil, fmt.Errorf("reading response body from Apple endpoint: %w", err)
}
var dest APIResponse
var dest AssetMetadata
if err := json.Unmarshal(body, &dest); err != nil {
return nil, fmt.Errorf("decoding response data from Apple endpoint: %w", err)
}
+3 -6
View File
@@ -24,7 +24,7 @@ func TestGetLatest(t *testing.T) {
_, err = w.Write(b)
require.NoError(t, err)
}))
defer srv.Close()
t.Cleanup(srv.Close)
dev_mode.SetOverride("FLEET_DEV_GDMF_URL", srv.URL, t)
// test the function
@@ -225,11 +225,8 @@ func TestRetries(t *testing.T) {
_, err := w.Write([]byte(`{"error": "bad request"}`))
require.NoError(t, err)
}))
dev_mode.SetOverride("FLEET_DEV_GDMF_URL", srv.URL)
t.Cleanup(func() {
srv.Close()
dev_mode.ClearOverride("FLEET_DEV_GDMF_URL")
})
t.Cleanup(srv.Close)
dev_mode.SetOverride("FLEET_DEV_GDMF_URL", srv.URL, t)
latest, err := GetLatestOSVersion(fleet.MDMAppleMachineInfo{
OSVersion: "14.4.1",
+29
View File
@@ -5,7 +5,16 @@ import (
"crypto/x509/pkix"
"encoding/asn1"
"math/big"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"runtime"
"testing"
"time"
"github.com/fleetdm/fleet/v4/server/dev_mode"
"github.com/stretchr/testify/require"
)
func NewTestMDMAppleCertTemplate() *x509.Certificate {
@@ -27,3 +36,23 @@ func NewTestMDMAppleCertTemplate() *x509.Certificate {
BasicConstraintsValid: true,
}
}
// StartNewAppleGDMFTestServer creates a new test server that serves the GDMF data from the testdata
// file. It also sets the necessary dev mode overrides to point to the test server and disable
// caching. It closes the server and clears the underlying overrides when the test finishes.
func StartNewAppleGDMFTestServer(t *testing.T) {
_, thisFile, _, _ := runtime.Caller(0)
gdmfTestDataPath := filepath.Join(filepath.Dir(thisFile), "../apple/gdmf/testdata/gdmf.json")
appleGDMFSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
// load the test data from the file
b, err := os.ReadFile(gdmfTestDataPath)
require.NoError(t, err)
_, err = w.Write(b)
require.NoError(t, err)
}))
t.Cleanup(appleGDMFSrv.Close)
dev_mode.SetOverride("FLEET_DEV_GDMF_URL", appleGDMFSrv.URL, t)
}
+10
View File
@@ -27,6 +27,7 @@ import (
"github.com/fleetdm/fleet/v4/server/contexts/license"
"github.com/fleetdm/fleet/v4/server/contexts/viewer"
"github.com/fleetdm/fleet/v4/server/fleet"
apple_mdm "github.com/fleetdm/fleet/v4/server/mdm/apple"
"github.com/fleetdm/fleet/v4/server/platform/endpointer"
"github.com/fleetdm/fleet/v4/server/platform/logging"
"github.com/fleetdm/fleet/v4/server/version"
@@ -1486,6 +1487,15 @@ func (svc *Service) validateMDM(
invalid.Append("ipados_updates", err.Error())
}
// Always check whether specified versions are supported by Apple (even if they weren't updated)
// Note that we're validating against the full, non-public asset set of OS versions here because
// in our DEP flow the minimum version just acts as the threshold for whether or not to update
// the host to the latest, public version. We don't need to install the specified version on the
// host during DEP so it doesn't need to be in the public asset set.
for k, v := range apple_mdm.ValidateMDMSettingsAppleSupportedOSVersion(*mdm, false) {
invalid.Append(k, v.Error())
}
if err := mdm.MacOSSetup.Validate(); err != nil {
var invalidArgErr *fleet.InvalidArgumentError
if errors.As(err, &invalidArgErr) {
+34 -28
View File
@@ -2081,16 +2081,17 @@ func (svc *Service) CheckMDMAppleEnrollmentWithMinimumOSVersion(ctx context.Cont
return nil, nil
}
needsUpdate, err := svc.needsOSUpdateForDEPEnrollment(ctx, *m)
// shouldUpdate depends on the app_config settings for minimum_version and update_new_hosts
shouldUpdate, err := svc.shouldOSUpdateForDEPEnrollment(ctx, *m)
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "checking os updates settings", "serial", m.Serial)
}
if !needsUpdate {
} else if !shouldUpdate {
svc.logger.DebugContext(ctx, "device is above minimum or update new host not checked, skipping os version check", "serial", m.Serial)
return nil, nil
}
// if the device should update based on appconfig settings, we also need to check what versions
// are actually available for the device from Apple
sur, err := svc.getAppleSoftwareUpdateRequiredForDEPEnrollment(*m)
if err != nil {
// log for debugging but allow enrollment to proceed
@@ -2101,7 +2102,7 @@ func (svc *Service) CheckMDMAppleEnrollmentWithMinimumOSVersion(ctx context.Cont
return sur, nil
}
func (svc *Service) needsOSUpdateForDEPEnrollment(ctx context.Context, m fleet.MDMAppleMachineInfo) (bool, error) {
func (svc *Service) shouldOSUpdateForDEPEnrollment(ctx context.Context, m fleet.MDMAppleMachineInfo) (bool, error) {
// NOTE: Under the hood, the datastore is joining host_dep_assignments to the hosts table to
// look up DEP hosts by serial number. It grabs the team id and platform from the
// hosts table. Then it uses the team id to get either the global config or team config.
@@ -2124,35 +2125,40 @@ func (svc *Service) needsOSUpdateForDEPEnrollment(ctx context.Context, m fleet.M
}
minVersion := settings.MinimumVersion.Value
hasMinVersion := settings.MinimumVersion.Set && settings.MinimumVersion.Valid && minVersion != ""
// For macOS hosts, whether to update new hosts during DEP enrollment is determined solely by UpdateNewHosts
if platform == "darwin" {
updateNewHosts := settings.UpdateNewHosts.Set && settings.UpdateNewHosts.Valid && settings.UpdateNewHosts.Value
svc.logger.InfoContext(ctx, "checking os updates settings for macos, update will be forced if UpdateNewHosts is set",
"update_new_hosts", updateNewHosts,
"serial", m.Serial,
)
return updateNewHosts, nil
isSetMinVersion := settings.MinimumVersion.Set && settings.MinimumVersion.Valid && minVersion != ""
logs := []any{
"platform", platform,
"minimum_version", minVersion,
"current_version", m.OSVersion,
"serial", m.Serial,
}
// TODO: confirm what this check should do
if !hasMinVersion {
svc.logger.InfoContext(ctx, "checking os updates settings, minimum version not set",
"serial", m.Serial,
"current_version", m.OSVersion,
"minimum_version", minVersion,
)
if platform != "darwin" && !isSetMinVersion {
svc.logger.InfoContext(ctx, "checking os updates settings for non-macos platform, minimum version not set, skipping version check", logs...)
return false, nil
}
if platform == "darwin" {
updateNewHosts := settings.UpdateNewHosts.Set && settings.UpdateNewHosts.Valid && settings.UpdateNewHosts.Value
logs = append(logs, "update_new_hosts", updateNewHosts)
switch {
case !updateNewHosts:
// never update macos if updateNewHosts is false
svc.logger.InfoContext(ctx, "checking os updates settings for macos, new hosts should not update", logs...)
return false, nil
case !isSetMinVersion:
// always update macos if updateNewHosts is true and minimum version is not set
svc.logger.InfoContext(ctx, "checking os updates settings for macos, new hosts should always update to latest", logs...)
return true, nil
default:
// default to normal version check (require update if less than minimum version)
svc.logger.InfoContext(ctx, "checking os updates settings for macos, new hosts should update to latest if below minimum version", logs...)
}
}
needsUpdate, err := apple_mdm.IsLessThanVersion(m.OSVersion, minVersion)
if err != nil {
svc.logger.InfoContext(ctx, "checking os updates settings, cannot compare versions",
"serial", m.Serial,
"current_version", m.OSVersion,
"minimum_version", minVersion,
)
svc.logger.InfoContext(ctx, "checking os updates settings, cannot compare versions", logs...)
return false, nil
}
+123 -25
View File
@@ -5287,7 +5287,7 @@ func TestUnmarshalAppList(t *testing.T) {
assert.ElementsMatch(t, expectedSoftware, software)
}
func TestNeedsOSUpdateForDEPEnrollment(t *testing.T) {
func TestShouldOSUpdateForDEPEnrollment(t *testing.T) {
testCases := []struct {
name string
platform string
@@ -5321,7 +5321,7 @@ func TestNeedsOSUpdateForDEPEnrollment(t *testing.T) {
expectedResult: false,
},
{
name: "if platform is macOS and update_new_hosts is set",
name: "if platform is macOS and both update_new_hosts and minimum_version are set and host is below the minimum version",
platform: string(fleet.MacOSPlatform),
appleMachineInfo: fleet.MDMAppleMachineInfo{
OSVersion: "16.0.1",
@@ -5332,6 +5332,29 @@ func TestNeedsOSUpdateForDEPEnrollment(t *testing.T) {
},
expectedResult: true,
},
{
name: "if platform is macOS and both update_new_hosts and minimum_version are set and host is at the minimum version",
platform: string(fleet.MacOSPlatform),
appleMachineInfo: fleet.MDMAppleMachineInfo{
OSVersion: "16.0.2",
},
appleOSUpdateSettings: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("16.0.2"),
UpdateNewHosts: optjson.SetBool(true),
},
expectedResult: false,
},
{
name: "if platform is macOS and update_new_hosts is set but minimum_version is not set",
platform: string(fleet.MacOSPlatform),
appleMachineInfo: fleet.MDMAppleMachineInfo{
OSVersion: "16.0.1",
},
appleOSUpdateSettings: fleet.AppleOSUpdateSettings{
UpdateNewHosts: optjson.SetBool(true),
},
expectedResult: true,
},
{
name: "if platform is not macOS and min_version is not set",
platform: string(fleet.IPadOSPlatform),
@@ -5376,7 +5399,7 @@ func TestNeedsOSUpdateForDEPEnrollment(t *testing.T) {
svc := &Service{ds: ds, logger: slog.New(slog.DiscardHandler)}
t.Run(tt.name, func(t *testing.T) {
result, err := svc.needsOSUpdateForDEPEnrollment(ctx, tt.appleMachineInfo)
result, err := svc.shouldOSUpdateForDEPEnrollment(ctx, tt.appleMachineInfo)
require.Equal(t, tt.expectedResult, result)
require.Equal(t, tt.expectedErr, err)
})
@@ -5400,6 +5423,9 @@ func TestCheckMDMAppleEnrollmentWithMinimumOSVersion(t *testing.T) {
latestMacOSVersion := "14.6.1"
latestMacOSBuild := "23G93"
latestIOSVersion := "17.6.1"
latestIOSBuild := "21G93"
testCases := []struct {
name string
machineInfo *fleet.MDMAppleMachineInfo
@@ -5485,17 +5511,62 @@ func TestCheckMDMAppleEnrollmentWithMinimumOSVersion(t *testing.T) {
},
}
// FIXME: When we have more time, this whole test is overdue for a refactor because a bunch of jank
// came with the update new hosts settings for macOS that made the test cases more dependent on
// subtle differences in the machine info for macOS vs non-macOS platforms and made the setup
// more complex and harder to reason about. For now, we can get away with some nested subtests
// to reuse the test cases for both macOS and non-macOS platforms, but ideally we would refactor
// the function under test to separate out the platform-specific logic so that we can have
// clearer and more focused tests for each platform without needing to have a bunch of
// conditional logic in the test itself.
for _, tt := range testCases {
// Non-macOS platforms
for _, platform := range []string{"ios", "ipados"} {
if tt.name == "no match for software update device ID" {
// skip this test case for non-macOS platforms since SUDeviceID is really only relevant for macOS updates
continue
}
t.Run(fmt.Sprintf("%s: %s", platform, tt.name), func(t *testing.T) {
// switch up the machine info to match the platform because test cases were
// originally written with macOS in mind
var product, osVersion, suDeviceID string
var mi *fleet.MDMAppleMachineInfo
if tt.machineInfo != nil {
osVersion = strings.Replace(tt.machineInfo.OSVersion, "14", "17", 1)
if platform == "ios" {
product = "iPhone16,2"
suDeviceID = strings.Replace(tt.machineInfo.SoftwareUpdateDeviceID, "J516sAP", "iPhone", 1)
} else {
product = "iPad14,11"
suDeviceID = strings.Replace(tt.machineInfo.SoftwareUpdateDeviceID, "J516sAP", "iPad", 1)
}
mi = &fleet.MDMAppleMachineInfo{
MDMCanRequestSoftwareUpdate: tt.machineInfo.MDMCanRequestSoftwareUpdate,
Product: product,
OSVersion: osVersion,
SupplementalBuildVersion: tt.machineInfo.SupplementalBuildVersion,
SoftwareUpdateDeviceID: suDeviceID,
}
}
// same for update required details
var details *fleet.MDMAppleSoftwareUpdateRequiredDetails
if tt.updateRequired != nil {
details = &fleet.MDMAppleSoftwareUpdateRequiredDetails{
OSVersion: latestIOSVersion,
BuildVersion: latestIOSBuild,
}
}
t.Run("settings minimum equal to latest", func(t *testing.T) {
ds.GetMDMAppleOSUpdatesSettingsByHostSerialFunc = func(ctx context.Context, serial string) (string, *fleet.AppleOSUpdateSettings, error) {
return "ios", &fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString(latestMacOSVersion),
return platform, &fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString(latestIOSVersion),
}, nil
}
sur, err := svc.CheckMDMAppleEnrollmentWithMinimumOSVersion(ctx, tt.machineInfo)
sur, err := svc.CheckMDMAppleEnrollmentWithMinimumOSVersion(ctx, mi)
if tt.err != "" {
require.Error(t, err)
require.Contains(t, err.Error(), tt.err)
@@ -5505,7 +5576,7 @@ func TestCheckMDMAppleEnrollmentWithMinimumOSVersion(t *testing.T) {
if tt.updateRequired != nil {
require.Equal(t, &fleet.MDMAppleSoftwareUpdateRequired{
Code: fleet.MDMAppleSoftwareUpdateRequiredCode,
Details: *tt.updateRequired,
Details: *details,
}, sur)
} else {
require.Nil(t, sur)
@@ -5514,11 +5585,11 @@ func TestCheckMDMAppleEnrollmentWithMinimumOSVersion(t *testing.T) {
t.Run("settings minimum below latest", func(t *testing.T) {
ds.GetMDMAppleOSUpdatesSettingsByHostSerialFunc = func(ctx context.Context, serial string) (string, *fleet.AppleOSUpdateSettings, error) {
return "ios", &fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("14.5"),
return platform, &fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("17.5"),
}, nil
}
sur, err := svc.CheckMDMAppleEnrollmentWithMinimumOSVersion(ctx, tt.machineInfo)
sur, err := svc.CheckMDMAppleEnrollmentWithMinimumOSVersion(ctx, mi)
if tt.err != "" {
require.Error(t, err)
require.Contains(t, err.Error(), tt.err)
@@ -5528,7 +5599,7 @@ func TestCheckMDMAppleEnrollmentWithMinimumOSVersion(t *testing.T) {
if tt.updateRequired != nil {
require.Equal(t, &fleet.MDMAppleSoftwareUpdateRequired{
Code: fleet.MDMAppleSoftwareUpdateRequiredCode,
Details: *tt.updateRequired,
Details: *details,
}, sur)
} else {
require.Nil(t, sur)
@@ -5538,11 +5609,11 @@ func TestCheckMDMAppleEnrollmentWithMinimumOSVersion(t *testing.T) {
t.Run("settings minimum above latest", func(t *testing.T) {
// edge case, but in practice it would get treated as if minimum was equal to latest
ds.GetMDMAppleOSUpdatesSettingsByHostSerialFunc = func(ctx context.Context, serial string) (string, *fleet.AppleOSUpdateSettings, error) {
return "ios", &fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("14.7"),
return platform, &fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("17.7"),
}, nil
}
sur, err := svc.CheckMDMAppleEnrollmentWithMinimumOSVersion(ctx, tt.machineInfo)
sur, err := svc.CheckMDMAppleEnrollmentWithMinimumOSVersion(ctx, mi)
if tt.err != "" {
require.Error(t, err)
require.Contains(t, err.Error(), tt.err)
@@ -5552,7 +5623,7 @@ func TestCheckMDMAppleEnrollmentWithMinimumOSVersion(t *testing.T) {
if tt.updateRequired != nil {
require.Equal(t, &fleet.MDMAppleSoftwareUpdateRequired{
Code: fleet.MDMAppleSoftwareUpdateRequiredCode,
Details: *tt.updateRequired,
Details: *details,
}, sur)
} else {
require.Nil(t, sur)
@@ -5561,11 +5632,11 @@ func TestCheckMDMAppleEnrollmentWithMinimumOSVersion(t *testing.T) {
t.Run("device above settings minimum", func(t *testing.T) {
ds.GetMDMAppleOSUpdatesSettingsByHostSerialFunc = func(ctx context.Context, serial string) (string, *fleet.AppleOSUpdateSettings, error) {
return "ios", &fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("14.1"),
return platform, &fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("17.1"),
}, nil
}
sur, err := svc.CheckMDMAppleEnrollmentWithMinimumOSVersion(ctx, tt.machineInfo)
sur, err := svc.CheckMDMAppleEnrollmentWithMinimumOSVersion(ctx, mi)
if tt.err != "" {
require.Error(t, err)
require.Contains(t, err.Error(), tt.err)
@@ -5578,27 +5649,27 @@ func TestCheckMDMAppleEnrollmentWithMinimumOSVersion(t *testing.T) {
t.Run("minimum not set", func(t *testing.T) {
ds.GetMDMAppleOSUpdatesSettingsByHostSerialFunc = func(ctx context.Context, serial string) (string, *fleet.AppleOSUpdateSettings, error) {
return "ios", &fleet.AppleOSUpdateSettings{}, nil
return platform, &fleet.AppleOSUpdateSettings{}, nil
}
sur, err := svc.CheckMDMAppleEnrollmentWithMinimumOSVersion(ctx, tt.machineInfo)
sur, err := svc.CheckMDMAppleEnrollmentWithMinimumOSVersion(ctx, mi)
require.NoError(t, err)
require.Nil(t, sur)
ds.GetMDMAppleOSUpdatesSettingsByHostSerialFunc = func(ctx context.Context, serial string) (string, *fleet.AppleOSUpdateSettings, error) {
return "ios", &fleet.AppleOSUpdateSettings{
return platform, &fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString(""),
}, nil
}
sur, err = svc.CheckMDMAppleEnrollmentWithMinimumOSVersion(ctx, tt.machineInfo)
sur, err = svc.CheckMDMAppleEnrollmentWithMinimumOSVersion(ctx, mi)
require.NoError(t, err)
require.Nil(t, sur)
})
t.Run("minimum not found", func(t *testing.T) {
ds.GetMDMAppleOSUpdatesSettingsByHostSerialFunc = func(ctx context.Context, serial string) (string, *fleet.AppleOSUpdateSettings, error) {
return "ios", nil, &notFoundError{}
return platform, nil, &notFoundError{}
}
sur, err := svc.CheckMDMAppleEnrollmentWithMinimumOSVersion(ctx, tt.machineInfo)
sur, err := svc.CheckMDMAppleEnrollmentWithMinimumOSVersion(ctx, mi)
require.NoError(t, err)
require.Nil(t, sur)
})
@@ -5621,6 +5692,7 @@ func TestCheckMDMAppleEnrollmentWithMinimumOSVersion(t *testing.T) {
})
t.Run("when UpdateNewHosts is set and minimum is not set", func(t *testing.T) {
// test with min version not present
ds.GetMDMAppleOSUpdatesSettingsByHostSerialFunc = func(ctx context.Context, serial string) (string, *fleet.AppleOSUpdateSettings, error) {
return "darwin", &fleet.AppleOSUpdateSettings{
UpdateNewHosts: optjson.SetBool(true),
@@ -5640,6 +5712,7 @@ func TestCheckMDMAppleEnrollmentWithMinimumOSVersion(t *testing.T) {
require.Nil(t, sur)
}
// test again with min version explicitly set to empty string
ds.GetMDMAppleOSUpdatesSettingsByHostSerialFunc = func(ctx context.Context, serial string) (string, *fleet.AppleOSUpdateSettings, error) {
return "darwin", &fleet.AppleOSUpdateSettings{
UpdateNewHosts: optjson.SetBool(true),
@@ -5664,6 +5737,7 @@ func TestCheckMDMAppleEnrollmentWithMinimumOSVersion(t *testing.T) {
ds.GetMDMAppleOSUpdatesSettingsByHostSerialFunc = func(ctx context.Context, serial string) (string, *fleet.AppleOSUpdateSettings, error) {
return "darwin", nil, &notFoundError{}
}
// never block enrollment when settings are not found
sur, err := svc.CheckMDMAppleEnrollmentWithMinimumOSVersion(ctx, tt.machineInfo)
require.NoError(t, err)
require.Nil(t, sur)
@@ -5692,6 +5766,30 @@ func TestCheckMDMAppleEnrollmentWithMinimumOSVersion(t *testing.T) {
require.Nil(t, sur)
}
})
t.Run("when UpdateNewHosts is set and required minimum is less than latest", func(t *testing.T) {
ds.GetMDMAppleOSUpdatesSettingsByHostSerialFunc = func(ctx context.Context, serial string) (string, *fleet.AppleOSUpdateSettings, error) {
return "darwin", &fleet.AppleOSUpdateSettings{
UpdateNewHosts: optjson.SetBool(true),
MinimumVersion: optjson.SetString("14.5"),
}, nil
}
sur, err := svc.CheckMDMAppleEnrollmentWithMinimumOSVersion(ctx, tt.machineInfo)
if tt.err != "" {
require.Error(t, err)
require.Contains(t, err.Error(), tt.err)
} else {
require.NoError(t, err)
}
if tt.updateRequired != nil {
require.Equal(t, &fleet.MDMAppleSoftwareUpdateRequired{
Code: fleet.MDMAppleSoftwareUpdateRequiredCode,
Details: *tt.updateRequired,
}, sur)
} else {
require.Nil(t, sur)
}
})
})
}
@@ -5701,7 +5799,7 @@ func TestCheckMDMAppleEnrollmentWithMinimumOSVersion(t *testing.T) {
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
ds.GetMDMAppleOSUpdatesSettingsByHostSerialFunc = func(ctx context.Context, serial string) (string, *fleet.AppleOSUpdateSettings, error) {
return "ios", &fleet.AppleOSUpdateSettings{MinimumVersion: optjson.SetString(latestMacOSVersion)}, nil
return "macos", &fleet.AppleOSUpdateSettings{MinimumVersion: optjson.SetString(latestMacOSVersion), UpdateNewHosts: optjson.SetBool(true)}, nil
}
sur, err := svc.CheckMDMAppleEnrollmentWithMinimumOSVersion(ctx, tt.machineInfo)
+101 -88
View File
@@ -51,6 +51,7 @@ import (
"github.com/fleetdm/fleet/v4/server/live_query/live_query_mock"
"github.com/fleetdm/fleet/v4/server/mdm"
maintained_apps "github.com/fleetdm/fleet/v4/server/mdm/maintainedapps"
mdmtest "github.com/fleetdm/fleet/v4/server/mdm/testing_utils"
"github.com/fleetdm/fleet/v4/server/policies"
"github.com/fleetdm/fleet/v4/server/ptr"
"github.com/fleetdm/fleet/v4/server/pubsub"
@@ -180,6 +181,9 @@ func (s *integrationEnterpriseTestSuite) clearOktaConditionalAccess() {
func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
t := s.T()
// Mock Apple GDMF API (required for validating OS update minimum version settings)
mdmtest.StartNewAppleGDMFTestServer(t)
// create a team through the service so it initializes the agent ops
teamName := t.Name() + "team1"
teamNameDecomposed := teamName + "ᄀ" + "ᅡ" // Add a decomposed Unicode character
@@ -228,16 +232,16 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
"features": &features,
"mdm": map[string]any{
"macos_updates": map[string]any{
"minimum_version": "10.15.0",
"minimum_version": "14.6.1",
"deadline": "2021-01-01",
"update_new_hosts": true,
},
"ios_updates": map[string]any{
"minimum_version": "17.5.1",
"minimum_version": "17.6.1",
"deadline": "2024-07-23",
},
"ipados_updates": map[string]any{
"minimum_version": "18.0",
"minimum_version": "17.6.1",
"deadline": "2024-08-24",
},
},
@@ -260,17 +264,17 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
}, team.Config.Features)
require.Equal(t, fleet.TeamMDM{
MacOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("10.15.0"),
MinimumVersion: optjson.SetString("14.6.1"),
Deadline: optjson.SetString("2021-01-01"),
UpdateNewHosts: optjson.SetBool(true),
},
IOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("17.5.1"),
MinimumVersion: optjson.SetString("17.6.1"),
Deadline: optjson.SetString("2024-07-23"),
UpdateNewHosts: optjson.Bool{Set: true},
},
IPadOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("18.0"),
MinimumVersion: optjson.SetString("17.6.1"),
Deadline: optjson.SetString("2024-08-24"),
UpdateNewHosts: optjson.Bool{Set: true},
},
@@ -393,17 +397,17 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
require.Equal(t, applyResp.TeamIDsByName[teamName], team.ID)
require.Equal(t, fleet.TeamMDM{
MacOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("10.15.0"),
MinimumVersion: optjson.SetString("14.6.1"),
Deadline: optjson.SetString("2021-01-01"),
UpdateNewHosts: optjson.SetBool(true),
},
IOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("17.5.1"),
MinimumVersion: optjson.SetString("17.6.1"),
Deadline: optjson.SetString("2024-07-23"),
UpdateNewHosts: optjson.Bool{Set: true},
},
IPadOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("18.0"),
MinimumVersion: optjson.SetString("17.6.1"),
Deadline: optjson.SetString("2024-08-24"),
UpdateNewHosts: optjson.Bool{Set: true},
},
@@ -434,17 +438,17 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
s.DoJSON("GET", "/api/latest/fleet/teams/"+fmt.Sprint(team.ID), nil, http.StatusOK, &getTmResp)
require.Equal(t, fleet.TeamMDM{
MacOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("10.15.0"),
MinimumVersion: optjson.SetString("14.6.1"),
Deadline: optjson.SetString("2021-01-01"),
UpdateNewHosts: optjson.SetBool(true),
},
IOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("17.5.1"),
MinimumVersion: optjson.SetString("17.6.1"),
Deadline: optjson.SetString("2024-07-23"),
UpdateNewHosts: optjson.Bool{Set: true},
},
IPadOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("18.0"),
MinimumVersion: optjson.SetString("17.6.1"),
Deadline: optjson.SetString("2024-08-24"),
UpdateNewHosts: optjson.Bool{Set: true},
},
@@ -477,17 +481,17 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
require.Equal(t, team.ID, listTmResp.Teams[0].ID)
require.Equal(t, fleet.TeamMDM{
MacOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("10.15.0"),
MinimumVersion: optjson.SetString("14.6.1"),
Deadline: optjson.SetString("2021-01-01"),
UpdateNewHosts: optjson.SetBool(true),
},
IOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("17.5.1"),
MinimumVersion: optjson.SetString("17.6.1"),
Deadline: optjson.SetString("2024-07-23"),
UpdateNewHosts: optjson.Bool{Set: true},
},
IPadOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("18.0"),
MinimumVersion: optjson.SetString("17.6.1"),
Deadline: optjson.SetString("2024-08-24"),
UpdateNewHosts: optjson.Bool{Set: true},
},
@@ -2979,6 +2983,9 @@ func (s *integrationEnterpriseTestSuite) TestNoTeamFailingPolicyWebhookTrigger()
func (s *integrationEnterpriseTestSuite) TestWindowsUpdatesTeamConfig() {
t := s.T()
// Mock Apple GDMF API (required for validating OS update minimum version settings)
mdmtest.StartNewAppleGDMFTestServer(t)
// Create a team
team := &fleet.Team{
Name: t.Name(),
@@ -3075,12 +3082,12 @@ func (s *integrationEnterpriseTestSuite) TestWindowsUpdatesTeamConfig() {
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
"mdm": map[string]any{
"macos_updates": &fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("10.15.0"),
MinimumVersion: optjson.SetString("14.6.1"),
Deadline: optjson.SetString("2021-01-01"),
},
},
}, http.StatusOK, &tmResp)
require.Equal(t, "10.15.0", tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion.Value)
require.Equal(t, "14.6.1", tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion.Value)
require.Equal(t, "2021-01-01", tmResp.Team.Config.MDM.MacOSUpdates.Deadline.Value)
require.Equal(t, 6, tmResp.Team.Config.MDM.WindowsUpdates.DeadlineDays.Value)
require.Equal(t, 2, tmResp.Team.Config.MDM.WindowsUpdates.GracePeriodDays.Value)
@@ -3102,7 +3109,7 @@ func (s *integrationEnterpriseTestSuite) TestWindowsUpdatesTeamConfig() {
"windows_updates": nil,
},
}, http.StatusOK, &tmResp)
require.Equal(t, "10.15.0", tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion.Value)
require.Equal(t, "14.6.1", tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion.Value)
require.Equal(t, "2021-01-01", tmResp.Team.Config.MDM.MacOSUpdates.Deadline.Value)
require.Equal(t, 6, tmResp.Team.Config.MDM.WindowsUpdates.DeadlineDays.Value)
require.Equal(t, 2, tmResp.Team.Config.MDM.WindowsUpdates.GracePeriodDays.Value)
@@ -3246,6 +3253,9 @@ func (s *integrationEnterpriseTestSuite) assertAppleOSUpdatesDeclaration(teamID
func (s *integrationEnterpriseTestSuite) TestAppleOSUpdatesTeamConfig() {
t := s.T()
// Mock Apple GDMF API (required for validating OS update minimum version settings)
mdmtest.StartNewAppleGDMFTestServer(t)
team := &fleet.Team{
Name: t.Name(),
Description: "Team description",
@@ -3263,7 +3273,7 @@ func (s *integrationEnterpriseTestSuite) TestAppleOSUpdatesTeamConfig() {
// modify the team's config (macOS first)
macOSUpdates := &fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("10.15.0"),
MinimumVersion: optjson.SetString("14.6.1"),
Deadline: optjson.SetString("2021-01-01"),
UpdateNewHosts: optjson.SetBool(true),
}
@@ -3272,13 +3282,13 @@ func (s *integrationEnterpriseTestSuite) TestAppleOSUpdatesTeamConfig() {
"macos_updates": macOSUpdates,
},
}, http.StatusOK, &tmResp)
require.Equal(t, "10.15.0", tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion.Value)
require.Equal(t, "14.6.1", tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion.Value)
require.Equal(t, "2021-01-01", tmResp.Team.Config.MDM.MacOSUpdates.Deadline.Value)
require.Equal(t, true, tmResp.Team.Config.MDM.MacOSUpdates.UpdateNewHosts.Value)
s.lastActivityOfTypeMatches(
fleet.ActivityTypeEditedMacOSMinVersion{}.ActivityName(),
fmt.Sprintf(`{"team_id": %d, "team_name": %q, "fleet_id": %d, "fleet_name": %q, "minimum_version": "10.15.0", "deadline": "2021-01-01"}`, team.ID, team.Name, team.ID, team.Name), 0)
fmt.Sprintf(`{"team_id": %d, "team_name": %q, "fleet_id": %d, "fleet_name": %q, "minimum_version": "14.6.1", "deadline": "2021-01-01"}`, team.ID, team.Name, team.ID, team.Name), 0)
s.lastActivityOfTypeMatches(
fleet.ActivityTypeEnabledMacosUpdateNewHosts{}.ActivityName(),
@@ -3295,12 +3305,12 @@ func (s *integrationEnterpriseTestSuite) TestAppleOSUpdatesTeamConfig() {
// modify the team's config (now iOS and iPadOS)
iOSUpdates := &fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("11.11.11"),
MinimumVersion: optjson.SetString("17.6.1"),
Deadline: optjson.SetString("2022-02-02"),
UpdateNewHosts: optjson.SetBool(true),
}
iPadOSUpdates := &fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("12.12.12"),
MinimumVersion: optjson.SetString("17.6.1"),
Deadline: optjson.SetString("2023-03-03"),
UpdateNewHosts: optjson.SetBool(true),
}
@@ -3311,23 +3321,23 @@ func (s *integrationEnterpriseTestSuite) TestAppleOSUpdatesTeamConfig() {
"ipados_updates": iPadOSUpdates,
},
}, http.StatusOK, &tmResp)
require.Equal(t, "10.15.0", tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion.Value)
require.Equal(t, "14.6.1", tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion.Value)
require.Equal(t, "2021-01-01", tmResp.Team.Config.MDM.MacOSUpdates.Deadline.Value)
require.Equal(t, optjson.SetBool(true), tmResp.Team.Config.MDM.MacOSUpdates.UpdateNewHosts)
require.Equal(t, "11.11.11", tmResp.Team.Config.MDM.IOSUpdates.MinimumVersion.Value)
require.Equal(t, "17.6.1", tmResp.Team.Config.MDM.IOSUpdates.MinimumVersion.Value)
require.Equal(t, "2022-02-02", tmResp.Team.Config.MDM.IOSUpdates.Deadline.Value)
// UpdateNewHosts values are ignored for iOS
require.Equal(t, optjson.Bool{Set: true}, tmResp.Team.Config.MDM.IOSUpdates.UpdateNewHosts)
require.Equal(t, "12.12.12", tmResp.Team.Config.MDM.IPadOSUpdates.MinimumVersion.Value)
require.Equal(t, "17.6.1", tmResp.Team.Config.MDM.IPadOSUpdates.MinimumVersion.Value)
require.Equal(t, "2023-03-03", tmResp.Team.Config.MDM.IPadOSUpdates.Deadline.Value)
// UpdateNewHosts values are ignored for iPadOS
require.Equal(t, optjson.Bool{Set: true}, tmResp.Team.Config.MDM.IPadOSUpdates.UpdateNewHosts)
s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedMacOSMinVersion{}.ActivityName(), fmt.Sprintf(`{"team_id": %d, "team_name": %q, "fleet_id": %d, "fleet_name": %q, "minimum_version": "10.15.0", "deadline": "2021-01-01"}`, team.ID, team.Name, team.ID, team.Name), 0)
s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedIOSMinVersion{}.ActivityName(), fmt.Sprintf(`{"team_id": %d, "team_name": %q, "fleet_id": %d, "fleet_name": %q, "minimum_version": "11.11.11", "deadline": "2022-02-02"}`, team.ID, team.Name, team.ID, team.Name), 0)
s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedIPadOSMinVersion{}.ActivityName(), fmt.Sprintf(`{"team_id": %d, "team_name": %q, "fleet_id": %d, "fleet_name": %q, "minimum_version": "12.12.12", "deadline": "2023-03-03"}`, team.ID, team.Name, team.ID, team.Name), 0)
s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedMacOSMinVersion{}.ActivityName(), fmt.Sprintf(`{"team_id": %d, "team_name": %q, "fleet_id": %d, "fleet_name": %q, "minimum_version": "14.6.1", "deadline": "2021-01-01"}`, team.ID, team.Name, team.ID, team.Name), 0)
s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedIOSMinVersion{}.ActivityName(), fmt.Sprintf(`{"team_id": %d, "team_name": %q, "fleet_id": %d, "fleet_name": %q, "minimum_version": "17.6.1", "deadline": "2022-02-02"}`, team.ID, team.Name, team.ID, team.Name), 0)
s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedIPadOSMinVersion{}.ActivityName(), fmt.Sprintf(`{"team_id": %d, "team_name": %q, "fleet_id": %d, "fleet_name": %q, "minimum_version": "17.6.1", "deadline": "2023-03-03"}`, team.ID, team.Name, team.ID, team.Name), 0)
s.assertAppleOSUpdatesDeclaration(&team.ID, mdm.FleetMacOSUpdatesProfileName, macOSUpdates)
s.assertAppleOSUpdatesDeclaration(&team.ID, mdm.FleetIOSUpdatesProfileName, iOSUpdates)
@@ -3335,16 +3345,16 @@ func (s *integrationEnterpriseTestSuite) TestAppleOSUpdatesTeamConfig() {
// only update the deadlines
macOSUpdates = &fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("10.15.0"),
MinimumVersion: optjson.SetString("14.6.1"),
Deadline: optjson.SetString("2025-10-01"),
UpdateNewHosts: optjson.SetBool(true),
}
iOSUpdates = &fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("11.11.11"),
MinimumVersion: optjson.SetString("17.6.1"),
Deadline: optjson.SetString("2024-02-02"),
}
iPadOSUpdates = &fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("12.12.12"),
MinimumVersion: optjson.SetString("17.6.1"),
Deadline: optjson.SetString("2024-03-03"),
}
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
@@ -3354,21 +3364,21 @@ func (s *integrationEnterpriseTestSuite) TestAppleOSUpdatesTeamConfig() {
"ipados_updates": iPadOSUpdates,
},
}, http.StatusOK, &tmResp)
require.Equal(t, "10.15.0", tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion.Value)
require.Equal(t, "14.6.1", tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion.Value)
require.Equal(t, "2025-10-01", tmResp.Team.Config.MDM.MacOSUpdates.Deadline.Value)
require.Equal(t, optjson.SetBool(true), tmResp.Team.Config.MDM.MacOSUpdates.UpdateNewHosts)
require.Equal(t, "11.11.11", tmResp.Team.Config.MDM.IOSUpdates.MinimumVersion.Value)
require.Equal(t, "17.6.1", tmResp.Team.Config.MDM.IOSUpdates.MinimumVersion.Value)
require.Equal(t, "2024-02-02", tmResp.Team.Config.MDM.IOSUpdates.Deadline.Value)
require.Equal(t, optjson.Bool{Set: true}, tmResp.Team.Config.MDM.IOSUpdates.UpdateNewHosts)
require.Equal(t, "12.12.12", tmResp.Team.Config.MDM.IPadOSUpdates.MinimumVersion.Value)
require.Equal(t, "17.6.1", tmResp.Team.Config.MDM.IPadOSUpdates.MinimumVersion.Value)
require.Equal(t, "2024-03-03", tmResp.Team.Config.MDM.IPadOSUpdates.Deadline.Value)
require.Equal(t, optjson.Bool{Set: true}, tmResp.Team.Config.MDM.IPadOSUpdates.UpdateNewHosts)
macOSLastActivity := s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedMacOSMinVersion{}.ActivityName(), fmt.Sprintf(`{"team_id": %d, "team_name": %q, "fleet_id": %d, "fleet_name": %q, "minimum_version": "10.15.0", "deadline": "2025-10-01"}`, team.ID, team.Name, team.ID, team.Name), 0)
iOSLastActivity := s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedIOSMinVersion{}.ActivityName(), fmt.Sprintf(`{"team_id": %d, "team_name": %q, "fleet_id": %d, "fleet_name": %q, "minimum_version": "11.11.11", "deadline": "2024-02-02"}`, team.ID, team.Name, team.ID, team.Name), 0)
iPadOSLastActivity := s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedIPadOSMinVersion{}.ActivityName(), fmt.Sprintf(`{"team_id": %d, "team_name": %q, "fleet_id": %d, "fleet_name": %q, "minimum_version": "12.12.12", "deadline": "2024-03-03"}`, team.ID, team.Name, team.ID, team.Name), 0)
macOSLastActivity := s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedMacOSMinVersion{}.ActivityName(), fmt.Sprintf(`{"team_id": %d, "team_name": %q, "fleet_id": %d, "fleet_name": %q, "minimum_version": "14.6.1", "deadline": "2025-10-01"}`, team.ID, team.Name, team.ID, team.Name), 0)
iOSLastActivity := s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedIOSMinVersion{}.ActivityName(), fmt.Sprintf(`{"team_id": %d, "team_name": %q, "fleet_id": %d, "fleet_name": %q, "minimum_version": "17.6.1", "deadline": "2024-02-02"}`, team.ID, team.Name, team.ID, team.Name), 0)
iPadOSLastActivity := s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedIPadOSMinVersion{}.ActivityName(), fmt.Sprintf(`{"team_id": %d, "team_name": %q, "fleet_id": %d, "fleet_name": %q, "minimum_version": "17.6.1", "deadline": "2024-03-03"}`, team.ID, team.Name, team.ID, team.Name), 0)
s.assertAppleOSUpdatesDeclaration(&team.ID, mdm.FleetMacOSUpdatesProfileName, macOSUpdates)
s.assertAppleOSUpdatesDeclaration(&team.ID, mdm.FleetIOSUpdatesProfileName, iOSUpdates)
@@ -3376,16 +3386,16 @@ func (s *integrationEnterpriseTestSuite) TestAppleOSUpdatesTeamConfig() {
// Unchecking the UpdateNewHosts flag should register as an activity
macOSUpdates = &fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("10.15.0"),
MinimumVersion: optjson.SetString("14.6.1"),
Deadline: optjson.SetString("2025-10-01"),
UpdateNewHosts: optjson.SetBool(false),
}
iOSUpdates = &fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("11.11.11"),
MinimumVersion: optjson.SetString("17.6.1"),
Deadline: optjson.SetString("2024-02-02"),
}
iPadOSUpdates = &fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("12.12.12"),
MinimumVersion: optjson.SetString("17.6.1"),
Deadline: optjson.SetString("2024-03-03"),
}
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
@@ -3415,11 +3425,11 @@ func (s *integrationEnterpriseTestSuite) TestAppleOSUpdatesTeamConfig() {
},
},
}, http.StatusOK, &tmResp)
require.Equal(t, "10.15.0", tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion.Value)
require.Equal(t, "14.6.1", tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion.Value)
require.Equal(t, "2025-10-01", tmResp.Team.Config.MDM.MacOSUpdates.Deadline.Value)
require.Equal(t, "11.11.11", tmResp.Team.Config.MDM.IOSUpdates.MinimumVersion.Value)
require.Equal(t, "17.6.1", tmResp.Team.Config.MDM.IOSUpdates.MinimumVersion.Value)
require.Equal(t, "2024-02-02", tmResp.Team.Config.MDM.IOSUpdates.Deadline.Value)
require.Equal(t, "12.12.12", tmResp.Team.Config.MDM.IPadOSUpdates.MinimumVersion.Value)
require.Equal(t, "17.6.1", tmResp.Team.Config.MDM.IPadOSUpdates.MinimumVersion.Value)
require.Equal(t, "2024-03-03", tmResp.Team.Config.MDM.IPadOSUpdates.Deadline.Value)
require.Equal(t, 10, tmResp.Team.Config.MDM.WindowsUpdates.DeadlineDays.Value)
require.Equal(t, 2, tmResp.Team.Config.MDM.WindowsUpdates.GracePeriodDays.Value)
@@ -3442,11 +3452,11 @@ func (s *integrationEnterpriseTestSuite) TestAppleOSUpdatesTeamConfig() {
"macos_updates": nil,
},
}, http.StatusOK, &tmResp)
require.Equal(t, "10.15.0", tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion.Value)
require.Equal(t, "14.6.1", tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion.Value)
require.Equal(t, "2025-10-01", tmResp.Team.Config.MDM.MacOSUpdates.Deadline.Value)
require.Equal(t, "11.11.11", tmResp.Team.Config.MDM.IOSUpdates.MinimumVersion.Value)
require.Equal(t, "17.6.1", tmResp.Team.Config.MDM.IOSUpdates.MinimumVersion.Value)
require.Equal(t, "2024-02-02", tmResp.Team.Config.MDM.IOSUpdates.Deadline.Value)
require.Equal(t, "12.12.12", tmResp.Team.Config.MDM.IPadOSUpdates.MinimumVersion.Value)
require.Equal(t, "17.6.1", tmResp.Team.Config.MDM.IPadOSUpdates.MinimumVersion.Value)
require.Equal(t, "2024-03-03", tmResp.Team.Config.MDM.IPadOSUpdates.Deadline.Value)
// no new activity is created
s.lastActivityMatches("", "", lastActivity)
@@ -3463,7 +3473,7 @@ func (s *integrationEnterpriseTestSuite) TestAppleOSUpdatesTeamConfig() {
},
},
}, http.StatusOK, &tmResp)
require.Equal(t, "10.15.0", tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion.Value)
require.Equal(t, "14.6.1", 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)
@@ -3509,7 +3519,7 @@ func (s *integrationEnterpriseTestSuite) TestAppleOSUpdatesTeamConfig() {
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",
"minimum_version": "14.6.1",
"deadline": "2021-01-01T00:00:00Z",
},
},
@@ -3517,7 +3527,7 @@ func (s *integrationEnterpriseTestSuite) TestAppleOSUpdatesTeamConfig() {
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
"mdm": map[string]any{
"ios_updates": map[string]any{
"minimum_version": "10.15.0",
"minimum_version": "14.6.1",
"deadline": "2021-01-01T00:00:00Z",
},
},
@@ -3525,7 +3535,7 @@ func (s *integrationEnterpriseTestSuite) TestAppleOSUpdatesTeamConfig() {
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
"mdm": map[string]any{
"ipados_updates": map[string]any{
"minimum_version": "10.15.0",
"minimum_version": "14.6.1",
"deadline": "2021-01-01T00:00:00Z",
},
},
@@ -3535,7 +3545,7 @@ func (s *integrationEnterpriseTestSuite) TestAppleOSUpdatesTeamConfig() {
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)",
"minimum_version": "14.6.1 (19A583)",
"deadline": "2021-01-01T00:00:00Z",
},
},
@@ -3543,7 +3553,7 @@ func (s *integrationEnterpriseTestSuite) TestAppleOSUpdatesTeamConfig() {
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
"mdm": map[string]any{
"ios_updates": map[string]any{
"minimum_version": "10.15.0 (19A583)",
"minimum_version": "14.6.1 (19A583)",
"deadline": "2021-01-01T00:00:00Z",
},
},
@@ -3551,7 +3561,7 @@ func (s *integrationEnterpriseTestSuite) TestAppleOSUpdatesTeamConfig() {
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
"mdm": map[string]any{
"ipados_updates": map[string]any{
"minimum_version": "10.15.0 (19A583)",
"minimum_version": "14.6.1 (19A583)",
"deadline": "2021-01-01T00:00:00Z",
},
},
@@ -3607,21 +3617,21 @@ func (s *integrationEnterpriseTestSuite) TestAppleOSUpdatesTeamConfig() {
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)",
"minimum_version": "14.6.1 (19A583)",
},
},
}, http.StatusUnprocessableEntity, &tmResp)
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
"mdm": map[string]any{
"ios_updates": map[string]any{
"minimum_version": "10.15.0 (19A583)",
"minimum_version": "14.6.1 (19A583)",
},
},
}, http.StatusUnprocessableEntity, &tmResp)
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
"mdm": map[string]any{
"ipados_updates": map[string]any{
"minimum_version": "10.15.0 (19A583)",
"minimum_version": "14.6.1 (19A583)",
},
},
}, http.StatusUnprocessableEntity, &tmResp)
@@ -4363,6 +4373,9 @@ func (s *integrationEnterpriseTestSuite) TestMDMWindowsUpdates() {
func (s *integrationEnterpriseTestSuite) TestMDMAppleOSUpdates() {
t := s.T()
// Mock Apple GDMF API (required for validating OS update minimum version settings)
mdmtest.StartNewAppleGDMFTestServer(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")
@@ -4489,64 +4502,64 @@ func (s *integrationEnterpriseTestSuite) TestMDMAppleOSUpdates() {
s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{
"mdm": {
"macos_updates": {
"minimum_version": "12.3.1",
"minimum_version": "13.6.9",
"deadline": "2022-01-01",
"update_new_hosts": true
},
"ios_updates": {
"minimum_version": "13.13.13",
"minimum_version": "17.6",
"deadline": "2023-03-03",
"update_new_hosts": true
},
"ipados_updates": {
"minimum_version": "14.14.14",
"minimum_version": "17.6",
"deadline": "2024-04-04",
"update_new_hosts": true
}
}
}`), http.StatusOK, &acResp)
require.Equal(t, "12.3.1", acResp.MDM.MacOSUpdates.MinimumVersion.Value)
require.Equal(t, "13.6.9", acResp.MDM.MacOSUpdates.MinimumVersion.Value)
require.Equal(t, "2022-01-01", acResp.MDM.MacOSUpdates.Deadline.Value)
require.Equal(t, optjson.SetBool(true), acResp.MDM.MacOSUpdates.UpdateNewHosts)
require.Equal(t, "13.13.13", acResp.MDM.IOSUpdates.MinimumVersion.Value)
require.Equal(t, "17.6", acResp.MDM.IOSUpdates.MinimumVersion.Value)
require.Equal(t, "2023-03-03", acResp.MDM.IOSUpdates.Deadline.Value)
require.Equal(t, optjson.Bool{Set: true}, acResp.MDM.IOSUpdates.UpdateNewHosts) // posted value is ignored for iOS
require.Equal(t, "14.14.14", acResp.MDM.IPadOSUpdates.MinimumVersion.Value)
require.Equal(t, "17.6", acResp.MDM.IPadOSUpdates.MinimumVersion.Value)
require.Equal(t, "2024-04-04", acResp.MDM.IPadOSUpdates.Deadline.Value)
require.Equal(t, optjson.Bool{Set: true}, acResp.MDM.IPadOSUpdates.UpdateNewHosts) // posted value is ignored for iOS
// edited macos min version activity got created
s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedMacOSMinVersion{}.ActivityName(), `{"deadline":"2022-01-01", "minimum_version":"12.3.1", "team_id": null, "team_name": null, "fleet_id": null, "fleet_name": null}`, 0)
s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedIOSMinVersion{}.ActivityName(), `{"deadline":"2023-03-03", "minimum_version":"13.13.13", "team_id": null, "team_name": null, "fleet_id": null, "fleet_name": null}`, 0)
s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedIPadOSMinVersion{}.ActivityName(), `{"deadline":"2024-04-04", "minimum_version":"14.14.14", "team_id": null, "team_name": null, "fleet_id": null, "fleet_name": null}`, 0)
s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedMacOSMinVersion{}.ActivityName(), `{"deadline":"2022-01-01", "minimum_version":"13.6.9", "team_id": null, "team_name": null, "fleet_id": null, "fleet_name": null}`, 0)
s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedIOSMinVersion{}.ActivityName(), `{"deadline":"2023-03-03", "minimum_version":"17.6", "team_id": null, "team_name": null, "fleet_id": null, "fleet_name": null}`, 0)
s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedIPadOSMinVersion{}.ActivityName(), `{"deadline":"2024-04-04", "minimum_version":"17.6", "team_id": null, "team_name": null, "fleet_id": null, "fleet_name": null}`, 0)
// Activity for 'Update New Hosts checked' got created
s.lastActivityOfTypeMatches(fleet.ActivityTypeEnabledMacosUpdateNewHosts{}.ActivityName(), "", 0)
s.assertAppleOSUpdatesDeclaration(nil, mdm.FleetMacOSUpdatesProfileName, &fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("12.3.1"), Deadline: optjson.SetString("2022-01-01"),
MinimumVersion: optjson.SetString("13.6.9"), Deadline: optjson.SetString("2022-01-01"),
})
s.assertAppleOSUpdatesDeclaration(nil, mdm.FleetIOSUpdatesProfileName, &fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("13.13.13"), Deadline: optjson.SetString("2023-03-03"),
MinimumVersion: optjson.SetString("17.6"), Deadline: optjson.SetString("2023-03-03"),
})
s.assertAppleOSUpdatesDeclaration(nil, mdm.FleetIPadOSUpdatesProfileName, &fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("14.14.14"), Deadline: optjson.SetString("2024-04-04"),
MinimumVersion: optjson.SetString("17.6"), Deadline: optjson.SetString("2024-04-04"),
})
// 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.Value)
require.Equal(t, "13.6.9", acResp.MDM.MacOSUpdates.MinimumVersion.Value)
require.Equal(t, "2022-01-01", acResp.MDM.MacOSUpdates.Deadline.Value)
require.Equal(t, optjson.SetBool(true), acResp.MDM.MacOSUpdates.UpdateNewHosts)
require.Equal(t, "13.13.13", acResp.MDM.IOSUpdates.MinimumVersion.Value)
require.Equal(t, "17.6", acResp.MDM.IOSUpdates.MinimumVersion.Value)
require.Equal(t, "2023-03-03", acResp.MDM.IOSUpdates.Deadline.Value)
require.Equal(t, optjson.Bool{Set: true}, acResp.MDM.IOSUpdates.UpdateNewHosts)
require.Equal(t, "14.14.14", acResp.MDM.IPadOSUpdates.MinimumVersion.Value)
require.Equal(t, "17.6", acResp.MDM.IPadOSUpdates.MinimumVersion.Value)
require.Equal(t, "2024-04-04", acResp.MDM.IPadOSUpdates.Deadline.Value)
require.Equal(t, optjson.Bool{Set: true}, acResp.MDM.IOSUpdates.UpdateNewHosts)
@@ -4555,63 +4568,63 @@ func (s *integrationEnterpriseTestSuite) TestMDMAppleOSUpdates() {
s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{
"mdm": {
"macos_updates": {
"minimum_version": "12.3.1",
"minimum_version": "13.6.9",
"deadline": "2024-01-01",
"update_new_hosts": false
},
"ios_updates": {
"minimum_version": "13.13.13",
"minimum_version": "17.6",
"deadline": "2025-05-05"
},
"ipados_updates": {
"minimum_version": "14.14.14",
"minimum_version": "17.6",
"deadline": "2026-06-06"
}
}
}`), http.StatusOK, &acResp)
require.Equal(t, "12.3.1", acResp.MDM.MacOSUpdates.MinimumVersion.Value)
require.Equal(t, "13.6.9", acResp.MDM.MacOSUpdates.MinimumVersion.Value)
require.Equal(t, "2024-01-01", acResp.MDM.MacOSUpdates.Deadline.Value)
require.Equal(t, optjson.SetBool(false), acResp.MDM.MacOSUpdates.UpdateNewHosts)
require.Equal(t, "13.13.13", acResp.MDM.IOSUpdates.MinimumVersion.Value)
require.Equal(t, "17.6", acResp.MDM.IOSUpdates.MinimumVersion.Value)
require.Equal(t, "2025-05-05", acResp.MDM.IOSUpdates.Deadline.Value)
require.Equal(t, optjson.Bool{Set: true}, acResp.MDM.IOSUpdates.UpdateNewHosts)
require.Equal(t, "14.14.14", acResp.MDM.IPadOSUpdates.MinimumVersion.Value)
require.Equal(t, "17.6", acResp.MDM.IPadOSUpdates.MinimumVersion.Value)
require.Equal(t, "2026-06-06", acResp.MDM.IPadOSUpdates.Deadline.Value)
require.Equal(t, optjson.Bool{Set: true}, acResp.MDM.IPadOSUpdates.UpdateNewHosts)
// another edited macos min version activity got created
s.lastActivityOfTypeMatches(fleet.ActivityTypeDisabledMacosUpdateNewHosts{}.ActivityName(), "", 0)
s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedMacOSMinVersion{}.ActivityName(), `{"deadline":"2024-01-01", "minimum_version":"12.3.1", "team_id": null, "team_name": null, "fleet_id": null, "fleet_name": null}`, 0)
s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedIOSMinVersion{}.ActivityName(), `{"deadline":"2025-05-05", "minimum_version":"13.13.13", "team_id": null, "team_name": null, "fleet_id": null, "fleet_name": null}`, 0)
lastActivity = s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedIPadOSMinVersion{}.ActivityName(), `{"deadline":"2026-06-06", "minimum_version":"14.14.14", "team_id": null, "team_name": null, "fleet_id": null, "fleet_name": null}`, 0)
s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedMacOSMinVersion{}.ActivityName(), `{"deadline":"2024-01-01", "minimum_version":"13.6.9", "team_id": null, "team_name": null, "fleet_id": null, "fleet_name": null}`, 0)
s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedIOSMinVersion{}.ActivityName(), `{"deadline":"2025-05-05", "minimum_version":"17.6", "team_id": null, "team_name": null, "fleet_id": null, "fleet_name": null}`, 0)
lastActivity = s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedIPadOSMinVersion{}.ActivityName(), `{"deadline":"2026-06-06", "minimum_version":"17.6", "team_id": null, "team_name": null, "fleet_id": null, "fleet_name": null}`, 0)
s.assertAppleOSUpdatesDeclaration(nil, mdm.FleetMacOSUpdatesProfileName, &fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("12.3.1"), Deadline: optjson.SetString("2024-01-01"),
MinimumVersion: optjson.SetString("13.6.9"), Deadline: optjson.SetString("2024-01-01"),
})
s.assertAppleOSUpdatesDeclaration(nil, mdm.FleetIOSUpdatesProfileName, &fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("13.13.13"), Deadline: optjson.SetString("2025-05-05"),
MinimumVersion: optjson.SetString("17.6"), Deadline: optjson.SetString("2025-05-05"),
})
s.assertAppleOSUpdatesDeclaration(nil, mdm.FleetIPadOSUpdatesProfileName, &fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("14.14.14"), Deadline: optjson.SetString("2026-06-06"),
MinimumVersion: optjson.SetString("17.6"), Deadline: optjson.SetString("2026-06-06"),
})
// 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, "13.6.9", acResp.MDM.MacOSUpdates.MinimumVersion.Value)
require.Equal(t, "2024-01-01", acResp.MDM.MacOSUpdates.Deadline.Value)
// no activity got created
s.lastActivityMatches("", ``, lastActivity)
s.assertAppleOSUpdatesDeclaration(nil, mdm.FleetMacOSUpdatesProfileName, &fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("12.3.1"), Deadline: optjson.SetString("2024-01-01"),
MinimumVersion: optjson.SetString("13.6.9"), Deadline: optjson.SetString("2024-01-01"),
})
s.assertAppleOSUpdatesDeclaration(nil, mdm.FleetIOSUpdatesProfileName, &fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("13.13.13"), Deadline: optjson.SetString("2025-05-05"),
MinimumVersion: optjson.SetString("17.6"), Deadline: optjson.SetString("2025-05-05"),
})
s.assertAppleOSUpdatesDeclaration(nil, mdm.FleetIPadOSUpdatesProfileName, &fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("14.14.14"), Deadline: optjson.SetString("2026-06-06"),
MinimumVersion: optjson.SetString("17.6"), Deadline: optjson.SetString("2026-06-06"),
})
// clear the apple OS requirements
+10 -10
View File
@@ -579,7 +579,7 @@ func (s *integrationMDMTestSuite) TestAppleProfileManagement() {
"mdm": {
"macos_updates": {
"deadline": "2023-12-31",
"minimum_version": "13.3.7"
"minimum_version": "14.6.1"
}
}
}`), http.StatusOK)
@@ -587,7 +587,7 @@ func (s *integrationMDMTestSuite) TestAppleProfileManagement() {
MDM: &fleet.TeamPayloadMDM{
MacOSUpdates: &fleet.AppleOSUpdateSettings{
Deadline: optjson.SetString("1992-01-01"),
MinimumVersion: optjson.SetString("13.1.1"),
MinimumVersion: optjson.SetString("14.6.1"),
},
},
}, http.StatusOK)
@@ -5275,7 +5275,7 @@ func (s *integrationMDMTestSuite) TestMDMBatchSetProfilesKeepsReservedNames() {
},
"macos_updates": {
"deadline": "2023-12-31",
"minimum_version": "13.3.7"
"minimum_version": "14.6.1"
}
}
}`), http.StatusOK, &acResp)
@@ -5328,7 +5328,7 @@ func (s *integrationMDMTestSuite) TestMDMBatchSetProfilesKeepsReservedNames() {
},
MacOSUpdates: &fleet.AppleOSUpdateSettings{
Deadline: optjson.SetString("2023-12-31"),
MinimumVersion: optjson.SetString("13.3.8"),
MinimumVersion: optjson.SetString("14.6.1"),
UpdateNewHosts: optjson.SetBool(true),
},
},
@@ -5340,7 +5340,7 @@ func (s *integrationMDMTestSuite) TestMDMBatchSetProfilesKeepsReservedNames() {
require.Equal(t, 4, tmResp.Team.Config.MDM.WindowsUpdates.DeadlineDays.Value)
require.Equal(t, 1, tmResp.Team.Config.MDM.WindowsUpdates.GracePeriodDays.Value)
require.Equal(t, "2023-12-31", tmResp.Team.Config.MDM.MacOSUpdates.Deadline.Value)
require.Equal(t, "13.3.8", tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion.Value)
require.Equal(t, "14.6.1", tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion.Value)
require.Equal(t, true, tmResp.Team.Config.MDM.MacOSUpdates.UpdateNewHosts.Value)
require.NoError(t, ReconcileAppleProfiles(ctx, s.ds, s.mdmCommander, s.logger))
@@ -8814,15 +8814,15 @@ func (s *integrationMDMTestSuite) TestSpecTeamsOSUpdatesDeployToHosts() {
Name: teamName,
MDM: fleet.TeamSpecMDM{
MacOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("14.1.0"),
MinimumVersion: optjson.SetString("14.6.1"),
Deadline: optjson.SetString("2024-03-01"),
},
IOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("17.1.0"),
MinimumVersion: optjson.SetString("17.6.1"),
Deadline: optjson.SetString("2024-03-01"),
},
IPadOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("17.1.0"),
MinimumVersion: optjson.SetString("17.6.1"),
Deadline: optjson.SetString("2024-03-01"),
},
WindowsUpdates: fleet.WindowsUpdates{
@@ -8878,11 +8878,11 @@ func (s *integrationMDMTestSuite) TestSpecTeamsOSUpdatesDeployToHosts() {
Deadline: optjson.SetString("2025-06-01"),
},
IOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("17.3.0"),
MinimumVersion: optjson.SetString("16.7.2"),
Deadline: optjson.SetString("2025-06-01"),
},
IPadOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("17.3.0"),
MinimumVersion: optjson.SetString("16.7.2"),
Deadline: optjson.SetString("2025-06-01"),
},
WindowsUpdates: fleet.WindowsUpdates{
+6 -6
View File
@@ -709,8 +709,8 @@ func (s *integrationMDMTestSuite) SetupSuite() {
_, err = w.Write(b)
require.NoError(s.T(), err)
}))
dev_mode.SetOverride("FLEET_DEV_GDMF_URL", s.appleGDMFSrv.URL, s.T())
s.T().Setenv("FLEET_DEV_GDMF_URL", s.appleGDMFSrv.URL)
s.T().Setenv("TEST_FLEETDM_API_URL", fleetdmSrv.URL)
s.T().Setenv("FLEET_DEV_STOKEN_AUTHENTICATED_APPS_URL", s.appleVPPProxySrv.URL)
@@ -7950,7 +7950,7 @@ func (s *integrationMDMTestSuite) TestOrbitConfigNudgeSettings() {
mdm:
macos_updates:
deadline: 2022-01-04
minimum_version: 12.1.3
minimum_version: 14.6.1
`))
// still empty if MDM is turned off for the host
@@ -7971,7 +7971,7 @@ func (s *integrationMDMTestSuite) 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.AppleOSUpdateSettings{Deadline: optjson.SetString("2022-01-04"), MinimumVersion: optjson.SetString("12.1.3")})
wantCfg, err := fleet.NewNudgeConfig(fleet.AppleOSUpdateSettings{Deadline: optjson.SetString("2022-01-04"), MinimumVersion: optjson.SetString("14.6.1")})
require.NoError(t, err)
require.Equal(t, wantCfg, resp.NudgeConfig)
require.Equal(t, wantCfg.OSVersionRequirements[0].RequiredInstallationDate.String(), "2022-01-04 20:00:00 +0000 UTC")
@@ -8001,7 +8001,7 @@ func (s *integrationMDMTestSuite) TestOrbitConfigNudgeSettings() {
MDM: &fleet.TeamPayloadMDM{
MacOSUpdates: &fleet.AppleOSUpdateSettings{
Deadline: optjson.SetString("1992-01-01"),
MinimumVersion: optjson.SetString("13.1.1"),
MinimumVersion: optjson.SetString("13.6.9"),
},
},
}, http.StatusOK, &tmResp)
@@ -8009,7 +8009,7 @@ func (s *integrationMDMTestSuite) 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.AppleOSUpdateSettings{Deadline: optjson.SetString("1992-01-01"), MinimumVersion: optjson.SetString("13.1.1")})
wantCfg, err = fleet.NewNudgeConfig(fleet.AppleOSUpdateSettings{Deadline: optjson.SetString("1992-01-01"), MinimumVersion: optjson.SetString("13.6.9")})
require.NoError(t, err)
require.Equal(t, wantCfg, resp.NudgeConfig)
require.Equal(t, wantCfg.OSVersionRequirements[0].RequiredInstallationDate.String(), "1992-01-01 20:00:00 +0000 UTC")
@@ -8031,7 +8031,7 @@ func (s *integrationMDMTestSuite) TestOrbitConfigNudgeSettings() {
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.AppleOSUpdateSettings{Deadline: optjson.SetString("2022-01-04"), MinimumVersion: optjson.SetString("12.1.3")})
wantCfg, err = fleet.NewNudgeConfig(fleet.AppleOSUpdateSettings{Deadline: optjson.SetString("2022-01-04"), MinimumVersion: optjson.SetString("14.6.1")})
require.NoError(t, err)
require.Equal(t, wantCfg, resp.NudgeConfig)
require.Equal(t, wantCfg.OSVersionRequirements[0].RequiredInstallationDate.String(), "2022-01-04 20:00:00 +0000 UTC")