Add enable_release_device_manually setting to team and no-team (#17698)

This commit is contained in:
Martin Angers 2024-03-19 13:21:16 -04:00 committed by GitHub
parent aef64e3241
commit b0ab7bbdc4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 463 additions and 53 deletions

View File

@ -0,0 +1 @@
* Added the `enable_release_device_manually` configuration setting for a team and no team. **Note** that the macOS automatic enrollment profile cannot set the `await_device_configured` option anymore, this setting is controlled by Fleet via the new `enable_release_device_manually` option.

View File

@ -14,6 +14,7 @@ import (
"path/filepath"
"sort"
"strconv"
"strings"
"sync"
"testing"
"time"
@ -201,12 +202,19 @@ spec:
MinimumVersion: optjson.SetString("12.3.1"),
Deadline: optjson.SetString("2011-03-01"),
},
MacOSSetup: fleet.MacOSSetup{
EnableReleaseDeviceManually: optjson.SetBool(false),
},
}
require.Equal(t, "[+] applied 2 teams\n", runAppForTest(t, []string{"apply", "-f", filename}))
assert.JSONEq(t, string(agentOpts), string(*teamsByName["team2"].Config.AgentOptions))
assert.JSONEq(t, string(newAgentOpts), string(*teamsByName["team1"].Config.AgentOptions))
assert.Equal(t, []*fleet.EnrollSecret{{Secret: "AAA"}}, enrolledSecretsCalled[uint(42)])
assert.Equal(t, fleet.TeamMDM{}, teamsByName["team2"].Config.MDM)
assert.Equal(t, fleet.TeamMDM{
MacOSSetup: fleet.MacOSSetup{
EnableReleaseDeviceManually: optjson.SetBool(false),
},
}, teamsByName["team2"].Config.MDM)
assert.Equal(t, newMDMSettings, teamsByName["team1"].Config.MDM)
assert.True(t, ds.ApplyEnrollSecretsFuncInvoked)
ds.ApplyEnrollSecretsFuncInvoked = false
@ -234,6 +242,9 @@ spec:
DeadlineDays: optjson.SetInt(5),
GracePeriodDays: optjson.SetInt(1),
},
MacOSSetup: fleet.MacOSSetup{
EnableReleaseDeviceManually: optjson.SetBool(false),
},
}
assert.Equal(t, newMDMSettings, teamsByName["team1"].Config.MDM)
@ -262,6 +273,9 @@ spec:
MacOSSettings: fleet.MacOSSettings{
CustomSettings: []fleet.MDMProfileSpec{{Path: mobileCfgPath}},
},
MacOSSetup: fleet.MacOSSetup{
EnableReleaseDeviceManually: optjson.SetBool(false),
},
}
assert.Contains(t, runAppForTest(t, []string{"apply", "-f", filename}), "[+] applied 1 teams\n")
@ -303,6 +317,9 @@ spec:
MacOSSettings: fleet.MacOSSettings{ // macos settings not provided, so not cleared
CustomSettings: []fleet.MDMProfileSpec{{Path: mobileCfgPath}},
},
MacOSSetup: fleet.MacOSSetup{
EnableReleaseDeviceManually: optjson.SetBool(false),
},
}
newAgentOpts = json.RawMessage(`{"config":{"views":{"foo":"qux"}}}`)
require.Equal(t, "[+] applied 1 teams\n", runAppForTest(t, []string{"apply", "-f", filename}))
@ -376,6 +393,9 @@ spec:
MacOSSettings: fleet.MacOSSettings{
CustomSettings: []fleet.MDMProfileSpec{},
},
MacOSSetup: fleet.MacOSSetup{
EnableReleaseDeviceManually: optjson.SetBool(false),
},
}
assert.Contains(t, runAppForTest(t, []string{"apply", "-f", filename}), "[+] applied 1 teams\n")
@ -524,6 +544,9 @@ spec:
MinimumVersion: optjson.SetString("12.1.1"),
Deadline: optjson.SetString("2011-02-01"),
},
MacOSSetup: fleet.MacOSSetup{
EnableReleaseDeviceManually: optjson.SetBool(false),
},
WindowsUpdates: fleet.WindowsUpdates{
DeadlineDays: optjson.SetInt(5),
GracePeriodDays: optjson.SetInt(1),
@ -576,6 +599,9 @@ spec:
MinimumVersion: optjson.SetString("12.1.1"),
Deadline: optjson.SetString("2011-02-01"),
},
MacOSSetup: fleet.MacOSSetup{
EnableReleaseDeviceManually: optjson.SetBool(false),
},
WindowsUpdates: fleet.WindowsUpdates{
DeadlineDays: optjson.Int{Set: true},
GracePeriodDays: optjson.Int{Set: true},
@ -1136,7 +1162,8 @@ spec:
assert.Equal(t, fleet.MDM{
EnabledAndConfigured: true,
MacOSSetup: fleet.MacOSSetup{
MacOSSetupAssistant: optjson.SetString(emptySetupAsst),
MacOSSetupAssistant: optjson.SetString(emptySetupAsst),
EnableReleaseDeviceManually: optjson.SetBool(false),
},
MacOSUpdates: fleet.MacOSUpdates{
MinimumVersion: optjson.SetString("10.10.10"),
@ -1177,8 +1204,9 @@ spec:
assert.Equal(t, fleet.MDM{
EnabledAndConfigured: true,
MacOSSetup: fleet.MacOSSetup{
MacOSSetupAssistant: optjson.SetString(emptySetupAsst),
BootstrapPackage: optjson.SetString(bootstrapURL),
MacOSSetupAssistant: optjson.SetString(emptySetupAsst),
BootstrapPackage: optjson.SetString(bootstrapURL),
EnableReleaseDeviceManually: optjson.SetBool(false),
},
MacOSUpdates: fleet.MacOSUpdates{
MinimumVersion: optjson.SetString("10.10.10"),
@ -1235,6 +1263,9 @@ spec:
MinimumVersion: optjson.SetString("10.10.10"),
Deadline: optjson.SetString("1992-03-01"),
},
MacOSSetup: fleet.MacOSSetup{
EnableReleaseDeviceManually: optjson.SetBool(false),
},
WindowsUpdates: fleet.WindowsUpdates{
DeadlineDays: optjson.SetInt(0),
GracePeriodDays: optjson.SetInt(1),
@ -1279,7 +1310,8 @@ spec:
GracePeriodDays: optjson.SetInt(1),
},
MacOSSetup: fleet.MacOSSetup{
MacOSSetupAssistant: optjson.SetString(emptySetupAsst),
MacOSSetupAssistant: optjson.SetString(emptySetupAsst),
EnableReleaseDeviceManually: optjson.SetBool(false),
},
}, savedTeam.Config.MDM)
@ -1315,8 +1347,9 @@ spec:
GracePeriodDays: optjson.SetInt(1),
},
MacOSSetup: fleet.MacOSSetup{
MacOSSetupAssistant: optjson.SetString(emptySetupAsst),
BootstrapPackage: optjson.SetString(bootstrapURL),
MacOSSetupAssistant: optjson.SetString(emptySetupAsst),
BootstrapPackage: optjson.SetString(bootstrapURL),
EnableReleaseDeviceManually: optjson.SetBool(false),
},
}, savedTeam.Config.MDM)
@ -1728,6 +1761,9 @@ func TestApplyMacosSetup(t *testing.T) {
invalidURLMacosSetup := writeTmpJSON(t, map[string]any{
"url": "https://example.com",
})
invalidAwaitMacosSetup := writeTmpJSON(t, map[string]any{
"await_device_configured": true,
})
const (
appConfigSpec = `
@ -1738,6 +1774,9 @@ spec:
macos_setup:
bootstrap_package: %s
macos_setup_assistant: %s
`
appConfigEnableReleaseSpec = appConfigSpec + `
enable_release_device_manually: %s
`
appConfigNoKeySpec = `
apiVersion: v1
@ -1764,6 +1803,9 @@ spec:
macos_setup:
bootstrap_package: %s
macos_setup_assistant: %s
`
team1EnableReleaseSpec = team1Spec + `
enable_release_device_manually: %s
`
team1NoKeySpec = `
apiVersion: v1
@ -1925,11 +1967,17 @@ spec:
b, err = os.ReadFile(filepath.Join("testdata", "macosSetupExpectedAppConfigSet.yml"))
require.NoError(t, err)
expectedAppCfgSet := fmt.Sprintf(string(b), "", emptyMacosSetup)
expectedAppCfgSetReleaseEnabled := strings.ReplaceAll(expectedAppCfgSet, `enable_release_device_manually: false`, `enable_release_device_manually: true`)
b, err = os.ReadFile(filepath.Join("testdata", "macosSetupExpectedTeam1Empty.yml"))
require.NoError(t, err)
expectedEmptyTm1 := string(b)
b, err = os.ReadFile(filepath.Join("testdata", "macosSetupExpectedTeam1Set.yml"))
require.NoError(t, err)
expectedTm1Set := fmt.Sprintf(string(b), "", "")
expectedTm1SetReleaseEnabled := strings.ReplaceAll(expectedTm1Set, `enable_release_device_manually: false`, `enable_release_device_manually: true`)
b, err = os.ReadFile(filepath.Join("testdata", "macosSetupExpectedTeam1And2Empty.yml"))
require.NoError(t, err)
expectedEmptyTm1And2 := string(b)
@ -1958,8 +2006,8 @@ spec:
assert.YAMLEq(t, expectedEmptyAppCfg, runAppForTest(t, []string{"get", "config", "--yaml"}))
assert.YAMLEq(t, expectedEmptyTm1, runAppForTest(t, []string{"get", "teams", "--yaml"}))
// apply appconfig for real
name = writeTmpYml(t, fmt.Sprintf(appConfigSpec, "", emptyMacosSetup))
// apply appconfig for real, and enable release device
name = writeTmpYml(t, fmt.Sprintf(appConfigEnableReleaseSpec, "", emptyMacosSetup, "true"))
assert.Equal(t, "[+] applied fleet config\n", runAppForTest(t, []string{"apply", "-f", name}))
assert.True(t, ds.SetOrUpdateMDMAppleSetupAssistantFuncInvoked)
assert.True(t, ds.SaveAppConfigFuncInvoked)
@ -1972,7 +2020,7 @@ spec:
assert.True(t, ds.SaveTeamFuncInvoked)
// get, setup assistant is now set
assert.YAMLEq(t, expectedAppCfgSet, runAppForTest(t, []string{"get", "config", "--yaml"}))
assert.YAMLEq(t, expectedAppCfgSetReleaseEnabled, runAppForTest(t, []string{"get", "config", "--yaml"}))
assert.YAMLEq(t, expectedTm1And2Set, runAppForTest(t, []string{"get", "teams", "--yaml"}))
// clear with dry-run, appconfig
@ -2008,11 +2056,11 @@ spec:
assert.True(t, ds.SaveTeamFuncInvoked)
// get, results unchanged
assert.YAMLEq(t, expectedAppCfgSet, runAppForTest(t, []string{"get", "config", "--yaml"}))
assert.YAMLEq(t, expectedAppCfgSetReleaseEnabled, runAppForTest(t, []string{"get", "config", "--yaml"}))
assert.YAMLEq(t, expectedTm1And2Set, runAppForTest(t, []string{"get", "teams", "--yaml"}))
// clear appconfig for real
name = writeTmpYml(t, fmt.Sprintf(appConfigSpec, "", ""))
name = writeTmpYml(t, fmt.Sprintf(appConfigEnableReleaseSpec, "", "", "false"))
ds.SaveAppConfigFuncInvoked = false
assert.Equal(t, "[+] applied fleet config\n", runAppForTest(t, []string{"apply", "-f", name}))
assert.False(t, ds.SetOrUpdateMDMAppleSetupAssistantFuncInvoked)
@ -2031,16 +2079,40 @@ spec:
assert.YAMLEq(t, expectedEmptyAppCfg, runAppForTest(t, []string{"get", "config", "--yaml"}))
assert.YAMLEq(t, expectedEmptyTm1And2, runAppForTest(t, []string{"get", "teams", "--yaml"}))
// apply appconfig with invalid key
// apply team 1 without the setup assistant key but enable device release
name = writeTmpYml(t, fmt.Sprintf(team1EnableReleaseSpec, "", "", "true"))
ds.SetOrUpdateMDMAppleSetupAssistantFuncInvoked = false
ds.DeleteMDMAppleSetupAssistantFuncInvoked = false
ds.SaveTeamFuncInvoked = false
assert.Equal(t, "[+] applied 1 teams\n", runAppForTest(t, []string{"apply", "-f", name}))
assert.False(t, ds.SetOrUpdateMDMAppleSetupAssistantFuncInvoked)
assert.False(t, ds.DeleteMDMAppleSetupAssistantFuncInvoked)
assert.True(t, ds.SaveTeamFuncInvoked)
assert.YAMLEq(t, expectedTm1SetReleaseEnabled, runAppForTest(t, []string{"get", "teams", "--yaml"}))
// apply appconfig with invalid URL key
name = writeTmpYml(t, fmt.Sprintf(appConfigSpec, "", invalidURLMacosSetup))
_, err = runAppNoChecks([]string{"apply", "-f", name})
require.ErrorContains(t, err, "The automatic enrollment profile cant include url.")
require.ErrorContains(t, err, "The automatic enrollment profile can't include url.")
assert.False(t, ds.SetOrUpdateMDMAppleSetupAssistantFuncInvoked)
// apply teams with invalid key
// apply teams with invalid URL key
name = writeTmpYml(t, fmt.Sprintf(team1And2Spec, "", invalidURLMacosSetup, "", invalidURLMacosSetup))
_, err = runAppNoChecks([]string{"apply", "-f", name})
require.ErrorContains(t, err, "The automatic enrollment profile cant include url.")
require.ErrorContains(t, err, "The automatic enrollment profile can't include url.")
assert.False(t, ds.SetOrUpdateMDMAppleSetupAssistantFuncInvoked)
// apply appconfig with invalid await_device_configured key
name = writeTmpYml(t, fmt.Sprintf(appConfigSpec, "", invalidAwaitMacosSetup))
_, err = runAppNoChecks([]string{"apply", "-f", name})
require.ErrorContains(t, err, `The profile can't include "await_device_configured" option.`)
assert.False(t, ds.SetOrUpdateMDMAppleSetupAssistantFuncInvoked)
// apply teams with invalid await_device_configured key
name = writeTmpYml(t, fmt.Sprintf(team1And2Spec, "", invalidAwaitMacosSetup, "", invalidAwaitMacosSetup))
_, err = runAppNoChecks([]string{"apply", "-f", name})
require.ErrorContains(t, err, `The profile can't include "await_device_configured" option.`)
assert.False(t, ds.SetOrUpdateMDMAppleSetupAssistantFuncInvoked)
})

View File

@ -107,7 +107,8 @@
"macos_setup": {
"bootstrap_package": null,
"enable_end_user_authentication": false,
"macos_setup_assistant": null
"macos_setup_assistant": null,
"enable_release_device_manually": false
},
"windows_settings": {
"custom_settings": null

View File

@ -35,6 +35,7 @@ spec:
macos_setup:
bootstrap_package:
enable_end_user_authentication: false
enable_release_device_manually: false
macos_setup_assistant:
windows_settings:
custom_settings: null

View File

@ -65,7 +65,8 @@
"macos_setup": {
"bootstrap_package": null,
"enable_end_user_authentication": false,
"macos_setup_assistant": null
"macos_setup_assistant": null,
"enable_release_device_manually": false
},
"windows_settings": {
"custom_settings": null

View File

@ -35,6 +35,7 @@ spec:
macos_setup:
bootstrap_package:
enable_end_user_authentication: false
enable_release_device_manually: false
macos_setup_assistant:
windows_settings:
custom_settings:

View File

@ -44,7 +44,8 @@
"macos_setup": {
"bootstrap_package": null,
"enable_end_user_authentication": false,
"macos_setup_assistant": null
"macos_setup_assistant": null,
"enable_release_device_manually": false
},
"windows_settings": {
"custom_settings": null
@ -117,7 +118,8 @@
"macos_setup": {
"bootstrap_package": null,
"enable_end_user_authentication": false,
"macos_setup_assistant": null
"macos_setup_assistant": null,
"enable_release_device_manually": false
},
"windows_settings": {
"custom_settings": null

View File

@ -24,6 +24,7 @@ spec:
macos_setup:
bootstrap_package:
enable_end_user_authentication: false
enable_release_device_manually: false
macos_setup_assistant:
scripts: null
webhook_settings:
@ -64,6 +65,7 @@ spec:
macos_setup:
bootstrap_package:
enable_end_user_authentication: false
enable_release_device_manually: false
macos_setup_assistant:
scripts: null
webhook_settings:

View File

@ -32,6 +32,7 @@ spec:
bootstrap_package: null
enable_end_user_authentication: false
macos_setup_assistant: null
enable_release_device_manually: false
macos_updates:
deadline: null
minimum_version: null

View File

@ -32,6 +32,7 @@ spec:
bootstrap_package: %s
enable_end_user_authentication: false
macos_setup_assistant: %s
enable_release_device_manually: false
macos_updates:
deadline: null
minimum_version: null

View File

@ -19,6 +19,7 @@ spec:
bootstrap_package: null
enable_end_user_authentication: false
macos_setup_assistant: null
enable_release_device_manually: false
macos_updates:
deadline: null
minimum_version: null
@ -49,6 +50,7 @@ spec:
macos_setup:
bootstrap_package: null
macos_setup_assistant: null
enable_release_device_manually: false
macos_updates:
deadline: null
minimum_version: null

View File

@ -19,6 +19,7 @@ spec:
bootstrap_package: %s
enable_end_user_authentication: false
macos_setup_assistant: %s
enable_release_device_manually: false
macos_updates:
deadline: null
minimum_version: null
@ -49,6 +50,7 @@ spec:
macos_setup:
bootstrap_package: %s
macos_setup_assistant: %s
enable_release_device_manually: false
macos_updates:
deadline: null
minimum_version: null

View File

@ -17,6 +17,7 @@ spec:
bootstrap_package: null
enable_end_user_authentication: false
macos_setup_assistant: null
enable_release_device_manually: false
macos_updates:
deadline: null
minimum_version: null

View File

@ -0,0 +1,31 @@
apiVersion: v1
kind: team
spec:
team:
features:
enable_host_users: true
enable_software_inventory: true
host_expiry_settings:
host_expiry_enabled: false
host_expiry_window: 0
mdm:
enable_disk_encryption: false
macos_settings:
custom_settings: null
windows_settings:
custom_settings: null
macos_setup:
bootstrap_package: %s
enable_end_user_authentication: false
macos_setup_assistant: %s
enable_release_device_manually: false
macos_updates:
deadline: null
minimum_version: null
windows_updates:
deadline_days: null
grace_period_days: null
scripts: null
webhook_settings:
host_status_webhook: null
name: tm1

View File

@ -237,6 +237,13 @@ func (svc *Service) updateAppConfigMDMAppleSetup(ctx context.Context, payload fl
}
}
if payload.EnableReleaseDeviceManually != nil {
if ac.MDM.MacOSSetup.EnableReleaseDeviceManually.Value != *payload.EnableReleaseDeviceManually {
ac.MDM.MacOSSetup.EnableReleaseDeviceManually = optjson.SetBool(*payload.EnableReleaseDeviceManually)
didUpdate = true
}
}
if didUpdate {
if err := svc.ds.SaveAppConfig(ctx, ac); err != nil {
return err
@ -550,7 +557,10 @@ func (svc *Service) SetOrUpdateMDMAppleSetupAssistant(ctx context.Context, asst
}
if _, ok := m["url"]; ok {
return nil, ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("profile", `Couldnt edit macos_setup_assistant. The automatic enrollment profile cant include url.`))
return nil, ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("profile", `Couldn't edit macos_setup_assistant. The automatic enrollment profile can't include url.`))
}
if _, ok := m["await_device_configured"]; ok {
return nil, ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("profile", `Couldn't edit macos_setup_assistant. The profile can't include "await_device_configured" option.`))
}
// must read the existing setup assistant first to detect if it did change
@ -933,6 +943,7 @@ func (svc *Service) getOrCreatePreassignTeam(ctx context.Context, groups []strin
// instead by CopyDefaultMDMAppleBootstrapPackage below
// BootstrapPackage: ac.MDM.MacOSSetup.BootstrapPackage,
EnableEndUserAuthentication: ac.MDM.MacOSSetup.EnableEndUserAuthentication,
// TODO(mna): should we copy the EnableReleaseDeviceManually setting from the global config?
},
}

View File

@ -7,6 +7,7 @@ import (
"fmt"
"net/http"
"github.com/fleetdm/fleet/v4/pkg/optjson"
"github.com/fleetdm/fleet/v4/server"
"github.com/fleetdm/fleet/v4/server/authz"
authz_ctx "github.com/fleetdm/fleet/v4/server/contexts/authz"
@ -856,12 +857,16 @@ func (svc *Service) createTeamFromSpec(
return nil, err
}
macOSSetup := spec.MDM.MacOSSetup
if macOSSetup.MacOSSetupAssistant.Value != "" || macOSSetup.BootstrapPackage.Value != "" {
if !macOSSetup.EnableReleaseDeviceManually.Valid {
macOSSetup.EnableReleaseDeviceManually = optjson.SetBool(false)
}
if macOSSetup.MacOSSetupAssistant.Value != "" || macOSSetup.BootstrapPackage.Value != "" || macOSSetup.EnableReleaseDeviceManually.Value {
if !appCfg.MDM.EnabledAndConfigured {
return nil, ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("macos_setup",
`Couldn't update macos_setup because MDM features aren't turned on in Fleet. Use fleetctl generate mdm-apple and then fleet serve with mdm configuration to turn on MDM features.`))
}
}
enableDiskEncryption := spec.MDM.EnableDiskEncryption.Value
if !spec.MDM.EnableDiskEncryption.Valid {
if de := macOSSettings.DeprecatedEnableDiskEncryption; de != nil {
@ -993,8 +998,11 @@ func (svc *Service) editTeamFromSpec(
`Couldn't edit enable_disk_encryption. Neither macOS MDM nor Windows is turned on. Visit https://fleetdm.com/docs/using-fleet to learn how to turn on MDM.`))
}
if !team.Config.MDM.MacOSSetup.EnableReleaseDeviceManually.Valid {
team.Config.MDM.MacOSSetup.EnableReleaseDeviceManually = optjson.SetBool(false)
}
oldMacOSSetup := team.Config.MDM.MacOSSetup
var didUpdateSetupAssistant, didUpdateBootstrapPackage bool
var didUpdateSetupAssistant, didUpdateBootstrapPackage, didUpdateEnableReleaseManually bool
if spec.MDM.MacOSSetup.MacOSSetupAssistant.Set {
didUpdateSetupAssistant = oldMacOSSetup.MacOSSetupAssistant.Value != spec.MDM.MacOSSetup.MacOSSetupAssistant.Value
team.Config.MDM.MacOSSetup.MacOSSetupAssistant = spec.MDM.MacOSSetup.MacOSSetupAssistant
@ -1003,12 +1011,17 @@ func (svc *Service) editTeamFromSpec(
didUpdateBootstrapPackage = oldMacOSSetup.BootstrapPackage.Value != spec.MDM.MacOSSetup.BootstrapPackage.Value
team.Config.MDM.MacOSSetup.BootstrapPackage = spec.MDM.MacOSSetup.BootstrapPackage
}
if spec.MDM.MacOSSetup.EnableReleaseDeviceManually.Valid {
didUpdateEnableReleaseManually = oldMacOSSetup.EnableReleaseDeviceManually.Value != spec.MDM.MacOSSetup.EnableReleaseDeviceManually.Value
team.Config.MDM.MacOSSetup.EnableReleaseDeviceManually = spec.MDM.MacOSSetup.EnableReleaseDeviceManually
}
// TODO(mna): doesn't look like we create an activity for macos updates when
// modified via spec? Doing the same for Windows, but should we?
if !appCfg.MDM.EnabledAndConfigured &&
((didUpdateSetupAssistant && team.Config.MDM.MacOSSetup.MacOSSetupAssistant.Value != "") ||
(didUpdateBootstrapPackage && team.Config.MDM.MacOSSetup.BootstrapPackage.Value != "")) {
(didUpdateBootstrapPackage && team.Config.MDM.MacOSSetup.BootstrapPackage.Value != "") ||
(didUpdateEnableReleaseManually && team.Config.MDM.MacOSSetup.EnableReleaseDeviceManually.Value)) {
return ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("macos_setup",
`Couldn't update macos_setup because MDM features aren't turned on in Fleet. Use fleetctl generate mdm-apple and then fleet serve with mdm configuration to turn on MDM features.`))
}
@ -1237,6 +1250,13 @@ func (svc *Service) updateTeamMDMAppleSetup(ctx context.Context, tm *fleet.Team,
}
}
if payload.EnableReleaseDeviceManually != nil {
if tm.Config.MDM.MacOSSetup.EnableReleaseDeviceManually.Value != *payload.EnableReleaseDeviceManually {
tm.Config.MDM.MacOSSetup.EnableReleaseDeviceManually = optjson.SetBool(*payload.EnableReleaseDeviceManually)
didUpdate = true
}
}
if didUpdate {
if _, err := svc.ds.SaveTeam(ctx, tm); err != nil {
return err

View File

@ -41,7 +41,7 @@ CREATE TABLE `app_config_json` (
UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
INSERT INTO `app_config_json` VALUES (1,'{\"mdm\": {\"macos_setup\": {\"bootstrap_package\": null, \"macos_setup_assistant\": null, \"enable_end_user_authentication\": false}, \"macos_updates\": {\"deadline\": null, \"minimum_version\": null}, \"macos_settings\": {\"custom_settings\": null}, \"macos_migration\": {\"mode\": \"\", \"enable\": false, \"webhook_url\": \"\"}, \"windows_updates\": {\"deadline_days\": null, \"grace_period_days\": null}, \"windows_settings\": {\"custom_settings\": null}, \"apple_bm_default_team\": \"\", \"apple_bm_terms_expired\": false, \"enable_disk_encryption\": false, \"enabled_and_configured\": false, \"end_user_authentication\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"issuer_uri\": \"\", \"metadata_url\": \"\"}, \"windows_enabled_and_configured\": false, \"apple_bm_enabled_and_configured\": false}, \"scripts\": null, \"features\": {\"enable_host_users\": true, \"enable_software_inventory\": false}, \"org_info\": {\"org_name\": \"\", \"contact_url\": \"\", \"org_logo_url\": \"\", \"org_logo_url_light_background\": \"\"}, \"integrations\": {\"jira\": null, \"zendesk\": null}, \"sso_settings\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"enable_sso\": false, \"issuer_uri\": \"\", \"metadata_url\": \"\", \"idp_image_url\": \"\", \"enable_jit_role_sync\": false, \"enable_sso_idp_login\": false, \"enable_jit_provisioning\": false}, \"agent_options\": {\"config\": {\"options\": {\"logger_plugin\": \"tls\", \"pack_delimiter\": \"/\", \"logger_tls_period\": 10, \"distributed_plugin\": \"tls\", \"disable_distributed\": false, \"logger_tls_endpoint\": \"/api/osquery/log\", \"distributed_interval\": 10, \"distributed_tls_max_attempts\": 3}, \"decorators\": {\"load\": [\"SELECT uuid AS host_uuid FROM system_info;\", \"SELECT hostname AS hostname FROM system_info;\"]}}, \"overrides\": {}}, \"fleet_desktop\": {\"transparency_url\": \"\"}, \"smtp_settings\": {\"port\": 587, \"domain\": \"\", \"server\": \"\", \"password\": \"\", \"user_name\": \"\", \"configured\": false, \"enable_smtp\": false, \"enable_ssl_tls\": true, \"sender_address\": \"\", \"enable_start_tls\": true, \"verify_ssl_certs\": true, \"authentication_type\": \"0\", \"authentication_method\": \"0\"}, \"server_settings\": {\"server_url\": \"\", \"enable_analytics\": false, \"scripts_disabled\": false, \"deferred_save_host\": false, \"live_query_disabled\": false, \"query_reports_disabled\": false}, \"webhook_settings\": {\"interval\": \"0s\", \"host_status_webhook\": {\"days_count\": 0, \"destination_url\": \"\", \"host_percentage\": 0, \"enable_host_status_webhook\": false}, \"vulnerabilities_webhook\": {\"destination_url\": \"\", \"host_batch_size\": 0, \"enable_vulnerabilities_webhook\": false}, \"failing_policies_webhook\": {\"policy_ids\": null, \"destination_url\": \"\", \"host_batch_size\": 0, \"enable_failing_policies_webhook\": false}}, \"host_expiry_settings\": {\"host_expiry_window\": 0, \"host_expiry_enabled\": false}, \"vulnerability_settings\": {\"databases_path\": \"\"}}','2020-01-01 01:01:01','2020-01-01 01:01:01');
INSERT INTO `app_config_json` VALUES (1,'{\"mdm\": {\"macos_setup\": {\"bootstrap_package\": null, \"macos_setup_assistant\": null, \"enable_end_user_authentication\": false, \"enable_release_device_manually\": false}, \"macos_updates\": {\"deadline\": null, \"minimum_version\": null}, \"macos_settings\": {\"custom_settings\": null}, \"macos_migration\": {\"mode\": \"\", \"enable\": false, \"webhook_url\": \"\"}, \"windows_updates\": {\"deadline_days\": null, \"grace_period_days\": null}, \"windows_settings\": {\"custom_settings\": null}, \"apple_bm_default_team\": \"\", \"apple_bm_terms_expired\": false, \"enable_disk_encryption\": false, \"enabled_and_configured\": false, \"end_user_authentication\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"issuer_uri\": \"\", \"metadata_url\": \"\"}, \"windows_enabled_and_configured\": false, \"apple_bm_enabled_and_configured\": false}, \"scripts\": null, \"features\": {\"enable_host_users\": true, \"enable_software_inventory\": false}, \"org_info\": {\"org_name\": \"\", \"contact_url\": \"\", \"org_logo_url\": \"\", \"org_logo_url_light_background\": \"\"}, \"integrations\": {\"jira\": null, \"zendesk\": null}, \"sso_settings\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"enable_sso\": false, \"issuer_uri\": \"\", \"metadata_url\": \"\", \"idp_image_url\": \"\", \"enable_jit_role_sync\": false, \"enable_sso_idp_login\": false, \"enable_jit_provisioning\": false}, \"agent_options\": {\"config\": {\"options\": {\"logger_plugin\": \"tls\", \"pack_delimiter\": \"/\", \"logger_tls_period\": 10, \"distributed_plugin\": \"tls\", \"disable_distributed\": false, \"logger_tls_endpoint\": \"/api/osquery/log\", \"distributed_interval\": 10, \"distributed_tls_max_attempts\": 3}, \"decorators\": {\"load\": [\"SELECT uuid AS host_uuid FROM system_info;\", \"SELECT hostname AS hostname FROM system_info;\"]}}, \"overrides\": {}}, \"fleet_desktop\": {\"transparency_url\": \"\"}, \"smtp_settings\": {\"port\": 587, \"domain\": \"\", \"server\": \"\", \"password\": \"\", \"user_name\": \"\", \"configured\": false, \"enable_smtp\": false, \"enable_ssl_tls\": true, \"sender_address\": \"\", \"enable_start_tls\": true, \"verify_ssl_certs\": true, \"authentication_type\": \"0\", \"authentication_method\": \"0\"}, \"server_settings\": {\"server_url\": \"\", \"enable_analytics\": false, \"scripts_disabled\": false, \"deferred_save_host\": false, \"live_query_disabled\": false, \"query_reports_disabled\": false}, \"webhook_settings\": {\"interval\": \"0s\", \"host_status_webhook\": {\"days_count\": 0, \"destination_url\": \"\", \"host_percentage\": 0, \"enable_host_status_webhook\": false}, \"vulnerabilities_webhook\": {\"destination_url\": \"\", \"host_batch_size\": 0, \"enable_vulnerabilities_webhook\": false}, \"failing_policies_webhook\": {\"policy_ids\": null, \"destination_url\": \"\", \"host_batch_size\": 0, \"enable_failing_policies_webhook\": false}}, \"host_expiry_settings\": {\"host_expiry_window\": 0, \"host_expiry_enabled\": false}, \"vulnerability_settings\": {\"databases_path\": \"\"}}','2020-01-01 01:01:01','2020-01-01 01:01:01');
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `carve_blocks` (

View File

@ -613,8 +613,9 @@ func testTeamsMDMConfig(t *testing.T, ds *Datastore) {
GracePeriodDays: optjson.SetInt(3),
},
MacOSSetup: fleet.MacOSSetup{
BootstrapPackage: optjson.SetString("bootstrap"),
MacOSSetupAssistant: optjson.SetString("assistant"),
BootstrapPackage: optjson.SetString("bootstrap"),
MacOSSetupAssistant: optjson.SetString("assistant"),
EnableReleaseDeviceManually: optjson.SetBool(false),
},
WindowsSettings: fleet.WindowsSettings{
CustomSettings: optjson.SetSlice([]fleet.MDMProfileSpec{{Path: "foo"}, {Path: "bar"}}),

View File

@ -380,6 +380,7 @@ type MacOSSetup struct {
BootstrapPackage optjson.String `json:"bootstrap_package"`
EnableEndUserAuthentication bool `json:"enable_end_user_authentication"`
MacOSSetupAssistant optjson.String `json:"macos_setup_assistant"`
EnableReleaseDeviceManually optjson.Bool `json:"enable_release_device_manually"`
}
// MacOSMigration contains settings related to the MDM migration work flow.
@ -809,6 +810,9 @@ func (c AppConfig) MarshalJSON() ([]byte, error) {
if !c.MDM.EnableDiskEncryption.Valid {
c.MDM.EnableDiskEncryption = optjson.SetBool(false)
}
if !c.MDM.MacOSSetup.EnableReleaseDeviceManually.Valid {
c.MDM.MacOSSetup.EnableReleaseDeviceManually = optjson.SetBool(false)
}
type aliasConfig AppConfig
aa := aliasConfig(c)

View File

@ -416,6 +416,7 @@ func (p MDMAppleSettingsPayload) AuthzType() string {
type MDMAppleSetupPayload struct {
TeamID *uint `json:"team_id"`
EnableEndUserAuthentication *bool `json:"enable_end_user_authentication"`
EnableReleaseDeviceManually *bool `json:"enable_release_device_manually"`
}
// AuthzType implements authz.AuthzTyper.

View File

@ -120,6 +120,9 @@ func (t *Team) UnmarshalJSON(b []byte) error {
return err
}
if !x.MDM.MacOSSetup.EnableReleaseDeviceManually.Valid {
x.MDM.MacOSSetup.EnableReleaseDeviceManually = optjson.SetBool(false)
}
*t = Team{
ID: x.ID,
CreatedAt: x.CreatedAt,
@ -241,6 +244,10 @@ func (t *TeamConfig) Scan(val interface{}) error {
// Value implements the sql.Valuer interface
func (t TeamConfig) Value() (driver.Value, error) {
// force-save as the default `false` value if not set
if !t.MDM.MacOSSetup.EnableReleaseDeviceManually.Valid {
t.MDM.MacOSSetup.EnableReleaseDeviceManually = optjson.SetBool(false)
}
return json.Marshal(t)
}

View File

@ -14,6 +14,7 @@ import (
"net/http"
"net/url"
"github.com/fleetdm/fleet/v4/pkg/optjson"
"github.com/fleetdm/fleet/v4/pkg/rawjson"
"github.com/fleetdm/fleet/v4/server/authz"
authz_ctx "github.com/fleetdm/fleet/v4/server/contexts/authz"
@ -343,6 +344,19 @@ func (svc *Service) ModifyAppConfig(ctx context.Context, p []byte, applyOpts fle
} else if appConfig.MDM.EnableDiskEncryption.Set && !appConfig.MDM.EnableDiskEncryption.Valid {
appConfig.MDM.EnableDiskEncryption = oldAppConfig.MDM.EnableDiskEncryption
}
// this is to handle the case where `enable_release_device_manually: null` is
// passed in the request payload, which should be treated as "not present/not
// changed" by the PATCH. We should really try to find a more general way to
// handle this.
if !oldAppConfig.MDM.MacOSSetup.EnableReleaseDeviceManually.Valid {
// this makes a DB migration unnecessary, will update the field to its default false value as necessary
oldAppConfig.MDM.MacOSSetup.EnableReleaseDeviceManually = optjson.SetBool(false)
}
if newAppConfig.MDM.MacOSSetup.EnableReleaseDeviceManually.Valid {
appConfig.MDM.MacOSSetup.EnableReleaseDeviceManually = newAppConfig.MDM.MacOSSetup.EnableReleaseDeviceManually
} else {
appConfig.MDM.MacOSSetup.EnableReleaseDeviceManually = oldAppConfig.MDM.MacOSSetup.EnableReleaseDeviceManually
}
var legacyUsedWarning error
if legacyKeys := appConfig.DidUnmarshalLegacySettings(); len(legacyKeys) > 0 {
@ -674,6 +688,9 @@ func (svc *Service) validateMDM(
if mdm.MacOSSetup.MacOSSetupAssistant.Value != "" && oldMdm.MacOSSetup.MacOSSetupAssistant.Value != mdm.MacOSSetup.MacOSSetupAssistant.Value && !license.IsPremium() {
invalid.Append("macos_setup.macos_setup_assistant", ErrMissingLicense.Error())
}
if mdm.MacOSSetup.EnableReleaseDeviceManually.Value && oldMdm.MacOSSetup.EnableReleaseDeviceManually.Value != mdm.MacOSSetup.EnableReleaseDeviceManually.Value && !license.IsPremium() {
invalid.Append("macos_setup.enable_release_device_manually", ErrMissingLicense.Error())
}
if mdm.MacOSSetup.BootstrapPackage.Value != "" && oldMdm.MacOSSetup.BootstrapPackage.Value != mdm.MacOSSetup.BootstrapPackage.Value && !license.IsPremium() {
invalid.Append("macos_setup.bootstrap_package", ErrMissingLicense.Error())
}
@ -694,6 +711,11 @@ func (svc *Service) validateMDM(
`Couldn't update macos_setup because MDM features aren't turned on in Fleet. Use fleetctl generate mdm-apple and then fleet serve with mdm configuration to turn on MDM features.`)
}
if mdm.MacOSSetup.EnableReleaseDeviceManually.Value && oldMdm.MacOSSetup.EnableReleaseDeviceManually.Value != mdm.MacOSSetup.EnableReleaseDeviceManually.Value {
invalid.Append("macos_setup.enable_release_device_manually",
`Couldn't update macos_setup because MDM features aren't turned on in Fleet. Use fleetctl generate mdm-apple and then fleet serve with mdm configuration to turn on MDM features.`)
}
if mdm.MacOSSetup.BootstrapPackage.Value != "" && oldMdm.MacOSSetup.BootstrapPackage.Value != mdm.MacOSSetup.BootstrapPackage.Value {
invalid.Append("macos_setup.bootstrap_package",
`Couldn't update macos_setup because MDM features aren't turned on in Fleet. Use fleetctl generate mdm-apple and then fleet serve with mdm configuration to turn on MDM features.`)

View File

@ -810,7 +810,7 @@ func TestMDMAppleConfig(t *testing.T) {
name: "nochange",
licenseTier: "free",
expectedMDM: fleet.MDM{
MacOSSetup: fleet.MacOSSetup{BootstrapPackage: optjson.String{Set: true}, MacOSSetupAssistant: optjson.String{Set: true}},
MacOSSetup: fleet.MacOSSetup{BootstrapPackage: optjson.String{Set: true}, MacOSSetupAssistant: optjson.String{Set: true}, EnableReleaseDeviceManually: optjson.SetBool(false)},
MacOSUpdates: fleet.MacOSUpdates{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
WindowsUpdates: fleet.WindowsUpdates{DeadlineDays: optjson.Int{Set: true}, GracePeriodDays: optjson.Int{Set: true}},
WindowsSettings: fleet.WindowsSettings{
@ -840,7 +840,7 @@ func TestMDMAppleConfig(t *testing.T) {
newMDM: fleet.MDM{AppleBMDefaultTeam: "foobar"},
expectedMDM: fleet.MDM{
AppleBMDefaultTeam: "foobar",
MacOSSetup: fleet.MacOSSetup{BootstrapPackage: optjson.String{Set: true}, MacOSSetupAssistant: optjson.String{Set: true}},
MacOSSetup: fleet.MacOSSetup{BootstrapPackage: optjson.String{Set: true}, MacOSSetupAssistant: optjson.String{Set: true}, EnableReleaseDeviceManually: optjson.SetBool(false)},
MacOSUpdates: fleet.MacOSUpdates{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
WindowsUpdates: fleet.WindowsUpdates{DeadlineDays: optjson.Int{Set: true}, GracePeriodDays: optjson.Int{Set: true}},
WindowsSettings: fleet.WindowsSettings{
@ -855,7 +855,7 @@ func TestMDMAppleConfig(t *testing.T) {
newMDM: fleet.MDM{AppleBMDefaultTeam: "foobar"},
expectedMDM: fleet.MDM{
AppleBMDefaultTeam: "foobar",
MacOSSetup: fleet.MacOSSetup{BootstrapPackage: optjson.String{Set: true}, MacOSSetupAssistant: optjson.String{Set: true}},
MacOSSetup: fleet.MacOSSetup{BootstrapPackage: optjson.String{Set: true}, MacOSSetupAssistant: optjson.String{Set: true}, EnableReleaseDeviceManually: optjson.SetBool(false)},
MacOSUpdates: fleet.MacOSUpdates{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
WindowsUpdates: fleet.WindowsUpdates{DeadlineDays: optjson.Int{Set: true}, GracePeriodDays: optjson.Int{Set: true}},
WindowsSettings: fleet.WindowsSettings{
@ -876,7 +876,7 @@ func TestMDMAppleConfig(t *testing.T) {
oldMDM: fleet.MDM{EndUserAuthentication: fleet.MDMEndUserAuthentication{SSOProviderSettings: fleet.SSOProviderSettings{EntityID: "foo"}}},
expectedMDM: fleet.MDM{
EndUserAuthentication: fleet.MDMEndUserAuthentication{SSOProviderSettings: fleet.SSOProviderSettings{EntityID: "foo"}},
MacOSSetup: fleet.MacOSSetup{BootstrapPackage: optjson.String{Set: true}, MacOSSetupAssistant: optjson.String{Set: true}},
MacOSSetup: fleet.MacOSSetup{BootstrapPackage: optjson.String{Set: true}, MacOSSetupAssistant: optjson.String{Set: true}, EnableReleaseDeviceManually: optjson.SetBool(false)},
MacOSUpdates: fleet.MacOSUpdates{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
WindowsUpdates: fleet.WindowsUpdates{DeadlineDays: optjson.Int{Set: true}, GracePeriodDays: optjson.Int{Set: true}},
WindowsSettings: fleet.WindowsSettings{
@ -900,7 +900,7 @@ func TestMDMAppleConfig(t *testing.T) {
MetadataURL: "http://isser.metadata.com",
IDPName: "onelogin",
}},
MacOSSetup: fleet.MacOSSetup{BootstrapPackage: optjson.String{Set: true}, MacOSSetupAssistant: optjson.String{Set: true}},
MacOSSetup: fleet.MacOSSetup{BootstrapPackage: optjson.String{Set: true}, MacOSSetupAssistant: optjson.String{Set: true}, EnableReleaseDeviceManually: optjson.SetBool(false)},
MacOSUpdates: fleet.MacOSUpdates{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
WindowsUpdates: fleet.WindowsUpdates{DeadlineDays: optjson.Int{Set: true}, GracePeriodDays: optjson.Int{Set: true}},
WindowsSettings: fleet.WindowsSettings{
@ -958,7 +958,7 @@ func TestMDMAppleConfig(t *testing.T) {
},
expectedMDM: fleet.MDM{
EnableDiskEncryption: optjson.Bool{Set: true, Valid: true, Value: false},
MacOSSetup: fleet.MacOSSetup{BootstrapPackage: optjson.String{Set: true}, MacOSSetupAssistant: optjson.String{Set: true}},
MacOSSetup: fleet.MacOSSetup{BootstrapPackage: optjson.String{Set: true}, MacOSSetupAssistant: optjson.String{Set: true}, EnableReleaseDeviceManually: optjson.SetBool(false)},
MacOSUpdates: fleet.MacOSUpdates{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
WindowsUpdates: fleet.WindowsUpdates{DeadlineDays: optjson.Int{Set: true}, GracePeriodDays: optjson.Int{Set: true}},
WindowsSettings: fleet.WindowsSettings{

View File

@ -5402,6 +5402,19 @@ func (s *integrationTestSuite) TestPremiumEndpointsWithoutLicense() {
s.Do("POST", "/api/v1/fleet/hosts/123/lock", nil, http.StatusPaymentRequired)
s.Do("POST", "/api/v1/fleet/hosts/123/unlock", nil, http.StatusPaymentRequired)
s.Do("POST", "/api/v1/fleet/hosts/123/wipe", nil, http.StatusPaymentRequired)
// try to update the enable_release_device_manually setting, requires premium
// (but /setup_experience catches the error of the MDM middleware check, so not
// StatusPaymentRequired)
res = s.Do("PATCH", "/api/v1/fleet/setup_experience", fleet.MDMAppleSetupPayload{EnableReleaseDeviceManually: ptr.Bool(true)}, http.StatusBadRequest)
errMsg = extractServerErrorText(res.Body)
require.Contains(t, errMsg, fleet.ErrMDMNotConfigured.Error())
res = s.Do("PATCH", "/api/v1/fleet/config", json.RawMessage(`{
"mdm": { "macos_setup": { "enable_release_device_manually": true } }
}`), http.StatusUnprocessableEntity)
errMsg = extractServerErrorText(res.Body)
require.Contains(t, errMsg, "missing or invalid license")
}
func (s *integrationTestSuite) TestScriptsEndpointsWithoutLicense() {

View File

@ -8,7 +8,6 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/fleetdm/fleet/v4/server/pubsub"
"io"
"net/http"
"net/http/httptest"
@ -20,6 +19,8 @@ import (
"testing"
"time"
"github.com/fleetdm/fleet/v4/server/pubsub"
"github.com/fleetdm/fleet/v4/pkg/optjson"
"github.com/fleetdm/fleet/v4/server/datastore/mysql"
"github.com/fleetdm/fleet/v4/server/datastore/redis/redistest"
@ -149,8 +150,9 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
// because the MacOSSetup was marshalled to JSON to be saved in the DB,
// it did get marshalled, and then when unmarshalled it was set (but
// null).
MacOSSetupAssistant: optjson.String{Set: true},
BootstrapPackage: optjson.String{Set: true},
MacOSSetupAssistant: optjson.String{Set: true},
BootstrapPackage: optjson.String{Set: true},
EnableReleaseDeviceManually: optjson.SetBool(false),
},
// because the WindowsSettings was marshalled to JSON to be saved in the DB,
// it did get marshalled, and then when unmarshalled it was set (but
@ -210,8 +212,9 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
GracePeriodDays: optjson.SetInt(1),
},
MacOSSetup: fleet.MacOSSetup{
MacOSSetupAssistant: optjson.String{Set: true},
BootstrapPackage: optjson.String{Set: true},
MacOSSetupAssistant: optjson.String{Set: true},
BootstrapPackage: optjson.String{Set: true},
EnableReleaseDeviceManually: optjson.SetBool(false),
},
WindowsSettings: fleet.WindowsSettings{
CustomSettings: optjson.Slice[fleet.MDMProfileSpec]{Set: true, Value: []fleet.MDMProfileSpec{}},
@ -231,8 +234,9 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
GracePeriodDays: optjson.SetInt(1),
},
MacOSSetup: fleet.MacOSSetup{
MacOSSetupAssistant: optjson.String{Set: true},
BootstrapPackage: optjson.String{Set: true},
MacOSSetupAssistant: optjson.String{Set: true},
BootstrapPackage: optjson.String{Set: true},
EnableReleaseDeviceManually: optjson.SetBool(false),
},
WindowsSettings: fleet.WindowsSettings{
CustomSettings: optjson.Slice[fleet.MDMProfileSpec]{Set: true, Value: []fleet.MDMProfileSpec{}},
@ -254,8 +258,9 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
GracePeriodDays: optjson.SetInt(1),
},
MacOSSetup: fleet.MacOSSetup{
MacOSSetupAssistant: optjson.String{Set: true},
BootstrapPackage: optjson.String{Set: true},
MacOSSetupAssistant: optjson.String{Set: true},
BootstrapPackage: optjson.String{Set: true},
EnableReleaseDeviceManually: optjson.SetBool(false),
},
WindowsSettings: fleet.WindowsSettings{
CustomSettings: optjson.Slice[fleet.MDMProfileSpec]{Set: true, Value: []fleet.MDMProfileSpec{}},
@ -344,6 +349,40 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
errMsg = extractServerErrorText(res.Body)
require.Contains(t, errMsg, "Couldn't update macos_settings because MDM features aren't turned on in Fleet.")
// dry-run with macos enable release device set to false, no error
teamSpecs = map[string]any{
"specs": []any{
map[string]any{
"name": teamName,
"mdm": map[string]any{
"macos_setup": map[string]any{
"enable_release_device_manually": false,
},
},
},
},
}
applyResp = applyTeamSpecsResponse{}
s.DoJSON("POST", "/api/latest/fleet/spec/teams", teamSpecs, http.StatusOK, &applyResp, "dry_run", "true")
assert.Equal(t, map[string]uint{teamName: team.ID}, applyResp.TeamIDsByName)
// dry-run with macos enable release device manually set to true
teamSpecs = map[string]any{
"specs": []any{
map[string]any{
"name": teamName,
"mdm": map[string]any{
"macos_setup": map[string]any{
"enable_release_device_manually": true,
},
},
},
},
}
res = s.Do("POST", "/api/latest/fleet/spec/teams", teamSpecs, http.StatusUnprocessableEntity, "dry_run", "true")
errMsg = extractServerErrorText(res.Body)
require.Contains(t, errMsg, "Couldn't update macos_setup because MDM features aren't turned on in Fleet.")
// dry-run with invalid host_expiry_settings.host_expiry_window
teamSpecs = map[string]any{
"specs": []map[string]any{
@ -1920,8 +1959,9 @@ func (s *integrationEnterpriseTestSuite) TestWindowsUpdatesTeamConfig() {
GracePeriodDays: optjson.SetInt(2),
},
MacOSSetup: fleet.MacOSSetup{
MacOSSetupAssistant: optjson.String{Set: true},
BootstrapPackage: optjson.String{Set: true},
MacOSSetupAssistant: optjson.String{Set: true},
BootstrapPackage: optjson.String{Set: true},
EnableReleaseDeviceManually: optjson.SetBool(false),
},
WindowsSettings: fleet.WindowsSettings{
CustomSettings: optjson.Slice[fleet.MDMProfileSpec]{Set: true, Value: []fleet.MDMProfileSpec{}},
@ -3531,6 +3571,17 @@ func (s *integrationEnterpriseTestSuite) TestMDMNotConfiguredEndpoints() {
var reqCSRResp requestMDMAppleCSRResponse
s.DoJSON("POST", "/api/latest/fleet/mdm/apple/request_csr", requestMDMAppleCSRRequest{EmailAddress: "a@b.c", Organization: "test"}, http.StatusOK, &reqCSRResp)
s.Do("POST", "/api/latest/fleet/mdm/apple/dep/key_pair", nil, http.StatusOK)
// setting enable release device manually requires MDM
res := s.Do("PATCH", "/api/v1/fleet/setup_experience", fleet.MDMAppleSetupPayload{EnableReleaseDeviceManually: ptr.Bool(true)}, http.StatusBadRequest)
errMsg := extractServerErrorText(res.Body)
require.Contains(t, errMsg, fleet.ErrMDMNotConfigured.Error())
res = s.Do("PATCH", "/api/v1/fleet/config", json.RawMessage(`{
"mdm": { "macos_setup": { "enable_release_device_manually": true } }
}`), http.StatusUnprocessableEntity)
errMsg = extractServerErrorText(res.Body)
require.Contains(t, errMsg, `Couldn't update macos_setup because MDM features aren't turned on in Fleet.`)
}
func (s *integrationEnterpriseTestSuite) TestGlobalPolicyCreateReadPatch() {

View File

@ -6323,7 +6323,7 @@ func (s *integrationMDMTestSuite) TestMDMMacOSSetup() {
tm, err := s.ds.NewTeam(context.Background(), &fleet.Team{Name: "team1"})
require.NoError(t, err)
cases := []struct {
endUserAuthCases := []struct {
raw string
expected bool
}{
@ -6355,6 +6355,73 @@ func (s *integrationMDMTestSuite) TestMDMMacOSSetup() {
},
}
writeTmpJSON := func(t *testing.T, v any) string {
tmpFile, err := os.CreateTemp(t.TempDir(), "*.json")
require.NoError(t, err)
err = json.NewEncoder(tmpFile).Encode(v)
require.NoError(t, err)
return tmpFile.Name()
}
mustReadFile := func(t *testing.T, path string) string {
b, err := os.ReadFile(path)
require.NoError(t, err)
return string(b)
}
asstOk := writeTmpJSON(t, map[string]any{"ok": true})
asstURL := writeTmpJSON(t, map[string]any{"url": "https://example.com"})
asstAwait := writeTmpJSON(t, map[string]any{"await_device_configured": true})
asstsByName := map[string]string{
asstOk: mustReadFile(t, asstOk),
asstURL: mustReadFile(t, asstURL),
asstAwait: mustReadFile(t, asstAwait),
}
enableReleaseDeviceCases := []struct {
enableRelease *bool
setupAssistant string
expectedRelease bool
expectedAssistant string
expectedStatus int
}{
{
enableRelease: nil,
setupAssistant: "",
expectedRelease: false,
expectedAssistant: "",
expectedStatus: http.StatusOK,
},
{
enableRelease: ptr.Bool(true),
setupAssistant: "",
expectedRelease: true,
expectedAssistant: "",
expectedStatus: http.StatusOK,
},
{
enableRelease: ptr.Bool(false),
setupAssistant: "",
expectedRelease: false,
expectedAssistant: "",
expectedStatus: http.StatusOK,
},
{
enableRelease: ptr.Bool(false),
setupAssistant: asstURL,
expectedRelease: false,
expectedAssistant: "",
expectedStatus: http.StatusUnprocessableEntity,
},
{
enableRelease: ptr.Bool(true),
setupAssistant: asstAwait,
expectedRelease: false,
expectedAssistant: "",
expectedStatus: http.StatusUnprocessableEntity,
},
}
t.Run("UpdateAppConfig", func(t *testing.T) {
acResp := appConfigResponse{}
path := "/api/latest/fleet/config"
@ -6364,11 +6431,13 @@ func (s *integrationMDMTestSuite) TestMDMMacOSSetup() {
}`, s))
}
// get the initial appconfig; enable end user authentication default is false
// get the initial appconfig; enable end user authentication and release
// device default is false
s.DoJSON("GET", path, nil, http.StatusOK, &acResp)
require.False(t, acResp.MDM.MacOSSetup.EnableEndUserAuthentication)
require.False(t, acResp.MDM.MacOSSetup.EnableReleaseDeviceManually.Value)
for i, c := range cases {
for i, c := range endUserAuthCases {
t.Run(strconv.Itoa(i), func(t *testing.T) {
acResp = appConfigResponse{}
s.DoJSON("PATCH", path, fmtJSON(c.raw), http.StatusOK, &acResp)
@ -6379,6 +6448,43 @@ func (s *integrationMDMTestSuite) TestMDMMacOSSetup() {
require.Equal(t, c.expected, acResp.MDM.MacOSSetup.EnableEndUserAuthentication)
})
}
for i, c := range enableReleaseDeviceCases {
t.Run(strconv.Itoa(i), func(t *testing.T) {
macSetup := map[string]any{}
if c.enableRelease != nil {
macSetup["enable_release_device_manually"] = *c.enableRelease
}
if c.setupAssistant != "" {
macSetup["macos_setup_assistant"] = c.setupAssistant
}
uploadSucceeded := true
if c.setupAssistant != "" {
s.Do("POST", "/api/v1/fleet/enrollment_profiles/automatic", createMDMAppleSetupAssistantRequest{
Name: c.setupAssistant,
EnrollmentProfile: json.RawMessage(asstsByName[c.setupAssistant]),
}, c.expectedStatus)
if c.expectedStatus >= 300 {
uploadSucceeded = false
}
}
if uploadSucceeded {
acResp = appConfigResponse{}
s.DoJSON("PATCH", path,
json.RawMessage(jsonMustMarshal(t, map[string]any{"mdm": map[string]any{"macos_setup": macSetup}})),
c.expectedStatus, &acResp)
require.Equal(t, c.expectedRelease, acResp.MDM.MacOSSetup.EnableReleaseDeviceManually.Value)
require.Equal(t, c.expectedAssistant, acResp.MDM.MacOSSetup.MacOSSetupAssistant.Value)
}
acResp = appConfigResponse{}
s.DoJSON("GET", path, nil, http.StatusOK, &acResp)
require.Equal(t, c.expectedRelease, acResp.MDM.MacOSSetup.EnableReleaseDeviceManually.Value)
require.Equal(t, c.expectedAssistant, acResp.MDM.MacOSSetup.MacOSSetupAssistant.Value)
})
}
})
t.Run("UpdateTeamConfig", func(t *testing.T) {
@ -6388,12 +6494,14 @@ func (s *integrationMDMTestSuite) TestMDMMacOSSetup() {
%s
}`
// get the initial team config; enable end user authentication default is false
// get the initial team config; enable end user authentication and release
// device default is false
teamResp := teamResponse{}
s.DoJSON("GET", path, nil, http.StatusOK, &teamResp)
require.False(t, teamResp.Team.Config.MDM.MacOSSetup.EnableEndUserAuthentication)
require.False(t, teamResp.Team.Config.MDM.MacOSSetup.EnableReleaseDeviceManually.Value)
for i, c := range cases {
for i, c := range endUserAuthCases {
t.Run(strconv.Itoa(i), func(t *testing.T) {
teamResp = teamResponse{}
s.DoJSON("PATCH", path, json.RawMessage(fmt.Sprintf(fmtJSON, tm.Name, c.raw)), http.StatusOK, &teamResp)
@ -6404,6 +6512,54 @@ func (s *integrationMDMTestSuite) TestMDMMacOSSetup() {
require.Equal(t, c.expected, teamResp.Team.Config.MDM.MacOSSetup.EnableEndUserAuthentication)
})
}
for i, c := range enableReleaseDeviceCases {
expectedPatchStatus := c.expectedStatus
if expectedPatchStatus == http.StatusOK {
expectedPatchStatus = http.StatusNoContent
}
t.Run(strconv.Itoa(i), func(t *testing.T) {
if c.setupAssistant != "" {
s.Do("POST", "/api/v1/fleet/enrollment_profiles/automatic", createMDMAppleSetupAssistantRequest{
TeamID: &tm.ID,
Name: c.setupAssistant,
EnrollmentProfile: json.RawMessage(asstsByName[c.setupAssistant]),
}, c.expectedStatus)
uploadSucceeded := c.expectedStatus < 300
if uploadSucceeded {
// use the apply team specs to set both the setup assistant and the
// enable release at once
macSetup := fleet.MacOSSetup{
MacOSSetupAssistant: optjson.SetString(c.setupAssistant),
}
if c.enableRelease != nil {
macSetup.EnableReleaseDeviceManually = optjson.SetBool(*c.enableRelease)
}
teamSpecs := applyTeamSpecsRequest{Specs: []*fleet.TeamSpec{{
Name: tm.Name,
MDM: fleet.TeamSpecMDM{MacOSSetup: macSetup},
}}}
s.Do("POST", "/api/latest/fleet/spec/teams", teamSpecs, http.StatusOK)
}
} else {
// no setup assistant, use the PATCH /setup_experience endpoint
payload := map[string]any{
"team_id": tm.ID,
}
if c.enableRelease != nil {
payload["enable_release_device_manually"] = *c.enableRelease
}
s.Do("PATCH", "/api/latest/fleet/setup_experience", json.RawMessage(jsonMustMarshal(t, payload)), expectedPatchStatus)
}
teamResp = teamResponse{}
s.DoJSON("GET", path, nil, http.StatusOK, &teamResp)
require.Equal(t, c.expectedRelease, teamResp.Team.Config.MDM.MacOSSetup.EnableReleaseDeviceManually.Value)
require.Equal(t, c.expectedAssistant, teamResp.Team.Config.MDM.MacOSSetup.MacOSSetupAssistant.Value)
})
}
})
t.Run("TestMDMAppleSetupEndpoint", func(t *testing.T) {
@ -6675,7 +6831,7 @@ func (s *integrationMDMTestSuite) TestMacosSetupAssistant() {
EnrollmentProfile: json.RawMessage(tmProf),
}, http.StatusUnprocessableEntity)
errMsg := extractServerErrorText(res.Body)
require.Contains(t, errMsg, `The automatic enrollment profile cant include url.`)
require.Contains(t, errMsg, `The automatic enrollment profile can't include url.`)
s.lastActivityMatches(fleet.ActivityTypeChangedMacosSetupAssistant{}.ActivityName(),
fmt.Sprintf(`{"name": "team2", "team_id": %d, "team_name": %q}`, tm.ID, tm.Name), latestChangedActID)

View File

@ -110,6 +110,10 @@ github.com/fleetdm/fleet/v4/server/fleet/MDM MacOSSetup fleet.MacOSSetup
github.com/fleetdm/fleet/v4/server/fleet/MacOSSetup BootstrapPackage optjson.String
github.com/fleetdm/fleet/v4/server/fleet/MacOSSetup EnableEndUserAuthentication bool
github.com/fleetdm/fleet/v4/server/fleet/MacOSSetup MacOSSetupAssistant optjson.String
github.com/fleetdm/fleet/v4/server/fleet/MacOSSetup EnableReleaseDeviceManually optjson.Bool
github.com/fleetdm/fleet/v4/pkg/optjson/Bool Set bool
github.com/fleetdm/fleet/v4/pkg/optjson/Bool Valid bool
github.com/fleetdm/fleet/v4/pkg/optjson/Bool Value bool
github.com/fleetdm/fleet/v4/server/fleet/MDM MacOSMigration fleet.MacOSMigration
github.com/fleetdm/fleet/v4/server/fleet/MacOSMigration Enable bool
github.com/fleetdm/fleet/v4/server/fleet/MacOSMigration Mode fleet.MacOSMigrationMode string
@ -118,9 +122,6 @@ github.com/fleetdm/fleet/v4/server/fleet/MDM EndUserAuthentication fleet.MDMEndU
github.com/fleetdm/fleet/v4/server/fleet/MDMEndUserAuthentication SSOProviderSettings fleet.SSOProviderSettings
github.com/fleetdm/fleet/v4/server/fleet/MDM WindowsEnabledAndConfigured bool
github.com/fleetdm/fleet/v4/server/fleet/MDM EnableDiskEncryption optjson.Bool
github.com/fleetdm/fleet/v4/pkg/optjson/Bool Set bool
github.com/fleetdm/fleet/v4/pkg/optjson/Bool Valid bool
github.com/fleetdm/fleet/v4/pkg/optjson/Bool Value bool
github.com/fleetdm/fleet/v4/server/fleet/MDM WindowsSettings fleet.WindowsSettings
github.com/fleetdm/fleet/v4/server/fleet/WindowsSettings CustomSettings optjson.Slice[github.com/fleetdm/fleet/v4/server/fleet.MDMProfileSpec]
github.com/fleetdm/fleet/v4/pkg/optjson/Slice[github.com/fleetdm/fleet/v4/server/fleet.MDMProfileSpec] Set bool

View File

@ -20,6 +20,10 @@ github.com/fleetdm/fleet/v4/server/fleet/TeamMDM MacOSSetup fleet.MacOSSetup
github.com/fleetdm/fleet/v4/server/fleet/MacOSSetup BootstrapPackage optjson.String
github.com/fleetdm/fleet/v4/server/fleet/MacOSSetup EnableEndUserAuthentication bool
github.com/fleetdm/fleet/v4/server/fleet/MacOSSetup MacOSSetupAssistant optjson.String
github.com/fleetdm/fleet/v4/server/fleet/MacOSSetup EnableReleaseDeviceManually optjson.Bool
github.com/fleetdm/fleet/v4/pkg/optjson/Bool Set bool
github.com/fleetdm/fleet/v4/pkg/optjson/Bool Valid bool
github.com/fleetdm/fleet/v4/pkg/optjson/Bool Value bool
github.com/fleetdm/fleet/v4/server/fleet/TeamMDM WindowsSettings fleet.WindowsSettings
github.com/fleetdm/fleet/v4/server/fleet/WindowsSettings CustomSettings optjson.Slice[github.com/fleetdm/fleet/v4/server/fleet.MDMProfileSpec]
github.com/fleetdm/fleet/v4/pkg/optjson/Slice[github.com/fleetdm/fleet/v4/server/fleet.MDMProfileSpec] Set bool