Add Windows admin account config (#49863)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #48720 Subtask of https://github.com/fleetdm/fleet/issues/43488 This PR only adds the Windows config, and doesn't mess with macOS configs. # Checklist for submitter - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters. ## Testing - [x] Added/updated automated tests - [x] QA'd all new/changed functionality manually ## New Fleet configuration settings - [x] Verified that the setting is exported via `fleetctl generate-gitops` - [x] Verified the setting is documented in a separate PR to [the GitOps documentation](https://github.com/fleetdm/fleet/blob/main/docs/Configuration/yaml-files.md#L485) - [x] Verified that the setting is cleared on the server if it is not supplied in a YAML file (or that it is documented as being optional) - [x] Verified that any relevant UI is disabled when GitOps mode is enabled <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added managed local account settings for Windows to app and team configuration, including GitOps support. * Exposed an explicit enabled/disabled toggle in configuration output and Fleet controls. * Added licensing and Windows MDM prerequisites for enabling the setting. * **Bug Fixes** * Managed local account enable/disable actions are now correctly persisted and declaratively applied. * Activity feed messages now display platform-specific (macOS vs Windows) wording. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -901,7 +901,8 @@ func testTeamsMDMConfig(t *testing.T, ds *Datastore) {
|
||||
EndUserLocalAccountType: optjson.SetString("admin"),
|
||||
},
|
||||
WindowsSettings: fleet.WindowsSettings{
|
||||
CustomSettings: optjson.SetSlice([]fleet.MDMProfileSpec{{Path: "foo"}, {Path: "bar"}}),
|
||||
CustomSettings: optjson.SetSlice([]fleet.MDMProfileSpec{{Path: "foo"}, {Path: "bar"}}),
|
||||
ManagedLocalAccountSettings: fleet.ManagedLocalAccountSettings{Enabled: optjson.SetBool(false)},
|
||||
},
|
||||
AndroidSettings: fleet.AndroidSettings{
|
||||
CustomSettings: optjson.SetSlice([]fleet.MDMProfileSpec{{Path: "baz"}, {Path: "qux"}}),
|
||||
|
||||
@@ -724,6 +724,7 @@ func (a ActivityTypeViewedManagedLocalAccount) HostIDs() []uint {
|
||||
type ActivityTypeEnabledManagedLocalAccount struct {
|
||||
TeamID *uint `json:"team_id" renameto:"fleet_id"`
|
||||
TeamName *string `json:"team_name" renameto:"fleet_name"`
|
||||
Platform string `json:"platform,omitempty"`
|
||||
}
|
||||
|
||||
func (a ActivityTypeEnabledManagedLocalAccount) ActivityName() string {
|
||||
@@ -733,6 +734,7 @@ func (a ActivityTypeEnabledManagedLocalAccount) ActivityName() string {
|
||||
type ActivityTypeDisabledManagedLocalAccount struct {
|
||||
TeamID *uint `json:"team_id" renameto:"fleet_id"`
|
||||
TeamName *string `json:"team_name" renameto:"fleet_name"`
|
||||
Platform string `json:"platform,omitempty"`
|
||||
}
|
||||
|
||||
func (a ActivityTypeDisabledManagedLocalAccount) ActivityName() string {
|
||||
|
||||
@@ -2116,10 +2116,32 @@ func (v *Version) AuthzType() string {
|
||||
return "version"
|
||||
}
|
||||
|
||||
// ManagedLocalAccountSettings configures the hidden managed local admin account for one platform.
|
||||
// Future fields (username, password policy) land here.
|
||||
type ManagedLocalAccountSettings struct {
|
||||
Enabled optjson.Bool `json:"enabled"`
|
||||
}
|
||||
|
||||
// MarshalJSON defaults the enabled flag to false when it was never set, so every serialization
|
||||
// path (API responses, stored config JSON, spec exports, GitOps payloads) emits a boolean
|
||||
// rather than null. Request payloads are unaffected: clients send raw JSON, not this struct.
|
||||
func (m ManagedLocalAccountSettings) MarshalJSON() ([]byte, error) {
|
||||
if !m.Enabled.Valid {
|
||||
m.Enabled = optjson.SetBool(false)
|
||||
}
|
||||
// the alias type has no methods, so marshaling it avoids infinite recursion into this MarshalJSON
|
||||
type alias ManagedLocalAccountSettings
|
||||
return json.Marshal(alias(m))
|
||||
}
|
||||
|
||||
type WindowsSettings struct {
|
||||
// NOTE: These are only present here for informational purposes.
|
||||
// (The source of truth for profiles is in MySQL.)
|
||||
CustomSettings optjson.Slice[MDMProfileSpec] `json:"custom_settings" renameto:"configuration_profiles"`
|
||||
|
||||
// ManagedLocalAccountSettings configures the hidden managed local admin account created by
|
||||
// fleetd on Windows hosts during Autopilot/OOBE enrollment.
|
||||
ManagedLocalAccountSettings ManagedLocalAccountSettings `json:"managed_local_account_settings"`
|
||||
}
|
||||
|
||||
func (ws WindowsSettings) GetMDMProfileSpecs() []MDMProfileSpec {
|
||||
|
||||
@@ -912,3 +912,31 @@ func TestMacOSSetupValidate(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestManagedLocalAccountSettingsMarshalDefaults verifies every marshal/save path defaults the
|
||||
// Windows managed local account toggle to enabled: false and preserves a set value.
|
||||
func TestManagedLocalAccountSettingsMarshalDefaults(t *testing.T) {
|
||||
windowsSettings := func(b []byte) any {
|
||||
var out map[string]any
|
||||
require.NoError(t, json.Unmarshal(b, &out))
|
||||
return out["mdm"].(map[string]any)["windows_settings"].(map[string]any)["managed_local_account_settings"]
|
||||
}
|
||||
marshaled := func(v any) any {
|
||||
b, err := json.Marshal(v)
|
||||
require.NoError(t, err)
|
||||
return windowsSettings(b)
|
||||
}
|
||||
|
||||
var ac AppConfig
|
||||
require.Equal(t, map[string]any{"enabled": false}, marshaled(ac))
|
||||
ac.MDM.WindowsSettings.ManagedLocalAccountSettings.Enabled = optjson.SetBool(true)
|
||||
require.Equal(t, map[string]any{"enabled": true}, marshaled(ac))
|
||||
|
||||
team := Team{ID: 1, Name: "t1"}
|
||||
require.Equal(t, map[string]any{"enabled": false}, marshaled(team))
|
||||
|
||||
// the DB save path (TeamConfig.Value) applies the same default
|
||||
v, err := team.Config.Value()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, map[string]any{"enabled": false}, windowsSettings(v.([]byte)))
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ const (
|
||||
OSUpdatesAlreadyConfiguredErrorMessage = "Couldn't add profile. OS updates are already configured. Remove the OS updates settings first."
|
||||
CouldNotUpdateAppleOSSettingsWithCustomProfileErrorMessage = "Couldn't update OS updates settings. A custom OS updates declaration profile already exists. Remove the custom profile first."
|
||||
CouldNotUpdateWindowsOSSettingsWithCustomProfileErrorMessage = "Couldn't update OS updates settings. A custom OS updates profile already exists. Remove the custom profile first."
|
||||
WindowsMDMNotTurnedOnMessage = `Windows MDM isn’t turned on. This can be enabled by setting "controls.windows_enabled_and_configured: true" in the default configuration. Visit https://fleetdm.com/guides/windows-mdm-setup and https://fleetdm.com/docs/configuration/yaml-files#controls to learn more about enabling MDM.`
|
||||
)
|
||||
|
||||
// FleetVarName represents the name of a Fleet variable (without the FLEET_VAR_ prefix).
|
||||
|
||||
@@ -103,6 +103,15 @@ type TeamPayloadMDM struct {
|
||||
|
||||
MacOSSetup *MacOSSetup `json:"macos_setup"`
|
||||
HostNameTemplate optjson.String `json:"name_template"`
|
||||
|
||||
// WindowsSettings exposes only the managed local account surface on the team PATCH endpoint;
|
||||
// configuration profiles are managed through their own endpoints.
|
||||
WindowsSettings *TeamPayloadWindowsSettings `json:"windows_settings"`
|
||||
}
|
||||
|
||||
// TeamPayloadWindowsSettings is the subset of windows_settings fields settable via the team PATCH endpoint.
|
||||
type TeamPayloadWindowsSettings struct {
|
||||
ManagedLocalAccountSettings ManagedLocalAccountSettings `json:"managed_local_account_settings"`
|
||||
}
|
||||
|
||||
// Team is the data representation for the "Team" concept (group of hosts and
|
||||
|
||||
@@ -877,6 +877,17 @@ func (svc *Service) ModifyAppConfig(ctx context.Context, p []byte, applyOpts fle
|
||||
appConfig.MDM.MacOSSetup.EndUserLocalAccountType = oldAppConfig.MDM.MacOSSetup.EndUserLocalAccountType
|
||||
}
|
||||
|
||||
// windows_settings.managed_local_account_settings.enabled: like EnableDiskEncryption above, an explicit JSON null
|
||||
// means "not provided": keep the old value rather than persisting an invalid optjson state.
|
||||
if !oldAppConfig.MDM.WindowsSettings.ManagedLocalAccountSettings.Enabled.Valid {
|
||||
oldAppConfig.MDM.WindowsSettings.ManagedLocalAccountSettings.Enabled = optjson.SetBool(false)
|
||||
}
|
||||
if newAppConfig.MDM.WindowsSettings.ManagedLocalAccountSettings.Enabled.Valid {
|
||||
appConfig.MDM.WindowsSettings.ManagedLocalAccountSettings.Enabled = newAppConfig.MDM.WindowsSettings.ManagedLocalAccountSettings.Enabled
|
||||
} else {
|
||||
appConfig.MDM.WindowsSettings.ManagedLocalAccountSettings.Enabled = oldAppConfig.MDM.WindowsSettings.ManagedLocalAccountSettings.Enabled
|
||||
}
|
||||
|
||||
if appConfig.MDM.MacOSSetup.ManualAgentInstall.Valid && appConfig.MDM.MacOSSetup.ManualAgentInstall.Value {
|
||||
if !lic.IsPremium() {
|
||||
invalid.Append("setup_experience.macos_manual_agent_install", ErrMissingLicense.Error())
|
||||
@@ -1540,15 +1551,27 @@ func (svc *Service) ModifyAppConfig(ctx context.Context, p []byte, applyOpts fle
|
||||
if oldAppConfig.MDM.MacOSSetup.EnableManagedLocalAccount.Value != appConfig.MDM.MacOSSetup.EnableManagedLocalAccount.Value {
|
||||
var act fleet.ActivityDetails
|
||||
if appConfig.MDM.MacOSSetup.EnableManagedLocalAccount.Value {
|
||||
act = fleet.ActivityTypeEnabledManagedLocalAccount{}
|
||||
act = fleet.ActivityTypeEnabledManagedLocalAccount{Platform: "darwin"}
|
||||
} else {
|
||||
act = fleet.ActivityTypeDisabledManagedLocalAccount{}
|
||||
act = fleet.ActivityTypeDisabledManagedLocalAccount{Platform: "darwin"}
|
||||
}
|
||||
if err := svc.NewActivity(ctx, authz.UserFromContext(ctx), act); err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "create activity for macos enable managed local account change")
|
||||
}
|
||||
}
|
||||
|
||||
if oldAppConfig.MDM.WindowsSettings.ManagedLocalAccountSettings.Enabled.Value != appConfig.MDM.WindowsSettings.ManagedLocalAccountSettings.Enabled.Value {
|
||||
var act fleet.ActivityDetails
|
||||
if appConfig.MDM.WindowsSettings.ManagedLocalAccountSettings.Enabled.Value {
|
||||
act = fleet.ActivityTypeEnabledManagedLocalAccount{Platform: "windows"}
|
||||
} else {
|
||||
act = fleet.ActivityTypeDisabledManagedLocalAccount{Platform: "windows"}
|
||||
}
|
||||
if err := svc.NewActivity(ctx, authz.UserFromContext(ctx), act); err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "create activity for windows enable managed local account change")
|
||||
}
|
||||
}
|
||||
|
||||
mdmSSOSettingsChanged := oldAppConfig.MDM.EndUserAuthentication.SSOProviderSettings !=
|
||||
appConfig.MDM.EndUserAuthentication.SSOProviderSettings
|
||||
serverURLChanged := oldAppConfig.ServerSettings.ServerURL != appConfig.ServerSettings.ServerURL
|
||||
@@ -1881,6 +1904,10 @@ func (svc *Service) validateMDM(
|
||||
if mdm.MacOSSetup.ManualAgentInstall.Valid && oldMdm.MacOSSetup.ManualAgentInstall.Value != mdm.MacOSSetup.ManualAgentInstall.Value && !lic.IsPremium() {
|
||||
invalid.Append("setup_experience.macos_manual_agent_install", ErrMissingLicense.Error())
|
||||
}
|
||||
if mdm.WindowsSettings.ManagedLocalAccountSettings.Enabled.Value &&
|
||||
mdm.WindowsSettings.ManagedLocalAccountSettings.Enabled.Value != oldMdm.WindowsSettings.ManagedLocalAccountSettings.Enabled.Value && !lic.IsPremium() {
|
||||
invalid.Append("windows_settings.managed_local_account_settings.enabled", ErrMissingLicense.Error())
|
||||
}
|
||||
if mdm.WindowsMigrationEnabled && !lic.IsPremium() {
|
||||
invalid.Append("windows_migration_enabled", ErrMissingLicense.Error())
|
||||
}
|
||||
@@ -1949,7 +1976,13 @@ func (svc *Service) validateMDM(
|
||||
len(mdm.WindowsSettings.CustomSettings.Value) > 0 &&
|
||||
!fleet.MDMProfileSpecsMatch(mdm.WindowsSettings.CustomSettings.Value, oldMdm.WindowsSettings.CustomSettings.Value) {
|
||||
invalid.Append("windows_settings.configuration_profiles",
|
||||
`Couldn’t edit windows_settings.configuration_profiles. Windows MDM isn’t turned on. This can be enabled by setting "controls.windows_enabled_and_configured: true" in the default configuration. Visit https://fleetdm.com/guides/windows-mdm-setup and https://fleetdm.com/docs/configuration/yaml-files#controls to learn more about enabling MDM.`)
|
||||
"Couldn’t edit windows_settings.configuration_profiles. "+fleet.WindowsMDMNotTurnedOnMessage)
|
||||
}
|
||||
|
||||
if mdm.WindowsSettings.ManagedLocalAccountSettings.Enabled.Value &&
|
||||
!oldMdm.WindowsSettings.ManagedLocalAccountSettings.Enabled.Value {
|
||||
invalid.Append("windows_settings.managed_local_account_settings.enabled",
|
||||
"Couldn’t enable windows_settings.managed_local_account_settings. "+fleet.WindowsMDMNotTurnedOnMessage)
|
||||
}
|
||||
}
|
||||
fleet.ValidateMDMProfileSpecs(invalid, "windows", mdm.WindowsSettings.CustomSettings.Value)
|
||||
|
||||
@@ -1225,7 +1225,8 @@ func TestMDMConfig(t *testing.T) {
|
||||
VolumePurchasingProgram: optjson.Slice[fleet.MDMAppleVolumePurchasingProgramInfo]{Set: true, Value: []fleet.MDMAppleVolumePurchasingProgramInfo{}},
|
||||
WindowsUpdates: fleet.WindowsUpdates{DeadlineDays: optjson.Int{Set: true}, GracePeriodDays: optjson.Int{Set: true}},
|
||||
WindowsSettings: fleet.WindowsSettings{
|
||||
CustomSettings: optjson.Slice[fleet.MDMProfileSpec]{Set: true, Value: []fleet.MDMProfileSpec{}},
|
||||
CustomSettings: optjson.Slice[fleet.MDMProfileSpec]{Set: true, Value: []fleet.MDMProfileSpec{}},
|
||||
ManagedLocalAccountSettings: fleet.ManagedLocalAccountSettings{Enabled: optjson.SetBool(false)},
|
||||
},
|
||||
AndroidSettings: fleet.AndroidSettings{
|
||||
CustomSettings: optjson.Slice[fleet.MDMProfileSpec]{Set: true, Value: []fleet.MDMProfileSpec{}},
|
||||
@@ -3042,57 +3043,121 @@ func TestModifyAppConfigClearBootstrapPackageAlreadyDeleted(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// TestModifyAppConfigManagedLocalAccount covers the no-team (team 0) path
|
||||
// through PATCH /config, which ModifyTeam doesn't handle.
|
||||
// TestModifyAppConfigManagedLocalAccount covers the no-team (team 0) path through PATCH /config for both managed
|
||||
// local account platform toggles, which ModifyTeam doesn't handle.
|
||||
func TestModifyAppConfigManagedLocalAccount(t *testing.T) {
|
||||
admin := &fleet.User{GlobalRole: new(fleet.RoleAdmin)}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
mdmConfigured bool
|
||||
startEnabled bool
|
||||
patch string
|
||||
wantErr string
|
||||
wantActivity string
|
||||
name string
|
||||
freeTier bool
|
||||
appleMDMOff bool
|
||||
windowsMDMOff bool
|
||||
startMacOS bool
|
||||
startWindows bool
|
||||
patch string
|
||||
wantErr string
|
||||
wantActivities []string
|
||||
wantWindowsEnabled bool
|
||||
}{
|
||||
{
|
||||
name: "MDM not configured rejects the change",
|
||||
patch: `{"mdm": {"macos_setup": {"enable_managed_local_account": true}}}`,
|
||||
wantErr: "setup_experience.enable_managed_local_account",
|
||||
name: "macOS: enabling managed local account requires Apple MDM",
|
||||
appleMDMOff: true,
|
||||
patch: `{"mdm": {"macos_setup": {"enable_managed_local_account": true}}}`,
|
||||
wantErr: "setup_experience.enable_managed_local_account",
|
||||
},
|
||||
{
|
||||
name: "enabling emits the enabled activity",
|
||||
mdmConfigured: true,
|
||||
patch: `{"mdm": {"macos_setup": {"enable_managed_local_account": true}}}`,
|
||||
wantActivity: fleet.ActivityTypeEnabledManagedLocalAccount{}.ActivityName(),
|
||||
name: "macOS: enabling managed local account emits the enabled activity",
|
||||
patch: `{"mdm": {"macos_setup": {"enable_managed_local_account": true}}}`,
|
||||
wantActivities: []string{"enabled_managed_local_account:darwin"},
|
||||
},
|
||||
{
|
||||
name: "disabling emits the disabled activity",
|
||||
mdmConfigured: true,
|
||||
startEnabled: true,
|
||||
patch: `{"mdm": {"macos_setup": {"enable_managed_local_account": false}}}`,
|
||||
wantActivity: fleet.ActivityTypeDisabledManagedLocalAccount{}.ActivityName(),
|
||||
name: "macOS: disabling managed local account emits the disabled activity",
|
||||
startMacOS: true,
|
||||
patch: `{"mdm": {"macos_setup": {"enable_managed_local_account": false}}}`,
|
||||
wantActivities: []string{"disabled_managed_local_account:darwin"},
|
||||
},
|
||||
{
|
||||
name: "no-op change emits no activity",
|
||||
mdmConfigured: true,
|
||||
patch: `{"mdm": {"macos_setup": {"enable_managed_local_account": false}}}`,
|
||||
name: "macOS: managed local account no-op change emits no activity",
|
||||
patch: `{"mdm": {"macos_setup": {"enable_managed_local_account": false}}}`,
|
||||
},
|
||||
{
|
||||
name: "windows: enabling managed local account persists and fires activity",
|
||||
patch: `{"mdm": {"windows_settings": {"managed_local_account_settings": {"enabled": true}}}}`,
|
||||
wantActivities: []string{"enabled_managed_local_account:windows"},
|
||||
wantWindowsEnabled: true,
|
||||
},
|
||||
{
|
||||
name: "windows: disabling managed local account persists and fires activity",
|
||||
startWindows: true,
|
||||
patch: `{"mdm": {"windows_settings": {"managed_local_account_settings": {"enabled": false}}}}`,
|
||||
wantActivities: []string{"disabled_managed_local_account:windows"},
|
||||
},
|
||||
{
|
||||
name: "windows: null managed local account enabled means not provided",
|
||||
startWindows: true,
|
||||
patch: `{"mdm": {"windows_settings": {"managed_local_account_settings": {"enabled": null}}}}`,
|
||||
wantWindowsEnabled: true,
|
||||
},
|
||||
{
|
||||
name: "windows: managed local account no-op change fires no activity",
|
||||
patch: `{"mdm": {"windows_settings": {"managed_local_account_settings": {"enabled": false}}}}`,
|
||||
},
|
||||
{
|
||||
name: "enabling managed local account on both platforms in one payload fires one activity per platform",
|
||||
patch: `{"mdm": {"macos_setup": {"enable_managed_local_account": true}, "windows_settings": {"managed_local_account_settings": {"enabled": true}}}}`,
|
||||
wantActivities: []string{"enabled_managed_local_account:darwin", "enabled_managed_local_account:windows"},
|
||||
wantWindowsEnabled: true,
|
||||
},
|
||||
{
|
||||
name: "windows: enabling managed local account requires premium",
|
||||
freeTier: true,
|
||||
patch: `{"mdm": {"windows_settings": {"managed_local_account_settings": {"enabled": true}}}}`,
|
||||
wantErr: "missing or invalid license",
|
||||
},
|
||||
{
|
||||
name: "windows: disabling managed local account is allowed without premium (license downgrade)",
|
||||
freeTier: true,
|
||||
startWindows: true,
|
||||
patch: `{"mdm": {"windows_settings": {"managed_local_account_settings": {"enabled": false}}}}`,
|
||||
wantActivities: []string{"disabled_managed_local_account:windows"},
|
||||
},
|
||||
{
|
||||
name: "windows: enabling managed local account requires Windows MDM",
|
||||
windowsMDMOff: true,
|
||||
patch: `{"mdm": {"windows_settings": {"managed_local_account_settings": {"enabled": true}}}}`,
|
||||
wantErr: "windows_settings.managed_local_account_settings",
|
||||
},
|
||||
{
|
||||
name: "windows: managed local account with end_user_local_account_type rejected",
|
||||
patch: `{"mdm": {"windows_settings": {"managed_local_account_settings": {"enabled": true}, "end_user_local_account_type": "admin"}}}`,
|
||||
wantErr: "end_user_local_account_type",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range testCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ds := new(mock.Store)
|
||||
opts := &TestServerOpts{License: &fleet.LicenseInfo{Tier: fleet.TierPremium}}
|
||||
svc, ctx := newTestService(t, ds, nil, nil, opts)
|
||||
tier := fleet.TierPremium
|
||||
if tt.freeTier {
|
||||
tier = fleet.TierFree
|
||||
}
|
||||
opts := &TestServerOpts{License: &fleet.LicenseInfo{Tier: tier}}
|
||||
// keeping Windows MDM enabled across the PATCH requires a configured WSTEP cert/key pair
|
||||
cfg := config.TestConfig()
|
||||
cfg.MDM.WindowsWSTEPIdentityCert = "testdata/server.pem"
|
||||
cfg.MDM.WindowsWSTEPIdentityKey = "testdata/server.key"
|
||||
svc, ctx := newTestServiceWithConfig(t, ds, cfg, nil, nil, opts)
|
||||
ctx = viewer.NewContext(ctx, viewer.Viewer{User: admin})
|
||||
|
||||
dsAppConfig := &fleet.AppConfig{
|
||||
OrgInfo: fleet.OrgInfo{OrgName: "Test"},
|
||||
ServerSettings: fleet.ServerSettings{ServerURL: "https://example.org"},
|
||||
}
|
||||
dsAppConfig.MDM.EnabledAndConfigured = tt.mdmConfigured
|
||||
dsAppConfig.MDM.MacOSSetup.EnableManagedLocalAccount = optjson.SetBool(tt.startEnabled)
|
||||
dsAppConfig.MDM.EnabledAndConfigured = !tt.appleMDMOff
|
||||
dsAppConfig.MDM.WindowsEnabledAndConfigured = !tt.windowsMDMOff
|
||||
dsAppConfig.MDM.MacOSSetup.EnableManagedLocalAccount = optjson.SetBool(tt.startMacOS)
|
||||
dsAppConfig.MDM.WindowsSettings.ManagedLocalAccountSettings.Enabled = optjson.SetBool(tt.startWindows)
|
||||
|
||||
ds.AppConfigFunc = func(ctx context.Context) (*fleet.AppConfig, error) { return dsAppConfig, nil }
|
||||
ds.SaveAppConfigFunc = func(ctx context.Context, conf *fleet.AppConfig) error {
|
||||
@@ -3105,9 +3170,11 @@ func TestModifyAppConfigManagedLocalAccount(t *testing.T) {
|
||||
|
||||
var gotActivities []string
|
||||
opts.ActivityMock.NewActivityFunc = func(_ context.Context, _ *activity_api.User, act activity_api.ActivityDetails) error {
|
||||
switch act.(type) {
|
||||
case fleet.ActivityTypeEnabledManagedLocalAccount, fleet.ActivityTypeDisabledManagedLocalAccount:
|
||||
gotActivities = append(gotActivities, act.ActivityName())
|
||||
switch a := act.(type) {
|
||||
case fleet.ActivityTypeEnabledManagedLocalAccount:
|
||||
gotActivities = append(gotActivities, a.ActivityName()+":"+a.Platform)
|
||||
case fleet.ActivityTypeDisabledManagedLocalAccount:
|
||||
gotActivities = append(gotActivities, a.ActivityName()+":"+a.Platform)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -3118,15 +3185,12 @@ func TestModifyAppConfigManagedLocalAccount(t *testing.T) {
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), tt.wantErr)
|
||||
require.Empty(t, gotActivities)
|
||||
require.False(t, ds.SaveAppConfigFuncInvoked, "config should not have been saved")
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
var wantActivities []string
|
||||
if tt.wantActivity != "" {
|
||||
wantActivities = []string{tt.wantActivity}
|
||||
}
|
||||
require.Equal(t, wantActivities, gotActivities)
|
||||
require.Equal(t, tt.wantActivities, gotActivities)
|
||||
require.Equal(t, tt.wantWindowsEnabled, dsAppConfig.MDM.WindowsSettings.ManagedLocalAccountSettings.Enabled.Value)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,6 +218,16 @@ func (s *integrationEnterpriseTestSuite) clearOktaConditionalAccess() {
|
||||
s.DoRaw("PATCH", "/api/latest/fleet/config", b, http.StatusOK)
|
||||
}
|
||||
|
||||
// defaultExpectedWindowsSettings returns the WindowsSettings shape produced by a team config
|
||||
// save/load round trip with nothing configured: an empty custom settings slice and the managed
|
||||
// local account toggle force-defaulted to disabled.
|
||||
func defaultExpectedWindowsSettings() fleet.WindowsSettings {
|
||||
return fleet.WindowsSettings{
|
||||
CustomSettings: optjson.Slice[fleet.MDMProfileSpec]{Set: true, Value: []fleet.MDMProfileSpec{}},
|
||||
ManagedLocalAccountSettings: fleet.ManagedLocalAccountSettings{Enabled: optjson.SetBool(false)},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
|
||||
t := s.T()
|
||||
|
||||
@@ -340,9 +350,7 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
|
||||
// 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
|
||||
// empty).
|
||||
WindowsSettings: fleet.WindowsSettings{
|
||||
CustomSettings: optjson.Slice[fleet.MDMProfileSpec]{Set: true, Value: []fleet.MDMProfileSpec{}},
|
||||
},
|
||||
WindowsSettings: defaultExpectedWindowsSettings(),
|
||||
AndroidSettings: fleet.AndroidSettings{
|
||||
CustomSettings: optjson.Slice[fleet.MDMProfileSpec]{Set: true, Value: []fleet.MDMProfileSpec{}},
|
||||
Certificates: optjson.Slice[fleet.CertificateTemplateSpec]{Set: true, Value: []fleet.CertificateTemplateSpec{}},
|
||||
@@ -469,9 +477,7 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
|
||||
EnableManagedLocalAccount: optjson.SetBool(false),
|
||||
EndUserLocalAccountType: optjson.SetString("admin"),
|
||||
},
|
||||
WindowsSettings: fleet.WindowsSettings{
|
||||
CustomSettings: optjson.Slice[fleet.MDMProfileSpec]{Set: true, Value: []fleet.MDMProfileSpec{}},
|
||||
},
|
||||
WindowsSettings: defaultExpectedWindowsSettings(),
|
||||
AndroidSettings: fleet.AndroidSettings{
|
||||
CustomSettings: optjson.Slice[fleet.MDMProfileSpec]{Set: true, Value: []fleet.MDMProfileSpec{}},
|
||||
Certificates: optjson.Slice[fleet.CertificateTemplateSpec]{Set: true, Value: []fleet.CertificateTemplateSpec{}},
|
||||
@@ -512,9 +518,7 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
|
||||
EnableManagedLocalAccount: optjson.SetBool(false),
|
||||
EndUserLocalAccountType: optjson.SetString("admin"),
|
||||
},
|
||||
WindowsSettings: fleet.WindowsSettings{
|
||||
CustomSettings: optjson.Slice[fleet.MDMProfileSpec]{Set: true, Value: []fleet.MDMProfileSpec{}},
|
||||
},
|
||||
WindowsSettings: defaultExpectedWindowsSettings(),
|
||||
AndroidSettings: fleet.AndroidSettings{
|
||||
CustomSettings: optjson.Slice[fleet.MDMProfileSpec]{Set: true, Value: []fleet.MDMProfileSpec{}},
|
||||
Certificates: optjson.Slice[fleet.CertificateTemplateSpec]{Set: true, Value: []fleet.CertificateTemplateSpec{}},
|
||||
@@ -557,9 +561,7 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
|
||||
EnableManagedLocalAccount: optjson.SetBool(false),
|
||||
EndUserLocalAccountType: optjson.SetString("admin"),
|
||||
},
|
||||
WindowsSettings: fleet.WindowsSettings{
|
||||
CustomSettings: optjson.Slice[fleet.MDMProfileSpec]{Set: true, Value: []fleet.MDMProfileSpec{}},
|
||||
},
|
||||
WindowsSettings: defaultExpectedWindowsSettings(),
|
||||
AndroidSettings: fleet.AndroidSettings{
|
||||
CustomSettings: optjson.Slice[fleet.MDMProfileSpec]{Set: true, Value: []fleet.MDMProfileSpec{}},
|
||||
Certificates: optjson.Slice[fleet.CertificateTemplateSpec]{Set: true, Value: []fleet.CertificateTemplateSpec{}},
|
||||
@@ -3460,9 +3462,7 @@ func (s *integrationEnterpriseTestSuite) TestWindowsUpdatesTeamConfig() {
|
||||
EnableManagedLocalAccount: optjson.SetBool(false),
|
||||
EndUserLocalAccountType: optjson.SetString("admin"),
|
||||
},
|
||||
WindowsSettings: fleet.WindowsSettings{
|
||||
CustomSettings: optjson.Slice[fleet.MDMProfileSpec]{Set: true, Value: []fleet.MDMProfileSpec{}},
|
||||
},
|
||||
WindowsSettings: defaultExpectedWindowsSettings(),
|
||||
AndroidSettings: fleet.AndroidSettings{
|
||||
CustomSettings: optjson.Slice[fleet.MDMProfileSpec]{Set: true, Value: []fleet.MDMProfileSpec{}},
|
||||
Certificates: optjson.Slice[fleet.CertificateTemplateSpec]{Set: true, Value: []fleet.CertificateTemplateSpec{}},
|
||||
|
||||
@@ -24657,7 +24657,7 @@ func (s *integrationMDMTestSuite) TestManagedLocalAccount() {
|
||||
s.Do("PATCH", "/api/latest/fleet/setup_experience",
|
||||
fleet.MDMAppleSetupPayload{TeamID: &team.ID, EnableManagedLocalAccount: new(true)}, http.StatusNoContent)
|
||||
s.lastActivityOfTypeMatches(fleet.ActivityTypeEnabledManagedLocalAccount{}.ActivityName(),
|
||||
fmt.Sprintf(`{"team_id": %d, "team_name": %q, "fleet_id": %d, "fleet_name": %q}`, team.ID, team.Name, team.ID, team.Name), 0)
|
||||
fmt.Sprintf(`{"team_id": %d, "team_name": %q, "fleet_id": %d, "fleet_name": %q, "platform": "darwin"}`, team.ID, team.Name, team.ID, team.Name), 0)
|
||||
|
||||
// Assign ABM org to the team
|
||||
var acResp appConfigResponse
|
||||
@@ -24854,7 +24854,7 @@ func (s *integrationMDMTestSuite) TestManagedLocalAccount() {
|
||||
s.Do("PATCH", "/api/latest/fleet/setup_experience",
|
||||
fleet.MDMAppleSetupPayload{TeamID: &team.ID, EnableManagedLocalAccount: new(false)}, http.StatusNoContent)
|
||||
s.lastActivityOfTypeMatches(fleet.ActivityTypeDisabledManagedLocalAccount{}.ActivityName(),
|
||||
fmt.Sprintf(`{"team_id": %d, "team_name": %q, "fleet_id": %d, "fleet_name": %q}`, team.ID, team.Name, team.ID, team.Name), 0)
|
||||
fmt.Sprintf(`{"team_id": %d, "team_name": %q, "fleet_id": %d, "fleet_name": %q, "platform": "darwin"}`, team.ID, team.Name, team.ID, team.Name), 0)
|
||||
|
||||
// Existing host's password is still readable
|
||||
pwdResp = getHostManagedAccountPasswordResponse{}
|
||||
@@ -24939,7 +24939,7 @@ func (s *integrationMDMTestSuite) TestManagedLocalAccount() {
|
||||
require.True(t, acResp.MDM.MacOSSetup.EnableManagedLocalAccount.Valid)
|
||||
require.True(t, acResp.MDM.MacOSSetup.EnableManagedLocalAccount.Value)
|
||||
lastActivityID := s.lastActivityOfTypeMatches(fleet.ActivityTypeEnabledManagedLocalAccount{}.ActivityName(),
|
||||
`{"team_id": null, "team_name": null, "fleet_id": null, "fleet_name": null}`, 0)
|
||||
`{"team_id": null, "team_name": null, "fleet_id": null, "fleet_name": null, "platform": "darwin"}`, 0)
|
||||
|
||||
// Patching same value again should not create a new activity
|
||||
s.Do("PATCH", "/api/latest/fleet/setup_experience",
|
||||
@@ -24956,7 +24956,7 @@ func (s *integrationMDMTestSuite) TestManagedLocalAccount() {
|
||||
require.True(t, acResp.MDM.MacOSSetup.EnableManagedLocalAccount.Valid)
|
||||
require.False(t, acResp.MDM.MacOSSetup.EnableManagedLocalAccount.Value)
|
||||
require.Greater(t, s.lastActivityOfTypeMatches(fleet.ActivityTypeDisabledManagedLocalAccount{}.ActivityName(),
|
||||
`{"team_id": null, "team_name": null, "fleet_id": null, "fleet_name": null}`, 0), lastActivityID)
|
||||
`{"team_id": null, "team_name": null, "fleet_id": null, "fleet_name": null, "platform": "darwin"}`, 0), lastActivityID)
|
||||
})
|
||||
|
||||
t.Run("Rotation flow", func(t *testing.T) {
|
||||
@@ -25212,7 +25212,7 @@ func (s *integrationMDMTestSuite) TestManagedLocalAccount() {
|
||||
s.DoJSON("POST", "/api/latest/fleet/teams", &fleet.Team{Name: t.Name() + "team"}, http.StatusOK, &createTeamResp)
|
||||
tm := createTeamResp.Team
|
||||
tmConfigPath := fmt.Sprintf("/api/latest/fleet/teams/%d", tm.ID)
|
||||
expectedDetail := fmt.Sprintf(`{"team_id": %d, "team_name": %q, "fleet_id": %d, "fleet_name": %q}`, tm.ID, tm.Name, tm.ID, tm.Name)
|
||||
expectedDetail := fmt.Sprintf(`{"team_id": %d, "team_name": %q, "fleet_id": %d, "fleet_name": %q, "platform": "darwin"}`, tm.ID, tm.Name, tm.ID, tm.Name)
|
||||
|
||||
// Enable via PATCH /setup_experience
|
||||
s.Do("PATCH", "/api/latest/fleet/setup_experience",
|
||||
@@ -25249,7 +25249,7 @@ func (s *integrationMDMTestSuite) TestManagedLocalAccount() {
|
||||
s.DoJSON("POST", "/api/latest/fleet/teams", &fleet.Team{Name: t.Name() + "team"}, http.StatusOK, &createTeamResp)
|
||||
tm := createTeamResp.Team
|
||||
tmConfigPath := fmt.Sprintf("/api/latest/fleet/teams/%d", tm.ID)
|
||||
expectedDetail := fmt.Sprintf(`{"team_id": %d, "team_name": %q, "fleet_id": %d, "fleet_name": %q}`, tm.ID, tm.Name, tm.ID, tm.Name)
|
||||
expectedDetail := fmt.Sprintf(`{"team_id": %d, "team_name": %q, "fleet_id": %d, "fleet_name": %q, "platform": "darwin"}`, tm.ID, tm.Name, tm.ID, tm.Name)
|
||||
|
||||
// Enable via PATCH /teams/:id
|
||||
var tmResp teamResponse
|
||||
|
||||
Reference in New Issue
Block a user