fix: return bad request error during MDM migration when appropriate (#16551)

> Related issue: https://github.com/fleetdm/confidential/issues/5138

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

<!-- Note that API documentation changes are now addressed by the
product design team. -->

- [x] Changes file added for user-visible changes in `changes/` or
`orbit/changes/`.
See [Changes
files](https://fleetdm.com/docs/contributing/committing-changes#changes-files)
for more information.
- [x] Added/updated tests
- [x] Manual QA for all new/changed functionality
This commit is contained in:
Jahziel Villasana-Espinoza 2024-03-06 15:38:44 -05:00 committed by GitHub
parent 91f2f11f9c
commit babf4e17c8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 93 additions and 2 deletions

View File

@ -0,0 +1,2 @@
- Fixes issue where a bad request response from a 3rd party MDM solution would result in a 500 error
in Fleet during MDM migration.

View File

@ -6196,6 +6196,75 @@ func (s *integrationMDMTestSuite) TestMigrateMDMDeviceWebhook() {
require.False(t, webhookCalled)
}
func (s *integrationMDMTestSuite) TestMigrateMDMDeviceWebhookErrors() {
t := s.T()
h := createHostAndDeviceToken(t, s.ds, "good-token")
var webhookCalled bool
webhookSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
webhookCalled = true
w.WriteHeader(http.StatusBadRequest)
}))
defer webhookSrv.Close()
// patch app config with webhook url
acResp := fleet.AppConfig{}
s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(fmt.Sprintf(`{
"mdm": {
"macos_migration": {
"enable": true,
"mode": "voluntary",
"webhook_url": "%s/test_mdm_migration"
}
}
}`, webhookSrv.URL)), http.StatusOK, &acResp)
require.True(t, acResp.MDM.MacOSMigration.Enable)
isServer, enrolled, installedFromDEP := true, true, true
mdmName := "ExampleMDM"
mdmURL := "https://mdm.example.com"
// host is enrolled to a third-party MDM but hasn't been assigned in
// ABM yet, so migration is not allowed
require.NoError(t, s.ds.SetOrUpdateMDMData(context.Background(), h.ID, !isServer, enrolled, mdmURL, installedFromDEP, mdmName, ""))
s.Do("POST", fmt.Sprintf("/api/v1/fleet/device/%s/migrate_mdm", "good-token"), nil, http.StatusBadRequest)
require.False(t, webhookCalled)
// simulate that the device is assigned to Fleet in ABM
s.mockDEPResponse(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
switch r.URL.Path {
case "/session":
_, _ = w.Write([]byte(`{"auth_session_token": "xyz"}`))
case "/profile":
encoder := json.NewEncoder(w)
err := encoder.Encode(godep.ProfileResponse{ProfileUUID: "abc"})
require.NoError(t, err)
case "/server/devices", "/devices/sync":
encoder := json.NewEncoder(w)
err := encoder.Encode(godep.DeviceResponse{
Devices: []godep.Device{
{
SerialNumber: h.HardwareSerial,
Model: "Mac Mini",
OS: "osx",
OpType: "added",
},
},
})
require.NoError(t, err)
}
}))
s.runDEPSchedule()
// hosts meets all requirements, webhook is run but returns an error, server should respond with
// the same status code
require.False(t, webhookCalled)
s.Do("POST", fmt.Sprintf("/api/v1/fleet/device/%s/migrate_mdm", "good-token"), nil, http.StatusBadRequest)
require.True(t, webhookCalled)
}
func (s *integrationMDMTestSuite) TestMDMMacOSSetup() {
t := s.T()

View File

@ -37,6 +37,22 @@ func httpSuccessStatus(statusCode int) bool {
return statusCode >= 200 && statusCode <= 299
}
// errWithStatus is an error with a particular status code.
type errWithStatus struct {
err string
statusCode int
}
// Error implements the error interface
func (e *errWithStatus) Error() string {
return e.err
}
// StatusCode implements the StatusCoder interface for returning custom status codes.
func (e *errWithStatus) StatusCode() int {
return e.statusCode
}
func PostJSONWithTimeout(ctx context.Context, url string, v interface{}) error {
jsonBytes, err := json.Marshal(v)
if err != nil {
@ -59,7 +75,7 @@ func PostJSONWithTimeout(ctx context.Context, url string, v interface{}) error {
if !httpSuccessStatus(resp.StatusCode) {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("error posting to %s: %d. %s", MaskSecretURLParams(url), resp.StatusCode, string(body))
return &errWithStatus{err: fmt.Sprintf("error posting to %s: %d. %s", MaskSecretURLParams(url), resp.StatusCode, string(body)), statusCode: resp.StatusCode}
}
return nil

View File

@ -41,6 +41,10 @@ module.exports = {
unauthorized: {
responseType: 'unauthorized',
description: 'This webhook request could not be verified.',
},
badRequest: {
responseType: 'badRequest',
description: 'Invalid MDM migration request.'
}
},
@ -102,7 +106,7 @@ module.exports = {
if(err.raw.statusCode === 404){
return new Error(`When sending a request to unenroll a host from a Workspace One instance (Host information: Serial number: ${host.hardware_serial}, id: ${host.id}, uuid: ${host.uuid}), the specified host was not found on the customer's Workspace One instance. Full error: ${err.stack}`);
} else if(err.raw.statusCode === 400) {
return new Error(`When sending a request to unenroll a host from a Workspace One instance (Host information: Serial number: ${host.hardware_serial}, id: ${host.id}, uuid: ${host.uuid}), the Workspace One instance could not unenroll the specified host. Full error: ${err.stack}`);
return { badRequest: `When sending a request to unenroll a host from a Workspace One instance (Host information: Serial number: ${host.hardware_serial}, id: ${host.id}, uuid: ${host.uuid}), the Workspace One instance could not unenroll the specified host. Full error: ${err.stack}` };
} else {
return new Error(`When sending a request to unenroll a host from a Workspace One instance (Host information: Serial number: ${host.hardware_serial}, id: ${host.id}, uuid: ${host.uuid}), an error occured. Full error: ${err.stack}`);
}