API + auth + UI changes for team labels (#37208)

Covers #36760, #36758.

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [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.

- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)

## 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)

- [ ] QA'd all new/changed functionality manually
This commit is contained in:
Ian Littman
2025-12-29 21:28:45 -06:00
committed by GitHub
parent 04d2a98b50
commit 8e4e89f4e9
100 changed files with 1673 additions and 841 deletions
@@ -149,11 +149,11 @@ func (s *enterpriseIntegrationGitopsTestSuite) TearDownTest() {
return err
})
lbls, err := s.DS.ListLabels(ctx, fleet.TeamFilter{User: test.UserAdmin}, fleet.ListOptions{})
lbls, err := s.DS.ListLabels(ctx, fleet.TeamFilter{User: test.UserAdmin}, fleet.ListOptions{}, false)
require.NoError(t, err)
for _, lbl := range lbls {
if lbl.LabelType != fleet.LabelTypeBuiltIn {
err := s.DS.DeleteLabel(ctx, lbl.Name)
err := s.DS.DeleteLabel(ctx, lbl.Name, fleet.TeamFilter{User: test.UserAdmin})
require.NoError(t, err)
}
}
@@ -1602,7 +1602,7 @@ func (s *enterpriseIntegrationGitopsTestSuite) TestFleetGitOpsDeletesNonManagedL
_ = fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", opsFile})
// Check label was removed successfully
result, err := s.DS.LabelIDsByName(ctx, []string{nonManagedLabel.Name})
result, err := s.DS.LabelIDsByName(ctx, []string{nonManagedLabel.Name}, fleet.TeamFilter{})
require.NoError(t, err)
require.Empty(t, result)
}
@@ -1999,7 +1999,7 @@ labels:
s.assertRealRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name()}))
// Verify the label was created and has the correct hosts
labels, err := s.DS.LabelsByName(ctx, []string{"my-label"})
labels, err := s.DS.LabelsByName(ctx, []string{"my-label"}, fleet.TeamFilter{})
require.NoError(t, err)
require.Len(t, labels, 1)
label := labels["my-label"]
@@ -106,7 +106,7 @@ func TestGitOpsTeamSoftwareInstallers(t *testing.T) {
}, nil
}
ds.GetLabelSpecsFunc = func(ctx context.Context) ([]*fleet.LabelSpec, error) {
ds.GetLabelSpecsFunc = func(ctx context.Context, filter fleet.TeamFilter) ([]*fleet.LabelSpec, error) {
return []*fleet.LabelSpec{
{
Name: "a",
@@ -128,10 +128,10 @@ func TestGitOpsTeamSoftwareInstallers(t *testing.T) {
"a": 2,
"b": 3,
}
ds.LabelIDsByNameFunc = func(ctx context.Context, labels []string) (map[string]uint, error) {
ds.LabelIDsByNameFunc = func(ctx context.Context, names []string, filter fleet.TeamFilter) (map[string]uint, error) {
// for this test, recognize labels a and b (as well as the built-in macos 14+ one)
ret := make(map[string]uint)
for _, lbl := range labels {
for _, lbl := range names {
id, ok := labelToIDs[lbl]
if ok {
ret[lbl] = id
@@ -272,10 +272,10 @@ func TestGitOpsNoTeamVPPPolicies(t *testing.T) {
"a": 2,
"b": 3,
}
ds.LabelIDsByNameFunc = func(ctx context.Context, labels []string) (map[string]uint, error) {
ds.LabelIDsByNameFunc = func(ctx context.Context, names []string, filter fleet.TeamFilter) (map[string]uint, error) {
// for this test, recognize labels a and b (as well as the built-in macos 14+ one)
ret := make(map[string]uint)
for _, lbl := range labels {
for _, lbl := range names {
id, ok := labelToIDs[lbl]
if ok {
ret[lbl] = id
@@ -283,7 +283,7 @@ func TestGitOpsNoTeamVPPPolicies(t *testing.T) {
}
return ret, nil
}
ds.LabelsByNameFunc = func(ctx context.Context, names []string) (map[string]*fleet.Label, error) {
ds.LabelsByNameFunc = func(ctx context.Context, names []string, filter fleet.TeamFilter) (map[string]*fleet.Label, error) {
return map[string]*fleet.Label{
"a": {
ID: 1,
@@ -295,6 +295,9 @@ func TestGitOpsNoTeamVPPPolicies(t *testing.T) {
},
}, nil
}
ds.SetAsideLabelsFunc = func(ctx context.Context, notOnTeamID *uint, names []string, user fleet.User) error {
return nil
}
ds.GetSoftwareCategoryIDsFunc = func(ctx context.Context, names []string) ([]uint, error) {
return []uint{}, nil
}
@@ -387,7 +390,7 @@ func TestGitOpsNoTeamSoftwareInstallers(t *testing.T) {
Teams: nil,
}, nil
}
ds.GetLabelSpecsFunc = func(ctx context.Context) ([]*fleet.LabelSpec, error) {
ds.GetLabelSpecsFunc = func(ctx context.Context, filter fleet.TeamFilter) ([]*fleet.LabelSpec, error) {
return []*fleet.LabelSpec{
{
Name: "a",
@@ -403,15 +406,18 @@ func TestGitOpsNoTeamSoftwareInstallers(t *testing.T) {
},
}, nil
}
ds.SetAsideLabelsFunc = func(ctx context.Context, notOnTeamID *uint, names []string, user fleet.User) error {
return nil
}
labelToIDs := map[string]uint{
fleet.BuiltinLabelMacOS14Plus: 1,
"a": 2,
"b": 3,
}
ds.LabelIDsByNameFunc = func(ctx context.Context, labels []string) (map[string]uint, error) {
ds.LabelIDsByNameFunc = func(ctx context.Context, names []string, filter fleet.TeamFilter) (map[string]uint, error) {
// for this test, recognize labels a and b (as well as the built-in macos 14+ one)
ret := make(map[string]uint)
for _, lbl := range labels {
for _, lbl := range names {
id, ok := labelToIDs[lbl]
if ok {
ret[lbl] = id
@@ -522,7 +528,7 @@ func TestGitOpsTeamVPPApps(t *testing.T) {
}, nil
}
ds.GetLabelSpecsFunc = func(ctx context.Context) ([]*fleet.LabelSpec, error) {
ds.GetLabelSpecsFunc = func(ctx context.Context, filter fleet.TeamFilter) ([]*fleet.LabelSpec, error) {
return []*fleet.LabelSpec{
{
Name: "label 1",
@@ -543,15 +549,15 @@ func TestGitOpsTeamVPPApps(t *testing.T) {
}
found := make(map[string]uint)
ds.LabelIDsByNameFunc = func(ctx context.Context, labels []string) (map[string]uint, error) {
for _, l := range labels {
ds.LabelIDsByNameFunc = func(ctx context.Context, names []string, filter fleet.TeamFilter) (map[string]uint, error) {
for _, l := range names {
if id, ok := c.expectedLabels[l]; ok {
found[l] = id
}
}
return found, nil
}
ds.LabelsByNameFunc = func(ctx context.Context, names []string) (map[string]*fleet.Label, error) {
ds.LabelsByNameFunc = func(ctx context.Context, names []string, filter fleet.TeamFilter) (map[string]*fleet.Label, error) {
found2 := make(map[string]*fleet.Label)
for _, l := range names {
if id, ok := c.expectedLabels[l]; ok {