Skip to content

Commit

Permalink
Revert "Disallow user-scoped profiles for Windows MDM" (#26940)
Browse files Browse the repository at this point in the history
Reverts #26153
  • Loading branch information
georgekarrv authored Mar 7, 2025
1 parent a0f8d09 commit 36aba53
Show file tree
Hide file tree
Showing 6 changed files with 15 additions and 41 deletions.
3 changes: 0 additions & 3 deletions changes/25731-mdm-windows-user-scoped

This file was deleted.

12 changes: 0 additions & 12 deletions server/fleet/windows_mdm.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,6 @@ func (m *MDMWindowsConfigProfile) ValidateUserProvided() error {

case xml.CharData:
if inLocURI {
if err := validateUserScopedLocURI(string(t)); err != nil {
return err
}
if err := validateFleetProvidedLocURI(string(t)); err != nil {
return err
}
Expand All @@ -151,15 +148,6 @@ func validateFleetProvidedLocURI(locURI string) error {
return nil
}

func validateUserScopedLocURI(locURI string) error {
sanitizedLocURI := strings.TrimSpace(locURI)
if strings.Contains(sanitizedLocURI, syncml.FleetUserScopedTargetLocURI) {
return errors.New(`The configuration profile can't be user scoped ("./User/"). <LocURI> must start with "./Device/" ("./Device/" can be omitted, it will default to device scope).`)
}

return nil
}

type MDMWindowsProfilePayload struct {
ProfileUUID string `db:"profile_uuid"`
ProfileName string `db:"profile_name"`
Expand Down
5 changes: 2 additions & 3 deletions server/mdm/microsoft/syncml/syncml.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,8 @@ const (
)

const (
FleetBitLockerTargetLocURI = "/Vendor/MSFT/BitLocker"
FleetOSUpdateTargetLocURI = "/Vendor/MSFT/Policy/Config/Update"
FleetUserScopedTargetLocURI = "./User/Vendor"
FleetBitLockerTargetLocURI = "/Vendor/MSFT/BitLocker"
FleetOSUpdateTargetLocURI = "/Vendor/MSFT/Policy/Config/Update"
)

// Supported MS-MDE2 enrollment versions
Expand Down
29 changes: 10 additions & 19 deletions server/service/integration_mdm_profiles_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2173,8 +2173,7 @@ func (s *integrationMDMTestSuite) TestBatchSetMDMAppleProfiles() {
mobileconfigForTest(p, p),
}}, http.StatusUnprocessableEntity, "team_id", fmt.Sprint(tm.ID))
errMsg := extractServerErrorText(res.Body)
require.Contains(t, errMsg, "Validation Failed")
require.Contains(t, errMsg, fmt.Sprintf("payload identifier %s is not allowed", p))
require.Contains(t, errMsg, fmt.Sprintf("Validation Failed: payload identifier %s is not allowed", p))
}

// payloads with reserved types
Expand All @@ -2183,8 +2182,7 @@ func (s *integrationMDMTestSuite) TestBatchSetMDMAppleProfiles() {
mobileconfigForTestWithContent("N1", "I1", "II1", p, ""),
}}, http.StatusUnprocessableEntity, "team_id", fmt.Sprint(tm.ID))
errMsg := extractServerErrorText(res.Body)
require.Contains(t, errMsg, "Validation Failed")
require.Contains(t, errMsg, fmt.Sprintf("unsupported PayloadType(s): %s", p))
require.Contains(t, errMsg, fmt.Sprintf("Validation Failed: unsupported PayloadType(s): %s", p))
}

// payloads with reserved identifiers
Expand All @@ -2193,8 +2191,7 @@ func (s *integrationMDMTestSuite) TestBatchSetMDMAppleProfiles() {
mobileconfigForTestWithContent("N1", "I1", p, "random", ""),
}}, http.StatusUnprocessableEntity, "team_id", fmt.Sprint(tm.ID))
errMsg := extractServerErrorText(res.Body)
require.Contains(t, errMsg, "Validation Failed")
require.Contains(t, errMsg, fmt.Sprintf("unsupported PayloadIdentifier(s): %s", p))
require.Contains(t, errMsg, fmt.Sprintf("Validation Failed: unsupported PayloadIdentifier(s): %s", p))
}

// successfully apply a profile for the team
Expand Down Expand Up @@ -4082,7 +4079,7 @@ func (s *integrationMDMTestSuite) TestBatchSetMDMProfiles() {
// Profile is too big
resp := s.Do("POST", "/api/v1/fleet/mdm/profiles/batch", batchSetMDMProfilesRequest{Profiles: []fleet.MDMProfileBatchPayload{{Contents: []byte(bigString)}}},
http.StatusUnprocessableEntity)
require.Contains(t, extractServerErrorText(resp.Body), "maximum configuration profile file size is 1 MB")
require.Contains(t, extractServerErrorText(resp.Body), "Validation Failed: maximum configuration profile file size is 1 MB")

// apply an empty set to no-team
s.Do("POST", "/api/v1/fleet/mdm/profiles/batch", batchSetMDMProfilesRequest{Profiles: nil}, http.StatusNoContent)
Expand Down Expand Up @@ -4128,8 +4125,7 @@ func (s *integrationMDMTestSuite) TestBatchSetMDMProfiles() {
{Name: "N4", Contents: declarationForTest("D1")},
}}, http.StatusUnprocessableEntity, "team_id", fmt.Sprint(tm.ID))
errMsg := extractServerErrorText(res.Body)
require.Contains(t, errMsg, "Validation Failed")
require.Contains(t, errMsg, fmt.Sprintf("payload identifier %s is not allowed", p))
require.Contains(t, errMsg, fmt.Sprintf("Validation Failed: payload identifier %s is not allowed", p))
}

// payloads with reserved types
Expand All @@ -4140,8 +4136,7 @@ func (s *integrationMDMTestSuite) TestBatchSetMDMProfiles() {
{Name: "N4", Contents: declarationForTest("D1")},
}}, http.StatusUnprocessableEntity, "team_id", fmt.Sprint(tm.ID))
errMsg := extractServerErrorText(res.Body)
require.Contains(t, errMsg, "Validation Failed")
require.Contains(t, errMsg, fmt.Sprintf("unsupported PayloadType(s): %s", p))
require.Contains(t, errMsg, fmt.Sprintf("Validation Failed: unsupported PayloadType(s): %s", p))
}

// payloads with reserved identifiers
Expand All @@ -4152,8 +4147,7 @@ func (s *integrationMDMTestSuite) TestBatchSetMDMProfiles() {
{Name: "N4", Contents: declarationForTest("D1")},
}}, http.StatusUnprocessableEntity, "team_id", fmt.Sprint(tm.ID))
errMsg := extractServerErrorText(res.Body)
require.Contains(t, errMsg, "Validation Failed")
require.Contains(t, errMsg, fmt.Sprintf("unsupported PayloadIdentifier(s): %s", p))
require.Contains(t, errMsg, fmt.Sprintf("Validation Failed: unsupported PayloadIdentifier(s): %s", p))
}

// profiles with forbidden declaration types
Expand Down Expand Up @@ -4406,8 +4400,7 @@ func (s *integrationMDMTestSuite) TestBatchSetMDMProfilesBackwardsCompat() {
"N3": syncMLForTest("./Foo/Bar"),
}}, http.StatusUnprocessableEntity, "team_id", fmt.Sprint(tm.ID))
errMsg := extractServerErrorText(res.Body)
require.Contains(t, errMsg, "Validation Failed")
require.Contains(t, errMsg, fmt.Sprintf("payload identifier %s is not allowed", p))
require.Contains(t, errMsg, fmt.Sprintf("Validation Failed: payload identifier %s is not allowed", p))
}

// payloads with reserved types
Expand All @@ -4417,8 +4410,7 @@ func (s *integrationMDMTestSuite) TestBatchSetMDMProfilesBackwardsCompat() {
"N3": syncMLForTest("./Foo/Bar"),
}}, http.StatusUnprocessableEntity, "team_id", fmt.Sprint(tm.ID))
errMsg := extractServerErrorText(res.Body)
require.Contains(t, errMsg, "Validation Failed")
require.Contains(t, errMsg, fmt.Sprintf("unsupported PayloadType(s): %s", p))
require.Contains(t, errMsg, fmt.Sprintf("Validation Failed: unsupported PayloadType(s): %s", p))
}

// payloads with reserved identifiers
Expand All @@ -4428,8 +4420,7 @@ func (s *integrationMDMTestSuite) TestBatchSetMDMProfilesBackwardsCompat() {
"N3": syncMLForTest("./Foo/Bar"),
}}, http.StatusUnprocessableEntity, "team_id", fmt.Sprint(tm.ID))
errMsg := extractServerErrorText(res.Body)
require.Contains(t, errMsg, "Validation Failed")
require.Contains(t, errMsg, fmt.Sprintf("unsupported PayloadIdentifier(s): %s", p))
require.Contains(t, errMsg, fmt.Sprintf("Validation Failed: unsupported PayloadIdentifier(s): %s", p))
}

// profiles with reserved Windows location URIs
Expand Down
4 changes: 2 additions & 2 deletions server/service/mdm.go
Original file line number Diff line number Diff line change
Expand Up @@ -1983,7 +1983,7 @@ func getAppleProfiles(

if err := mdmProf.ValidateUserProvided(); err != nil {
return nil, nil, ctxerr.Wrap(ctx,
fleet.NewInvalidArgumentError("mdm", fmt.Sprintf("macos_settings: %s: %s", prof.Name, err.Error())))
fleet.NewInvalidArgumentError(prof.Name, err.Error()))
}

if mdmProf.Name != prof.Name {
Expand Down Expand Up @@ -2077,7 +2077,7 @@ func getWindowsProfiles(

if err := mdmProf.ValidateUserProvided(); err != nil {
return nil, ctxerr.Wrap(ctx,
fleet.NewInvalidArgumentError("mdm", fmt.Sprintf("windows_settings: %s: %s", profile.Name, err.Error())))
fleet.NewInvalidArgumentError(fmt.Sprintf("profiles[%s]", profile.Name), err.Error()))
}

profs[i] = mdmProf
Expand Down
3 changes: 1 addition & 2 deletions server/service/mdm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1213,7 +1213,6 @@ func TestUploadWindowsMDMConfigProfileValidations(t *testing.T) {
{"BitLocker profile", 0, `<Replace><Item><Target><LocURI>./Device/Vendor/MSFT/BitLocker/AllowStandardUserEncryption</LocURI></Target></Item></Replace>`, true, "Custom configuration profiles can't include BitLocker settings."},
{"Windows updates profile", 0, `<Replace><Item><Target><LocURI> ./Device/Vendor/MSFT/Policy/Config/Update/ConfigureDeadlineNoAutoRebootForFeatureUpdates </LocURI></Target></Item></Replace>`, true, "Custom configuration profiles can't include Windows updates settings."},
{"unsupported Fleet variable", 0, `<Replace>$FLEET_VAR_BOZO</Replace>`, true, "Fleet variable"},
{"unsupported user-scoped profile", 0, `<Replace><Item><Target><LocURI>./User/Vendor/MSFT/Policy/Config/Bluetooth/AllowDiscoverableMode</LocURI></Target></Item></Replace>`, true, `The configuration profile can't be user scoped ("./User/"). <LocURI> must start with "./Device/" ("./Device/" can be omitted, it will default to device scope).`},

{"team empty profile", 1, "", true, "The file should include valid XML."},
{"team plist data", 1, string(mcBytesForTest("Foo", "Bar", "UUID")), true, "The file should include valid XML: processing instructions are not allowed."},
Expand All @@ -1225,7 +1224,7 @@ func TestUploadWindowsMDMConfigProfileValidations(t *testing.T) {
{"team Replace and non-Replace", 1, `<Replace>a</Replace><Get>b</Get>`, true, "Windows configuration profiles can only have <Replace> or <Add> top level elements."},
{"team BitLocker profile", 1, `<Replace><Item><Target><LocURI>./Device/Vendor/MSFT/BitLocker/AllowStandardUserEncryption</LocURI></Target></Item></Replace>`, true, "Custom configuration profiles can't include BitLocker settings."},
{"team Windows updates profile", 1, `<Replace><Item><Target><LocURI> ./Device/Vendor/MSFT/Policy/Config/Update/ConfigureDeadlineNoAutoRebootForFeatureUpdates </LocURI></Target></Item></Replace>`, true, "Custom configuration profiles can't include Windows updates settings."},
{"team unsupported user-scoped profile", 1, `<Replace><Item><Target><LocURI>./User/Vendor/MSFT/Policy/Config/Bluetooth/AllowDiscoverableMode</LocURI></Target></Item></Replace>`, true, `The configuration profile can't be user scoped ("./User/"). <LocURI> must start with "./Device/" ("./Device/" can be omitted, it will default to device scope).`},

{"invalid team", 2, `<Replace></Replace>`, true, "not found"},
}

Expand Down

0 comments on commit 36aba53

Please sign in to comment.