mirror of
https://github.com/fleetdm/fleet
synced 2026-04-21 21:47:20 +00:00
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #33418 Demo video: https://www.youtube.com/watch?v=gtsIYxmIOSo Docs: https://github.com/fleetdm/fleet/pull/42269/changes # Checklist for submitter - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/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 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Windows profiles now send SyncML <Delete> commands when profiles are removed or hosts change teams, ensuring profile settings are removed from devices like on macOS. * Deletion is handled as a two-phase flow: pending removals are enqueued and tracked instead of being immediately deleted. * **Tests** * Added/updated tests for delete-command generation, remove-status mappings, and end-to-end removal reconciliation. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
9515 lines
321 KiB
Go
9515 lines
321 KiB
Go
package mysql
|
||
|
||
import (
|
||
"context"
|
||
"crypto/x509"
|
||
"crypto/x509/pkix"
|
||
"database/sql"
|
||
"encoding/json"
|
||
"fmt"
|
||
"sort"
|
||
"strconv"
|
||
"strings"
|
||
"testing"
|
||
"time"
|
||
|
||
"github.com/fleetdm/fleet/v4/server/fleet"
|
||
mdm_types "github.com/fleetdm/fleet/v4/server/mdm"
|
||
"github.com/fleetdm/fleet/v4/server/mdm/apple/mobileconfig"
|
||
microsoft_mdm "github.com/fleetdm/fleet/v4/server/mdm/microsoft"
|
||
"github.com/fleetdm/fleet/v4/server/mdm/nanomdm/mdm"
|
||
"github.com/fleetdm/fleet/v4/server/mdm/nanomdm/service/certauth"
|
||
"github.com/fleetdm/fleet/v4/server/ptr"
|
||
"github.com/fleetdm/fleet/v4/server/test"
|
||
"github.com/google/uuid"
|
||
"github.com/jmoiron/sqlx"
|
||
"github.com/stretchr/testify/assert"
|
||
"github.com/stretchr/testify/require"
|
||
)
|
||
|
||
func TestMDMShared(t *testing.T) {
|
||
ds := CreateMySQLDS(t)
|
||
TruncateTables(t, ds)
|
||
|
||
cases := []struct {
|
||
name string
|
||
fn func(t *testing.T, ds *Datastore)
|
||
}{
|
||
{"TestMDMCommands", testMDMCommands},
|
||
{"TestListMDMCommandsWithTeamFilter", testListMDMCommandsWithTeamFilter},
|
||
{"TestBatchSetMDMProfiles", testBatchSetMDMProfiles},
|
||
{"TestListMDMConfigProfiles", testListMDMConfigProfiles},
|
||
{"TestBulkSetPendingMDMHostProfiles", testBulkSetPendingMDMHostProfiles},
|
||
{"TestBulkSetPendingMDMHostProfilesBatch2", testBulkSetPendingMDMHostProfilesBatch2},
|
||
{"TestBulkSetPendingMDMHostProfilesBatch3", testBulkSetPendingMDMHostProfilesBatch3},
|
||
{"TestGetHostMDMProfilesExpectedForVerification", testGetHostMDMProfilesExpectedForVerification},
|
||
{"TestBatchSetProfileLabelAssociations", testBatchSetProfileLabelAssociations},
|
||
{"TestMDMEULA", testMDMEULA},
|
||
{"TestGetHostCertAssociationsToExpire", testSCEPRenewalHelpers},
|
||
{"TestSCEPRenewalHelpers", testSCEPRenewalHelpers},
|
||
{"TestMDMProfilesSummaryAndHostFilters", testMDMProfilesSummaryAndHostFilters},
|
||
{"TestIsHostConnectedToFleetMDM", testIsHostConnectedToFleetMDM},
|
||
{"TestAreHostsConnectedToFleetMDM", testAreHostsConnectedToFleetMDM},
|
||
{"TestBulkSetPendingMDMHostProfilesExcludeAny", testBulkSetPendingMDMHostProfilesExcludeAny},
|
||
{"TestBulkSetPendingMDMHostProfilesLotsOfHosts", testBulkSetPendingMDMWindowsHostProfilesLotsOfHosts},
|
||
{"TestBatchResendProfileToHosts", testBatchResendProfileToHosts},
|
||
{"TestGetMDMConfigProfileStatus", testGetMDMConfigProfileStatus},
|
||
{"TestDeleteMDMProfilesCancelsInstalls", testDeleteMDMProfilesCancelsInstalls},
|
||
{"TestDeleteTeamCancelsWindowsProfileInstalls", testDeleteTeamCancelsWindowsProfileInstalls},
|
||
{"TestCleanUpMDMManagedCertificates", testCleanUpMDMManagedCertificates},
|
||
}
|
||
|
||
for _, c := range cases {
|
||
t.Run(c.name, func(t *testing.T) {
|
||
defer TruncateTables(t, ds)
|
||
c.fn(t, ds)
|
||
})
|
||
}
|
||
}
|
||
|
||
func testMDMCommands(t *testing.T, ds *Datastore) {
|
||
ctx := context.Background()
|
||
|
||
// no commands or devices enrolled => no results
|
||
cmds, _, _, err := ds.ListMDMCommands(ctx, fleet.TeamFilter{}, &fleet.MDMCommandListOptions{})
|
||
require.NoError(t, err)
|
||
require.Empty(t, cmds)
|
||
|
||
// enroll a windows device
|
||
windowsH, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: "test-host", // ambiguous hostname shared with macOS host
|
||
OsqueryHostID: ptr.String("osquery-windows"),
|
||
NodeKey: ptr.String("node-key-windows"),
|
||
UUID: uuid.NewString(),
|
||
Platform: "windows",
|
||
HardwareSerial: "123456",
|
||
})
|
||
require.NoError(t, err)
|
||
windowsEnrollment := &fleet.MDMWindowsEnrolledDevice{
|
||
MDMDeviceID: uuid.New().String(),
|
||
MDMHardwareID: uuid.New().String() + uuid.New().String(),
|
||
MDMDeviceState: uuid.New().String(),
|
||
MDMDeviceType: "CIMClient_Windows",
|
||
MDMDeviceName: "DESKTOP-1C3ARC1",
|
||
MDMEnrollType: "ProgrammaticEnrollment",
|
||
MDMEnrollUserID: "",
|
||
MDMEnrollProtoVersion: "5.0",
|
||
MDMEnrollClientVersion: "10.0.19045.2965",
|
||
MDMNotInOOBE: false,
|
||
HostUUID: windowsH.UUID,
|
||
}
|
||
err = ds.MDMWindowsInsertEnrolledDevice(ctx, windowsEnrollment)
|
||
require.NoError(t, err)
|
||
_, err = ds.UpdateMDMWindowsEnrollmentsHostUUID(
|
||
ctx,
|
||
windowsEnrollment.HostUUID,
|
||
windowsEnrollment.MDMDeviceID,
|
||
)
|
||
require.NoError(t, err)
|
||
windowsEnrollment, err = ds.MDMWindowsGetEnrolledDeviceWithDeviceID(
|
||
ctx,
|
||
windowsEnrollment.MDMDeviceID,
|
||
)
|
||
require.NoError(t, err)
|
||
|
||
// enroll a macOS device
|
||
macH, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: "test-host", // ambiguous hostname shared with windows host
|
||
OsqueryHostID: ptr.String("osquery-macos"),
|
||
NodeKey: ptr.String("node-key-macos"),
|
||
UUID: uuid.NewString(),
|
||
Platform: "darwin",
|
||
HardwareSerial: "654321",
|
||
})
|
||
require.NoError(t, err)
|
||
nanoEnroll(t, ds, macH, false)
|
||
|
||
// no commands => no results
|
||
cmds, _, _, err = ds.ListMDMCommands(
|
||
ctx,
|
||
fleet.TeamFilter{User: test.UserAdmin},
|
||
&fleet.MDMCommandListOptions{},
|
||
)
|
||
require.NoError(t, err)
|
||
require.Empty(t, cmds)
|
||
|
||
// insert a windows command
|
||
winCmd := &fleet.MDMWindowsCommand{
|
||
CommandUUID: uuid.NewString(),
|
||
RawCommand: []byte("<Exec></Exec>"),
|
||
TargetLocURI: "./test/uri",
|
||
}
|
||
err = ds.MDMWindowsInsertCommandForHosts(ctx, []string{windowsEnrollment.MDMDeviceID}, winCmd)
|
||
require.NoError(t, err)
|
||
|
||
// we get one result
|
||
cmds, total, _, err := ds.ListMDMCommands(
|
||
ctx,
|
||
fleet.TeamFilter{User: test.UserAdmin},
|
||
&fleet.MDMCommandListOptions{},
|
||
)
|
||
require.NoError(t, err)
|
||
require.Len(t, cmds, 1)
|
||
require.Equal(t, winCmd.CommandUUID, cmds[0].CommandUUID)
|
||
require.Equal(t, winCmd.TargetLocURI, cmds[0].RequestType)
|
||
require.Equal(t, "101", cmds[0].Status)
|
||
require.Nil(t, total)
|
||
|
||
appleCmdUUID := uuid.New().String()
|
||
appleCmd := createRawAppleCmd("ProfileList", appleCmdUUID)
|
||
commander, appleCommanderStorage := createMDMAppleCommanderAndStorage(t, ds)
|
||
err = commander.EnqueueCommand(ctx, []string{macH.UUID}, appleCmd)
|
||
require.NoError(t, err)
|
||
|
||
// we get both commands
|
||
cmds, total, _, err = ds.ListMDMCommands(
|
||
ctx,
|
||
fleet.TeamFilter{User: test.UserAdmin},
|
||
&fleet.MDMCommandListOptions{
|
||
ListOptions: fleet.ListOptions{OrderKey: "hostname"},
|
||
})
|
||
require.NoError(t, err)
|
||
require.Len(t, cmds, 2)
|
||
require.Equal(t, appleCmdUUID, cmds[0].CommandUUID)
|
||
require.Equal(t, "ProfileList", cmds[0].RequestType)
|
||
require.Equal(t, "Pending", cmds[0].Status)
|
||
require.Equal(t, winCmd.CommandUUID, cmds[1].CommandUUID)
|
||
require.Equal(t, winCmd.TargetLocURI, cmds[1].RequestType)
|
||
require.Equal(t, "101", cmds[1].Status)
|
||
require.Nil(t, total)
|
||
|
||
// store results for both commands
|
||
err = appleCommanderStorage.StoreCommandReport(&mdm.Request{
|
||
EnrollID: &mdm.EnrollID{ID: macH.UUID},
|
||
Context: ctx,
|
||
}, &mdm.CommandResults{
|
||
CommandUUID: appleCmdUUID,
|
||
Status: "Acknowledged",
|
||
Raw: []byte(appleCmd),
|
||
})
|
||
require.NoError(t, err)
|
||
|
||
err = ds.MDMWindowsSaveResponse(ctx, windowsEnrollment.MDMDeviceID, fleet.EnrichedSyncML{
|
||
SyncML: &fleet.SyncML{
|
||
Raw: []byte("<xml></xml>"),
|
||
},
|
||
CmdRefUUIDToStatus: map[string]fleet.SyncMLCmd{winCmd.CommandUUID: {
|
||
Data: ptr.String("200"),
|
||
}},
|
||
CmdRefUUIDs: []string{winCmd.CommandUUID},
|
||
}, []string{})
|
||
require.NoError(t, err)
|
||
|
||
// we get both commands
|
||
cmds, total, _, err = ds.ListMDMCommands(
|
||
ctx,
|
||
fleet.TeamFilter{User: test.UserAdmin},
|
||
&fleet.MDMCommandListOptions{
|
||
ListOptions: fleet.ListOptions{OrderKey: "hostname"},
|
||
})
|
||
require.NoError(t, err)
|
||
require.Len(t, cmds, 2)
|
||
require.Equal(t, appleCmdUUID, cmds[0].CommandUUID)
|
||
require.Equal(t, "ProfileList", cmds[0].RequestType)
|
||
require.Equal(t, "Acknowledged", cmds[0].Status)
|
||
require.Equal(t, winCmd.CommandUUID, cmds[1].CommandUUID)
|
||
require.Equal(t, winCmd.TargetLocURI, cmds[1].RequestType)
|
||
require.Equal(t, "200", cmds[1].Status)
|
||
require.Nil(t, total)
|
||
|
||
// add more windows commands
|
||
winCmd2 := &fleet.MDMWindowsCommand{
|
||
CommandUUID: uuid.NewString(),
|
||
RawCommand: []byte("<Exec></Exec>"),
|
||
TargetLocURI: "./test/uri2",
|
||
}
|
||
winCmd3 := &fleet.MDMWindowsCommand{
|
||
CommandUUID: uuid.NewString(),
|
||
RawCommand: []byte("<Exec></Exec>"),
|
||
TargetLocURI: "./test/uri3",
|
||
}
|
||
err = ds.MDMWindowsInsertCommandForHosts(ctx, []string{windowsEnrollment.MDMDeviceID}, winCmd2)
|
||
require.NoError(t, err)
|
||
err = ds.MDMWindowsInsertCommandForHosts(ctx, []string{windowsEnrollment.MDMDeviceID}, winCmd3)
|
||
require.NoError(t, err)
|
||
|
||
// add more macos commands
|
||
appleCmdUUID2 := uuid.New().String()
|
||
appleCmd2 := createRawAppleCmd("InstallProfile", appleCmdUUID2)
|
||
err = commander.EnqueueCommand(ctx, []string{macH.UUID}, appleCmd2)
|
||
require.NoError(t, err)
|
||
|
||
appleCmdUUID3 := uuid.New().String()
|
||
appleCmd3 := createRawAppleCmd("RemoveProfile", appleCmdUUID3)
|
||
err = commander.EnqueueCommand(ctx, []string{macH.UUID}, appleCmd3)
|
||
require.NoError(t, err)
|
||
|
||
// non-existent host identifier
|
||
cmds, _, _, err = ds.ListMDMCommands(
|
||
ctx,
|
||
fleet.TeamFilter{User: test.UserAdmin},
|
||
&fleet.MDMCommandListOptions{
|
||
Filters: fleet.MDMCommandFilters{
|
||
HostIdentifier: "non-existent",
|
||
},
|
||
},
|
||
)
|
||
require.NoError(t, err)
|
||
require.Len(t, cmds, 0)
|
||
|
||
// non-existent request type
|
||
cmds, _, _, err = ds.ListMDMCommands(
|
||
ctx,
|
||
fleet.TeamFilter{User: test.UserAdmin},
|
||
&fleet.MDMCommandListOptions{
|
||
Filters: fleet.MDMCommandFilters{
|
||
RequestType: "non-existent",
|
||
},
|
||
},
|
||
)
|
||
require.NoError(t, err)
|
||
require.Len(t, cmds, 0)
|
||
|
||
for _, tc := range []struct {
|
||
name string
|
||
identifier string
|
||
commandStatus *fleet.MDMCommandStatusFilter
|
||
expected []string
|
||
requestType string
|
||
}{
|
||
{
|
||
name: "windows host by hostname ambiguous with macOS host",
|
||
identifier: windowsH.Hostname,
|
||
expected: []string{
|
||
winCmd.CommandUUID, winCmd2.CommandUUID, winCmd3.CommandUUID,
|
||
appleCmdUUID, appleCmdUUID2, appleCmdUUID3,
|
||
},
|
||
},
|
||
{
|
||
name: "windows host by UUID",
|
||
identifier: windowsH.UUID,
|
||
expected: []string{winCmd.CommandUUID, winCmd2.CommandUUID, winCmd3.CommandUUID},
|
||
},
|
||
{
|
||
name: "windows host by UUID, filter by request type",
|
||
identifier: windowsH.UUID,
|
||
expected: []string{winCmd2.CommandUUID},
|
||
requestType: "./test/uri2",
|
||
},
|
||
{
|
||
name: "windows host by hardware serial",
|
||
identifier: windowsH.HardwareSerial,
|
||
expected: []string{winCmd.CommandUUID, winCmd2.CommandUUID, winCmd3.CommandUUID},
|
||
},
|
||
{
|
||
name: "macOS host by hostname ambiguous with windows host",
|
||
identifier: macH.Hostname,
|
||
expected: []string{
|
||
appleCmdUUID, appleCmdUUID2, appleCmdUUID3,
|
||
winCmd.CommandUUID, winCmd2.CommandUUID, winCmd3.CommandUUID,
|
||
},
|
||
},
|
||
{
|
||
name: "macOS host by UUID",
|
||
identifier: macH.UUID,
|
||
commandStatus: ptr.T(fleet.MDMCommandStatusFilterPending),
|
||
expected: []string{appleCmdUUID2, appleCmdUUID3},
|
||
},
|
||
{
|
||
name: "macOS host by hardware serial",
|
||
identifier: macH.HardwareSerial,
|
||
expected: []string{appleCmdUUID, appleCmdUUID2, appleCmdUUID3},
|
||
},
|
||
} {
|
||
t.Run(tc.name, func(t *testing.T) {
|
||
commandStatuses := []fleet.MDMCommandStatusFilter{}
|
||
if tc.commandStatus != nil {
|
||
commandStatuses = append(commandStatuses, *tc.commandStatus)
|
||
}
|
||
cmds, total, _, err := ds.ListMDMCommands(
|
||
ctx,
|
||
fleet.TeamFilter{User: test.UserAdmin},
|
||
&fleet.MDMCommandListOptions{
|
||
Filters: fleet.MDMCommandFilters{
|
||
HostIdentifier: tc.identifier,
|
||
CommandStatuses: commandStatuses,
|
||
RequestType: tc.requestType,
|
||
},
|
||
},
|
||
)
|
||
|
||
require.NoError(t, err)
|
||
require.Len(t, cmds, len(tc.expected))
|
||
var got []string
|
||
for _, cmd := range cmds {
|
||
got = append(got, cmd.CommandUUID)
|
||
}
|
||
require.ElementsMatch(t, tc.expected, got)
|
||
|
||
if tc.commandStatus != nil && *tc.commandStatus == fleet.MDMCommandStatusFilterPending {
|
||
require.Equal(t, int64(len(tc.expected)), *total)
|
||
} else {
|
||
require.Nil(t, total)
|
||
}
|
||
})
|
||
}
|
||
|
||
// add macos host
|
||
macH2, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: "macos-test-2",
|
||
OsqueryHostID: ptr.String("osquery-macos-2"),
|
||
NodeKey: ptr.String("node-key-macos-2"),
|
||
UUID: uuid.NewString(),
|
||
Platform: "darwin",
|
||
HardwareSerial: "654322",
|
||
})
|
||
require.NoError(t, err)
|
||
nanoEnroll(t, ds, macH2, false)
|
||
|
||
// add more macos commands
|
||
appleCmdUUID4 := uuid.New().String()
|
||
appleCmd4 := createRawAppleCmd("InstallProfile", appleCmdUUID4)
|
||
err = commander.EnqueueCommand(ctx, []string{macH2.UUID}, appleCmd4)
|
||
require.NoError(t, err)
|
||
|
||
// filter by request_type
|
||
cmds, _, _, err = ds.ListMDMCommands(
|
||
ctx,
|
||
fleet.TeamFilter{User: test.UserAdmin},
|
||
&fleet.MDMCommandListOptions{
|
||
Filters: fleet.MDMCommandFilters{
|
||
RequestType: "InstallProfile",
|
||
},
|
||
ListOptions: fleet.ListOptions{OrderKey: "hostname", OrderDirection: fleet.OrderAscending},
|
||
},
|
||
)
|
||
require.NoError(t, err)
|
||
require.Len(t, cmds, 2)
|
||
require.Equal(t, appleCmdUUID4, cmds[0].CommandUUID)
|
||
require.Equal(t, appleCmdUUID2, cmds[1].CommandUUID)
|
||
|
||
// filter by request_type and host_identifier
|
||
cmds, _, _, err = ds.ListMDMCommands(
|
||
ctx,
|
||
fleet.TeamFilter{User: test.UserAdmin},
|
||
&fleet.MDMCommandListOptions{
|
||
Filters: fleet.MDMCommandFilters{
|
||
RequestType: "InstallProfile",
|
||
HostIdentifier: macH.UUID,
|
||
},
|
||
},
|
||
)
|
||
require.NoError(t, err)
|
||
require.Len(t, cmds, 1)
|
||
require.Equal(t, appleCmdUUID2, cmds[0].CommandUUID)
|
||
|
||
// filter by command status for windows host
|
||
cmds, total, _, err = ds.ListMDMCommands(
|
||
ctx,
|
||
fleet.TeamFilter{User: test.UserAdmin},
|
||
&fleet.MDMCommandListOptions{
|
||
Filters: fleet.MDMCommandFilters{
|
||
HostIdentifier: "123456",
|
||
CommandStatuses: []fleet.MDMCommandStatusFilter{fleet.MDMCommandStatusFilterPending},
|
||
},
|
||
},
|
||
)
|
||
require.Error(t, err)
|
||
require.ErrorContains(t, err, `Currently, "command_status" filter is only available for macOS, iOS, and iPadOS hosts.`)
|
||
require.Nil(t, cmds)
|
||
require.Nil(t, total)
|
||
|
||
failedAppleCmdUUID := uuid.New().String()
|
||
appleCmd = createRawAppleCmd("ProfileList", failedAppleCmdUUID)
|
||
err = commander.EnqueueCommand(ctx, []string{macH.UUID}, appleCmd)
|
||
require.NoError(t, err)
|
||
|
||
// store failed result for the command
|
||
err = appleCommanderStorage.StoreCommandReport(&mdm.Request{
|
||
EnrollID: &mdm.EnrollID{ID: macH.UUID},
|
||
Context: ctx,
|
||
}, &mdm.CommandResults{
|
||
CommandUUID: failedAppleCmdUUID,
|
||
Status: "Error",
|
||
Raw: []byte(appleCmd),
|
||
})
|
||
require.NoError(t, err)
|
||
// Update the timestamp to ensure there is enough difference for ordering to have it always come first
|
||
// using default list options
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
_, err := q.ExecContext(
|
||
ctx,
|
||
`UPDATE nano_command_results SET updated_at = updated_at + INTERVAL 1 SECOND WHERE command_uuid = ?`,
|
||
failedAppleCmdUUID,
|
||
)
|
||
return err
|
||
})
|
||
|
||
cmds, total, _, err = ds.ListMDMCommands(
|
||
ctx,
|
||
fleet.TeamFilter{User: test.UserAdmin},
|
||
&fleet.MDMCommandListOptions{
|
||
Filters: fleet.MDMCommandFilters{
|
||
HostIdentifier: macH.UUID,
|
||
CommandStatuses: []fleet.MDMCommandStatusFilter{fleet.MDMCommandStatusFilterRan, fleet.MDMCommandStatusFilterFailed},
|
||
},
|
||
},
|
||
)
|
||
require.NoError(t, err)
|
||
require.Nil(t, total)
|
||
require.Len(t, cmds, 2)
|
||
var got []string
|
||
for _, cmd := range cmds {
|
||
got = append(got, cmd.CommandUUID)
|
||
}
|
||
require.Equal(t, []string{failedAppleCmdUUID, appleCmdUUID}, got)
|
||
|
||
// pagination and pagination meta data
|
||
cmds, _, meta, err := ds.ListMDMCommands(
|
||
ctx,
|
||
fleet.TeamFilter{User: test.UserAdmin},
|
||
&fleet.MDMCommandListOptions{
|
||
ListOptions: fleet.ListOptions{Page: 0, PerPage: 1, IncludeMetadata: true},
|
||
Filters: fleet.MDMCommandFilters{
|
||
HostIdentifier: macH.UUID,
|
||
CommandStatuses: []fleet.MDMCommandStatusFilter{fleet.MDMCommandStatusFilterRan, fleet.MDMCommandStatusFilterFailed},
|
||
},
|
||
},
|
||
)
|
||
require.Len(t, cmds, 1)
|
||
require.NoError(t, err)
|
||
require.Equal(t, true, meta.HasNextResults)
|
||
require.Equal(t, false, meta.HasPreviousResults)
|
||
}
|
||
|
||
// testListMDMCommandsWithTeamFilter tests listing MDM commands with team filters
|
||
// This specifically tests the regression where h.team_id was referenced but the query uses combined_commands alias
|
||
func testListMDMCommandsWithTeamFilter(t *testing.T, ds *Datastore) {
|
||
ctx := t.Context()
|
||
|
||
// Create a team
|
||
team, err := ds.NewTeam(ctx, &fleet.Team{Name: "test-team-mdm-commands"})
|
||
require.NoError(t, err)
|
||
|
||
// Create a host in the team
|
||
teamHost, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: "team-host",
|
||
OsqueryHostID: ptr.String("osquery-team"),
|
||
NodeKey: ptr.String("node-key-team"),
|
||
UUID: uuid.NewString(),
|
||
Platform: "darwin",
|
||
HardwareSerial: "789012",
|
||
TeamID: &team.ID,
|
||
})
|
||
require.NoError(t, err)
|
||
nanoEnroll(t, ds, teamHost, false)
|
||
|
||
// Create a command for the team host
|
||
teamCmdUUID := uuid.New().String()
|
||
teamCmd := createRawAppleCmd("ProfileList", teamCmdUUID)
|
||
commander, _ := createMDMAppleCommanderAndStorage(t, ds)
|
||
err = commander.EnqueueCommand(ctx, []string{teamHost.UUID}, teamCmd)
|
||
require.NoError(t, err)
|
||
|
||
// Create a user with team access
|
||
teamUser := &fleet.User{
|
||
ID: 999,
|
||
Email: "team-user@example.com",
|
||
Teams: []fleet.UserTeam{{Team: *team, Role: fleet.RoleMaintainer}},
|
||
}
|
||
|
||
// List commands with team filter (no host identifier) - this would trigger the bug hit by customer
|
||
cmds, _, _, err := ds.ListMDMCommands(
|
||
ctx,
|
||
fleet.TeamFilter{User: teamUser},
|
||
&fleet.MDMCommandListOptions{},
|
||
)
|
||
require.NoError(t, err)
|
||
require.Len(t, cmds, 1)
|
||
require.Equal(t, teamCmdUUID, cmds[0].CommandUUID)
|
||
|
||
// Test with a specific team filter
|
||
cmds, _, _, err = ds.ListMDMCommands(
|
||
ctx,
|
||
fleet.TeamFilter{User: teamUser, TeamID: &team.ID},
|
||
&fleet.MDMCommandListOptions{},
|
||
)
|
||
require.NoError(t, err)
|
||
require.Len(t, cmds, 1)
|
||
require.Equal(t, teamCmdUUID, cmds[0].CommandUUID)
|
||
|
||
// Create a host in no team
|
||
globalHost, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: "global-host",
|
||
OsqueryHostID: ptr.String("osquery-global"),
|
||
NodeKey: ptr.String("node-key-global"),
|
||
UUID: uuid.NewString(),
|
||
Platform: "darwin",
|
||
HardwareSerial: "789013",
|
||
})
|
||
require.NoError(t, err)
|
||
nanoEnroll(t, ds, globalHost, false)
|
||
|
||
// Create a command for the global host
|
||
globalCmdUUID := uuid.New().String()
|
||
globalCmd := createRawAppleCmd("ProfileList", globalCmdUUID)
|
||
err = commander.EnqueueCommand(ctx, []string{globalHost.UUID}, globalCmd)
|
||
require.NoError(t, err)
|
||
|
||
// Team user should only see team command
|
||
cmds, _, _, err = ds.ListMDMCommands(
|
||
ctx,
|
||
fleet.TeamFilter{User: teamUser},
|
||
&fleet.MDMCommandListOptions{},
|
||
)
|
||
require.NoError(t, err)
|
||
require.Len(t, cmds, 1)
|
||
require.Equal(t, teamCmdUUID, cmds[0].CommandUUID)
|
||
|
||
// Test with admin user (should see all commands)
|
||
adminUser := test.UserAdmin
|
||
cmds, _, _, err = ds.ListMDMCommands(
|
||
ctx,
|
||
fleet.TeamFilter{User: adminUser},
|
||
&fleet.MDMCommandListOptions{},
|
||
)
|
||
require.NoError(t, err)
|
||
require.Len(t, cmds, 2)
|
||
var got []string
|
||
for _, cmd := range cmds {
|
||
got = append(got, cmd.CommandUUID)
|
||
}
|
||
require.ElementsMatch(t, []string{teamCmdUUID, globalCmdUUID}, got)
|
||
}
|
||
|
||
func testBatchSetMDMProfiles(t *testing.T, ds *Datastore) {
|
||
ctx := context.Background()
|
||
|
||
applyAndExpect := func(
|
||
newAppleSet []*fleet.MDMAppleConfigProfile,
|
||
newWindowsSet []*fleet.MDMWindowsConfigProfile,
|
||
newAppleDeclSet []*fleet.MDMAppleDeclaration,
|
||
newAndroidSet []*fleet.MDMAndroidConfigProfile,
|
||
tmID *uint,
|
||
wantApple []*fleet.MDMAppleConfigProfile,
|
||
wantWindows []*fleet.MDMWindowsConfigProfile,
|
||
wantAppleDecl []*fleet.MDMAppleDeclaration,
|
||
wantAndroid []*fleet.MDMAndroidConfigProfile,
|
||
wantUpdates fleet.MDMProfilesUpdates,
|
||
) {
|
||
updates, err := ds.BatchSetMDMProfiles(ctx, tmID, newAppleSet, newWindowsSet, newAppleDeclSet, newAndroidSet, nil)
|
||
require.NoError(t, err)
|
||
expectAppleProfiles(t, ds, tmID, wantApple)
|
||
expectWindowsProfiles(t, ds, tmID, wantWindows)
|
||
expectAppleDeclarations(t, ds, tmID, wantAppleDecl)
|
||
expectAndroidProfiles(t, ds, tmID, wantAndroid)
|
||
assert.Equal(t, wantUpdates, updates)
|
||
}
|
||
|
||
withTeamIDApple := func(p *fleet.MDMAppleConfigProfile, tmID uint) *fleet.MDMAppleConfigProfile {
|
||
p.TeamID = &tmID
|
||
return p
|
||
}
|
||
|
||
withTeamIDDecl := func(d *fleet.MDMAppleDeclaration, tmID uint) *fleet.MDMAppleDeclaration {
|
||
d.TeamID = &tmID
|
||
return d
|
||
}
|
||
|
||
withTeamIDWindows := func(p *fleet.MDMWindowsConfigProfile, tmID uint) *fleet.MDMWindowsConfigProfile {
|
||
p.TeamID = &tmID
|
||
return p
|
||
}
|
||
|
||
withTeamIDAndroid := func(p *fleet.MDMAndroidConfigProfile, tmID uint) *fleet.MDMAndroidConfigProfile {
|
||
p.TeamID = &tmID
|
||
return p
|
||
}
|
||
|
||
// empty set for no team (Apple, Windows and Android)
|
||
applyAndExpect(nil, nil, nil, nil, nil, nil, nil, nil, nil, fleet.MDMProfilesUpdates{})
|
||
|
||
// single Apple, Windows and Android profile set for a specific team
|
||
applyAndExpect(
|
||
[]*fleet.MDMAppleConfigProfile{configProfileForTest(t, "N1", "I1", "a")},
|
||
[]*fleet.MDMWindowsConfigProfile{windowsConfigProfileForTest(t, "W1", "l1")},
|
||
[]*fleet.MDMAppleDeclaration{declForTest("D1", "D1", "foo")},
|
||
[]*fleet.MDMAndroidConfigProfile{androidConfigProfileForTest(t, "A1", nil)},
|
||
ptr.Uint(1),
|
||
[]*fleet.MDMAppleConfigProfile{
|
||
withTeamIDApple(configProfileForTest(t, "N1", "I1", "a"), 1),
|
||
},
|
||
[]*fleet.MDMWindowsConfigProfile{
|
||
withTeamIDWindows(windowsConfigProfileForTest(t, "W1", "l1"), 1),
|
||
},
|
||
[]*fleet.MDMAppleDeclaration{withTeamIDDecl(declForTest("D1", "D1", "foo"), 1)},
|
||
[]*fleet.MDMAndroidConfigProfile{withTeamIDAndroid(androidConfigProfileForTest(t, "A1", nil), 1)},
|
||
fleet.MDMProfilesUpdates{AppleConfigProfile: true, WindowsConfigProfile: true, AppleDeclaration: true, AndroidConfigProfile: true},
|
||
)
|
||
|
||
// single Apple, Windows and Android profile set for no team
|
||
applyAndExpect(
|
||
[]*fleet.MDMAppleConfigProfile{configProfileForTest(t, "N1", "I1", "a")},
|
||
[]*fleet.MDMWindowsConfigProfile{windowsConfigProfileForTest(t, "W1", "l1")},
|
||
[]*fleet.MDMAppleDeclaration{declForTest("D1", "D1", "foo")},
|
||
[]*fleet.MDMAndroidConfigProfile{androidConfigProfileForTest(t, "A1", nil)},
|
||
nil,
|
||
[]*fleet.MDMAppleConfigProfile{configProfileForTest(t, "N1", "I1", "a")},
|
||
[]*fleet.MDMWindowsConfigProfile{windowsConfigProfileForTest(t, "W1", "l1")},
|
||
[]*fleet.MDMAppleDeclaration{declForTest("D1", "D1", "foo")},
|
||
[]*fleet.MDMAndroidConfigProfile{androidConfigProfileForTest(t, "A1", nil)},
|
||
fleet.MDMProfilesUpdates{AppleConfigProfile: true, WindowsConfigProfile: true, AppleDeclaration: true, AndroidConfigProfile: true},
|
||
)
|
||
|
||
// new Apple, Windows and Android profile sets for a specific team
|
||
applyAndExpect(
|
||
[]*fleet.MDMAppleConfigProfile{
|
||
configProfileForTest(t, "N1", "I1", "a"), // unchanged
|
||
configProfileForTest(t, "N2", "I2", "b"),
|
||
},
|
||
[]*fleet.MDMWindowsConfigProfile{
|
||
windowsConfigProfileForTest(t, "W1", "l1"), // unchanged
|
||
windowsConfigProfileForTest(t, "W2", "l2"),
|
||
},
|
||
[]*fleet.MDMAppleDeclaration{
|
||
declForTest("D1", "D1", "foo"), // unchanged
|
||
declForTest("D2", "D2", "foo"),
|
||
},
|
||
[]*fleet.MDMAndroidConfigProfile{
|
||
androidConfigProfileForTest(t, "A1", nil), // unchanged
|
||
androidConfigProfileForTest(t, "A2", nil),
|
||
},
|
||
ptr.Uint(1),
|
||
[]*fleet.MDMAppleConfigProfile{
|
||
withTeamIDApple(configProfileForTest(t, "N1", "I1", "a"), 1),
|
||
withTeamIDApple(configProfileForTest(t, "N2", "I2", "b"), 1),
|
||
},
|
||
[]*fleet.MDMWindowsConfigProfile{
|
||
withTeamIDWindows(windowsConfigProfileForTest(t, "W1", "l1"), 1),
|
||
withTeamIDWindows(windowsConfigProfileForTest(t, "W2", "l2"), 1),
|
||
},
|
||
[]*fleet.MDMAppleDeclaration{
|
||
withTeamIDDecl(declForTest("D1", "D1", "foo"), 1),
|
||
withTeamIDDecl(declForTest("D2", "D2", "foo"), 1),
|
||
},
|
||
[]*fleet.MDMAndroidConfigProfile{
|
||
withTeamIDAndroid(androidConfigProfileForTest(t, "A1", nil), 1),
|
||
withTeamIDAndroid(androidConfigProfileForTest(t, "A2", nil), 1),
|
||
},
|
||
fleet.MDMProfilesUpdates{AppleConfigProfile: true, WindowsConfigProfile: true, AppleDeclaration: true, AndroidConfigProfile: true},
|
||
)
|
||
|
||
// edited profiles, unchanged profiles, and new profiles for a specific team
|
||
applyAndExpect(
|
||
[]*fleet.MDMAppleConfigProfile{
|
||
configProfileForTest(t, "N1", "I1", "a-updated"), // content updated
|
||
configProfileForTest(t, "N2", "I2", "b"), // unchanged
|
||
configProfileForTest(t, "N3", "I3", "c"), // new
|
||
},
|
||
[]*fleet.MDMWindowsConfigProfile{
|
||
windowsConfigProfileForTest(t, "W1", "l1-updated"), // content updated
|
||
windowsConfigProfileForTest(t, "W2", "l2"), // unchanged
|
||
windowsConfigProfileForTest(t, "W3", "l3"), // new
|
||
},
|
||
[]*fleet.MDMAppleDeclaration{
|
||
declForTest("D1", "D1", "foo-updated"), // content updated
|
||
declForTest("D2", "D2", "foo"), // unchanged
|
||
declForTest("D3", "D3", "bar"), // new
|
||
},
|
||
[]*fleet.MDMAndroidConfigProfile{
|
||
androidConfigProfileForTest(t, "A1", map[string]any{"cameraDisabled": true}), // Content updated
|
||
androidConfigProfileForTest(t, "A2", nil), // unchanged
|
||
androidConfigProfileForTest(t, "A3", nil), // new
|
||
},
|
||
ptr.Uint(1),
|
||
[]*fleet.MDMAppleConfigProfile{
|
||
withTeamIDApple(configProfileForTest(t, "N1", "I1", "a-updated"), 1),
|
||
withTeamIDApple(configProfileForTest(t, "N2", "I2", "b"), 1),
|
||
withTeamIDApple(configProfileForTest(t, "N3", "I3", "c"), 1),
|
||
},
|
||
[]*fleet.MDMWindowsConfigProfile{
|
||
withTeamIDWindows(windowsConfigProfileForTest(t, "W1", "l1-updated"), 1),
|
||
withTeamIDWindows(windowsConfigProfileForTest(t, "W2", "l2"), 1),
|
||
withTeamIDWindows(windowsConfigProfileForTest(t, "W3", "l3"), 1),
|
||
},
|
||
[]*fleet.MDMAppleDeclaration{
|
||
withTeamIDDecl(declForTest("D1", "D1", "foo-updated"), 1),
|
||
withTeamIDDecl(declForTest("D2", "D2", "foo"), 1),
|
||
withTeamIDDecl(declForTest("D3", "D3", "bar"), 1),
|
||
},
|
||
[]*fleet.MDMAndroidConfigProfile{
|
||
withTeamIDAndroid(androidConfigProfileForTest(t, "A1", map[string]any{"cameraDisabled": true}), 1),
|
||
withTeamIDAndroid(androidConfigProfileForTest(t, "A2", nil), 1),
|
||
withTeamIDAndroid(androidConfigProfileForTest(t, "A3", nil), 1),
|
||
},
|
||
fleet.MDMProfilesUpdates{AppleConfigProfile: true, WindowsConfigProfile: true, AppleDeclaration: true, AndroidConfigProfile: true},
|
||
)
|
||
|
||
// new Apple, Windows and Android profiles to no team
|
||
applyAndExpect(
|
||
[]*fleet.MDMAppleConfigProfile{
|
||
configProfileForTest(t, "N4", "I4", "d"),
|
||
configProfileForTest(t, "N5", "I5", "e"),
|
||
},
|
||
[]*fleet.MDMWindowsConfigProfile{
|
||
windowsConfigProfileForTest(t, "W4", "l4"),
|
||
windowsConfigProfileForTest(t, "W5", "l5"),
|
||
},
|
||
[]*fleet.MDMAppleDeclaration{
|
||
declForTest("D5", "D4", "foo"),
|
||
declForTest("D4", "D5", "foo"),
|
||
},
|
||
[]*fleet.MDMAndroidConfigProfile{
|
||
androidConfigProfileForTest(t, "A4", nil),
|
||
androidConfigProfileForTest(t, "A5", nil),
|
||
},
|
||
nil,
|
||
[]*fleet.MDMAppleConfigProfile{
|
||
configProfileForTest(t, "N4", "I4", "d"),
|
||
configProfileForTest(t, "N5", "I5", "e"),
|
||
},
|
||
[]*fleet.MDMWindowsConfigProfile{
|
||
windowsConfigProfileForTest(t, "W4", "l4"),
|
||
windowsConfigProfileForTest(t, "W5", "l5"),
|
||
},
|
||
[]*fleet.MDMAppleDeclaration{
|
||
declForTest("D5", "D4", "foo"),
|
||
declForTest("D4", "D5", "foo"),
|
||
},
|
||
[]*fleet.MDMAndroidConfigProfile{
|
||
androidConfigProfileForTest(t, "A4", nil),
|
||
androidConfigProfileForTest(t, "A5", nil),
|
||
},
|
||
fleet.MDMProfilesUpdates{AppleConfigProfile: true, WindowsConfigProfile: true, AppleDeclaration: true, AndroidConfigProfile: true},
|
||
)
|
||
|
||
// Apply the same profiles again -- no update should be detected
|
||
applyAndExpect(
|
||
[]*fleet.MDMAppleConfigProfile{
|
||
configProfileForTest(t, "N4", "I4", "d"),
|
||
configProfileForTest(t, "N5", "I5", "e"),
|
||
},
|
||
[]*fleet.MDMWindowsConfigProfile{
|
||
windowsConfigProfileForTest(t, "W4", "l4"),
|
||
windowsConfigProfileForTest(t, "W5", "l5"),
|
||
},
|
||
[]*fleet.MDMAppleDeclaration{
|
||
declForTest("D5", "D4", "foo"),
|
||
declForTest("D4", "D5", "foo"),
|
||
},
|
||
[]*fleet.MDMAndroidConfigProfile{
|
||
androidConfigProfileForTest(t, "A4", nil),
|
||
androidConfigProfileForTest(t, "A5", nil),
|
||
},
|
||
nil,
|
||
[]*fleet.MDMAppleConfigProfile{
|
||
configProfileForTest(t, "N4", "I4", "d"),
|
||
configProfileForTest(t, "N5", "I5", "e"),
|
||
},
|
||
[]*fleet.MDMWindowsConfigProfile{
|
||
windowsConfigProfileForTest(t, "W4", "l4"),
|
||
windowsConfigProfileForTest(t, "W5", "l5"),
|
||
},
|
||
[]*fleet.MDMAppleDeclaration{
|
||
declForTest("D5", "D4", "foo"),
|
||
declForTest("D4", "D5", "foo"),
|
||
},
|
||
[]*fleet.MDMAndroidConfigProfile{
|
||
androidConfigProfileForTest(t, "A4", nil),
|
||
androidConfigProfileForTest(t, "A5", nil),
|
||
},
|
||
fleet.MDMProfilesUpdates{AppleConfigProfile: false, WindowsConfigProfile: false, AppleDeclaration: false, AndroidConfigProfile: false},
|
||
)
|
||
|
||
// Test Case 8: Clear profiles for a specific team
|
||
applyAndExpect(nil, nil, nil, nil, ptr.Uint(1), nil, nil, nil, nil,
|
||
fleet.MDMProfilesUpdates{AppleConfigProfile: true, WindowsConfigProfile: true, AppleDeclaration: true, AndroidConfigProfile: true},
|
||
)
|
||
|
||
// create some labels to test batch-setting label-scoped declarations
|
||
lblExcl, err := ds.NewLabel(ctx, &fleet.Label{Name: "exclude-label-1", Query: "select 1"})
|
||
require.NoError(t, err)
|
||
lblExcl2, err := ds.NewLabel(ctx, &fleet.Label{Name: "exclude-label-2", Query: "select 2"})
|
||
require.NoError(t, err)
|
||
lblInclAny, err := ds.NewLabel(ctx, &fleet.Label{Name: "include-label-3", Query: "select 3"})
|
||
require.NoError(t, err)
|
||
lblInclAny2, err := ds.NewLabel(ctx, &fleet.Label{Name: "include-label-4", Query: "select 4"})
|
||
require.NoError(t, err)
|
||
lblInclAll, err := ds.NewLabel(ctx, &fleet.Label{Name: "inclall-label-5", Query: "select 5"})
|
||
require.NoError(t, err)
|
||
lblInclAll2, err := ds.NewLabel(ctx, &fleet.Label{Name: "inclall-label-6", Query: "select 6"})
|
||
require.NoError(t, err)
|
||
|
||
// we only care about declarations here, as batch-setting labels for profiles
|
||
// is tested elsewhere.
|
||
applyAndExpect(nil, nil, []*fleet.MDMAppleDeclaration{
|
||
declForTest("D1", "D1", "foo", lblExcl, lblExcl2),
|
||
declForTest("D2", "D2", "foo", lblInclAll, lblInclAll2),
|
||
}, nil, nil,
|
||
nil, nil, []*fleet.MDMAppleDeclaration{
|
||
declForTest("D1", "D1", "foo", lblExcl, lblExcl2),
|
||
declForTest("D2", "D2", "foo", lblInclAll, lblInclAll2),
|
||
}, nil,
|
||
// this removed the apple, windows and android profiles for no team, so updated is true
|
||
fleet.MDMProfilesUpdates{AppleConfigProfile: true, WindowsConfigProfile: true, AppleDeclaration: true, AndroidConfigProfile: true},
|
||
)
|
||
|
||
applyAndExpect(nil, nil, []*fleet.MDMAppleDeclaration{
|
||
declForTest("D1", "D1", "foo", lblInclAny, lblInclAny2),
|
||
declForTest("D2", "D2", "foo"),
|
||
}, nil, nil,
|
||
nil, nil, []*fleet.MDMAppleDeclaration{
|
||
declForTest("D1", "D1", "foo", lblInclAny, lblInclAny2),
|
||
declForTest("D2", "D2", "foo"),
|
||
}, nil,
|
||
fleet.MDMProfilesUpdates{AppleConfigProfile: false, WindowsConfigProfile: false, AppleDeclaration: true, AndroidConfigProfile: false},
|
||
)
|
||
applyAndExpect(nil, nil, []*fleet.MDMAppleDeclaration{
|
||
declForTest("D1", "D1", "foo"),
|
||
}, nil, nil,
|
||
nil, nil, []*fleet.MDMAppleDeclaration{
|
||
declForTest("D1", "D1", "foo"),
|
||
}, nil,
|
||
fleet.MDMProfilesUpdates{AppleConfigProfile: false, WindowsConfigProfile: false, AppleDeclaration: true, AndroidConfigProfile: false},
|
||
)
|
||
}
|
||
|
||
func testListMDMConfigProfiles(t *testing.T, ds *Datastore) {
|
||
ctx := context.Background()
|
||
|
||
opts := fleet.ListOptions{OrderKey: "name", IncludeMetadata: true}
|
||
winProf := []byte("<Replace></Replace>")
|
||
|
||
// create a team
|
||
team, err := ds.NewTeam(ctx, &fleet.Team{Name: "test team"})
|
||
require.NoError(t, err)
|
||
|
||
// both profile tables are empty
|
||
profs, meta, err := ds.ListMDMConfigProfiles(ctx, nil, opts)
|
||
require.NoError(t, err)
|
||
require.Len(t, profs, 0)
|
||
require.Equal(t, *meta, fleet.PaginationMetadata{})
|
||
|
||
// add fleet-managed Apple profiles for the team and globally
|
||
for idf := range mobileconfig.FleetPayloadIdentifiers() {
|
||
_, err = ds.NewMDMAppleConfigProfile(ctx, *generateAppleCP("name_"+idf, idf, team.ID), nil)
|
||
require.NoError(t, err)
|
||
_, err = ds.NewMDMAppleConfigProfile(ctx, *generateAppleCP("name_"+idf, idf, 0), nil)
|
||
require.NoError(t, err)
|
||
}
|
||
|
||
// still returns no result
|
||
profs, meta, err = ds.ListMDMConfigProfiles(ctx, nil, opts)
|
||
require.NoError(t, err)
|
||
require.Len(t, profs, 0)
|
||
require.Equal(t, *meta, fleet.PaginationMetadata{})
|
||
|
||
profs, meta, err = ds.ListMDMConfigProfiles(ctx, &team.ID, opts)
|
||
require.NoError(t, err)
|
||
require.Len(t, profs, 0)
|
||
require.Equal(t, *meta, fleet.PaginationMetadata{})
|
||
|
||
// add fleet-managed Windows profiles for the team and globally
|
||
for name := range mdm_types.FleetReservedProfileNames() {
|
||
_, err = ds.NewMDMWindowsConfigProfile(
|
||
ctx,
|
||
fleet.MDMWindowsConfigProfile{Name: name, TeamID: &team.ID, SyncML: winProf},
|
||
nil,
|
||
)
|
||
require.NoError(t, err)
|
||
_, err = ds.NewMDMWindowsConfigProfile(
|
||
ctx,
|
||
fleet.MDMWindowsConfigProfile{Name: name, TeamID: nil, SyncML: winProf},
|
||
nil,
|
||
)
|
||
require.NoError(t, err)
|
||
}
|
||
|
||
// still returns no result
|
||
profs, meta, err = ds.ListMDMConfigProfiles(ctx, nil, opts)
|
||
require.NoError(t, err)
|
||
require.Len(t, profs, 0)
|
||
require.Equal(t, *meta, fleet.PaginationMetadata{})
|
||
|
||
profs, meta, err = ds.ListMDMConfigProfiles(ctx, &team.ID, opts)
|
||
require.NoError(t, err)
|
||
require.Len(t, profs, 0)
|
||
require.Equal(t, *meta, fleet.PaginationMetadata{})
|
||
|
||
// create a mac profile for global and a Windows profile for team
|
||
profA, err := ds.NewMDMAppleConfigProfile(ctx, *generateAppleCP("A", "A", 0), nil)
|
||
require.NoError(t, err)
|
||
profB, err := ds.NewMDMWindowsConfigProfile(
|
||
ctx,
|
||
fleet.MDMWindowsConfigProfile{Name: "B", TeamID: &team.ID, SyncML: winProf},
|
||
nil,
|
||
)
|
||
require.NoError(t, err)
|
||
|
||
// get global profiles returns the mac one
|
||
profs, meta, err = ds.ListMDMConfigProfiles(ctx, nil, opts)
|
||
require.NoError(t, err)
|
||
require.Len(t, profs, 1)
|
||
require.Equal(t, profA.Name, profs[0].Name)
|
||
require.Equal(t, *meta, fleet.PaginationMetadata{})
|
||
|
||
// get team profiles returns the Windows one
|
||
profs, meta, err = ds.ListMDMConfigProfiles(ctx, &team.ID, opts)
|
||
require.NoError(t, err)
|
||
require.Len(t, profs, 1)
|
||
require.Equal(t, profB.Name, profs[0].Name)
|
||
require.Equal(t, *meta, fleet.PaginationMetadata{})
|
||
|
||
// create 12 labels for label-based profiles
|
||
var labels []*fleet.Label
|
||
for i := 0; i < 12; i++ {
|
||
lbl, err := ds.NewLabel(ctx, &fleet.Label{Name: "l" + strconv.Itoa(i), Query: "select 1"})
|
||
require.NoError(t, err)
|
||
labels = append(labels, lbl)
|
||
}
|
||
|
||
// create more profiles and test the pagination with a table-driven test so that
|
||
// global and team both have 9 profiles (including A and B already created above).
|
||
for i := 0; i < 3; i++ {
|
||
inc := i * 6 // e.g. C, D, E, F, G, H on first loop, I, J, K, L, M, N on second loop, O, P, Q, R, S, T on third
|
||
|
||
// create label-based profiles for i==0, meaning CDEF will be label-based
|
||
acp := *generateAppleCP(string(rune('C'+inc)), string(rune('C'+inc)), 0) // C, I and O
|
||
if i == 0 {
|
||
acp.LabelsIncludeAll = []fleet.ConfigurationProfileLabel{
|
||
{LabelName: labels[0].Name, LabelID: labels[0].ID},
|
||
{LabelName: labels[1].Name, LabelID: labels[1].ID},
|
||
}
|
||
}
|
||
_, err = ds.NewMDMAppleConfigProfile(ctx, acp, nil)
|
||
require.NoError(t, err)
|
||
|
||
acp = *generateAppleCP(string(rune('C'+inc+1)), string(rune('C'+inc+1)), team.ID) // D, J and P
|
||
if i == 0 {
|
||
acp.LabelsIncludeAll = []fleet.ConfigurationProfileLabel{
|
||
{LabelName: labels[2].Name, LabelID: labels[2].ID},
|
||
{LabelName: labels[3].Name, LabelID: labels[3].ID},
|
||
}
|
||
}
|
||
_, err = ds.NewMDMAppleConfigProfile(ctx, acp, nil)
|
||
require.NoError(t, err)
|
||
|
||
wcp := fleet.MDMWindowsConfigProfile{ // E K and Q
|
||
Name: string(rune('C' + inc + 2)),
|
||
TeamID: nil,
|
||
SyncML: winProf,
|
||
}
|
||
if i == 0 {
|
||
wcp.LabelsIncludeAll = []fleet.ConfigurationProfileLabel{
|
||
{LabelName: labels[4].Name, LabelID: labels[4].ID},
|
||
{LabelName: labels[5].Name, LabelID: labels[5].ID},
|
||
}
|
||
}
|
||
_, err = ds.NewMDMWindowsConfigProfile(ctx, wcp, nil)
|
||
require.NoError(t, err)
|
||
|
||
wcp = fleet.MDMWindowsConfigProfile{ // F L and R
|
||
Name: string(rune('C' + inc + 3)),
|
||
TeamID: &team.ID,
|
||
SyncML: winProf,
|
||
}
|
||
if i == 0 {
|
||
wcp.LabelsIncludeAll = []fleet.ConfigurationProfileLabel{
|
||
{LabelName: labels[6].Name, LabelID: labels[6].ID},
|
||
{LabelName: labels[7].Name, LabelID: labels[7].ID},
|
||
}
|
||
}
|
||
_, err = ds.NewMDMWindowsConfigProfile(ctx, wcp, nil)
|
||
require.NoError(t, err)
|
||
|
||
// gcp = Android("google") config profile
|
||
gcp := fleet.MDMAndroidConfigProfile{ // G M and S
|
||
Name: string(rune('C' + inc + 4)),
|
||
TeamID: nil,
|
||
RawJSON: []byte(`{"hello4": "world4"}`),
|
||
}
|
||
|
||
if i == 0 {
|
||
gcp.LabelsIncludeAll = []fleet.ConfigurationProfileLabel{
|
||
{LabelName: labels[8].Name, LabelID: labels[8].ID},
|
||
{LabelName: labels[9].Name, LabelID: labels[9].ID},
|
||
}
|
||
}
|
||
_, err = ds.NewMDMAndroidConfigProfile(ctx, gcp)
|
||
require.NoError(t, err)
|
||
|
||
gcp = fleet.MDMAndroidConfigProfile{ // H N and T
|
||
Name: string(rune('C' + inc + 5)),
|
||
TeamID: &team.ID,
|
||
RawJSON: []byte(`{"hello5": "world5"}`),
|
||
}
|
||
if i == 0 {
|
||
gcp.LabelsIncludeAll = []fleet.ConfigurationProfileLabel{
|
||
{LabelName: labels[10].Name, LabelID: labels[10].ID},
|
||
{LabelName: labels[11].Name, LabelID: labels[11].ID},
|
||
}
|
||
}
|
||
_, err = ds.NewMDMAndroidConfigProfile(ctx, gcp)
|
||
require.NoError(t, err)
|
||
}
|
||
// delete label 3, 4 and 8 so that profiles D, E and G are broken
|
||
require.NoError(t, ds.DeleteLabel(ctx, labels[3].Name, fleet.TeamFilter{User: &fleet.User{GlobalRole: ptr.String(fleet.RoleAdmin)}}))
|
||
require.NoError(t, ds.DeleteLabel(ctx, labels[4].Name, fleet.TeamFilter{User: &fleet.User{GlobalRole: ptr.String(fleet.RoleAdmin)}}))
|
||
require.NoError(t, ds.DeleteLabel(ctx, labels[8].Name, fleet.TeamFilter{User: &fleet.User{GlobalRole: ptr.String(fleet.RoleAdmin)}}))
|
||
profLabels := map[string][]fleet.ConfigurationProfileLabel{
|
||
"C": {
|
||
{LabelName: labels[0].Name, LabelID: labels[0].ID, RequireAll: true},
|
||
{LabelName: labels[1].Name, LabelID: labels[1].ID, RequireAll: true},
|
||
},
|
||
"D": {
|
||
{LabelName: labels[2].Name, LabelID: labels[2].ID, RequireAll: true},
|
||
{LabelName: labels[3].Name, LabelID: 0, Broken: true, RequireAll: true},
|
||
},
|
||
"E": {
|
||
{LabelName: labels[4].Name, LabelID: 0, Broken: true, RequireAll: true},
|
||
{LabelName: labels[5].Name, LabelID: labels[5].ID, RequireAll: true},
|
||
},
|
||
"F": {
|
||
{LabelName: labels[6].Name, LabelID: labels[6].ID, RequireAll: true},
|
||
{LabelName: labels[7].Name, LabelID: labels[7].ID, RequireAll: true},
|
||
},
|
||
"G": {
|
||
{LabelName: labels[8].Name, Broken: true, RequireAll: true},
|
||
{LabelName: labels[9].Name, LabelID: labels[9].ID, RequireAll: true},
|
||
},
|
||
"H": {
|
||
{LabelName: labels[10].Name, LabelID: labels[10].ID, RequireAll: true},
|
||
{LabelName: labels[11].Name, LabelID: labels[11].ID, RequireAll: true},
|
||
},
|
||
}
|
||
|
||
cases := []struct {
|
||
desc string
|
||
tmID *uint
|
||
opts fleet.ListOptions
|
||
wantNames []string
|
||
wantMeta fleet.PaginationMetadata
|
||
}{
|
||
{
|
||
"all global",
|
||
nil,
|
||
fleet.ListOptions{OrderKey: "name", IncludeMetadata: true},
|
||
[]string{"A", "C", "E", "G", "I", "K", "M", "O", "Q", "S"},
|
||
fleet.PaginationMetadata{},
|
||
},
|
||
{
|
||
"all team",
|
||
&team.ID,
|
||
fleet.ListOptions{OrderKey: "name", IncludeMetadata: true},
|
||
[]string{"B", "D", "F", "H", "J", "L", "N", "P", "R", "T"},
|
||
fleet.PaginationMetadata{},
|
||
},
|
||
|
||
{
|
||
"page 0 per page 2, global",
|
||
nil,
|
||
fleet.ListOptions{OrderKey: "name", IncludeMetadata: true, PerPage: 2},
|
||
[]string{"A", "C"},
|
||
fleet.PaginationMetadata{HasNextResults: true},
|
||
},
|
||
{
|
||
"page 1 per page 2, global",
|
||
nil,
|
||
fleet.ListOptions{OrderKey: "name", IncludeMetadata: true, PerPage: 2, Page: 1},
|
||
[]string{"E", "G"},
|
||
fleet.PaginationMetadata{HasPreviousResults: true, HasNextResults: true},
|
||
},
|
||
{
|
||
"page 2 per page 2, global",
|
||
nil,
|
||
fleet.ListOptions{OrderKey: "name", IncludeMetadata: true, PerPage: 2, Page: 2},
|
||
[]string{"I", "K"},
|
||
fleet.PaginationMetadata{HasPreviousResults: true, HasNextResults: true},
|
||
},
|
||
{
|
||
"page 3 per page 2, global",
|
||
nil,
|
||
fleet.ListOptions{OrderKey: "name", IncludeMetadata: true, PerPage: 2, Page: 3},
|
||
[]string{"M", "O"},
|
||
fleet.PaginationMetadata{HasPreviousResults: true, HasNextResults: true},
|
||
},
|
||
{
|
||
"page 4 per page 2, global",
|
||
nil,
|
||
fleet.ListOptions{OrderKey: "name", IncludeMetadata: true, PerPage: 2, Page: 4},
|
||
[]string{"Q", "S"},
|
||
fleet.PaginationMetadata{HasPreviousResults: true},
|
||
},
|
||
{
|
||
"page 5 per page 2, global",
|
||
nil,
|
||
fleet.ListOptions{OrderKey: "name", IncludeMetadata: true, PerPage: 2, Page: 5},
|
||
[]string{},
|
||
fleet.PaginationMetadata{HasPreviousResults: true},
|
||
},
|
||
|
||
{
|
||
"page 0 per page 2, team",
|
||
&team.ID,
|
||
fleet.ListOptions{OrderKey: "name", IncludeMetadata: true, PerPage: 2},
|
||
[]string{"B", "D"},
|
||
fleet.PaginationMetadata{HasNextResults: true},
|
||
},
|
||
{
|
||
"page 1 per page 2, team",
|
||
&team.ID,
|
||
fleet.ListOptions{OrderKey: "name", IncludeMetadata: true, PerPage: 2, Page: 1},
|
||
[]string{"F", "H"},
|
||
fleet.PaginationMetadata{HasPreviousResults: true, HasNextResults: true},
|
||
},
|
||
{
|
||
"page 2 per page 2, team",
|
||
&team.ID,
|
||
fleet.ListOptions{OrderKey: "name", IncludeMetadata: true, PerPage: 2, Page: 2},
|
||
[]string{"J", "L"},
|
||
fleet.PaginationMetadata{HasPreviousResults: true, HasNextResults: true},
|
||
},
|
||
{
|
||
"page 3 per page 2, team",
|
||
&team.ID,
|
||
fleet.ListOptions{OrderKey: "name", IncludeMetadata: true, PerPage: 2, Page: 3},
|
||
[]string{"N", "P"},
|
||
fleet.PaginationMetadata{HasPreviousResults: true, HasNextResults: true},
|
||
},
|
||
{
|
||
"page 4 per page 2, team",
|
||
&team.ID,
|
||
fleet.ListOptions{OrderKey: "name", IncludeMetadata: true, PerPage: 2, Page: 4},
|
||
[]string{"R", "T"},
|
||
fleet.PaginationMetadata{HasPreviousResults: true},
|
||
},
|
||
{
|
||
"page 5 per page 2, team",
|
||
&team.ID,
|
||
fleet.ListOptions{OrderKey: "name", IncludeMetadata: true, PerPage: 2, Page: 5},
|
||
[]string{},
|
||
fleet.PaginationMetadata{HasPreviousResults: true},
|
||
},
|
||
{
|
||
"page 0 per page 3, global",
|
||
nil,
|
||
fleet.ListOptions{OrderKey: "name", IncludeMetadata: true, PerPage: 3},
|
||
[]string{"A", "C", "E"},
|
||
fleet.PaginationMetadata{HasNextResults: true},
|
||
},
|
||
{
|
||
"page 1 per page 3, global",
|
||
nil,
|
||
fleet.ListOptions{OrderKey: "name", IncludeMetadata: true, PerPage: 3, Page: 1},
|
||
[]string{"G", "I", "K"},
|
||
fleet.PaginationMetadata{HasPreviousResults: true, HasNextResults: true},
|
||
},
|
||
{
|
||
"page 2 per page 3, global",
|
||
nil,
|
||
fleet.ListOptions{OrderKey: "name", IncludeMetadata: true, PerPage: 3, Page: 2},
|
||
[]string{"M", "O", "Q"},
|
||
fleet.PaginationMetadata{HasPreviousResults: true, HasNextResults: true},
|
||
},
|
||
{
|
||
"page 3 per page 3, global",
|
||
nil,
|
||
fleet.ListOptions{OrderKey: "name", IncludeMetadata: true, PerPage: 3, Page: 3},
|
||
[]string{"S"},
|
||
fleet.PaginationMetadata{HasPreviousResults: true},
|
||
},
|
||
{
|
||
"page 4 per page 3, global",
|
||
nil,
|
||
fleet.ListOptions{OrderKey: "name", IncludeMetadata: true, PerPage: 3, Page: 4},
|
||
[]string{},
|
||
fleet.PaginationMetadata{HasPreviousResults: true},
|
||
},
|
||
{
|
||
"page 0 per page 3, team",
|
||
&team.ID,
|
||
fleet.ListOptions{OrderKey: "name", IncludeMetadata: true, PerPage: 3},
|
||
[]string{"B", "D", "F"},
|
||
fleet.PaginationMetadata{HasNextResults: true},
|
||
},
|
||
{
|
||
"page 1 per page 3, team",
|
||
&team.ID,
|
||
fleet.ListOptions{OrderKey: "name", IncludeMetadata: true, PerPage: 3, Page: 1},
|
||
[]string{"H", "J", "L"},
|
||
fleet.PaginationMetadata{HasPreviousResults: true, HasNextResults: true},
|
||
},
|
||
{
|
||
"page 2 per page 3, team",
|
||
&team.ID,
|
||
fleet.ListOptions{OrderKey: "name", IncludeMetadata: true, PerPage: 3, Page: 2},
|
||
[]string{"N", "P", "R"},
|
||
fleet.PaginationMetadata{HasPreviousResults: true, HasNextResults: true},
|
||
},
|
||
{
|
||
"page 3 per page 3, team",
|
||
&team.ID,
|
||
fleet.ListOptions{OrderKey: "name", IncludeMetadata: true, PerPage: 3, Page: 3},
|
||
[]string{"T"},
|
||
fleet.PaginationMetadata{HasPreviousResults: true},
|
||
},
|
||
{
|
||
"page 4 per page 3, team",
|
||
&team.ID,
|
||
fleet.ListOptions{OrderKey: "name", IncludeMetadata: true, PerPage: 3, Page: 4},
|
||
[]string{},
|
||
fleet.PaginationMetadata{HasPreviousResults: true},
|
||
},
|
||
|
||
{
|
||
"no metadata, global",
|
||
nil,
|
||
fleet.ListOptions{OrderKey: "name", IncludeMetadata: false, PerPage: 2, Page: 1},
|
||
[]string{"E", "G"},
|
||
fleet.PaginationMetadata{},
|
||
},
|
||
{
|
||
"no metadata, team",
|
||
&team.ID,
|
||
fleet.ListOptions{OrderKey: "name", IncludeMetadata: false, PerPage: 2, Page: 1},
|
||
[]string{"F", "H"},
|
||
fleet.PaginationMetadata{},
|
||
},
|
||
}
|
||
for _, c := range cases {
|
||
t.Run(c.desc, func(t *testing.T) {
|
||
profs, meta, err := ds.ListMDMConfigProfiles(ctx, c.tmID, c.opts)
|
||
require.NoError(t, err)
|
||
require.Len(t, profs, len(c.wantNames))
|
||
|
||
got := make([]string, len(profs))
|
||
for i, p := range profs {
|
||
got[i] = p.Name
|
||
|
||
wantProfs := profLabels[p.Name]
|
||
require.Equal(t, len(wantProfs), len(p.LabelsIncludeAll), "profile name: %s", p.Name)
|
||
if len(wantProfs) > 0 {
|
||
// clear the profile uuids from the labels list
|
||
for i, l := range p.LabelsIncludeAll {
|
||
l.ProfileUUID = ""
|
||
p.LabelsIncludeAll[i] = l
|
||
}
|
||
require.ElementsMatch(t, wantProfs, p.LabelsIncludeAll, "profile name: %s", p.Name)
|
||
}
|
||
}
|
||
require.Equal(t, c.wantNames, got)
|
||
|
||
var gotMeta fleet.PaginationMetadata
|
||
if meta != nil {
|
||
gotMeta = *meta
|
||
}
|
||
require.Equal(t, c.wantMeta, gotMeta)
|
||
})
|
||
}
|
||
}
|
||
|
||
func testBulkSetPendingMDMHostProfilesBatch2(t *testing.T, ds *Datastore) {
|
||
ds.testUpsertMDMDesiredProfilesBatchSize = 2
|
||
ds.testDeleteMDMProfilesBatchSize = 2
|
||
t.Cleanup(func() {
|
||
ds.testUpsertMDMDesiredProfilesBatchSize = 0
|
||
ds.testDeleteMDMProfilesBatchSize = 0
|
||
})
|
||
testBulkSetPendingMDMHostProfiles(t, ds)
|
||
}
|
||
|
||
func testBulkSetPendingMDMHostProfilesBatch3(t *testing.T, ds *Datastore) {
|
||
ds.testUpsertMDMDesiredProfilesBatchSize = 3
|
||
ds.testDeleteMDMProfilesBatchSize = 3
|
||
t.Cleanup(func() {
|
||
ds.testUpsertMDMDesiredProfilesBatchSize = 0
|
||
ds.testDeleteMDMProfilesBatchSize = 0
|
||
})
|
||
testBulkSetPendingMDMHostProfiles(t, ds)
|
||
}
|
||
|
||
type anyProfile struct {
|
||
ProfileUUID string
|
||
Status *fleet.MDMDeliveryStatus
|
||
OperationType fleet.MDMOperationType
|
||
IdentifierOrName string
|
||
}
|
||
|
||
// cleanupStaleWindowsRemoveRows simulates the full Windows profile removal
|
||
// lifecycle (reconciler sends <Delete> → device confirms → row deleted) for
|
||
// remove rows that are NOT expected in the test assertions. Without this,
|
||
// remove rows from previous test phases accumulate and cause count mismatches.
|
||
//
|
||
// Scoped to only the Windows hosts present in the want map so that rows
|
||
// belonging to hosts not in the current assertion are left untouched. This
|
||
// prevents implicitly hiding issues for hosts the test phase doesn't check.
|
||
func cleanupStaleWindowsRemoveRows(t *testing.T, ds *Datastore, want map[*fleet.Host][]anyProfile) {
|
||
// Collect the set of Windows host UUIDs in the assertion and the
|
||
// (profile_uuid, host_uuid) pairs that are expected as remove rows.
|
||
wantWindowsHostUUIDs := make([]string, 0)
|
||
wantRemoveKeys := make(map[string]bool)
|
||
for h, profs := range want {
|
||
if h.Platform != "windows" {
|
||
continue
|
||
}
|
||
wantWindowsHostUUIDs = append(wantWindowsHostUUIDs, h.UUID)
|
||
for _, p := range profs {
|
||
if p.OperationType == fleet.MDMOperationTypeRemove {
|
||
wantRemoveKeys[p.ProfileUUID+"\n"+h.UUID] = true
|
||
}
|
||
}
|
||
}
|
||
if len(wantWindowsHostUUIDs) == 0 {
|
||
return
|
||
}
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
// Only select remove rows for hosts in the current assertion's want map.
|
||
stmt, args, err := sqlx.In(
|
||
`SELECT profile_uuid, host_uuid FROM host_mdm_windows_profiles WHERE operation_type = 'remove' AND host_uuid IN (?)`,
|
||
wantWindowsHostUUIDs)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
var rows []struct {
|
||
ProfileUUID string `db:"profile_uuid"`
|
||
HostUUID string `db:"host_uuid"`
|
||
}
|
||
if err := sqlx.SelectContext(context.Background(), q, &rows, stmt, args...); err != nil {
|
||
return err
|
||
}
|
||
for _, r := range rows {
|
||
key := r.ProfileUUID + "\n" + r.HostUUID
|
||
if !wantRemoveKeys[key] {
|
||
if _, err := q.ExecContext(context.Background(),
|
||
`DELETE FROM host_mdm_windows_profiles WHERE profile_uuid = ? AND host_uuid = ? AND operation_type = 'remove'`,
|
||
r.ProfileUUID, r.HostUUID); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
}
|
||
return nil
|
||
})
|
||
}
|
||
|
||
// assertHostProfiles only asserts the profile UUID, status, and operation.
|
||
func assertHostProfiles(t *testing.T, ds *Datastore, want map[*fleet.Host][]anyProfile) {
|
||
cleanupStaleWindowsRemoveRows(t, ds, want)
|
||
|
||
ctx := context.Background()
|
||
for h, wantProfs := range want {
|
||
var gotProfs []anyProfile
|
||
|
||
switch h.Platform {
|
||
case "windows":
|
||
profs, err := ds.GetHostMDMWindowsProfiles(ctx, h.UUID)
|
||
require.NoError(t, err)
|
||
require.Equal(t, len(wantProfs), len(profs), "host uuid: %s", h.UUID)
|
||
for _, p := range profs {
|
||
gotProfs = append(gotProfs, anyProfile{
|
||
ProfileUUID: p.ProfileUUID,
|
||
Status: p.Status,
|
||
OperationType: p.OperationType,
|
||
IdentifierOrName: p.Name,
|
||
})
|
||
}
|
||
case "android":
|
||
profs, err := ds.GetHostMDMAndroidProfiles(ctx, h.UUID)
|
||
require.NoError(t, err)
|
||
require.Equal(t, len(wantProfs), len(profs), "host uuid: %s", h.UUID)
|
||
for _, p := range profs {
|
||
gotProfs = append(gotProfs, anyProfile{
|
||
ProfileUUID: p.ProfileUUID,
|
||
Status: p.Status,
|
||
OperationType: p.OperationType,
|
||
IdentifierOrName: p.Name,
|
||
})
|
||
}
|
||
default:
|
||
profs, err := ds.GetHostMDMAppleProfiles(ctx, h.UUID)
|
||
require.NoError(t, err)
|
||
require.Equal(t, len(wantProfs), len(profs), "host uuid: %s", h.UUID)
|
||
for _, p := range profs {
|
||
gotProfs = append(gotProfs, anyProfile{
|
||
ProfileUUID: p.ProfileUUID,
|
||
Status: p.Status,
|
||
OperationType: p.OperationType,
|
||
IdentifierOrName: p.Identifier,
|
||
})
|
||
}
|
||
}
|
||
|
||
sortProfs := func(profs []anyProfile) []anyProfile {
|
||
sort.Slice(profs, func(i, j int) bool {
|
||
l, r := profs[i], profs[j]
|
||
if l.ProfileUUID == r.ProfileUUID {
|
||
return l.OperationType < r.OperationType
|
||
}
|
||
|
||
// default alphabetical comparison
|
||
return l.IdentifierOrName < r.IdentifierOrName
|
||
})
|
||
return profs
|
||
}
|
||
|
||
gotProfs = sortProfs(gotProfs)
|
||
wantProfs = sortProfs(wantProfs)
|
||
for i, wp := range wantProfs {
|
||
gp := gotProfs[i]
|
||
require.Equal(
|
||
t,
|
||
wp.ProfileUUID,
|
||
gp.ProfileUUID,
|
||
"host uuid: %s, prof id or name: %s",
|
||
h.UUID,
|
||
gp.IdentifierOrName,
|
||
)
|
||
require.Equal(
|
||
t,
|
||
wp.Status,
|
||
gp.Status,
|
||
"host uuid: %s, prof id or name: %s",
|
||
h.UUID,
|
||
gp.IdentifierOrName,
|
||
)
|
||
require.Equal(
|
||
t,
|
||
wp.OperationType,
|
||
gp.OperationType,
|
||
"host uuid: %s, prof id or name: %s",
|
||
h.UUID,
|
||
gp.IdentifierOrName,
|
||
)
|
||
}
|
||
}
|
||
}
|
||
|
||
func testBulkSetPendingMDMHostProfiles(t *testing.T, ds *Datastore) {
|
||
ctx := context.Background()
|
||
// NOTE: this test is now a monster, it's pretty much impossible to change as it's too big
|
||
// to understand what the expected assertion 500 lines in is supposed to be. Please avoid
|
||
// adding to it.
|
||
|
||
hostIDsFromHosts := func(hosts ...*fleet.Host) []uint {
|
||
ids := make([]uint, len(hosts))
|
||
for i, h := range hosts {
|
||
ids[i] = h.ID
|
||
}
|
||
return ids
|
||
}
|
||
|
||
getProfs := func(teamID *uint) []*fleet.MDMConfigProfilePayload {
|
||
// TODO(roberto): the docs says that you can pass a comma separated
|
||
// list of columns to OrderKey, but that doesn't seem to work
|
||
profs, _, err := ds.ListMDMConfigProfiles(ctx, teamID, fleet.ListOptions{})
|
||
require.NoError(t, err)
|
||
sort.Slice(profs, func(i, j int) bool {
|
||
l, r := profs[i], profs[j]
|
||
if l.Platform != r.Platform {
|
||
return l.Platform < r.Platform
|
||
}
|
||
|
||
return l.Name < r.Name
|
||
})
|
||
return profs
|
||
}
|
||
|
||
// create some darwin hosts, all enrolled
|
||
var darwinHosts []*fleet.Host // not pre-allocating, causes gosec false positive
|
||
for i := 0; i < 3; i++ {
|
||
h, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: fmt.Sprintf("test-host%d-name", i),
|
||
OsqueryHostID: ptr.String(fmt.Sprintf("osquery-%d", i)),
|
||
NodeKey: ptr.String(fmt.Sprintf("nodekey-%d", i)),
|
||
UUID: fmt.Sprintf("test-uuid-%d", i),
|
||
Platform: "darwin",
|
||
})
|
||
require.NoError(t, err)
|
||
nanoEnroll(t, ds, h, false)
|
||
darwinHosts = append(darwinHosts, h)
|
||
t.Logf("enrolled darwin host [%d]: %s", i, h.UUID)
|
||
}
|
||
|
||
// create a non-enrolled host
|
||
i := 3
|
||
unenrolledHost, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: fmt.Sprintf("test-host%d-name", i),
|
||
OsqueryHostID: ptr.String(fmt.Sprintf("osquery-%d", i)),
|
||
NodeKey: ptr.String(fmt.Sprintf("nodekey-%d", i)),
|
||
UUID: fmt.Sprintf("test-uuid-%d", i),
|
||
Platform: "darwin",
|
||
})
|
||
require.NoError(t, err)
|
||
|
||
// create a non-darwin host
|
||
i = 4
|
||
linuxHost, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: fmt.Sprintf("test-host%d-name", i),
|
||
OsqueryHostID: ptr.String(fmt.Sprintf("osquery-%d", i)),
|
||
NodeKey: ptr.String(fmt.Sprintf("nodekey-%d", i)),
|
||
UUID: fmt.Sprintf("test-uuid-%d", i),
|
||
Platform: "linux",
|
||
})
|
||
require.NoError(t, err)
|
||
|
||
// create some windows hosts, all enrolled
|
||
i = 5
|
||
var windowsHosts []*fleet.Host // not preallocating, causes gosec false positive
|
||
for j := 0; j < 3; j++ {
|
||
h, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: fmt.Sprintf("test-host%d-name", i+j),
|
||
OsqueryHostID: ptr.String(fmt.Sprintf("osquery-%d", i+j)),
|
||
NodeKey: ptr.String(fmt.Sprintf("nodekey-%d", i+j)),
|
||
UUID: fmt.Sprintf("test-uuid-%d", i+j),
|
||
Platform: "windows",
|
||
})
|
||
require.NoError(t, err)
|
||
windowsEnroll(t, ds, h)
|
||
windowsHosts = append(windowsHosts, h)
|
||
t.Logf("enrolled windows host [%d]: %s", j, h.UUID)
|
||
}
|
||
|
||
// bulk set for no target ids, does nothing
|
||
updates, err := ds.BulkSetPendingMDMHostProfiles(ctx, nil, nil, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.False(t, updates.AppleConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
assert.False(t, updates.WindowsConfigProfile)
|
||
|
||
// bulk set for combination of target ids, not allowed
|
||
_, err = ds.BulkSetPendingMDMHostProfiles(ctx, []uint{1}, []uint{2}, nil, nil)
|
||
require.Error(t, err)
|
||
|
||
// bulk set for all created hosts, no profiles yet so nothing changed
|
||
allHosts := darwinHosts
|
||
allHosts = append(allHosts, unenrolledHost, linuxHost)
|
||
allHosts = append(allHosts, windowsHosts...)
|
||
updates, err = ds.BulkSetPendingMDMHostProfiles(ctx, hostIDsFromHosts(allHosts...), nil, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.False(t, updates.AppleConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
assert.False(t, updates.WindowsConfigProfile)
|
||
assertHostProfiles(t, ds, map[*fleet.Host][]anyProfile{
|
||
darwinHosts[0]: {},
|
||
darwinHosts[1]: {},
|
||
darwinHosts[2]: {},
|
||
unenrolledHost: {},
|
||
linuxHost: {},
|
||
windowsHosts[0]: {},
|
||
windowsHosts[1]: {},
|
||
windowsHosts[2]: {},
|
||
})
|
||
|
||
// create some global (no-team) profiles
|
||
macGlobalProfiles := []*fleet.MDMAppleConfigProfile{
|
||
configProfileForTest(t, "G1a", "G1a", "a"),
|
||
configProfileForTest(t, "G2a", "G2a", "b"),
|
||
configProfileForTest(t, "G3a", "G3a", "c"),
|
||
}
|
||
macGlobalDeclarations := []*fleet.MDMAppleDeclaration{
|
||
declForTest("G1d", "G1d", "foo"),
|
||
declForTest("G2d", "G2d", "bar"),
|
||
}
|
||
winGlobalProfiles := []*fleet.MDMWindowsConfigProfile{
|
||
windowsConfigProfileForTest(t, "G1w", "L1"),
|
||
windowsConfigProfileForTest(t, "G2w", "L2"),
|
||
windowsConfigProfileForTest(t, "G3w", "L3"),
|
||
}
|
||
updates, err = ds.BatchSetMDMProfiles(
|
||
ctx,
|
||
nil,
|
||
macGlobalProfiles,
|
||
winGlobalProfiles,
|
||
macGlobalDeclarations,
|
||
nil,
|
||
nil,
|
||
)
|
||
require.NoError(t, err)
|
||
macGlobalProfiles, err = ds.ListMDMAppleConfigProfiles(ctx, nil)
|
||
require.NoError(t, err)
|
||
require.Len(t, macGlobalProfiles, 3)
|
||
globalProfiles := getProfs(nil)
|
||
require.Len(t, globalProfiles, 8)
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.True(t, updates.AppleDeclaration)
|
||
assert.True(t, updates.WindowsConfigProfile)
|
||
|
||
// list profiles to install, should result in the global profiles for all
|
||
// enrolled hosts
|
||
toInstallDarwin, err := ds.ListMDMAppleProfilesToInstall(ctx, "")
|
||
require.NoError(t, err)
|
||
require.Len(t, toInstallDarwin, len(macGlobalProfiles)*len(darwinHosts))
|
||
toInstallWindows, err := ds.ListMDMWindowsProfilesToInstall(ctx)
|
||
require.NoError(t, err)
|
||
require.Len(t, toInstallWindows, len(winGlobalProfiles)*len(windowsHosts))
|
||
|
||
// none are listed as "to remove"
|
||
toRemoveDarwin, err := ds.ListMDMAppleProfilesToRemove(ctx)
|
||
require.NoError(t, err)
|
||
require.Len(t, toRemoveDarwin, 0)
|
||
toRemoveWindows, err := ds.ListMDMWindowsProfilesToRemove(ctx)
|
||
require.NoError(t, err)
|
||
require.Len(t, toRemoveWindows, 0)
|
||
|
||
// bulk set for all created hosts, enrolled hosts get the no-team profiles
|
||
updates, err = ds.BulkSetPendingMDMHostProfiles(ctx, hostIDsFromHosts(allHosts...), nil, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.True(t, updates.AppleDeclaration)
|
||
assert.True(t, updates.WindowsConfigProfile)
|
||
assertHostProfiles(t, ds, map[*fleet.Host][]anyProfile{
|
||
darwinHosts[0]: {
|
||
{
|
||
ProfileUUID: globalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[0].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[1].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[2].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[3].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[4].Identifier,
|
||
},
|
||
},
|
||
darwinHosts[1]: {
|
||
{
|
||
ProfileUUID: globalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[0].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[1].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[2].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[3].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[4].Identifier,
|
||
},
|
||
},
|
||
darwinHosts[2]: {
|
||
{
|
||
ProfileUUID: globalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[0].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[1].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[2].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[3].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[4].Identifier,
|
||
},
|
||
},
|
||
unenrolledHost: {},
|
||
linuxHost: {},
|
||
windowsHosts[0]: {
|
||
{
|
||
ProfileUUID: globalProfiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[1]: {
|
||
{
|
||
ProfileUUID: globalProfiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[2]: {
|
||
{
|
||
ProfileUUID: globalProfiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
})
|
||
|
||
// create a team
|
||
team1, err := ds.NewTeam(ctx, &fleet.Team{Name: "team 1"})
|
||
require.NoError(t, err)
|
||
|
||
// move darwinHosts[0] and windowsHosts[0] to that team
|
||
err = ds.AddHostsToTeam(ctx, fleet.NewAddHostsToTeamParams(&team1.ID, []uint{darwinHosts[0].ID, windowsHosts[0].ID}))
|
||
require.NoError(t, err)
|
||
|
||
// 6 are still reported as "to install" because op=install and status=nil
|
||
toInstallDarwin, err = ds.ListMDMAppleProfilesToInstall(ctx, "")
|
||
require.NoError(t, err)
|
||
require.Len(t, toInstallDarwin, 6)
|
||
toInstallWindows, err = ds.ListMDMWindowsProfilesToInstall(ctx)
|
||
require.NoError(t, err)
|
||
require.Len(t, toInstallWindows, 6)
|
||
|
||
// those installed to enrolledHosts[0] are listed as "to remove"
|
||
toRemoveDarwin, err = ds.ListMDMAppleProfilesToRemove(ctx)
|
||
require.NoError(t, err)
|
||
require.Len(t, toRemoveDarwin, 3)
|
||
toRemoveWindows, err = ds.ListMDMWindowsProfilesToRemove(ctx)
|
||
require.NoError(t, err)
|
||
require.Len(t, toRemoveWindows, 3)
|
||
|
||
// update status of the moved host (team has no profiles)
|
||
updates, err = ds.BulkSetPendingMDMHostProfiles(
|
||
ctx,
|
||
hostIDsFromHosts(darwinHosts[0], windowsHosts[0]),
|
||
nil,
|
||
nil,
|
||
nil,
|
||
)
|
||
require.NoError(t, err)
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.True(t, updates.AppleDeclaration)
|
||
assert.True(t, updates.WindowsConfigProfile)
|
||
assertHostProfiles(t, ds, map[*fleet.Host][]anyProfile{
|
||
darwinHosts[0]: {
|
||
{
|
||
ProfileUUID: globalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[0].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[1].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[2].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[3].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[4].Identifier,
|
||
},
|
||
},
|
||
darwinHosts[1]: {
|
||
{
|
||
ProfileUUID: globalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[0].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[1].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[2].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[3].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[4].Identifier,
|
||
},
|
||
},
|
||
darwinHosts[2]: {
|
||
{
|
||
ProfileUUID: globalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[0].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[1].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[2].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[3].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[4].Identifier,
|
||
},
|
||
},
|
||
unenrolledHost: {},
|
||
linuxHost: {},
|
||
windowsHosts[0]: {
|
||
{
|
||
ProfileUUID: globalProfiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
},
|
||
windowsHosts[1]: {
|
||
{
|
||
ProfileUUID: globalProfiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[2]: {
|
||
{
|
||
ProfileUUID: globalProfiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
})
|
||
|
||
// create another team
|
||
team2, err := ds.NewTeam(ctx, &fleet.Team{Name: "team 2"})
|
||
require.NoError(t, err)
|
||
|
||
// move enrolledHosts[1] to that team
|
||
err = ds.AddHostsToTeam(ctx, fleet.NewAddHostsToTeamParams(&team2.ID, []uint{darwinHosts[1].ID, windowsHosts[1].ID}))
|
||
require.NoError(t, err)
|
||
|
||
// 3 are still reported as "to install" because op=install and status=nil
|
||
toInstallDarwin, err = ds.ListMDMAppleProfilesToInstall(ctx, "")
|
||
require.NoError(t, err)
|
||
require.Len(t, toInstallDarwin, 3)
|
||
toInstallWindows, err = ds.ListMDMWindowsProfilesToInstall(ctx)
|
||
require.NoError(t, err)
|
||
require.Len(t, toInstallWindows, 3)
|
||
|
||
// 6 are now "to remove" for darwin
|
||
toRemoveDarwin, err = ds.ListMDMAppleProfilesToRemove(ctx)
|
||
require.NoError(t, err)
|
||
require.Len(t, toRemoveDarwin, 6)
|
||
// 6 are now "to remove" for windows (3 for windowsHosts[0] marked remove+NULL, 3 for windowsHosts[1] install+NULL)
|
||
toRemoveWindows, err = ds.ListMDMWindowsProfilesToRemove(ctx)
|
||
require.NoError(t, err)
|
||
require.Len(t, toRemoveWindows, 6)
|
||
|
||
// update status of the moved host via its uuid (team has no profiles)
|
||
updates, err = ds.BulkSetPendingMDMHostProfiles(
|
||
ctx,
|
||
nil,
|
||
nil,
|
||
nil,
|
||
[]string{darwinHosts[1].UUID, windowsHosts[1].UUID},
|
||
)
|
||
require.NoError(t, err)
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.True(t, updates.AppleDeclaration)
|
||
assert.True(t, updates.WindowsConfigProfile)
|
||
assertHostProfiles(t, ds, map[*fleet.Host][]anyProfile{
|
||
darwinHosts[0]: {
|
||
{
|
||
ProfileUUID: globalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[0].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[1].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[2].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[3].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[4].Identifier,
|
||
},
|
||
},
|
||
darwinHosts[1]: {
|
||
{
|
||
ProfileUUID: globalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[0].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[1].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[2].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[3].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[4].Identifier,
|
||
},
|
||
},
|
||
darwinHosts[2]: {
|
||
{
|
||
ProfileUUID: globalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[0].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[1].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[2].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[3].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[4].Identifier,
|
||
},
|
||
},
|
||
unenrolledHost: {},
|
||
linuxHost: {},
|
||
windowsHosts[0]: {
|
||
{
|
||
ProfileUUID: globalProfiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
},
|
||
// windows profiles are now marked for removal instead of being directly deleted
|
||
windowsHosts[1]: {
|
||
{
|
||
ProfileUUID: globalProfiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
},
|
||
windowsHosts[2]: {
|
||
{
|
||
ProfileUUID: globalProfiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
})
|
||
|
||
// create profiles for team 1
|
||
tm1DarwinProfiles := []*fleet.MDMAppleConfigProfile{
|
||
configProfileForTest(t, "T1.1a", "T1.1a", "d"),
|
||
configProfileForTest(t, "T1.2a", "T1.2a", "e"),
|
||
}
|
||
tm1WindowsProfiles := []*fleet.MDMWindowsConfigProfile{
|
||
windowsConfigProfileForTest(t, "T1.1w", "T1.1"),
|
||
windowsConfigProfileForTest(t, "T1.2w", "T1.2"),
|
||
}
|
||
updates, err = ds.BatchSetMDMProfiles(ctx, &team1.ID, tm1DarwinProfiles, tm1WindowsProfiles, nil, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
assert.True(t, updates.WindowsConfigProfile)
|
||
|
||
tm1Profiles := getProfs(&team1.ID)
|
||
require.Len(t, tm1Profiles, 4)
|
||
|
||
// 5 are now reported as "to install" (3 global + 2 team1)
|
||
toInstallDarwin, err = ds.ListMDMAppleProfilesToInstall(ctx, "")
|
||
require.NoError(t, err)
|
||
require.Len(t, toInstallDarwin, 5)
|
||
toInstallWindows, err = ds.ListMDMWindowsProfilesToInstall(ctx)
|
||
require.NoError(t, err)
|
||
require.Len(t, toInstallWindows, 5)
|
||
|
||
// 6 are still "to remove"
|
||
toRemoveDarwin, err = ds.ListMDMAppleProfilesToRemove(ctx)
|
||
require.NoError(t, err)
|
||
require.Len(t, toRemoveDarwin, 6)
|
||
// 6 profiles still to remove in windows (3 remove+NULL on windowsHosts[0], 3 remove+NULL on windowsHosts[1])
|
||
toRemoveWindows, err = ds.ListMDMWindowsProfilesToRemove(ctx)
|
||
require.NoError(t, err)
|
||
require.Len(t, toRemoveWindows, 6)
|
||
|
||
// update status of the affected team
|
||
updates, err = ds.BulkSetPendingMDMHostProfiles(ctx, nil, []uint{team1.ID}, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
assert.True(t, updates.WindowsConfigProfile)
|
||
assertHostProfiles(t, ds, map[*fleet.Host][]anyProfile{
|
||
darwinHosts[0]: {
|
||
{
|
||
ProfileUUID: globalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[0].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[1].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[2].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[3].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[4].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: tm1Profiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: tm1Profiles[0].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: tm1Profiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: tm1Profiles[1].Identifier,
|
||
},
|
||
},
|
||
darwinHosts[1]: {
|
||
{
|
||
ProfileUUID: globalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[0].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[1].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[2].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[3].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[4].Identifier,
|
||
},
|
||
},
|
||
darwinHosts[2]: {
|
||
{
|
||
ProfileUUID: globalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[0].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[1].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[2].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[3].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[4].Identifier,
|
||
},
|
||
},
|
||
unenrolledHost: {},
|
||
linuxHost: {},
|
||
windowsHosts[0]: {
|
||
{
|
||
ProfileUUID: globalProfiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: tm1Profiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: tm1Profiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[1]: {
|
||
{
|
||
ProfileUUID: globalProfiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
},
|
||
windowsHosts[2]: {
|
||
{
|
||
ProfileUUID: globalProfiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
})
|
||
|
||
darwinGlobalProfiles, err := ds.ListMDMAppleConfigProfiles(ctx, nil)
|
||
sort.Slice(darwinGlobalProfiles, func(i, j int) bool {
|
||
l, r := darwinGlobalProfiles[i], darwinGlobalProfiles[j]
|
||
return l.Name < r.Name
|
||
})
|
||
require.NoError(t, err)
|
||
|
||
// successfully remove globalProfiles[0, 1] for darwinHosts[0], and remove as
|
||
// failed globalProfiles[2] Do *not* use UpdateOrDeleteHostMDMAppleProfile
|
||
// here, as it deletes/updates based on command uuid (meant to be called from
|
||
// the MDMDirector in response from MDM commands), it would delete/update all
|
||
// rows in this test since we don't have command uuids.
|
||
err = ds.BulkUpsertMDMAppleHostProfiles(ctx, []*fleet.MDMAppleBulkUpsertHostProfilePayload{
|
||
{
|
||
HostUUID: darwinHosts[0].UUID, ProfileUUID: darwinGlobalProfiles[0].ProfileUUID, ProfileIdentifier: darwinGlobalProfiles[0].Identifier,
|
||
Status: &fleet.MDMDeliveryVerifying, OperationType: fleet.MDMOperationTypeRemove, Checksum: []byte("csum"), Scope: fleet.PayloadScopeSystem,
|
||
},
|
||
{
|
||
HostUUID: darwinHosts[0].UUID, ProfileUUID: darwinGlobalProfiles[1].ProfileUUID, ProfileIdentifier: darwinGlobalProfiles[1].Identifier,
|
||
Status: &fleet.MDMDeliveryVerifying, OperationType: fleet.MDMOperationTypeRemove, Checksum: []byte("csum"), Scope: fleet.PayloadScopeSystem,
|
||
},
|
||
{
|
||
HostUUID: darwinHosts[0].UUID, ProfileUUID: darwinGlobalProfiles[2].ProfileUUID, ProfileIdentifier: darwinGlobalProfiles[2].Identifier,
|
||
Status: &fleet.MDMDeliveryFailed, OperationType: fleet.MDMOperationTypeRemove, Checksum: []byte("csum"), Scope: fleet.PayloadScopeSystem,
|
||
},
|
||
})
|
||
require.NoError(t, err)
|
||
|
||
// add a profile to team1, and remove profile T1.1 on Apple, T1.2 on Windows
|
||
newTm1DarwinProfiles := []*fleet.MDMAppleConfigProfile{
|
||
configProfileForTest(t, "T1.2a", "T1.2a", "e"),
|
||
configProfileForTest(t, "T1.3a", "T1.3a", "f"),
|
||
}
|
||
newTm1WindowsProfiles := []*fleet.MDMWindowsConfigProfile{
|
||
windowsConfigProfileForTest(t, "T1.1w", "T1.1"),
|
||
windowsConfigProfileForTest(t, "T1.3w", "T1.3"),
|
||
}
|
||
|
||
updates, err = ds.BatchSetMDMProfiles(ctx, &team1.ID, newTm1DarwinProfiles, newTm1WindowsProfiles, nil, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.True(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
newTm1Profiles := getProfs(&team1.ID)
|
||
require.Len(t, newTm1Profiles, 4)
|
||
|
||
// update status of the affected team
|
||
updates, err = ds.BulkSetPendingMDMHostProfiles(ctx, nil, []uint{team1.ID}, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.True(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
assertHostProfiles(t, ds, map[*fleet.Host][]anyProfile{
|
||
darwinHosts[0]: {
|
||
{
|
||
ProfileUUID: newTm1Profiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: newTm1Profiles[0].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: newTm1Profiles[1].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[3].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[1].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryFailed,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[4].Identifier,
|
||
},
|
||
},
|
||
darwinHosts[1]: {
|
||
{
|
||
ProfileUUID: globalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[0].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[1].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[2].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[3].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[4].Identifier,
|
||
},
|
||
},
|
||
darwinHosts[2]: {
|
||
{
|
||
ProfileUUID: globalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[0].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[1].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[2].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[3].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[4].Identifier,
|
||
},
|
||
},
|
||
unenrolledHost: {},
|
||
linuxHost: {},
|
||
windowsHosts[0]: {
|
||
{
|
||
ProfileUUID: newTm1Profiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[1]: {},
|
||
windowsHosts[2]: {
|
||
{
|
||
ProfileUUID: globalProfiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
})
|
||
|
||
// update again -- nothing should change
|
||
updates, err = ds.BulkSetPendingMDMHostProfiles(ctx, nil, []uint{team1.ID}, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.False(t, updates.AppleConfigProfile)
|
||
assert.False(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
// re-add tm1Profiles[0] to list of team1 profiles (T1.1 on Apple, T1.2 on Windows)
|
||
// NOTE: even though it is the same profile, it's unique DB ID is different because
|
||
// it got deleted and re-inserted from the team's profiles, so this is reflected in
|
||
// the host's profiles list.
|
||
newTm1DarwinProfiles = []*fleet.MDMAppleConfigProfile{
|
||
tm1DarwinProfiles[0], //nolint:gosec // dismiss G602
|
||
configProfileForTest(t, "T1.2a", "T1.2a", "e"),
|
||
configProfileForTest(t, "T1.3a", "T1.3a", "f"),
|
||
}
|
||
newTm1WindowsProfiles = []*fleet.MDMWindowsConfigProfile{
|
||
windowsConfigProfileForTest(t, "T1.1w", "T1.1"),
|
||
tm1WindowsProfiles[1], //nolint:gosec // dismiss G602
|
||
windowsConfigProfileForTest(t, "T1.3w", "T1.3"),
|
||
}
|
||
|
||
updates, err = ds.BatchSetMDMProfiles(ctx, &team1.ID, newTm1DarwinProfiles, newTm1WindowsProfiles, nil, nil, nil)
|
||
require.NoError(t, err)
|
||
newTm1Profiles = getProfs(&team1.ID)
|
||
require.Len(t, newTm1Profiles, 6)
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.True(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
// update status of the affected team
|
||
updates, err = ds.BulkSetPendingMDMHostProfiles(ctx, nil, []uint{team1.ID}, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.True(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
assertHostProfiles(t, ds, map[*fleet.Host][]anyProfile{
|
||
darwinHosts[0]: {
|
||
{
|
||
ProfileUUID: newTm1Profiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: newTm1Profiles[0].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: newTm1Profiles[1].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: newTm1Profiles[2].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[3].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[1].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryFailed,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[4].Identifier,
|
||
},
|
||
},
|
||
darwinHosts[1]: {
|
||
{
|
||
ProfileUUID: globalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[0].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[1].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[2].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[3].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[4].Identifier,
|
||
},
|
||
},
|
||
darwinHosts[2]: {
|
||
{
|
||
ProfileUUID: globalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[0].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[1].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[2].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[3].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: globalProfiles[4].Identifier,
|
||
},
|
||
},
|
||
unenrolledHost: {},
|
||
linuxHost: {},
|
||
windowsHosts[0]: {
|
||
{
|
||
ProfileUUID: newTm1Profiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[1]: {},
|
||
windowsHosts[2]: {
|
||
{
|
||
ProfileUUID: globalProfiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
})
|
||
|
||
// remove a global profile and add a new one
|
||
|
||
newDarwinGlobalProfiles := []*fleet.MDMAppleConfigProfile{
|
||
configProfileForTest(t, "G2a", "G2a", "b"),
|
||
configProfileForTest(t, "G3a", "G3a", "c"),
|
||
configProfileForTest(t, "G4a", "G4a", "d"),
|
||
}
|
||
newWindowsGlobalProfiles := []*fleet.MDMWindowsConfigProfile{
|
||
windowsConfigProfileForTest(t, "G2w", "G2"),
|
||
windowsConfigProfileForTest(t, "G3w", "G3"),
|
||
windowsConfigProfileForTest(t, "G4w", "G4"),
|
||
}
|
||
|
||
// TODO(roberto): add new darwin declarations for this and all subsequent assertions
|
||
updates, err = ds.BatchSetMDMProfiles(ctx, nil, newDarwinGlobalProfiles, newWindowsGlobalProfiles, nil, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.True(t, updates.WindowsConfigProfile)
|
||
assert.True(t, updates.AppleDeclaration)
|
||
|
||
newGlobalProfiles := getProfs(nil)
|
||
require.Len(t, newGlobalProfiles, 6)
|
||
|
||
// update status of the affected "no-team"
|
||
updates, err = ds.BulkSetPendingMDMHostProfiles(ctx, nil, []uint{0}, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.True(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration) // host status for deleted declaration was already set on delete
|
||
|
||
require.NoError(t, ds.MDMAppleStoreDDMStatusReport(ctx, darwinHosts[0].UUID, nil))
|
||
require.NoError(t, ds.MDMAppleStoreDDMStatusReport(ctx, darwinHosts[1].UUID, nil))
|
||
require.NoError(t, ds.MDMAppleStoreDDMStatusReport(ctx, darwinHosts[2].UUID, nil))
|
||
|
||
assertHostProfiles(t, ds, map[*fleet.Host][]anyProfile{
|
||
darwinHosts[0]: {
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryFailed,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[4].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: newTm1Profiles[0].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: newTm1Profiles[1].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: newTm1Profiles[2].Identifier,
|
||
},
|
||
},
|
||
darwinHosts[1]: {
|
||
{
|
||
ProfileUUID: globalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
},
|
||
darwinHosts[2]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
unenrolledHost: {},
|
||
linuxHost: {},
|
||
windowsHosts[0]: {
|
||
{
|
||
ProfileUUID: newTm1Profiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[1]: {},
|
||
windowsHosts[2]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
})
|
||
|
||
// add another global profile
|
||
|
||
newDarwinGlobalProfiles = []*fleet.MDMAppleConfigProfile{
|
||
configProfileForTest(t, "G2a", "G2a", "b"),
|
||
configProfileForTest(t, "G3a", "G3a", "c"),
|
||
configProfileForTest(t, "G4a", "G4a", "d"),
|
||
configProfileForTest(t, "G5a", "G5a", "e"),
|
||
}
|
||
|
||
newWindowsGlobalProfiles = []*fleet.MDMWindowsConfigProfile{
|
||
windowsConfigProfileForTest(t, "G2w", "G2"),
|
||
windowsConfigProfileForTest(t, "G3w", "G3"),
|
||
windowsConfigProfileForTest(t, "G4w", "G4"),
|
||
windowsConfigProfileForTest(t, "G5w", "G5"),
|
||
}
|
||
|
||
updates, err = ds.BatchSetMDMProfiles(ctx, nil, newDarwinGlobalProfiles, newWindowsGlobalProfiles, nil, nil, nil)
|
||
require.NoError(t, err)
|
||
newGlobalProfiles = getProfs(nil)
|
||
require.Len(t, newGlobalProfiles, 8)
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.True(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
// bulk-set only those affected by the new Apple global profile
|
||
newDarwinProfileUUID := newGlobalProfiles[3].ProfileUUID
|
||
updates, err = ds.BulkSetPendingMDMHostProfiles(ctx, nil, nil, []string{newDarwinProfileUUID}, nil)
|
||
require.NoError(t, err)
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.False(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
assertHostProfiles(t, ds, map[*fleet.Host][]anyProfile{
|
||
darwinHosts[0]: {
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryFailed,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
darwinHosts[1]: {
|
||
{
|
||
ProfileUUID: globalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
},
|
||
darwinHosts[2]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
unenrolledHost: {},
|
||
linuxHost: {},
|
||
windowsHosts[0]: {
|
||
{
|
||
ProfileUUID: newTm1Profiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[1]: {},
|
||
windowsHosts[2]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
})
|
||
|
||
// bulk-set only those affected by the new Apple global profile
|
||
newWindowsProfileUUID := newGlobalProfiles[7].ProfileUUID
|
||
updates, err = ds.BulkSetPendingMDMHostProfiles(ctx, nil, nil, []string{newWindowsProfileUUID}, nil)
|
||
require.NoError(t, err)
|
||
assert.False(t, updates.AppleConfigProfile)
|
||
assert.True(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
assertHostProfiles(t, ds, map[*fleet.Host][]anyProfile{
|
||
darwinHosts[0]: {
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryFailed,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
darwinHosts[1]: {
|
||
{
|
||
ProfileUUID: globalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
},
|
||
darwinHosts[2]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
unenrolledHost: {},
|
||
linuxHost: {},
|
||
windowsHosts[0]: {
|
||
{
|
||
ProfileUUID: newTm1Profiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[1]: {},
|
||
windowsHosts[2]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
})
|
||
|
||
// add a profile to team2
|
||
|
||
tm2DarwinProfiles := []*fleet.MDMAppleConfigProfile{
|
||
configProfileForTest(t, "T2.1a", "T2.1a", "a"),
|
||
}
|
||
|
||
tm2WindowsProfiles := []*fleet.MDMWindowsConfigProfile{
|
||
windowsConfigProfileForTest(t, "T2.1w", "T2.1"),
|
||
}
|
||
|
||
updates, err = ds.BatchSetMDMProfiles(ctx, &team2.ID, tm2DarwinProfiles, tm2WindowsProfiles, nil, nil, nil)
|
||
require.NoError(t, err)
|
||
tm2Profiles := getProfs(&team2.ID)
|
||
require.Len(t, tm2Profiles, 2)
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.True(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
// update status via tm2 id and the global 0 id to test that custom sql statement
|
||
updates, err = ds.BulkSetPendingMDMHostProfiles(ctx, nil, []uint{team2.ID, 0}, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.True(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
assertHostProfiles(t, ds, map[*fleet.Host][]anyProfile{
|
||
darwinHosts[0]: {
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryFailed,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
darwinHosts[1]: {
|
||
{
|
||
ProfileUUID: globalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: tm2Profiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
darwinHosts[2]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
unenrolledHost: {},
|
||
linuxHost: {},
|
||
windowsHosts[0]: {
|
||
{
|
||
ProfileUUID: newTm1Profiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[1]: {
|
||
{
|
||
ProfileUUID: tm2Profiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[2]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
})
|
||
|
||
// create some labels for label-based profiles
|
||
var labels []*fleet.Label
|
||
for i := 0; i < 6; i++ {
|
||
lbl, err := ds.NewLabel(ctx, &fleet.Label{Name: "l" + strconv.Itoa(i), Query: "select 1"})
|
||
require.NoError(t, err)
|
||
labels = append(labels, lbl)
|
||
}
|
||
|
||
// TODO(mna): temporary, until BatchSetMDMProfiles supports labels
|
||
setProfileLabels := func(t *testing.T, p *fleet.MDMConfigProfilePayload, labels ...*fleet.Label) {
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
if _, err := q.ExecContext(ctx, `DELETE FROM mdm_configuration_profile_labels WHERE apple_profile_uuid = ? OR windows_profile_uuid = ?`, p.ProfileUUID, p.ProfileUUID); err != nil {
|
||
return err
|
||
}
|
||
|
||
var auuid, wuuid *string
|
||
if p.Platform == "windows" {
|
||
wuuid = &p.ProfileUUID
|
||
} else {
|
||
auuid = &p.ProfileUUID
|
||
}
|
||
for _, lbl := range labels {
|
||
if _, err := q.ExecContext(ctx, `INSERT INTO mdm_configuration_profile_labels
|
||
(apple_profile_uuid, windows_profile_uuid, label_name, label_id)
|
||
VALUES
|
||
(?, ?, ?, ?)`, auuid, wuuid, lbl.Name, lbl.ID); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
return err
|
||
})
|
||
}
|
||
|
||
// create two global label-based profiles for each OS, and two team-based
|
||
newDarwinGlobalProfiles = []*fleet.MDMAppleConfigProfile{
|
||
configProfileForTest(t, "G2a", "G2a", "b"),
|
||
configProfileForTest(t, "G3a", "G3a", "c"),
|
||
configProfileForTest(t, "G4a", "G4a", "d"),
|
||
configProfileForTest(t, "G5a", "G5a", "e"),
|
||
configProfileForTest(t, "G6a", "G6a", "f", labels[0], labels[1]),
|
||
configProfileForTest(t, "G7a", "G7a", "g", labels[2]),
|
||
}
|
||
|
||
newWindowsGlobalProfiles = []*fleet.MDMWindowsConfigProfile{
|
||
windowsConfigProfileForTest(t, "G2w", "G2"),
|
||
windowsConfigProfileForTest(t, "G3w", "G3"),
|
||
windowsConfigProfileForTest(t, "G4w", "G4"),
|
||
windowsConfigProfileForTest(t, "G5w", "G5"),
|
||
windowsConfigProfileForTest(t, "G6w", "G6", labels[3], labels[4]),
|
||
windowsConfigProfileForTest(t, "G7w", "G7", labels[5]),
|
||
}
|
||
|
||
updates, err = ds.BatchSetMDMProfiles(ctx, nil, newDarwinGlobalProfiles, newWindowsGlobalProfiles, nil, nil, nil)
|
||
require.NoError(t, err)
|
||
newGlobalProfiles = getProfs(nil)
|
||
require.Len(t, newGlobalProfiles, 12)
|
||
// TODO(mna): temporary until BatchSetMDMProfiles supports labels
|
||
setProfileLabels(t, newGlobalProfiles[4], labels[0], labels[1])
|
||
setProfileLabels(t, newGlobalProfiles[5], labels[2])
|
||
setProfileLabels(t, newGlobalProfiles[10], labels[3], labels[4])
|
||
setProfileLabels(t, newGlobalProfiles[11], labels[5])
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.True(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
// simulate an entry with some values set to NULL
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
_, err := q.ExecContext(
|
||
ctx,
|
||
`UPDATE host_mdm_apple_profiles SET detail = NULL WHERE profile_uuid = ?`,
|
||
globalProfiles[2].ProfileUUID,
|
||
)
|
||
return err
|
||
})
|
||
|
||
// do a sync of all hosts, should not change anything as no host is a member
|
||
// of the new label-based profiles (indices change due to new Apple and
|
||
// Windows profiles)
|
||
updates, err = ds.BulkSetPendingMDMHostProfiles(
|
||
ctx,
|
||
hostIDsFromHosts(
|
||
append(darwinHosts, append(windowsHosts, unenrolledHost, linuxHost)...)...),
|
||
nil,
|
||
nil,
|
||
nil,
|
||
)
|
||
require.NoError(t, err)
|
||
assert.False(t, updates.AppleConfigProfile)
|
||
assert.False(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
assertHostProfiles(t, ds, map[*fleet.Host][]anyProfile{
|
||
darwinHosts[0]: {
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryFailed,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
darwinHosts[1]: {
|
||
{
|
||
ProfileUUID: globalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[0].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[2].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[4].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: tm2Profiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: tm2Profiles[0].Identifier,
|
||
},
|
||
},
|
||
darwinHosts[2]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
unenrolledHost: {},
|
||
linuxHost: {},
|
||
windowsHosts[0]: {
|
||
{
|
||
ProfileUUID: newTm1Profiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[1]: {
|
||
{
|
||
ProfileUUID: tm2Profiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[2]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[8].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[9].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
})
|
||
|
||
// create a new Apple and Windows hosts, global (no team)
|
||
i = 8
|
||
h, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: fmt.Sprintf("test-host%d-name", i),
|
||
OsqueryHostID: ptr.String(fmt.Sprintf("osquery-%d", i)),
|
||
NodeKey: ptr.String(fmt.Sprintf("nodekey-%d", i)),
|
||
UUID: fmt.Sprintf("test-uuid-%d", i),
|
||
Platform: "windows",
|
||
})
|
||
require.NoError(t, err)
|
||
windowsEnroll(t, ds, h)
|
||
windowsHosts = append(windowsHosts, h)
|
||
t.Logf("enrolled windows host [%d]: %s", len(windowsHosts)-1, h.UUID)
|
||
|
||
i = 9
|
||
h, err = ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: fmt.Sprintf("test-host%d-name", i),
|
||
OsqueryHostID: ptr.String(fmt.Sprintf("osquery-%d", i)),
|
||
NodeKey: ptr.String(fmt.Sprintf("nodekey-%d", i)),
|
||
UUID: fmt.Sprintf("test-uuid-%d", i),
|
||
Platform: "darwin",
|
||
})
|
||
require.NoError(t, err)
|
||
nanoEnroll(t, ds, h, false)
|
||
darwinHosts = append(darwinHosts, h)
|
||
t.Logf("enrolled darwin host [%d]: %s", len(darwinHosts)-1, h.UUID)
|
||
|
||
// make the new Apple host a member of labels[0] and [1]
|
||
// make the new Windows host a member of labels[3] and [4]
|
||
err = ds.AsyncBatchInsertLabelMembership(ctx, [][2]uint{
|
||
{labels[0].ID, darwinHosts[3].ID},
|
||
{labels[1].ID, darwinHosts[3].ID},
|
||
{labels[3].ID, windowsHosts[3].ID},
|
||
{labels[4].ID, windowsHosts[3].ID},
|
||
})
|
||
require.NoError(t, err)
|
||
|
||
// do a full sync, the new global hosts get the standard global profiles and
|
||
// also the label-based profile that they are a member of
|
||
updates, err = ds.BulkSetPendingMDMHostProfiles(
|
||
ctx,
|
||
hostIDsFromHosts(
|
||
append(darwinHosts, append(windowsHosts, unenrolledHost, linuxHost)...)...),
|
||
nil,
|
||
nil,
|
||
nil,
|
||
)
|
||
require.NoError(t, err)
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.True(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
assertHostProfiles(t, ds, map[*fleet.Host][]anyProfile{
|
||
darwinHosts[0]: {
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryFailed,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
darwinHosts[1]: {
|
||
{
|
||
ProfileUUID: globalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: tm2Profiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
darwinHosts[2]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
darwinHosts[3]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
unenrolledHost: {},
|
||
linuxHost: {},
|
||
windowsHosts[0]: {
|
||
{
|
||
ProfileUUID: newTm1Profiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[1]: {
|
||
{
|
||
ProfileUUID: tm2Profiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[2]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[8].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[9].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[3]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[8].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[9].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[10].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
})
|
||
|
||
// make the darwinHosts[2] host a member of all labels
|
||
// make the windowsHosts[2] host a member of all labels
|
||
err = ds.AsyncBatchInsertLabelMembership(ctx, [][2]uint{
|
||
{labels[0].ID, darwinHosts[2].ID},
|
||
{labels[1].ID, darwinHosts[2].ID},
|
||
{labels[2].ID, darwinHosts[2].ID},
|
||
{labels[3].ID, darwinHosts[2].ID},
|
||
{labels[4].ID, darwinHosts[2].ID},
|
||
{labels[5].ID, darwinHosts[2].ID},
|
||
{labels[0].ID, windowsHosts[2].ID},
|
||
{labels[1].ID, windowsHosts[2].ID},
|
||
{labels[2].ID, windowsHosts[2].ID},
|
||
{labels[3].ID, windowsHosts[2].ID},
|
||
{labels[4].ID, windowsHosts[2].ID},
|
||
{labels[5].ID, windowsHosts[2].ID},
|
||
})
|
||
require.NoError(t, err)
|
||
|
||
// do a sync of those hosts, they will get the two label-based profiles of their platform
|
||
updates, err = ds.BulkSetPendingMDMHostProfiles(
|
||
ctx,
|
||
hostIDsFromHosts(darwinHosts[2], windowsHosts[2]),
|
||
nil,
|
||
nil,
|
||
nil,
|
||
)
|
||
require.NoError(t, err)
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.True(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
assertHostProfiles(t, ds, map[*fleet.Host][]anyProfile{
|
||
darwinHosts[0]: {
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryFailed,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
darwinHosts[1]: {
|
||
{
|
||
ProfileUUID: globalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: tm2Profiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
darwinHosts[2]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
darwinHosts[3]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
unenrolledHost: {},
|
||
linuxHost: {},
|
||
windowsHosts[0]: {
|
||
{
|
||
ProfileUUID: newTm1Profiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[1]: {
|
||
{
|
||
ProfileUUID: tm2Profiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[2]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[8].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[9].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[10].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[11].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[3]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[8].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[9].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[10].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
})
|
||
|
||
// "break" the two G6 label-based profile by deleting labels[0] and [3]
|
||
require.NoError(t, ds.DeleteLabel(ctx, labels[0].Name, fleet.TeamFilter{User: &fleet.User{GlobalRole: ptr.String(fleet.RoleAdmin)}}))
|
||
require.NoError(t, ds.DeleteLabel(ctx, labels[3].Name, fleet.TeamFilter{User: &fleet.User{GlobalRole: ptr.String(fleet.RoleAdmin)}}))
|
||
|
||
// sync the affected profiles
|
||
updates, err = ds.BulkSetPendingMDMHostProfiles(
|
||
ctx,
|
||
nil,
|
||
nil,
|
||
[]string{newGlobalProfiles[4].ProfileUUID},
|
||
nil,
|
||
)
|
||
require.NoError(t, err)
|
||
assert.False(t, updates.AppleConfigProfile)
|
||
assert.False(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
updates, err = ds.BulkSetPendingMDMHostProfiles(
|
||
ctx,
|
||
nil,
|
||
nil,
|
||
[]string{newGlobalProfiles[10].ProfileUUID},
|
||
nil,
|
||
)
|
||
require.NoError(t, err)
|
||
assert.False(t, updates.AppleConfigProfile)
|
||
assert.False(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
// nothing changes - broken label-based profiles are simply ignored
|
||
assertHostProfiles(t, ds, map[*fleet.Host][]anyProfile{
|
||
darwinHosts[0]: {
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryFailed,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
darwinHosts[1]: {
|
||
{
|
||
ProfileUUID: globalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: tm2Profiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
darwinHosts[2]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
darwinHosts[3]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
unenrolledHost: {},
|
||
linuxHost: {},
|
||
windowsHosts[0]: {
|
||
{
|
||
ProfileUUID: newTm1Profiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[1]: {
|
||
{
|
||
ProfileUUID: tm2Profiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[2]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[8].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[9].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[10].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[11].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[3]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[8].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[9].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[10].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
})
|
||
|
||
// update darwin/windows[2] so they are not members of labels[1][2] and [4][5], which
|
||
// should remove the G7 label-based profile, but not G6 as it is broken.
|
||
err = ds.AsyncBatchDeleteLabelMembership(ctx, [][2]uint{
|
||
{labels[1].ID, darwinHosts[2].ID},
|
||
{labels[2].ID, darwinHosts[2].ID},
|
||
{labels[4].ID, windowsHosts[2].ID},
|
||
{labels[5].ID, windowsHosts[2].ID},
|
||
})
|
||
require.NoError(t, err)
|
||
|
||
updates, err = ds.BulkSetPendingMDMHostProfiles(
|
||
ctx,
|
||
hostIDsFromHosts(darwinHosts[2], windowsHosts[2]),
|
||
nil,
|
||
nil,
|
||
nil,
|
||
)
|
||
require.NoError(t, err)
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.True(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
assertHostProfiles(t, ds, map[*fleet.Host][]anyProfile{
|
||
darwinHosts[0]: {
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryFailed,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
darwinHosts[1]: {
|
||
{
|
||
ProfileUUID: globalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: tm2Profiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
darwinHosts[2]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
},
|
||
darwinHosts[3]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
unenrolledHost: {},
|
||
linuxHost: {},
|
||
windowsHosts[0]: {
|
||
{
|
||
ProfileUUID: newTm1Profiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[1]: {
|
||
{
|
||
ProfileUUID: tm2Profiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[2]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[8].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[9].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[10].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[3]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[8].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[9].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[10].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
})
|
||
|
||
// "unbreak" the two G6 label-based profiles by removing the deleted labels
|
||
// from their requirements
|
||
setProfileLabels(t, newGlobalProfiles[4], labels[1])
|
||
setProfileLabels(t, newGlobalProfiles[10], labels[4])
|
||
|
||
updates, err = ds.BulkSetPendingMDMHostProfiles(
|
||
ctx,
|
||
nil,
|
||
nil,
|
||
[]string{newGlobalProfiles[4].ProfileUUID},
|
||
nil,
|
||
)
|
||
require.NoError(t, err)
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.False(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
updates, err = ds.BulkSetPendingMDMHostProfiles(
|
||
ctx,
|
||
nil,
|
||
nil,
|
||
[]string{newGlobalProfiles[10].ProfileUUID},
|
||
nil,
|
||
)
|
||
require.NoError(t, err)
|
||
assert.False(t, updates.AppleConfigProfile)
|
||
assert.True(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
assertHostProfiles(t, ds, map[*fleet.Host][]anyProfile{
|
||
darwinHosts[0]: {
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryFailed,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
darwinHosts[1]: {
|
||
{
|
||
ProfileUUID: globalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: tm2Profiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
darwinHosts[2]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
},
|
||
darwinHosts[3]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
unenrolledHost: {},
|
||
linuxHost: {},
|
||
windowsHosts[0]: {
|
||
{
|
||
ProfileUUID: newTm1Profiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[1]: {
|
||
{
|
||
ProfileUUID: tm2Profiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[2]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[8].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[9].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[3]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[8].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[9].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[10].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
})
|
||
|
||
// add a label-based profile to team 2
|
||
tm2DarwinProfiles = []*fleet.MDMAppleConfigProfile{
|
||
configProfileForTest(t, "T2.1a", "T2.1a", "a"),
|
||
configProfileForTest(t, "T2.2a", "T2.2a", "b", labels[1], labels[2]),
|
||
}
|
||
tm2WindowsProfiles = []*fleet.MDMWindowsConfigProfile{
|
||
windowsConfigProfileForTest(t, "T2.1w", "T2.1"),
|
||
windowsConfigProfileForTest(t, "T2.2w", "T2.2", labels[4], labels[5]),
|
||
}
|
||
|
||
updates, err = ds.BatchSetMDMProfiles(ctx, &team2.ID, tm2DarwinProfiles, tm2WindowsProfiles, nil, nil, nil)
|
||
require.NoError(t, err)
|
||
tm2Profiles = getProfs(&team2.ID)
|
||
require.Len(t, tm2Profiles, 4)
|
||
// TODO(mna): temporary until BatchSetMDMProfiles supports labels
|
||
setProfileLabels(t, tm2Profiles[1], labels[1], labels[2])
|
||
setProfileLabels(t, tm2Profiles[3], labels[4], labels[5])
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.True(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
// sync team 2, no changes because no host is a member of the labels (except
|
||
// index change due to new profiles)
|
||
updates, err = ds.BulkSetPendingMDMHostProfiles(ctx, nil, []uint{team2.ID}, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.False(t, updates.AppleConfigProfile)
|
||
assert.False(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
assertHostProfiles(t, ds, map[*fleet.Host][]anyProfile{
|
||
darwinHosts[0]: {
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryFailed,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
darwinHosts[1]: {
|
||
{
|
||
ProfileUUID: globalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: tm2Profiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
darwinHosts[2]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
},
|
||
darwinHosts[3]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
unenrolledHost: {},
|
||
linuxHost: {},
|
||
windowsHosts[0]: {
|
||
{
|
||
ProfileUUID: newTm1Profiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[1]: {
|
||
{
|
||
ProfileUUID: tm2Profiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[2]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[8].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[9].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[3]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[8].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[9].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[10].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
})
|
||
|
||
// make darwinHosts[1] and windowsHosts[1] members of the required labels
|
||
err = ds.AsyncBatchInsertLabelMembership(ctx, [][2]uint{
|
||
{labels[1].ID, darwinHosts[1].ID},
|
||
{labels[2].ID, darwinHosts[1].ID},
|
||
{labels[4].ID, windowsHosts[1].ID},
|
||
{labels[5].ID, windowsHosts[1].ID},
|
||
})
|
||
require.NoError(t, err)
|
||
|
||
// sync team 2, the label-based profile of team2 is now pending install
|
||
updates, err = ds.BulkSetPendingMDMHostProfiles(ctx, nil, []uint{team2.ID}, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.True(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
assertHostProfiles(t, ds, map[*fleet.Host][]anyProfile{
|
||
darwinHosts[0]: {
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryFailed,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
darwinHosts[1]: {
|
||
{
|
||
ProfileUUID: globalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[0].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[2].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[4].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: tm2Profiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: tm2Profiles[0].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: tm2Profiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: tm2Profiles[1].Identifier,
|
||
},
|
||
},
|
||
darwinHosts[2]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
},
|
||
darwinHosts[3]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
unenrolledHost: {},
|
||
linuxHost: {},
|
||
windowsHosts[0]: {
|
||
{
|
||
ProfileUUID: newTm1Profiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[1]: {
|
||
{
|
||
ProfileUUID: tm2Profiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: tm2Profiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[2]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[8].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[9].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[3]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[8].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[9].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[10].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
})
|
||
|
||
// "break" the team 2 label-based profile by deleting a label
|
||
require.NoError(t, ds.DeleteLabel(ctx, labels[1].Name, fleet.TeamFilter{User: &fleet.User{GlobalRole: ptr.String(fleet.RoleAdmin)}}))
|
||
require.NoError(t, ds.DeleteLabel(ctx, labels[4].Name, fleet.TeamFilter{User: &fleet.User{GlobalRole: ptr.String(fleet.RoleAdmin)}}))
|
||
|
||
// sync team 2, the label-based profile of team2 is left untouched (broken
|
||
// profiles are ignored)
|
||
updates, err = ds.BulkSetPendingMDMHostProfiles(ctx, nil, []uint{team2.ID}, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.False(t, updates.AppleConfigProfile)
|
||
assert.False(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
assertHostProfiles(t, ds, map[*fleet.Host][]anyProfile{
|
||
darwinHosts[0]: {
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryFailed,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
darwinHosts[1]: {
|
||
{
|
||
ProfileUUID: globalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[0].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[2].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: globalProfiles[4].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: tm2Profiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: tm2Profiles[0].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: tm2Profiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: tm2Profiles[1].Identifier,
|
||
},
|
||
},
|
||
darwinHosts[2]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
},
|
||
darwinHosts[3]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
unenrolledHost: {},
|
||
linuxHost: {},
|
||
windowsHosts[0]: {
|
||
{
|
||
ProfileUUID: newTm1Profiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[1]: {
|
||
{
|
||
ProfileUUID: tm2Profiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: tm2Profiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[2]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[8].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[9].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[3]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[8].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[9].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[10].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
})
|
||
|
||
// remove team 2 hosts membership from labels
|
||
err = ds.AsyncBatchDeleteLabelMembership(ctx, [][2]uint{
|
||
{labels[1].ID, darwinHosts[1].ID},
|
||
{labels[2].ID, darwinHosts[1].ID},
|
||
{labels[4].ID, windowsHosts[1].ID},
|
||
{labels[5].ID, windowsHosts[1].ID},
|
||
})
|
||
require.NoError(t, err)
|
||
|
||
// sync team 2, the label-based profile of team2 is still left untouched
|
||
// because even if the hosts are not members anymore, the profile is broken
|
||
updates, err = ds.BulkSetPendingMDMHostProfiles(ctx, nil, []uint{team2.ID}, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.False(t, updates.AppleConfigProfile)
|
||
assert.False(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
assertHostProfiles(t, ds, map[*fleet.Host][]anyProfile{
|
||
darwinHosts[0]: {
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryFailed,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
darwinHosts[1]: {
|
||
{
|
||
ProfileUUID: globalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: tm2Profiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: tm2Profiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
darwinHosts[2]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
},
|
||
darwinHosts[3]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
unenrolledHost: {},
|
||
linuxHost: {},
|
||
windowsHosts[0]: {
|
||
{
|
||
ProfileUUID: newTm1Profiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[1]: {
|
||
{
|
||
ProfileUUID: tm2Profiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: tm2Profiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[2]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[8].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[9].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[3]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[8].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[9].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[10].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
})
|
||
|
||
// "unbreak" the profile by removing the deleted label from its requirements
|
||
setProfileLabels(t, tm2Profiles[1], labels[2])
|
||
setProfileLabels(t, tm2Profiles[3], labels[5])
|
||
|
||
// sync team 2, now it sees that the hosts are not members and the profile
|
||
// gets removed
|
||
updates, err = ds.BulkSetPendingMDMHostProfiles(ctx, nil, []uint{team2.ID}, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.True(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
assertHostProfiles(t, ds, map[*fleet.Host][]anyProfile{
|
||
darwinHosts[0]: {
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryFailed,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
darwinHosts[1]: {
|
||
{
|
||
ProfileUUID: globalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: tm2Profiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: tm2Profiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
},
|
||
darwinHosts[2]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
},
|
||
darwinHosts[3]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
unenrolledHost: {},
|
||
linuxHost: {},
|
||
windowsHosts[0]: {
|
||
{
|
||
ProfileUUID: newTm1Profiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[1]: {
|
||
{
|
||
ProfileUUID: tm2Profiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[2]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[8].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[9].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[3]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[8].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[9].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[10].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
})
|
||
|
||
// sanity-check, a full sync does not change anything
|
||
updates, err = ds.BulkSetPendingMDMHostProfiles(
|
||
ctx,
|
||
hostIDsFromHosts(
|
||
append(darwinHosts, append(windowsHosts, unenrolledHost, linuxHost)...)...),
|
||
nil,
|
||
nil,
|
||
nil,
|
||
)
|
||
require.NoError(t, err)
|
||
assert.False(t, updates.AppleConfigProfile)
|
||
assert.False(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
assertHostProfiles(t, ds, map[*fleet.Host][]anyProfile{
|
||
darwinHosts[0]: {
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryFailed,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
darwinHosts[1]: {
|
||
{
|
||
ProfileUUID: globalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: globalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: tm2Profiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: tm2Profiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
},
|
||
darwinHosts[2]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
},
|
||
},
|
||
darwinHosts[3]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
unenrolledHost: {},
|
||
linuxHost: {},
|
||
windowsHosts[0]: {
|
||
{
|
||
ProfileUUID: newTm1Profiles[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[4].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newTm1Profiles[5].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[1]: {
|
||
{
|
||
ProfileUUID: tm2Profiles[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[2]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[8].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[9].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
windowsHosts[3]: {
|
||
{
|
||
ProfileUUID: newGlobalProfiles[6].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[7].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[8].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[9].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
{
|
||
ProfileUUID: newGlobalProfiles[10].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
},
|
||
},
|
||
})
|
||
}
|
||
|
||
func testGetHostMDMProfilesExpectedForVerification(t *testing.T, ds *Datastore) {
|
||
ctx := context.Background()
|
||
|
||
// Setup funcs
|
||
|
||
// ===================================================
|
||
// MacOS base tests
|
||
// ===================================================
|
||
baseEarliestInstallDate := time.Now().UTC().Add(-24 * time.Hour).Truncate(time.Second)
|
||
overrideEarliestInstallDate := time.Now().UTC().Add(-6 * time.Hour).Truncate(time.Second)
|
||
|
||
macosBasicTeamProfNoLabelsSetup := func() (uint, *fleet.Host) {
|
||
host, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: "macos-test-1",
|
||
OsqueryHostID: ptr.String("osquery-macos-1"),
|
||
NodeKey: ptr.String("node-key-macos-1"),
|
||
UUID: uuid.NewString(),
|
||
Platform: "darwin",
|
||
})
|
||
require.NoError(t, err)
|
||
nanoEnroll(t, ds, host, false)
|
||
|
||
// create a team
|
||
team, err := ds.NewTeam(ctx, &fleet.Team{Name: "macos team 1"})
|
||
require.NoError(t, err)
|
||
|
||
err = ds.AddHostsToTeam(ctx, fleet.NewAddHostsToTeamParams(&team.ID, []uint{host.ID}))
|
||
require.NoError(t, err)
|
||
|
||
// create profiles for team 1
|
||
profiles := []*fleet.MDMAppleConfigProfile{
|
||
configProfileForTest(t, "T1.1", "T1.1", "d"),
|
||
configProfileForTest(t, "T1.2", "T1.2", "e"),
|
||
}
|
||
|
||
updates, err := ds.BatchSetMDMProfiles(ctx, &team.ID, profiles, nil, nil, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.False(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
profs, _, err := ds.ListMDMConfigProfiles(ctx, &team.ID, fleet.ListOptions{})
|
||
require.NoError(t, err)
|
||
require.Len(t, profs, 2)
|
||
|
||
return team.ID, host
|
||
}
|
||
|
||
macosLabeledTeamProfSetup := func() (uint, *fleet.Host) {
|
||
host, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: "macos-test-2",
|
||
OsqueryHostID: ptr.String("osquery-macos-2"),
|
||
NodeKey: ptr.String("node-key-macos-2"),
|
||
UUID: uuid.NewString(),
|
||
Platform: "darwin",
|
||
})
|
||
require.NoError(t, err)
|
||
nanoEnroll(t, ds, host, false)
|
||
|
||
// create a team
|
||
team, err := ds.NewTeam(ctx, &fleet.Team{Name: "macos team 2"})
|
||
require.NoError(t, err)
|
||
|
||
err = ds.AddHostsToTeam(ctx, fleet.NewAddHostsToTeamParams(&team.ID, []uint{host.ID}))
|
||
require.NoError(t, err)
|
||
|
||
// create profiles for team 1
|
||
profiles := []*fleet.MDMAppleConfigProfile{
|
||
configProfileForTest(t, "T2.1", "T2.1", "d"),
|
||
configProfileForTest(t, "T2.2", "T2.2", "e"),
|
||
configProfileForTest(t, "labeled_prof", "labeled_prof", "labeled_prof"),
|
||
}
|
||
|
||
label, err := ds.NewLabel(ctx, &fleet.Label{Name: "test_label_1"})
|
||
require.NoError(t, err)
|
||
|
||
updates, err := ds.BatchSetMDMProfiles(ctx, &team.ID, profiles, nil, nil, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.False(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
var uid string
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
return sqlx.GetContext(
|
||
ctx,
|
||
q,
|
||
&uid,
|
||
`SELECT profile_uuid FROM mdm_apple_configuration_profiles WHERE identifier = ?`,
|
||
"labeled_prof",
|
||
)
|
||
})
|
||
|
||
// Update label with host membership
|
||
ExecAdhocSQL(
|
||
t, ds, func(db sqlx.ExtContext) error {
|
||
_, err := db.ExecContext(
|
||
context.Background(),
|
||
"INSERT IGNORE INTO label_membership (host_id, label_id) VALUES (?, ?)",
|
||
host.ID,
|
||
label.ID,
|
||
)
|
||
return err
|
||
},
|
||
)
|
||
|
||
// Update profile <-> label mapping
|
||
ExecAdhocSQL(
|
||
t, ds, func(db sqlx.ExtContext) error {
|
||
_, err := db.ExecContext(
|
||
context.Background(),
|
||
"INSERT INTO mdm_configuration_profile_labels (apple_profile_uuid, label_name, label_id) VALUES (?, ?, ?)",
|
||
uid,
|
||
label.Name,
|
||
label.ID,
|
||
)
|
||
return err
|
||
},
|
||
)
|
||
|
||
profs, _, err := ds.ListMDMConfigProfiles(ctx, &team.ID, fleet.ListOptions{})
|
||
require.NoError(t, err)
|
||
require.Len(t, profs, 3)
|
||
|
||
return team.ID, host
|
||
}
|
||
|
||
macosLabeledTeamProfWithAdditionalLabeledProfSetup := func() (uint, *fleet.Host) {
|
||
host, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: "macos-test-3",
|
||
OsqueryHostID: ptr.String("osquery-macos-3"),
|
||
NodeKey: ptr.String("node-key-macos-3"),
|
||
UUID: uuid.NewString(),
|
||
Platform: "darwin",
|
||
})
|
||
require.NoError(t, err)
|
||
nanoEnroll(t, ds, host, false)
|
||
|
||
// create a team
|
||
team, err := ds.NewTeam(ctx, &fleet.Team{Name: "macos team 3"})
|
||
require.NoError(t, err)
|
||
|
||
err = ds.AddHostsToTeam(ctx, fleet.NewAddHostsToTeamParams(&team.ID, []uint{host.ID}))
|
||
require.NoError(t, err)
|
||
|
||
// create profiles for team 1
|
||
profiles := []*fleet.MDMAppleConfigProfile{
|
||
configProfileForTest(t, "T3.1", "T3.1", "d"),
|
||
configProfileForTest(t, "T3.2", "T3.2", "e"),
|
||
configProfileForTest(t, "labeled_prof_2", "labeled_prof_2", "labeled_prof_2"),
|
||
}
|
||
|
||
testLabel2, err := ds.NewLabel(ctx, &fleet.Label{Name: "test_label_2"})
|
||
require.NoError(t, err)
|
||
|
||
testLabel3, err := ds.NewLabel(ctx, &fleet.Label{Name: "test_label_3"})
|
||
require.NoError(t, err)
|
||
|
||
updates, err := ds.BatchSetMDMProfiles(ctx, &team.ID, profiles, nil, nil, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.False(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
var uid string
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
return sqlx.GetContext(
|
||
ctx,
|
||
q,
|
||
&uid,
|
||
`SELECT profile_uuid FROM mdm_apple_configuration_profiles WHERE identifier = ?`,
|
||
"labeled_prof_2",
|
||
)
|
||
})
|
||
|
||
// Update label with host membership
|
||
ExecAdhocSQL(
|
||
t, ds, func(db sqlx.ExtContext) error {
|
||
_, err := db.ExecContext(
|
||
context.Background(),
|
||
"INSERT IGNORE INTO label_membership (host_id, label_id) VALUES (?, ?)",
|
||
host.ID,
|
||
testLabel2.ID,
|
||
)
|
||
return err
|
||
},
|
||
)
|
||
|
||
// Update profile <-> label mapping
|
||
ExecAdhocSQL(
|
||
t, ds, func(db sqlx.ExtContext) error {
|
||
_, err := db.ExecContext(
|
||
context.Background(),
|
||
"INSERT INTO mdm_configuration_profile_labels (apple_profile_uuid, label_name, label_id) VALUES (?, ?, ?)",
|
||
uid,
|
||
testLabel2.Name,
|
||
testLabel2.ID,
|
||
)
|
||
return err
|
||
},
|
||
)
|
||
|
||
// Also add mapping to test label 3
|
||
ExecAdhocSQL(
|
||
t, ds, func(db sqlx.ExtContext) error {
|
||
_, err := db.ExecContext(
|
||
context.Background(),
|
||
"INSERT INTO mdm_configuration_profile_labels (apple_profile_uuid, label_name, label_id) VALUES (?, ?, ?)",
|
||
uid,
|
||
testLabel3.Name,
|
||
testLabel3.ID,
|
||
)
|
||
return err
|
||
},
|
||
)
|
||
|
||
profs, _, err := ds.ListMDMConfigProfiles(ctx, &team.ID, fleet.ListOptions{})
|
||
require.NoError(t, err)
|
||
require.Len(t, profs, 3)
|
||
|
||
return team.ID, host
|
||
}
|
||
|
||
macosProfWithBrokenLabelSetup := func() (uint, *fleet.Host) {
|
||
host, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: "macos-test-4",
|
||
OsqueryHostID: ptr.String("osquery-macos-4"),
|
||
NodeKey: ptr.String("node-key-macos-4"),
|
||
UUID: uuid.NewString(),
|
||
Platform: "darwin",
|
||
})
|
||
require.NoError(t, err)
|
||
nanoEnroll(t, ds, host, false)
|
||
|
||
// create a team
|
||
team, err := ds.NewTeam(ctx, &fleet.Team{Name: "macos team 4"})
|
||
require.NoError(t, err)
|
||
|
||
err = ds.AddHostsToTeam(ctx, fleet.NewAddHostsToTeamParams(&team.ID, []uint{host.ID}))
|
||
require.NoError(t, err)
|
||
|
||
// create profiles for team
|
||
profiles := []*fleet.MDMAppleConfigProfile{
|
||
configProfileForTest(t, "T4.1", "T4.1", "d"),
|
||
configProfileForTest(t, "T4.2", "T4.2", "e"),
|
||
configProfileForTest(t, "broken_label_prof", "broken_label_prof", "broken_label_prof"),
|
||
}
|
||
|
||
testLabel4, err := ds.NewLabel(ctx, &fleet.Label{Name: "test_label_4"})
|
||
require.NoError(t, err)
|
||
|
||
updates, err := ds.BatchSetMDMProfiles(ctx, &team.ID, profiles, nil, nil, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.False(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
var uid string
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
return sqlx.GetContext(
|
||
ctx,
|
||
q,
|
||
&uid,
|
||
`SELECT profile_uuid FROM mdm_apple_configuration_profiles WHERE identifier = ?`,
|
||
"broken_label_prof",
|
||
)
|
||
})
|
||
|
||
// Update label with host membership
|
||
ExecAdhocSQL(
|
||
t, ds, func(db sqlx.ExtContext) error {
|
||
_, err := db.ExecContext(
|
||
context.Background(),
|
||
"INSERT IGNORE INTO label_membership (host_id, label_id) VALUES (?, ?)",
|
||
host.ID,
|
||
testLabel4.ID,
|
||
)
|
||
return err
|
||
},
|
||
)
|
||
|
||
// Update profile <-> label mapping
|
||
ExecAdhocSQL(
|
||
t, ds, func(db sqlx.ExtContext) error {
|
||
_, err := db.ExecContext(
|
||
context.Background(),
|
||
"INSERT INTO mdm_configuration_profile_labels (apple_profile_uuid, label_name, label_id) VALUES (?, ?, ?)",
|
||
uid,
|
||
testLabel4.Name,
|
||
testLabel4.ID,
|
||
)
|
||
return err
|
||
},
|
||
)
|
||
|
||
profs, _, err := ds.ListMDMConfigProfiles(ctx, &team.ID, fleet.ListOptions{})
|
||
require.NoError(t, err)
|
||
require.Len(t, profs, 3)
|
||
|
||
// Now delete label, we shouldn't see the related profile
|
||
err = ds.DeleteLabel(ctx, testLabel4.Name, fleet.TeamFilter{User: &fleet.User{GlobalRole: ptr.String(fleet.RoleAdmin)}})
|
||
require.NoError(t, err)
|
||
|
||
return team.ID, host
|
||
}
|
||
|
||
macosInstallDateOverrideSetup := func() (uint, *fleet.Host) {
|
||
host, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: "macos-test-5",
|
||
OsqueryHostID: ptr.String("osquery-macos-5"),
|
||
NodeKey: ptr.String("node-key-macos-5"),
|
||
UUID: uuid.NewString(),
|
||
Platform: "darwin",
|
||
})
|
||
require.NoError(t, err)
|
||
nanoEnroll(t, ds, host, false)
|
||
|
||
// create a team
|
||
team, err := ds.NewTeam(ctx, &fleet.Team{Name: "macos team 5"})
|
||
require.NoError(t, err)
|
||
|
||
err = ds.AddHostsToTeam(ctx, fleet.NewAddHostsToTeamParams(&team.ID, []uint{host.ID}))
|
||
require.NoError(t, err)
|
||
|
||
// create profiles for team 1
|
||
profiles := []*fleet.MDMAppleConfigProfile{
|
||
configProfileForTest(t, "T5.1", "T5.1", "d"),
|
||
configProfileForTest(t, "T5.2", "T5.2", "e"),
|
||
configProfileForTest(t, "T5.3", "T5.3", "f"),
|
||
}
|
||
|
||
updates, err := ds.BatchSetMDMProfiles(ctx, &team.ID, profiles, nil, nil, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.False(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
profs, _, err := ds.ListMDMConfigProfiles(ctx, &team.ID, fleet.ListOptions{})
|
||
require.NoError(t, err)
|
||
require.Len(t, profs, 3)
|
||
|
||
// We cannot control the generated profile UUIDs here so we need to map them back to the
|
||
// created profiles for test correctness
|
||
for i := 0; i < len(profs); i++ {
|
||
for j := 0; j < len(profiles); j++ {
|
||
if profs[i].Identifier == profiles[j].Identifier {
|
||
profiles[j].ProfileUUID = profs[i].ProfileUUID
|
||
break
|
||
}
|
||
}
|
||
}
|
||
|
||
ExecAdhocSQL(
|
||
t, ds, func(db sqlx.ExtContext) error {
|
||
_, err := db.ExecContext(
|
||
context.Background(),
|
||
"UPDATE mdm_apple_configuration_profiles SET uploaded_at = ? WHERE profile_uuid IN (?, ?, ?)",
|
||
baseEarliestInstallDate,
|
||
profiles[0].ProfileUUID,
|
||
profiles[1].ProfileUUID,
|
||
profiles[2].ProfileUUID,
|
||
)
|
||
return err
|
||
},
|
||
)
|
||
|
||
// Note what we're doing here is overriding install date for the first profile, creating an
|
||
// HMAP entry that doesn't override it for the second, then the third has no corresponding
|
||
// HMAP entry at all(which also means no override).
|
||
err = ds.BulkUpsertMDMAppleHostProfiles(ctx, []*fleet.MDMAppleBulkUpsertHostProfilePayload{
|
||
{
|
||
ProfileUUID: profiles[0].ProfileUUID,
|
||
ProfileIdentifier: profiles[0].Identifier,
|
||
ProfileName: profiles[0].Name,
|
||
HostUUID: host.UUID,
|
||
CommandUUID: uuid.NewString(),
|
||
Checksum: profiles[0].Checksum,
|
||
VariablesUpdatedAt: &overrideEarliestInstallDate,
|
||
Status: &fleet.MDMDeliveryVerified,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
Scope: fleet.PayloadScopeSystem,
|
||
},
|
||
{
|
||
ProfileUUID: profiles[1].ProfileUUID,
|
||
ProfileIdentifier: profiles[1].Identifier,
|
||
ProfileName: profiles[1].Name,
|
||
HostUUID: host.UUID,
|
||
CommandUUID: uuid.NewString(),
|
||
Checksum: profiles[1].Checksum,
|
||
Status: &fleet.MDMDeliveryVerified,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
Scope: fleet.PayloadScopeSystem,
|
||
},
|
||
})
|
||
require.NoError(t, err)
|
||
return team.ID, host
|
||
}
|
||
|
||
macosLabeledProfileRulesSetup := func() (uint, *fleet.Host) {
|
||
host, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: "macos-test-6",
|
||
OsqueryHostID: ptr.String("osquery-macos-6"),
|
||
NodeKey: ptr.String("node-key-macos-6"),
|
||
UUID: uuid.NewString(),
|
||
Platform: "darwin",
|
||
})
|
||
require.NoError(t, err)
|
||
nanoEnroll(t, ds, host, false)
|
||
|
||
// create a team
|
||
team, err := ds.NewTeam(ctx, &fleet.Team{Name: "macos team 6"})
|
||
require.NoError(t, err)
|
||
|
||
err = ds.AddHostsToTeam(ctx, fleet.NewAddHostsToTeamParams(&team.ID, []uint{host.ID}))
|
||
require.NoError(t, err)
|
||
|
||
// Include any labels
|
||
includeAnyMatchedLabel1, err := ds.NewLabel(ctx, &fleet.Label{Name: "include-any-macos-matched-label-1"})
|
||
require.NoError(t, err)
|
||
includeAnyMatchedLabel2, err := ds.NewLabel(ctx, &fleet.Label{Name: "include-any-macos-matched-label-2"})
|
||
require.NoError(t, err)
|
||
includeAnyUnmatchedLabel, err := ds.NewLabel(ctx, &fleet.Label{Name: "include-any-macos-unmatched-label"})
|
||
require.NoError(t, err)
|
||
|
||
// Include all labels
|
||
includeAllMatchedLabel1, err := ds.NewLabel(ctx, &fleet.Label{Name: "include-all-macos-matched-label-1"})
|
||
require.NoError(t, err)
|
||
includeAllMatchedLabel2, err := ds.NewLabel(ctx, &fleet.Label{Name: "include-all-macos-matched-label-2"})
|
||
require.NoError(t, err)
|
||
includeAllUnmatchedLabel, err := ds.NewLabel(ctx, &fleet.Label{Name: "include-all-macos-unmatched-label"})
|
||
require.NoError(t, err)
|
||
|
||
// Exclude labels
|
||
excludeMatchedLabel1, err := ds.NewLabel(ctx, &fleet.Label{Name: "exclude-macos-matched-label-1"})
|
||
require.NoError(t, err)
|
||
excludeMatchedLabel2, err := ds.NewLabel(ctx, &fleet.Label{Name: "exclude-macos-matched-label-2"})
|
||
require.NoError(t, err)
|
||
excludeUnmatchedLabel, err := ds.NewLabel(ctx, &fleet.Label{Name: "exclude-macos-unmatched-label"})
|
||
require.NoError(t, err)
|
||
|
||
// create profiles for team
|
||
// include_any_all_match_prof
|
||
// include_any_one_matches_prof
|
||
// include_all_all_match_prof
|
||
// exclude_none_match_prof
|
||
profiles := []*fleet.MDMAppleConfigProfile{
|
||
// Two profiles with no label rules, thus should always be included
|
||
configProfileForTest(t, "T6.1", "T6.1", "g"),
|
||
configProfileForTest(t, "T6.2", "T6.2", "h"),
|
||
|
||
// This profile will use an "include any" rule where the host has both of the labels, thus should be included
|
||
configProfileForTest(t, "include_any_all_match_prof", "include_any_all_match_prof", "i", includeAnyMatchedLabel1, includeAnyMatchedLabel2),
|
||
// This profile will use an "include any" rule where the host has one of the labels, thus should be included
|
||
configProfileForTest(t, "include_any_one_matches_prof", "include_any_one_matches_prof", "j", includeAnyMatchedLabel1, includeAnyUnmatchedLabel),
|
||
// This profile will use an "include any" rule where the host has none of the labels, thus should be excluded
|
||
configProfileForTest(t, "include_any_none_match_prof", "include_any_none_match_prof", "k", includeAnyUnmatchedLabel),
|
||
|
||
// This profile will use an "include all" rule where the host has all of the labels and thus should be included
|
||
configProfileForTest(t, "include_all_all_match_prof", "include_all_all_match_prof", "l", includeAllMatchedLabel1, includeAllMatchedLabel2),
|
||
// This profile will use an "include all" rule where the host has one of the labels and thus should be excluded
|
||
configProfileForTest(t, "include_all_one_matches_prof", "include_all_one_matches_prof", "m", includeAllMatchedLabel1, includeAllUnmatchedLabel),
|
||
// This profile will use an "include any" rule where the host has none of the labels and thus should be excluded
|
||
configProfileForTest(t, "include_all_none_match_prof", "include_all_none_match_prof", "n", includeAllUnmatchedLabel),
|
||
|
||
// This profile will use an "exclude" rule where the host has both of the labels, thus should be excluded
|
||
configProfileForTest(t, "exclude_all_match_prof", "exclude_all_match_prof", "o", excludeMatchedLabel1, excludeMatchedLabel2),
|
||
// This profile will use an "exclude" rule where the host has one of the labels, thus should be excluded
|
||
configProfileForTest(t, "exclude_one_matches_prof", "exclude_one_matches_prof", "p", excludeMatchedLabel1, excludeUnmatchedLabel),
|
||
// This profile will use an "exclude" rule where the host has none of the labels, thus should be included
|
||
configProfileForTest(t, "exclude_none_match_prof", "exclude_none_match_prof", "q", excludeUnmatchedLabel),
|
||
}
|
||
|
||
updates, err := ds.BatchSetMDMProfiles(ctx, &team.ID, profiles, nil, nil, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.False(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
// Update labels with host membership
|
||
ExecAdhocSQL(
|
||
t, ds, func(db sqlx.ExtContext) error {
|
||
_, err := db.ExecContext(
|
||
context.Background(),
|
||
"INSERT IGNORE INTO label_membership (host_id, label_id) VALUES (?, ?), (?, ?), (?, ?), (?, ?), (?, ?), (?, ?)",
|
||
host.ID,
|
||
includeAnyMatchedLabel1.ID,
|
||
host.ID,
|
||
includeAnyMatchedLabel2.ID,
|
||
host.ID,
|
||
includeAllMatchedLabel1.ID,
|
||
host.ID,
|
||
includeAllMatchedLabel2.ID,
|
||
host.ID,
|
||
excludeMatchedLabel1.ID,
|
||
host.ID,
|
||
excludeMatchedLabel2.ID,
|
||
)
|
||
return err
|
||
},
|
||
)
|
||
|
||
profs, _, err := ds.ListMDMConfigProfiles(ctx, &team.ID, fleet.ListOptions{})
|
||
require.NoError(t, err)
|
||
require.Len(t, profs, 11)
|
||
|
||
return team.ID, host
|
||
}
|
||
|
||
// ===================================================
|
||
// Windows
|
||
// ===================================================
|
||
|
||
windowsBasicTeamProfNoLabelsSetup := func() (uint, *fleet.Host) {
|
||
host, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: "windows-test",
|
||
OsqueryHostID: ptr.String("osquery-windows"),
|
||
NodeKey: ptr.String("node-key-windows"),
|
||
UUID: uuid.NewString(),
|
||
Platform: "windows",
|
||
})
|
||
require.NoError(t, err)
|
||
windowsEnroll(t, ds, host)
|
||
|
||
// create a team
|
||
team, err := ds.NewTeam(ctx, &fleet.Team{Name: "windows team 1"})
|
||
require.NoError(t, err)
|
||
|
||
err = ds.AddHostsToTeam(ctx, fleet.NewAddHostsToTeamParams(&team.ID, []uint{host.ID}))
|
||
require.NoError(t, err)
|
||
|
||
// create profiles for team 1
|
||
profiles := []*fleet.MDMWindowsConfigProfile{
|
||
windowsConfigProfileForTest(t, "T1.1", "T1.1"),
|
||
windowsConfigProfileForTest(t, "T1.2", "T1.2"),
|
||
}
|
||
|
||
updates, err := ds.BatchSetMDMProfiles(ctx, &team.ID, nil, profiles, nil, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.False(t, updates.AppleConfigProfile)
|
||
assert.True(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
profs, _, err := ds.ListMDMConfigProfiles(ctx, &team.ID, fleet.ListOptions{})
|
||
require.NoError(t, err)
|
||
require.Len(t, profs, 2)
|
||
|
||
return team.ID, host
|
||
}
|
||
|
||
windowsLabeledTeamProfSetup := func() (uint, *fleet.Host) {
|
||
host, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: "windows-test-2",
|
||
OsqueryHostID: ptr.String("osquery-windows-2"),
|
||
NodeKey: ptr.String("node-key-windows-2"),
|
||
UUID: uuid.NewString(),
|
||
Platform: "windows",
|
||
})
|
||
require.NoError(t, err)
|
||
windowsEnroll(t, ds, host)
|
||
|
||
// create a team
|
||
team, err := ds.NewTeam(ctx, &fleet.Team{Name: "windows team 2"})
|
||
require.NoError(t, err)
|
||
|
||
err = ds.AddHostsToTeam(ctx, fleet.NewAddHostsToTeamParams(&team.ID, []uint{host.ID}))
|
||
require.NoError(t, err)
|
||
|
||
// create profiles for team 1
|
||
profiles := []*fleet.MDMWindowsConfigProfile{
|
||
windowsConfigProfileForTest(t, "T2.1", "T2.1"),
|
||
windowsConfigProfileForTest(t, "T2.2", "T2.2"),
|
||
windowsConfigProfileForTest(t, "labeled_prof", "labeled_prof"),
|
||
}
|
||
|
||
label, err := ds.NewLabel(ctx, &fleet.Label{Name: "test_label_6"})
|
||
require.NoError(t, err)
|
||
|
||
updates, err := ds.BatchSetMDMProfiles(ctx, &team.ID, nil, profiles, nil, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.False(t, updates.AppleConfigProfile)
|
||
assert.True(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
var uid string
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
return sqlx.GetContext(
|
||
ctx,
|
||
q,
|
||
&uid,
|
||
`SELECT profile_uuid FROM mdm_windows_configuration_profiles WHERE name = ?`,
|
||
"labeled_prof",
|
||
)
|
||
})
|
||
|
||
// Update label with host membership
|
||
ExecAdhocSQL(
|
||
t, ds, func(db sqlx.ExtContext) error {
|
||
_, err := db.ExecContext(
|
||
context.Background(),
|
||
"INSERT IGNORE INTO label_membership (host_id, label_id) VALUES (?, ?)",
|
||
host.ID,
|
||
label.ID,
|
||
)
|
||
return err
|
||
},
|
||
)
|
||
|
||
// Update profile <-> label mapping
|
||
ExecAdhocSQL(
|
||
t, ds, func(db sqlx.ExtContext) error {
|
||
_, err := db.ExecContext(
|
||
context.Background(),
|
||
"INSERT INTO mdm_configuration_profile_labels (windows_profile_uuid, label_name, label_id) VALUES (?, ?, ?)",
|
||
uid,
|
||
label.Name,
|
||
label.ID,
|
||
)
|
||
return err
|
||
},
|
||
)
|
||
|
||
profs, _, err := ds.ListMDMConfigProfiles(ctx, &team.ID, fleet.ListOptions{})
|
||
require.NoError(t, err)
|
||
require.Len(t, profs, 3)
|
||
|
||
return team.ID, host
|
||
}
|
||
|
||
windowsLabeledTeamProfWithAdditionalLabeledProfSetup := func() (uint, *fleet.Host) {
|
||
host, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: "windows-test-3",
|
||
OsqueryHostID: ptr.String("osquery-windows-3"),
|
||
NodeKey: ptr.String("node-key-windows-3"),
|
||
UUID: uuid.NewString(),
|
||
Platform: "windows",
|
||
})
|
||
require.NoError(t, err)
|
||
windowsEnroll(t, ds, host)
|
||
|
||
// create a team
|
||
team, err := ds.NewTeam(ctx, &fleet.Team{Name: "windows team 3"})
|
||
require.NoError(t, err)
|
||
|
||
err = ds.AddHostsToTeam(ctx, fleet.NewAddHostsToTeamParams(&team.ID, []uint{host.ID}))
|
||
require.NoError(t, err)
|
||
|
||
// create profiles for team 1
|
||
profiles := []*fleet.MDMWindowsConfigProfile{
|
||
windowsConfigProfileForTest(t, "T3.1", "T3.1"),
|
||
windowsConfigProfileForTest(t, "T3.2", "T3.7"),
|
||
windowsConfigProfileForTest(t, "labeled_prof_2", "labeled_prof_2"),
|
||
}
|
||
|
||
testLabel2, err := ds.NewLabel(ctx, &fleet.Label{Name: uuid.NewString()})
|
||
require.NoError(t, err)
|
||
|
||
testLabel3, err := ds.NewLabel(ctx, &fleet.Label{Name: uuid.NewString()})
|
||
require.NoError(t, err)
|
||
|
||
updates, err := ds.BatchSetMDMProfiles(ctx, &team.ID, nil, profiles, nil, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.False(t, updates.AppleConfigProfile)
|
||
assert.True(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
var uid string
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
return sqlx.GetContext(
|
||
ctx,
|
||
q,
|
||
&uid,
|
||
`SELECT profile_uuid FROM mdm_windows_configuration_profiles WHERE name = ?`,
|
||
"labeled_prof_2",
|
||
)
|
||
})
|
||
|
||
// Update label with host membership
|
||
ExecAdhocSQL(
|
||
t, ds, func(db sqlx.ExtContext) error {
|
||
_, err := db.ExecContext(
|
||
context.Background(),
|
||
"INSERT IGNORE INTO label_membership (host_id, label_id) VALUES (?, ?)",
|
||
host.ID,
|
||
testLabel2.ID,
|
||
)
|
||
return err
|
||
},
|
||
)
|
||
|
||
// Update profile <-> label mapping
|
||
ExecAdhocSQL(
|
||
t, ds, func(db sqlx.ExtContext) error {
|
||
_, err := db.ExecContext(
|
||
context.Background(),
|
||
"INSERT INTO mdm_configuration_profile_labels (windows_profile_uuid, label_name, label_id) VALUES (?, ?, ?)",
|
||
uid,
|
||
testLabel2.Name,
|
||
testLabel2.ID,
|
||
)
|
||
return err
|
||
},
|
||
)
|
||
|
||
// Also add mapping to test label 3
|
||
ExecAdhocSQL(
|
||
t, ds, func(db sqlx.ExtContext) error {
|
||
_, err := db.ExecContext(
|
||
context.Background(),
|
||
"INSERT INTO mdm_configuration_profile_labels (windows_profile_uuid, label_name, label_id) VALUES (?, ?, ?)",
|
||
uid,
|
||
testLabel3.Name,
|
||
testLabel3.ID,
|
||
)
|
||
return err
|
||
},
|
||
)
|
||
|
||
profs, _, err := ds.ListMDMConfigProfiles(ctx, &team.ID, fleet.ListOptions{})
|
||
require.NoError(t, err)
|
||
require.Len(t, profs, 3)
|
||
|
||
return team.ID, host
|
||
}
|
||
|
||
windowsProfWithBrokenLabelSetup := func() (uint, *fleet.Host) {
|
||
host, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: "windows-test-4",
|
||
OsqueryHostID: ptr.String("osquery-windows-4"),
|
||
NodeKey: ptr.String("node-key-windows-4"),
|
||
UUID: uuid.NewString(),
|
||
Platform: "windows",
|
||
})
|
||
require.NoError(t, err)
|
||
windowsEnroll(t, ds, host)
|
||
|
||
// create a team
|
||
team, err := ds.NewTeam(ctx, &fleet.Team{Name: "windows team 4"})
|
||
require.NoError(t, err)
|
||
|
||
err = ds.AddHostsToTeam(ctx, fleet.NewAddHostsToTeamParams(&team.ID, []uint{host.ID}))
|
||
require.NoError(t, err)
|
||
|
||
// create profiles for team
|
||
profiles := []*fleet.MDMWindowsConfigProfile{
|
||
windowsConfigProfileForTest(t, "T4.1", "T4.1"),
|
||
windowsConfigProfileForTest(t, "T4.2", "T4.2"),
|
||
windowsConfigProfileForTest(t, "broken_label_prof", "broken_label_prof"),
|
||
}
|
||
|
||
label, err := ds.NewLabel(ctx, &fleet.Label{Name: uuid.NewString()})
|
||
require.NoError(t, err)
|
||
|
||
updates, err := ds.BatchSetMDMProfiles(ctx, &team.ID, nil, profiles, nil, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.False(t, updates.AppleConfigProfile)
|
||
assert.True(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
var uid string
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
return sqlx.GetContext(
|
||
ctx,
|
||
q,
|
||
&uid,
|
||
`SELECT profile_uuid FROM mdm_windows_configuration_profiles WHERE name = ?`,
|
||
"broken_label_prof",
|
||
)
|
||
})
|
||
|
||
// Update label with host membership
|
||
ExecAdhocSQL(
|
||
t, ds, func(db sqlx.ExtContext) error {
|
||
_, err := db.ExecContext(
|
||
context.Background(),
|
||
"INSERT IGNORE INTO label_membership (host_id, label_id) VALUES (?, ?)",
|
||
host.ID,
|
||
label.ID,
|
||
)
|
||
return err
|
||
},
|
||
)
|
||
|
||
// Update profile <-> label mapping
|
||
ExecAdhocSQL(
|
||
t, ds, func(db sqlx.ExtContext) error {
|
||
_, err := db.ExecContext(
|
||
context.Background(),
|
||
"INSERT INTO mdm_configuration_profile_labels (windows_profile_uuid, label_name, label_id) VALUES (?, ?, ?)",
|
||
uid,
|
||
label.Name,
|
||
label.ID,
|
||
)
|
||
return err
|
||
},
|
||
)
|
||
|
||
profs, _, err := ds.ListMDMConfigProfiles(ctx, &team.ID, fleet.ListOptions{})
|
||
require.NoError(t, err)
|
||
require.Len(t, profs, 3)
|
||
|
||
// Now delete label, we shouldn't see the related profile
|
||
err = ds.DeleteLabel(ctx, label.Name, fleet.TeamFilter{User: &fleet.User{GlobalRole: ptr.String(fleet.RoleAdmin)}})
|
||
require.NoError(t, err)
|
||
|
||
return team.ID, host
|
||
}
|
||
|
||
windowsLabeledProfileRulesSetup := func() (uint, *fleet.Host) {
|
||
host, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: "windows-test-5",
|
||
OsqueryHostID: ptr.String("osquery-windows-5"),
|
||
NodeKey: ptr.String("node-key-windows-5"),
|
||
UUID: uuid.NewString(),
|
||
Platform: "windows",
|
||
})
|
||
require.NoError(t, err)
|
||
windowsEnroll(t, ds, host)
|
||
|
||
// create a team
|
||
team, err := ds.NewTeam(ctx, &fleet.Team{Name: "windows team 5"})
|
||
require.NoError(t, err)
|
||
|
||
err = ds.AddHostsToTeam(ctx, fleet.NewAddHostsToTeamParams(&team.ID, []uint{host.ID}))
|
||
require.NoError(t, err)
|
||
|
||
// Include any labels
|
||
includeAnyMatchedLabel1, err := ds.NewLabel(ctx, &fleet.Label{Name: "include-any-windows-matched-label-1"})
|
||
require.NoError(t, err)
|
||
includeAnyMatchedLabel2, err := ds.NewLabel(ctx, &fleet.Label{Name: "include-any-windows-matched-label-2"})
|
||
require.NoError(t, err)
|
||
includeAnyUnmatchedLabel, err := ds.NewLabel(ctx, &fleet.Label{Name: "include-any-windows-unmatched-label"})
|
||
require.NoError(t, err)
|
||
|
||
// Include all labels
|
||
includeAllMatchedLabel1, err := ds.NewLabel(ctx, &fleet.Label{Name: "include-all-windows-matched-label-1"})
|
||
require.NoError(t, err)
|
||
includeAllMatchedLabel2, err := ds.NewLabel(ctx, &fleet.Label{Name: "include-all-windows-matched-label-2"})
|
||
require.NoError(t, err)
|
||
includeAllUnmatchedLabel, err := ds.NewLabel(ctx, &fleet.Label{Name: "include-all-windows-unmatched-label"})
|
||
require.NoError(t, err)
|
||
|
||
// Exclude labels
|
||
excludeMatchedLabel1, err := ds.NewLabel(ctx, &fleet.Label{Name: "exclude-windows-matched-label-1"})
|
||
require.NoError(t, err)
|
||
excludeMatchedLabel2, err := ds.NewLabel(ctx, &fleet.Label{Name: "exclude-windows-matched-label-2"})
|
||
require.NoError(t, err)
|
||
excludeUnmatchedLabel, err := ds.NewLabel(ctx, &fleet.Label{Name: "exclude-windows-unmatched-label"})
|
||
require.NoError(t, err)
|
||
|
||
// create profiles for team
|
||
// include_any_all_match_prof
|
||
// include_any_one_matches_prof
|
||
// include_all_all_match_prof
|
||
// exclude_none_match_prof
|
||
profiles := []*fleet.MDMWindowsConfigProfile{
|
||
// Two profiles with no label rules, thus should always be included
|
||
windowsConfigProfileForTest(t, "T5.1", "T5.1"),
|
||
windowsConfigProfileForTest(t, "T5.2", "T5.2"),
|
||
|
||
// This profile will use an "include any" rule where the host has both of the labels, thus should be included
|
||
windowsConfigProfileForTest(t, "include_any_all_match_prof", "include_any_all_match_prof", includeAnyMatchedLabel1, includeAnyMatchedLabel2),
|
||
// This profile will use an "include any" rule where the host has one of the labels, thus should be included
|
||
windowsConfigProfileForTest(t, "include_any_one_matches_prof", "include_any_one_matches_prof", includeAnyMatchedLabel1, includeAnyUnmatchedLabel),
|
||
// This profile will use an "include any" rule where the host has none of the labels, thus should be excluded
|
||
windowsConfigProfileForTest(t, "include_any_none_match_prof", "include_any_none_match_prof", includeAnyUnmatchedLabel),
|
||
|
||
// This profile will use an "include all" rule where the host has all of the labels and thus should be included
|
||
windowsConfigProfileForTest(t, "include_all_all_match_prof", "include_all_all_match_prof", includeAllMatchedLabel1, includeAllMatchedLabel2),
|
||
// This profile will use an "include all" rule where the host has one of the labels and thus should be excluded
|
||
windowsConfigProfileForTest(t, "include_all_one_matches_prof", "include_all_one_matches_prof", includeAllMatchedLabel1, includeAllUnmatchedLabel),
|
||
// This profile will use an "include any" rule where the host has none of the labels and thus should be excluded
|
||
windowsConfigProfileForTest(t, "include_all_none_match_prof", "include_all_none_match_prof", includeAllUnmatchedLabel),
|
||
|
||
// This profile will use an "exclude" rule where the host has both of the labels, thus should be excluded
|
||
windowsConfigProfileForTest(t, "exclude_all_match_prof", "exclude_all_match_prof", excludeMatchedLabel1, excludeMatchedLabel2),
|
||
// This profile will use an "exclude" rule where the host has one of the labels, thus should be excluded
|
||
windowsConfigProfileForTest(t, "exclude_one_matches_prof", "exclude_one_matches_prof", excludeMatchedLabel1, excludeUnmatchedLabel),
|
||
// This profile will use an "exclude" rule where the host has none of the labels, thus should be included
|
||
windowsConfigProfileForTest(t, "exclude_none_match_prof", "exclude_none_match_prof", excludeUnmatchedLabel),
|
||
}
|
||
|
||
updates, err := ds.BatchSetMDMProfiles(ctx, &team.ID, nil, profiles, nil, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.False(t, updates.AppleConfigProfile)
|
||
assert.True(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
|
||
// Update labels with host membership
|
||
ExecAdhocSQL(
|
||
t, ds, func(db sqlx.ExtContext) error {
|
||
_, err := db.ExecContext(
|
||
context.Background(),
|
||
"INSERT IGNORE INTO label_membership (host_id, label_id) VALUES (?, ?), (?, ?), (?, ?), (?, ?), (?, ?), (?, ?)",
|
||
host.ID,
|
||
includeAnyMatchedLabel1.ID,
|
||
host.ID,
|
||
includeAnyMatchedLabel2.ID,
|
||
host.ID,
|
||
includeAllMatchedLabel1.ID,
|
||
host.ID,
|
||
includeAllMatchedLabel2.ID,
|
||
host.ID,
|
||
excludeMatchedLabel1.ID,
|
||
host.ID,
|
||
excludeMatchedLabel2.ID,
|
||
)
|
||
return err
|
||
},
|
||
)
|
||
|
||
profs, _, err := ds.ListMDMConfigProfiles(ctx, &team.ID, fleet.ListOptions{})
|
||
require.NoError(t, err)
|
||
require.Len(t, profs, 11)
|
||
|
||
return team.ID, host
|
||
}
|
||
|
||
tests := []struct {
|
||
name string
|
||
setupFunc func() (uint, *fleet.Host)
|
||
wantMac map[string]*fleet.ExpectedMDMProfile
|
||
wantWindows map[string]*fleet.ExpectedMDMProfile
|
||
os string
|
||
}{
|
||
{
|
||
name: "macos basic team profiles no labels",
|
||
setupFunc: macosBasicTeamProfNoLabelsSetup,
|
||
wantMac: map[string]*fleet.ExpectedMDMProfile{
|
||
"T1.1": {Identifier: "T1.1"},
|
||
"T1.2": {Identifier: "T1.2"},
|
||
},
|
||
},
|
||
{
|
||
name: "macos labeled team profile",
|
||
setupFunc: macosLabeledTeamProfSetup,
|
||
wantMac: map[string]*fleet.ExpectedMDMProfile{
|
||
"T2.1": {Identifier: "T2.1"},
|
||
"T2.2": {Identifier: "T2.2"},
|
||
"labeled_prof": {Identifier: "labeled_prof"},
|
||
},
|
||
},
|
||
{
|
||
name: "macos labeled team profile with additional labeled profile",
|
||
setupFunc: macosLabeledTeamProfWithAdditionalLabeledProfSetup,
|
||
// Our expected profiles should not include the labeled profile, because it
|
||
// maps to a label that is not applied to the host.
|
||
wantMac: map[string]*fleet.ExpectedMDMProfile{
|
||
"T3.1": {Identifier: "T3.1"},
|
||
"T3.2": {Identifier: "T3.2"},
|
||
},
|
||
},
|
||
{
|
||
name: "macos profile with broken label",
|
||
setupFunc: macosProfWithBrokenLabelSetup,
|
||
// Our expected profiles should not include the labeled profile, because it is broken
|
||
// (the label was deleted)
|
||
wantMac: map[string]*fleet.ExpectedMDMProfile{
|
||
"T4.1": {Identifier: "T4.1"},
|
||
"T4.2": {Identifier: "T4.2"},
|
||
},
|
||
},
|
||
{
|
||
name: "macos basic team profiles no labels, install date overridden on first",
|
||
setupFunc: macosInstallDateOverrideSetup,
|
||
wantMac: map[string]*fleet.ExpectedMDMProfile{
|
||
"T5.1": {Identifier: "T5.1", EarliestInstallDate: overrideEarliestInstallDate},
|
||
"T5.2": {Identifier: "T5.2", EarliestInstallDate: baseEarliestInstallDate},
|
||
"T5.3": {Identifier: "T5.3", EarliestInstallDate: baseEarliestInstallDate},
|
||
},
|
||
},
|
||
{
|
||
name: "macos labels include any/all and exclude rules",
|
||
setupFunc: macosLabeledProfileRulesSetup,
|
||
wantMac: map[string]*fleet.ExpectedMDMProfile{
|
||
"T6.1": {Identifier: "T6.1"},
|
||
"T6.2": {Identifier: "T6.2"},
|
||
"include_any_all_match_prof": {Identifier: "include_any_all_match_prof"},
|
||
"include_any_one_matches_prof": {Identifier: "include_any_one_matches_prof"},
|
||
"include_all_all_match_prof": {Identifier: "include_all_all_match_prof"},
|
||
"exclude_none_match_prof": {Identifier: "exclude_none_match_prof"},
|
||
},
|
||
},
|
||
{
|
||
name: "windows basic team profiles no labels",
|
||
setupFunc: windowsBasicTeamProfNoLabelsSetup,
|
||
wantWindows: map[string]*fleet.ExpectedMDMProfile{
|
||
"T1.1": {Name: "T1.1"},
|
||
"T1.2": {Name: "T1.2"},
|
||
},
|
||
},
|
||
{
|
||
name: "windows labeled team profile",
|
||
setupFunc: windowsLabeledTeamProfSetup,
|
||
wantWindows: map[string]*fleet.ExpectedMDMProfile{
|
||
"T2.1": {Name: "T2.1"},
|
||
"T2.2": {Name: "T2.2"},
|
||
"labeled_prof": {Name: "labeled_prof"},
|
||
},
|
||
},
|
||
{
|
||
name: "windows labeled team profile with additional labeled profile",
|
||
setupFunc: windowsLabeledTeamProfWithAdditionalLabeledProfSetup,
|
||
// Our expected profiles should not include the labeled profile, because it
|
||
// maps to a label that is not applied to the host.
|
||
wantWindows: map[string]*fleet.ExpectedMDMProfile{
|
||
"T3.1": {Name: "T3.1"},
|
||
"T3.2": {Name: "T3.2"},
|
||
},
|
||
},
|
||
{
|
||
name: "windows profile with broken label",
|
||
setupFunc: windowsProfWithBrokenLabelSetup,
|
||
// Our expected profiles should not include the labeled profile, because it is broken
|
||
// (the label was deleted)
|
||
wantWindows: map[string]*fleet.ExpectedMDMProfile{
|
||
"T4.1": {Name: "T4.1"},
|
||
"T4.2": {Name: "T4.2"},
|
||
},
|
||
},
|
||
{
|
||
name: "windows labels include any/all and exclude rules",
|
||
setupFunc: windowsLabeledProfileRulesSetup,
|
||
wantWindows: map[string]*fleet.ExpectedMDMProfile{
|
||
"T5.1": {Name: "T5.1"},
|
||
"T5.2": {Name: "T5.2"},
|
||
"include_any_all_match_prof": {Name: "include_any_all_match_prof"},
|
||
"include_any_one_matches_prof": {Name: "include_any_one_matches_prof"},
|
||
"include_all_all_match_prof": {Name: "include_all_all_match_prof"},
|
||
"exclude_none_match_prof": {Name: "exclude_none_match_prof"},
|
||
},
|
||
},
|
||
}
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
teamID, host := tt.setupFunc()
|
||
timeZero := time.Time{}
|
||
if len(tt.wantMac) > 0 {
|
||
got, err := ds.getHostMDMAppleProfilesExpectedForVerification(ctx, teamID, host)
|
||
require.NoError(t, err)
|
||
for k, v := range tt.wantMac {
|
||
require.Contains(t, got, k)
|
||
require.Equal(t, v.Identifier, got[k].Identifier)
|
||
// Only check earliest install date if we are overriding it in the test setup
|
||
if v.EarliestInstallDate != timeZero {
|
||
require.Equal(t, v.EarliestInstallDate, got[k].EarliestInstallDate)
|
||
}
|
||
}
|
||
}
|
||
|
||
if len(tt.wantWindows) > 0 {
|
||
got, err := ds.getHostMDMWindowsProfilesExpectedForVerification(ctx, teamID, host.ID)
|
||
require.NoError(t, err)
|
||
for k, v := range tt.wantWindows {
|
||
require.Contains(t, got, k)
|
||
require.Equal(t, v.Name, got[k].Name)
|
||
// windows does not currently use or care about earliest install date
|
||
}
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
func testBatchSetProfileLabelAssociations(t *testing.T, ds *Datastore) {
|
||
ctx := context.Background()
|
||
|
||
// create a label
|
||
label := &fleet.Label{
|
||
Name: "my label",
|
||
Description: "a label",
|
||
Query: "select 1 from processes;",
|
||
}
|
||
label, err := ds.NewLabel(ctx, label)
|
||
require.NoError(t, err)
|
||
|
||
// create a macOS config profile
|
||
macOSProfile, err := ds.NewMDMAppleConfigProfile(
|
||
ctx,
|
||
fleet.MDMAppleConfigProfile{
|
||
Name: "DummyTestName",
|
||
Identifier: "DummyTestIdentifier",
|
||
Mobileconfig: mobileconfig.Mobileconfig([]byte("DummyTestMobileconfigBytes")),
|
||
TeamID: nil,
|
||
},
|
||
nil,
|
||
)
|
||
require.NoError(t, err)
|
||
otherMacProfile, err := ds.NewMDMAppleConfigProfile(
|
||
ctx,
|
||
fleet.MDMAppleConfigProfile{
|
||
Name: "OtherDummyTestName",
|
||
Identifier: "OtherDummyTestIdentifier",
|
||
Mobileconfig: mobileconfig.Mobileconfig([]byte("OtherDummyTestMobileconfigBytes")),
|
||
TeamID: nil,
|
||
},
|
||
nil,
|
||
)
|
||
require.NoError(t, err)
|
||
|
||
// create a Windows config profile
|
||
windowsProfile, err := ds.NewMDMWindowsConfigProfile(
|
||
ctx,
|
||
fleet.MDMWindowsConfigProfile{
|
||
Name: "with-labels",
|
||
TeamID: nil,
|
||
SyncML: []byte("<Replace></Replace>"),
|
||
},
|
||
nil,
|
||
)
|
||
require.NoError(t, err)
|
||
otherWinProfile, err := ds.NewMDMWindowsConfigProfile(
|
||
ctx,
|
||
fleet.MDMWindowsConfigProfile{
|
||
Name: "other-with-labels",
|
||
TeamID: nil,
|
||
SyncML: []byte("<Replace></Replace>"),
|
||
},
|
||
nil,
|
||
)
|
||
require.NoError(t, err)
|
||
|
||
// assign the label to the "other" profiles, should not change throughout the test
|
||
wantOtherWin := []fleet.ConfigurationProfileLabel{
|
||
{ProfileUUID: otherWinProfile.ProfileUUID, LabelName: label.Name, LabelID: label.ID},
|
||
}
|
||
updatedDB, err := batchSetProfileLabelAssociationsDB(ctx, ds.writer(ctx), wantOtherWin, []string{windowsProfile.ProfileUUID}, "windows")
|
||
require.NoError(t, err)
|
||
assert.True(t, updatedDB)
|
||
// make it an "exclude" label on the other macos profile
|
||
wantOtherMac := []fleet.ConfigurationProfileLabel{
|
||
{ProfileUUID: otherMacProfile.ProfileUUID, LabelName: label.Name, LabelID: label.ID, Exclude: true},
|
||
}
|
||
updatedDB, err = batchSetProfileLabelAssociationsDB(ctx, ds.writer(ctx), wantOtherMac, []string{macOSProfile.ProfileUUID}, "darwin")
|
||
require.NoError(t, err)
|
||
assert.True(t, updatedDB)
|
||
|
||
platforms := map[string]string{
|
||
"darwin": macOSProfile.ProfileUUID,
|
||
"windows": windowsProfile.ProfileUUID,
|
||
}
|
||
|
||
for platform, uuid := range platforms {
|
||
expectLabels := func(t *testing.T, profUUID, platform string, want []fleet.ConfigurationProfileLabel) {
|
||
p := platform
|
||
if p == "darwin" {
|
||
p = "apple"
|
||
}
|
||
|
||
query := fmt.Sprintf(
|
||
"SELECT %s_profile_uuid as profile_uuid, label_id, label_name, exclude FROM mdm_configuration_profile_labels WHERE %s_profile_uuid = ?",
|
||
p,
|
||
p,
|
||
)
|
||
|
||
var got []fleet.ConfigurationProfileLabel
|
||
ExecAdhocSQL(t, ds, func(tx sqlx.ExtContext) error {
|
||
err := sqlx.SelectContext(ctx, tx, &got, query, profUUID)
|
||
require.NoError(t, err)
|
||
require.Len(t, got, len(want))
|
||
return nil
|
||
})
|
||
require.ElementsMatch(t, want, got)
|
||
}
|
||
|
||
t.Run("empty input "+platform, func(t *testing.T) {
|
||
want := []fleet.ConfigurationProfileLabel{}
|
||
err := ds.withTx(ctx, func(tx sqlx.ExtContext) error {
|
||
updatedDB, err := batchSetProfileLabelAssociationsDB(ctx, tx, want, nil, platform)
|
||
require.NoError(t, err)
|
||
assert.False(t, updatedDB)
|
||
return err
|
||
})
|
||
require.NoError(t, err)
|
||
expectLabels(t, uuid, platform, want)
|
||
// does not change other profiles
|
||
expectLabels(t, otherWinProfile.ProfileUUID, "windows", wantOtherWin)
|
||
expectLabels(t, otherMacProfile.ProfileUUID, "darwin", wantOtherMac)
|
||
})
|
||
|
||
t.Run("valid input "+platform, func(t *testing.T) {
|
||
profileLabels := []fleet.ConfigurationProfileLabel{
|
||
{ProfileUUID: uuid, LabelName: label.Name, LabelID: label.ID},
|
||
}
|
||
err := ds.withTx(ctx, func(tx sqlx.ExtContext) error {
|
||
updatedDB, err := batchSetProfileLabelAssociationsDB(ctx, tx, profileLabels, nil, platform)
|
||
require.NoError(t, err)
|
||
assert.True(t, updatedDB)
|
||
return err
|
||
})
|
||
require.NoError(t, err)
|
||
expectLabels(t, uuid, platform, profileLabels)
|
||
// does not change other profiles
|
||
expectLabels(t, otherWinProfile.ProfileUUID, "windows", wantOtherWin)
|
||
expectLabels(t, otherMacProfile.ProfileUUID, "darwin", wantOtherMac)
|
||
|
||
// now set it with Exclude mode
|
||
profileLabels = []fleet.ConfigurationProfileLabel{
|
||
{ProfileUUID: uuid, LabelName: label.Name, LabelID: label.ID, Exclude: true},
|
||
}
|
||
err = ds.withTx(ctx, func(tx sqlx.ExtContext) error {
|
||
updatedDB, err := batchSetProfileLabelAssociationsDB(ctx, tx, profileLabels, nil, platform)
|
||
require.NoError(t, err)
|
||
assert.True(t, updatedDB)
|
||
return err
|
||
})
|
||
require.NoError(t, err)
|
||
expectLabels(t, uuid, platform, profileLabels)
|
||
// does not change other profiles
|
||
expectLabels(t, otherWinProfile.ProfileUUID, "windows", wantOtherWin)
|
||
expectLabels(t, otherMacProfile.ProfileUUID, "darwin", wantOtherMac)
|
||
})
|
||
|
||
t.Run("invalid profile UUID "+platform, func(t *testing.T) {
|
||
invalidProfileLabels := []fleet.ConfigurationProfileLabel{
|
||
{ProfileUUID: "invalid-uuid", LabelName: label.Name, LabelID: label.ID},
|
||
}
|
||
|
||
err := ds.withTx(ctx, func(tx sqlx.ExtContext) error {
|
||
_, err := batchSetProfileLabelAssociationsDB(ctx, tx, invalidProfileLabels, nil, platform)
|
||
return err
|
||
})
|
||
require.Error(t, err)
|
||
})
|
||
|
||
t.Run("invalid label data "+platform, func(t *testing.T) {
|
||
// invalid id
|
||
invalidProfileLabels := []fleet.ConfigurationProfileLabel{
|
||
{ProfileUUID: uuid, LabelName: label.Name, LabelID: 12345},
|
||
}
|
||
err := ds.withTx(ctx, func(tx sqlx.ExtContext) error {
|
||
_, err := batchSetProfileLabelAssociationsDB(ctx, tx, invalidProfileLabels, nil, platform)
|
||
return err
|
||
})
|
||
require.Error(t, err)
|
||
|
||
// both invalid
|
||
invalidProfileLabels = []fleet.ConfigurationProfileLabel{
|
||
{ProfileUUID: uuid, LabelName: "xyz", LabelID: 1235},
|
||
}
|
||
err = ds.withTx(ctx, func(tx sqlx.ExtContext) error {
|
||
_, err := batchSetProfileLabelAssociationsDB(ctx, tx, invalidProfileLabels, nil, platform)
|
||
return err
|
||
})
|
||
require.Error(t, err)
|
||
})
|
||
|
||
t.Run("labels are removed "+platform, func(t *testing.T) {
|
||
// create a new label
|
||
newLabel := &fleet.Label{
|
||
Name: "new label" + platform,
|
||
Description: "a label",
|
||
Query: "select 1 from orbit_info;",
|
||
}
|
||
newLabel, err := ds.NewLabel(ctx, newLabel)
|
||
require.NoError(t, err)
|
||
|
||
// apply a batch set with the new label
|
||
profileLabels := []fleet.ConfigurationProfileLabel{
|
||
{ProfileUUID: uuid, LabelName: label.Name, LabelID: label.ID, Exclude: true},
|
||
{ProfileUUID: uuid, LabelName: newLabel.Name, LabelID: newLabel.ID, Exclude: true},
|
||
}
|
||
err = ds.withTx(ctx, func(tx sqlx.ExtContext) error {
|
||
updatedDB, err := batchSetProfileLabelAssociationsDB(ctx, tx, profileLabels, nil, platform)
|
||
require.NoError(t, err)
|
||
assert.True(t, updatedDB)
|
||
return err
|
||
})
|
||
require.NoError(t, err)
|
||
// both are stored in the DB
|
||
expectLabels(t, uuid, platform, profileLabels)
|
||
|
||
// batch apply again without the newLabel, and without Exclude flag
|
||
profileLabels = []fleet.ConfigurationProfileLabel{
|
||
{ProfileUUID: uuid, LabelName: label.Name, LabelID: label.ID},
|
||
}
|
||
err = ds.withTx(ctx, func(tx sqlx.ExtContext) error {
|
||
updatedDB, err := batchSetProfileLabelAssociationsDB(ctx, tx, profileLabels, nil, platform)
|
||
require.NoError(t, err)
|
||
assert.True(t, updatedDB)
|
||
return err
|
||
})
|
||
require.NoError(t, err)
|
||
expectLabels(t, uuid, platform, profileLabels)
|
||
|
||
// batch apply again this time without any label
|
||
err = ds.withTx(ctx, func(tx sqlx.ExtContext) error {
|
||
updatedDB, err := batchSetProfileLabelAssociationsDB(ctx, tx, nil, []string{uuid}, platform)
|
||
require.NoError(t, err)
|
||
assert.True(t, updatedDB)
|
||
return err
|
||
})
|
||
require.NoError(t, err)
|
||
expectLabels(t, uuid, platform, nil)
|
||
|
||
// does not change other profiles
|
||
expectLabels(t, otherWinProfile.ProfileUUID, "windows", wantOtherWin)
|
||
expectLabels(t, otherMacProfile.ProfileUUID, "darwin", wantOtherMac)
|
||
|
||
// batch apply again with no change returns false
|
||
err = ds.withTx(ctx, func(tx sqlx.ExtContext) error {
|
||
updatedDB, err := batchSetProfileLabelAssociationsDB(ctx, tx, nil, []string{uuid}, platform)
|
||
require.NoError(t, err)
|
||
assert.False(t, updatedDB)
|
||
return err
|
||
})
|
||
require.NoError(t, err)
|
||
expectLabels(t, uuid, platform, nil)
|
||
})
|
||
}
|
||
|
||
t.Run("unsupported platform", func(t *testing.T) {
|
||
err := ds.withTx(ctx, func(tx sqlx.ExtContext) error {
|
||
_, err := batchSetProfileLabelAssociationsDB(
|
||
ctx,
|
||
tx,
|
||
[]fleet.ConfigurationProfileLabel{{}},
|
||
nil,
|
||
"unsupported",
|
||
)
|
||
return err
|
||
})
|
||
require.Error(t, err)
|
||
})
|
||
}
|
||
|
||
func testMDMEULA(t *testing.T, ds *Datastore) {
|
||
ctx := context.Background()
|
||
eula := &fleet.MDMEULA{
|
||
Token: uuid.New().String(),
|
||
Name: "eula.pdf",
|
||
Bytes: []byte("contents"),
|
||
Sha256: []byte("test-sha256"),
|
||
}
|
||
|
||
err := ds.MDMInsertEULA(ctx, eula)
|
||
require.NoError(t, err)
|
||
|
||
var ae fleet.AlreadyExistsError
|
||
err = ds.MDMInsertEULA(ctx, eula)
|
||
require.ErrorAs(t, err, &ae)
|
||
|
||
gotEULA, err := ds.MDMGetEULAMetadata(ctx)
|
||
require.NoError(t, err)
|
||
require.NotEmpty(t, gotEULA.CreatedAt)
|
||
require.Equal(t, eula.Token, gotEULA.Token)
|
||
require.Equal(t, eula.Name, gotEULA.Name)
|
||
|
||
gotEULABytes, err := ds.MDMGetEULABytes(ctx, eula.Token)
|
||
require.NoError(t, err)
|
||
require.EqualValues(t, eula.Bytes, gotEULABytes.Bytes)
|
||
require.Equal(t, eula.Name, gotEULABytes.Name)
|
||
|
||
err = ds.MDMDeleteEULA(ctx, eula.Token)
|
||
require.NoError(t, err)
|
||
|
||
var nfe fleet.NotFoundError
|
||
_, err = ds.MDMGetEULAMetadata(ctx)
|
||
require.ErrorAs(t, err, &nfe)
|
||
_, err = ds.MDMGetEULABytes(ctx, eula.Token)
|
||
require.ErrorAs(t, err, &nfe)
|
||
err = ds.MDMDeleteEULA(ctx, eula.Token)
|
||
require.ErrorAs(t, err, &nfe)
|
||
|
||
err = ds.MDMInsertEULA(ctx, eula)
|
||
require.NoError(t, err)
|
||
}
|
||
|
||
func testSCEPRenewalHelpers(t *testing.T, ds *Datastore) {
|
||
ctx := context.Background()
|
||
scepDepot, err := ds.NewSCEPDepot()
|
||
require.NoError(t, err)
|
||
|
||
nanoStorage, err := ds.NewMDMAppleMDMStorage()
|
||
require.NoError(t, err)
|
||
|
||
addCert := func(notAfter time.Time, h *fleet.Host) {
|
||
serial, err := scepDepot.Serial()
|
||
require.NoError(t, err)
|
||
cert := &x509.Certificate{
|
||
SerialNumber: serial,
|
||
Subject: pkix.Name{
|
||
CommonName: "Fleet Identity",
|
||
},
|
||
NotBefore: time.Now().Add(-24 * time.Hour),
|
||
NotAfter: notAfter,
|
||
// use a random value, just to make sure they're
|
||
// different from each other, we don't care about the
|
||
// DER contents here
|
||
Raw: []byte(uuid.NewString()),
|
||
}
|
||
err = scepDepot.Put(cert.Subject.CommonName, cert)
|
||
require.NoError(t, err)
|
||
req := mdm.Request{
|
||
EnrollID: &mdm.EnrollID{ID: h.UUID},
|
||
Context: ctx,
|
||
}
|
||
certHash := certauth.HashCert(cert)
|
||
err = nanoStorage.AssociateCertHash(&req, certHash, notAfter)
|
||
require.NoError(t, err)
|
||
createdat := time.Now().AddDate(0, int(serial.Int64()), 0)
|
||
|
||
// due to mysql timestamp resolution, this test is flaky unless
|
||
// we do this because we insert multiple certificates for the
|
||
// same device in quick succession, and later on we assert on a
|
||
// specific order which is based on created_at
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
_, err := q.ExecContext(ctx, `
|
||
UPDATE nano_cert_auth_associations
|
||
SET created_at = ?
|
||
WHERE sha256 = ?
|
||
`, createdat, certHash)
|
||
return err
|
||
})
|
||
}
|
||
|
||
var i int
|
||
setHost := func(notAfter time.Time, doUserDeviceEnrollment bool) *fleet.Host {
|
||
i++
|
||
h, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: fmt.Sprintf("test-host%d-name", i),
|
||
OsqueryHostID: ptr.String(fmt.Sprintf("osquery-%d", i)),
|
||
NodeKey: ptr.String(fmt.Sprintf("nodekey-%d", i)),
|
||
UUID: fmt.Sprintf("test-uuid-%d", i),
|
||
Platform: "darwin",
|
||
})
|
||
require.NoError(t, err)
|
||
|
||
// create a cert + association
|
||
addCert(notAfter, h)
|
||
if doUserDeviceEnrollment {
|
||
nanoEnrollUserDevice(t, ds, h)
|
||
} else {
|
||
nanoEnroll(t, ds, h, true)
|
||
}
|
||
return h
|
||
}
|
||
|
||
// certs expired at lest 1 year ago
|
||
h1 := setHost(time.Now().AddDate(-1, -3, 0), false)
|
||
h2 := setHost(time.Now().AddDate(-1, -2, 0), false)
|
||
// cert that expires in 1 month
|
||
h3 := setHost(time.Now().AddDate(0, 1, 0), false)
|
||
// User Enrollment (Device) cert that expires in 1 month and 1 day just
|
||
// so we can add some assertions on the returned enrollment type
|
||
h4 := setHost(time.Now().AddDate(0, 1, 1), true)
|
||
// cert that expires in 1 year
|
||
h5 := setHost(time.Now().AddDate(1, 0, 0), false)
|
||
// expired cert for a host migrated using touchless migration
|
||
hMigrated := setHost(time.Now().AddDate(-1, -1, 0), false)
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
_, err := q.ExecContext(ctx, `
|
||
UPDATE nano_enrollments
|
||
SET enrolled_from_migration = 1
|
||
WHERE id = ?
|
||
`, hMigrated.UUID)
|
||
return err
|
||
})
|
||
|
||
// list assocs that expire in the next 10 days
|
||
assocs, err := ds.GetHostCertAssociationsToExpire(ctx, 10, 100)
|
||
require.NoError(t, err)
|
||
require.Len(t, assocs, 3)
|
||
require.Equal(t, h1.UUID, assocs[0].HostUUID)
|
||
require.Equal(t, h2.UUID, assocs[1].HostUUID)
|
||
require.Equal(t, hMigrated.UUID, assocs[2].HostUUID)
|
||
require.True(t, assocs[2].EnrolledFromMigration)
|
||
|
||
// list certs that expire in the next 1000 days with limit = 1
|
||
assocs, err = ds.GetHostCertAssociationsToExpire(ctx, 1000, 1)
|
||
require.NoError(t, err)
|
||
require.Len(t, assocs, 1)
|
||
require.Equal(t, h1.UUID, assocs[0].HostUUID)
|
||
|
||
// list certs that expire in the next 50 days
|
||
assocs, err = ds.GetHostCertAssociationsToExpire(ctx, 50, 100)
|
||
require.NoError(t, err)
|
||
require.Len(t, assocs, 5)
|
||
require.Equal(t, h1.UUID, assocs[0].HostUUID)
|
||
assert.Equal(t, "Device", assocs[0].EnrollmentType)
|
||
require.Equal(t, h2.UUID, assocs[1].HostUUID)
|
||
assert.Equal(t, "Device", assocs[1].EnrollmentType)
|
||
require.Equal(t, hMigrated.UUID, assocs[2].HostUUID)
|
||
assert.Equal(t, "Device", assocs[2].EnrollmentType)
|
||
require.Equal(t, h3.UUID, assocs[3].HostUUID)
|
||
assert.Equal(t, "Device", assocs[3].EnrollmentType)
|
||
require.Equal(t, h4.UUID, assocs[4].HostUUID)
|
||
assert.Equal(t, "User Enrollment (Device)", assocs[4].EnrollmentType)
|
||
|
||
// list certs that expire in the next 1000 days
|
||
assocs, err = ds.GetHostCertAssociationsToExpire(ctx, 1000, 100)
|
||
require.NoError(t, err)
|
||
require.Len(t, assocs, 6)
|
||
require.Equal(t, h1.UUID, assocs[0].HostUUID)
|
||
assert.Equal(t, "Device", assocs[0].EnrollmentType)
|
||
require.Equal(t, h2.UUID, assocs[1].HostUUID)
|
||
assert.Equal(t, "Device", assocs[1].EnrollmentType)
|
||
require.Equal(t, hMigrated.UUID, assocs[2].HostUUID)
|
||
assert.Equal(t, "Device", assocs[2].EnrollmentType)
|
||
require.Equal(t, h3.UUID, assocs[3].HostUUID)
|
||
assert.Equal(t, "Device", assocs[3].EnrollmentType)
|
||
require.Equal(t, h4.UUID, assocs[4].HostUUID)
|
||
assert.Equal(t, "User Enrollment (Device)", assocs[4].EnrollmentType)
|
||
require.Equal(t, h5.UUID, assocs[5].HostUUID)
|
||
assert.Equal(t, "Device", assocs[5].EnrollmentType)
|
||
|
||
// add a new host with a very old expiriy so it shows first, verify
|
||
// that it's present before deleting it.
|
||
h6 := setHost(time.Now().AddDate(-2, -1, 0), false)
|
||
assocs, err = ds.GetHostCertAssociationsToExpire(ctx, 1000, 100)
|
||
require.NoError(t, err)
|
||
require.Len(t, assocs, 7)
|
||
require.Equal(t, h6.UUID, assocs[0].HostUUID)
|
||
require.Equal(t, h1.UUID, assocs[1].HostUUID)
|
||
require.Equal(t, h2.UUID, assocs[2].HostUUID)
|
||
require.Equal(t, hMigrated.UUID, assocs[3].HostUUID)
|
||
require.Equal(t, h3.UUID, assocs[4].HostUUID)
|
||
require.Equal(t, h4.UUID, assocs[5].HostUUID)
|
||
require.Equal(t, h5.UUID, assocs[6].HostUUID)
|
||
|
||
// delete the host and verify that things work as expected
|
||
// see https://github.com/fleetdm/fleet/issues/19149
|
||
require.NoError(t, ds.DeleteHost(ctx, h6.ID))
|
||
assocs, err = ds.GetHostCertAssociationsToExpire(ctx, 1000, 100)
|
||
require.NoError(t, err)
|
||
require.Len(t, assocs, 6)
|
||
require.Equal(t, h1.UUID, assocs[0].HostUUID)
|
||
require.Equal(t, h2.UUID, assocs[1].HostUUID)
|
||
require.Equal(t, hMigrated.UUID, assocs[2].HostUUID)
|
||
require.Equal(t, h3.UUID, assocs[3].HostUUID)
|
||
require.Equal(t, h4.UUID, assocs[4].HostUUID)
|
||
require.Equal(t, h5.UUID, assocs[5].HostUUID)
|
||
|
||
// add a second expired cert to one of the hosts
|
||
addCert(time.Now().AddDate(-1, 0, 0), h1)
|
||
assocs, err = ds.GetHostCertAssociationsToExpire(ctx, 1000, 100)
|
||
require.Len(t, assocs, 6)
|
||
require.Equal(t, h2.UUID, assocs[0].HostUUID)
|
||
require.Equal(t, hMigrated.UUID, assocs[1].HostUUID)
|
||
require.Equal(t, h1.UUID, assocs[2].HostUUID)
|
||
require.Equal(t, h3.UUID, assocs[3].HostUUID)
|
||
require.Equal(t, h4.UUID, assocs[4].HostUUID)
|
||
require.Equal(t, h5.UUID, assocs[5].HostUUID)
|
||
|
||
checkSCEPRenew := func(assoc fleet.SCEPIdentityAssociation, want *string) {
|
||
var got *string
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
return sqlx.GetContext(
|
||
ctx,
|
||
q,
|
||
&got,
|
||
`SELECT renew_command_uuid FROM nano_cert_auth_associations WHERE id = ? AND sha256 = ?`,
|
||
assoc.HostUUID,
|
||
assoc.SHA256,
|
||
)
|
||
})
|
||
require.EqualValues(t, want, got)
|
||
}
|
||
|
||
// insert dummy nano commands
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
_, err = q.ExecContext(ctx, `
|
||
INSERT INTO nano_commands (command_uuid, request_type, command)
|
||
VALUES ('foo', 'foo', '<?xml'), ('bar', 'bar', '<?xml')
|
||
`)
|
||
return err
|
||
})
|
||
|
||
err = ds.SetCommandForPendingSCEPRenewal(ctx, []fleet.SCEPIdentityAssociation{}, "foo")
|
||
checkSCEPRenew(assocs[0], nil)
|
||
checkSCEPRenew(assocs[1], nil)
|
||
checkSCEPRenew(assocs[2], nil)
|
||
checkSCEPRenew(assocs[3], nil)
|
||
checkSCEPRenew(assocs[4], nil)
|
||
checkSCEPRenew(assocs[5], nil)
|
||
require.NoError(t, err)
|
||
|
||
err = ds.SetCommandForPendingSCEPRenewal(ctx, []fleet.SCEPIdentityAssociation{assocs[0]}, "foo")
|
||
require.NoError(t, err)
|
||
checkSCEPRenew(assocs[0], ptr.String("foo"))
|
||
checkSCEPRenew(assocs[1], nil)
|
||
checkSCEPRenew(assocs[2], nil)
|
||
checkSCEPRenew(assocs[3], nil)
|
||
checkSCEPRenew(assocs[4], nil)
|
||
checkSCEPRenew(assocs[5], nil)
|
||
|
||
err = ds.SetCommandForPendingSCEPRenewal(ctx, assocs, "bar")
|
||
require.NoError(t, err)
|
||
checkSCEPRenew(assocs[0], ptr.String("bar"))
|
||
checkSCEPRenew(assocs[1], ptr.String("bar"))
|
||
checkSCEPRenew(assocs[2], ptr.String("bar"))
|
||
checkSCEPRenew(assocs[3], ptr.String("bar"))
|
||
checkSCEPRenew(assocs[4], ptr.String("bar"))
|
||
checkSCEPRenew(assocs[5], ptr.String("bar"))
|
||
|
||
err = ds.SetCommandForPendingSCEPRenewal(
|
||
ctx,
|
||
[]fleet.SCEPIdentityAssociation{{HostUUID: "foo", SHA256: "bar"}},
|
||
"bar",
|
||
)
|
||
require.ErrorContains(t, err, "this function can only be used to update existing associations")
|
||
|
||
err = ds.CleanSCEPRenewRefs(ctx, "does-not-exist")
|
||
require.Error(t, err)
|
||
|
||
err = ds.CleanSCEPRenewRefs(ctx, h1.UUID)
|
||
require.NoError(t, err)
|
||
checkSCEPRenew(assocs[2], nil)
|
||
}
|
||
|
||
func testMDMProfilesSummaryAndHostFilters(t *testing.T, ds *Datastore) {
|
||
// TODO: Expand this test to include:
|
||
// - more scenarios for windows
|
||
// - disk encryption (mac and windows)
|
||
// - more scenarios for labels
|
||
|
||
ctx := context.Background()
|
||
|
||
checkSummaryWindows := func(t *testing.T, teamID *uint, expected fleet.MDMProfilesSummary) {
|
||
ps, err := ds.GetMDMWindowsProfilesSummary(ctx, teamID)
|
||
require.NoError(t, err)
|
||
require.NotNil(t, ps)
|
||
require.Equal(t, expected, *ps)
|
||
}
|
||
|
||
checkSummaryMac := func(t *testing.T, teamID *uint, expected fleet.MDMProfilesSummary) {
|
||
ps, err := ds.GetMDMAppleProfilesSummary(ctx, teamID)
|
||
require.NoError(t, err)
|
||
require.NotNil(t, ps)
|
||
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) {
|
||
expectSummaryWindows := map[fleet.MDMDeliveryStatus]uint{}
|
||
expectSummaryMac := map[fleet.MDMDeliveryStatus]uint{}
|
||
for status, ids := range ep {
|
||
if len(ids) > 0 {
|
||
for _, id := range ids {
|
||
if id < 5 {
|
||
expectSummaryWindows[status]++
|
||
} else {
|
||
expectSummaryMac[status]++
|
||
}
|
||
}
|
||
}
|
||
}
|
||
checkSummaryMac(t, teamID, fleet.MDMProfilesSummary{
|
||
Pending: expectSummaryMac[fleet.MDMDeliveryPending],
|
||
Failed: expectSummaryMac[fleet.MDMDeliveryFailed],
|
||
Verifying: expectSummaryMac[fleet.MDMDeliveryVerifying],
|
||
Verified: expectSummaryMac[fleet.MDMDeliveryVerified],
|
||
})
|
||
|
||
checkSummaryWindows(t, teamID, fleet.MDMProfilesSummary{
|
||
Pending: expectSummaryWindows[fleet.MDMDeliveryPending],
|
||
Failed: expectSummaryWindows[fleet.MDMDeliveryFailed],
|
||
Verifying: expectSummaryWindows[fleet.MDMDeliveryVerifying],
|
||
Verified: expectSummaryWindows[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],
|
||
)
|
||
}
|
||
|
||
// checkWinHostProfiles := func(t *testing.T, hostUUID string, statusByProfUUID map[string]string) {
|
||
// profs, err := ds.GetHostMDMWindowsProfiles(ctx, hostUUID)
|
||
// require.NoError(t, err)
|
||
// require.Len(t, profs, len(statusByProfUUID))
|
||
// for _, prof := range profs {
|
||
// ep, ok := statusByProfUUID[prof.ProfileUUID]
|
||
// require.True(t, ok)
|
||
// require.Equal(t, ep, prof.Status)
|
||
// }
|
||
// }
|
||
|
||
checkMacHostProfiles := func(t *testing.T, hostUUID string, statusByProfUUID map[string]string) {
|
||
profs, err := ds.GetHostMDMAppleProfiles(ctx, hostUUID)
|
||
require.NoError(t, err)
|
||
require.Len(t, profs, len(statusByProfUUID))
|
||
for _, prof := range profs {
|
||
ep, ok := statusByProfUUID[prof.ProfileUUID]
|
||
require.True(t, ok)
|
||
require.NotNil(t, prof.Status)
|
||
require.Equal(t, fleet.MDMDeliveryStatus(ep), *prof.Status)
|
||
}
|
||
}
|
||
|
||
upsertHostProfileStatus := func(t *testing.T, hostUUID string, profUUID string, status *fleet.MDMDeliveryStatus) {
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
var table string
|
||
var profType string
|
||
switch {
|
||
case strings.HasPrefix(profUUID, "a"):
|
||
table = "host_mdm_apple_profiles"
|
||
profType = "profile"
|
||
case strings.HasPrefix(profUUID, "w"):
|
||
table = "host_mdm_windows_profiles"
|
||
profType = "profile"
|
||
case strings.HasPrefix(profUUID, "d"):
|
||
table = "host_mdm_apple_declarations"
|
||
profType = "declaration"
|
||
default:
|
||
require.FailNow(t, "unknown profile type")
|
||
}
|
||
|
||
var stmt string
|
||
var err error
|
||
switch {
|
||
case table == "host_mdm_apple_profiles":
|
||
// Apple profiles require profile_identifier, checksum, and command_uuid
|
||
commandUUID := "cmd-" + profUUID
|
||
stmt = fmt.Sprintf(
|
||
`INSERT INTO %s (host_uuid, %s_uuid, status, profile_identifier, checksum, command_uuid) VALUES (?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE status = ?`,
|
||
table,
|
||
profType,
|
||
)
|
||
testChecksum := test.MakeTestBytes() // 16 bytes for checksum
|
||
_, err = q.ExecContext(ctx, stmt, hostUUID, profUUID, status, "com.test."+profUUID, testChecksum, commandUUID, status)
|
||
case table == "host_mdm_apple_declarations":
|
||
// Apple declarations require declaration_identifier and token
|
||
testToken := test.MakeTestBytes() // 16 bytes for token
|
||
stmt = fmt.Sprintf(
|
||
`INSERT INTO %s (host_uuid, %s_uuid, status, declaration_identifier, token) VALUES (?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE status = ?`,
|
||
table,
|
||
profType,
|
||
)
|
||
_, err = q.ExecContext(ctx, stmt, hostUUID, profUUID, status, "com.test."+profUUID, testToken, status)
|
||
case strings.HasPrefix(profUUID, "w"):
|
||
// Windows profiles require command_uuid
|
||
commandUUID := "cmd-" + profUUID
|
||
stmt = fmt.Sprintf(
|
||
`INSERT INTO %s (host_uuid, %s_uuid, status, command_uuid) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE status = ?`,
|
||
table,
|
||
profType,
|
||
)
|
||
_, err = q.ExecContext(ctx, stmt, hostUUID, profUUID, status, commandUUID, status)
|
||
default:
|
||
stmt = fmt.Sprintf(
|
||
`INSERT INTO %s (host_uuid, %s_uuid, status) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE status = ?`,
|
||
table,
|
||
profType,
|
||
)
|
||
_, err = q.ExecContext(ctx, stmt, hostUUID, profUUID, status, status)
|
||
}
|
||
if err != nil {
|
||
require.NoError(t, err)
|
||
return err
|
||
}
|
||
stmt = fmt.Sprintf(
|
||
`UPDATE %s SET operation_type = ? WHERE host_uuid = ? AND %s_uuid = ?`,
|
||
table,
|
||
profType,
|
||
)
|
||
_, err = q.ExecContext(ctx, stmt, fleet.MDMOperationTypeInstall, hostUUID, profUUID)
|
||
require.NoError(t, err)
|
||
return err
|
||
})
|
||
}
|
||
|
||
cleanupTables := func(t *testing.T) {
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
_, err := q.ExecContext(ctx, `DELETE FROM host_mdm_windows_profiles`)
|
||
return err
|
||
})
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
_, err := q.ExecContext(ctx, `DELETE FROM host_mdm_apple_profiles`)
|
||
return err
|
||
})
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
_, err := q.ExecContext(ctx, `DELETE FROM host_mdm_apple_declarations`)
|
||
return err
|
||
})
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
_, err := q.ExecContext(ctx, `DELETE FROM host_disk_encryption_keys`)
|
||
return err
|
||
})
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
_, err := q.ExecContext(ctx, `DELETE FROM host_disks`)
|
||
return err
|
||
})
|
||
}
|
||
|
||
// updateHostDisks := func(t *testing.T, hostID uint, encrypted bool, updated_at time.Time) {
|
||
// ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
// stmt := `UPDATE host_disks SET encrypted = ?, updated_at = ? where host_id = ?`
|
||
// _, err := q.ExecContext(ctx, stmt, encrypted, updated_at, hostID)
|
||
// return err
|
||
// })
|
||
// }
|
||
|
||
// Create some hosts
|
||
var hosts []*fleet.Host
|
||
macHostsByID := make(map[uint]*fleet.Host, 5)
|
||
winHostsByID := make(map[uint]*fleet.Host, 5)
|
||
for i := 0; i < 10; i++ {
|
||
p := "windows"
|
||
if i >= 5 {
|
||
p = "darwin"
|
||
}
|
||
u := uuid.New().String()
|
||
h, err := ds.NewHost(ctx, &fleet.Host{
|
||
DetailUpdatedAt: time.Now(),
|
||
LabelUpdatedAt: time.Now(),
|
||
PolicyUpdatedAt: time.Now(),
|
||
SeenTime: time.Now(),
|
||
NodeKey: &u,
|
||
UUID: u,
|
||
Hostname: u,
|
||
Platform: p,
|
||
})
|
||
require.NoError(t, err)
|
||
require.NotNil(t, h)
|
||
hosts = append(hosts, h)
|
||
if p == "darwin" {
|
||
nanoEnroll(t, ds, h, false)
|
||
macHostsByID[h.ID] = h
|
||
} else {
|
||
winHostsByID[h.ID] = h
|
||
windowsEnrollment := &fleet.MDMWindowsEnrolledDevice{
|
||
MDMDeviceID: uuid.New().String(),
|
||
MDMHardwareID: uuid.New().String() + uuid.New().String(),
|
||
MDMDeviceState: microsoft_mdm.MDMDeviceStateEnrolled,
|
||
MDMDeviceType: "CIMClient_Windows",
|
||
MDMDeviceName: "DESKTOP-1C3ARC1",
|
||
MDMEnrollType: "ProgrammaticEnrollment",
|
||
MDMEnrollUserID: "",
|
||
MDMEnrollProtoVersion: "5.0",
|
||
MDMEnrollClientVersion: "10.0.19045.2965",
|
||
MDMNotInOOBE: false,
|
||
HostUUID: h.UUID,
|
||
}
|
||
err = ds.MDMWindowsInsertEnrolledDevice(ctx, windowsEnrollment)
|
||
require.NoError(t, err)
|
||
}
|
||
|
||
require.NoError(
|
||
t,
|
||
ds.SetOrUpdateMDMData(
|
||
ctx,
|
||
h.ID,
|
||
false,
|
||
true,
|
||
"https://example.com",
|
||
false,
|
||
fleet.WellKnownMDMFleet,
|
||
"",
|
||
false,
|
||
),
|
||
)
|
||
}
|
||
|
||
checkExpected(t, nil, nil)
|
||
|
||
upsertHostProfileStatus(t, hosts[0].UUID, "w1", &fleet.MDMDeliveryPending)
|
||
|
||
checkExpected(t, nil, hostIDsByProfileStatus{
|
||
fleet.MDMDeliveryPending: []uint{hosts[0].ID},
|
||
})
|
||
|
||
// add some mac profiles with different statuses
|
||
upsertHostProfileStatus(t, hosts[9].UUID, "a1", &fleet.MDMDeliveryFailed)
|
||
upsertHostProfileStatus(t, hosts[9].UUID, "a2", &fleet.MDMDeliveryPending)
|
||
upsertHostProfileStatus(t, hosts[9].UUID, "a3", &fleet.MDMDeliveryVerifying)
|
||
upsertHostProfileStatus(t, hosts[9].UUID, "a4", &fleet.MDMDeliveryVerified)
|
||
|
||
// add some mac declarations with different statuses
|
||
upsertHostProfileStatus(t, hosts[9].UUID, "d1", &fleet.MDMDeliveryFailed)
|
||
upsertHostProfileStatus(t, hosts[9].UUID, "d2", &fleet.MDMDeliveryPending)
|
||
upsertHostProfileStatus(t, hosts[9].UUID, "d3", &fleet.MDMDeliveryVerifying)
|
||
upsertHostProfileStatus(t, hosts[9].UUID, "d4", &fleet.MDMDeliveryVerified)
|
||
|
||
checkExpected(t, nil, hostIDsByProfileStatus{
|
||
fleet.MDMDeliveryPending: []uint{hosts[0].ID},
|
||
fleet.MDMDeliveryFailed: []uint{hosts[9].ID},
|
||
})
|
||
expectedHostProfiles := map[string]string{
|
||
"a1": "failed",
|
||
"a2": "pending",
|
||
"a3": "verifying",
|
||
"a4": "verified",
|
||
"d1": "failed",
|
||
"d2": "pending",
|
||
"d3": "verifying",
|
||
"d4": "verified",
|
||
}
|
||
checkMacHostProfiles(t, hosts[9].UUID, expectedHostProfiles)
|
||
|
||
// set failed mac profile to pending, still failed because of failed declaration
|
||
upsertHostProfileStatus(t, hosts[9].UUID, "a1", &fleet.MDMDeliveryPending)
|
||
expectedHostProfiles["a1"] = "pending"
|
||
checkExpected(t, nil, hostIDsByProfileStatus{
|
||
fleet.MDMDeliveryPending: []uint{hosts[0].ID},
|
||
fleet.MDMDeliveryFailed: []uint{hosts[9].ID},
|
||
})
|
||
checkMacHostProfiles(t, hosts[9].UUID, expectedHostProfiles)
|
||
|
||
// set failed mac declaration to pending, now host stsatus is pending
|
||
upsertHostProfileStatus(t, hosts[9].UUID, "d1", &fleet.MDMDeliveryPending)
|
||
expectedHostProfiles["d1"] = "pending"
|
||
checkExpected(t, nil, hostIDsByProfileStatus{
|
||
fleet.MDMDeliveryPending: []uint{hosts[0].ID, hosts[9].ID},
|
||
})
|
||
checkMacHostProfiles(t, hosts[9].UUID, expectedHostProfiles)
|
||
|
||
// set pending mac declaration to failed, host status is now failed
|
||
upsertHostProfileStatus(t, hosts[9].UUID, "d2", &fleet.MDMDeliveryFailed)
|
||
expectedHostProfiles["d2"] = "failed"
|
||
checkExpected(t, nil, hostIDsByProfileStatus{
|
||
fleet.MDMDeliveryPending: []uint{hosts[0].ID},
|
||
fleet.MDMDeliveryFailed: []uint{hosts[9].ID},
|
||
})
|
||
checkMacHostProfiles(t, hosts[9].UUID, expectedHostProfiles)
|
||
|
||
// set failed mac declaration to verifying, host status is now pending
|
||
upsertHostProfileStatus(t, hosts[9].UUID, "d2", &fleet.MDMDeliveryVerifying)
|
||
expectedHostProfiles["d2"] = "verifying"
|
||
checkExpected(t, nil, hostIDsByProfileStatus{
|
||
fleet.MDMDeliveryPending: []uint{hosts[0].ID, hosts[9].ID},
|
||
})
|
||
checkMacHostProfiles(t, hosts[9].UUID, expectedHostProfiles)
|
||
|
||
// set pending mac profiles to verifying, host status is still pending because d1 is still pending
|
||
upsertHostProfileStatus(t, hosts[9].UUID, "a1", &fleet.MDMDeliveryVerifying)
|
||
expectedHostProfiles["a1"] = "verifying"
|
||
upsertHostProfileStatus(t, hosts[9].UUID, "a2", &fleet.MDMDeliveryVerifying)
|
||
expectedHostProfiles["a2"] = "verifying"
|
||
checkExpected(t, nil, hostIDsByProfileStatus{
|
||
fleet.MDMDeliveryPending: []uint{hosts[0].ID, hosts[9].ID},
|
||
})
|
||
checkMacHostProfiles(t, hosts[9].UUID, expectedHostProfiles)
|
||
|
||
// set pending mac declarations to verifying, host status is now verifying
|
||
upsertHostProfileStatus(t, hosts[9].UUID, "d1", &fleet.MDMDeliveryVerifying)
|
||
expectedHostProfiles["d1"] = "verifying"
|
||
checkExpected(t, nil, hostIDsByProfileStatus{
|
||
fleet.MDMDeliveryPending: []uint{hosts[0].ID},
|
||
fleet.MDMDeliveryVerifying: []uint{hosts[9].ID},
|
||
})
|
||
checkMacHostProfiles(t, hosts[9].UUID, expectedHostProfiles)
|
||
|
||
// set a mac profile to failed, host status is now failed
|
||
upsertHostProfileStatus(t, hosts[9].UUID, "a1", &fleet.MDMDeliveryFailed)
|
||
expectedHostProfiles["a1"] = "failed"
|
||
checkExpected(t, nil, hostIDsByProfileStatus{
|
||
fleet.MDMDeliveryPending: []uint{hosts[0].ID},
|
||
fleet.MDMDeliveryFailed: []uint{hosts[9].ID},
|
||
})
|
||
checkMacHostProfiles(t, hosts[9].UUID, expectedHostProfiles)
|
||
|
||
// set mac profiles to verified, host status is now verifying because declarations are still
|
||
// verifying
|
||
upsertHostProfileStatus(t, hosts[9].UUID, "a1", &fleet.MDMDeliveryVerified)
|
||
expectedHostProfiles["a1"] = "verified"
|
||
upsertHostProfileStatus(t, hosts[9].UUID, "a2", &fleet.MDMDeliveryVerified)
|
||
expectedHostProfiles["a2"] = "verified"
|
||
upsertHostProfileStatus(t, hosts[9].UUID, "a3", &fleet.MDMDeliveryVerified)
|
||
expectedHostProfiles["a3"] = "verified"
|
||
upsertHostProfileStatus(t, hosts[9].UUID, "a4", &fleet.MDMDeliveryVerified)
|
||
expectedHostProfiles["a4"] = "verified"
|
||
checkExpected(t, nil, hostIDsByProfileStatus{
|
||
fleet.MDMDeliveryPending: []uint{hosts[0].ID},
|
||
fleet.MDMDeliveryVerifying: []uint{hosts[9].ID},
|
||
})
|
||
checkMacHostProfiles(t, hosts[9].UUID, expectedHostProfiles)
|
||
|
||
// set mac declarations to verified, host status is now verified
|
||
upsertHostProfileStatus(t, hosts[9].UUID, "d1", &fleet.MDMDeliveryVerified)
|
||
expectedHostProfiles["d1"] = "verified"
|
||
upsertHostProfileStatus(t, hosts[9].UUID, "d2", &fleet.MDMDeliveryVerified)
|
||
expectedHostProfiles["d2"] = "verified"
|
||
upsertHostProfileStatus(t, hosts[9].UUID, "d3", &fleet.MDMDeliveryVerified)
|
||
expectedHostProfiles["d3"] = "verified"
|
||
checkExpected(t, nil, hostIDsByProfileStatus{
|
||
fleet.MDMDeliveryPending: []uint{hosts[0].ID},
|
||
fleet.MDMDeliveryVerified: []uint{hosts[9].ID},
|
||
})
|
||
checkMacHostProfiles(t, hosts[9].UUID, expectedHostProfiles)
|
||
|
||
// set a mac declaration to nil, host status is now pending
|
||
upsertHostProfileStatus(t, hosts[9].UUID, "d1", nil)
|
||
expectedHostProfiles["d1"] = "pending"
|
||
checkExpected(t, nil, hostIDsByProfileStatus{
|
||
fleet.MDMDeliveryPending: []uint{hosts[0].ID, hosts[9].ID},
|
||
})
|
||
checkMacHostProfiles(t, hosts[9].UUID, expectedHostProfiles)
|
||
|
||
// works as expected if we remove mac declarations
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
_, err := q.ExecContext(ctx, `DELETE FROM host_mdm_apple_declarations`)
|
||
return err
|
||
})
|
||
delete(expectedHostProfiles, "d1")
|
||
delete(expectedHostProfiles, "d2")
|
||
delete(expectedHostProfiles, "d3")
|
||
delete(expectedHostProfiles, "d4")
|
||
checkExpected(t, nil, hostIDsByProfileStatus{
|
||
fleet.MDMDeliveryPending: []uint{hosts[0].ID},
|
||
fleet.MDMDeliveryVerified: []uint{hosts[9].ID}, // all profiles were verified
|
||
})
|
||
checkMacHostProfiles(t, hosts[9].UUID, expectedHostProfiles)
|
||
|
||
// works as expected if we remove mac profiles
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
_, err := q.ExecContext(ctx, `DELETE FROM host_mdm_apple_profiles`)
|
||
return err
|
||
})
|
||
delete(expectedHostProfiles, "a1")
|
||
delete(expectedHostProfiles, "a2")
|
||
delete(expectedHostProfiles, "a3")
|
||
delete(expectedHostProfiles, "a4")
|
||
checkExpected(t, nil, hostIDsByProfileStatus{
|
||
fleet.MDMDeliveryPending: []uint{hosts[0].ID},
|
||
})
|
||
checkMacHostProfiles(t, hosts[9].UUID, expectedHostProfiles)
|
||
|
||
// works as expected if declarations but no profiles
|
||
upsertHostProfileStatus(t, hosts[9].UUID, "d1", &fleet.MDMDeliveryPending)
|
||
expectedHostProfiles["d1"] = "pending"
|
||
checkExpected(t, nil, hostIDsByProfileStatus{
|
||
fleet.MDMDeliveryPending: []uint{hosts[0].ID, hosts[9].ID},
|
||
})
|
||
checkMacHostProfiles(t, hosts[9].UUID, expectedHostProfiles)
|
||
|
||
cleanupTables(t)
|
||
}
|
||
|
||
func testAreHostsConnectedToFleetMDM(t *testing.T, ds *Datastore) {
|
||
ctx := context.Background()
|
||
|
||
notConnectedMac, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: "macos-test",
|
||
OsqueryHostID: ptr.String("osquery-macos-not-connected"),
|
||
NodeKey: ptr.String("node-key-macos-not-connected"),
|
||
UUID: uuid.NewString(),
|
||
Platform: "darwin",
|
||
})
|
||
require.NoError(t, err)
|
||
|
||
connectedMac, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: "macos-test",
|
||
OsqueryHostID: ptr.String("osquery-macos"),
|
||
NodeKey: ptr.String("node-key-macos"),
|
||
UUID: uuid.NewString(),
|
||
Platform: "darwin",
|
||
})
|
||
require.NoError(t, err)
|
||
nanoEnroll(t, ds, connectedMac, false)
|
||
err = ds.SetOrUpdateMDMData(ctx, connectedMac.ID, false, true, "http://foo.com", false, "foo", "", false)
|
||
require.NoError(t, err)
|
||
|
||
disconnectedWithoutCheckoutMac, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: "macos-test-disconnected",
|
||
OsqueryHostID: ptr.String("osquery-macos-disconnected"),
|
||
NodeKey: ptr.String("node-key-macos-disconnected"),
|
||
UUID: uuid.NewString(),
|
||
Platform: "darwin",
|
||
})
|
||
require.NoError(t, err)
|
||
nanoEnroll(t, ds, disconnectedWithoutCheckoutMac, false)
|
||
err = ds.SetOrUpdateMDMData(ctx, disconnectedWithoutCheckoutMac.ID, false, false, "", false, "", "", false)
|
||
require.NoError(t, err)
|
||
|
||
notConnectedWin, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: "windows-test",
|
||
OsqueryHostID: ptr.String("osquery-windows-not-connected"),
|
||
NodeKey: ptr.String("node-key-windows-not-connected"),
|
||
UUID: uuid.NewString(),
|
||
Platform: "windows",
|
||
})
|
||
require.NoError(t, err)
|
||
|
||
connectedWin, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: "windows-test",
|
||
OsqueryHostID: ptr.String("osquery-windows"),
|
||
NodeKey: ptr.String("node-key-windows"),
|
||
UUID: uuid.NewString(),
|
||
Platform: "windows",
|
||
})
|
||
require.NoError(t, err)
|
||
|
||
windowsEnrollment := &fleet.MDMWindowsEnrolledDevice{
|
||
MDMDeviceID: uuid.New().String(),
|
||
MDMHardwareID: uuid.New().String() + uuid.New().String(),
|
||
MDMDeviceState: microsoft_mdm.MDMDeviceStateEnrolled,
|
||
MDMDeviceType: "CIMClient_Windows",
|
||
MDMDeviceName: "DESKTOP-1C3ARC1",
|
||
MDMEnrollType: "ProgrammaticEnrollment",
|
||
MDMEnrollUserID: "",
|
||
MDMEnrollProtoVersion: "5.0",
|
||
MDMEnrollClientVersion: "10.0.19045.2965",
|
||
MDMNotInOOBE: false,
|
||
HostUUID: connectedWin.UUID,
|
||
}
|
||
err = ds.MDMWindowsInsertEnrolledDevice(ctx, windowsEnrollment)
|
||
require.NoError(t, err)
|
||
err = ds.SetOrUpdateMDMData(ctx, connectedWin.ID, false, true, "http://foo.com", false, "foo", "", false)
|
||
require.NoError(t, err)
|
||
|
||
disconnectedWithoutCheckoutWin, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: "windows-test-disconnected",
|
||
OsqueryHostID: ptr.String("osquery-windows-disconnected"),
|
||
NodeKey: ptr.String("node-key-windows-disconnected"),
|
||
UUID: uuid.NewString(),
|
||
Platform: "windows",
|
||
})
|
||
require.NoError(t, err)
|
||
windowsEnrollmentDisconnectedWithoutCheckout := &fleet.MDMWindowsEnrolledDevice{
|
||
MDMDeviceID: uuid.New().String(),
|
||
MDMHardwareID: uuid.New().String() + uuid.New().String(),
|
||
MDMDeviceState: microsoft_mdm.MDMDeviceStateEnrolled,
|
||
MDMDeviceType: "CIMClient_Windows",
|
||
MDMDeviceName: "DESKTOP-1C3ARC1-disconnected",
|
||
MDMEnrollType: "ProgrammaticEnrollment",
|
||
MDMEnrollUserID: "",
|
||
MDMEnrollProtoVersion: "5.0",
|
||
MDMEnrollClientVersion: "10.0.19045.2965",
|
||
MDMNotInOOBE: false,
|
||
HostUUID: disconnectedWithoutCheckoutWin.UUID,
|
||
}
|
||
err = ds.MDMWindowsInsertEnrolledDevice(ctx, windowsEnrollmentDisconnectedWithoutCheckout)
|
||
require.NoError(t, err)
|
||
err = ds.SetOrUpdateMDMData(ctx, disconnectedWithoutCheckoutWin.ID, false, false, "", false, "", "", false)
|
||
require.NoError(t, err)
|
||
|
||
connectedMap, err := ds.AreHostsConnectedToFleetMDM(ctx, []*fleet.Host{
|
||
notConnectedMac,
|
||
connectedMac,
|
||
connectedWin,
|
||
notConnectedWin,
|
||
disconnectedWithoutCheckoutMac,
|
||
disconnectedWithoutCheckoutWin,
|
||
})
|
||
require.NoError(t, err)
|
||
require.Equal(t, map[string]bool{
|
||
notConnectedMac.UUID: false,
|
||
connectedMac.UUID: true,
|
||
connectedWin.UUID: true,
|
||
notConnectedWin.UUID: false,
|
||
disconnectedWithoutCheckoutMac.UUID: false,
|
||
disconnectedWithoutCheckoutWin.UUID: false,
|
||
}, connectedMap)
|
||
|
||
linuxHost, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: "linux-test",
|
||
OsqueryHostID: ptr.String("osquery-linux"),
|
||
NodeKey: ptr.String("node-key-linux"),
|
||
UUID: uuid.NewString(),
|
||
Platform: "linux",
|
||
})
|
||
require.NoError(t, err)
|
||
connectedMap, err = ds.AreHostsConnectedToFleetMDM(ctx, []*fleet.Host{
|
||
notConnectedMac,
|
||
connectedMac,
|
||
connectedWin,
|
||
notConnectedWin,
|
||
linuxHost,
|
||
disconnectedWithoutCheckoutMac,
|
||
disconnectedWithoutCheckoutWin,
|
||
})
|
||
require.NoError(t, err)
|
||
require.Equal(t, map[string]bool{
|
||
notConnectedMac.UUID: false,
|
||
connectedMac.UUID: true,
|
||
connectedWin.UUID: true,
|
||
notConnectedWin.UUID: false,
|
||
linuxHost.UUID: false,
|
||
disconnectedWithoutCheckoutMac.UUID: false,
|
||
disconnectedWithoutCheckoutWin.UUID: false,
|
||
}, connectedMap)
|
||
}
|
||
|
||
func testIsHostConnectedToFleetMDM(t *testing.T, ds *Datastore) {
|
||
ctx := context.Background()
|
||
macH, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: "macos-test",
|
||
OsqueryHostID: ptr.String("osquery-macos"),
|
||
NodeKey: ptr.String("node-key-macos"),
|
||
UUID: uuid.NewString(),
|
||
Platform: "darwin",
|
||
})
|
||
require.NoError(t, err)
|
||
|
||
connected, err := ds.IsHostConnectedToFleetMDM(ctx, macH)
|
||
require.NoError(t, err)
|
||
require.False(t, connected)
|
||
|
||
nanoEnroll(t, ds, macH, false)
|
||
err = ds.SetOrUpdateMDMData(ctx, macH.ID, false, true, "http://foo.com", false, "foo", "", false)
|
||
require.NoError(t, err)
|
||
|
||
connected, err = ds.IsHostConnectedToFleetMDM(ctx, macH)
|
||
require.NoError(t, err)
|
||
require.True(t, connected)
|
||
|
||
byodIpadH, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: "ipados-test",
|
||
OsqueryHostID: ptr.String("osquery-ipados"),
|
||
NodeKey: ptr.String("node-key-ipados"),
|
||
UUID: uuid.NewString(),
|
||
Platform: "ipados",
|
||
})
|
||
require.NoError(t, err)
|
||
|
||
nanoEnrollUserDevice(t, ds, byodIpadH)
|
||
err = ds.SetOrUpdateMDMData(ctx, byodIpadH.ID, false, true, "http://foo.com", false, "foo", "", false)
|
||
require.NoError(t, err)
|
||
|
||
connected, err = ds.IsHostConnectedToFleetMDM(ctx, byodIpadH)
|
||
require.NoError(t, err)
|
||
require.True(t, connected)
|
||
|
||
windowsH, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: "windows-test",
|
||
OsqueryHostID: ptr.String("osquery-windows"),
|
||
NodeKey: ptr.String("node-key-windows"),
|
||
UUID: uuid.NewString(),
|
||
Platform: "windows",
|
||
})
|
||
require.NoError(t, err)
|
||
connected, err = ds.IsHostConnectedToFleetMDM(ctx, windowsH)
|
||
require.NoError(t, err)
|
||
require.False(t, connected)
|
||
|
||
windowsEnrollment := &fleet.MDMWindowsEnrolledDevice{
|
||
MDMDeviceID: uuid.New().String(),
|
||
MDMHardwareID: uuid.New().String() + uuid.New().String(),
|
||
MDMDeviceState: microsoft_mdm.MDMDeviceStateEnrolled,
|
||
MDMDeviceType: "CIMClient_Windows",
|
||
MDMDeviceName: "DESKTOP-1C3ARC1",
|
||
MDMEnrollType: "ProgrammaticEnrollment",
|
||
MDMEnrollUserID: "",
|
||
MDMEnrollProtoVersion: "5.0",
|
||
MDMEnrollClientVersion: "10.0.19045.2965",
|
||
MDMNotInOOBE: false,
|
||
HostUUID: windowsH.UUID,
|
||
}
|
||
err = ds.MDMWindowsInsertEnrolledDevice(ctx, windowsEnrollment)
|
||
require.NoError(t, err)
|
||
err = ds.SetOrUpdateMDMData(ctx, windowsH.ID, false, true, "http://foo.com", false, "foo", "", false)
|
||
require.NoError(t, err)
|
||
|
||
connected, err = ds.IsHostConnectedToFleetMDM(ctx, windowsH)
|
||
require.NoError(t, err)
|
||
require.True(t, connected)
|
||
|
||
// now simulate an un-enrollment without checkout, in this case, osquery reports the host as not-enrolled
|
||
err = ds.SetOrUpdateMDMData(ctx, macH.ID, false, false, "", false, "", "", false)
|
||
require.NoError(t, err)
|
||
err = ds.SetOrUpdateMDMData(ctx, windowsH.ID, false, false, "", false, "", "", false)
|
||
require.NoError(t, err)
|
||
|
||
connected, err = ds.IsHostConnectedToFleetMDM(ctx, macH)
|
||
require.NoError(t, err)
|
||
require.False(t, connected)
|
||
|
||
connected, err = ds.IsHostConnectedToFleetMDM(ctx, windowsH)
|
||
require.NoError(t, err)
|
||
require.False(t, connected)
|
||
|
||
// Simulate the ipad checking out(user removing work account)
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
_, err := q.ExecContext(ctx, `UPDATE nano_enrollments SET enabled = 0 WHERE id = ?`, byodIpadH.UUID)
|
||
return err
|
||
})
|
||
|
||
connected, err = ds.IsHostConnectedToFleetMDM(ctx, byodIpadH)
|
||
require.NoError(t, err)
|
||
require.False(t, connected)
|
||
}
|
||
|
||
func testBulkSetPendingMDMHostProfilesExcludeAny(t *testing.T, ds *Datastore) {
|
||
test.AddBuiltinLabels(t, ds)
|
||
ctx := context.Background()
|
||
|
||
// create some "exclude" labels
|
||
var labels []*fleet.Label
|
||
for i := 0; i < 8; i++ {
|
||
lbl, err := ds.NewLabel(ctx, &fleet.Label{Name: "exclude-label-" + strconv.Itoa(i), Query: "select 1"})
|
||
require.NoError(t, err)
|
||
labels = append(labels, lbl)
|
||
}
|
||
|
||
// create an Apple profile, a Windows profile and an Apple Declaration with excluded labels
|
||
appleProfs := []*fleet.MDMAppleConfigProfile{
|
||
configProfileForTest(t, "A1", "A1", uuid.NewString(), labels[0], labels[1]),
|
||
}
|
||
windowsProfs := []*fleet.MDMWindowsConfigProfile{
|
||
windowsConfigProfileForTest(t, "W1", "W1", labels[2]),
|
||
}
|
||
appleDecls := []*fleet.MDMAppleDeclaration{
|
||
declForTest("D1", "D1", "{}", labels[3], labels[4], labels[5]),
|
||
}
|
||
androidProfs := []*fleet.MDMAndroidConfigProfile{
|
||
androidConfigProfileForTest(t, "G1", nil, labels[6]),
|
||
}
|
||
|
||
updates, err := ds.BatchSetMDMProfiles(ctx, nil, appleProfs, windowsProfs, appleDecls, androidProfs, nil)
|
||
require.NoError(t, err)
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.True(t, updates.WindowsConfigProfile)
|
||
assert.True(t, updates.AppleDeclaration)
|
||
assert.True(t, updates.AndroidConfigProfile)
|
||
|
||
// must reload them to get the profile/declaration uuid
|
||
getProfs := func(teamID *uint) []*fleet.MDMConfigProfilePayload {
|
||
// TODO(roberto): the docs says that you can pass a comma separated
|
||
// list of columns to OrderKey, but that doesn't seem to work
|
||
profs, _, err := ds.ListMDMConfigProfiles(ctx, teamID, fleet.ListOptions{})
|
||
require.NoError(t, err)
|
||
sort.Slice(profs, func(i, j int) bool {
|
||
l, r := profs[i], profs[j]
|
||
if l.Platform != r.Platform {
|
||
return l.Platform < r.Platform
|
||
}
|
||
|
||
return l.Name < r.Name
|
||
})
|
||
return profs
|
||
}
|
||
allProfs := getProfs(nil)
|
||
|
||
// create an Apple and Windows hosts, not members of any label
|
||
var i int
|
||
winHost, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: fmt.Sprintf("win-host%d-name", i),
|
||
OsqueryHostID: ptr.String(fmt.Sprintf("osquery-%d", i)),
|
||
NodeKey: ptr.String(fmt.Sprintf("nodekey-%d", i)),
|
||
UUID: fmt.Sprintf("win-uuid-%d", i),
|
||
Platform: "windows",
|
||
})
|
||
require.NoError(t, err)
|
||
windowsEnroll(t, ds, winHost)
|
||
|
||
i++
|
||
appleHost, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: fmt.Sprintf("apple-host%d-name", i),
|
||
OsqueryHostID: ptr.String(fmt.Sprintf("osquery-%d", i)),
|
||
NodeKey: ptr.String(fmt.Sprintf("nodekey-%d", i)),
|
||
UUID: fmt.Sprintf("apple-uuid-%d", i),
|
||
Platform: "darwin",
|
||
})
|
||
require.NoError(t, err)
|
||
nanoEnroll(t, ds, appleHost, false)
|
||
|
||
i++
|
||
androidHostObj := createAndroidHost(fmt.Sprintf("android-host%d-name", i))
|
||
androidHostObj, err = ds.NewAndroidHost(ctx, androidHostObj, false)
|
||
androidHost := androidHostObj.Host
|
||
require.NoError(t, err)
|
||
|
||
// Set LabelUpdatedAt to a time before labels were created to simulate hosts that haven't reported label membership
|
||
// Use a time in the past (2020) to ensure it's before any label created in this test
|
||
oldTime := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
|
||
winHost.LabelUpdatedAt = oldTime
|
||
appleHost.LabelUpdatedAt = oldTime
|
||
androidHost.LabelUpdatedAt = oldTime
|
||
err = ds.UpdateHost(ctx, winHost)
|
||
require.NoError(t, err)
|
||
err = ds.UpdateHost(ctx, appleHost)
|
||
require.NoError(t, err)
|
||
err = ds.UpdateHost(ctx, androidHost)
|
||
require.NoError(t, err)
|
||
|
||
// at this point the hosts have not reported any label results, so a sync
|
||
// does NOT install the exclude any profiles as we don't know yet if the
|
||
// hosts will be members or not
|
||
updates, err = ds.BulkSetPendingMDMHostProfiles(ctx, []uint{winHost.ID, appleHost.ID, androidHost.ID}, nil, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.False(t, updates.AppleConfigProfile)
|
||
assert.False(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
assert.False(t, updates.AndroidConfigProfile)
|
||
assertHostProfiles(t, ds, map[*fleet.Host][]anyProfile{
|
||
appleHost: {},
|
||
winHost: {},
|
||
androidHost: {},
|
||
})
|
||
|
||
// setting the LabelsUpdatedAt timestamp means that labels results were reported, so now
|
||
// the profiles will be installed as the hosts are not members of the excluded labels.
|
||
winHost.LabelUpdatedAt = time.Now()
|
||
appleHost.LabelUpdatedAt = time.Now()
|
||
androidHost.LabelUpdatedAt = time.Now()
|
||
err = ds.UpdateHost(ctx, winHost)
|
||
require.NoError(t, err)
|
||
err = ds.UpdateHost(ctx, appleHost)
|
||
require.NoError(t, err)
|
||
err = ds.UpdateHost(ctx, androidHost)
|
||
require.NoError(t, err)
|
||
|
||
updates, err = ds.BulkSetPendingMDMHostProfiles(ctx, []uint{winHost.ID, appleHost.ID, androidHost.ID}, nil, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.True(t, updates.WindowsConfigProfile)
|
||
assert.True(t, updates.AppleDeclaration)
|
||
assert.True(t, updates.AndroidConfigProfile)
|
||
|
||
assertHostProfiles(t, ds, map[*fleet.Host][]anyProfile{
|
||
androidHost: {
|
||
{
|
||
ProfileUUID: allProfs[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: allProfs[0].Name,
|
||
},
|
||
},
|
||
appleHost: {
|
||
{
|
||
ProfileUUID: allProfs[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: allProfs[1].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: allProfs[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: allProfs[2].Identifier,
|
||
},
|
||
},
|
||
winHost: {
|
||
{
|
||
ProfileUUID: allProfs[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: allProfs[3].Name,
|
||
},
|
||
},
|
||
})
|
||
|
||
// make all hosts members of labels[1], [2], [3], [6] so that all profiles are
|
||
// excluded
|
||
err = ds.AsyncBatchInsertLabelMembership(ctx, [][2]uint{
|
||
{labels[1].ID, appleHost.ID},
|
||
{labels[2].ID, appleHost.ID},
|
||
{labels[3].ID, appleHost.ID},
|
||
{labels[6].ID, appleHost.ID},
|
||
{labels[1].ID, winHost.ID},
|
||
{labels[2].ID, winHost.ID},
|
||
{labels[3].ID, winHost.ID},
|
||
{labels[6].ID, winHost.ID},
|
||
{labels[1].ID, androidHost.ID},
|
||
{labels[2].ID, androidHost.ID},
|
||
{labels[3].ID, androidHost.ID},
|
||
{labels[6].ID, androidHost.ID},
|
||
})
|
||
require.NoError(t, err)
|
||
|
||
updates, err = ds.BulkSetPendingMDMHostProfiles(ctx, []uint{winHost.ID, appleHost.ID, androidHost.ID}, nil, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.True(t, updates.WindowsConfigProfile)
|
||
assert.True(t, updates.AppleDeclaration)
|
||
assert.True(t, updates.AndroidConfigProfile)
|
||
|
||
assertHostProfiles(t, ds, map[*fleet.Host][]anyProfile{
|
||
appleHost: {
|
||
{
|
||
ProfileUUID: allProfs[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: allProfs[1].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: allProfs[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: allProfs[2].Identifier,
|
||
},
|
||
},
|
||
winHost: {
|
||
{
|
||
ProfileUUID: allProfs[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: allProfs[3].Name,
|
||
},
|
||
},
|
||
androidHost: {
|
||
{
|
||
ProfileUUID: allProfs[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: allProfs[0].Name,
|
||
},
|
||
},
|
||
})
|
||
|
||
// make apple host member of labels[2], and windows and android host member of [3], which are irrelevant
|
||
// for their platforms' profiles, so they get all profiles
|
||
err = ds.AsyncBatchDeleteLabelMembership(ctx, [][2]uint{
|
||
{labels[1].ID, appleHost.ID},
|
||
{labels[3].ID, appleHost.ID},
|
||
{labels[6].ID, appleHost.ID},
|
||
{labels[1].ID, winHost.ID},
|
||
{labels[2].ID, winHost.ID},
|
||
{labels[6].ID, winHost.ID},
|
||
{labels[1].ID, androidHost.ID},
|
||
{labels[2].ID, androidHost.ID},
|
||
{labels[6].ID, androidHost.ID},
|
||
})
|
||
require.NoError(t, err)
|
||
|
||
updates, err = ds.BulkSetPendingMDMHostProfiles(ctx, []uint{winHost.ID, appleHost.ID, androidHost.ID}, nil, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.True(t, updates.AppleConfigProfile)
|
||
assert.True(t, updates.WindowsConfigProfile)
|
||
assert.True(t, updates.AppleDeclaration)
|
||
assert.True(t, updates.AndroidConfigProfile)
|
||
|
||
assertHostProfiles(t, ds, map[*fleet.Host][]anyProfile{
|
||
androidHost: {
|
||
{
|
||
ProfileUUID: allProfs[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: allProfs[0].Name,
|
||
},
|
||
},
|
||
appleHost: {
|
||
{
|
||
ProfileUUID: allProfs[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: allProfs[1].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: allProfs[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: allProfs[2].Identifier,
|
||
},
|
||
},
|
||
winHost: {
|
||
{
|
||
ProfileUUID: allProfs[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: allProfs[3].Name,
|
||
},
|
||
},
|
||
})
|
||
|
||
// delete labels 0, 2, 3, and 6, breaking all profiles
|
||
err = ds.DeleteLabel(ctx, labels[0].Name, fleet.TeamFilter{User: &fleet.User{GlobalRole: ptr.String(fleet.RoleAdmin)}})
|
||
require.NoError(t, err)
|
||
err = ds.DeleteLabel(ctx, labels[2].Name, fleet.TeamFilter{User: &fleet.User{GlobalRole: ptr.String(fleet.RoleAdmin)}})
|
||
require.NoError(t, err)
|
||
err = ds.DeleteLabel(ctx, labels[3].Name, fleet.TeamFilter{User: &fleet.User{GlobalRole: ptr.String(fleet.RoleAdmin)}})
|
||
require.NoError(t, err)
|
||
err = ds.DeleteLabel(ctx, labels[6].Name, fleet.TeamFilter{User: &fleet.User{GlobalRole: ptr.String(fleet.RoleAdmin)}})
|
||
require.NoError(t, err)
|
||
|
||
updates, err = ds.BulkSetPendingMDMHostProfiles(ctx, []uint{winHost.ID, appleHost.ID, androidHost.ID}, nil, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.False(t, updates.AppleConfigProfile)
|
||
assert.False(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
assert.True(t, updates.AndroidConfigProfile) // "Broken profiles" behave differently for android.
|
||
|
||
// broken profiles do not get reported as "to remove", except for android
|
||
assertHostProfiles(t, ds, map[*fleet.Host][]anyProfile{
|
||
androidHost: {
|
||
{
|
||
ProfileUUID: allProfs[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: allProfs[0].Name,
|
||
},
|
||
},
|
||
appleHost: {
|
||
{
|
||
ProfileUUID: allProfs[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: allProfs[1].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: allProfs[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: allProfs[2].Identifier,
|
||
},
|
||
},
|
||
winHost: {
|
||
{
|
||
ProfileUUID: allProfs[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: allProfs[3].Name,
|
||
},
|
||
},
|
||
})
|
||
|
||
// create a new windows, apple, and android host, not a member of any label
|
||
i++
|
||
winHost2, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: fmt.Sprintf("win-host%d-name", i),
|
||
OsqueryHostID: ptr.String(fmt.Sprintf("osquery-%d", i)),
|
||
NodeKey: ptr.String(fmt.Sprintf("nodekey-%d", i)),
|
||
UUID: fmt.Sprintf("win-uuid-%d", i),
|
||
Platform: "windows",
|
||
})
|
||
require.NoError(t, err)
|
||
windowsEnroll(t, ds, winHost2)
|
||
|
||
i++
|
||
appleHost2, err := ds.NewHost(ctx, &fleet.Host{
|
||
Hostname: fmt.Sprintf("apple-host%d-name", i),
|
||
OsqueryHostID: ptr.String(fmt.Sprintf("osquery-%d", i)),
|
||
NodeKey: ptr.String(fmt.Sprintf("nodekey-%d", i)),
|
||
UUID: fmt.Sprintf("apple-uuid-%d", i),
|
||
Platform: "darwin",
|
||
})
|
||
require.NoError(t, err)
|
||
nanoEnroll(t, ds, appleHost2, false)
|
||
|
||
i++
|
||
androidHostObj2 := createAndroidHost(fmt.Sprintf("android-host%d-name", i))
|
||
androidHostObj2, err = ds.NewAndroidHost(ctx, androidHostObj2, false)
|
||
require.NoError(t, err)
|
||
androidHost2 := androidHostObj2.Host
|
||
|
||
winHost2.LabelUpdatedAt = time.Now()
|
||
appleHost2.LabelUpdatedAt = time.Now()
|
||
androidHost2.LabelUpdatedAt = time.Now()
|
||
err = ds.UpdateHost(ctx, winHost2)
|
||
require.NoError(t, err)
|
||
err = ds.UpdateHost(ctx, appleHost2)
|
||
require.NoError(t, err)
|
||
err = ds.UpdateHost(ctx, androidHost2)
|
||
require.NoError(t, err)
|
||
|
||
updates, err = ds.BulkSetPendingMDMHostProfiles(ctx, []uint{winHost.ID, appleHost.ID, androidHost.ID, winHost2.ID, appleHost2.ID, androidHost2.ID}, nil, nil, nil)
|
||
require.NoError(t, err)
|
||
assert.False(t, updates.AppleConfigProfile)
|
||
assert.False(t, updates.WindowsConfigProfile)
|
||
assert.False(t, updates.AppleDeclaration)
|
||
assert.True(t, updates.AndroidConfigProfile) // True from first host
|
||
|
||
// broken profiles do not get reported as "to install"
|
||
assertHostProfiles(t, ds, map[*fleet.Host][]anyProfile{
|
||
androidHost: {
|
||
{
|
||
ProfileUUID: allProfs[0].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeRemove,
|
||
IdentifierOrName: allProfs[0].Name,
|
||
},
|
||
},
|
||
appleHost: {
|
||
{
|
||
ProfileUUID: allProfs[1].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: allProfs[1].Identifier,
|
||
},
|
||
{
|
||
ProfileUUID: allProfs[2].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: allProfs[2].Identifier,
|
||
},
|
||
},
|
||
winHost: {
|
||
{
|
||
ProfileUUID: allProfs[3].ProfileUUID,
|
||
Status: &fleet.MDMDeliveryPending,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
IdentifierOrName: allProfs[3].Name,
|
||
},
|
||
},
|
||
appleHost2: {},
|
||
winHost2: {},
|
||
androidHost2: {},
|
||
})
|
||
}
|
||
|
||
func testBulkSetPendingMDMWindowsHostProfilesLotsOfHosts(t *testing.T, ds *Datastore) {
|
||
ctx := context.Background()
|
||
|
||
var hostUUIDs []string
|
||
// The bug this test was built to reproduce is visible down to ~16400 hosts; keeping this at 66k for scale testing
|
||
for range 66000 {
|
||
hostUUIDs = append(hostUUIDs, uuid.NewString())
|
||
}
|
||
|
||
_, err := ds.bulkSetPendingMDMWindowsHostProfilesDB(ctx, ds.writer(ctx), hostUUIDs, nil)
|
||
require.NoError(t, err)
|
||
}
|
||
|
||
func testBatchResendProfileToHosts(t *testing.T, ds *Datastore) {
|
||
ctx := t.Context()
|
||
|
||
// create some hosts and some profiles
|
||
host1 := test.NewHost(t, ds, "host1", "1", "h1key", "host1uuid", time.Now())
|
||
host2 := test.NewHost(t, ds, "host2", "2", "h2key", "host2uuid", time.Now())
|
||
host3 := test.NewHost(t, ds, "host3", "3", "h3key", "host3uuid", time.Now())
|
||
host4 := test.NewHost(t, ds, "host4", "4", "h4key", "host4uuid", time.Now())
|
||
|
||
// create a team and make host4 part of that team
|
||
team, err := ds.NewTeam(ctx, &fleet.Team{Name: "team1"})
|
||
require.NoError(t, err)
|
||
err = ds.AddHostsToTeam(ctx, fleet.NewAddHostsToTeamParams(&team.ID, []uint{host4.ID}))
|
||
require.NoError(t, err)
|
||
|
||
// create some profiles , a and b for no team, c for team
|
||
profA, err := ds.NewMDMAppleConfigProfile(ctx, *generateAppleCP("a", "a", 0), nil)
|
||
require.NoError(t, err)
|
||
profB, err := ds.NewMDMAppleConfigProfile(ctx, *generateAppleCP("b", "b", 0), nil)
|
||
require.NoError(t, err)
|
||
profC, err := ds.NewMDMAppleConfigProfile(ctx, *generateAppleCP("c", "c", team.ID), nil)
|
||
require.NoError(t, err)
|
||
|
||
t.Logf("profA=%s, profB=%s, profC=%s", profA.ProfileUUID, profB.ProfileUUID, profC.ProfileUUID)
|
||
|
||
assertHostProfileStatus(t, ds, host1.UUID)
|
||
assertHostProfileStatus(t, ds, host2.UUID)
|
||
assertHostProfileStatus(t, ds, host3.UUID)
|
||
assertHostProfileStatus(t, ds, host4.UUID)
|
||
|
||
// make profile A installed for all no team
|
||
forceSetAppleHostProfileStatus(t, ds, host1.UUID, profA, fleet.MDMOperationTypeInstall, fleet.MDMDeliveryVerifying)
|
||
forceSetAppleHostProfileStatus(t, ds, host2.UUID, profA, fleet.MDMOperationTypeInstall, fleet.MDMDeliveryVerifying)
|
||
forceSetAppleHostProfileStatus(t, ds, host3.UUID, profA, fleet.MDMOperationTypeInstall, fleet.MDMDeliveryVerifying)
|
||
|
||
// batch-resend profile A, does not impact any host
|
||
n, err := ds.BatchResendMDMProfileToHosts(ctx, profA.ProfileUUID, fleet.BatchResendMDMProfileFilters{ProfileStatus: fleet.MDMDeliveryFailed})
|
||
require.NoError(t, err)
|
||
require.EqualValues(t, 0, n)
|
||
|
||
assertHostProfileStatus(t, ds, host1.UUID,
|
||
hostProfileStatus{profA.ProfileUUID, fleet.MDMDeliveryVerifying})
|
||
assertHostProfileStatus(t, ds, host2.UUID,
|
||
hostProfileStatus{profA.ProfileUUID, fleet.MDMDeliveryVerifying})
|
||
assertHostProfileStatus(t, ds, host3.UUID,
|
||
hostProfileStatus{profA.ProfileUUID, fleet.MDMDeliveryVerifying})
|
||
assertHostProfileStatus(t, ds, host4.UUID)
|
||
|
||
// batch-resend profile B, does not impact any host as it's not delievered to any yet
|
||
n, err = ds.BatchResendMDMProfileToHosts(ctx, profB.ProfileUUID, fleet.BatchResendMDMProfileFilters{ProfileStatus: fleet.MDMDeliveryFailed})
|
||
require.NoError(t, err)
|
||
require.EqualValues(t, 0, n)
|
||
|
||
assertHostProfileStatus(t, ds, host1.UUID,
|
||
hostProfileStatus{profA.ProfileUUID, fleet.MDMDeliveryVerifying})
|
||
assertHostProfileStatus(t, ds, host2.UUID,
|
||
hostProfileStatus{profA.ProfileUUID, fleet.MDMDeliveryVerifying})
|
||
assertHostProfileStatus(t, ds, host3.UUID,
|
||
hostProfileStatus{profA.ProfileUUID, fleet.MDMDeliveryVerifying})
|
||
assertHostProfileStatus(t, ds, host4.UUID)
|
||
|
||
// make profile A failed on a couple hosts, verified on the other
|
||
forceSetAppleHostProfileStatus(t, ds, host1.UUID, profA, fleet.MDMOperationTypeInstall, fleet.MDMDeliveryFailed)
|
||
forceSetAppleHostProfileStatus(t, ds, host2.UUID, profA, fleet.MDMOperationTypeInstall, fleet.MDMDeliveryFailed)
|
||
forceSetAppleHostProfileStatus(t, ds, host3.UUID, profA, fleet.MDMOperationTypeInstall, fleet.MDMDeliveryVerified)
|
||
|
||
// batch-resend profile A, impacts host 1 and 2
|
||
n, err = ds.BatchResendMDMProfileToHosts(ctx, profA.ProfileUUID, fleet.BatchResendMDMProfileFilters{ProfileStatus: fleet.MDMDeliveryFailed})
|
||
require.NoError(t, err)
|
||
require.EqualValues(t, 2, n)
|
||
|
||
assertHostProfileStatus(t, ds, host1.UUID,
|
||
hostProfileStatus{profA.ProfileUUID, fleet.MDMDeliveryPending})
|
||
assertHostProfileStatus(t, ds, host2.UUID,
|
||
hostProfileStatus{profA.ProfileUUID, fleet.MDMDeliveryPending})
|
||
assertHostProfileStatus(t, ds, host3.UUID,
|
||
hostProfileStatus{profA.ProfileUUID, fleet.MDMDeliveryVerified})
|
||
assertHostProfileStatus(t, ds, host4.UUID)
|
||
|
||
// batch-resend profile A again, no change as it is already pending on the impacted hosts
|
||
n, err = ds.BatchResendMDMProfileToHosts(ctx, profA.ProfileUUID, fleet.BatchResendMDMProfileFilters{ProfileStatus: fleet.MDMDeliveryFailed})
|
||
require.NoError(t, err)
|
||
require.EqualValues(t, 0, n)
|
||
|
||
assertHostProfileStatus(t, ds, host1.UUID,
|
||
hostProfileStatus{profA.ProfileUUID, fleet.MDMDeliveryPending})
|
||
assertHostProfileStatus(t, ds, host2.UUID,
|
||
hostProfileStatus{profA.ProfileUUID, fleet.MDMDeliveryPending})
|
||
assertHostProfileStatus(t, ds, host3.UUID,
|
||
hostProfileStatus{profA.ProfileUUID, fleet.MDMDeliveryVerified})
|
||
assertHostProfileStatus(t, ds, host4.UUID)
|
||
|
||
// make profile B failed on all hosts
|
||
forceSetAppleHostProfileStatus(t, ds, host1.UUID, profB, fleet.MDMOperationTypeInstall, fleet.MDMDeliveryFailed)
|
||
forceSetAppleHostProfileStatus(t, ds, host2.UUID, profB, fleet.MDMOperationTypeInstall, fleet.MDMDeliveryFailed)
|
||
forceSetAppleHostProfileStatus(t, ds, host3.UUID, profB, fleet.MDMOperationTypeInstall, fleet.MDMDeliveryFailed)
|
||
|
||
assertHostProfileStatus(t, ds, host1.UUID,
|
||
hostProfileStatus{profA.ProfileUUID, fleet.MDMDeliveryPending},
|
||
hostProfileStatus{profB.ProfileUUID, fleet.MDMDeliveryFailed})
|
||
assertHostProfileStatus(t, ds, host2.UUID,
|
||
hostProfileStatus{profA.ProfileUUID, fleet.MDMDeliveryPending},
|
||
hostProfileStatus{profB.ProfileUUID, fleet.MDMDeliveryFailed})
|
||
assertHostProfileStatus(t, ds, host3.UUID,
|
||
hostProfileStatus{profA.ProfileUUID, fleet.MDMDeliveryVerified},
|
||
hostProfileStatus{profB.ProfileUUID, fleet.MDMDeliveryFailed})
|
||
assertHostProfileStatus(t, ds, host4.UUID)
|
||
|
||
// batch-resend profile B, all hosts affected
|
||
n, err = ds.BatchResendMDMProfileToHosts(ctx, profB.ProfileUUID, fleet.BatchResendMDMProfileFilters{ProfileStatus: fleet.MDMDeliveryFailed})
|
||
require.NoError(t, err)
|
||
require.EqualValues(t, 3, n)
|
||
|
||
assertHostProfileStatus(t, ds, host1.UUID,
|
||
hostProfileStatus{profA.ProfileUUID, fleet.MDMDeliveryPending},
|
||
hostProfileStatus{profB.ProfileUUID, fleet.MDMDeliveryPending})
|
||
assertHostProfileStatus(t, ds, host2.UUID,
|
||
hostProfileStatus{profA.ProfileUUID, fleet.MDMDeliveryPending},
|
||
hostProfileStatus{profB.ProfileUUID, fleet.MDMDeliveryPending})
|
||
assertHostProfileStatus(t, ds, host3.UUID,
|
||
hostProfileStatus{profA.ProfileUUID, fleet.MDMDeliveryVerified},
|
||
hostProfileStatus{profB.ProfileUUID, fleet.MDMDeliveryPending})
|
||
assertHostProfileStatus(t, ds, host4.UUID)
|
||
|
||
// make profile C failed on host 4, other profiles Verified
|
||
forceSetAppleHostProfileStatus(t, ds, host4.UUID, profC, fleet.MDMOperationTypeInstall, fleet.MDMDeliveryFailed)
|
||
forceSetAppleHostProfileStatus(t, ds, host1.UUID, profA, fleet.MDMOperationTypeInstall, fleet.MDMDeliveryVerified)
|
||
forceSetAppleHostProfileStatus(t, ds, host2.UUID, profA, fleet.MDMOperationTypeInstall, fleet.MDMDeliveryVerified)
|
||
forceSetAppleHostProfileStatus(t, ds, host1.UUID, profB, fleet.MDMOperationTypeInstall, fleet.MDMDeliveryVerified)
|
||
forceSetAppleHostProfileStatus(t, ds, host2.UUID, profB, fleet.MDMOperationTypeInstall, fleet.MDMDeliveryVerified)
|
||
forceSetAppleHostProfileStatus(t, ds, host3.UUID, profB, fleet.MDMOperationTypeInstall, fleet.MDMDeliveryVerified)
|
||
|
||
// batch-resend profile C, host 4 affected
|
||
n, err = ds.BatchResendMDMProfileToHosts(ctx, profC.ProfileUUID, fleet.BatchResendMDMProfileFilters{ProfileStatus: fleet.MDMDeliveryFailed})
|
||
require.NoError(t, err)
|
||
require.EqualValues(t, 1, n)
|
||
|
||
assertHostProfileStatus(t, ds, host1.UUID,
|
||
hostProfileStatus{profA.ProfileUUID, fleet.MDMDeliveryVerified},
|
||
hostProfileStatus{profB.ProfileUUID, fleet.MDMDeliveryVerified})
|
||
assertHostProfileStatus(t, ds, host2.UUID,
|
||
hostProfileStatus{profA.ProfileUUID, fleet.MDMDeliveryVerified},
|
||
hostProfileStatus{profB.ProfileUUID, fleet.MDMDeliveryVerified})
|
||
assertHostProfileStatus(t, ds, host3.UUID,
|
||
hostProfileStatus{profA.ProfileUUID, fleet.MDMDeliveryVerified},
|
||
hostProfileStatus{profB.ProfileUUID, fleet.MDMDeliveryVerified})
|
||
assertHostProfileStatus(t, ds, host4.UUID,
|
||
hostProfileStatus{profC.ProfileUUID, fleet.MDMDeliveryPending})
|
||
|
||
// batch-resend profile C again, no change
|
||
n, err = ds.BatchResendMDMProfileToHosts(ctx, profC.ProfileUUID, fleet.BatchResendMDMProfileFilters{ProfileStatus: fleet.MDMDeliveryFailed})
|
||
require.NoError(t, err)
|
||
require.EqualValues(t, 0, n)
|
||
|
||
assertHostProfileStatus(t, ds, host1.UUID,
|
||
hostProfileStatus{profA.ProfileUUID, fleet.MDMDeliveryVerified},
|
||
hostProfileStatus{profB.ProfileUUID, fleet.MDMDeliveryVerified})
|
||
assertHostProfileStatus(t, ds, host2.UUID,
|
||
hostProfileStatus{profA.ProfileUUID, fleet.MDMDeliveryVerified},
|
||
hostProfileStatus{profB.ProfileUUID, fleet.MDMDeliveryVerified})
|
||
assertHostProfileStatus(t, ds, host3.UUID,
|
||
hostProfileStatus{profA.ProfileUUID, fleet.MDMDeliveryVerified},
|
||
hostProfileStatus{profB.ProfileUUID, fleet.MDMDeliveryVerified})
|
||
assertHostProfileStatus(t, ds, host4.UUID,
|
||
hostProfileStatus{profC.ProfileUUID, fleet.MDMDeliveryPending})
|
||
}
|
||
|
||
func testGetMDMConfigProfileStatus(t *testing.T, ds *Datastore) {
|
||
test.AddBuiltinLabels(t, ds)
|
||
ctx := t.Context()
|
||
|
||
// create a team
|
||
team, err := ds.NewTeam(ctx, &fleet.Team{Name: "team1"})
|
||
require.NoError(t, err)
|
||
|
||
// create some Apple and Windows profiles and declaration for No team
|
||
appleProfsNoTm := []*fleet.MDMAppleConfigProfile{
|
||
configProfileForTest(t, "A1", "A1", uuid.NewString()),
|
||
}
|
||
windowsProfsNoTm := []*fleet.MDMWindowsConfigProfile{
|
||
windowsConfigProfileForTest(t, "W1", "W1"),
|
||
}
|
||
appleDeclsNoTm := []*fleet.MDMAppleDeclaration{
|
||
declForTest("D1", "D1", "{}"),
|
||
}
|
||
// Create an android profile for No Team
|
||
androidProfilesNoTm := []*fleet.MDMAndroidConfigProfile{
|
||
androidProfileForTest("G1"),
|
||
}
|
||
_, err = ds.BatchSetMDMProfiles(ctx, nil, appleProfsNoTm, windowsProfsNoTm, appleDeclsNoTm, androidProfilesNoTm, nil)
|
||
require.NoError(t, err)
|
||
|
||
// create some Apple and Windows profiles and declaration for the team
|
||
appleProfsTm := []*fleet.MDMAppleConfigProfile{
|
||
configProfileForTest(t, "A2", "A2", uuid.NewString()),
|
||
}
|
||
windowsProfsTm := []*fleet.MDMWindowsConfigProfile{
|
||
windowsConfigProfileForTest(t, "W2", "W2"),
|
||
}
|
||
appleDeclsTm := []*fleet.MDMAppleDeclaration{
|
||
declForTest("D2", "D2", "{}"),
|
||
}
|
||
androidProfilesTm := []*fleet.MDMAndroidConfigProfile{
|
||
androidProfileForTest("G2"),
|
||
}
|
||
androidProfilesTm[0].TeamID = &team.ID
|
||
_, err = ds.BatchSetMDMProfiles(ctx, &team.ID, appleProfsTm, windowsProfsTm, appleDeclsTm, androidProfilesTm, nil)
|
||
require.NoError(t, err)
|
||
|
||
// collect the profiles in a lookup table by name
|
||
profNameToProf := make(map[string]*fleet.MDMConfigProfilePayload)
|
||
profs, _, err := ds.ListMDMConfigProfiles(ctx, nil, fleet.ListOptions{})
|
||
require.NoError(t, err)
|
||
for _, prof := range profs {
|
||
profNameToProf[prof.Name] = prof
|
||
}
|
||
profs, _, err = ds.ListMDMConfigProfiles(ctx, &team.ID, fleet.ListOptions{})
|
||
require.NoError(t, err)
|
||
for _, prof := range profs {
|
||
profNameToProf[prof.Name] = prof
|
||
}
|
||
|
||
// create some hosts, macOS and Windows, No team and Team
|
||
host1 := test.NewHost(t, ds, "host1", "1", "h1key", "host1uuid", time.Now())
|
||
host2 := test.NewHost(t, ds, "host2", "2", "h2key", "host2uuid", time.Now())
|
||
host3 := test.NewHost(t, ds, "host3", "3", "h3key", "host3uuid", time.Now())
|
||
host4 := test.NewHost(t, ds, "host4", "4", "h4key", "host4uuid", time.Now())
|
||
host5 := test.NewHost(t, ds, "host5", "5", "h5key", "host5uuid", time.Now())
|
||
nanoEnroll(t, ds, host1, false)
|
||
nanoEnroll(t, ds, host2, false)
|
||
nanoEnroll(t, ds, host3, false)
|
||
nanoEnroll(t, ds, host4, false)
|
||
nanoEnroll(t, ds, host5, false)
|
||
|
||
host6 := test.NewHost(t, ds, "host6", "6", "h6key", "host6uuid", time.Now())
|
||
host6.Platform = "windows"
|
||
err = ds.UpdateHost(ctx, host6)
|
||
require.NoError(t, err)
|
||
windowsEnroll(t, ds, host6)
|
||
|
||
host7 := test.NewHost(t, ds, "host7", "7", "h7key", "host7uuid", time.Now())
|
||
host7.Platform = "windows"
|
||
err = ds.UpdateHost(ctx, host7)
|
||
require.NoError(t, err)
|
||
windowsEnroll(t, ds, host7)
|
||
|
||
host8 := test.NewHost(t, ds, "host8", "8", "h8key", "host8uuid", time.Now())
|
||
host8.Platform = "windows"
|
||
err = ds.UpdateHost(ctx, host8)
|
||
require.NoError(t, err)
|
||
windowsEnroll(t, ds, host8)
|
||
|
||
androidHost9 := createAndroidHost("enterprise-id-9")
|
||
newHost, err := ds.NewAndroidHost(context.Background(), androidHost9, false)
|
||
require.NoError(t, err)
|
||
require.NotNil(t, newHost)
|
||
host9 := newHost.Host
|
||
|
||
androidHost10 := createAndroidHost("enterprise-id-10")
|
||
newHost, err = ds.NewAndroidHost(context.Background(), androidHost10, false)
|
||
require.NoError(t, err)
|
||
require.NotNil(t, newHost)
|
||
host10 := newHost.Host
|
||
|
||
androidHost11 := createAndroidHost("enterprise-id-11")
|
||
newHost, err = ds.NewAndroidHost(context.Background(), androidHost11, false)
|
||
require.NoError(t, err)
|
||
require.NotNil(t, newHost)
|
||
host11 := newHost.Host
|
||
|
||
for _, h := range []*fleet.Host{host1, host2, host3, host4, host5, host6, host7, host8} {
|
||
err = ds.SetOrUpdateMDMData(ctx, h.ID, false, true, "https://fleetdm.com", false, fleet.WellKnownMDMFleet, "", false)
|
||
require.NoError(t, err)
|
||
}
|
||
|
||
// host 4, 5, 8, 10, 11 are on team
|
||
err = ds.AddHostsToTeam(ctx, fleet.NewAddHostsToTeamParams(&team.ID, []uint{host4.ID, host5.ID, host8.ID, host10.ID, host11.ID}))
|
||
require.NoError(t, err)
|
||
_, _, _, _ = host1, host2, host3, host11
|
||
|
||
// currently no status for any profile
|
||
for _, name := range []string{"A1", "W1", "D1", "A2", "W2", "D2", "G1", "G2"} {
|
||
status, err := ds.GetMDMConfigProfileStatus(ctx, profNameToProf[name].ProfileUUID)
|
||
require.NoError(t, err, name)
|
||
require.Equal(t, fleet.MDMConfigProfileStatus{}, status, name)
|
||
}
|
||
// unknown profile with invalid prefix is not found (we don't test unknown
|
||
// profile with valid prefix here as the not found is handled at the service
|
||
// layer - it loads the profile for authorization)
|
||
_, err = ds.GetMDMConfigProfileStatus(ctx, "ZZ-NOT-FOUND")
|
||
require.Error(t, err)
|
||
var nfe fleet.NotFoundError
|
||
require.ErrorAs(t, err, &nfe)
|
||
|
||
cases := []struct {
|
||
desc string
|
||
profileUUID string
|
||
setup func(t *testing.T)
|
||
want fleet.MDMConfigProfileStatus
|
||
}{
|
||
{
|
||
desc: "macOS no team profile A1 all pending/NULL",
|
||
profileUUID: profNameToProf["A1"].ProfileUUID,
|
||
setup: func(t *testing.T) {
|
||
appleA1 := test.ToMDMAppleConfigProfile(profNameToProf["A1"])
|
||
forceSetAppleHostProfileStatus(t, ds, host1.UUID, appleA1, fleet.MDMOperationTypeInstall, fleet.MDMDeliveryPending)
|
||
forceSetAppleHostProfileStatus(t, ds, host2.UUID, appleA1, fleet.MDMOperationTypeInstall, fleet.MDMDeliveryPending)
|
||
forceSetAppleHostProfileStatus(t, ds, host3.UUID, appleA1, fleet.MDMOperationTypeInstall, "")
|
||
},
|
||
want: fleet.MDMConfigProfileStatus{Pending: 3},
|
||
},
|
||
{
|
||
desc: "windows no team profile W1 all pending/NULL",
|
||
profileUUID: profNameToProf["W1"].ProfileUUID,
|
||
setup: func(t *testing.T) {
|
||
winW1 := test.ToMDMWindowsConfigProfile(profNameToProf["W1"])
|
||
forceSetWindowsHostProfileStatus(t, ds, host6.UUID, winW1, fleet.MDMOperationTypeInstall, fleet.MDMDeliveryPending)
|
||
forceSetWindowsHostProfileStatus(t, ds, host7.UUID, winW1, fleet.MDMOperationTypeInstall, "")
|
||
},
|
||
want: fleet.MDMConfigProfileStatus{Pending: 2},
|
||
},
|
||
{
|
||
desc: "windows no team profile W1 pending failed",
|
||
profileUUID: profNameToProf["W1"].ProfileUUID,
|
||
setup: func(t *testing.T) {
|
||
winW1 := test.ToMDMWindowsConfigProfile(profNameToProf["W1"])
|
||
forceSetWindowsHostProfileStatus(t, ds, host6.UUID, winW1, fleet.MDMOperationTypeInstall, fleet.MDMDeliveryPending)
|
||
forceSetWindowsHostProfileStatus(t, ds, host7.UUID, winW1, fleet.MDMOperationTypeInstall, fleet.MDMDeliveryFailed)
|
||
},
|
||
want: fleet.MDMConfigProfileStatus{Pending: 1, Failed: 1},
|
||
},
|
||
{
|
||
desc: "macOS no team decl D1 pending failed verified",
|
||
profileUUID: profNameToProf["D1"].ProfileUUID,
|
||
setup: func(t *testing.T) {
|
||
declD1 := test.ToMDMAppleDecl(profNameToProf["D1"])
|
||
forceSetAppleHostDeclarationStatus(t, ds, host1.UUID, declD1, fleet.MDMOperationTypeInstall, fleet.MDMDeliveryPending)
|
||
forceSetAppleHostDeclarationStatus(t, ds, host2.UUID, declD1, fleet.MDMOperationTypeInstall, fleet.MDMDeliveryFailed)
|
||
forceSetAppleHostDeclarationStatus(t, ds, host3.UUID, declD1, fleet.MDMOperationTypeInstall, fleet.MDMDeliveryVerified)
|
||
},
|
||
want: fleet.MDMConfigProfileStatus{Pending: 1, Failed: 1, Verified: 1},
|
||
},
|
||
{
|
||
desc: "macOS team profile A2 verifying verified",
|
||
profileUUID: profNameToProf["A2"].ProfileUUID,
|
||
setup: func(t *testing.T) {
|
||
appleA2 := test.ToMDMAppleConfigProfile(profNameToProf["A2"])
|
||
forceSetAppleHostProfileStatus(t, ds, host4.UUID, appleA2, fleet.MDMOperationTypeInstall, fleet.MDMDeliveryVerifying)
|
||
forceSetAppleHostProfileStatus(t, ds, host5.UUID, appleA2, fleet.MDMOperationTypeInstall, fleet.MDMDeliveryVerified)
|
||
},
|
||
want: fleet.MDMConfigProfileStatus{Verifying: 1, Verified: 1},
|
||
},
|
||
{
|
||
desc: "macOS team decl D2 all failed",
|
||
profileUUID: profNameToProf["D2"].ProfileUUID,
|
||
setup: func(t *testing.T) {
|
||
declD2 := test.ToMDMAppleDecl(profNameToProf["D2"])
|
||
forceSetAppleHostDeclarationStatus(t, ds, host4.UUID, declD2, fleet.MDMOperationTypeInstall, fleet.MDMDeliveryFailed)
|
||
forceSetAppleHostDeclarationStatus(t, ds, host5.UUID, declD2, fleet.MDMOperationTypeInstall, fleet.MDMDeliveryFailed)
|
||
},
|
||
want: fleet.MDMConfigProfileStatus{Failed: 2},
|
||
},
|
||
{
|
||
desc: "windows team profile W2 pending",
|
||
profileUUID: profNameToProf["W2"].ProfileUUID,
|
||
setup: func(t *testing.T) {
|
||
winW2 := test.ToMDMWindowsConfigProfile(profNameToProf["W2"])
|
||
forceSetWindowsHostProfileStatus(t, ds, host8.UUID, winW2, fleet.MDMOperationTypeInstall, fleet.MDMDeliveryPending)
|
||
},
|
||
want: fleet.MDMConfigProfileStatus{Pending: 1},
|
||
},
|
||
{
|
||
desc: "android no team G1 profile failed",
|
||
profileUUID: profNameToProf["G1"].ProfileUUID,
|
||
setup: func(t *testing.T) {
|
||
androidG1 := profNameToProf["G1"]
|
||
upsertAndroidHostProfileStatus(t, ds, host9.UUID, androidG1.ProfileUUID, &fleet.MDMDeliveryFailed)
|
||
},
|
||
want: fleet.MDMConfigProfileStatus{Failed: 1},
|
||
},
|
||
{
|
||
desc: "android team G2 profile nil=pending",
|
||
profileUUID: profNameToProf["G2"].ProfileUUID,
|
||
setup: func(t *testing.T) {
|
||
androidG2 := profNameToProf["G2"]
|
||
upsertAndroidHostProfileStatus(t, ds, host10.UUID, androidG2.ProfileUUID, nil)
|
||
},
|
||
want: fleet.MDMConfigProfileStatus{Pending: 1},
|
||
},
|
||
{
|
||
desc: "android team G2 profile nil + pending = 2 pending",
|
||
profileUUID: profNameToProf["G2"].ProfileUUID,
|
||
setup: func(t *testing.T) {
|
||
androidG2 := profNameToProf["G2"]
|
||
upsertAndroidHostProfileStatus(t, ds, host10.UUID, androidG2.ProfileUUID, nil)
|
||
upsertAndroidHostProfileStatus(t, ds, host11.UUID, androidG2.ProfileUUID, &fleet.MDMDeliveryPending)
|
||
},
|
||
want: fleet.MDMConfigProfileStatus{Pending: 2},
|
||
},
|
||
{
|
||
desc: "android team G2 profile pending + verifying",
|
||
profileUUID: profNameToProf["G2"].ProfileUUID,
|
||
setup: func(t *testing.T) {
|
||
androidG2 := profNameToProf["G2"]
|
||
upsertAndroidHostProfileStatus(t, ds, host10.UUID, androidG2.ProfileUUID, &fleet.MDMDeliveryVerifying)
|
||
upsertAndroidHostProfileStatus(t, ds, host11.UUID, androidG2.ProfileUUID, &fleet.MDMDeliveryPending)
|
||
},
|
||
want: fleet.MDMConfigProfileStatus{Pending: 1, Verifying: 1},
|
||
},
|
||
{
|
||
desc: "android team G2 profile verified + verifying",
|
||
profileUUID: profNameToProf["G2"].ProfileUUID,
|
||
setup: func(t *testing.T) {
|
||
androidG2 := profNameToProf["G2"]
|
||
upsertAndroidHostProfileStatus(t, ds, host10.UUID, androidG2.ProfileUUID, &fleet.MDMDeliveryVerified)
|
||
upsertAndroidHostProfileStatus(t, ds, host11.UUID, androidG2.ProfileUUID, &fleet.MDMDeliveryVerifying)
|
||
},
|
||
want: fleet.MDMConfigProfileStatus{Verifying: 1, Verified: 1},
|
||
},
|
||
{
|
||
desc: "android team G2 profile failed + verified",
|
||
profileUUID: profNameToProf["G2"].ProfileUUID,
|
||
setup: func(t *testing.T) {
|
||
// Set an unrelated profile status on another team just to ensure isolation
|
||
androidG1 := profNameToProf["G1"]
|
||
upsertAndroidHostProfileStatus(t, ds, host9.UUID, androidG1.ProfileUUID, &fleet.MDMDeliveryPending)
|
||
|
||
androidG2 := profNameToProf["G2"]
|
||
upsertAndroidHostProfileStatus(t, ds, host10.UUID, androidG2.ProfileUUID, &fleet.MDMDeliveryVerified)
|
||
upsertAndroidHostProfileStatus(t, ds, host11.UUID, androidG2.ProfileUUID, &fleet.MDMDeliveryFailed)
|
||
},
|
||
want: fleet.MDMConfigProfileStatus{Failed: 1, Verified: 1},
|
||
},
|
||
}
|
||
for _, c := range cases {
|
||
t.Run(c.desc, func(t *testing.T) {
|
||
c.setup(t)
|
||
got, err := ds.GetMDMConfigProfileStatus(ctx, c.profileUUID)
|
||
require.NoError(t, err)
|
||
require.Equal(t, c.want, got)
|
||
})
|
||
}
|
||
}
|
||
|
||
func testDeleteMDMProfilesCancelsInstalls(t *testing.T, ds *Datastore) {
|
||
ctx := t.Context()
|
||
|
||
SetTestABMAssets(t, ds, "fleet")
|
||
|
||
// create some Apple and Windows profiles and declaration
|
||
appleProfs := []*fleet.MDMAppleConfigProfile{
|
||
configProfileForTest(t, "A1", "A1", uuid.NewString()),
|
||
configProfileForTest(t, "A2", "A2", uuid.NewString()),
|
||
configProfileForTest(t, "A3", "A3", uuid.NewString()),
|
||
}
|
||
windowsProfs := []*fleet.MDMWindowsConfigProfile{
|
||
windowsConfigProfileForTest(t, "W1", "W1"),
|
||
windowsConfigProfileForTest(t, "W2", "W2"),
|
||
}
|
||
appleDecls := []*fleet.MDMAppleDeclaration{
|
||
declForTest("D1", "D1", "{}"),
|
||
declForTest("D2", "D2", "{}"),
|
||
}
|
||
androidProfs := []*fleet.MDMAndroidConfigProfile{
|
||
androidConfigProfileForTest(t, "G1", nil),
|
||
androidConfigProfileForTest(t, "G2", nil),
|
||
}
|
||
_, err := ds.BatchSetMDMProfiles(ctx, nil, appleProfs, windowsProfs, appleDecls, androidProfs, nil)
|
||
require.NoError(t, err)
|
||
|
||
// collect the profiles in a lookup table by name
|
||
profNameToProf := make(map[string]*fleet.MDMConfigProfilePayload)
|
||
profs, _, err := ds.ListMDMConfigProfiles(ctx, nil, fleet.ListOptions{})
|
||
require.NoError(t, err)
|
||
for _, prof := range profs {
|
||
profNameToProf[prof.Name] = prof
|
||
}
|
||
|
||
// delete all kinds of profiles without any host impacted
|
||
err = ds.DeleteMDMAppleConfigProfile(ctx, profNameToProf["A1"].ProfileUUID)
|
||
require.NoError(t, err)
|
||
err = ds.DeleteMDMWindowsConfigProfile(ctx, profNameToProf["W1"].ProfileUUID)
|
||
require.NoError(t, err)
|
||
err = ds.DeleteMDMAppleDeclaration(ctx, profNameToProf["D1"].ProfileUUID)
|
||
require.NoError(t, err)
|
||
err = ds.DeleteMDMAndroidConfigProfile(ctx, profNameToProf["G1"].ProfileUUID)
|
||
require.NoError(t, err)
|
||
|
||
// create some macOS, Windows and android hosts
|
||
host1 := test.NewHost(t, ds, "host1", "1", "h1key", "host1uuid", time.Now())
|
||
host2 := test.NewHost(t, ds, "host2", "2", "h2key", "host2uuid", time.Now())
|
||
nanoEnroll(t, ds, host1, false)
|
||
nanoEnroll(t, ds, host2, false)
|
||
|
||
host3 := test.NewHost(t, ds, "host3", "3", "h3key", "host3uuid", time.Now())
|
||
host3.Platform = "windows"
|
||
err = ds.UpdateHost(ctx, host3)
|
||
require.NoError(t, err)
|
||
windowsEnroll(t, ds, host3)
|
||
|
||
host4 := test.NewHost(t, ds, "host4", "4", "h4key", "host4uuid", time.Now())
|
||
host4.Platform = "windows"
|
||
err = ds.UpdateHost(ctx, host4)
|
||
require.NoError(t, err)
|
||
windowsEnroll(t, ds, host4)
|
||
|
||
host5 := test.NewHost(t, ds, "host5", "5", "h5key", "host5uuid", time.Now())
|
||
host5.Platform = "android"
|
||
err = ds.UpdateHost(ctx, host5)
|
||
require.NoError(t, err)
|
||
|
||
host6 := test.NewHost(t, ds, "host6", "6", "h6key", "host6uuid", time.Now())
|
||
host6.Platform = "android"
|
||
err = ds.UpdateHost(ctx, host6)
|
||
require.NoError(t, err)
|
||
|
||
for _, h := range []*fleet.Host{host1, host2, host3, host4, host5, host6} {
|
||
err = ds.SetOrUpdateMDMData(ctx, h.ID, false, true, "https://fleetdm.com", false, fleet.WellKnownMDMFleet, "", false)
|
||
require.NoError(t, err)
|
||
}
|
||
|
||
// set the declaration as pending install on host1, installed on host2
|
||
forceSetAppleHostDeclarationStatus(t, ds, host1.UUID, test.ToMDMAppleDecl(profNameToProf["D2"]), fleet.MDMOperationTypeInstall, "")
|
||
forceSetAppleHostDeclarationStatus(t, ds, host2.UUID, test.ToMDMAppleDecl(profNameToProf["D2"]), fleet.MDMOperationTypeInstall, fleet.MDMDeliveryVerified)
|
||
assertHostProfileOpStatus(t, ds, host1.UUID,
|
||
hostProfileOpStatus{profNameToProf["D2"].ProfileUUID, fleet.MDMDeliveryPending, fleet.MDMOperationTypeInstall})
|
||
assertHostProfileOpStatus(t, ds, host2.UUID,
|
||
hostProfileOpStatus{profNameToProf["D2"].ProfileUUID, fleet.MDMDeliveryVerified, fleet.MDMOperationTypeInstall})
|
||
|
||
err = ds.DeleteMDMAppleDeclaration(ctx, profNameToProf["D2"].ProfileUUID)
|
||
require.NoError(t, err)
|
||
|
||
assertHostProfileOpStatus(t, ds, host1.UUID)
|
||
assertHostProfileOpStatus(t, ds, host2.UUID,
|
||
hostProfileOpStatus{profNameToProf["D2"].ProfileUUID, fleet.MDMDeliveryPending, fleet.MDMOperationTypeRemove})
|
||
|
||
// set the Windows profile as pending install on host3, installed on host4
|
||
forceSetWindowsHostProfileStatus(t, ds, host3.UUID, test.ToMDMWindowsConfigProfile(profNameToProf["W2"]), fleet.MDMOperationTypeInstall, fleet.MDMDeliveryPending)
|
||
forceSetWindowsHostProfileStatus(t, ds, host4.UUID, test.ToMDMWindowsConfigProfile(profNameToProf["W2"]), fleet.MDMOperationTypeInstall, fleet.MDMDeliveryVerified)
|
||
assertHostProfileOpStatus(t, ds, host3.UUID,
|
||
hostProfileOpStatus{profNameToProf["W2"].ProfileUUID, fleet.MDMDeliveryPending, fleet.MDMOperationTypeInstall})
|
||
assertHostProfileOpStatus(t, ds, host4.UUID,
|
||
hostProfileOpStatus{profNameToProf["W2"].ProfileUUID, fleet.MDMDeliveryVerified, fleet.MDMOperationTypeInstall})
|
||
|
||
err = ds.DeleteMDMWindowsConfigProfile(ctx, profNameToProf["W2"].ProfileUUID)
|
||
require.NoError(t, err)
|
||
|
||
assertHostProfileOpStatus(t, ds, host3.UUID,
|
||
hostProfileOpStatus{profNameToProf["W2"].ProfileUUID, fleet.MDMDeliveryPending, fleet.MDMOperationTypeRemove})
|
||
assertHostProfileOpStatus(t, ds, host4.UUID,
|
||
hostProfileOpStatus{profNameToProf["W2"].ProfileUUID, fleet.MDMDeliveryPending, fleet.MDMOperationTypeRemove})
|
||
|
||
// set the android profile as pending install on host 5 and installed on host 6
|
||
forceSetAndroidHostProfileStatus(t, ds, host5.UUID, test.ToMDMAndroidConfigProfile(profNameToProf["G2"]), fleet.MDMOperationTypeInstall, fleet.MDMDeliveryPending)
|
||
forceSetAndroidHostProfileStatus(t, ds, host6.UUID, test.ToMDMAndroidConfigProfile(profNameToProf["G2"]), fleet.MDMOperationTypeInstall, fleet.MDMDeliveryVerified)
|
||
assertHostProfileOpStatus(t, ds, host5.UUID,
|
||
hostProfileOpStatus{profNameToProf["G2"].ProfileUUID, fleet.MDMDeliveryPending, fleet.MDMOperationTypeInstall})
|
||
assertHostProfileOpStatus(t, ds, host6.UUID,
|
||
hostProfileOpStatus{profNameToProf["G2"].ProfileUUID, fleet.MDMDeliveryVerified, fleet.MDMOperationTypeInstall})
|
||
|
||
err = ds.DeleteMDMAndroidConfigProfile(ctx, profNameToProf["G2"].ProfileUUID)
|
||
require.NoError(t, err)
|
||
|
||
// We can't fully delete failed install profiles, as they might have values sent to the device even if some is overriden.
|
||
// So check for pending, remove instead of fully removed
|
||
assertHostProfileOpStatus(t, ds, host5.UUID,
|
||
hostProfileOpStatus{profNameToProf["G2"].ProfileUUID, fleet.MDMDeliveryPending, fleet.MDMOperationTypeRemove})
|
||
assertHostProfileOpStatus(t, ds, host6.UUID,
|
||
hostProfileOpStatus{profNameToProf["G2"].ProfileUUID, fleet.MDMDeliveryPending, fleet.MDMOperationTypeRemove})
|
||
|
||
commander, _ := createMDMAppleCommanderAndStorage(t, ds)
|
||
|
||
// set the Apple profile as pending install on host1, installed on host2
|
||
forceSetAppleHostProfileStatus(t, ds, host1.UUID, test.ToMDMAppleConfigProfile(profNameToProf["A2"]), fleet.MDMOperationTypeInstall, "")
|
||
forceSetAppleHostProfileStatus(t, ds, host2.UUID, test.ToMDMAppleConfigProfile(profNameToProf["A2"]), fleet.MDMOperationTypeInstall, fleet.MDMDeliveryVerifying)
|
||
// enqueue the corresponding command for the installed profile
|
||
cmdUUID := uuid.New().String()
|
||
err = commander.InstallProfile(ctx, []string{host2.UUID}, appleProfs[1].Mobileconfig, cmdUUID)
|
||
require.NoError(t, err)
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
_, err := q.ExecContext(ctx, `UPDATE host_mdm_apple_profiles SET command_uuid = ? WHERE host_uuid = ? AND profile_uuid = ?`, cmdUUID, host2.UUID, profNameToProf["A2"].ProfileUUID)
|
||
return err
|
||
})
|
||
assertHostProfileOpStatus(t, ds, host1.UUID,
|
||
hostProfileOpStatus{profNameToProf["A2"].ProfileUUID, fleet.MDMDeliveryPending, fleet.MDMOperationTypeInstall})
|
||
assertHostProfileOpStatus(t, ds, host2.UUID,
|
||
hostProfileOpStatus{profNameToProf["A2"].ProfileUUID, fleet.MDMDeliveryVerifying, fleet.MDMOperationTypeInstall},
|
||
hostProfileOpStatus{profNameToProf["D2"].ProfileUUID, fleet.MDMDeliveryPending, fleet.MDMOperationTypeRemove})
|
||
|
||
err = ds.DeleteMDMAppleConfigProfile(ctx, profNameToProf["A2"].ProfileUUID)
|
||
require.NoError(t, err)
|
||
|
||
assertHostProfileOpStatus(t, ds, host1.UUID)
|
||
assertHostProfileOpStatus(t, ds, host2.UUID,
|
||
hostProfileOpStatus{profNameToProf["A2"].ProfileUUID, fleet.MDMDeliveryPending, fleet.MDMOperationTypeRemove},
|
||
hostProfileOpStatus{profNameToProf["D2"].ProfileUUID, fleet.MDMDeliveryPending, fleet.MDMOperationTypeRemove})
|
||
|
||
// nano command is still active because it was already completed (verifying)
|
||
var active bool
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
return sqlx.GetContext(ctx, q, &active, `SELECT active FROM nano_enrollment_queue WHERE id = ? AND command_uuid = ?`, host2.UUID, cmdUUID)
|
||
})
|
||
require.True(t, active)
|
||
|
||
// set the Apple profile as actually pending install (not NULL) on host1
|
||
forceSetAppleHostProfileStatus(t, ds, host1.UUID, test.ToMDMAppleConfigProfile(profNameToProf["A3"]), fleet.MDMOperationTypeInstall, fleet.MDMDeliveryPending)
|
||
// enqueue the corresponding command for the installed profile
|
||
cmdUUID = uuid.New().String()
|
||
err = commander.InstallProfile(ctx, []string{host1.UUID}, appleProfs[2].Mobileconfig, cmdUUID)
|
||
require.NoError(t, err)
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
_, err := q.ExecContext(ctx, `UPDATE host_mdm_apple_profiles SET command_uuid = ? WHERE host_uuid = ? AND profile_uuid = ?`, cmdUUID, host1.UUID, profNameToProf["A3"].ProfileUUID)
|
||
return err
|
||
})
|
||
assertHostProfileOpStatus(t, ds, host1.UUID,
|
||
hostProfileOpStatus{profNameToProf["A3"].ProfileUUID, fleet.MDMDeliveryPending, fleet.MDMOperationTypeInstall})
|
||
assertHostProfileOpStatus(t, ds, host2.UUID,
|
||
hostProfileOpStatus{profNameToProf["A2"].ProfileUUID, fleet.MDMDeliveryPending, fleet.MDMOperationTypeRemove},
|
||
hostProfileOpStatus{profNameToProf["D2"].ProfileUUID, fleet.MDMDeliveryPending, fleet.MDMOperationTypeRemove})
|
||
|
||
err = ds.DeleteMDMAppleConfigProfile(ctx, profNameToProf["A3"].ProfileUUID)
|
||
require.NoError(t, err)
|
||
|
||
assertHostProfileOpStatus(t, ds, host1.UUID,
|
||
hostProfileOpStatus{profNameToProf["A3"].ProfileUUID, fleet.MDMDeliveryPending, fleet.MDMOperationTypeRemove})
|
||
assertHostProfileOpStatus(t, ds, host2.UUID,
|
||
hostProfileOpStatus{profNameToProf["A2"].ProfileUUID, fleet.MDMDeliveryPending, fleet.MDMOperationTypeRemove},
|
||
hostProfileOpStatus{profNameToProf["D2"].ProfileUUID, fleet.MDMDeliveryPending, fleet.MDMOperationTypeRemove})
|
||
|
||
// nano command is now inactive because it was in a pending state and we want to prevent delivery
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
return sqlx.GetContext(ctx, q, &active, `SELECT active FROM nano_enrollment_queue WHERE id = ? AND command_uuid = ?`, host1.UUID, cmdUUID)
|
||
})
|
||
require.False(t, active)
|
||
|
||
// listing the MDM commands does not return the inactive one
|
||
cmds, _, _, err := ds.ListMDMCommands(ctx, fleet.TeamFilter{
|
||
User: test.UserAdmin,
|
||
IncludeObserver: true,
|
||
}, &fleet.MDMCommandListOptions{Filters: fleet.MDMCommandFilters{HostIdentifier: host1.UUID}})
|
||
require.NoError(t, err)
|
||
require.Len(t, cmds, 0)
|
||
}
|
||
|
||
// testDeleteTeamCancelsWindowsProfileInstalls verifies that when a team is
|
||
// deleted, <Delete> commands are generated for Windows profiles that were
|
||
// delivered to hosts. This ensures settings are actually removed from devices
|
||
// rather than silently orphaned.
|
||
func testDeleteTeamCancelsWindowsProfileInstalls(t *testing.T, ds *Datastore) {
|
||
ctx := t.Context()
|
||
|
||
// Create a team with Windows profiles.
|
||
team, err := ds.NewTeam(ctx, &fleet.Team{Name: "delete-team-test"})
|
||
require.NoError(t, err)
|
||
|
||
windowsProfs := []*fleet.MDMWindowsConfigProfile{
|
||
windowsConfigProfileForTest(t, "TW1", "TW1"),
|
||
windowsConfigProfileForTest(t, "TW2", "TW2"),
|
||
}
|
||
_, err = ds.BatchSetMDMProfiles(ctx, &team.ID, nil, windowsProfs, nil, nil, nil)
|
||
require.NoError(t, err)
|
||
|
||
// Collect profile UUIDs.
|
||
profs, _, err := ds.ListMDMConfigProfiles(ctx, &team.ID, fleet.ListOptions{})
|
||
require.NoError(t, err)
|
||
require.Len(t, profs, 2)
|
||
profUUIDs := make([]string, len(profs))
|
||
for i, p := range profs {
|
||
profUUIDs[i] = p.ProfileUUID
|
||
}
|
||
|
||
// Create Windows hosts enrolled in MDM and assigned to the team.
|
||
host1 := test.NewHost(t, ds, "tw-host1", "tw1", "tw1key", "tw-host1-uuid", time.Now())
|
||
host1.Platform = "windows"
|
||
host1.TeamID = &team.ID
|
||
err = ds.UpdateHost(ctx, host1)
|
||
require.NoError(t, err)
|
||
windowsEnroll(t, ds, host1)
|
||
|
||
host2 := test.NewHost(t, ds, "tw-host2", "tw2", "tw2key", "tw-host2-uuid", time.Now())
|
||
host2.Platform = "windows"
|
||
host2.TeamID = &team.ID
|
||
err = ds.UpdateHost(ctx, host2)
|
||
require.NoError(t, err)
|
||
windowsEnroll(t, ds, host2)
|
||
|
||
// Simulate profiles delivered to both hosts (install + verified).
|
||
for _, h := range []*fleet.Host{host1, host2} {
|
||
for _, p := range profs {
|
||
forceSetWindowsHostProfileStatus(t, ds, h.UUID,
|
||
test.ToMDMWindowsConfigProfile(p),
|
||
fleet.MDMOperationTypeInstall, fleet.MDMDeliveryVerified)
|
||
}
|
||
}
|
||
|
||
// Verify: 4 rows, all install+verified.
|
||
assertHostProfileOpStatus(t, ds, host1.UUID,
|
||
hostProfileOpStatus{profUUIDs[0], fleet.MDMDeliveryVerified, fleet.MDMOperationTypeInstall},
|
||
hostProfileOpStatus{profUUIDs[1], fleet.MDMDeliveryVerified, fleet.MDMOperationTypeInstall})
|
||
assertHostProfileOpStatus(t, ds, host2.UUID,
|
||
hostProfileOpStatus{profUUIDs[0], fleet.MDMDeliveryVerified, fleet.MDMOperationTypeInstall},
|
||
hostProfileOpStatus{profUUIDs[1], fleet.MDMDeliveryVerified, fleet.MDMOperationTypeInstall})
|
||
|
||
// Delete the team — this should generate <Delete> commands.
|
||
err = ds.DeleteTeam(ctx, team.ID)
|
||
require.NoError(t, err)
|
||
|
||
// Config profiles should be gone.
|
||
teamProfs, _, err := ds.ListMDMConfigProfiles(ctx, &team.ID, fleet.ListOptions{})
|
||
require.NoError(t, err)
|
||
require.Len(t, teamProfs, 0)
|
||
|
||
// Host-profile rows should be remove+pending (not remove+NULL, not deleted).
|
||
assertHostProfileOpStatus(t, ds, host1.UUID,
|
||
hostProfileOpStatus{profUUIDs[0], fleet.MDMDeliveryPending, fleet.MDMOperationTypeRemove},
|
||
hostProfileOpStatus{profUUIDs[1], fleet.MDMDeliveryPending, fleet.MDMOperationTypeRemove})
|
||
assertHostProfileOpStatus(t, ds, host2.UUID,
|
||
hostProfileOpStatus{profUUIDs[0], fleet.MDMDeliveryPending, fleet.MDMOperationTypeRemove},
|
||
hostProfileOpStatus{profUUIDs[1], fleet.MDMDeliveryPending, fleet.MDMOperationTypeRemove})
|
||
|
||
// Verify <Delete> commands were enqueued.
|
||
var deleteCmdCount int
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
return sqlx.GetContext(ctx, q, &deleteCmdCount, `
|
||
SELECT COUNT(*) FROM windows_mdm_command_queue wmcq
|
||
JOIN windows_mdm_commands wmc ON wmc.command_uuid = wmcq.command_uuid
|
||
WHERE wmc.raw_command LIKE '%<Delete>%'`)
|
||
})
|
||
// 2 profiles × 2 hosts = 4 queue entries (but command rows are shared per profile)
|
||
require.Equal(t, 4, deleteCmdCount, "expected 4 delete command queue entries (2 profiles × 2 hosts)")
|
||
}
|
||
|
||
func androidConfigProfileForTest(t *testing.T, name string, content map[string]any, labels ...*fleet.Label) *fleet.MDMAndroidConfigProfile {
|
||
if content == nil {
|
||
content = make(map[string]any)
|
||
}
|
||
content["name"] = name
|
||
rawJSON, err := json.Marshal(content)
|
||
require.NoError(t, err)
|
||
|
||
prof := &fleet.MDMAndroidConfigProfile{
|
||
Name: name,
|
||
RawJSON: rawJSON,
|
||
}
|
||
|
||
for _, lbl := range labels {
|
||
switch {
|
||
case strings.HasPrefix(lbl.Name, "exclude-"):
|
||
prof.LabelsExcludeAny = append(prof.LabelsExcludeAny, fleet.ConfigurationProfileLabel{LabelName: lbl.Name, LabelID: lbl.ID})
|
||
case strings.HasPrefix(lbl.Name, "include-any-"):
|
||
prof.LabelsIncludeAny = append(prof.LabelsIncludeAny, fleet.ConfigurationProfileLabel{LabelName: lbl.Name, LabelID: lbl.ID})
|
||
default:
|
||
prof.LabelsIncludeAll = append(prof.LabelsIncludeAll, fleet.ConfigurationProfileLabel{LabelName: lbl.Name, LabelID: lbl.ID})
|
||
}
|
||
}
|
||
|
||
return prof
|
||
}
|
||
|
||
func forceSetWindowsHostProfileStatus(t *testing.T, ds *Datastore, hostUUID string, profile *fleet.MDMWindowsConfigProfile, operation fleet.MDMOperationType, status fleet.MDMDeliveryStatus) {
|
||
ctx := t.Context()
|
||
|
||
// empty status string means set to NULL
|
||
var actualStatus *fleet.MDMDeliveryStatus
|
||
if status != "" {
|
||
actualStatus = &status
|
||
}
|
||
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
_, err := q.ExecContext(ctx, `INSERT INTO host_mdm_windows_profiles
|
||
(host_uuid, status, operation_type, command_uuid, profile_name, checksum, profile_uuid)
|
||
VALUES
|
||
(?, ?, ?, ?, ?, UNHEX(MD5(?)), ?)
|
||
ON DUPLICATE KEY UPDATE
|
||
status = VALUES(status),
|
||
operation_type = VALUES(operation_type)
|
||
`,
|
||
hostUUID, actualStatus, operation, uuid.NewString(), profile.Name, profile.SyncML, profile.ProfileUUID)
|
||
return err
|
||
})
|
||
}
|
||
|
||
func forceSetAppleHostDeclarationStatus(t *testing.T, ds *Datastore, hostUUID string, profile *fleet.MDMAppleDeclaration, operation fleet.MDMOperationType, status fleet.MDMDeliveryStatus) {
|
||
ctx := t.Context()
|
||
|
||
// empty status string means set to NULL
|
||
var actualStatus *fleet.MDMDeliveryStatus
|
||
if status != "" {
|
||
actualStatus = &status
|
||
}
|
||
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
token := uuid.New() // Generate binary UUID
|
||
_, err := q.ExecContext(ctx, `INSERT INTO host_mdm_apple_declarations
|
||
(declaration_identifier, host_uuid, status, operation_type, token, declaration_name, declaration_uuid)
|
||
VALUES
|
||
(?, ?, ?, ?, ?, ?, ?)
|
||
ON DUPLICATE KEY UPDATE
|
||
status = VALUES(status),
|
||
operation_type = VALUES(operation_type)
|
||
`,
|
||
profile.Identifier, hostUUID, actualStatus, operation, token[:], profile.Name, profile.DeclarationUUID)
|
||
return err
|
||
})
|
||
}
|
||
|
||
func forceSetAndroidHostProfileStatus(t *testing.T, ds *Datastore, hostUUID string, profile *fleet.MDMAndroidConfigProfile, operation fleet.MDMOperationType, status fleet.MDMDeliveryStatus) {
|
||
ctx := t.Context()
|
||
|
||
// empty status string means set to NULL
|
||
var actualStatus *fleet.MDMDeliveryStatus
|
||
if status != "" {
|
||
actualStatus = &status
|
||
}
|
||
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
_, err := q.ExecContext(ctx, `INSERT INTO host_mdm_android_profiles
|
||
(host_uuid, status, operation_type, profile_name, profile_uuid)
|
||
VALUES
|
||
(?, ?, ?, ?, ?)
|
||
ON DUPLICATE KEY UPDATE
|
||
status = VALUES(status),
|
||
operation_type = VALUES(operation_type)
|
||
`,
|
||
hostUUID, actualStatus, operation, profile.Name, profile.ProfileUUID)
|
||
return err
|
||
})
|
||
}
|
||
|
||
func testCleanUpMDMManagedCertificates(t *testing.T, ds *Datastore) {
|
||
ctx := t.Context()
|
||
|
||
host, err := ds.NewHost(ctx, &fleet.Host{
|
||
DetailUpdatedAt: time.Now(),
|
||
LabelUpdatedAt: time.Now(),
|
||
PolicyUpdatedAt: time.Now(),
|
||
SeenTime: time.Now(),
|
||
OsqueryHostID: ptr.String("host0-osquery-id"),
|
||
NodeKey: ptr.String("host0-node-key"),
|
||
UUID: "host0-test-mdm-profiles",
|
||
Hostname: "hostname0",
|
||
})
|
||
require.NoError(t, err)
|
||
|
||
t.Run("non matching host profile record", func(t *testing.T) {
|
||
badProfileUUID := uuid.NewString()
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
_, err := q.ExecContext(ctx, `
|
||
INSERT INTO host_mdm_managed_certificates (host_uuid, profile_uuid) VALUES (?, ?)
|
||
`, host.UUID, badProfileUUID)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return nil
|
||
})
|
||
var uid string
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
return sqlx.GetContext(ctx, q, &uid, `SELECT profile_uuid FROM host_mdm_managed_certificates WHERE profile_uuid = ?`,
|
||
badProfileUUID)
|
||
})
|
||
require.Equal(t, badProfileUUID, uid)
|
||
|
||
// Cleanup should delete the above orphaned record
|
||
err = ds.CleanUpMDMManagedCertificates(ctx)
|
||
require.NoError(t, err)
|
||
err = ExecAdhocSQLWithError(ds, func(q sqlx.ExtContext) error {
|
||
return sqlx.GetContext(ctx, q, &uid, `SELECT profile_uuid FROM host_mdm_managed_certificates WHERE profile_uuid = ?`,
|
||
badProfileUUID)
|
||
})
|
||
require.ErrorIs(t, err, sql.ErrNoRows)
|
||
})
|
||
|
||
t.Run("valid windows record stays", func(t *testing.T) {
|
||
t.Cleanup(func() {
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
_, err := q.ExecContext(ctx, `
|
||
DELETE FROM host_mdm_managed_certificates;
|
||
DELETE FROM host_mdm_windows_profiles;
|
||
`)
|
||
return err
|
||
})
|
||
})
|
||
windowsProfileUUID := uuid.NewString()
|
||
err := ds.BulkUpsertMDMWindowsHostProfiles(ctx, []*fleet.MDMWindowsBulkUpsertHostProfilePayload{{
|
||
ProfileUUID: windowsProfileUUID,
|
||
HostUUID: host.UUID,
|
||
Checksum: []byte("gibberish"),
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
}})
|
||
require.NoError(t, err)
|
||
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
_, err := q.ExecContext(ctx, `
|
||
INSERT INTO host_mdm_managed_certificates (host_uuid, profile_uuid) VALUES (?, ?)
|
||
`, host.UUID, windowsProfileUUID)
|
||
return err
|
||
})
|
||
|
||
// Validate record stays after cleanup
|
||
err = ds.CleanUpMDMManagedCertificates(ctx)
|
||
require.NoError(t, err)
|
||
var uid string
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
return sqlx.GetContext(ctx, q, &uid, `SELECT profile_uuid FROM host_mdm_managed_certificates WHERE profile_uuid = ?`,
|
||
windowsProfileUUID)
|
||
})
|
||
require.Equal(t, windowsProfileUUID, uid)
|
||
})
|
||
|
||
t.Run("valid apple record stays", func(t *testing.T) {
|
||
t.Cleanup(func() {
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
_, err := q.ExecContext(ctx, `
|
||
DELETE FROM host_mdm_managed_certificates;
|
||
DELETE FROM host_mdm_apple_profiles;
|
||
`)
|
||
return err
|
||
})
|
||
})
|
||
|
||
appleProfileUUID := uuid.NewString()
|
||
err := ds.BulkUpsertMDMAppleHostProfiles(ctx, []*fleet.MDMAppleBulkUpsertHostProfilePayload{{
|
||
ProfileUUID: appleProfileUUID,
|
||
HostUUID: host.UUID,
|
||
Checksum: []byte("gibberish"),
|
||
Scope: fleet.PayloadScopeSystem,
|
||
OperationType: fleet.MDMOperationTypeInstall,
|
||
}})
|
||
require.NoError(t, err)
|
||
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
_, err := q.ExecContext(ctx, `
|
||
INSERT INTO host_mdm_managed_certificates (host_uuid, profile_uuid) VALUES (?, ?)
|
||
`, host.UUID, appleProfileUUID)
|
||
return err
|
||
})
|
||
|
||
// Validate record stays after cleanup
|
||
err = ds.CleanUpMDMManagedCertificates(ctx)
|
||
require.NoError(t, err)
|
||
var uid string
|
||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||
return sqlx.GetContext(ctx, q, &uid, `SELECT profile_uuid FROM host_mdm_managed_certificates WHERE profile_uuid = ?`,
|
||
appleProfileUUID)
|
||
})
|
||
require.Equal(t, appleProfileUUID, uid)
|
||
})
|
||
}
|