mirror of
https://github.com/fleetdm/fleet
synced 2026-05-23 08:58:41 +00:00
Clear profiles on Android host unenroll (#34343)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #34335 No changes file as this is an unreleased bug in 4.75.0 and covered by initial feature changes file # Checklist for submitter If some of the following don't apply, delete the relevant line. - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements) - [x] If paths of existing endpoints are modified without backwards compatibility, checked the frontend/CLI for any necessary changes ## Testing - [x] Added/updated automated tests - [x] Where appropriate, [automated tests simulate multiple hosts and test for host isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing) (updates to one hosts's records do not affect another) - [x] QA'd all new/changed functionality manually For unreleased bug fixes in a release candidate, one of: - [x] Confirmed that the fix is not expected to adversely impact load test results
This commit is contained in:
parent
21d1421566
commit
d8d25e6297
2 changed files with 103 additions and 9 deletions
|
|
@ -370,22 +370,47 @@ UPDATE host_mdm
|
|||
WHERE host_id IN (
|
||||
SELECT id FROM hosts WHERE platform = 'android'
|
||||
)`)
|
||||
return ctxerr.Wrap(ctx, err, "set host_mdm to unenrolled for android")
|
||||
if err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "set host_mdm to unenrolled for android hosts in bulk")
|
||||
}
|
||||
// Delete all Android custom OS settings for unenrolled hosts.
|
||||
// We do this in one query using a JOIN to avoid doing it one host at a time.
|
||||
_, err = ds.writer(ctx).ExecContext(ctx, `DELETE FROM host_mdm_android_profiles`)
|
||||
if err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "delete Android custom OS settings for unenrolled hosts in bulk")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetAndroidHostUnenrolled sets a single android host to unenrolled in host_mdm.
|
||||
// If the host is not enrolled, it does nothing and returns false.
|
||||
// SetAndroidHostUnenrolled sets a single android host to unenrolled in host_mdm and OS settings records
|
||||
// associated with it. If the host is not enrolled, it does nothing and returns false.
|
||||
func (ds *Datastore) SetAndroidHostUnenrolled(ctx context.Context, hostID uint) (bool, error) {
|
||||
result, err := ds.writer(ctx).ExecContext(ctx, `
|
||||
var rows int64
|
||||
err := ds.withTx(ctx, func(tx sqlx.ExtContext) error {
|
||||
result, err := tx.ExecContext(ctx, `
|
||||
UPDATE host_mdm
|
||||
SET server_url = '', mdm_id = NULL, enrolled = 0
|
||||
WHERE host_id = ? AND enrolled = 1`, hostID)
|
||||
if err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "set host_mdm to unenrolled for android host")
|
||||
}
|
||||
rows, err = result.RowsAffected()
|
||||
if err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "get rows affected for set host_mdm unenrolled for android host")
|
||||
}
|
||||
if rows > 0 {
|
||||
var uuid string
|
||||
err = sqlx.GetContext(ctx, tx, &uuid, `SELECT uuid FROM hosts WHERE id = ?`, hostID)
|
||||
if err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "get host uuid")
|
||||
}
|
||||
err = ds.deleteMDMOSCustomSettingsForHost(ctx, tx, uuid, "android")
|
||||
return ctxerr.Wrap(ctx, err, "delete Android custom OS settings for unenrolled host")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return false, ctxerr.Wrap(ctx, err, "set host_mdm to unenrolled for android host")
|
||||
}
|
||||
rows, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return false, ctxerr.Wrap(ctx, err, "get rows affected for set host_mdm unenrolled for android host")
|
||||
return false, err
|
||||
}
|
||||
return rows > 0, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ func TestAndroid(t *testing.T) {
|
|||
{"NewAndroidHostWithIdP", testNewAndroidHostWithIdP},
|
||||
{"AndroidBYODDetection", testAndroidBYODDetection},
|
||||
{"SetAndroidHostUnenrolled", testSetAndroidHostUnenrolled},
|
||||
{"BulkSetAndroidHostsUnenrolled", testBulkSetAndroidHostsUnenrolled},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
|
|
@ -2053,6 +2054,9 @@ func testSetAndroidHostUnenrolled(t *testing.T, ds *Datastore) {
|
|||
require.NotEmpty(t, serverURL)
|
||||
require.Equal(t, 0, mdmIDIsNull)
|
||||
|
||||
upsertAndroidHostProfileStatus(t, ds, res.Host.UUID, "profile-1", &fleet.MDMDeliveryPending)
|
||||
upsertAndroidHostProfileStatus(t, ds, res.Host.UUID, "profile-2", &fleet.MDMDeliveryPending)
|
||||
|
||||
// Perform single-host unenroll
|
||||
didUnenroll, err := ds.SetAndroidHostUnenrolled(testCtx(), res.Host.ID)
|
||||
require.NoError(t, err)
|
||||
|
|
@ -2063,6 +2067,8 @@ func testSetAndroidHostUnenrolled(t *testing.T, ds *Datastore) {
|
|||
require.NoError(t, err)
|
||||
require.False(t, didUnenroll)
|
||||
|
||||
profileCountForHost := 0
|
||||
|
||||
// Validate host_mdm row updated
|
||||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||||
return sqlx.GetContext(testCtx(), q, &enrolled, `SELECT enrolled FROM host_mdm WHERE host_id = ?`, res.Host.ID)
|
||||
|
|
@ -2073,7 +2079,70 @@ func testSetAndroidHostUnenrolled(t *testing.T, ds *Datastore) {
|
|||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||||
return sqlx.GetContext(testCtx(), q, &mdmIDIsNull, `SELECT CASE WHEN mdm_id IS NULL THEN 1 ELSE 0 END FROM host_mdm WHERE host_id = ?`, res.Host.ID)
|
||||
})
|
||||
// validate profile records deleted
|
||||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||||
return sqlx.GetContext(testCtx(), q, &profileCountForHost, `SELECT COUNT(*) FROM host_mdm_android_profiles WHERE host_uuid=?`, res.Host.UUID)
|
||||
})
|
||||
assert.Equal(t, 0, enrolled)
|
||||
assert.Equal(t, "", serverURL)
|
||||
assert.Equal(t, 1, mdmIDIsNull)
|
||||
assert.Equal(t, 0, profileCountForHost)
|
||||
}
|
||||
|
||||
func testBulkSetAndroidHostsUnenrolled(t *testing.T, ds *Datastore) {
|
||||
// Set a non-empty server URL so initial enrolled row has data to clear
|
||||
appCfg, err := ds.AppConfig(testCtx())
|
||||
require.NoError(t, err)
|
||||
appCfg.ServerSettings.ServerURL = "https://mdm.example.com"
|
||||
require.NoError(t, ds.SaveAppConfig(testCtx(), appCfg))
|
||||
|
||||
// Create 5 android hosts
|
||||
for i := 0; i < 5; i++ {
|
||||
esid := "enterprise-" + uuid.NewString()
|
||||
h := createAndroidHost(esid)
|
||||
res, err := ds.NewAndroidHost(testCtx(), h)
|
||||
require.NoError(t, err)
|
||||
|
||||
upsertAndroidHostProfileStatus(t, ds, res.Host.UUID, "profile-1", &fleet.MDMDeliveryPending)
|
||||
upsertAndroidHostProfileStatus(t, ds, res.Host.UUID, "profile-2", &fleet.MDMDeliveryPending)
|
||||
}
|
||||
|
||||
// Create a macOS host (to verify we don't unenroll non-Android hosts)
|
||||
macHost, err := ds.NewHost(testCtx(), &fleet.Host{
|
||||
Hostname: "test-host1-name",
|
||||
OsqueryHostID: ptr.String("1337"),
|
||||
NodeKey: ptr.String("1337"),
|
||||
UUID: "test-uuid-1",
|
||||
Platform: "darwin",
|
||||
HardwareSerial: uuid.NewString(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
nanoEnroll(t, ds, macHost, false)
|
||||
err = ds.MDMAppleUpsertHost(testCtx(), macHost, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Initial sanity check
|
||||
enrolledCount := 0
|
||||
androidHostProfileCount := 0
|
||||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||||
return sqlx.GetContext(testCtx(), q, &enrolledCount, `SELECT COUNT(*) FROM host_mdm WHERE enrolled = 1`)
|
||||
})
|
||||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||||
return sqlx.GetContext(testCtx(), q, &androidHostProfileCount, `SELECT COUNT(*) FROM host_mdm_android_profiles`)
|
||||
})
|
||||
assert.Equal(t, 10, androidHostProfileCount)
|
||||
require.Equal(t, 6, enrolledCount) // 5 android + 1 macOS
|
||||
|
||||
err = ds.BulkSetAndroidHostsUnenrolled(testCtx())
|
||||
require.NoError(t, err)
|
||||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||||
return sqlx.GetContext(testCtx(), q, &enrolledCount, `SELECT COUNT(*) FROM host_mdm WHERE enrolled = 1`)
|
||||
})
|
||||
require.Equal(t, 1, enrolledCount)
|
||||
|
||||
// validate profile records deleted
|
||||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||||
return sqlx.GetContext(testCtx(), q, &androidHostProfileCount, `SELECT COUNT(*) FROM host_mdm_android_profiles`)
|
||||
})
|
||||
assert.Equal(t, 0, androidHostProfileCount)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue