diff --git a/server/fleet/utils.go b/server/fleet/utils.go index 7095585d6ebf..ddab26e8e797 100644 --- a/server/fleet/utils.go +++ b/server/fleet/utils.go @@ -4,8 +4,10 @@ import ( "encoding/json" "errors" "io" + "strings" "github.com/fatih/color" + "golang.org/x/text/unicode/norm" ) func WriteExpiredLicenseBanner(w io.Writer) { @@ -56,3 +58,10 @@ func JSONStrictDecode(r io.Reader, v interface{}) error { return nil } + +func Preprocess(input string) string { + // Remove leading/trailing whitespace. + input = strings.TrimSpace(input) + // Normalize Unicode characters. + return norm.NFC.String(input) +} diff --git a/server/service/appconfig.go b/server/service/appconfig.go index ba6d6840e804..9d2fdcca2c50 100644 --- a/server/service/appconfig.go +++ b/server/service/appconfig.go @@ -328,11 +328,32 @@ func (svc *Service) ModifyAppConfig(ctx context.Context, p []byte, applyOpts fle } } + // We apply the config that is incoming to the old one + appConfig.EnableStrictDecoding() + if err := json.Unmarshal(p, &appConfig); err != nil { + err = fleet.NewUserMessageError(err, http.StatusBadRequest) + return nil, ctxerr.Wrap(ctx, err) + } + // Validate NDES SCEP URLs if they changed. Validation is done in both dry run and normal mode. - if license.IsPremium() && newAppConfig.Integrations.NDESSCEPProxy.Set && newAppConfig.Integrations.NDESSCEPProxy.Valid { + switch { + case !license.IsPremium(): + appConfig.Integrations.NDESSCEPProxy.Valid = false + case !newAppConfig.Integrations.NDESSCEPProxy.Set: + // Nothing is set -- keep the old value + appConfig.Integrations.NDESSCEPProxy = oldAppConfig.Integrations.NDESSCEPProxy + case !newAppConfig.Integrations.NDESSCEPProxy.Valid: + // User is explicitly clearing this setting + appConfig.Integrations.NDESSCEPProxy.Valid = false + default: + // User is updating the setting + appConfig.Integrations.NDESSCEPProxy.Value.URL = fleet.Preprocess(newAppConfig.Integrations.NDESSCEPProxy.Value.URL) + appConfig.Integrations.NDESSCEPProxy.Value.AdminURL = fleet.Preprocess(newAppConfig.Integrations.NDESSCEPProxy.Value.AdminURL) + appConfig.Integrations.NDESSCEPProxy.Value.Username = fleet.Preprocess(newAppConfig.Integrations.NDESSCEPProxy.Value.Username) + // do not preprocess password validateAdminURL, validateSCEPURL := false, false - newSCEPProxy := newAppConfig.Integrations.NDESSCEPProxy.Value + newSCEPProxy := appConfig.Integrations.NDESSCEPProxy.Value if !oldAppConfig.Integrations.NDESSCEPProxy.Valid { validateAdminURL, validateSCEPURL = true, true } else { @@ -360,17 +381,6 @@ func (svc *Service) ModifyAppConfig(ctx context.Context, p []byte, applyOpts fle } } - // We apply the config that is incoming to the old one - appConfig.EnableStrictDecoding() - if err := json.Unmarshal(p, &appConfig); err != nil { - err = fleet.NewUserMessageError(err, http.StatusBadRequest) - return nil, ctxerr.Wrap(ctx, err) - } - - if !license.IsPremium() { - appConfig.Integrations.NDESSCEPProxy.Valid = false - } - // EnableDiskEncryption is an optjson.Bool field in order to support the // legacy field under "mdm.macos_settings". If the field provided to the // PATCH endpoint is set but invalid (that is, "enable_disk_encryption": diff --git a/server/service/appconfig_test.go b/server/service/appconfig_test.go index cfea150dc592..8eea64eba02a 100644 --- a/server/service/appconfig_test.go +++ b/server/service/appconfig_test.go @@ -1349,6 +1349,9 @@ func TestModifyAppConfigForNDESSCEPProxy(t *testing.T) { ds.ListABMTokensFunc = func(ctx context.Context) ([]*fleet.ABMToken, error) { return []*fleet.ABMToken{{ID: 1}}, nil } + ds.SaveABMTokenFunc = func(ctx context.Context, token *fleet.ABMToken) error { + return nil + } jsonPayloadBase := ` { @@ -1407,9 +1410,9 @@ func TestModifyAppConfigForNDESSCEPProxy(t *testing.T) { appConfig = ac validateNDESSCEPURLCalled = false validateNDESSCEPAdminURLCalled = false - jsonPayload = fmt.Sprintf(jsonPayloadBase, scepURL, adminURL, username, fleet.MaskedPassword) + jsonPayload = fmt.Sprintf(jsonPayloadBase, " "+scepURL, adminURL+" ", " "+username+" ", fleet.MaskedPassword) ac, err = svc.ModifyAppConfig(ctx, []byte(jsonPayload), fleet.ApplySpecOptions{}) - require.NoError(t, err) + require.NoError(t, err, jsonPayload) checkSCEPProxy() assert.False(t, validateNDESSCEPURLCalled) assert.False(t, validateNDESSCEPAdminURLCalled) diff --git a/tools/cloner-check/generated_files/appconfig.txt b/tools/cloner-check/generated_files/appconfig.txt index 47299f2daa42..9588b0cb5bcd 100644 --- a/tools/cloner-check/generated_files/appconfig.txt +++ b/tools/cloner-check/generated_files/appconfig.txt @@ -95,11 +95,15 @@ github.com/fleetdm/fleet/v4/server/fleet/ZendeskIntegration EnableSoftwareVulner github.com/fleetdm/fleet/v4/server/fleet/Integrations GoogleCalendar []*fleet.GoogleCalendarIntegration github.com/fleetdm/fleet/v4/server/fleet/GoogleCalendarIntegration Domain string github.com/fleetdm/fleet/v4/server/fleet/GoogleCalendarIntegration ApiKey map[string]string -github.com/fleetdm/fleet/v4/server/fleet/Integrations NDESSCEPProxy *fleet.NDESSCEPProxyIntegration +github.com/fleetdm/fleet/v4/server/fleet/Integrations NDESSCEPProxy optjson.Any[github.com/fleetdm/fleet/v4/server/fleet.NDESSCEPProxyIntegration] +github.com/fleetdm/fleet/v4/pkg/optjson/Any[github.com/fleetdm/fleet/v4/server/fleet.NDESSCEPProxyIntegration] Set bool +github.com/fleetdm/fleet/v4/pkg/optjson/Any[github.com/fleetdm/fleet/v4/server/fleet.NDESSCEPProxyIntegration] Valid bool +github.com/fleetdm/fleet/v4/pkg/optjson/Any[github.com/fleetdm/fleet/v4/server/fleet.NDESSCEPProxyIntegration] Value fleet.NDESSCEPProxyIntegration github.com/fleetdm/fleet/v4/server/fleet/NDESSCEPProxyIntegration URL string github.com/fleetdm/fleet/v4/server/fleet/NDESSCEPProxyIntegration AdminURL string github.com/fleetdm/fleet/v4/server/fleet/NDESSCEPProxyIntegration Username string github.com/fleetdm/fleet/v4/server/fleet/NDESSCEPProxyIntegration Password string +github.com/fleetdm/fleet/v4/pkg/optjson/Any[github.com/fleetdm/fleet/v4/server/fleet.NDESSCEPProxyIntegration] ZeroValue func() fleet.NDESSCEPProxyIntegration github.com/fleetdm/fleet/v4/server/fleet/AppConfig MDM fleet.MDM github.com/fleetdm/fleet/v4/server/fleet/MDM DeprecatedAppleBMDefaultTeam string github.com/fleetdm/fleet/v4/server/fleet/MDM AppleBusinessManager optjson.Slice[github.com/fleetdm/fleet/v4/server/fleet.MDMAppleABMAssignmentInfo]