@@ -2472,23 +2472,32 @@ func (ds *Datastore) GetWindowsHostMDMCertificateProfile(ctx context.Context, ho
|
||||
return &profile, nil
|
||||
}
|
||||
|
||||
func (ds *Datastore) GetWindowsMDMCommandsForResending(ctx context.Context, failedCommandIds []string) ([]*fleet.MDMWindowsCommand, error) {
|
||||
func (ds *Datastore) GetWindowsMDMCommandsForResending(ctx context.Context, deviceID string, failedCommandIds []string) ([]*fleet.MDMWindowsCommand, error) {
|
||||
if len(failedCommandIds) == 0 {
|
||||
return []*fleet.MDMWindowsCommand{}, nil
|
||||
}
|
||||
|
||||
stmt := `SELECT command_uuid, raw_command, target_loc_uri, created_at, updated_at
|
||||
FROM windows_mdm_commands WHERE`
|
||||
stmt := `SELECT wmc.command_uuid, wmc.raw_command, wmc.target_loc_uri, wmc.created_at, wmc.updated_at
|
||||
FROM windows_mdm_commands wmc INNER JOIN windows_mdm_command_queue wmcq ON wmcq.enrollment_id = (SELECT id from mdm_windows_enrollments WHERE mdm_device_id = ?) AND wmcq.command_uuid = wmc.command_uuid WHERE`
|
||||
|
||||
args := []any{}
|
||||
args := []any{deviceID}
|
||||
for idx, commandId := range failedCommandIds {
|
||||
stmt += " raw_command LIKE ? OR "
|
||||
args = append(args, "%"+commandId+"%")
|
||||
if commandId == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
stmt += " wmc.raw_command LIKE ? OR "
|
||||
args = append(args, "%<CmdID>"+commandId+"</CmdID>%")
|
||||
if idx == len(failedCommandIds)-1 {
|
||||
stmt = strings.TrimSuffix(stmt, " OR ")
|
||||
}
|
||||
}
|
||||
|
||||
if len(args) == 1 {
|
||||
// No valid command IDs were provided, return empty result to avoid returning all commands for the device.
|
||||
return []*fleet.MDMWindowsCommand{}, nil
|
||||
}
|
||||
|
||||
stmt += fmt.Sprintf(" ORDER BY created_at DESC LIMIT %d", len(failedCommandIds))
|
||||
|
||||
var commands []*fleet.MDMWindowsCommand
|
||||
|
||||
@@ -3702,27 +3702,29 @@ func testGetWindowsMDMCommandsForResending(t *testing.T, ds *Datastore) {
|
||||
dev := createMDMWindowsEnrollment(ctx, t, ds)
|
||||
|
||||
// No commands in windows_mdm_commands so doesn't matter what we put in
|
||||
commands, err := ds.GetWindowsMDMCommandsForResending(ctx, []string{cmdUUID})
|
||||
commands, err := ds.GetWindowsMDMCommandsForResending(ctx, dev.MDMDeviceID, []string{cmdUUID})
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, commands)
|
||||
|
||||
// Insert a command
|
||||
rawCommand := []byte(cmdUUID)
|
||||
rawCommand := fmt.Appendf(nil, "<CmdID>%s</CmdID>", cmdUUID)
|
||||
err = ds.mdmWindowsInsertCommandForHostsDB(ctx, ds.writer(ctx), []string{dev.HostUUID}, &fleet.MDMWindowsCommand{
|
||||
CommandUUID: topLevelCmdUUID,
|
||||
RawCommand: rawCommand,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
//
|
||||
|
||||
// Fetch command for resending
|
||||
commands, err = ds.GetWindowsMDMCommandsForResending(ctx, []string{cmdUUID})
|
||||
commands, err = ds.GetWindowsMDMCommandsForResending(ctx, dev.MDMDeviceID, []string{cmdUUID})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, commands, 1)
|
||||
assert.Equal(t, topLevelCmdUUID, commands[0].CommandUUID)
|
||||
assert.Equal(t, rawCommand, commands[0].RawCommand)
|
||||
|
||||
// Check that we search raw body and not match on command_uuid
|
||||
commands, err = ds.GetWindowsMDMCommandsForResending(ctx, []string{topLevelCmdUUID})
|
||||
commands, err = ds.GetWindowsMDMCommandsForResending(ctx, dev.MDMDeviceID, []string{topLevelCmdUUID})
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, commands)
|
||||
}
|
||||
|
||||
@@ -2682,7 +2682,7 @@ type Datastore interface {
|
||||
// and need to be resent based on their command IDs.
|
||||
//
|
||||
// Returns a slice of MDMWindowsCommand pointers containing the commands to be resent.
|
||||
GetWindowsMDMCommandsForResending(ctx context.Context, failedCommandIds []string) ([]*MDMWindowsCommand, error)
|
||||
GetWindowsMDMCommandsForResending(ctx context.Context, deviceID string, failedCommandIds []string) ([]*MDMWindowsCommand, error)
|
||||
|
||||
// ResendWindowsMDMCommand marks the specified Windows MDM command for resend
|
||||
// by inserting a new command entry, command queue, but also updates the host profile reference.
|
||||
|
||||
@@ -1765,7 +1765,7 @@ type GetCurrentTimeFunc func(ctx context.Context) (time.Time, error)
|
||||
|
||||
type UpdateOrDeleteHostMDMWindowsProfileFunc func(ctx context.Context, profile *fleet.HostMDMWindowsProfile) error
|
||||
|
||||
type GetWindowsMDMCommandsForResendingFunc func(ctx context.Context, failedCommandIds []string) ([]*fleet.MDMWindowsCommand, error)
|
||||
type GetWindowsMDMCommandsForResendingFunc func(ctx context.Context, deviceID string, failedCommandIds []string) ([]*fleet.MDMWindowsCommand, error)
|
||||
|
||||
type ResendWindowsMDMCommandFunc func(ctx context.Context, mdmDeviceId string, newCmd *fleet.MDMWindowsCommand, oldCmd *fleet.MDMWindowsCommand) error
|
||||
|
||||
@@ -10509,11 +10509,11 @@ func (s *DataStore) UpdateOrDeleteHostMDMWindowsProfile(ctx context.Context, pro
|
||||
return s.UpdateOrDeleteHostMDMWindowsProfileFunc(ctx, profile)
|
||||
}
|
||||
|
||||
func (s *DataStore) GetWindowsMDMCommandsForResending(ctx context.Context, failedCommandIds []string) ([]*fleet.MDMWindowsCommand, error) {
|
||||
func (s *DataStore) GetWindowsMDMCommandsForResending(ctx context.Context, deviceID string, failedCommandIds []string) ([]*fleet.MDMWindowsCommand, error) {
|
||||
s.mu.Lock()
|
||||
s.GetWindowsMDMCommandsForResendingFuncInvoked = true
|
||||
s.mu.Unlock()
|
||||
return s.GetWindowsMDMCommandsForResendingFunc(ctx, failedCommandIds)
|
||||
return s.GetWindowsMDMCommandsForResendingFunc(ctx, deviceID, failedCommandIds)
|
||||
}
|
||||
|
||||
func (s *DataStore) ResendWindowsMDMCommand(ctx context.Context, mdmDeviceId string, newCmd *fleet.MDMWindowsCommand, oldCmd *fleet.MDMWindowsCommand) error {
|
||||
|
||||
@@ -8692,6 +8692,49 @@ func (s *integrationMDMTestSuite) TestWindowsProfileRetry() {
|
||||
require.NoError(t, err)
|
||||
require.Len(t, cmds, 1) // only ack returned
|
||||
})
|
||||
|
||||
t.Run("Other hosts can not get all commands", func(t *testing.T) {
|
||||
// Let's insert a command for the original host, with some random raw_command data
|
||||
commandUUID := uuid.NewString()
|
||||
mysql.ExecAdhocSQL(t, s.ds, func(q sqlx.ExtContext) error {
|
||||
commandData := `<Add><!-- CmdID generated by Fleet --><CmdID>` + commandUUID + `</CmdID><Item><Target><LocURI>./BogusLocURI</LocURI></Target><Meta><Type xmlns="syncml:metinf">text/plain</Type><Format xmlns="syncml:metinf">bool</Format></Meta><Data>true</Data></Item></Add>`
|
||||
_, err := q.ExecContext(ctx, `INSERT INTO windows_mdm_commands (command_uuid, raw_command, target_loc_uri) VALUES (?, ?, '')`, commandUUID, commandData)
|
||||
require.NoError(t, err)
|
||||
return nil
|
||||
})
|
||||
|
||||
// Create another host and enroll in MDM
|
||||
_, mdmDevice2 := createWindowsHostThenEnrollMDM(s.ds, s.server.URL, t)
|
||||
|
||||
// Start connection, and then reply with a missing CmdRef
|
||||
cmds, err := mdmDevice2.StartManagementSession()
|
||||
require.NoError(t, err)
|
||||
msgID, err := mdmDevice2.GetCurrentMsgID()
|
||||
require.NoError(t, err)
|
||||
for _, cmd := range cmds {
|
||||
if cmd.Verb == "Status" {
|
||||
continue
|
||||
}
|
||||
}
|
||||
syncCmd := fleet.SyncMLCmd{
|
||||
XMLName: xml.Name{Local: fleet.CmdStatus},
|
||||
MsgRef: &msgID,
|
||||
Cmd: ptr.String("Add"),
|
||||
CmdRef: ptr.String(""),
|
||||
Data: ptr.String(syncml.CmdStatusAlreadyExists),
|
||||
CmdID: fleet.CmdID{Value: uuid.NewString()},
|
||||
}
|
||||
mdmDevice2.AppendResponse(syncCmd)
|
||||
cmds, err = mdmDevice2.SendResponse()
|
||||
require.NoError(t, err)
|
||||
require.Len(t, cmds, 1) // Only 1 status message
|
||||
|
||||
for _, cmd := range cmds {
|
||||
if cmd.Verb != "Status" {
|
||||
t.Errorf("Expected only Status command, got %s", cmd.Verb)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (s *integrationMDMTestSuite) TestHostMDMAndroidProfilesStatus() {
|
||||
|
||||
@@ -1743,7 +1743,7 @@ func (svc *Service) processIncomingMDMCmds(ctx context.Context, deviceID string,
|
||||
|
||||
// Iterate over the operations and process them
|
||||
for _, protoCMD := range reqMsg.GetOrderedCmds() {
|
||||
if protoCMD.Cmd.Data != nil && *protoCMD.Cmd.Data == "418" {
|
||||
if protoCMD.Cmd.Data != nil && *protoCMD.Cmd.Data == "418" && protoCMD.Cmd.CmdRef != nil {
|
||||
// 418 = Already exists, and indicate that an <Add> failed due to the item already existing on the device
|
||||
// We need to re-issue a <Replace> command for this item
|
||||
alreadyExistsCmdIDs = append(alreadyExistsCmdIDs, *protoCMD.Cmd.CmdRef)
|
||||
@@ -1782,7 +1782,7 @@ func (svc *Service) processIncomingMDMCmds(ctx context.Context, deviceID string,
|
||||
}
|
||||
|
||||
func handleResendingAlreadyExistsCommands(ctx context.Context, svc *Service, alreadyExistsCmdIDs []string, deviceID string) ([]string, error) {
|
||||
commands, err := svc.ds.GetWindowsMDMCommandsForResending(ctx, alreadyExistsCmdIDs)
|
||||
commands, err := svc.ds.GetWindowsMDMCommandsForResending(ctx, deviceID, alreadyExistsCmdIDs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get commands for resending: %w", err)
|
||||
}
|
||||
|
||||
@@ -1044,7 +1044,7 @@ func TestRekeyWindowsDevice(t *testing.T) {
|
||||
ds.MDMWindowsGetPendingCommandsFunc = func(ctx context.Context, deviceID string) ([]*fleet.MDMWindowsCommand, error) {
|
||||
return []*fleet.MDMWindowsCommand{}, nil
|
||||
}
|
||||
ds.GetWindowsMDMCommandsForResendingFunc = func(ctx context.Context, failedCommandIds []string) ([]*fleet.MDMWindowsCommand, error) {
|
||||
ds.GetWindowsMDMCommandsForResendingFunc = func(ctx context.Context, deviceID string, failedCommandIds []string) ([]*fleet.MDMWindowsCommand, error) {
|
||||
return []*fleet.MDMWindowsCommand{}, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user