mirror of
https://github.com/fleetdm/fleet
synced 2026-05-22 08:28:52 +00:00
feat: backend for VPP related global activities (#20484)
> Related issue: #19870 # Checklist for submitter If some of the following don't apply, delete the relevant line. <!-- Note that API documentation changes are now addressed by the product design team. --> - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://fleetdm.com/docs/contributing/committing-changes#changes-files) for more information. - [x] Added/updated tests - [x] Manual QA for all new/changed functionality
This commit is contained in:
parent
464c248f30
commit
5d2e40bc8b
7 changed files with 213 additions and 3 deletions
1
changes/19870-vpp-activities-backend
Normal file
1
changes/19870-vpp-activities-backend
Normal file
|
|
@ -0,0 +1 @@
|
|||
- Adds global activity support for VPP related activities.
|
||||
|
|
@ -1158,7 +1158,7 @@ Generated when a software installer is deleted from Fleet.
|
|||
This activity contains the following fields:
|
||||
- "software_title": Name of the software.
|
||||
- "software_package": Filename of the installer.
|
||||
- "team_name": Name of the team to which this software was added. `null if it was added to no team.
|
||||
- "team_name": Name of the team to which this software was added. `null` if it was added to no team.
|
||||
- "team_id": The ID of the team to which this software was added. `null` if it was added to no team.
|
||||
- "self_service": Whether the software was available for installation by the end user.
|
||||
|
||||
|
|
@ -1174,6 +1174,83 @@ This activity contains the following fields:
|
|||
}
|
||||
```
|
||||
|
||||
## vpp_enabled
|
||||
|
||||
Generated when the VPP feature is enabled in Fleet.
|
||||
|
||||
|
||||
|
||||
## vpp_disabled
|
||||
|
||||
Generated when the VPP feature is disabled in Fleet.
|
||||
|
||||
|
||||
|
||||
## added_app_store_app
|
||||
|
||||
Generated when an App Store app is added to Fleet.
|
||||
|
||||
This activity contains the following fields:
|
||||
- "software_title": Name of the App Store app.
|
||||
- "app_store_id": ID of the app on the Apple App Store.
|
||||
- "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.
|
||||
|
||||
#### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"software_title": "Logic Pro",
|
||||
"app_store_id": "1234567",
|
||||
"team_name": "Workstations",
|
||||
"team_id": 1
|
||||
}
|
||||
```
|
||||
|
||||
## deleted_app_store_app
|
||||
|
||||
Generated when an App Store app is deleted from Fleet.
|
||||
|
||||
This activity contains the following fields:
|
||||
- "software_title": Name of the App Store app.
|
||||
- "app_store_id": ID of the app on the Apple App Store.
|
||||
- "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.
|
||||
|
||||
#### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"software_title": "Logic Pro",
|
||||
"app_store_id": "1234567",
|
||||
"team_name": "Workstations",
|
||||
"team_id": 1
|
||||
}
|
||||
```
|
||||
|
||||
## installed_app_store_app
|
||||
|
||||
Generated when an App Store app is installed on a device.
|
||||
|
||||
This activity contains the following fields:
|
||||
- host_id: ID of the host on which the app was installed.
|
||||
- host_display_name: Display name of the host.
|
||||
- software_title: Name of the App Store app.
|
||||
- app_store_id: ID of the app on the Apple App Store.
|
||||
- command_uuid: UUID of the MDM command used to install the app.
|
||||
|
||||
#### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"host_id": 42,
|
||||
"host_display_name": "Anna's MacBook Pro",
|
||||
"software_title": "Logic Pro",
|
||||
"app_store_id": "1234567",
|
||||
"command_uuid": "98765432-1234-1234-1234-1234567890ab"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
<meta name="title" value="Audit logs">
|
||||
<meta name="pageOrderInSection" value="1400">
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/fleetdm/fleet/v4/server/authz"
|
||||
"github.com/fleetdm/fleet/v4/server/contexts/ctxerr"
|
||||
"github.com/fleetdm/fleet/v4/server/fleet"
|
||||
"github.com/fleetdm/fleet/v4/server/mdm/apple/itunes"
|
||||
|
|
@ -159,5 +160,15 @@ func (svc *Service) AddAppStoreApp(ctx context.Context, teamID *uint, adamID str
|
|||
return ctxerr.Wrap(ctx, err, "writing VPP app to db")
|
||||
}
|
||||
|
||||
act := fleet.ActivityAddedAppStoreApp{
|
||||
AppStoreID: app.AdamID,
|
||||
TeamName: &teamName,
|
||||
SoftwareTitle: app.Name,
|
||||
TeamID: teamID,
|
||||
}
|
||||
if err := svc.NewActivity(ctx, authz.UserFromContext(ctx), act); err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "create activity for add app store app")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,6 +99,11 @@ var ActivityDetailsList = []ActivityDetails{
|
|||
ActivityTypeInstalledSoftware{},
|
||||
ActivityTypeAddedSoftware{},
|
||||
ActivityTypeDeletedSoftware{},
|
||||
ActivityEnabledVPP{},
|
||||
ActivityDisabledVPP{},
|
||||
ActivityAddedAppStoreApp{},
|
||||
ActivityDeletedAppStoreApp{},
|
||||
ActivityInstalledAppStoreApp{},
|
||||
}
|
||||
|
||||
type ActivityDetails interface {
|
||||
|
|
@ -1507,7 +1512,7 @@ func (a ActivityTypeDeletedSoftware) Documentation() (string, string, string) {
|
|||
return `Generated when a software installer is deleted from Fleet.`, `This activity contains the following fields:
|
||||
- "software_title": Name of the software.
|
||||
- "software_package": Filename of the installer.
|
||||
- "team_name": Name of the team to which this software was added.` + " `null " + `if it was added to no team.
|
||||
- "team_name": Name of the team to which this software was added.` + " `null` " + `if it was added to no team.
|
||||
- "team_id": The ID of the team to which this software was added.` + " `null` " + `if it was added to no team.
|
||||
- "self_service": Whether the software was available for installation by the end user.`, `{
|
||||
"software_title": "Falcon.app",
|
||||
|
|
@ -1598,3 +1603,97 @@ func LogRoleChangeActivities(
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ActivityEnabledVPP struct{}
|
||||
|
||||
func (a ActivityEnabledVPP) ActivityName() string {
|
||||
return "vpp_enabled"
|
||||
}
|
||||
|
||||
func (a ActivityEnabledVPP) Documentation() (activity string, details string, detailsExample string) {
|
||||
return "Generated when the VPP feature is enabled in Fleet.", "", ""
|
||||
}
|
||||
|
||||
type ActivityDisabledVPP struct{}
|
||||
|
||||
func (a ActivityDisabledVPP) ActivityName() string {
|
||||
return "vpp_disabled"
|
||||
}
|
||||
|
||||
func (a ActivityDisabledVPP) Documentation() (activity string, details string, detailsExample string) {
|
||||
return "Generated when the VPP feature is disabled in Fleet.", "", ""
|
||||
}
|
||||
|
||||
type ActivityAddedAppStoreApp struct {
|
||||
SoftwareTitle string `json:"software_title"`
|
||||
AppStoreID string `json:"app_store_id"`
|
||||
TeamName *string `json:"team_name"`
|
||||
TeamID *uint `json:"team_id"`
|
||||
}
|
||||
|
||||
func (a ActivityAddedAppStoreApp) ActivityName() string {
|
||||
return "added_app_store_app"
|
||||
}
|
||||
|
||||
func (a ActivityAddedAppStoreApp) Documentation() (activity string, details string, detailsExample string) {
|
||||
return "Generated when an App Store app is added to Fleet.", `This activity contains the following fields:
|
||||
- "software_title": Name of the App Store app.
|
||||
- "app_store_id": ID of the app on the Apple App Store.
|
||||
- "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.`, `{
|
||||
"software_title": "Logic Pro",
|
||||
"app_store_id": "1234567",
|
||||
"team_name": "Workstations",
|
||||
"team_id": 1
|
||||
}`
|
||||
}
|
||||
|
||||
type ActivityDeletedAppStoreApp struct {
|
||||
SoftwareTitle string `json:"software_title"`
|
||||
AppStoreID string `json:"app_store_id"`
|
||||
TeamName string `json:"team_name"`
|
||||
}
|
||||
|
||||
func (a ActivityDeletedAppStoreApp) ActivityName() string {
|
||||
return "deleted_app_store_app"
|
||||
}
|
||||
|
||||
func (a ActivityDeletedAppStoreApp) Documentation() (activity string, details string, detailsExample string) {
|
||||
return "Generated when an App Store app is deleted from Fleet.", `This activity contains the following fields:
|
||||
- "software_title": Name of the App Store app.
|
||||
- "app_store_id": ID of the app on the Apple App Store.
|
||||
- "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.`, `{
|
||||
"software_title": "Logic Pro",
|
||||
"app_store_id": "1234567",
|
||||
"team_name": "Workstations",
|
||||
"team_id": 1
|
||||
}`
|
||||
}
|
||||
|
||||
type ActivityInstalledAppStoreApp struct {
|
||||
HostID int `json:"host_id"`
|
||||
HostDisplayName string `json:"host_display_name"`
|
||||
SoftwareTitle string `json:"software_title"`
|
||||
AppStoreID int `json:"app_store_id"`
|
||||
CommandUUID string `json:"command_uuid"`
|
||||
}
|
||||
|
||||
func (a ActivityInstalledAppStoreApp) ActivityName() string {
|
||||
return "installed_app_store_app"
|
||||
}
|
||||
|
||||
func (a ActivityInstalledAppStoreApp) Documentation() (string, string, string) {
|
||||
return "Generated when an App Store app is installed on a device.", `This activity contains the following fields:
|
||||
- host_id: ID of the host on which the app was installed.
|
||||
- host_display_name: Display name of the host.
|
||||
- software_title: Name of the App Store app.
|
||||
- app_store_id: ID of the app on the Apple App Store.
|
||||
- command_uuid: UUID of the MDM command used to install the app.`, `{
|
||||
"host_id": 42,
|
||||
"host_display_name": "Anna's MacBook Pro",
|
||||
"software_title": "Logic Pro",
|
||||
"app_store_id": "1234567",
|
||||
"command_uuid": "98765432-1234-1234-1234-1234567890ab"
|
||||
}`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10702,6 +10702,8 @@ func (s *integrationMDMTestSuite) TestVPPApps() {
|
|||
t.Setenv("FLEET_DEV_VPP_URL", s.appleVPPConfigSrv.URL)
|
||||
s.uploadDataViaForm("/api/latest/fleet/mdm/apple/vpp_token", "token", "token.vpptoken", []byte(base64.StdEncoding.EncodeToString([]byte(tokenJSON))), http.StatusAccepted, "")
|
||||
|
||||
s.lastActivityMatches(fleet.ActivityEnabledVPP{}.ActivityName(), "", 0)
|
||||
|
||||
// Get the token
|
||||
var resp getMDMAppleVPPTokenResponse
|
||||
s.DoJSON("GET", "/api/latest/fleet/vpp", &getMDMAppleVPPTokenRequest{}, http.StatusOK, &resp)
|
||||
|
|
@ -10755,6 +10757,7 @@ func (s *integrationMDMTestSuite) TestVPPApps() {
|
|||
// Add an app store app to team 1
|
||||
var addAppResp addAppStoreAppResponse
|
||||
s.DoJSON("POST", "/api/latest/fleet/software/app_store_apps", &addAppStoreAppRequest{TeamID: &team.ID, AppStoreID: appResp.AppStoreApps[0].AdamID}, http.StatusOK, &addAppResp)
|
||||
s.lastActivityMatches(fleet.ActivityAddedAppStoreApp{}.ActivityName(), fmt.Sprintf(`{"team_name": "%s", "software_title": "%s", "app_store_id": "%s", "team_id": %d}`, team.Name, appResp.AppStoreApps[0].Name, appResp.AppStoreApps[0].AdamID, team.ID), 0)
|
||||
|
||||
// Add an app store app to non-existent team
|
||||
s.DoJSON("POST", "/api/latest/fleet/software/app_store_apps", &addAppStoreAppRequest{TeamID: ptr.Uint(9999), AppStoreID: appResp.AppStoreApps[0].AdamID}, http.StatusNotFound, &addAppResp)
|
||||
|
|
@ -10777,6 +10780,7 @@ func (s *integrationMDMTestSuite) TestVPPApps() {
|
|||
// Delete VPP token and check that it's not appearing anymore
|
||||
s.Do("DELETE", "/api/latest/fleet/mdm/apple/vpp_token", &deleteMDMAppleVPPTokenRequest{}, http.StatusNoContent)
|
||||
s.DoJSON("GET", "/api/latest/fleet/vpp", &getMDMAppleVPPTokenRequest{}, http.StatusNotFound, &resp)
|
||||
s.lastActivityMatches(fleet.ActivityDisabledVPP{}.ActivityName(), "", 0)
|
||||
}
|
||||
|
||||
// 1. software title uploaded doesn't match existing title
|
||||
|
|
|
|||
|
|
@ -2646,6 +2646,11 @@ func (svc *Service) UploadMDMAppleVPPToken(ctx context.Context, token io.ReadSee
|
|||
return ctxerr.Wrap(ctx, err, "writing VPP token to db")
|
||||
}
|
||||
|
||||
act := fleet.ActivityEnabledVPP{}
|
||||
if err := svc.NewActivity(ctx, authz.UserFromContext(ctx), act); err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "create activity for upload VPP token")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -2729,5 +2734,14 @@ func (svc *Service) DeleteMDMAppleVPPToken(ctx context.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
return svc.ds.DeleteMDMConfigAssetsByName(ctx, []fleet.MDMAssetName{fleet.MDMAssetVPPToken})
|
||||
if err := svc.ds.DeleteMDMConfigAssetsByName(ctx, []fleet.MDMAssetName{fleet.MDMAssetVPPToken}); err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "delete VPP token")
|
||||
}
|
||||
|
||||
act := fleet.ActivityDisabledVPP{}
|
||||
if err := svc.NewActivity(ctx, authz.UserFromContext(ctx), act); err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "create activity for delete VPP token")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,6 +119,10 @@ func TestMDMAppleAuthorization(t *testing.T) {
|
|||
return nil
|
||||
}
|
||||
|
||||
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails, details []byte, createdAt time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
ds.DeleteMDMConfigAssetsByNameFunc = func(ctx context.Context, assetNames []fleet.MDMAssetName) error { return nil }
|
||||
|
||||
// use a custom implementation of checkAuthErr as the service call will fail
|
||||
|
|
|
|||
Loading…
Reference in a new issue