Skip to content

Commit

Permalink
Add labels and editing for VPP apps (#25979)
Browse files Browse the repository at this point in the history
For #24609

---------

Co-authored-by: Jahziel Villasana-Espinoza <[email protected]>
Co-authored-by: Jahziel Villasana-Espinoza <[email protected]>
  • Loading branch information
3 people authored Feb 3, 2025
1 parent f0cd0a6 commit d38d180
Show file tree
Hide file tree
Showing 31 changed files with 1,856 additions and 196 deletions.
38 changes: 30 additions & 8 deletions cmd/fleetctl/gitops_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2200,15 +2200,21 @@ func TestGitOpsTeamVPPApps(t *testing.T) {
file string
wantErr string
tokenExpiration time.Time
expectedLabels map[string]uint
}{
{"testdata/gitops/team_vpp_valid_app.yml", "", time.Now().Add(24 * time.Hour)},
{"testdata/gitops/team_vpp_valid_app_self_service.yml", "", time.Now().Add(24 * time.Hour)},
{"testdata/gitops/team_vpp_valid_empty.yml", "", time.Now().Add(24 * time.Hour)},
{"testdata/gitops/team_vpp_valid_empty.yml", "", time.Now().Add(-24 * time.Hour)},
{"testdata/gitops/team_vpp_valid_app.yml", "VPP token expired", time.Now().Add(-24 * time.Hour)},
{"testdata/gitops/team_vpp_invalid_app.yml", "app not available on vpp account", time.Now().Add(24 * time.Hour)},
{"testdata/gitops/team_vpp_incorrect_type.yml", "\"app_store_apps.app_store_id\" must be a string, found number", time.Now().Add(24 * time.Hour)},
{"testdata/gitops/team_vpp_empty_adamid.yml", "software app store id required", time.Now().Add(24 * time.Hour)},
{"testdata/gitops/team_vpp_valid_app.yml", "", time.Now().Add(24 * time.Hour), map[string]uint{}},
{"testdata/gitops/team_vpp_valid_app_self_service.yml", "", time.Now().Add(24 * time.Hour), map[string]uint{}},
{"testdata/gitops/team_vpp_valid_empty.yml", "", time.Now().Add(24 * time.Hour), map[string]uint{}},
{"testdata/gitops/team_vpp_valid_empty.yml", "", time.Now().Add(-24 * time.Hour), map[string]uint{}},
{"testdata/gitops/team_vpp_valid_app.yml", "VPP token expired", time.Now().Add(-24 * time.Hour), map[string]uint{}},
{"testdata/gitops/team_vpp_invalid_app.yml", "app not available on vpp account", time.Now().Add(24 * time.Hour), map[string]uint{}},
{"testdata/gitops/team_vpp_incorrect_type.yml", "\"app_store_apps.app_store_id\" must be a string, found number", time.Now().Add(24 * time.Hour), map[string]uint{}},
{"testdata/gitops/team_vpp_empty_adamid.yml", "software app store id required", time.Now().Add(24 * time.Hour), map[string]uint{}},
{"testdata/gitops/team_vpp_valid_app_labels_exclude_any.yml", "", time.Now().Add(24 * time.Hour), map[string]uint{"label 1": 1, "label 2": 2}},
{"testdata/gitops/team_vpp_valid_app_labels_include_any.yml", "", time.Now().Add(24 * time.Hour), map[string]uint{"label 1": 1, "label 2": 2}},
{"testdata/gitops/team_vpp_invalid_app_labels_exclude_any.yml", "some or all the labels provided don't exist", time.Now().Add(24 * time.Hour), map[string]uint{"label 1": 1, "label 2": 2}},
{"testdata/gitops/team_vpp_invalid_app_labels_include_any.yml", "some or all the labels provided don't exist", time.Now().Add(24 * time.Hour), map[string]uint{"label 1": 1, "label 2": 2}},
{"testdata/gitops/team_vpp_invalid_app_labels_both.yml", `only one of "labels_exclude_any" or "labels_include_any" can be specified for app store app`, time.Now().Add(24 * time.Hour), map[string]uint{}},
}

for _, c := range cases {
Expand Down Expand Up @@ -2238,9 +2244,25 @@ func TestGitOpsTeamVPPApps(t *testing.T) {
}, nil
}

found := make(map[string]uint)
ds.LabelIDsByNameFunc = func(ctx context.Context, labels []string) (map[string]uint, error) {
for _, l := range labels {
if id, ok := c.expectedLabels[l]; ok {
found[l] = id
}
}
return found, nil
}

_, err = runAppNoChecks([]string{"gitops", "-f", c.file})

if c.wantErr == "" {
require.NoError(t, err)
if len(c.expectedLabels) > 0 {
require.True(t, ds.LabelIDsByNameFuncInvoked)
}

require.Equal(t, c.expectedLabels, found)
} else {
require.ErrorContains(t, err, c.wantErr)
}
Expand Down
21 changes: 21 additions & 0 deletions cmd/fleetctl/testdata/gitops/team_vpp_invalid_app_labels_both.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: "${TEST_TEAM_NAME}"
team_settings:
secrets:
- secret: "ABC"
features:
enable_host_users: true
enable_software_inventory: true
host_expiry_settings:
host_expiry_enabled: true
host_expiry_window: 30
agent_options:
controls:
policies:
queries:
software:
app_store_apps:
- app_store_id: "1"
labels_exclude_any:
- "label 1"
labels_include_any:
- "label 2"
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: "${TEST_TEAM_NAME}"
team_settings:
secrets:
- secret: "ABC"
features:
enable_host_users: true
enable_software_inventory: true
host_expiry_settings:
host_expiry_enabled: true
host_expiry_window: 30
agent_options:
controls:
policies:
queries:
software:
app_store_apps:
- app_store_id: "1"
labels_exclude_any:
- "label non-existent"
- "label 2"
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: "${TEST_TEAM_NAME}"
team_settings:
secrets:
- secret: "ABC"
features:
enable_host_users: true
enable_software_inventory: true
host_expiry_settings:
host_expiry_enabled: true
host_expiry_window: 30
agent_options:
controls:
policies:
queries:
software:
app_store_apps:
- app_store_id: "1"
labels_include_any:
- "label non-existent"
- "label 2"
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: "${TEST_TEAM_NAME}"
team_settings:
secrets:
- secret: "ABC"
features:
enable_host_users: true
enable_software_inventory: true
host_expiry_settings:
host_expiry_enabled: true
host_expiry_window: 30
agent_options:
controls:
policies:
queries:
software:
app_store_apps:
- app_store_id: "1"
labels_exclude_any:
- "label 1"
- "label 2"
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: "${TEST_TEAM_NAME}"
team_settings:
secrets:
- secret: "ABC"
features:
enable_host_users: true
enable_software_inventory: true
host_expiry_settings:
host_expiry_enabled: true
host_expiry_window: 30
agent_options:
controls:
policies:
queries:
software:
app_store_apps:
- app_store_id: "1"
labels_include_any:
- "label 1"
- "label 2"
67 changes: 65 additions & 2 deletions docs/Contributing/Audit-logs.md
Original file line number Diff line number Diff line change
Expand Up @@ -1382,6 +1382,8 @@ This activity contains the following fields:
- "self_service": App installation can be initiated by device owner.
- "team_name": Name of the team to which this App Store app was added, or `null` if it was added to no team.
- "team_id": ID of the team to which this App Store app was added, or `null`if it was added to no team.
- "labels_include_any": Target hosts that have any label in the array.
- "labels_exclude_any": Target hosts that don't have any label in the array.

#### Example

Expand All @@ -1393,7 +1395,17 @@ This activity contains the following fields:
"platform": "darwin",
"self_service": false,
"team_name": "Workstations",
"team_id": 1
"team_id": 1,
"labels_include_any": [
{
"name": "Engineering",
"id": 12
},
{
"name": "Product",
"id": 17
}
]
}
```

Expand All @@ -1407,6 +1419,8 @@ This activity contains the following fields:
- "platform": Platform of the app (`darwin`, `ios`, or `ipados`).
- "team_name": Name of the team from which this App Store app was deleted, or `null` if it was deleted from no team.
- "team_id": ID of the team from which this App Store app was deleted, or `null`if it was deleted from no team.
- "labels_include_any": Target hosts that have any label in the array.
- "labels_exclude_any": Target hosts that don't have any label in the array

#### Example

Expand All @@ -1416,7 +1430,17 @@ This activity contains the following fields:
"app_store_id": "1234567",
"platform": "darwin",
"team_name": "Workstations",
"team_id": 1
"team_id": 1,
"labels_include_any": [
{
"name": "Engineering",
"id": 12
},
{
"name": "Product",
"id": 17
}
]
}
```

Expand Down Expand Up @@ -1450,6 +1474,45 @@ This activity contains the following fields:
}
```

## edited_app_store_app

Generated when an App Store app is updated in Fleet.

This activity contains the following fields:
- "software_title": Name of the App Store app.
- "software_title_id": ID of the updated app's software title.
- "app_store_id": ID of the app on the Apple App Store.
- "platform": Platform of the app (`darwin`, `ios`, or `ipados`).
- "self_service": App installation can be initiated by device owner.
- "team_name": Name of the team on which this App Store app was updated, or `null` if it was updated on no team.
- "team_id": ID of the team on which this App Store app was updated, or `null`if it was updated on no team.
- "labels_include_any": Target hosts that have any label in the array.
- "labels_exclude_any": Target hosts that don't have any label in the array.

#### Example

```json
{
"software_title": "Logic Pro",
"software_title_id": 123,
"app_store_id": "1234567",
"platform": "darwin",
"self_service": true,
"team_name": "Workstations",
"team_id": 1,
"labels_include_any": [
{
"name": "Engineering",
"id": 12
},
{
"name": "Product",
"id": 17
}
]
}
```

## added_ndes_scep_proxy

Generated when NDES SCEP proxy is configured in Fleet.
Expand Down
39 changes: 33 additions & 6 deletions ee/server/service/software_installers.go
Original file line number Diff line number Diff line change
Expand Up @@ -638,12 +638,16 @@ func (svc *Service) deleteVPPApp(ctx context.Context, teamID *uint, meta *fleet.
teamName = &t.Name
}

actLabelsIncl, actLabelsExcl := activitySoftwareLabelsFromSoftwareScopeLabels(meta.LabelsIncludeAny, meta.LabelsExcludeAny)

if err := svc.NewActivity(ctx, vc.User, fleet.ActivityDeletedAppStoreApp{
AppStoreID: meta.AdamID,
SoftwareTitle: meta.Name,
TeamName: teamName,
TeamID: teamID,
Platform: meta.Platform,
AppStoreID: meta.AdamID,
SoftwareTitle: meta.Name,
TeamName: teamName,
TeamID: teamID,
Platform: meta.Platform,
LabelsIncludeAny: actLabelsIncl,
LabelsExcludeAny: actLabelsExcl,
}); err != nil {
return ctxerr.Wrap(ctx, err, "creating activity for deleted VPP app")
}
Expand Down Expand Up @@ -968,7 +972,7 @@ func (svc *Service) InstallSoftwareTitle(ctx context.Context, hostID uint, softw
if lastInstallRequest != nil && lastInstallRequest.Status != nil &&
(*lastInstallRequest.Status == fleet.SoftwareInstallPending || *lastInstallRequest.Status == fleet.SoftwareUninstallPending) {
return &fleet.BadRequestError{
Message: "Couldn't install software. Host has a pending install/uninstall request.",
Message: "Couldn't install. This host isn't a member of the labels defined for this software title.",
InternalErr: ctxerr.WrapWithData(
ctx, err, "host already has a pending install/uninstall for this installer",
map[string]any{
Expand Down Expand Up @@ -1001,6 +1005,18 @@ func (svc *Service) InstallSoftwareTitle(ctx context.Context, hostID uint, softw
return ctxerr.Wrap(ctx, err, "finding VPP app for title")
}

// check the label scoping for this VPP app and host
scoped, err := svc.ds.IsVPPAppLabelScoped(ctx, vppApp.VPPAppTeam.AppTeamID, hostID)
if err != nil {
return ctxerr.Wrap(ctx, err, "checking label scoping during vpp software install attempt")
}

if !scoped {
return &fleet.BadRequestError{
Message: "Couldn't install. This host isn't a member of the labels defined for this software title.",
}
}

_, err = svc.installSoftwareFromVPP(ctx, host, vppApp, mobileAppleDevice || fleet.AppleDevicePlatform(platform) == fleet.MacOSPlatform, false)
return err
}
Expand Down Expand Up @@ -1849,6 +1865,17 @@ func (svc *Service) SelfServiceInstallSoftwareTitle(ctx context.Context, host *f
}
}

scoped, err := svc.ds.IsVPPAppLabelScoped(ctx, vppApp.VPPAppTeam.AppTeamID, host.ID)
if err != nil {
return ctxerr.Wrap(ctx, err, "checking vpp label scoping during software install attempt")
}

if !scoped {
return &fleet.BadRequestError{
Message: "Couldn't install. This software is not available for this host.",
}
}

platform := host.FleetPlatform()
mobileAppleDevice := fleet.AppleDevicePlatform(platform) == fleet.IOSPlatform || fleet.AppleDevicePlatform(platform) == fleet.IPadOSPlatform

Expand Down
Loading

0 comments on commit d38d180

Please sign in to comment.