mirror of
https://github.com/fleetdm/fleet
synced 2026-05-23 08:58:41 +00:00
Update categorization of Windows OS updates to exclude from user-defined Windows MDM profiles in API responses (#15924)
This commit is contained in:
parent
ca948da440
commit
fa14eaf63a
6 changed files with 264 additions and 34 deletions
3
changes/15714-windows-os-updates-profile-summary
Normal file
3
changes/15714-windows-os-updates-profile-summary
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
- Modified hosts and labels endpoints so that only user-defined Windows MDM profiles are included in
|
||||
filtered results, host details, and profiles summaries API responses (more specifically,
|
||||
"Windows OS updates" is excluded from these responses).
|
||||
|
|
@ -1077,7 +1077,10 @@ func (ds *Datastore) applyHostFilters(
|
|||
}
|
||||
return "", nil, err
|
||||
} else if opt.OSSettingsFilter.IsValid() {
|
||||
sqlStmt, params = ds.filterHostsByOSSettingsStatus(sqlStmt, opt, params, enableDiskEncryption)
|
||||
sqlStmt, params, err = ds.filterHostsByOSSettingsStatus(sqlStmt, opt, params, enableDiskEncryption)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
} else if opt.OSSettingsDiskEncryptionFilter.IsValid() {
|
||||
sqlStmt, params = ds.filterHostsByOSSettingsDiskEncryptionStatus(sqlStmt, opt, params, enableDiskEncryption)
|
||||
}
|
||||
|
|
@ -1234,9 +1237,9 @@ func filterHostsByMacOSDiskEncryptionStatus(sql string, opt fleet.HostListOption
|
|||
return sql + fmt.Sprintf(` AND EXISTS (%s)`, subquery), append(params, subqueryParams...)
|
||||
}
|
||||
|
||||
func (ds *Datastore) filterHostsByOSSettingsStatus(sql string, opt fleet.HostListOptions, params []interface{}, isDiskEncryptionEnabled bool) (string, []interface{}) {
|
||||
func (ds *Datastore) filterHostsByOSSettingsStatus(sql string, opt fleet.HostListOptions, params []interface{}, isDiskEncryptionEnabled bool) (string, []interface{}, error) {
|
||||
if !opt.OSSettingsFilter.IsValid() {
|
||||
return sql, params
|
||||
return sql, params, nil
|
||||
}
|
||||
|
||||
// TODO: Look into ways we can convert some of the LEFT JOINs in the main list hosts query
|
||||
|
|
@ -1278,13 +1281,25 @@ func (ds *Datastore) filterHostsByOSSettingsStatus(sql string, opt fleet.HostLis
|
|||
// construct the WHERE for windows
|
||||
whereWindows = `hmdm.name = ? AND hmdm.enrolled = 1 AND hmdm.is_server = 0`
|
||||
paramsWindows := []interface{}{fleet.WellKnownMDMFleet}
|
||||
subqueryFailed, paramsFailed := subqueryHostsMDMWindowsOSSettingsStatusFailed()
|
||||
subqueryFailed, paramsFailed, err := subqueryHostsMDMWindowsOSSettingsStatusFailed()
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
paramsWindows = append(paramsWindows, paramsFailed...)
|
||||
subqueryPending, paramsPending := subqueryHostsMDMWindowsOSSettingsStatusPending()
|
||||
subqueryPending, paramsPending, err := subqueryHostsMDMWindowsOSSettingsStatusPending()
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
paramsWindows = append(paramsWindows, paramsPending...)
|
||||
subqueryVerifying, paramsVerifying := subqueryHostsMDMWindowsOSSettingsStatusVerifying()
|
||||
subqueryVerifying, paramsVerifying, err := subqueryHostsMDMWindowsOSSettingsStatusVerifying()
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
paramsWindows = append(paramsWindows, paramsVerifying...)
|
||||
subqueryVerified, paramsVerified := subqueryHostsMDMWindowsOSSettingsStatusVerified()
|
||||
subqueryVerified, paramsVerified, err := subqueryHostsMDMWindowsOSSettingsStatusVerified()
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
paramsWindows = append(paramsWindows, paramsVerified...)
|
||||
|
||||
profilesStatus := fmt.Sprintf(`
|
||||
|
|
@ -1365,7 +1380,7 @@ func (ds *Datastore) filterHostsByOSSettingsStatus(sql string, opt fleet.HostLis
|
|||
params = append(params, paramsWindows...)
|
||||
params = append(params, paramsMacOS...)
|
||||
|
||||
return sql + fmt.Sprintf(sqlFmt, whereWindows, whereMacOS), params
|
||||
return sql + fmt.Sprintf(sqlFmt, whereWindows, whereMacOS), params, nil
|
||||
}
|
||||
|
||||
func (ds *Datastore) filterHostsByOSSettingsDiskEncryptionStatus(sql string, opt fleet.HostListOptions, params []interface{}, enableDiskEncryption bool) (string, []interface{}) {
|
||||
|
|
|
|||
|
|
@ -593,7 +593,10 @@ func (ds *Datastore) applyHostLabelFilters(ctx context.Context, filter fleet.Tea
|
|||
if enableDiskEncryption, err := ds.getConfigEnableDiskEncryption(ctx, opt.TeamFilter); err != nil {
|
||||
return "", nil, err
|
||||
} else if opt.OSSettingsFilter.IsValid() {
|
||||
query, params = ds.filterHostsByOSSettingsStatus(query, opt, params, enableDiskEncryption)
|
||||
query, params, err = ds.filterHostsByOSSettingsStatus(query, opt, params, enableDiskEncryption)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
} else if opt.OSSettingsDiskEncryptionFilter.IsValid() {
|
||||
query, params = ds.filterHostsByOSSettingsDiskEncryptionStatus(query, opt, params, enableDiskEncryption)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -729,40 +729,46 @@ func (ds *Datastore) DeleteMDMWindowsConfigProfileByTeamAndName(ctx context.Cont
|
|||
return nil
|
||||
}
|
||||
|
||||
func subqueryHostsMDMWindowsOSSettingsStatusFailed() (string, []interface{}) {
|
||||
func subqueryHostsMDMWindowsOSSettingsStatusFailed() (string, []interface{}, error) {
|
||||
sql := `
|
||||
SELECT
|
||||
1 FROM host_mdm_windows_profiles hmwp
|
||||
WHERE
|
||||
h.uuid = hmwp.host_uuid
|
||||
AND hmwp.status = ?`
|
||||
AND hmwp.status = ?
|
||||
AND hmwp.profile_name NOT IN(?)`
|
||||
args := []interface{}{
|
||||
fleet.MDMDeliveryFailed,
|
||||
mdm.ListFleetReservedWindowsProfileNames(),
|
||||
}
|
||||
|
||||
return sql, args
|
||||
return sqlx.In(sql, args...)
|
||||
}
|
||||
|
||||
func subqueryHostsMDMWindowsOSSettingsStatusPending() (string, []interface{}) {
|
||||
func subqueryHostsMDMWindowsOSSettingsStatusPending() (string, []interface{}, error) {
|
||||
sql := `
|
||||
SELECT
|
||||
1 FROM host_mdm_windows_profiles hmwp
|
||||
WHERE
|
||||
h.uuid = hmwp.host_uuid
|
||||
AND (hmwp.status IS NULL OR hmwp.status = ?)
|
||||
AND hmwp.profile_name NOT IN(?)
|
||||
AND NOT EXISTS (
|
||||
SELECT
|
||||
1 FROM host_mdm_windows_profiles hmwp2
|
||||
WHERE (h.uuid = hmwp2.host_uuid
|
||||
AND hmwp2.status = ?))`
|
||||
AND hmwp2.status = ?
|
||||
AND hmwp2.profile_name NOT IN(?)))`
|
||||
args := []interface{}{
|
||||
fleet.MDMDeliveryPending,
|
||||
mdm.ListFleetReservedWindowsProfileNames(),
|
||||
fleet.MDMDeliveryFailed,
|
||||
mdm.ListFleetReservedWindowsProfileNames(),
|
||||
}
|
||||
return sql, args
|
||||
return sqlx.In(sql, args...)
|
||||
}
|
||||
|
||||
func subqueryHostsMDMWindowsOSSettingsStatusVerifying() (string, []interface{}) {
|
||||
func subqueryHostsMDMWindowsOSSettingsStatusVerifying() (string, []interface{}, error) {
|
||||
sql := `
|
||||
SELECT
|
||||
1 FROM host_mdm_windows_profiles hmwp
|
||||
|
|
@ -770,25 +776,28 @@ func subqueryHostsMDMWindowsOSSettingsStatusVerifying() (string, []interface{})
|
|||
h.uuid = hmwp.host_uuid
|
||||
AND hmwp.operation_type = ?
|
||||
AND hmwp.status = ?
|
||||
AND hmwp.profile_name NOT IN(?)
|
||||
AND NOT EXISTS (
|
||||
SELECT
|
||||
1 FROM host_mdm_windows_profiles hmwp2
|
||||
WHERE (h.uuid = hmwp2.host_uuid
|
||||
AND hmwp2.operation_type = ?
|
||||
AND hmwp2.profile_name NOT IN(?)
|
||||
AND(hmwp2.status IS NULL
|
||||
OR hmwp2.status NOT IN(?, ?))))`
|
||||
OR hmwp2.status NOT IN(?))))`
|
||||
|
||||
args := []interface{}{
|
||||
fleet.MDMOperationTypeInstall,
|
||||
fleet.MDMDeliveryVerifying,
|
||||
mdm.ListFleetReservedWindowsProfileNames(),
|
||||
fleet.MDMOperationTypeInstall,
|
||||
fleet.MDMDeliveryVerifying,
|
||||
fleet.MDMDeliveryVerified,
|
||||
mdm.ListFleetReservedWindowsProfileNames(),
|
||||
[]interface{}{fleet.MDMDeliveryVerifying, fleet.MDMDeliveryVerified},
|
||||
}
|
||||
return sql, args
|
||||
return sqlx.In(sql, args...)
|
||||
}
|
||||
|
||||
func subqueryHostsMDMWindowsOSSettingsStatusVerified() (string, []interface{}) {
|
||||
func subqueryHostsMDMWindowsOSSettingsStatusVerified() (string, []interface{}, error) {
|
||||
sql := `
|
||||
SELECT
|
||||
1 FROM host_mdm_windows_profiles hmwp
|
||||
|
|
@ -796,20 +805,24 @@ func subqueryHostsMDMWindowsOSSettingsStatusVerified() (string, []interface{}) {
|
|||
h.uuid = hmwp.host_uuid
|
||||
AND hmwp.operation_type = ?
|
||||
AND hmwp.status = ?
|
||||
AND hmwp.profile_name NOT IN(?)
|
||||
AND NOT EXISTS (
|
||||
SELECT
|
||||
1 FROM host_mdm_windows_profiles hmwp2
|
||||
WHERE (h.uuid = hmwp2.host_uuid
|
||||
AND hmwp2.operation_type = ?
|
||||
AND hmwp2.profile_name NOT IN(?)
|
||||
AND(hmwp2.status IS NULL
|
||||
OR hmwp2.status != ?)))`
|
||||
args := []interface{}{
|
||||
fleet.MDMOperationTypeInstall,
|
||||
fleet.MDMDeliveryVerified,
|
||||
mdm.ListFleetReservedWindowsProfileNames(),
|
||||
fleet.MDMOperationTypeInstall,
|
||||
mdm.ListFleetReservedWindowsProfileNames(),
|
||||
fleet.MDMDeliveryVerified,
|
||||
}
|
||||
return sql, args
|
||||
return sqlx.In(sql, args...)
|
||||
}
|
||||
|
||||
func (ds *Datastore) GetMDMWindowsProfilesSummary(ctx context.Context, teamID *uint) (*fleet.MDMProfilesSummary, error) {
|
||||
|
|
@ -856,13 +869,25 @@ type statusCounts struct {
|
|||
|
||||
func getMDMWindowsStatusCountsProfilesOnlyDB(ctx context.Context, ds *Datastore, teamID *uint) ([]statusCounts, error) {
|
||||
var args []interface{}
|
||||
subqueryFailed, subqueryFailedArgs := subqueryHostsMDMWindowsOSSettingsStatusFailed()
|
||||
subqueryFailed, subqueryFailedArgs, err := subqueryHostsMDMWindowsOSSettingsStatusFailed()
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "subqueryHostsMDMWindowsOSSettingsStatusFailed")
|
||||
}
|
||||
args = append(args, subqueryFailedArgs...)
|
||||
subqueryPending, subqueryPendingArgs := subqueryHostsMDMWindowsOSSettingsStatusPending()
|
||||
subqueryPending, subqueryPendingArgs, err := subqueryHostsMDMWindowsOSSettingsStatusPending()
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "subqueryHostsMDMWindowsOSSettingsStatusPending")
|
||||
}
|
||||
args = append(args, subqueryPendingArgs...)
|
||||
subqueryVerifying, subqueryVeryingingArgs := subqueryHostsMDMWindowsOSSettingsStatusVerifying()
|
||||
subqueryVerifying, subqueryVeryingingArgs, err := subqueryHostsMDMWindowsOSSettingsStatusVerifying()
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "subqueryHostsMDMWindowsOSSettingsStatusVerifying")
|
||||
}
|
||||
args = append(args, subqueryVeryingingArgs...)
|
||||
subqueryVerified, subqueryVerifiedArgs := subqueryHostsMDMWindowsOSSettingsStatusVerified()
|
||||
subqueryVerified, subqueryVerifiedArgs, err := subqueryHostsMDMWindowsOSSettingsStatusVerified()
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "subqueryHostsMDMWindowsOSSettingsStatusVerified")
|
||||
}
|
||||
args = append(args, subqueryVerifiedArgs...)
|
||||
|
||||
teamFilter := "h.team_id IS NULL"
|
||||
|
|
@ -907,7 +932,7 @@ GROUP BY
|
|||
)
|
||||
|
||||
var counts []statusCounts
|
||||
err := sqlx.SelectContext(ctx, ds.reader(ctx), &counts, stmt, args...)
|
||||
err = sqlx.SelectContext(ctx, ds.reader(ctx), &counts, stmt, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -916,13 +941,25 @@ GROUP BY
|
|||
|
||||
func getMDMWindowsStatusCountsProfilesAndBitLockerDB(ctx context.Context, ds *Datastore, teamID *uint) ([]statusCounts, error) {
|
||||
var args []interface{}
|
||||
subqueryFailed, subqueryFailedArgs := subqueryHostsMDMWindowsOSSettingsStatusFailed()
|
||||
subqueryFailed, subqueryFailedArgs, err := subqueryHostsMDMWindowsOSSettingsStatusFailed()
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "subqueryHostsMDMWindowsOSSettingsStatusFailed")
|
||||
}
|
||||
args = append(args, subqueryFailedArgs...)
|
||||
subqueryPending, subqueryPendingArgs := subqueryHostsMDMWindowsOSSettingsStatusPending()
|
||||
subqueryPending, subqueryPendingArgs, err := subqueryHostsMDMWindowsOSSettingsStatusPending()
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "subqueryHostsMDMWindowsOSSettingsStatusPending")
|
||||
}
|
||||
args = append(args, subqueryPendingArgs...)
|
||||
subqueryVerifying, subqueryVeryingingArgs := subqueryHostsMDMWindowsOSSettingsStatusVerifying()
|
||||
subqueryVerifying, subqueryVeryingingArgs, err := subqueryHostsMDMWindowsOSSettingsStatusVerifying()
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "subqueryHostsMDMWindowsOSSettingsStatusVerifying")
|
||||
}
|
||||
args = append(args, subqueryVeryingingArgs...)
|
||||
subqueryVerified, subqueryVerifiedArgs := subqueryHostsMDMWindowsOSSettingsStatusVerified()
|
||||
subqueryVerified, subqueryVerifiedArgs, err := subqueryHostsMDMWindowsOSSettingsStatusVerified()
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "subqueryHostsMDMWindowsOSSettingsStatusVerified")
|
||||
}
|
||||
args = append(args, subqueryVerifiedArgs...)
|
||||
|
||||
profilesStatus := fmt.Sprintf(`
|
||||
|
|
@ -1030,7 +1067,7 @@ GROUP BY
|
|||
)
|
||||
|
||||
var counts []statusCounts
|
||||
err := sqlx.SelectContext(ctx, ds.reader(ctx), &counts, stmt, args...)
|
||||
err = sqlx.SelectContext(ctx, ds.reader(ctx), &counts, stmt, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -1651,7 +1688,7 @@ SELECT
|
|||
FROM
|
||||
host_mdm_windows_profiles
|
||||
WHERE
|
||||
host_uuid = ? AND NOT (operation_type = '%s' AND COALESCE(status, '%s') IN('%s', '%s'))`,
|
||||
host_uuid = ? AND profile_name NOT IN(?) AND NOT (operation_type = '%s' AND COALESCE(status, '%s') IN('%s', '%s'))`,
|
||||
fleet.MDMDeliveryPending,
|
||||
fleet.MDMOperationTypeRemove,
|
||||
fleet.MDMDeliveryPending,
|
||||
|
|
@ -1659,8 +1696,13 @@ host_uuid = ? AND NOT (operation_type = '%s' AND COALESCE(status, '%s') IN('%s',
|
|||
fleet.MDMDeliveryVerified,
|
||||
)
|
||||
|
||||
stmt, args, err := sqlx.In(stmt, hostUUID, mdm.ListFleetReservedWindowsProfileNames())
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "building in statement")
|
||||
}
|
||||
|
||||
var profiles []fleet.HostMDMWindowsProfile
|
||||
if err := sqlx.SelectContext(ctx, ds.reader(ctx), &profiles, stmt, hostUUID); err != nil {
|
||||
if err := sqlx.SelectContext(ctx, ds.reader(ctx), &profiles, stmt, args...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return profiles, nil
|
||||
|
|
|
|||
|
|
@ -80,3 +80,9 @@ func FleetReservedProfileNames() map[string]struct{} {
|
|||
FleetWindowsOSUpdatesProfileName: {},
|
||||
}
|
||||
}
|
||||
|
||||
// ListFleetReservedWindowsProfileNames returns a list of PayloadDisplayName strings
|
||||
// that are reserved by Fleet for Windows.
|
||||
func ListFleetReservedWindowsProfileNames() []string {
|
||||
return []string{FleetWindowsOSUpdatesProfileName}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -259,6 +259,8 @@ func (s *integrationMDMTestSuite) TearDownTest() {
|
|||
appCfg.MDM.WindowsEnabledAndConfigured = true
|
||||
// ensure global disk encryption is disabled on exit
|
||||
appCfg.MDM.EnableDiskEncryption = optjson.SetBool(false)
|
||||
// ensure global Windows OS updates are always disabled for the next test
|
||||
appCfg.MDM.WindowsUpdates = mdm_types.WindowsUpdates{}
|
||||
err := s.ds.SaveAppConfig(ctx, &appCfg.AppConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
|
@ -10049,10 +10051,68 @@ func (s *integrationMDMTestSuite) TestWindowsProfileManagement() {
|
|||
require.NotNil(t, p.Status)
|
||||
require.Equal(t, wantStatus, *p.Status, "profile", p.Name)
|
||||
require.Equal(t, "windows", p.Platform)
|
||||
// Fleet reserved profiles (e.g., OS updates) should be screened from the host details response
|
||||
require.NotContains(t, servermdm.ListFleetReservedWindowsProfileNames(), p.Name)
|
||||
}
|
||||
require.ElementsMatch(t, wantProfs, gotProfs)
|
||||
}
|
||||
|
||||
checkHostsFilteredByOSSettingsStatus := func(t *testing.T, wantHosts []string, wantStatus fleet.MDMDeliveryStatus, teamID *uint, labels ...*fleet.Label) {
|
||||
var teamFilter string
|
||||
if teamID != nil {
|
||||
teamFilter = fmt.Sprintf("&team_id=%d", *teamID)
|
||||
}
|
||||
var gotHostsResp listHostsResponse
|
||||
s.DoJSON("GET", fmt.Sprintf("/api/v1/fleet/hosts?os_settings=%s%s", wantStatus, teamFilter), nil, http.StatusOK, &gotHostsResp)
|
||||
require.NotNil(t, gotHostsResp.Hosts)
|
||||
var gotHosts []string
|
||||
for _, h := range gotHostsResp.Hosts {
|
||||
gotHosts = append(gotHosts, h.Hostname)
|
||||
}
|
||||
require.ElementsMatch(t, wantHosts, gotHosts)
|
||||
|
||||
var countHostsResp countHostsResponse
|
||||
s.DoJSON("GET", fmt.Sprintf("/api/v1/fleet/hosts/count?os_settings=%s%s", wantStatus, teamFilter), nil, http.StatusOK, &countHostsResp)
|
||||
require.Equal(t, len(wantHosts), countHostsResp.Count)
|
||||
|
||||
for _, l := range labels {
|
||||
gotHostsResp = listHostsResponse{}
|
||||
s.DoJSON("GET", fmt.Sprintf("/api/v1/fleet/labels/%d/hosts?os_settings=%s%s", l.ID, wantStatus, teamFilter), nil, http.StatusOK, &gotHostsResp)
|
||||
require.NotNil(t, gotHostsResp.Hosts)
|
||||
gotHosts = []string{}
|
||||
for _, h := range gotHostsResp.Hosts {
|
||||
gotHosts = append(gotHosts, h.Hostname)
|
||||
}
|
||||
require.ElementsMatch(t, wantHosts, gotHosts, "label", l.Name)
|
||||
|
||||
countHostsResp = countHostsResponse{}
|
||||
s.DoJSON("GET", fmt.Sprintf("/api/v1/fleet/hosts/count?label_id=%d&os_settings=%s%s", l.ID, wantStatus, teamFilter), nil, http.StatusOK, &countHostsResp)
|
||||
}
|
||||
}
|
||||
|
||||
getProfileUUID := func(t *testing.T, profName string, teamID *uint) string {
|
||||
var profUUID string
|
||||
mysql.ExecAdhocSQL(t, s.ds, func(tx sqlx.ExtContext) error {
|
||||
var globalOrTeamID uint
|
||||
if teamID != nil {
|
||||
globalOrTeamID = *teamID
|
||||
}
|
||||
return sqlx.GetContext(ctx, tx, &profUUID, `SELECT profile_uuid FROM mdm_windows_configuration_profiles WHERE team_id = ? AND name = ?`, globalOrTeamID, profName)
|
||||
})
|
||||
require.NotNil(t, profUUID)
|
||||
return profUUID
|
||||
}
|
||||
|
||||
checkHostProfileStatus := func(t *testing.T, hostUUID string, profUUID string, wantStatus fleet.MDMDeliveryStatus) {
|
||||
var gotStatus fleet.MDMDeliveryStatus
|
||||
mysql.ExecAdhocSQL(t, s.ds, func(q sqlx.ExtContext) error {
|
||||
stmt := `SELECT status FROM host_mdm_windows_profiles WHERE host_uuid = ? AND profile_uuid = ?`
|
||||
err := sqlx.GetContext(context.Background(), q, &gotStatus, stmt, hostUUID, profUUID)
|
||||
return err
|
||||
})
|
||||
require.Equal(t, wantStatus, gotStatus)
|
||||
}
|
||||
|
||||
// Create a host and then enroll to MDM.
|
||||
host, mdmDevice := createWindowsHostThenEnrollMDM(s.ds, s.server.URL, t)
|
||||
// trigger a profile sync
|
||||
|
|
@ -10060,9 +10120,68 @@ func (s *integrationMDMTestSuite) TestWindowsProfileManagement() {
|
|||
checkHostsProfilesMatch(host, globalProfiles)
|
||||
checkHostDetails(t, host, globalProfiles, fleet.MDMDeliveryVerifying)
|
||||
|
||||
// create new label that includes host
|
||||
label := &fleet.Label{
|
||||
Name: t.Name() + "foo",
|
||||
Query: "select * from foo;",
|
||||
}
|
||||
label, err = s.ds.NewLabel(context.Background(), label)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, s.ds.RecordLabelQueryExecutions(ctx, host, map[uint]*bool{label.ID: ptr.Bool(true)}, time.Now(), false))
|
||||
|
||||
// simulate osquery reporting host mdm details (host_mdm.enrolled = 1 is condition for
|
||||
// hosts filtering by os settings status and generating mdm profiles summaries)
|
||||
require.NoError(t, s.ds.SetOrUpdateMDMData(ctx, host.ID, false, true, s.server.URL, false, fleet.WellKnownMDMFleet, ""))
|
||||
checkHostsFilteredByOSSettingsStatus(t, []string{host.Hostname}, fleet.MDMDeliveryVerifying, nil, label)
|
||||
s.checkMDMProfilesSummaries(t, nil, fleet.MDMProfilesSummary{
|
||||
Verifying: 1,
|
||||
}, nil)
|
||||
|
||||
// another sync shouldn't return profiles
|
||||
verifyProfiles(mdmDevice, 0, false)
|
||||
|
||||
// make fleet add a Windows OS Updates profile
|
||||
acResp := appConfigResponse{}
|
||||
s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{"mdm": { "windows_updates": {"deadline_days": 1, "grace_period_days": 1} }}`), http.StatusOK, &acResp)
|
||||
osUpdatesProf := getProfileUUID(t, servermdm.FleetWindowsOSUpdatesProfileName, nil)
|
||||
|
||||
// os updates is sent via a profiles commands
|
||||
verifyProfiles(mdmDevice, 1, false)
|
||||
checkHostsProfilesMatch(host, append(globalProfiles, osUpdatesProf))
|
||||
// but is hidden from host details response
|
||||
checkHostDetails(t, host, globalProfiles, fleet.MDMDeliveryVerifying)
|
||||
|
||||
// os updates profile status doesn't matter for filtered hosts results or summaries
|
||||
checkHostProfileStatus(t, host.UUID, osUpdatesProf, fleet.MDMDeliveryVerifying)
|
||||
checkHostsFilteredByOSSettingsStatus(t, []string{host.Hostname}, fleet.MDMDeliveryVerifying, nil, label)
|
||||
s.checkMDMProfilesSummaries(t, nil, fleet.MDMProfilesSummary{
|
||||
Verifying: 1,
|
||||
}, nil)
|
||||
// force os updates profile to failed, doesn't impact filtered hosts results or summaries
|
||||
mysql.ExecAdhocSQL(t, s.ds, func(q sqlx.ExtContext) error {
|
||||
stmt := `UPDATE host_mdm_windows_profiles SET status = 'failed' WHERE profile_uuid = ?`
|
||||
_, err := q.ExecContext(context.Background(), stmt, osUpdatesProf)
|
||||
return err
|
||||
})
|
||||
checkHostProfileStatus(t, host.UUID, osUpdatesProf, fleet.MDMDeliveryFailed)
|
||||
checkHostsFilteredByOSSettingsStatus(t, []string{host.Hostname}, fleet.MDMDeliveryVerifying, nil, label)
|
||||
s.checkMDMProfilesSummaries(t, nil, fleet.MDMProfilesSummary{
|
||||
Verifying: 1,
|
||||
}, nil)
|
||||
// force another profile to failed, does impact filtered hosts results and summaries
|
||||
mysql.ExecAdhocSQL(t, s.ds, func(q sqlx.ExtContext) error {
|
||||
stmt := `UPDATE host_mdm_windows_profiles SET status = 'failed' WHERE profile_uuid = ?`
|
||||
_, err := q.ExecContext(context.Background(), stmt, globalProfiles[0])
|
||||
return err
|
||||
})
|
||||
checkHostProfileStatus(t, host.UUID, globalProfiles[0], fleet.MDMDeliveryFailed)
|
||||
checkHostsFilteredByOSSettingsStatus(t, []string{}, fleet.MDMDeliveryVerifying, nil, label) // expect no hosts
|
||||
checkHostsFilteredByOSSettingsStatus(t, []string{host.Hostname}, fleet.MDMDeliveryFailed, nil, label) // expect host
|
||||
s.checkMDMProfilesSummaries(t, nil, fleet.MDMProfilesSummary{
|
||||
Failed: 1,
|
||||
Verifying: 0,
|
||||
}, nil)
|
||||
|
||||
// add the host to a team
|
||||
err = s.ds.AddHostsToTeam(ctx, &tm.ID, []uint{host.ID})
|
||||
require.NoError(t, err)
|
||||
|
|
@ -10115,6 +10234,48 @@ func (s *integrationMDMTestSuite) TestWindowsProfileManagement() {
|
|||
|
||||
// another sync shouldn't return profiles
|
||||
verifyProfiles(mdmDevice, 0, false)
|
||||
|
||||
// make fleet add a Windows OS Updates profile
|
||||
tmResp := teamResponse{}
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", tm.ID), json.RawMessage(`{"mdm": { "windows_updates": {"deadline_days": 1, "grace_period_days": 1} }}`), http.StatusOK, &tmResp)
|
||||
osUpdatesProf = getProfileUUID(t, servermdm.FleetWindowsOSUpdatesProfileName, &tm.ID)
|
||||
|
||||
// os updates is sent via a profiles commands
|
||||
verifyProfiles(mdmDevice, 1, false)
|
||||
checkHostsProfilesMatch(host, append(teamProfiles, osUpdatesProf))
|
||||
// but is hidden from host details response
|
||||
checkHostDetails(t, host, teamProfiles, fleet.MDMDeliveryVerifying)
|
||||
|
||||
// os updates profile status doesn't matter for filtered hosts results or summaries
|
||||
checkHostProfileStatus(t, host.UUID, osUpdatesProf, fleet.MDMDeliveryVerifying)
|
||||
checkHostsFilteredByOSSettingsStatus(t, []string{host.Hostname}, fleet.MDMDeliveryVerifying, &tm.ID, label)
|
||||
s.checkMDMProfilesSummaries(t, &tm.ID, fleet.MDMProfilesSummary{
|
||||
Verifying: 1,
|
||||
}, nil)
|
||||
// force os updates profile to failed, doesn't impact filtered hosts results or summaries
|
||||
mysql.ExecAdhocSQL(t, s.ds, func(q sqlx.ExtContext) error {
|
||||
stmt := `UPDATE host_mdm_windows_profiles SET status = 'failed' WHERE profile_uuid = ?`
|
||||
_, err := q.ExecContext(context.Background(), stmt, osUpdatesProf)
|
||||
return err
|
||||
})
|
||||
checkHostProfileStatus(t, host.UUID, osUpdatesProf, fleet.MDMDeliveryFailed)
|
||||
checkHostsFilteredByOSSettingsStatus(t, []string{host.Hostname}, fleet.MDMDeliveryVerifying, &tm.ID, label)
|
||||
s.checkMDMProfilesSummaries(t, &tm.ID, fleet.MDMProfilesSummary{
|
||||
Verifying: 1,
|
||||
}, nil)
|
||||
// force another profile to failed, does impact filtered hosts results and summaries
|
||||
mysql.ExecAdhocSQL(t, s.ds, func(q sqlx.ExtContext) error {
|
||||
stmt := `UPDATE host_mdm_windows_profiles SET status = 'failed' WHERE profile_uuid = ?`
|
||||
_, err := q.ExecContext(context.Background(), stmt, teamProfiles[0])
|
||||
return err
|
||||
})
|
||||
checkHostProfileStatus(t, host.UUID, teamProfiles[0], fleet.MDMDeliveryFailed)
|
||||
checkHostsFilteredByOSSettingsStatus(t, []string{}, fleet.MDMDeliveryVerifying, &tm.ID, label) // expect no hosts
|
||||
checkHostsFilteredByOSSettingsStatus(t, []string{host.Hostname}, fleet.MDMDeliveryFailed, &tm.ID, label) // expect host
|
||||
s.checkMDMProfilesSummaries(t, &tm.ID, fleet.MDMProfilesSummary{
|
||||
Failed: 1,
|
||||
Verifying: 0,
|
||||
}, nil)
|
||||
}
|
||||
|
||||
func (s *integrationMDMTestSuite) TestAppConfigMDMWindowsProfiles() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue