mirror of
https://github.com/fleetdm/fleet
synced 2026-05-24 09:28:54 +00:00
Fix bug in MDM command listing (#32992)
Fixes #32996 # Checklist for submitter - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files) for more information. ## Testing - [x] Added/updated automated tests - [x] QA'd all new/changed functionality manually <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * Bug Fixes * Corrected team filtering when listing MDM commands to ensure accurate results. Team-scoped and global commands now display correctly for users with appropriate access, resolving cases of missing or incorrect entries when filtering by team. * Tests * Added comprehensive coverage for team-scoped MDM command listings, role-based visibility (team users vs. admins), and hostname ordering to prevent regressions. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
parent
ed3e755641
commit
c3d73d26a6
3 changed files with 103 additions and 1 deletions
1
changes/32996-mdm-commands
Normal file
1
changes/32996-mdm-commands
Normal file
|
|
@ -0,0 +1 @@
|
|||
* Fixed 422 error when hitting `/api/v1/fleet/commands` endpoint with team filter.
|
||||
|
|
@ -99,7 +99,7 @@ func (ds *Datastore) ListMDMCommands(
|
|||
}
|
||||
|
||||
jointStmt, params := getCombinedMDMCommandsQuery(ds, listOpts.Filters.HostIdentifier)
|
||||
jointStmt += ds.whereFilterHostsByTeams(tmFilter, "h")
|
||||
jointStmt += ds.whereFilterHostsByTeams(tmFilter, "combined_commands")
|
||||
jointStmt, params = addRequestTypeFilter(jointStmt, &listOpts.Filters, params)
|
||||
jointStmt, params = appendListOptionsWithCursorToSQL(jointStmt, params, &listOpts.ListOptions)
|
||||
var results []*fleet.MDMCommand
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ func TestMDMShared(t *testing.T) {
|
|||
fn func(t *testing.T, ds *Datastore)
|
||||
}{
|
||||
{"TestMDMCommands", testMDMCommands},
|
||||
{"TestListMDMCommandsWithTeamFilter", testListMDMCommandsWithTeamFilter},
|
||||
{"TestBatchSetMDMProfiles", testBatchSetMDMProfiles},
|
||||
{"TestListMDMConfigProfiles", testListMDMConfigProfiles},
|
||||
{"TestBulkSetPendingMDMHostProfiles", testBulkSetPendingMDMHostProfiles},
|
||||
|
|
@ -386,6 +387,106 @@ func testMDMCommands(t *testing.T, ds *Datastore) {
|
|||
require.Equal(t, appleCmdUUID2, cmds[0].CommandUUID)
|
||||
}
|
||||
|
||||
// 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()
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue