Update API endpoints that support os_setttings filter to include Windows MDM profiles status (#15188)
This commit is contained in:
@@ -0,0 +1 @@
|
||||
- Updated API endpoints that support `os_setttings` filter to include Windows profiles status.
|
||||
+111
-27
@@ -1180,47 +1180,133 @@ func (ds *Datastore) filterHostsByOSSettingsStatus(sql string, opt fleet.HostLis
|
||||
return sql, params
|
||||
}
|
||||
|
||||
// TODO: Look into ways we can convert some of the LEFT JOINs in the main list hosts query
|
||||
// to INNER JOINs if the OSSettingsFilter is set. This would allow us to use indices
|
||||
// from the `host_mdm` table, for example, to cut down on the number of rows that need
|
||||
// to be scanned. For now, this method assumes that LEFT JOINs are used in the main query
|
||||
// and adds extra where clauses to filter out Windows hosts that are not enrolled to Fleet MDM
|
||||
// or are servers. Similar logic could be applied to macOS hosts but is not included in this
|
||||
// current implementation.
|
||||
|
||||
sqlFmt := ` AND h.platform IN('windows', 'darwin')`
|
||||
if opt.TeamFilter == nil {
|
||||
// macOS settings filter is not compatible with the "all teams" option so append the "no
|
||||
// team" filter here (note that filterHostsByTeam applies the "no team" filter if TeamFilter == 0)
|
||||
// OS settings filter is not compatible with the "all teams" option so append the "no team"
|
||||
// filter here (note that filterHostsByTeam applies the "no team" filter if TeamFilter == 0)
|
||||
sqlFmt += ` AND h.team_id IS NULL`
|
||||
}
|
||||
var whereMacOS, whereWindows string
|
||||
sqlFmt += ` AND ((h.platform = 'windows' AND (%s)) OR (h.platform = 'darwin' AND (%s)))`
|
||||
|
||||
// construct the WHERE for macOS
|
||||
var subqueryMacOS string
|
||||
var subqueryParams []interface{}
|
||||
whereWindows := "FALSE"
|
||||
whereMacOS := "FALSE"
|
||||
|
||||
var paramsMacOS []interface{}
|
||||
switch opt.OSSettingsFilter {
|
||||
case fleet.OSSettingsFailed:
|
||||
subqueryMacOS, subqueryParams = subqueryHostsMacOSSettingsStatusFailed()
|
||||
if isDiskEncryptionEnabled {
|
||||
whereWindows = ds.whereBitLockerStatus(fleet.DiskEncryptionFailed)
|
||||
}
|
||||
subqueryMacOS, paramsMacOS = subqueryHostsMacOSSettingsStatusFailed()
|
||||
case fleet.OSSettingsPending:
|
||||
subqueryMacOS, subqueryParams = subqueryHostsMacOSSettingsStatusPending()
|
||||
if isDiskEncryptionEnabled {
|
||||
whereWindows = ds.whereBitLockerStatus(fleet.DiskEncryptionEnforcing)
|
||||
}
|
||||
subqueryMacOS, paramsMacOS = subqueryHostsMacOSSettingsStatusPending()
|
||||
case fleet.OSSettingsVerifying:
|
||||
subqueryMacOS, subqueryParams = subqueryHostsMacOSSetttingsStatusVerifying()
|
||||
if isDiskEncryptionEnabled {
|
||||
whereWindows = ds.whereBitLockerStatus(fleet.DiskEncryptionVerifying)
|
||||
}
|
||||
subqueryMacOS, paramsMacOS = subqueryHostsMacOSSetttingsStatusVerifying()
|
||||
case fleet.OSSettingsVerified:
|
||||
subqueryMacOS, subqueryParams = subqueryHostsMacOSSetttingsStatusVerified()
|
||||
if isDiskEncryptionEnabled {
|
||||
whereWindows = ds.whereBitLockerStatus(fleet.DiskEncryptionVerified)
|
||||
}
|
||||
subqueryMacOS, paramsMacOS = subqueryHostsMacOSSetttingsStatusVerified()
|
||||
}
|
||||
|
||||
if subqueryMacOS != "" {
|
||||
whereMacOS = "EXISTS (" + subqueryMacOS + ")"
|
||||
} else {
|
||||
whereMacOS = "FALSE"
|
||||
}
|
||||
|
||||
return sql + fmt.Sprintf(sqlFmt, whereWindows, whereMacOS), append(params, subqueryParams...)
|
||||
// construct the WHERE for windows
|
||||
whereWindows = `hmdm.name = ? AND hmdm.enrolled = 1 AND hmdm.is_server = 0`
|
||||
paramsWindows := []interface{}{fleet.WellKnownMDMFleet}
|
||||
subqueryFailed, paramsFailed := subqueryHostsMDMWindowsOSSettingsStatusFailed()
|
||||
paramsWindows = append(paramsWindows, paramsFailed...)
|
||||
subqueryPending, paramsPending := subqueryHostsMDMWindowsOSSettingsStatusPending()
|
||||
paramsWindows = append(paramsWindows, paramsPending...)
|
||||
subqueryVerifying, paramsVerifying := subqueryHostsMDMWindowsOSSettingsStatusVerifying()
|
||||
paramsWindows = append(paramsWindows, paramsVerifying...)
|
||||
subqueryVerified, paramsVerified := subqueryHostsMDMWindowsOSSettingsStatusVerified()
|
||||
paramsWindows = append(paramsWindows, paramsVerified...)
|
||||
|
||||
profilesStatus := fmt.Sprintf(`
|
||||
CASE WHEN EXISTS (%s) THEN
|
||||
'profiles_failed'
|
||||
WHEN EXISTS (%s) THEN
|
||||
'profiles_pending'
|
||||
WHEN EXISTS (%s) THEN
|
||||
'profiles_verifying'
|
||||
WHEN EXISTS (%s) THEN
|
||||
'profiles_verified'
|
||||
ELSE
|
||||
''
|
||||
END`,
|
||||
subqueryFailed,
|
||||
subqueryPending,
|
||||
subqueryVerifying,
|
||||
subqueryVerified,
|
||||
)
|
||||
|
||||
bitlockerStatus := `''`
|
||||
if isDiskEncryptionEnabled {
|
||||
bitlockerStatus = fmt.Sprintf(`
|
||||
CASE WHEN (%s) THEN
|
||||
'bitlocker_verified'
|
||||
WHEN (%s) THEN
|
||||
'bitlocker_verifying'
|
||||
WHEN (%s) THEN
|
||||
'bitlocker_pending'
|
||||
WHEN (%s) THEN
|
||||
'bitlocker_failed'
|
||||
ELSE
|
||||
''
|
||||
END`,
|
||||
ds.whereBitLockerStatus(fleet.DiskEncryptionVerified),
|
||||
ds.whereBitLockerStatus(fleet.DiskEncryptionVerifying),
|
||||
ds.whereBitLockerStatus(fleet.DiskEncryptionEnforcing),
|
||||
ds.whereBitLockerStatus(fleet.DiskEncryptionFailed),
|
||||
)
|
||||
}
|
||||
|
||||
whereWindows += fmt.Sprintf(` AND (
|
||||
CASE (%s)
|
||||
WHEN 'profiles_failed' THEN
|
||||
'failed'
|
||||
WHEN 'profiles_pending' THEN (
|
||||
CASE (%s)
|
||||
WHEN 'bitlocker_failed' THEN
|
||||
'failed'
|
||||
ELSE
|
||||
'pending'
|
||||
END)
|
||||
WHEN 'profiles_verifying' THEN (
|
||||
CASE (%s)
|
||||
WHEN 'bitlocker_failed' THEN
|
||||
'failed'
|
||||
WHEN 'bitlocker_pending' THEN
|
||||
'pending'
|
||||
ELSE
|
||||
'verifying'
|
||||
END)
|
||||
WHEN 'profiles_verified' THEN (
|
||||
CASE (%s)
|
||||
WHEN 'bitlocker_failed' THEN
|
||||
'failed'
|
||||
WHEN 'bitlocker_pending' THEN
|
||||
'pending'
|
||||
WHEN 'bitlocker_verifying' THEN
|
||||
'verifying'
|
||||
ELSE
|
||||
'verified'
|
||||
END)
|
||||
ELSE
|
||||
REPLACE((%s), 'bitlocker_', '')
|
||||
END) = ?`, profilesStatus, bitlockerStatus, bitlockerStatus, bitlockerStatus, bitlockerStatus)
|
||||
|
||||
paramsWindows = append(paramsWindows, opt.OSSettingsFilter)
|
||||
params = append(params, paramsWindows...)
|
||||
params = append(params, paramsMacOS...)
|
||||
|
||||
return sql + fmt.Sprintf(sqlFmt, whereWindows, whereMacOS), params
|
||||
}
|
||||
|
||||
func (ds *Datastore) filterHostsByOSSettingsDiskEncryptionStatus(sql string, opt fleet.HostListOptions, params []interface{}, enableDiskEncryption bool) (string, []interface{}) {
|
||||
@@ -1229,10 +1315,8 @@ func (ds *Datastore) filterHostsByOSSettingsDiskEncryptionStatus(sql string, opt
|
||||
}
|
||||
|
||||
sqlFmt := " AND h.platform IN('windows', 'darwin')"
|
||||
// TODO: Should we add no team filter here? It isn't included for the FileVault filter but is
|
||||
// for the general macOS settings filter.
|
||||
if opt.TeamFilter == nil {
|
||||
// macOS settings filter is not compatible with the "all teams" option so append the "no
|
||||
// OS settings filter is not compatible with the "all teams" option so append the "no
|
||||
// team" filter here (note that filterHostsByTeam applies the "no team" filter if TeamFilter == 0)
|
||||
sqlFmt += ` AND h.team_id IS NULL`
|
||||
}
|
||||
|
||||
@@ -879,6 +879,17 @@ SELECT
|
||||
ELSE
|
||||
'verifying'
|
||||
END)
|
||||
WHEN 'profiles_verified' THEN (
|
||||
CASE (%s)
|
||||
WHEN 'bitlocker_failed' THEN
|
||||
'failed'
|
||||
WHEN 'bitlocker_pending' THEN
|
||||
'pending'
|
||||
WHEN 'bitlocker_verifying' THEN
|
||||
'verifying'
|
||||
ELSE
|
||||
'verified'
|
||||
END)
|
||||
ELSE
|
||||
REPLACE((%s), 'bitlocker_', '')
|
||||
END as status,
|
||||
@@ -900,6 +911,7 @@ GROUP BY
|
||||
bitlockerStatus,
|
||||
bitlockerStatus,
|
||||
bitlockerStatus,
|
||||
bitlockerStatus,
|
||||
bitlockerJoin,
|
||||
fleet.WellKnownMDMFleet,
|
||||
teamFilter,
|
||||
|
||||
@@ -144,6 +144,10 @@ func testMDMWindowsDiskEncryption(t *testing.T, ds *Datastore) {
|
||||
for _, h := range gotHosts {
|
||||
require.Contains(t, expectedIDs, h.ID)
|
||||
}
|
||||
|
||||
count, err := ds.CountHosts(ctx, fleet.TeamFilter{User: test.UserAdmin}, fleet.HostListOptions{TeamFilter: teamID, OSSettingsDiskEncryptionFilter: status})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, len(expectedIDs), count, fmt.Sprintf("status: %s", status))
|
||||
}
|
||||
|
||||
checkHostBitLockerStatus := func(t *testing.T, expected fleet.DiskEncryptionStatus, hostIDs []uint) {
|
||||
@@ -223,14 +227,10 @@ func testMDMWindowsDiskEncryption(t *testing.T, ds *Datastore) {
|
||||
Verified: uint(len(ep[fleet.MDMDeliveryVerified])),
|
||||
})
|
||||
|
||||
checkListHostsFilterOSSettings(t, teamID, fleet.OSSettingsVerified, expectedDE[fleet.DiskEncryptionVerified])
|
||||
checkListHostsFilterOSSettings(t, teamID, fleet.OSSettingsVerifying, expectedDE[fleet.DiskEncryptionVerifying])
|
||||
checkListHostsFilterOSSettings(t, teamID, fleet.OSSettingsFailed, expectedDE[fleet.DiskEncryptionFailed])
|
||||
var expectedPending []uint
|
||||
expectedPending = append(expectedPending, expectedDE[fleet.DiskEncryptionEnforcing]...)
|
||||
expectedPending = append(expectedPending, expectedDE[fleet.DiskEncryptionRemovingEnforcement]...)
|
||||
expectedPending = append(expectedPending, expectedDE[fleet.DiskEncryptionActionRequired]...)
|
||||
checkListHostsFilterOSSettings(t, teamID, fleet.OSSettingsPending, expectedPending)
|
||||
checkListHostsFilterOSSettings(t, teamID, fleet.OSSettingsVerified, ep[fleet.MDMDeliveryVerified])
|
||||
checkListHostsFilterOSSettings(t, teamID, fleet.OSSettingsVerifying, ep[fleet.MDMDeliveryVerifying])
|
||||
checkListHostsFilterOSSettings(t, teamID, fleet.OSSettingsFailed, ep[fleet.MDMDeliveryFailed])
|
||||
checkListHostsFilterOSSettings(t, teamID, fleet.OSSettingsPending, ep[fleet.MDMDeliveryPending])
|
||||
}
|
||||
|
||||
updateHostDisks := func(t *testing.T, hostID uint, encrypted bool, updated_at time.Time) {
|
||||
@@ -292,8 +292,14 @@ func testMDMWindowsDiskEncryption(t *testing.T, ds *Datastore) {
|
||||
t.Run("Disk encryption disabled", func(t *testing.T) {
|
||||
ac, err := ds.AppConfig(ctx)
|
||||
require.NoError(t, err)
|
||||
ac.MDM.EnableDiskEncryption = optjson.SetBool(false)
|
||||
require.NoError(t, ds.SaveAppConfig(ctx, ac))
|
||||
ac, err = ds.AppConfig(ctx)
|
||||
require.NoError(t, err)
|
||||
require.False(t, ac.MDM.EnableDiskEncryption.Value)
|
||||
|
||||
cleanupHostProfiles(t)
|
||||
|
||||
checkExpected(t, nil, hostIDsByDEStatus{}) // no hosts are counted because disk encryption is not enabled
|
||||
})
|
||||
|
||||
@@ -554,6 +560,26 @@ func testMDMWindowsProfilesSummary(t *testing.T, ds *Datastore) {
|
||||
require.Equal(t, expected, *ps)
|
||||
}
|
||||
|
||||
checkListHostsFilterOSSettings := func(t *testing.T, teamID *uint, status fleet.OSSettingsStatus, expectedIDs []uint) {
|
||||
gotHosts, err := ds.ListHosts(ctx, fleet.TeamFilter{User: test.UserAdmin}, fleet.HostListOptions{TeamFilter: teamID, OSSettingsFilter: status})
|
||||
require.NoError(t, err)
|
||||
if len(expectedIDs) != len(gotHosts) {
|
||||
gotIDs := make([]uint, len(gotHosts))
|
||||
for _, h := range gotHosts {
|
||||
gotIDs = append(gotIDs, h.ID)
|
||||
}
|
||||
require.Len(t, gotHosts, len(expectedIDs), fmt.Sprintf("status: %s expected: %v got: %v", status, expectedIDs, gotIDs))
|
||||
|
||||
}
|
||||
for _, h := range gotHosts {
|
||||
require.Contains(t, expectedIDs, h.ID)
|
||||
}
|
||||
|
||||
count, err := ds.CountHosts(ctx, fleet.TeamFilter{User: test.UserAdmin}, fleet.HostListOptions{TeamFilter: teamID, OSSettingsFilter: status})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, len(expectedIDs), count, "status: %s", status)
|
||||
}
|
||||
|
||||
type hostIDsByProfileStatus map[fleet.MDMDeliveryStatus][]uint
|
||||
|
||||
checkExpected := func(t *testing.T, teamID *uint, ep hostIDsByProfileStatus) {
|
||||
@@ -563,6 +589,11 @@ func testMDMWindowsProfilesSummary(t *testing.T, ds *Datastore) {
|
||||
Verifying: uint(len(ep[fleet.MDMDeliveryVerifying])),
|
||||
Verified: uint(len(ep[fleet.MDMDeliveryVerified])),
|
||||
})
|
||||
|
||||
checkListHostsFilterOSSettings(t, teamID, fleet.OSSettingsVerified, ep[fleet.MDMDeliveryVerified])
|
||||
checkListHostsFilterOSSettings(t, teamID, fleet.OSSettingsVerifying, ep[fleet.MDMDeliveryVerifying])
|
||||
checkListHostsFilterOSSettings(t, teamID, fleet.OSSettingsFailed, ep[fleet.MDMDeliveryFailed])
|
||||
checkListHostsFilterOSSettings(t, teamID, fleet.OSSettingsPending, ep[fleet.MDMDeliveryPending])
|
||||
}
|
||||
|
||||
upsertHostProfileStatus := func(t *testing.T, hostUUID string, profUUID string, status *fleet.MDMDeliveryStatus) {
|
||||
|
||||
Reference in New Issue
Block a user