mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 08:55:24 +00:00
Mark "verifying" or "verified" MDM profiles as "failed" if osquery cannot confirm they are installed (#12414)
This commit is contained in:
parent
b754cb096c
commit
8cc7d38300
2
changes/issue-12330-mdm-verification-failed
Normal file
2
changes/issue-12330-mdm-verification-failed
Normal file
@ -0,0 +1,2 @@
|
||||
- Updated MDM detail query ingestion to switch MDM profiles from "verifying" or "verified"
|
||||
status to "failed" status when osquery reports that this profile is not installed on the host.
|
@ -1532,8 +1532,8 @@ func (ds *Datastore) BulkUpsertMDMAppleHostProfiles(ctx context.Context, payload
|
||||
var sb strings.Builder
|
||||
|
||||
for _, p := range payload {
|
||||
args = append(args, p.ProfileID, p.ProfileIdentifier, p.ProfileName, p.HostUUID, p.Status, p.OperationType, p.CommandUUID, p.Checksum)
|
||||
sb.WriteString("(?, ?, ?, ?, ?, ?, ?, ?),")
|
||||
args = append(args, p.ProfileID, p.ProfileIdentifier, p.ProfileName, p.HostUUID, p.Status, p.OperationType, p.Detail, p.CommandUUID, p.Checksum)
|
||||
sb.WriteString("(?, ?, ?, ?, ?, ?, ?, ?, ?),")
|
||||
}
|
||||
|
||||
stmt := fmt.Sprintf(`
|
||||
@ -1544,6 +1544,7 @@ func (ds *Datastore) BulkUpsertMDMAppleHostProfiles(ctx context.Context, payload
|
||||
host_uuid,
|
||||
status,
|
||||
operation_type,
|
||||
detail,
|
||||
command_uuid,
|
||||
checksum
|
||||
)
|
||||
@ -1551,6 +1552,7 @@ func (ds *Datastore) BulkUpsertMDMAppleHostProfiles(ctx context.Context, payload
|
||||
ON DUPLICATE KEY UPDATE
|
||||
status = VALUES(status),
|
||||
operation_type = VALUES(operation_type),
|
||||
detail = VALUES(detail),
|
||||
command_uuid = VALUES(command_uuid)`,
|
||||
strings.TrimSuffix(sb.String(), ","),
|
||||
)
|
||||
@ -1578,7 +1580,7 @@ func (ds *Datastore) UpdateOrDeleteHostMDMAppleProfile(ctx context.Context, prof
|
||||
return err
|
||||
}
|
||||
|
||||
func (ds *Datastore) SetVerifiedHostMacOSProfiles(ctx context.Context, host *fleet.Host, installedProfiles []*fleet.HostMacOSProfile) error {
|
||||
func (ds *Datastore) UpdateVerificationHostMacOSProfiles(ctx context.Context, host *fleet.Host, installedProfiles []*fleet.HostMacOSProfile) error {
|
||||
installedProfsByIdentifier := make(map[string]*fleet.HostMacOSProfile, len(installedProfiles))
|
||||
for _, p := range installedProfiles {
|
||||
installedProfsByIdentifier[p.Identifier] = p
|
||||
@ -1601,50 +1603,130 @@ WHERE
|
||||
return ctxerr.Wrap(ctx, err, "listing expected profiles to set verified host macOS profiles")
|
||||
}
|
||||
|
||||
verifiedIdentifiers := make([]string, 0, len(expectedProfs))
|
||||
foundIdentifiers := make([]string, 0, len(expectedProfs))
|
||||
missingIdentifiers := make([]string, 0, len(expectedProfs))
|
||||
|
||||
for _, ep := range expectedProfs {
|
||||
withinGracePeriod := ep.IsWithinGracePeriod(host.DetailUpdatedAt) // Note: The host detail timestamp is updated after the current set is ingested, see https://github.com/fleetdm/fleet/blob/e9fd28717d474668ca626efbacdd0615d42b2e0a/server/service/osquery.go#L950
|
||||
ip, ok := installedProfsByIdentifier[ep.Identifier]
|
||||
if !ok {
|
||||
// TODO: expected profile is not installed on host, skip it for now
|
||||
// expected profile is missing from host
|
||||
if !withinGracePeriod {
|
||||
missingIdentifiers = append(missingIdentifiers, ep.Identifier)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if ep.UpdatedAt.After(ip.InstallDate) {
|
||||
// TODO: host has an older version of expected profile installed, skip it for now
|
||||
// TODO: host has an older version of expected profile installed, treat it as a missing
|
||||
// profile for now but we should think about an appropriate grace period to account for
|
||||
// clock skew between the host and the server or a checksum comparison instead
|
||||
if !withinGracePeriod {
|
||||
missingIdentifiers = append(missingIdentifiers, ep.Identifier)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if ep.Name != ip.DisplayName {
|
||||
// TODO: host has a different name for expected profile, skip it for now
|
||||
// TODO: host has a different name for expected profile, treat it as a missing profile
|
||||
// for now but we should think about a checksum comparison instead
|
||||
if !withinGracePeriod {
|
||||
missingIdentifiers = append(missingIdentifiers, ep.Identifier)
|
||||
}
|
||||
continue
|
||||
}
|
||||
verifiedIdentifiers = append(verifiedIdentifiers, ep.Identifier)
|
||||
foundIdentifiers = append(foundIdentifiers, ep.Identifier)
|
||||
}
|
||||
|
||||
if len(verifiedIdentifiers) == 0 {
|
||||
if len(foundIdentifiers) == 0 && len(missingIdentifiers) == 0 {
|
||||
// nothing to update, return early
|
||||
return nil
|
||||
}
|
||||
|
||||
return ds.withRetryTxx(ctx, func(tx sqlx.ExtContext) error {
|
||||
if err := setMDMProfilesVerifiedDB(ctx, tx, host, foundIdentifiers); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := setMDMProfilesFailedDB(ctx, tx, host, missingIdentifiers); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// setMDMProfilesFailedDB sets the status of the given identifiers to failed if the current status
|
||||
// is verifying or verified. It also sets the detail to a message indicating that the profile was
|
||||
// either verifying or verified. Only profiles with the install operation type are updated.
|
||||
func setMDMProfilesFailedDB(ctx context.Context, tx sqlx.ExtContext, host *fleet.Host, identifiers []string) error {
|
||||
if len(identifiers) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
stmt := `
|
||||
UPDATE
|
||||
host_mdm_apple_profiles
|
||||
SET
|
||||
detail = if(status = ?, ?, ?),
|
||||
status = ?
|
||||
WHERE
|
||||
host_uuid = ?
|
||||
AND status IN(?)
|
||||
AND operation_type = ?
|
||||
AND profile_identifier IN(?)`
|
||||
|
||||
args := []interface{}{
|
||||
fleet.MDMAppleDeliveryVerifying,
|
||||
fleet.HostMDMProfileDetailFailedWasVerifying,
|
||||
fleet.HostMDMProfileDetailFailedWasVerified,
|
||||
fleet.MDMAppleDeliveryFailed,
|
||||
host.UUID,
|
||||
[]interface{}{fleet.MDMAppleDeliveryVerifying, fleet.MDMAppleDeliveryVerified},
|
||||
fleet.MDMAppleOperationTypeInstall,
|
||||
identifiers,
|
||||
}
|
||||
stmt, args, err := sqlx.In(stmt, args...)
|
||||
if err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "building sql statement to set failed host macOS profiles")
|
||||
}
|
||||
|
||||
if _, err := tx.ExecContext(ctx, stmt, args...); err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "setting failed host macOS profiles")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// setMDMProfilesVerifiedDB sets the status of the given identifiers to verified if the current
|
||||
// status is verifying. Only profiles with the install operation type are updated.
|
||||
func setMDMProfilesVerifiedDB(ctx context.Context, tx sqlx.ExtContext, host *fleet.Host, identifiers []string) error {
|
||||
if len(identifiers) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
stmt := `
|
||||
UPDATE
|
||||
host_mdm_apple_profiles
|
||||
SET
|
||||
detail = '',
|
||||
status = ?
|
||||
WHERE
|
||||
host_uuid = ?
|
||||
AND status = ?
|
||||
AND operation_type = 'install'
|
||||
AND operation_type = ?
|
||||
AND profile_identifier IN(?)`
|
||||
|
||||
args := []interface{}{fleet.MDMAppleDeliveryVerified, host.UUID, fleet.MDMAppleDeliveryVerifying, verifiedIdentifiers}
|
||||
args := []interface{}{
|
||||
fleet.MDMAppleDeliveryVerified,
|
||||
host.UUID,
|
||||
fleet.MDMAppleDeliveryVerifying,
|
||||
fleet.MDMAppleOperationTypeInstall,
|
||||
identifiers,
|
||||
}
|
||||
stmt, args, err := sqlx.In(stmt, args...)
|
||||
if err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "building sql statement to set verified host macOS profiles")
|
||||
}
|
||||
|
||||
if _, err := ds.writer(ctx).ExecContext(ctx, stmt, args...); err != nil {
|
||||
if _, err := tx.ExecContext(ctx, stmt, args...); err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "setting verified host macOS profiles")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"crypto/sha256"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"testing"
|
||||
@ -1236,8 +1237,8 @@ func testMDMAppleProfileManagement(t *testing.T, ds *Datastore) {
|
||||
{Identifier: globalPfs[1].Identifier, DisplayName: globalPfs[1].Name, InstallDate: time.Now()},
|
||||
{Identifier: globalPfs[2].Identifier, DisplayName: globalPfs[2].Name, InstallDate: time.Now()},
|
||||
}
|
||||
require.NoError(t, ds.SetVerifiedHostMacOSProfiles(ctx, host1, verified))
|
||||
require.NoError(t, ds.SetVerifiedHostMacOSProfiles(ctx, host3, verified))
|
||||
require.NoError(t, ds.UpdateVerificationHostMacOSProfiles(ctx, host1, verified))
|
||||
require.NoError(t, ds.UpdateVerificationHostMacOSProfiles(ctx, host3, verified))
|
||||
|
||||
// still no profiles to install
|
||||
profiles, err = ds.ListMDMAppleProfilesToInstall(ctx)
|
||||
@ -1552,7 +1553,7 @@ func testAggregateMacOSSettingsStatusWithFileVault(t *testing.T, ds *Datastore)
|
||||
require.Equal(t, uint(0), res.Verified)
|
||||
|
||||
// upsert hosts[0] filevault to verified
|
||||
require.NoError(t, ds.SetVerifiedHostMacOSProfiles(ctx, hosts[0], []*fleet.HostMacOSProfile{{Identifier: fvNoTeam.Identifier, DisplayName: fvNoTeam.Name, InstallDate: time.Now()}}))
|
||||
require.NoError(t, ds.UpdateVerificationHostMacOSProfiles(ctx, hosts[0], []*fleet.HostMacOSProfile{{Identifier: fvNoTeam.Identifier, DisplayName: fvNoTeam.Name, InstallDate: time.Now()}}))
|
||||
res, err = ds.GetMDMAppleHostsProfilesSummary(ctx, nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res)
|
||||
@ -3849,7 +3850,7 @@ func testSetVerifiedMacOSProfiles(t *testing.T, ds *Datastore) {
|
||||
var hosts []*fleet.Host
|
||||
for i := 0; i < 3; i++ {
|
||||
h := test.NewHost(t, ds, fmt.Sprintf("foo.local.%d", i), "1.1.1.1",
|
||||
fmt.Sprintf("%d", i), fmt.Sprintf("%d", i), time.Now())
|
||||
fmt.Sprintf("%d", i), fmt.Sprintf("%d", i), time.Now().Add(-1*time.Hour))
|
||||
hosts = append(hosts, h)
|
||||
expectedHostMDMStatus[h.ID] = map[string]fleet.MDMAppleDeliveryStatus{
|
||||
cp1.Identifier: fleet.MDMAppleDeliveryPending,
|
||||
@ -3884,13 +3885,13 @@ func testSetVerifiedMacOSProfiles(t *testing.T, ds *Datastore) {
|
||||
upsertHostCPs(hosts, []*fleet.MDMAppleConfigProfile{storedByIdentifier[cp3.Identifier]}, fleet.MDMAppleOperationTypeInstall, &fleet.MDMAppleDeliveryVerified, ctx, ds, t)
|
||||
checkHostMDMProfileStatuses()
|
||||
|
||||
// statuses don't change if profiles are missing (i.e. not installed)
|
||||
require.NoError(t, ds.SetVerifiedHostMacOSProfiles(ctx, hosts[0], []*fleet.HostMacOSProfile{}))
|
||||
// statuses don't change during the grace period if profiles are missing (i.e. not installed)
|
||||
require.NoError(t, ds.UpdateVerificationHostMacOSProfiles(ctx, hosts[0], []*fleet.HostMacOSProfile{}))
|
||||
checkHostMDMProfileStatuses()
|
||||
|
||||
// only "verifying" status can change to "verified" so status of cp1 doesn't change (it
|
||||
// remains "pending")
|
||||
require.NoError(t, ds.SetVerifiedHostMacOSProfiles(ctx, hosts[0], []*fleet.HostMacOSProfile{
|
||||
require.NoError(t, ds.UpdateVerificationHostMacOSProfiles(ctx, hosts[0], []*fleet.HostMacOSProfile{
|
||||
{
|
||||
Identifier: cp1.Identifier,
|
||||
DisplayName: cp1.Name,
|
||||
@ -3900,7 +3901,8 @@ func testSetVerifiedMacOSProfiles(t *testing.T, ds *Datastore) {
|
||||
checkHostMDMProfileStatuses()
|
||||
|
||||
// if install date is before the updated at timestamp of the profile, statuses don't change
|
||||
require.NoError(t, ds.SetVerifiedHostMacOSProfiles(ctx, hosts[1], []*fleet.HostMacOSProfile{
|
||||
// during the grace period
|
||||
require.NoError(t, ds.UpdateVerificationHostMacOSProfiles(ctx, hosts[1], []*fleet.HostMacOSProfile{
|
||||
{
|
||||
Identifier: cp1.Identifier,
|
||||
DisplayName: cp1.Name,
|
||||
@ -3921,7 +3923,7 @@ func testSetVerifiedMacOSProfiles(t *testing.T, ds *Datastore) {
|
||||
|
||||
// if install date is on or after the updated at timestamp of the profile, "verifying" status
|
||||
// changes to "verified"
|
||||
require.NoError(t, ds.SetVerifiedHostMacOSProfiles(ctx, hosts[2], []*fleet.HostMacOSProfile{
|
||||
require.NoError(t, ds.UpdateVerificationHostMacOSProfiles(ctx, hosts[2], []*fleet.HostMacOSProfile{
|
||||
{
|
||||
Identifier: cp1.Identifier,
|
||||
DisplayName: cp1.Name,
|
||||
@ -3942,7 +3944,7 @@ func testSetVerifiedMacOSProfiles(t *testing.T, ds *Datastore) {
|
||||
checkHostMDMProfileStatuses()
|
||||
|
||||
// repeated call doesn't change statuses
|
||||
require.NoError(t, ds.SetVerifiedHostMacOSProfiles(ctx, hosts[2], []*fleet.HostMacOSProfile{
|
||||
require.NoError(t, ds.UpdateVerificationHostMacOSProfiles(ctx, hosts[2], []*fleet.HostMacOSProfile{
|
||||
{
|
||||
Identifier: cp1.Identifier,
|
||||
DisplayName: cp1.Name,
|
||||
@ -3960,6 +3962,49 @@ func testSetVerifiedMacOSProfiles(t *testing.T, ds *Datastore) {
|
||||
},
|
||||
}))
|
||||
checkHostMDMProfileStatuses()
|
||||
|
||||
// simulate expired grace period by setting updated_at timestamp of profiles back by 24 hours
|
||||
ExecAdhocSQL(t, ds, func(tx sqlx.ExtContext) error {
|
||||
_, err := tx.ExecContext(ctx,
|
||||
`UPDATE mdm_apple_configuration_profiles SET updated_at = ? WHERE profile_id IN(?, ?, ?)`,
|
||||
time.Now().Add(-24*time.Hour),
|
||||
cp1.ProfileID, cp2.ProfileID, cp3.ProfileID,
|
||||
)
|
||||
return err
|
||||
})
|
||||
|
||||
// after the grace period, status changes to "failed" if a profile is missing (i.e. not installed)
|
||||
require.NoError(t, ds.UpdateVerificationHostMacOSProfiles(ctx, hosts[2], []*fleet.HostMacOSProfile{
|
||||
{
|
||||
Identifier: cp1.Identifier,
|
||||
DisplayName: cp1.Name,
|
||||
InstallDate: time.Now(),
|
||||
},
|
||||
{
|
||||
Identifier: cp2.Identifier,
|
||||
DisplayName: cp2.Name,
|
||||
InstallDate: time.Now(),
|
||||
},
|
||||
}))
|
||||
expectedHostMDMStatus[hosts[2].ID][cp3.Identifier] = fleet.MDMAppleDeliveryFailed // cp3 is missing
|
||||
checkHostMDMProfileStatuses()
|
||||
|
||||
// after the grace period, status changes to "failed" if a profile is outdated (i.e. installed
|
||||
// before the updated at timestamp of the profile)
|
||||
require.NoError(t, ds.UpdateVerificationHostMacOSProfiles(ctx, hosts[2], []*fleet.HostMacOSProfile{
|
||||
{
|
||||
Identifier: cp1.Identifier,
|
||||
DisplayName: cp1.Name,
|
||||
InstallDate: time.Now(),
|
||||
},
|
||||
{
|
||||
Identifier: cp2.Identifier,
|
||||
DisplayName: cp2.Name,
|
||||
InstallDate: time.Now().Add(-48 * time.Hour),
|
||||
},
|
||||
}))
|
||||
expectedHostMDMStatus[hosts[2].ID][cp2.Identifier] = fleet.MDMAppleDeliveryFailed // cp2 is outdated
|
||||
checkHostMDMProfileStatuses()
|
||||
}
|
||||
|
||||
func TestHostDEPAssignments(t *testing.T) {
|
||||
@ -4461,3 +4506,307 @@ func testMDMAppleDeleteHostDEPAssignments(t *testing.T, ds *Datastore) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMDMProfileVerification(t *testing.T) {
|
||||
ds := CreateMySQLDS(t)
|
||||
ctx := context.Background()
|
||||
|
||||
now := time.Now()
|
||||
twoMinutesAgo := now.Add(-2 * time.Minute)
|
||||
twoHoursAgo := now.Add(-2 * time.Hour)
|
||||
twoDaysAgo := now.Add(-2 * 24 * time.Hour)
|
||||
|
||||
type testCase struct {
|
||||
name string
|
||||
initialStatus fleet.MDMAppleDeliveryStatus
|
||||
expectedStatus fleet.MDMAppleDeliveryStatus
|
||||
expectedDetail string
|
||||
}
|
||||
|
||||
setupTestProfile := func(t *testing.T, suffix string) *fleet.MDMAppleConfigProfile {
|
||||
cp, err := ds.NewMDMAppleConfigProfile(ctx, *configProfileForTest(t,
|
||||
fmt.Sprintf("name-test-profile-%s", suffix),
|
||||
fmt.Sprintf("identifier-test-profile-%s", suffix),
|
||||
fmt.Sprintf("uuid-test-profile-%s", suffix)))
|
||||
require.NoError(t, err)
|
||||
return cp
|
||||
}
|
||||
|
||||
setProfileUpdatedAt := func(t *testing.T, cp *fleet.MDMAppleConfigProfile, ua time.Time) {
|
||||
ExecAdhocSQL(t, ds, func(tx sqlx.ExtContext) error {
|
||||
_, err := tx.ExecContext(ctx, `UPDATE mdm_apple_configuration_profiles SET updated_at = ? WHERE profile_id = ?`, ua, cp.ProfileID)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
checkHostStatus := func(t *testing.T, h *fleet.Host, expectedStatus fleet.MDMAppleDeliveryStatus, expectedDetail string) error {
|
||||
gotProfs, err := ds.GetHostMDMProfiles(ctx, h.UUID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(gotProfs) != 1 {
|
||||
return errors.New("expected exactly one profile")
|
||||
}
|
||||
if gotProfs[0].Status == nil {
|
||||
return errors.New("expected status to be non-nil")
|
||||
}
|
||||
if *gotProfs[0].Status != expectedStatus {
|
||||
return fmt.Errorf("expected status %s, got %s", expectedStatus, *gotProfs[0].Status)
|
||||
}
|
||||
if gotProfs[0].Detail != expectedDetail {
|
||||
return fmt.Errorf("expected detail %s, got %s", expectedDetail, gotProfs[0].Detail)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
t.Run("MissingProfile", func(t *testing.T) {
|
||||
// missing profile, verifying and verified statuses should change to failed after the grace period
|
||||
cases := []testCase{
|
||||
{
|
||||
name: "PendingThenMissing",
|
||||
initialStatus: fleet.MDMAppleDeliveryPending,
|
||||
expectedStatus: fleet.MDMAppleDeliveryPending, // no change
|
||||
expectedDetail: "",
|
||||
},
|
||||
{
|
||||
name: "VerifyingThenMissing",
|
||||
initialStatus: fleet.MDMAppleDeliveryVerifying,
|
||||
expectedStatus: fleet.MDMAppleDeliveryFailed, // change to failed
|
||||
expectedDetail: string(fleet.HostMDMProfileDetailFailedWasVerifying),
|
||||
},
|
||||
{
|
||||
name: "VerifiedThenMissing",
|
||||
initialStatus: fleet.MDMAppleDeliveryVerified,
|
||||
expectedStatus: fleet.MDMAppleDeliveryFailed, // change to failed
|
||||
expectedDetail: string(fleet.HostMDMProfileDetailFailedWasVerified),
|
||||
},
|
||||
{
|
||||
name: "FailedThenMissing",
|
||||
initialStatus: fleet.MDMAppleDeliveryFailed,
|
||||
expectedStatus: fleet.MDMAppleDeliveryFailed, // no change
|
||||
expectedDetail: "",
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range cases {
|
||||
// setup
|
||||
h := test.NewHost(t, ds, tc.name, tc.name, tc.name, tc.name, twoMinutesAgo)
|
||||
cp := setupTestProfile(t, fmt.Sprintf("%s-%d", tc.name, i))
|
||||
var reportedProfiles []*fleet.HostMacOSProfile // no profiles reported for this test
|
||||
|
||||
// initialize
|
||||
upsertHostCPs([]*fleet.Host{h}, []*fleet.MDMAppleConfigProfile{cp}, fleet.MDMAppleOperationTypeInstall, &tc.initialStatus, ctx, ds, t)
|
||||
require.NoError(t, checkHostStatus(t, h, tc.initialStatus, ""))
|
||||
|
||||
// within grace period
|
||||
setProfileUpdatedAt(t, cp, twoMinutesAgo)
|
||||
require.NoError(t, ds.UpdateVerificationHostMacOSProfiles(ctx, h, reportedProfiles))
|
||||
require.NoError(t, checkHostStatus(t, h, tc.initialStatus, "")) // if missing within grace period, no change
|
||||
|
||||
// reinitialize
|
||||
upsertHostCPs([]*fleet.Host{h}, []*fleet.MDMAppleConfigProfile{cp}, fleet.MDMAppleOperationTypeInstall, &tc.initialStatus, ctx, ds, t)
|
||||
require.NoError(t, checkHostStatus(t, h, tc.initialStatus, ""))
|
||||
|
||||
// outside grace period
|
||||
setProfileUpdatedAt(t, cp, twoHoursAgo)
|
||||
require.NoError(t, ds.UpdateVerificationHostMacOSProfiles(ctx, h, reportedProfiles))
|
||||
require.NoError(t, checkHostStatus(t, h, tc.expectedStatus, tc.expectedDetail)) // grace period expired, check expected status
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("OutdatedProfile", func(t *testing.T) {
|
||||
// found profile with the expected identifier, but it's outdated (i.e. the install date is
|
||||
// before the last update date) so treat it as missing the expected profile verifying and
|
||||
// verified statuses should change to failed after the grace period)
|
||||
cases := []testCase{
|
||||
{
|
||||
name: "PendingThenFoundOutdated",
|
||||
initialStatus: fleet.MDMAppleDeliveryPending,
|
||||
expectedStatus: fleet.MDMAppleDeliveryPending, // no change
|
||||
expectedDetail: "",
|
||||
},
|
||||
{
|
||||
name: "VerifyingThenFoundOutdated",
|
||||
initialStatus: fleet.MDMAppleDeliveryVerifying,
|
||||
expectedStatus: fleet.MDMAppleDeliveryFailed, // change to failed
|
||||
expectedDetail: string(fleet.HostMDMProfileDetailFailedWasVerifying),
|
||||
},
|
||||
{
|
||||
name: "VerifiedThenFoundOutdated",
|
||||
initialStatus: fleet.MDMAppleDeliveryVerified,
|
||||
expectedStatus: fleet.MDMAppleDeliveryFailed, // change to failed
|
||||
expectedDetail: string(fleet.HostMDMProfileDetailFailedWasVerified),
|
||||
},
|
||||
{
|
||||
name: "FailedThenFoundOutdated",
|
||||
initialStatus: fleet.MDMAppleDeliveryFailed,
|
||||
expectedStatus: fleet.MDMAppleDeliveryFailed, // no change
|
||||
expectedDetail: "",
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// setup
|
||||
h := test.NewHost(t, ds, tc.name, tc.name, tc.name, tc.name, twoMinutesAgo)
|
||||
cp := setupTestProfile(t, fmt.Sprintf("%s-%d", tc.name, i))
|
||||
reportedProfiles := []*fleet.HostMacOSProfile{
|
||||
{
|
||||
DisplayName: cp.Name,
|
||||
Identifier: cp.Identifier,
|
||||
InstallDate: twoDaysAgo,
|
||||
},
|
||||
}
|
||||
|
||||
// initialize
|
||||
upsertHostCPs([]*fleet.Host{h}, []*fleet.MDMAppleConfigProfile{cp}, fleet.MDMAppleOperationTypeInstall, &tc.initialStatus, ctx, ds, t)
|
||||
require.NoError(t, checkHostStatus(t, h, tc.initialStatus, ""))
|
||||
|
||||
// within grace period
|
||||
setProfileUpdatedAt(t, cp, twoMinutesAgo)
|
||||
require.NoError(t, ds.UpdateVerificationHostMacOSProfiles(ctx, h, reportedProfiles))
|
||||
require.NoError(t, checkHostStatus(t, h, tc.initialStatus, "")) // outdated profiles are treated similar to missing profiles so status doesn't change if within grace period
|
||||
|
||||
// reinitalize
|
||||
upsertHostCPs([]*fleet.Host{h}, []*fleet.MDMAppleConfigProfile{cp}, fleet.MDMAppleOperationTypeInstall, &tc.initialStatus, ctx, ds, t)
|
||||
require.NoError(t, checkHostStatus(t, h, tc.initialStatus, ""))
|
||||
|
||||
// outside grace period
|
||||
setProfileUpdatedAt(t, cp, twoHoursAgo)
|
||||
require.NoError(t, ds.UpdateVerificationHostMacOSProfiles(ctx, h, reportedProfiles))
|
||||
require.NoError(t, checkHostStatus(t, h, tc.expectedStatus, tc.expectedDetail)) // grace period expired, check expected status
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ExpectedProfile", func(t *testing.T) {
|
||||
// happy path, expected profile found so verifying should change to verified
|
||||
cases := []testCase{
|
||||
{
|
||||
name: "PendingThenFoundExpected",
|
||||
initialStatus: fleet.MDMAppleDeliveryPending,
|
||||
expectedStatus: fleet.MDMAppleDeliveryPending, // no change
|
||||
expectedDetail: "",
|
||||
},
|
||||
{
|
||||
name: "VerifyingThenFoundExpected",
|
||||
initialStatus: fleet.MDMAppleDeliveryVerifying,
|
||||
expectedStatus: fleet.MDMAppleDeliveryVerified, // change to verified
|
||||
expectedDetail: "",
|
||||
},
|
||||
{
|
||||
name: "VerifiedThenFoundExpected",
|
||||
initialStatus: fleet.MDMAppleDeliveryVerified,
|
||||
expectedStatus: fleet.MDMAppleDeliveryVerified, // no change
|
||||
expectedDetail: "",
|
||||
},
|
||||
{
|
||||
name: "FailedThenFoundExpected",
|
||||
initialStatus: fleet.MDMAppleDeliveryFailed,
|
||||
expectedStatus: fleet.MDMAppleDeliveryFailed, // no change
|
||||
expectedDetail: "",
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// setup
|
||||
h := test.NewHost(t, ds, tc.name, tc.name, tc.name, tc.name, twoMinutesAgo)
|
||||
cp := setupTestProfile(t, fmt.Sprintf("%s-%d", tc.name, i))
|
||||
reportedProfiles := []*fleet.HostMacOSProfile{
|
||||
{
|
||||
DisplayName: cp.Name,
|
||||
Identifier: cp.Identifier,
|
||||
InstallDate: now,
|
||||
},
|
||||
}
|
||||
|
||||
// initialize
|
||||
upsertHostCPs([]*fleet.Host{h}, []*fleet.MDMAppleConfigProfile{cp}, fleet.MDMAppleOperationTypeInstall, &tc.initialStatus, ctx, ds, t)
|
||||
require.NoError(t, checkHostStatus(t, h, tc.initialStatus, ""))
|
||||
|
||||
// within grace period
|
||||
setProfileUpdatedAt(t, cp, twoMinutesAgo)
|
||||
require.NoError(t, ds.UpdateVerificationHostMacOSProfiles(ctx, h, reportedProfiles))
|
||||
require.NoError(t, checkHostStatus(t, h, tc.expectedStatus, tc.expectedDetail)) // if found within grace period, verifying status can become verified so check expected status
|
||||
|
||||
// reinitialize
|
||||
upsertHostCPs([]*fleet.Host{h}, []*fleet.MDMAppleConfigProfile{cp}, fleet.MDMAppleOperationTypeInstall, &tc.initialStatus, ctx, ds, t)
|
||||
require.NoError(t, checkHostStatus(t, h, tc.initialStatus, ""))
|
||||
|
||||
// outside grace period
|
||||
setProfileUpdatedAt(t, cp, twoHoursAgo)
|
||||
require.NoError(t, ds.UpdateVerificationHostMacOSProfiles(ctx, h, reportedProfiles))
|
||||
require.NoError(t, checkHostStatus(t, h, tc.expectedStatus, tc.expectedDetail)) // grace period expired, check expected status
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("UnexpectedProfile", func(t *testing.T) {
|
||||
// unexpected profile is ignored and doesn't change status of existing profile
|
||||
cases := []testCase{
|
||||
{
|
||||
name: "PendingThenFoundExpectedAndUnexpected",
|
||||
initialStatus: fleet.MDMAppleDeliveryPending,
|
||||
expectedStatus: fleet.MDMAppleDeliveryPending, // no change
|
||||
expectedDetail: "",
|
||||
},
|
||||
{
|
||||
name: "VerifyingThenFoundExpectedAndUnexpected",
|
||||
initialStatus: fleet.MDMAppleDeliveryVerifying,
|
||||
expectedStatus: fleet.MDMAppleDeliveryVerified, // no change
|
||||
expectedDetail: "",
|
||||
},
|
||||
{
|
||||
name: "VerifiedThenFounExpectedAnddUnexpected",
|
||||
initialStatus: fleet.MDMAppleDeliveryVerified,
|
||||
expectedStatus: fleet.MDMAppleDeliveryVerified, // no change
|
||||
expectedDetail: "",
|
||||
},
|
||||
{
|
||||
name: "FailedThenFoundExpectedAndUnexpected",
|
||||
initialStatus: fleet.MDMAppleDeliveryFailed,
|
||||
expectedStatus: fleet.MDMAppleDeliveryFailed, // no change
|
||||
expectedDetail: "",
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// setup
|
||||
h := test.NewHost(t, ds, tc.name, tc.name, tc.name, tc.name, twoMinutesAgo)
|
||||
cp := setupTestProfile(t, fmt.Sprintf("%s-%d", tc.name, i))
|
||||
reportedProfiles := []*fleet.HostMacOSProfile{
|
||||
{
|
||||
DisplayName: "unexpected-name",
|
||||
Identifier: "unexpected-identifier",
|
||||
InstallDate: now,
|
||||
},
|
||||
{
|
||||
DisplayName: cp.Name,
|
||||
Identifier: cp.Identifier,
|
||||
InstallDate: now,
|
||||
},
|
||||
}
|
||||
|
||||
// initialize
|
||||
upsertHostCPs([]*fleet.Host{h}, []*fleet.MDMAppleConfigProfile{cp}, fleet.MDMAppleOperationTypeInstall, &tc.initialStatus, ctx, ds, t)
|
||||
require.NoError(t, checkHostStatus(t, h, tc.initialStatus, ""))
|
||||
|
||||
// within grace period
|
||||
setProfileUpdatedAt(t, cp, twoMinutesAgo)
|
||||
require.NoError(t, ds.UpdateVerificationHostMacOSProfiles(ctx, h, reportedProfiles))
|
||||
require.NoError(t, checkHostStatus(t, h, tc.expectedStatus, tc.expectedDetail)) // if found within grace period, verifying status can become verified so check expected status
|
||||
|
||||
// reinitialize
|
||||
upsertHostCPs([]*fleet.Host{h}, []*fleet.MDMAppleConfigProfile{cp}, fleet.MDMAppleOperationTypeInstall, &tc.initialStatus, ctx, ds, t)
|
||||
require.NoError(t, checkHostStatus(t, h, tc.initialStatus, ""))
|
||||
|
||||
// outside grace period
|
||||
setProfileUpdatedAt(t, cp, twoHoursAgo)
|
||||
require.NoError(t, ds.UpdateVerificationHostMacOSProfiles(ctx, h, reportedProfiles))
|
||||
require.NoError(t, checkHostStatus(t, h, tc.expectedStatus, tc.expectedDetail)) // grace period expired, check expected status
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -322,6 +322,18 @@ func (cp MDMAppleConfigProfile) ValidateUserProvided() error {
|
||||
return cp.Mobileconfig.ScreenPayloads()
|
||||
}
|
||||
|
||||
// IsWithinGracePeriod returns true if the host is within the grace period for the profile.
|
||||
//
|
||||
// The grace period is defined as 1 hour after the profile was updated. It is checked against the
|
||||
// host's detail_updated_at timestamp to allow for the host to check in at least once before the
|
||||
// profile is considered failed. If the host is online, it should report detail queries hourly by
|
||||
// default. If the host is offline, it should report detail queries shortly after it comes back
|
||||
// online.
|
||||
func (cp MDMAppleConfigProfile) IsWithinGracePeriod(hostDetailUpdatedAt time.Time) bool {
|
||||
gracePeriod := 1 * time.Hour
|
||||
return hostDetailUpdatedAt.Before(cp.UpdatedAt.Add(gracePeriod))
|
||||
}
|
||||
|
||||
// HostMDMAppleProfile represents the status of an Apple MDM profile in a host.
|
||||
type HostMDMAppleProfile struct {
|
||||
HostUUID string `db:"host_uuid" json:"-"`
|
||||
@ -345,6 +357,25 @@ func (p HostMDMAppleProfile) IgnoreMDMClientError() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type HostMDMProfileDetail string
|
||||
|
||||
const (
|
||||
HostMDMProfileDetailFailedWasVerified HostMDMProfileDetail = "Failed, was verified"
|
||||
HostMDMProfileDetailFailedWasVerifying HostMDMProfileDetail = "Failed, was verifying"
|
||||
)
|
||||
|
||||
// Message returns a human-friendly message for the detail.
|
||||
func (d HostMDMProfileDetail) Message() string {
|
||||
switch d {
|
||||
case HostMDMProfileDetailFailedWasVerified:
|
||||
return "This setting had been verified by osquery, but has since been found missing on the host."
|
||||
case HostMDMProfileDetailFailedWasVerifying:
|
||||
return "The MDM protocol returned a success but the setting couldn’t be verified by osquery."
|
||||
default:
|
||||
return string(d)
|
||||
}
|
||||
}
|
||||
|
||||
type MDMAppleProfilePayload struct {
|
||||
ProfileID uint `db:"profile_id"`
|
||||
ProfileIdentifier string `db:"profile_identifier"`
|
||||
@ -361,6 +392,7 @@ type MDMAppleBulkUpsertHostProfilePayload struct {
|
||||
CommandUUID string
|
||||
OperationType MDMAppleOperationType
|
||||
Status *MDMAppleDeliveryStatus
|
||||
Detail string
|
||||
Checksum []byte
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package fleet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
@ -326,3 +327,50 @@ func TestHostDEPAssignment(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMDMProfileIsWithinGracePeriod(t *testing.T) {
|
||||
// create a test profile
|
||||
var b bytes.Buffer
|
||||
params := mobileconfig.FleetdProfileOptions{
|
||||
EnrollSecret: t.Name(),
|
||||
ServerURL: "https://example.com",
|
||||
PayloadType: mobileconfig.FleetdConfigPayloadIdentifier,
|
||||
}
|
||||
err := mobileconfig.FleetdProfileTemplate.Execute(&b, params)
|
||||
require.NoError(t, err)
|
||||
testProfile, err := NewMDMAppleConfigProfile(b.Bytes(), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// set profile updated at 2 hours ago
|
||||
testProfile.UpdatedAt = time.Now().Truncate(time.Second).Add(-2 * time.Hour)
|
||||
// set profile created at 24 hours ago (irrelevant but included for completeness)
|
||||
testProfile.CreatedAt = testProfile.UpdatedAt.Add(-24 * time.Hour)
|
||||
|
||||
cases := []struct {
|
||||
testName string
|
||||
hostDetailUpdatedAt time.Time
|
||||
expect bool
|
||||
}{
|
||||
{
|
||||
testName: "outside grace period",
|
||||
hostDetailUpdatedAt: testProfile.UpdatedAt.Add(61 * time.Minute), // more than 1 hour grace period
|
||||
expect: false,
|
||||
},
|
||||
{
|
||||
testName: "online host within grace period",
|
||||
hostDetailUpdatedAt: testProfile.UpdatedAt.Add(59 * time.Minute), // less than 1 hour grace period
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
testName: "offline host within grace period",
|
||||
hostDetailUpdatedAt: testProfile.UpdatedAt.Add(-48 * time.Hour), // grace period doesn't start until host is online (i.e. host detail updated at is after profile updated at)
|
||||
expect: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.testName, func(t *testing.T) {
|
||||
require.Equal(t, c.expect, testProfile.IsWithinGracePeriod(c.hostDetailUpdatedAt))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -698,8 +698,8 @@ type Datastore interface {
|
||||
|
||||
SetDiskEncryptionResetStatus(ctx context.Context, hostID uint, status bool) error
|
||||
|
||||
// SetVerifiedHostMacOSProfiles updates status of macOS profiles installed on a given host to verified.
|
||||
SetVerifiedHostMacOSProfiles(ctx context.Context, host *Host, installedProfiles []*HostMacOSProfile) error
|
||||
// UpdateVerificationHostMacOSProfiles updates status of macOS profiles installed on a given host to verified.
|
||||
UpdateVerificationHostMacOSProfiles(ctx context.Context, host *Host, installedProfiles []*HostMacOSProfile) error
|
||||
|
||||
// SetOrUpdateHostOrbitInfo inserts of updates the orbit info for a host
|
||||
SetOrUpdateHostOrbitInfo(ctx context.Context, hostID uint, version string) error
|
||||
|
@ -486,7 +486,7 @@ type GetHostDiskEncryptionKeyFunc func(ctx context.Context, hostID uint) (*fleet
|
||||
|
||||
type SetDiskEncryptionResetStatusFunc func(ctx context.Context, hostID uint, status bool) error
|
||||
|
||||
type SetVerifiedHostMacOSProfilesFunc func(ctx context.Context, host *fleet.Host, installedProfiles []*fleet.HostMacOSProfile) error
|
||||
type UpdateVerificationHostMacOSProfilesFunc func(ctx context.Context, host *fleet.Host, installedProfiles []*fleet.HostMacOSProfile) error
|
||||
|
||||
type SetOrUpdateHostOrbitInfoFunc func(ctx context.Context, hostID uint, version string) error
|
||||
|
||||
@ -1352,8 +1352,8 @@ type DataStore struct {
|
||||
SetDiskEncryptionResetStatusFunc SetDiskEncryptionResetStatusFunc
|
||||
SetDiskEncryptionResetStatusFuncInvoked bool
|
||||
|
||||
SetVerifiedHostMacOSProfilesFunc SetVerifiedHostMacOSProfilesFunc
|
||||
SetVerifiedHostMacOSProfilesFuncInvoked bool
|
||||
UpdateVerificationHostMacOSProfilesFunc UpdateVerificationHostMacOSProfilesFunc
|
||||
UpdateVerificationHostMacOSProfilesFuncInvoked bool
|
||||
|
||||
SetOrUpdateHostOrbitInfoFunc SetOrUpdateHostOrbitInfoFunc
|
||||
SetOrUpdateHostOrbitInfoFuncInvoked bool
|
||||
@ -3240,11 +3240,11 @@ func (s *DataStore) SetDiskEncryptionResetStatus(ctx context.Context, hostID uin
|
||||
return s.SetDiskEncryptionResetStatusFunc(ctx, hostID, status)
|
||||
}
|
||||
|
||||
func (s *DataStore) SetVerifiedHostMacOSProfiles(ctx context.Context, host *fleet.Host, installedProfiles []*fleet.HostMacOSProfile) error {
|
||||
func (s *DataStore) UpdateVerificationHostMacOSProfiles(ctx context.Context, host *fleet.Host, installedProfiles []*fleet.HostMacOSProfile) error {
|
||||
s.mu.Lock()
|
||||
s.SetVerifiedHostMacOSProfilesFuncInvoked = true
|
||||
s.UpdateVerificationHostMacOSProfilesFuncInvoked = true
|
||||
s.mu.Unlock()
|
||||
return s.SetVerifiedHostMacOSProfilesFunc(ctx, host, installedProfiles)
|
||||
return s.UpdateVerificationHostMacOSProfilesFunc(ctx, host, installedProfiles)
|
||||
}
|
||||
|
||||
func (s *DataStore) SetOrUpdateHostOrbitInfo(ctx context.Context, hostID uint, version string) error {
|
||||
|
@ -899,13 +899,14 @@ func (svc *Service) getHostDetails(ctx context.Context, host *fleet.Host, opts f
|
||||
policies = &hp
|
||||
}
|
||||
|
||||
// If Fleet MDM is enabled and configured, we want to include MDM profiles
|
||||
// and host's disk encryption status.
|
||||
var profiles []fleet.HostMDMAppleProfile
|
||||
// If Fleet MDM is enabled and configured, we want to include MDM profiles,
|
||||
// disk encryption status, and macOS setup details.
|
||||
ac, err := svc.ds.AppConfig(ctx)
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "get app config for host mdm profiles")
|
||||
return nil, ctxerr.Wrap(ctx, err, "get app config for host mdm details")
|
||||
}
|
||||
|
||||
var profiles []fleet.HostMDMAppleProfile
|
||||
if ac.MDM.EnabledAndConfigured {
|
||||
profs, err := svc.ds.GetHostMDMProfiles(ctx, host.UUID)
|
||||
if err != nil {
|
||||
@ -920,6 +921,7 @@ func (svc *Service) getHostDetails(ctx context.Context, host *fleet.Host, opts f
|
||||
if p.Identifier == mobileconfig.FleetFileVaultPayloadIdentifier {
|
||||
p.Status = host.MDM.ProfileStatusFromDiskEncryptionState(p.Status)
|
||||
}
|
||||
p.Detail = fleet.HostMDMProfileDetail(p.Detail).Message()
|
||||
profiles = append(profiles, p)
|
||||
}
|
||||
}
|
||||
|
@ -981,3 +981,102 @@ func TestHostEncryptionKey(t *testing.T) {
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestHostMDMProfileDetail(t *testing.T) {
|
||||
ds := new(mock.Store)
|
||||
testBMToken := &nanodep_client.OAuth1Tokens{
|
||||
ConsumerKey: "test_consumer",
|
||||
ConsumerSecret: "test_secret",
|
||||
AccessToken: "test_access_token",
|
||||
AccessSecret: "test_access_secret",
|
||||
AccessTokenExpiry: time.Date(2999, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
}
|
||||
testCert, testKey, err := apple_mdm.NewSCEPCACertKey()
|
||||
require.NoError(t, err)
|
||||
testCertPEM := tokenpki.PEMCertificate(testCert.Raw)
|
||||
testKeyPEM := tokenpki.PEMRSAPrivateKey(testKey)
|
||||
|
||||
fleetCfg := config.TestConfig()
|
||||
config.SetTestMDMConfig(t, &fleetCfg, testCertPEM, testKeyPEM, testBMToken)
|
||||
|
||||
svc, ctx := newTestServiceWithConfig(t, ds, fleetCfg, nil, nil)
|
||||
ctx = test.UserContext(ctx, test.UserAdmin)
|
||||
|
||||
ds.HostFunc = func(ctx context.Context, id uint) (*fleet.Host, error) {
|
||||
return &fleet.Host{
|
||||
ID: 1,
|
||||
}, nil
|
||||
}
|
||||
ds.LoadHostSoftwareFunc = func(ctx context.Context, host *fleet.Host, includeCVEScores bool) error {
|
||||
return nil
|
||||
}
|
||||
ds.ListLabelsForHostFunc = func(ctx context.Context, hid uint) ([]*fleet.Label, error) {
|
||||
return nil, nil
|
||||
}
|
||||
ds.ListPacksForHostFunc = func(ctx context.Context, hid uint) ([]*fleet.Pack, error) {
|
||||
return nil, nil
|
||||
}
|
||||
ds.ListHostBatteriesFunc = func(ctx context.Context, hid uint) ([]*fleet.HostBattery, error) {
|
||||
return nil, nil
|
||||
}
|
||||
ds.GetHostMDMMacOSSetupFunc = func(ctx context.Context, hid uint) (*fleet.HostMDMMacOSSetup, error) {
|
||||
return nil, nil
|
||||
}
|
||||
ds.AppConfigFunc = func(ctx context.Context) (*fleet.AppConfig, error) {
|
||||
return &fleet.AppConfig{
|
||||
MDM: fleet.MDM{
|
||||
EnabledAndConfigured: true,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
storedDetail string
|
||||
expectedDetail string
|
||||
}{
|
||||
{
|
||||
name: "no detail",
|
||||
storedDetail: "",
|
||||
expectedDetail: "",
|
||||
},
|
||||
{
|
||||
name: "other detail",
|
||||
storedDetail: "other detail",
|
||||
expectedDetail: "other detail",
|
||||
},
|
||||
{
|
||||
name: "failed was verifying",
|
||||
storedDetail: string(fleet.HostMDMProfileDetailFailedWasVerifying),
|
||||
expectedDetail: fleet.HostMDMProfileDetailFailedWasVerifying.Message(),
|
||||
},
|
||||
{
|
||||
name: "failed was verified",
|
||||
storedDetail: string(fleet.HostMDMProfileDetailFailedWasVerified),
|
||||
expectedDetail: fleet.HostMDMProfileDetailFailedWasVerified.Message(),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range cases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ds.GetHostMDMProfilesFunc = func(ctx context.Context, host_uuid string) ([]fleet.HostMDMAppleProfile, error) {
|
||||
return []fleet.HostMDMAppleProfile{
|
||||
{
|
||||
Name: "test",
|
||||
Identifier: "test",
|
||||
OperationType: fleet.MDMAppleOperationTypeInstall,
|
||||
Status: &fleet.MDMAppleDeliveryFailed,
|
||||
Detail: tt.storedDetail,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
h, err := svc.GetHost(ctx, uint(1), fleet.HostDetailOptions{})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, h.MDM.Profiles)
|
||||
profs := *h.MDM.Profiles
|
||||
require.Len(t, profs, 1)
|
||||
require.Equal(t, tt.expectedDetail, profs[0].Detail)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -2110,7 +2110,7 @@ func (s *integrationMDMTestSuite) TestMDMAppleDiskEncryptionAggregate() {
|
||||
require.Equal(t, uint(0), fvsResp.RemovingEnforcement)
|
||||
|
||||
// verified status for host 1
|
||||
require.NoError(t, s.ds.SetVerifiedHostMacOSProfiles(ctx, hosts[0], []*fleet.HostMacOSProfile{{Identifier: prof.Identifier, DisplayName: prof.Name, InstallDate: time.Now()}}))
|
||||
require.NoError(t, s.ds.UpdateVerificationHostMacOSProfiles(ctx, hosts[0], []*fleet.HostMacOSProfile{{Identifier: prof.Identifier, DisplayName: prof.Name, InstallDate: time.Now()}}))
|
||||
fvsResp = getMDMAppleFileVauleSummaryResponse{}
|
||||
s.DoJSON("GET", "/api/latest/fleet/mdm/apple/filevault/summary", nil, http.StatusOK, &fvsResp, "team_id", strconv.Itoa(int(tm.ID)))
|
||||
require.Equal(t, uint(2), fvsResp.Verifying)
|
||||
@ -2971,7 +2971,7 @@ func (s *integrationMDMTestSuite) TestHostMDMProfilesStatus() {
|
||||
})
|
||||
|
||||
// h1 verified one of the profiles
|
||||
require.NoError(t, s.ds.SetVerifiedHostMacOSProfiles(context.Background(), h1, []*fleet.HostMacOSProfile{
|
||||
require.NoError(t, s.ds.UpdateVerificationHostMacOSProfiles(context.Background(), h1, []*fleet.HostMacOSProfile{
|
||||
{Identifier: "G2b", DisplayName: "G2b", InstallDate: time.Now()},
|
||||
}))
|
||||
s.assertHostConfigProfiles(map[*fleet.Host][]fleet.HostMDMAppleProfile{
|
||||
|
@ -1521,7 +1521,7 @@ func directIngestMacOSProfiles(
|
||||
})
|
||||
}
|
||||
|
||||
return ds.SetVerifiedHostMacOSProfiles(ctx, host, mapping)
|
||||
return ds.UpdateVerificationHostMacOSProfiles(ctx, host, mapping)
|
||||
}
|
||||
|
||||
//go:generate go run gen_queries_doc.go ../../../docs/Using-Fleet/Detail-Queries-Summary.md
|
||||
|
@ -1124,7 +1124,7 @@ func TestDirectIngestHostMacOSProfiles(t *testing.T) {
|
||||
h := &fleet.Host{ID: 1}
|
||||
|
||||
var expectedProfiles []*fleet.HostMacOSProfile
|
||||
ds.SetVerifiedHostMacOSProfilesFunc = func(ctx context.Context, host *fleet.Host, installedProfiles []*fleet.HostMacOSProfile) error {
|
||||
ds.UpdateVerificationHostMacOSProfilesFunc = func(ctx context.Context, host *fleet.Host, installedProfiles []*fleet.HostMacOSProfile) error {
|
||||
require.Equal(t, h.ID, host.ID)
|
||||
require.Len(t, installedProfiles, len(expectedProfiles))
|
||||
expectedByIdentifier := make(map[string]*fleet.HostMacOSProfile, len(expectedProfiles))
|
||||
|
Loading…
Reference in New Issue
Block a user