fleet/server/datastore/mysql/vpp_test.go
Jonathan Katz 20230a688f
Android Setup Experience Gitops (#37468)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #35554

- Setup experience is generated to and can be set in the GitOps yaml
- No changes to policy creation, setup experience apps are still added
as `PREINSTALLED`
- API change: `GET /fleet/setup_experience/software` modified to be able
to take a comma separated list of platforms, like `GET
/fleet/setup_experience/software` does. Documentation update will be in
another PR.
- Modified `SetTeamVPPApps` to return if setup experience changed so the
function that calls it can create a "setup experience changed" activity.

# Checklist for submitter

## Testing

- [x] Added/updated automated tests
- [ ] 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)

- [x] QA'd all new/changed functionality manually
- Used generate-gitops to create a yaml file, edited setup experience
apps with it to test that it applies and creates activities correctly.
- Re-enrolled an Android phone after editing setup experience with
GitOps, all setup experience apps were installed.
2025-12-19 10:45:27 -05:00

2637 lines
95 KiB
Go

package mysql
import (
"context"
"encoding/hex"
"encoding/json"
"fmt"
"testing"
"time"
"github.com/fleetdm/fleet/v4/server/contexts/viewer"
"github.com/fleetdm/fleet/v4/server/fleet"
"github.com/fleetdm/fleet/v4/server/mdm/nanomdm/mdm"
nanomdm_mysql "github.com/fleetdm/fleet/v4/server/mdm/nanomdm/storage/mysql"
"github.com/fleetdm/fleet/v4/server/ptr"
"github.com/fleetdm/fleet/v4/server/test"
"github.com/google/uuid"
"github.com/jmoiron/sqlx"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestVPP(t *testing.T) {
ds := CreateMySQLDS(t)
cases := []struct {
name string
fn func(t *testing.T, ds *Datastore)
}{
{"SetTeamVPPApps", testSetTeamVPPApps},
{"SetTeamVPPAppsWithLabels", testSetTeamVPPAppsWithLabels},
{"VPPAppMetadata", testVPPAppMetadata},
{"VPPAppStatus", testVPPAppStatus},
{"VPPApps", testVPPApps},
{"GetVPPAppByTeamAndTitleID", testGetVPPAppByTeamAndTitleID},
{"VPPTokensCRUD", testVPPTokensCRUD},
{"VPPTokenAppTeamAssociations", testVPPTokenAppTeamAssociations},
{"VPPTokenReassignTeamsToAllTeams", testVPPTokenReassignTeamsToAllTeams},
{"GetOrInsertSoftwareTitleForVPPApp", testGetOrInsertSoftwareTitleForVPPApp},
{"DeleteVPPAssignedToPolicy", testDeleteVPPAssignedToPolicy},
{"TestVPPTokenTeamAssignment", testVPPTokenTeamAssignment},
{"TestGetAllVPPApps", testGetAllVPPApps},
{"TestGetUnverifiedVPPInstallsForHost", testGetUnverifiedVPPInstallsForHost},
{"SoftwareTitleDisplayName", testSoftwareTitleDisplayNameVPP},
{"AndroidVPPAppStatus", testAndroidVPPAppStatus},
{"GetVPPAppInstallStatusByCommandUUID", testGetVPPAppInstallStatusByCommandUUID},
{"AndroidAppConfigs", testAndroidAppConfigs},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
t.Helper()
defer TruncateTables(t, ds)
c.fn(t, ds)
})
}
}
func testVPPAppMetadata(t *testing.T, ds *Datastore) {
ctx := context.Background()
// create teams
team1, err := ds.NewTeam(ctx, &fleet.Team{Name: "team 1"})
require.NoError(t, err)
require.NotNil(t, team1)
team2, err := ds.NewTeam(ctx, &fleet.Team{Name: "team 2"})
require.NoError(t, err)
require.NotNil(t, team2)
test.CreateInsertGlobalVPPToken(t, ds)
// get for non-existing title
meta, err := ds.GetVPPAppMetadataByTeamAndTitleID(ctx, nil, 1)
require.Error(t, err)
var nfe fleet.NotFoundError
require.ErrorAs(t, err, &nfe)
require.Nil(t, meta)
// create no-team app
va1, err := ds.InsertVPPAppWithTeam(ctx, &fleet.VPPApp{
Name: "vpp1", BundleIdentifier: "com.app.vpp1",
VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "adam_vpp_app_1", Platform: fleet.MacOSPlatform}, SelfService: true},
}, nil)
require.NoError(t, err)
vpp1, titleID1 := va1.VPPAppID, va1.TitleID
// get no-team app
meta, err = ds.GetVPPAppMetadataByTeamAndTitleID(ctx, nil, titleID1)
require.NoError(t, err)
require.NotZero(t, meta.VPPAppsTeamsID)
meta.VPPAppsTeamsID = 0 // we don't care about the VPP app team PK for comparison purposes
meta.AddedAt = time.Time{} // we don't care about AddedAt here
require.Equal(t, &fleet.VPPAppStoreApp{Name: "vpp1", VPPAppID: vpp1, BundleIdentifier: "com.app.vpp1", SelfService: true}, meta)
// Check that getting metadata in team context works for no team
_, err = ds.GetVPPAppMetadataByAdamIDPlatformTeamID(ctx, "foo", meta.Platform, nil)
require.ErrorContains(t, err, "not found")
gotMeta, err := ds.GetVPPAppMetadataByAdamIDPlatformTeamID(ctx, meta.AdamID, meta.Platform, nil)
require.NoError(t, err)
require.Equal(t, "com.app.vpp1", gotMeta.BundleIdentifier)
require.Equal(t, "vpp1", gotMeta.Name)
require.Equal(t, titleID1, gotMeta.TitleID)
require.Equal(t, va1.VPPAppTeam.AppTeamID, gotMeta.VPPAppTeam.AppTeamID)
require.Equal(t, fleet.VPPAppID{Platform: fleet.MacOSPlatform, AdamID: meta.AdamID}, gotMeta.VPPAppTeam.VPPAppID)
require.True(t, gotMeta.SelfService)
// try to add the same app again, update self_service field
_, err = ds.InsertVPPAppWithTeam(ctx, &fleet.VPPApp{
Name: "vpp1", BundleIdentifier: "com.app.vpp1",
VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "adam_vpp_app_1", Platform: fleet.MacOSPlatform}, SelfService: true},
}, nil)
require.NoError(t, err)
// get no-team app
meta, err = ds.GetVPPAppMetadataByTeamAndTitleID(ctx, ptr.Uint(0), titleID1)
require.NoError(t, err)
title, err := ds.GetTitleInfoFromVPPAppsTeamsID(ctx, meta.VPPAppsTeamsID)
require.NoError(t, err)
require.Equal(t, titleID1, title.SoftwareTitleID)
meta.VPPAppsTeamsID = 0
meta.AddedAt = time.Time{} // we don't care about AddedAt here
require.Equal(t, &fleet.VPPAppStoreApp{Name: "vpp1", VPPAppID: vpp1, SelfService: true, BundleIdentifier: "com.app.vpp1"}, meta)
// get nonexistent title
_, err = ds.GetTitleInfoFromVPPAppsTeamsID(ctx, 0)
require.Error(t, err)
// create team1 app
va2, err := ds.InsertVPPAppWithTeam(ctx, &fleet.VPPApp{
Name: "vpp2", BundleIdentifier: "com.app.vpp2",
VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "adam_vpp_app_2", Platform: fleet.MacOSPlatform}},
}, &team1.ID)
require.NoError(t, err)
vpp2, titleID2 := va2.VPPAppID, va2.TitleID
// get it for team 1
meta, err = ds.GetVPPAppMetadataByTeamAndTitleID(ctx, &team1.ID, titleID2)
require.NoError(t, err)
meta.VPPAppsTeamsID = 0
meta.AddedAt = time.Time{}
require.Equal(t, &fleet.VPPAppStoreApp{Name: "vpp2", VPPAppID: vpp2, BundleIdentifier: "com.app.vpp2"}, meta)
// get it for all teams
meta, err = ds.GetVPPAppMetadataByTeamAndTitleID(ctx, nil, titleID2)
require.NoError(t, err)
meta.VPPAppsTeamsID = 0
meta.AddedAt = time.Time{}
require.Equal(t, &fleet.VPPAppStoreApp{Name: "vpp2", VPPAppID: vpp2, BundleIdentifier: "com.app.vpp2"}, meta)
// try to add the same app again, fails
_, err = ds.InsertVPPAppWithTeam(ctx, &fleet.VPPApp{
Name: "vpp2", BundleIdentifier: "com.app.vpp2",
VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "adam_vpp_app_2", Platform: fleet.MacOSPlatform}, SelfService: true},
}, &team1.ID)
require.NoError(t, err)
// get it for team 1
meta, err = ds.GetVPPAppMetadataByTeamAndTitleID(ctx, &team1.ID, titleID2)
require.NoError(t, err)
meta.VPPAppsTeamsID = 0
meta.AddedAt = time.Time{}
require.Equal(t, &fleet.VPPAppStoreApp{Name: "vpp2", VPPAppID: vpp2, SelfService: true, BundleIdentifier: "com.app.vpp2"}, meta)
// get it for team 2, does not exist
meta, err = ds.GetVPPAppMetadataByTeamAndTitleID(ctx, &team2.ID, titleID2)
require.Error(t, err)
require.ErrorAs(t, err, &nfe)
require.Nil(t, meta)
// create the same app for team2
_, err = ds.InsertVPPAppWithTeam(ctx, &fleet.VPPApp{
Name: "vpp2", BundleIdentifier: "com.app.vpp2",
VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "adam_vpp_app_2", Platform: fleet.MacOSPlatform}, SelfService: true},
}, &team2.ID)
require.NoError(t, err)
// get it for team 1 and team 2, both work
meta, err = ds.GetVPPAppMetadataByTeamAndTitleID(ctx, &team1.ID, titleID2)
require.NoError(t, err)
meta.VPPAppsTeamsID = 0 // we don't care about the VPP app team PK
meta.AddedAt = time.Time{}
require.Equal(t, &fleet.VPPAppStoreApp{Name: "vpp2", VPPAppID: vpp2, SelfService: true, BundleIdentifier: "com.app.vpp2"}, meta)
meta, err = ds.GetVPPAppMetadataByTeamAndTitleID(ctx, &team2.ID, titleID2)
require.NoError(t, err)
meta.VPPAppsTeamsID = 0
meta.AddedAt = time.Time{}
require.Equal(t, &fleet.VPPAppStoreApp{Name: "vpp2", VPPAppID: vpp2, BundleIdentifier: "com.app.vpp2", SelfService: true}, meta)
// create another no-team app
va3, err := ds.InsertVPPAppWithTeam(ctx, &fleet.VPPApp{
Name: "vpp3", BundleIdentifier: "com.app.vpp3",
VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "adam_vpp_app_3", Platform: fleet.MacOSPlatform}},
}, nil)
require.NoError(t, err)
vpp3, titleID3 := va3.VPPAppID, va3.TitleID
// get it for team 2, does not exist
meta, err = ds.GetVPPAppMetadataByTeamAndTitleID(ctx, &team2.ID, titleID3)
require.Error(t, err)
require.ErrorAs(t, err, &nfe)
require.Nil(t, meta)
// get it for no-team
meta, err = ds.GetVPPAppMetadataByTeamAndTitleID(ctx, nil, titleID3)
require.NoError(t, err)
meta.VPPAppsTeamsID = 0
meta.AddedAt = time.Time{}
require.Equal(t, &fleet.VPPAppStoreApp{Name: "vpp3", VPPAppID: vpp3, BundleIdentifier: "com.app.vpp3"}, meta)
// delete vpp1
err = ds.DeleteVPPAppFromTeam(ctx, nil, vpp1)
require.NoError(t, err)
// it is now not found
_, err = ds.GetVPPAppMetadataByTeamAndTitleID(ctx, nil, titleID1)
require.Error(t, err)
require.ErrorAs(t, err, &nfe)
// vpp3 (also in no team) is left untouched
meta, err = ds.GetVPPAppMetadataByTeamAndTitleID(ctx, nil, titleID3)
require.NoError(t, err)
meta.VPPAppsTeamsID = 0 // we don't care about the VPP app team PK
meta.AddedAt = time.Time{}
require.Equal(t, &fleet.VPPAppStoreApp{Name: "vpp3", VPPAppID: vpp3, BundleIdentifier: "com.app.vpp3"}, meta)
// delete vpp2 for team1
err = ds.DeleteVPPAppFromTeam(ctx, &team1.ID, vpp2)
require.NoError(t, err)
// it is now not found for team1
_, err = ds.GetVPPAppMetadataByTeamAndTitleID(ctx, &team1.ID, titleID2)
require.Error(t, err)
require.ErrorAs(t, err, &nfe)
// but still found for team2
meta, err = ds.GetVPPAppMetadataByTeamAndTitleID(ctx, &team2.ID, titleID2)
require.NoError(t, err)
expectedVPPAppsTeamsID := meta.VPPAppsTeamsID
meta.VPPAppsTeamsID = 0 // we don't care about the VPP app team PK
meta.AddedAt = time.Time{}
require.Equal(t, &fleet.VPPAppStoreApp{Name: "vpp2", VPPAppID: vpp2, BundleIdentifier: "com.app.vpp2", SelfService: true}, meta)
// Check that getting metadata in team context works
_, err = ds.GetVPPAppMetadataByAdamIDPlatformTeamID(ctx, "foo", meta.Platform, &team2.ID)
require.ErrorContains(t, err, "not found")
gotMeta, err = ds.GetVPPAppMetadataByAdamIDPlatformTeamID(ctx, meta.AdamID, meta.Platform, &team2.ID)
require.NoError(t, err)
require.Equal(t, "com.app.vpp2", gotMeta.BundleIdentifier)
require.Equal(t, "vpp2", gotMeta.Name)
require.Equal(t, titleID2, gotMeta.TitleID)
require.Equal(t, expectedVPPAppsTeamsID, gotMeta.VPPAppTeam.AppTeamID)
require.Equal(t, fleet.VPPAppID{Platform: fleet.MacOSPlatform, AdamID: meta.AdamID}, gotMeta.VPPAppTeam.VPPAppID)
require.True(t, gotMeta.SelfService)
// mark it as install_during_setup for team 2
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
_, err := q.ExecContext(ctx, `UPDATE vpp_apps_teams SET install_during_setup = 1 WHERE global_or_team_id = ? AND adam_id = ?`, team2.ID, vpp2.AdamID)
return err
})
// this prevents its deletion
err = ds.DeleteVPPAppFromTeam(ctx, &team2.ID, vpp2)
require.Error(t, err)
require.ErrorIs(t, err, errDeleteInstallerInstalledDuringSetup)
// delete vpp1 again fails, not found
err = ds.DeleteVPPAppFromTeam(ctx, nil, vpp1)
require.Error(t, err)
require.ErrorAs(t, err, &nfe)
// delete the software title
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
_, err := q.ExecContext(ctx, "DELETE FROM software_titles WHERE id = ?", titleID3)
return err
})
// cannot be returned anymore (deleting the title breaks the relationship)
meta, err = ds.GetVPPAppMetadataByTeamAndTitleID(ctx, nil, titleID3)
require.Error(t, err)
require.ErrorAs(t, err, &nfe)
require.Nil(t, meta)
}
func testVPPAppStatus(t *testing.T, ds *Datastore) {
ctx := context.Background()
// create a user
user, err := ds.NewUser(ctx, &fleet.User{
Password: []byte("p4ssw0rd.123"),
Name: "user1",
Email: "user1@example.com",
GlobalRole: ptr.String(fleet.RoleAdmin),
})
require.NoError(t, err)
// create a team
team1, err := ds.NewTeam(ctx, &fleet.Team{Name: "team 1"})
require.NoError(t, err)
require.NotNil(t, team1)
test.CreateInsertGlobalVPPToken(t, ds)
// create some apps, one for no-team, one for team1, and one in both
va1, err := ds.InsertVPPAppWithTeam(ctx, &fleet.VPPApp{
Name: "vpp1", BundleIdentifier: "com.app.vpp1",
VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "adam_vpp_app_1", Platform: fleet.MacOSPlatform}},
}, nil)
require.NoError(t, err)
vpp1 := va1.VPPAppID
va2, err := ds.InsertVPPAppWithTeam(ctx, &fleet.VPPApp{
Name: "vpp2", BundleIdentifier: "com.app.vpp2",
VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "adam_vpp_app_2", Platform: fleet.MacOSPlatform}},
}, &team1.ID)
require.NoError(t, err)
vpp2 := va2.VPPAppID
va3, err := ds.InsertVPPAppWithTeam(ctx, &fleet.VPPApp{
Name: "vpp3", BundleIdentifier: "com.app.vpp3",
VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "adam_vpp_app_3", Platform: fleet.MacOSPlatform}},
}, nil)
require.NoError(t, err)
vpp3 := va3.VPPAppID
_, err = ds.InsertVPPAppWithTeam(ctx, &fleet.VPPApp{
Name: "vpp3", BundleIdentifier: "com.app.vpp3",
VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "adam_vpp_app_3", Platform: fleet.MacOSPlatform}},
}, &team1.ID)
require.NoError(t, err)
// for now they all return zeroes
summary, err := ds.GetSummaryHostVPPAppInstalls(ctx, nil, vpp1)
require.NoError(t, err)
require.Equal(t, &fleet.VPPAppStatusSummary{Pending: 0, Failed: 0, Installed: 0}, summary)
summary, err = ds.GetSummaryHostVPPAppInstalls(ctx, &team1.ID, vpp2)
require.NoError(t, err)
require.Equal(t, &fleet.VPPAppStatusSummary{Pending: 0, Failed: 0, Installed: 0}, summary)
summary, err = ds.GetSummaryHostVPPAppInstalls(ctx, nil, vpp3)
require.NoError(t, err)
require.Equal(t, &fleet.VPPAppStatusSummary{Pending: 0, Failed: 0, Installed: 0}, summary)
// create a few enrolled hosts
h1, err := ds.NewHost(ctx, &fleet.Host{
Hostname: "macos-test-1",
OsqueryHostID: ptr.String("osquery-macos-1"),
NodeKey: ptr.String("node-key-macos-1"),
UUID: uuid.NewString(),
Platform: "darwin",
HardwareSerial: "654321a",
})
require.NoError(t, err)
nanoEnroll(t, ds, h1, false)
h2, err := ds.NewHost(ctx, &fleet.Host{
Hostname: "macos-test-2",
OsqueryHostID: ptr.String("osquery-macos-2"),
NodeKey: ptr.String("node-key-macos-2"),
UUID: uuid.NewString(),
Platform: "darwin",
HardwareSerial: "654321b",
})
require.NoError(t, err)
nanoEnroll(t, ds, h2, false)
h3, err := ds.NewHost(ctx, &fleet.Host{
Hostname: "macos-test-3",
OsqueryHostID: ptr.String("osquery-macos-3"),
NodeKey: ptr.String("node-key-macos-3"),
UUID: uuid.NewString(),
Platform: "darwin",
HardwareSerial: "654321c",
})
require.NoError(t, err)
nanoEnroll(t, ds, h3, false)
// move h3 to team1
err = ds.AddHostsToTeam(ctx, fleet.NewAddHostsToTeamParams(&team1.ID, []uint{h3.ID}))
require.NoError(t, err)
// simulate an install request of vpp1 on h1
cmd1 := createVPPAppInstallRequest(t, ds, h1, vpp1.AdamID, user)
summary, err = ds.GetSummaryHostVPPAppInstalls(ctx, nil, vpp1)
require.NoError(t, err)
require.Equal(t, &fleet.VPPAppStatusSummary{Pending: 1, Failed: 0, Installed: 0}, summary)
// record a failed result
createVPPAppInstallResult(t, ds, h1, cmd1, fleet.MDMAppleStatusError)
summary, err = ds.GetSummaryHostVPPAppInstalls(ctx, nil, vpp1)
require.NoError(t, err)
require.Equal(t, &fleet.VPPAppStatusSummary{Pending: 0, Failed: 1, Installed: 0}, summary)
// create a new request for h1 that supercedes the failed on, and a request
// for h2 with a successful result.
cmd2 := createVPPAppInstallRequest(t, ds, h1, vpp1.AdamID, user)
cmd3 := createVPPAppInstallRequest(t, ds, h2, vpp1.AdamID, user)
createVPPAppInstallResult(t, ds, h2, cmd3, fleet.MDMAppleStatusAcknowledged)
actUser, act, err := ds.GetPastActivityDataForVPPAppInstall(ctx, &mdm.CommandResults{CommandUUID: cmd3})
require.NoError(t, err)
require.Equal(t, user.ID, actUser.ID)
require.Equal(t, user.Name, actUser.Name)
require.Equal(t, cmd3, act.CommandUUID)
require.False(t, act.SelfService)
// both are pending because h2 is not verified yet
summary, err = ds.GetSummaryHostVPPAppInstalls(ctx, nil, vpp1)
require.NoError(t, err)
require.Equal(t, &fleet.VPPAppStatusSummary{Pending: 2, Failed: 0, Installed: 0}, summary)
// mark it as verified
err = ds.SetVPPInstallAsVerified(ctx, h2.ID, cmd3, uuid.NewString())
require.NoError(t, err)
// h2 is now installed
summary, err = ds.GetSummaryHostVPPAppInstalls(ctx, nil, vpp1)
require.NoError(t, err)
require.Equal(t, &fleet.VPPAppStatusSummary{Pending: 1, Failed: 0, Installed: 1}, summary)
// mark the pending request as successful and verified too
createVPPAppInstallResult(t, ds, h1, cmd2, fleet.MDMAppleStatusAcknowledged)
err = ds.SetVPPInstallAsVerified(ctx, h1.ID, cmd2, uuid.NewString())
require.NoError(t, err)
summary, err = ds.GetSummaryHostVPPAppInstalls(ctx, nil, vpp1)
require.NoError(t, err)
require.Equal(t, &fleet.VPPAppStatusSummary{Pending: 0, Failed: 0, Installed: 2}, summary)
// requesting for a team (the VPP app is not on any team) returns all zeroes
summary, err = ds.GetSummaryHostVPPAppInstalls(ctx, &team1.ID, vpp1)
require.NoError(t, err)
require.Equal(t, &fleet.VPPAppStatusSummary{Pending: 0, Failed: 0, Installed: 0}, summary)
// simulate a successful but unverified request for team app vpp2 on h3
cmd4 := createVPPAppInstallRequest(t, ds, h3, vpp2.AdamID, user)
createVPPAppInstallResult(t, ds, h3, cmd4, fleet.MDMAppleStatusAcknowledged)
summary, err = ds.GetSummaryHostVPPAppInstalls(ctx, &team1.ID, vpp2)
require.NoError(t, err)
require.Equal(t, &fleet.VPPAppStatusSummary{Pending: 1, Failed: 0, Installed: 0}, summary)
// verify it as failed
err = ds.SetVPPInstallAsFailed(ctx, h3.ID, cmd4, uuid.NewString())
require.NoError(t, err)
summary, err = ds.GetSummaryHostVPPAppInstalls(ctx, &team1.ID, vpp2)
require.NoError(t, err)
require.Equal(t, &fleet.VPPAppStatusSummary{Pending: 0, Failed: 1, Installed: 0}, summary)
// simulate a successful, failed and pending request for app vpp3 on team
// (h3) and no team (h1, h2)
cmd5 := createVPPAppInstallRequest(t, ds, h3, vpp3.AdamID, user)
createVPPAppInstallResult(t, ds, h3, cmd5, fleet.MDMAppleStatusAcknowledged)
err = ds.SetVPPInstallAsVerified(ctx, h3.ID, cmd5, uuid.NewString())
require.NoError(t, err)
cmd6 := createVPPAppInstallRequest(t, ds, h1, vpp3.AdamID, user)
createVPPAppInstallResult(t, ds, h1, cmd6, fleet.MDMAppleStatusCommandFormatError)
createVPPAppInstallRequest(t, ds, h2, vpp3.AdamID, user)
// for no team, it sees the failed and pending counts
summary, err = ds.GetSummaryHostVPPAppInstalls(ctx, nil, vpp3)
require.NoError(t, err)
require.Equal(t, &fleet.VPPAppStatusSummary{Pending: 1, Failed: 1, Installed: 0}, summary)
// for the team, it sees the successful count
summary, err = ds.GetSummaryHostVPPAppInstalls(ctx, &team1.ID, vpp3)
require.NoError(t, err)
require.Equal(t, &fleet.VPPAppStatusSummary{Pending: 0, Failed: 0, Installed: 1}, summary)
// simulate a self-service request
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
_, err := q.ExecContext(ctx,
`UPDATE host_vpp_software_installs SET self_service = true, user_id = NULL WHERE command_uuid = ?`,
cmd3)
return err
})
actUser, act, err = ds.GetPastActivityDataForVPPAppInstall(ctx, &mdm.CommandResults{CommandUUID: cmd3})
require.NoError(t, err)
require.Nil(t, actUser)
require.Equal(t, cmd3, act.CommandUUID)
require.True(t, act.SelfService)
}
// simulates creating the VPP app install request on the host, returns the command UUID.
func createVPPAppInstallRequest(t *testing.T, ds *Datastore, host *fleet.Host, adamID string, user *fleet.User) string {
ctx := context.Background()
ctx = viewer.NewContext(ctx, viewer.Viewer{User: user})
cmdUUID := uuid.NewString()
eventID := uuid.NewString()
err := ds.InsertHostVPPSoftwareInstall(ctx, host.ID, fleet.VPPAppID{
AdamID: adamID,
Platform: fleet.InstallableDevicePlatform(host.Platform),
}, cmdUUID, eventID, fleet.HostSoftwareInstallOptions{})
require.NoError(t, err)
return cmdUUID
}
func createVPPAppInstallResult(t *testing.T, ds *Datastore, host *fleet.Host, cmdUUID string, status string) {
ctx := context.Background()
ctx = context.WithValue(ctx, fleet.ActivityWebhookContextKey, true)
nanoDB, err := nanomdm_mysql.New(nanomdm_mysql.WithDB(ds.primary.DB))
require.NoError(t, err)
nanoCtx := &mdm.Request{EnrollID: &mdm.EnrollID{ID: host.UUID}, Context: ctx}
cmdRes := &mdm.CommandResults{
CommandUUID: cmdUUID,
Status: status,
Raw: []byte(`<?xml version="1.0" encoding="UTF-8"?>`),
}
err = nanoDB.StoreCommandReport(nanoCtx, cmdRes)
require.NoError(t, err)
// inserting the activity is what marks the upcoming activity as completed
// (and activates the next one).
err = ds.NewActivity(ctx, nil, fleet.ActivityInstalledAppStoreApp{
HostID: host.ID,
CommandUUID: cmdUUID,
}, []byte(`{}`), time.Now())
require.NoError(t, err)
}
func testVPPApps(t *testing.T, ds *Datastore) {
ctx := context.Background()
// Create a couple of teams
team, err := ds.NewTeam(ctx, &fleet.Team{Name: "foobar"})
require.NoError(t, err)
test.CreateInsertGlobalVPPToken(t, ds)
t.Run("vpp apps with labels", func(t *testing.T) {
teamWithLabels, err := ds.NewTeam(ctx, &fleet.Team{Name: "labels" + t.Name()})
require.NoError(t, err)
// Create some labels
label1, err := ds.NewLabel(ctx, &fleet.Label{
Name: "label1" + t.Name(),
Description: "a label",
Query: "select 1 from processes;",
Platform: "darwin",
})
require.NoError(t, err)
label2, err := ds.NewLabel(ctx, &fleet.Label{
Name: "label2" + t.Name(),
Description: "a label",
Query: "select 2 from processes;",
Platform: "darwin",
})
require.NoError(t, err)
// insert a VPP app with include_any labels
labeledApp := &fleet.VPPApp{
Name: "vpp_app_labels_1" + t.Name(),
VPPAppTeam: fleet.VPPAppTeam{
VPPAppID: fleet.VPPAppID{AdamID: "5", Platform: fleet.MacOSPlatform},
ValidatedLabels: &fleet.LabelIdentsWithScope{
LabelScope: fleet.LabelScopeIncludeAny,
ByName: map[string]fleet.LabelIdent{label1.Name: {
LabelID: label1.ID,
LabelName: label1.Name,
}, label2.Name: {LabelID: label2.ID, LabelName: label2.Name}},
},
},
BundleIdentifier: "b5",
}
_, err = ds.InsertVPPAppWithTeam(ctx, labeledApp, &teamWithLabels.ID)
require.NoError(t, err)
meta, err := ds.GetVPPAppMetadataByTeamAndTitleID(ctx, &teamWithLabels.ID, labeledApp.TitleID)
require.NoError(t, err)
require.Len(t, meta.LabelsIncludeAny, 2)
require.Len(t, meta.LabelsExcludeAny, 0)
// insert a VPP app with exclude_any labels
labeledApp = &fleet.VPPApp{
Name: "vpp_app_labels_2" + t.Name(),
VPPAppTeam: fleet.VPPAppTeam{
VPPAppID: fleet.VPPAppID{AdamID: "6", Platform: fleet.MacOSPlatform},
ValidatedLabels: &fleet.LabelIdentsWithScope{
LabelScope: fleet.LabelScopeExcludeAny,
ByName: map[string]fleet.LabelIdent{label1.Name: {
LabelID: label1.ID,
LabelName: label1.Name,
}, label2.Name: {LabelID: label2.ID, LabelName: label2.Name}},
},
},
BundleIdentifier: "b6",
}
_, err = ds.InsertVPPAppWithTeam(ctx, labeledApp, &teamWithLabels.ID)
require.NoError(t, err)
meta, err = ds.GetVPPAppMetadataByTeamAndTitleID(ctx, &teamWithLabels.ID, labeledApp.TitleID)
require.NoError(t, err)
require.Len(t, meta.LabelsIncludeAny, 0)
require.Len(t, meta.LabelsExcludeAny, 2)
})
// create a host with some non-VPP software
h1, err := ds.NewHost(ctx, &fleet.Host{
Hostname: "macos-test-1",
OsqueryHostID: ptr.String("osquery-macos-1"),
NodeKey: ptr.String("node-key-macos-1"),
UUID: uuid.NewString(),
Platform: "darwin",
HardwareSerial: "654321a",
})
require.NoError(t, err)
nanoEnrollAndSetHostMDMData(t, ds, h1, false)
software := []fleet.Software{
{Name: "foo", Version: "0.0.1", BundleIdentifier: "b1"},
{Name: "foo", Version: "0.0.2", BundleIdentifier: "b1"},
{Name: "bar", Version: "0.0.3", BundleIdentifier: "bar"},
}
_, err = ds.UpdateHostSoftware(ctx, h1.ID, software)
require.NoError(t, err)
// Insert some VPP apps for the team, "vpp_app_1" should match the existing "foo" title
app1 := &fleet.VPPApp{Name: "vpp_app_1", VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "1", Platform: fleet.MacOSPlatform}}, BundleIdentifier: "b1"}
app2 := &fleet.VPPApp{Name: "vpp_app_2", VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "2", Platform: fleet.MacOSPlatform}}, BundleIdentifier: "b2"}
_, err = ds.InsertVPPAppWithTeam(ctx, app1, &team.ID)
require.NoError(t, err)
_, err = ds.InsertVPPAppWithTeam(ctx, app2, &team.ID)
require.NoError(t, err)
// Insert some VPP apps for no team
appNoTeam1 := &fleet.VPPApp{
Name: "vpp_no_team_app_1", VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "3", Platform: fleet.MacOSPlatform}},
BundleIdentifier: "b3",
}
appNoTeam2 := &fleet.VPPApp{
Name: "vpp_no_team_app_2", VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "4", Platform: fleet.MacOSPlatform}},
BundleIdentifier: "b4",
}
_, err = ds.InsertVPPAppWithTeam(ctx, appNoTeam1, nil)
require.NoError(t, err)
_, err = ds.InsertVPPAppWithTeam(ctx, appNoTeam2, nil)
require.NoError(t, err)
// Check that inserting pending vpp installs works
u, err := ds.NewUser(ctx, &fleet.User{
Password: []byte("p4ssw0rd.123"),
Name: "user1",
Email: "user1@example.com",
GlobalRole: ptr.String(fleet.RoleAdmin),
})
require.NoError(t, err)
ctx = viewer.NewContext(ctx, viewer.Viewer{User: u})
err = ds.InsertHostVPPSoftwareInstall(ctx, h1.ID, app1.VPPAppID, "a", "b", fleet.HostSoftwareInstallOptions{})
require.NoError(t, err)
// non-existing host
err = ds.InsertHostVPPSoftwareInstall(ctx, h1.ID+1, app2.VPPAppID, "c", "d", fleet.HostSoftwareInstallOptions{SelfService: true})
require.Error(t, err)
var nfe fleet.NotFoundError
require.ErrorAs(t, err, &nfe)
// create host 2
h2, err := ds.NewHost(ctx, &fleet.Host{
Hostname: "macos-test-2",
OsqueryHostID: ptr.String("osquery-macos-2"),
NodeKey: ptr.String("node-key-macos-2"),
UUID: uuid.NewString(),
Platform: "darwin",
HardwareSerial: "654321b",
})
require.NoError(t, err)
nanoEnrollAndSetHostMDMData(t, ds, h2, false)
err = ds.InsertHostVPPSoftwareInstall(ctx, h2.ID, app2.VPPAppID, "c", "d", fleet.HostSoftwareInstallOptions{SelfService: true})
require.NoError(t, err)
acts, _, err := ds.ListHostUpcomingActivities(ctx, h1.ID, fleet.ListOptions{})
require.NoError(t, err)
require.Len(t, acts, 1)
require.NotNil(t, acts[0].ActorFullName)
require.Equal(t, u.Name, *acts[0].ActorFullName)
// app1 software title because it matched an existing software "foo" by bundle identifier
require.JSONEq(t, fmt.Sprintf(`{
"app_store_id":"%s",
"command_uuid":"a",
"host_display_name":"%s",
"host_id":%d,
"host_platform":"darwin",
"self_service":false,
"software_title":"foo",
"status":"pending_install"
}`, app1.AdamID, h1.DisplayName(), h1.ID), string(*acts[0].Details))
acts, _, err = ds.ListHostUpcomingActivities(ctx, h2.ID, fleet.ListOptions{})
require.NoError(t, err)
require.Len(t, acts, 1)
require.NotNil(t, acts[0].ActorFullName)
require.Equal(t, u.Name, *acts[0].ActorFullName)
require.JSONEq(t, fmt.Sprintf(`{
"app_store_id":"%s",
"command_uuid":"c",
"host_display_name":"%s",
"host_id":%d,
"host_platform":"darwin",
"self_service":true,
"software_title":"vpp_app_2",
"status":"pending_install"
}`, app2.AdamID, h2.DisplayName(), h2.ID), string(*acts[0].Details))
// Check that getting the assigned apps works
appSet, err := ds.GetAssignedVPPApps(ctx, &team.ID)
require.NoError(t, err)
meta, err := ds.GetVPPAppMetadataByAdamIDPlatformTeamID(ctx, app1.AdamID, app1.Platform, &team.ID)
require.NoError(t, err)
appTeamID1 := meta.AppTeamID
meta, err = ds.GetVPPAppMetadataByAdamIDPlatformTeamID(ctx, app2.AdamID, app2.Platform, &team.ID)
require.NoError(t, err)
appTeamID2 := meta.AppTeamID
assert.Equal(t, map[fleet.VPPAppID]fleet.VPPAppTeam{
app1.VPPAppID: {VPPAppID: app1.VPPAppID, InstallDuringSetup: ptr.Bool(false), AppTeamID: appTeamID1, AddedAt: appSet[app1.VPPAppID].AddedAt},
app2.VPPAppID: {VPPAppID: app2.VPPAppID, InstallDuringSetup: ptr.Bool(false), AppTeamID: appTeamID2, AddedAt: appSet[app2.VPPAppID].AddedAt},
}, appSet)
appSet, err = ds.GetAssignedVPPApps(ctx, nil)
require.NoError(t, err)
meta, err = ds.GetVPPAppMetadataByAdamIDPlatformTeamID(ctx, appNoTeam1.AdamID, app1.Platform, nil)
require.NoError(t, err)
appTeamID1 = meta.AppTeamID
meta, err = ds.GetVPPAppMetadataByAdamIDPlatformTeamID(ctx, appNoTeam2.AdamID, app2.Platform, nil)
require.NoError(t, err)
appTeamID2 = meta.AppTeamID
require.NoError(t, err)
assert.Equal(t, map[fleet.VPPAppID]fleet.VPPAppTeam{
appNoTeam1.VPPAppID: {VPPAppID: appNoTeam1.VPPAppID, InstallDuringSetup: ptr.Bool(false), AppTeamID: appTeamID1, AddedAt: appSet[appNoTeam1.VPPAppID].AddedAt},
appNoTeam2.VPPAppID: {VPPAppID: appNoTeam2.VPPAppID, InstallDuringSetup: ptr.Bool(false), AppTeamID: appTeamID2, AddedAt: appSet[appNoTeam2.VPPAppID].AddedAt},
}, appSet)
var appTitles []fleet.SoftwareTitle
err = sqlx.SelectContext(ctx, ds.reader(ctx), &appTitles, `SELECT name, bundle_identifier FROM software_titles WHERE bundle_identifier IN (?,?) ORDER BY bundle_identifier`, app1.BundleIdentifier, app2.BundleIdentifier)
require.NoError(t, err)
require.Len(t, appTitles, 2)
require.Equal(t, app1.BundleIdentifier, *appTitles[0].BundleIdentifier)
require.Equal(t, app2.BundleIdentifier, *appTitles[1].BundleIdentifier)
require.Equal(t, "foo", appTitles[0].Name)
require.Equal(t, app2.Name, appTitles[1].Name)
}
func testSetTeamVPPApps(t *testing.T, ds *Datastore) {
ctx := context.Background()
// Create a team
team, err := ds.NewTeam(ctx, &fleet.Team{Name: "vpp gang"})
require.NoError(t, err)
dataToken, err := test.CreateVPPTokenData(time.Now().Add(24*time.Hour), "Donkey Kong", "Jungle")
require.NoError(t, err)
tok1, err := ds.InsertVPPToken(ctx, dataToken)
assert.NoError(t, err)
_, err = ds.UpdateVPPTokenTeams(ctx, tok1.ID, []uint{})
assert.NoError(t, err)
// Insert some VPP apps for no team
app1 := &fleet.VPPApp{Name: "vpp_app_1", VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "1", Platform: fleet.MacOSPlatform}}, BundleIdentifier: "b1"}
_, err = ds.InsertVPPAppWithTeam(ctx, app1, nil)
require.NoError(t, err)
app2 := &fleet.VPPApp{Name: "vpp_app_2", VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "2", Platform: fleet.MacOSPlatform}}, BundleIdentifier: "b2"}
_, err = ds.InsertVPPAppWithTeam(ctx, app2, nil)
require.NoError(t, err)
app3 := &fleet.VPPApp{Name: "vpp_app_3", VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "3", Platform: fleet.MacOSPlatform}}, BundleIdentifier: "b3"}
_, err = ds.InsertVPPAppWithTeam(ctx, app3, nil)
require.NoError(t, err)
app4 := &fleet.VPPApp{Name: "vpp_app_4", VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "4", Platform: fleet.MacOSPlatform}}, BundleIdentifier: "b4"}
_, err = ds.InsertVPPAppWithTeam(ctx, app4, nil)
require.NoError(t, err)
assigned, err := ds.GetAssignedVPPApps(ctx, &team.ID)
require.NoError(t, err)
require.Len(t, assigned, 0)
forSetup, err := ds.GetVPPAppsToInstallDuringSetupExperience(ctx, &team.ID, "darwin")
require.NoError(t, err)
require.Len(t, forSetup, 0)
// Assign 2 apps
// make app1 install_during_setup for that team
_, err = ds.SetTeamVPPApps(ctx, &team.ID, []fleet.VPPAppTeam{
{VPPAppID: app1.VPPAppID, InstallDuringSetup: ptr.Bool(true)},
{VPPAppID: app2.VPPAppID, SelfService: true},
}, map[string]uint{})
require.NoError(t, err)
// create policies using two of the apps
app1Meta, err := ds.GetVPPAppMetadataByTeamAndTitleID(ctx, &team.ID, app1.TitleID)
require.NoError(t, err)
app2Meta, err := ds.GetVPPAppMetadataByTeamAndTitleID(ctx, &team.ID, app2.TitleID)
require.NoError(t, err)
policy1, err := ds.NewTeamPolicy(ctx, team.ID, nil, fleet.PolicyPayload{
Name: "Policy 1",
Query: "SELECT 1;",
Platform: "darwin",
VPPAppsTeamsID: &app1Meta.VPPAppsTeamsID,
})
require.NoError(t, err)
policy2, err := ds.NewTeamPolicy(ctx, team.ID, nil, fleet.PolicyPayload{
Name: "Policy 2",
Query: "SELECT 1;",
Platform: "darwin",
VPPAppsTeamsID: &app2Meta.VPPAppsTeamsID,
})
require.NoError(t, err)
assigned, err = ds.GetAssignedVPPApps(ctx, &team.ID)
require.NoError(t, err)
require.Len(t, assigned, 2)
assert.Contains(t, assigned, app1.VPPAppID)
assert.Contains(t, assigned, app2.VPPAppID)
assert.True(t, assigned[app2.VPPAppID].SelfService)
assert.True(t, *assigned[app1.VPPAppID].InstallDuringSetup)
forSetup, err = ds.GetVPPAppsToInstallDuringSetupExperience(ctx, &team.ID, "darwin")
require.NoError(t, err)
require.Len(t, forSetup, 1)
require.ElementsMatch(t, forSetup, []string{app1.VPPAppID.AdamID})
// Assign an additional app
_, err = ds.SetTeamVPPApps(ctx, &team.ID, []fleet.VPPAppTeam{
{VPPAppID: app1.VPPAppID, InstallDuringSetup: ptr.Bool(true)},
{VPPAppID: app2.VPPAppID},
{VPPAppID: app3.VPPAppID},
}, nil)
require.NoError(t, err)
policy1, err = ds.Policy(ctx, policy1.ID)
require.NoError(t, err)
require.NotNil(t, policy1.VPPAppsTeamsID)
assigned, err = ds.GetAssignedVPPApps(ctx, &team.ID)
require.NoError(t, err)
require.Len(t, assigned, 3)
require.Contains(t, assigned, app1.VPPAppID)
require.Contains(t, assigned, app2.VPPAppID)
require.Contains(t, assigned, app3.VPPAppID)
assert.False(t, assigned[app2.VPPAppID].SelfService)
assert.True(t, *assigned[app1.VPPAppID].InstallDuringSetup)
// Swap one app out for another
_, err = ds.SetTeamVPPApps(ctx, &team.ID, []fleet.VPPAppTeam{
{VPPAppID: app1.VPPAppID, InstallDuringSetup: ptr.Bool(true)},
{VPPAppID: app2.VPPAppID, SelfService: true},
{VPPAppID: app4.VPPAppID},
}, nil)
require.NoError(t, err)
policy1, err = ds.Policy(ctx, policy1.ID)
require.NoError(t, err)
require.Equal(t, app1Meta.VPPAppsTeamsID, *policy1.VPPAppsTeamsID)
assigned, err = ds.GetAssignedVPPApps(ctx, &team.ID)
require.NoError(t, err)
require.Len(t, assigned, 3)
require.Contains(t, assigned, app1.VPPAppID)
require.Contains(t, assigned, app2.VPPAppID)
require.Contains(t, assigned, app4.VPPAppID)
assert.True(t, assigned[app2.VPPAppID].SelfService)
assert.True(t, *assigned[app1.VPPAppID].InstallDuringSetup)
// Remove app1 fails because it is installed during setup
_, err = ds.SetTeamVPPApps(ctx, &team.ID, []fleet.VPPAppTeam{
{VPPAppID: app2.VPPAppID, SelfService: true},
{VPPAppID: app4.VPPAppID},
}, nil)
require.Error(t, err)
require.ErrorIs(t, err, errDeleteInstallerInstalledDuringSetup)
// make app1 NOT install_during_setup for that team
_, err = ds.SetTeamVPPApps(ctx, &team.ID, []fleet.VPPAppTeam{
{VPPAppID: app1.VPPAppID, InstallDuringSetup: ptr.Bool(false)},
{VPPAppID: app2.VPPAppID, SelfService: true},
{VPPAppID: app4.VPPAppID},
}, nil)
require.NoError(t, err)
// Remove app1 now works
_, err = ds.SetTeamVPPApps(ctx, &team.ID, []fleet.VPPAppTeam{
{VPPAppID: app2.VPPAppID, SelfService: true},
{VPPAppID: app4.VPPAppID},
}, nil)
require.NoError(t, err)
policy1, err = ds.Policy(ctx, policy1.ID)
require.NoError(t, err)
require.Nil(t, policy1.VPPAppsTeamsID)
policy2, err = ds.Policy(ctx, policy2.ID)
require.NoError(t, err)
require.Equal(t, app2Meta.VPPAppsTeamsID, *policy2.VPPAppsTeamsID)
// Remove all apps
_, err = ds.SetTeamVPPApps(ctx, &team.ID, []fleet.VPPAppTeam{}, nil)
require.NoError(t, err)
assigned, err = ds.GetAssignedVPPApps(ctx, &team.ID)
require.NoError(t, err)
require.Len(t, assigned, 0)
policy1, err = ds.Policy(ctx, policy1.ID)
require.NoError(t, err)
require.Nil(t, policy1.VPPAppsTeamsID)
policy2, err = ds.Policy(ctx, policy2.ID)
require.NoError(t, err)
require.Nil(t, policy2.VPPAppsTeamsID)
}
func testGetVPPAppByTeamAndTitleID(t *testing.T, ds *Datastore) {
ctx := context.Background()
team, err := ds.NewTeam(ctx, &fleet.Team{Name: "team 2"})
require.NoError(t, err)
test.CreateInsertGlobalVPPToken(t, ds)
var nfe fleet.NotFoundError
fooApp, err := ds.InsertVPPAppWithTeam(ctx,
&fleet.VPPApp{VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "foo", Platform: fleet.MacOSPlatform}}, BundleIdentifier: "b1", Name: "Foo"},
&team.ID)
require.NoError(t, err)
fooTitleID := fooApp.TitleID
gotVPPApp, err := ds.GetVPPAppByTeamAndTitleID(ctx, &team.ID, fooTitleID)
require.NoError(t, err)
require.Equal(t, "foo", gotVPPApp.AdamID)
require.Equal(t, fooTitleID, gotVPPApp.TitleID)
// title that doesn't exist
_, err = ds.GetVPPAppByTeamAndTitleID(ctx, &team.ID, 999)
require.ErrorAs(t, err, &nfe)
// create an entry for the global team
barApp, err := ds.InsertVPPAppWithTeam(ctx,
&fleet.VPPApp{VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "bar", Platform: fleet.MacOSPlatform}}, BundleIdentifier: "b2", Name: "Bar"}, nil)
require.NoError(t, err)
barTitleID := barApp.TitleID
// not found providing the team id
_, err = ds.GetVPPAppByTeamAndTitleID(ctx, &team.ID, barTitleID)
require.ErrorAs(t, err, &nfe)
// found for the global team
gotVPPApp, err = ds.GetVPPAppByTeamAndTitleID(ctx, nil, barTitleID)
require.NoError(t, err)
require.Equal(t, "bar", gotVPPApp.AdamID)
require.Equal(t, barTitleID, gotVPPApp.TitleID)
}
func testVPPTokensCRUD(t *testing.T, ds *Datastore) {
ctx := context.Background()
team, err := ds.NewTeam(ctx, &fleet.Team{Name: "Kritters"})
assert.NoError(t, err)
team2, err := ds.NewTeam(ctx, &fleet.Team{Name: "Zingers"})
assert.NoError(t, err)
tokens, err := ds.ListVPPTokens(ctx)
assert.NoError(t, err)
assert.Len(t, tokens, 0)
orgName := "Donkey Kong"
location := "Jungle"
dataToken, err := test.CreateVPPTokenData(time.Now().Add(24*time.Hour), orgName, location)
require.NoError(t, err)
orgName2 := "Diddy Kong"
location2 := "Mines"
dataToken2, err := test.CreateVPPTokenData(time.Now().Add(24*time.Hour), orgName2, location2)
require.NoError(t, err)
orgName3 := "Cranky Cong"
location3 := "Cranky's Cabin"
dataToken3, err := test.CreateVPPTokenData(time.Now().Add(24*time.Hour), orgName3, location3)
require.NoError(t, err)
orgName4 := "Funky Kong"
location4 := "Funky's Fishing Shack"
dataToken4, err := test.CreateVPPTokenData(time.Now().Add(24*time.Hour), orgName4, location4)
require.NoError(t, err)
orgName5 := "Lanky Kong"
location5 := "Lanky Kong's Pool"
dataToken5, err := test.CreateVPPTokenData(time.Now().Add(24*time.Hour), orgName5, location5)
require.NoError(t, err)
orgName6 := "Dixie Kong"
location6 := "Dixie's Island"
dataToken6, err := test.CreateVPPTokenData(time.Now().Add(24*time.Hour), orgName6, location6)
require.NoError(t, err)
// No assignments / disabled token
tok, err := ds.InsertVPPToken(ctx, dataToken)
tokID := tok.ID
assert.NoError(t, err)
assert.Equal(t, dataToken.Location, tok.Location)
assert.Equal(t, dataToken.Token, tok.Token)
assert.Equal(t, orgName, tok.OrgName)
assert.Equal(t, location, tok.Location)
assert.Nil(t, tok.Teams) // No team assigned
tok, err = ds.GetVPPToken(ctx, tokID)
assert.NoError(t, err)
assert.Equal(t, tokID, tok.ID)
assert.Equal(t, dataToken.Location, tok.Location)
assert.Equal(t, dataToken.Token, tok.Token)
assert.Equal(t, orgName, tok.OrgName)
assert.Equal(t, location, tok.Location)
assert.Nil(t, tok.Teams)
toks, err := ds.ListVPPTokens(ctx)
assert.NoError(t, err)
assert.Len(t, toks, 1)
assert.Equal(t, tokID, toks[0].ID)
assert.Equal(t, dataToken.Location, toks[0].Location)
assert.Equal(t, dataToken.Token, toks[0].Token)
assert.Equal(t, orgName, toks[0].OrgName)
assert.Equal(t, location, toks[0].Location)
assert.Nil(t, toks[0].Teams)
teamTok, err := ds.GetVPPTokenByTeamID(ctx, nil)
assert.Error(t, err)
assert.True(t, fleet.IsNotFound(err))
assert.Nil(t, teamTok)
teamTok, err = ds.GetVPPTokenByTeamID(ctx, &team.ID)
assert.Error(t, err)
assert.True(t, fleet.IsNotFound(err))
assert.Nil(t, teamTok)
// Assign to all teams
upTok, err := ds.UpdateVPPTokenTeams(ctx, tok.ID, []uint{})
assert.NoError(t, err)
assert.Equal(t, tokID, upTok.ID)
assert.Equal(t, dataToken.Location, upTok.Location)
assert.Equal(t, dataToken.Token, upTok.Token)
assert.Equal(t, orgName, upTok.OrgName)
assert.Equal(t, location, upTok.Location)
assert.NotNil(t, upTok.Teams) // "All Teams" team array is non-nil but empty
assert.Len(t, upTok.Teams, 0)
tok, err = ds.GetVPPToken(ctx, tok.ID)
assert.NoError(t, err)
assert.Equal(t, tokID, tok.ID)
assert.Equal(t, dataToken.Location, tok.Location)
assert.Equal(t, dataToken.Token, tok.Token)
assert.Equal(t, orgName, tok.OrgName)
assert.Equal(t, location, tok.Location)
assert.NotNil(t, tok.Teams) // "All Teams" teams array is non-nil but empty
assert.Len(t, tok.Teams, 0)
toks, err = ds.ListVPPTokens(ctx)
assert.NoError(t, err)
assert.Len(t, toks, 1)
assert.Equal(t, dataToken.Location, toks[0].Location)
assert.Equal(t, dataToken.Token, toks[0].Token)
assert.Equal(t, orgName, toks[0].OrgName)
assert.Equal(t, location, toks[0].Location)
assert.NotNil(t, toks[0].Teams)
assert.Len(t, toks[0].Teams, 0)
teamTok, err = ds.GetVPPTokenByTeamID(ctx, nil)
assert.NoError(t, err)
assert.Equal(t, tokID, teamTok.ID)
assert.Equal(t, dataToken.Location, teamTok.Location)
assert.Equal(t, dataToken.Token, teamTok.Token)
assert.Equal(t, orgName, teamTok.OrgName)
assert.Equal(t, location, teamTok.Location)
assert.NotNil(t, teamTok.Teams)
assert.Len(t, teamTok.Teams, 0)
teamTok, err = ds.GetVPPTokenByTeamID(ctx, &team.ID)
assert.NoError(t, err)
assert.Equal(t, tokID, teamTok.ID)
assert.Equal(t, dataToken.Location, teamTok.Location)
assert.Equal(t, dataToken.Token, teamTok.Token)
assert.Equal(t, orgName, teamTok.OrgName)
assert.Equal(t, location, teamTok.Location)
assert.NotNil(t, teamTok.Teams)
assert.Len(t, teamTok.Teams, 0)
// Assign to team "No Team"
upTok, err = ds.UpdateVPPTokenTeams(ctx, tok.ID, []uint{0})
require.NoError(t, err)
assert.Len(t, upTok.Teams, 1)
assert.Equal(t, tokID, upTok.ID)
assert.Equal(t, uint(0), upTok.Teams[0].ID)
assert.Equal(t, fleet.TeamNameNoTeam, upTok.Teams[0].Name)
tok, err = ds.GetVPPToken(ctx, tok.ID)
assert.NoError(t, err)
assert.Len(t, tok.Teams, 1)
assert.Equal(t, tokID, tok.ID)
assert.Equal(t, uint(0), tok.Teams[0].ID)
assert.Equal(t, fleet.TeamNameNoTeam, tok.Teams[0].Name)
toks, err = ds.ListVPPTokens(ctx)
assert.NoError(t, err)
assert.Len(t, toks, 1)
assert.Len(t, toks[0].Teams, 1)
assert.Equal(t, tokID, toks[0].ID)
assert.Equal(t, uint(0), toks[0].Teams[0].ID)
assert.Equal(t, fleet.TeamNameNoTeam, toks[0].Teams[0].Name)
teamTok, err = ds.GetVPPTokenByTeamID(ctx, nil)
assert.NoError(t, err)
assert.Equal(t, tokID, teamTok.ID)
assert.Equal(t, dataToken.Location, teamTok.Location)
assert.Equal(t, dataToken.Token, teamTok.Token)
assert.Equal(t, orgName, teamTok.OrgName)
assert.Equal(t, location, teamTok.Location)
assert.Len(t, teamTok.Teams, 1)
assert.Equal(t, uint(0), teamTok.Teams[0].ID)
assert.Equal(t, fleet.TeamNameNoTeam, teamTok.Teams[0].Name)
_, err = ds.GetVPPTokenByTeamID(ctx, &team.ID)
assert.Error(t, err)
assert.True(t, fleet.IsNotFound(err))
// Assign to normal team
upTok, err = ds.UpdateVPPTokenTeams(ctx, tok.ID, []uint{team.ID})
assert.NoError(t, err)
assert.Len(t, upTok.Teams, 1)
assert.Equal(t, team.ID, upTok.Teams[0].ID)
assert.Equal(t, team.Name, upTok.Teams[0].Name)
tok, err = ds.GetVPPToken(ctx, tok.ID)
assert.NoError(t, err)
assert.Len(t, tok.Teams, 1)
assert.Equal(t, team.ID, tok.Teams[0].ID)
assert.Equal(t, team.Name, tok.Teams[0].Name)
toks, err = ds.ListVPPTokens(ctx)
assert.NoError(t, err)
assert.Len(t, toks, 1)
assert.Len(t, toks[0].Teams, 1)
assert.Equal(t, team.ID, toks[0].Teams[0].ID)
assert.Equal(t, team.Name, toks[0].Teams[0].Name)
_, err = ds.GetVPPTokenByTeamID(ctx, nil)
assert.Error(t, err)
assert.True(t, fleet.IsNotFound(err))
teamTok, err = ds.GetVPPTokenByTeamID(ctx, &team.ID)
assert.NoError(t, err)
assert.Equal(t, tokID, teamTok.ID)
assert.Equal(t, dataToken.Location, teamTok.Location)
assert.Equal(t, dataToken.Token, teamTok.Token)
assert.Equal(t, orgName, teamTok.OrgName)
assert.Equal(t, location, teamTok.Location)
assert.NotNil(t, teamTok.Teams)
assert.Len(t, teamTok.Teams, 1)
assert.Equal(t, team.ID, teamTok.Teams[0].ID)
assert.Equal(t, team.Name, teamTok.Teams[0].Name)
// make sure renewing a VPP token doesn't affect associated VPP install automations
t1app, err := ds.InsertVPPAppWithTeam(ctx, &fleet.VPPApp{
Name: "vpp1", BundleIdentifier: "com.app.vpp1",
VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "adam_vpp_app_1", Platform: fleet.MacOSPlatform}},
}, &team.ID)
require.NoError(t, err)
t1meta, err := ds.GetVPPAppMetadataByTeamAndTitleID(ctx, &team.ID, t1app.TitleID)
require.NoError(t, err)
t1Policy, err := ds.NewTeamPolicy(ctx, team.ID, nil, fleet.PolicyPayload{
Name: "p1",
Query: "SELECT 1;",
VPPAppsTeamsID: &t1meta.VPPAppsTeamsID,
})
require.NoError(t, err)
// Renew flow
upTok, err = ds.UpdateVPPToken(ctx, tokID, dataToken6)
assert.NoError(t, err)
assert.Equal(t, tokID, upTok.ID)
assert.Equal(t, dataToken6.Location, upTok.Location)
assert.Equal(t, dataToken6.Token, upTok.Token)
assert.Equal(t, orgName6, upTok.OrgName)
assert.Equal(t, location6, upTok.Location)
assert.NotNil(t, upTok.Teams)
assert.Len(t, upTok.Teams, 1)
assert.Equal(t, team.ID, upTok.Teams[0].ID)
assert.Equal(t, team.Name, upTok.Teams[0].Name)
t1Policy, err = ds.Policy(ctx, t1Policy.ID)
require.NoError(t, err)
require.Equal(t, t1Policy.VPPAppsTeamsID, &t1meta.VPPAppsTeamsID)
tok, err = ds.GetVPPToken(ctx, tok.ID)
assert.NoError(t, err)
assert.Equal(t, tokID, tok.ID)
assert.Equal(t, dataToken6.Location, tok.Location)
assert.Equal(t, dataToken6.Token, tok.Token)
assert.Equal(t, orgName6, tok.OrgName)
assert.Equal(t, location6, tok.Location)
assert.NotNil(t, tok.Teams)
assert.Len(t, tok.Teams, 1)
assert.Equal(t, team.ID, tok.Teams[0].ID)
assert.Equal(t, team.Name, tok.Teams[0].Name)
// Assign back to no team / disabled
upTok, err = ds.UpdateVPPTokenTeams(ctx, tokID, nil)
assert.NoError(t, err)
assert.Nil(t, upTok.Teams)
toks, err = ds.ListVPPTokens(ctx)
assert.NoError(t, err)
assert.Len(t, toks, 1)
_, err = ds.GetVPPTokenByTeamID(ctx, &team.ID)
assert.Error(t, err)
assert.True(t, fleet.IsNotFound(err))
_, err = ds.GetVPPTokenByTeamID(ctx, nil)
assert.Error(t, err)
assert.True(t, fleet.IsNotFound(err))
// Delete
err = ds.DeleteVPPToken(ctx, tokID)
assert.NoError(t, err)
toks, err = ds.ListVPPTokens(ctx)
assert.NoError(t, err)
assert.Len(t, toks, 0)
// Multiple tokens and constraints tests
tokNone, err := ds.InsertVPPToken(ctx, dataToken)
assert.NoError(t, err)
_, err = ds.UpdateVPPTokenTeams(ctx, tokNone.ID, nil)
assert.NoError(t, err)
toks, err = ds.ListVPPTokens(ctx)
assert.NoError(t, err)
assert.Len(t, toks, 1)
_, err = ds.InsertVPPToken(ctx, dataToken)
assert.Error(t, err)
tokAll, err := ds.InsertVPPToken(ctx, dataToken2)
assert.NoError(t, err)
_, err = ds.UpdateVPPTokenTeams(ctx, tokAll.ID, []uint{})
assert.NoError(t, err)
_, err = ds.UpdateVPPTokenTeams(ctx, tokAll.ID, []uint{})
assert.NoError(t, err)
toks, err = ds.ListVPPTokens(ctx)
assert.NoError(t, err)
assert.Len(t, toks, 2)
// Remove tokAll from All teams
tokAll, err = ds.UpdateVPPTokenTeams(ctx, tokAll.ID, nil)
assert.NoError(t, err)
tokTeam, err := ds.InsertVPPToken(ctx, dataToken3)
assert.NoError(t, err)
_, err = ds.UpdateVPPTokenTeams(ctx, tokTeam.ID, []uint{team.ID})
assert.NoError(t, err)
_, err = ds.UpdateVPPTokenTeams(ctx, tokTeam.ID, []uint{team.ID, team.ID})
assert.Error(t, err)
// Cannot move tokAll to all teams now
_, err = ds.UpdateVPPTokenTeams(ctx, tokAll.ID, []uint{})
assert.Error(t, err)
_, err = ds.UpdateVPPTokenTeams(ctx, tokTeam.ID, []uint{0})
assert.NoError(t, err)
_, err = ds.UpdateVPPTokenTeams(ctx, tokAll.ID, []uint{})
assert.Error(t, err)
_, err = ds.UpdateVPPTokenTeams(ctx, tokTeam.ID, []uint{team.ID})
assert.NoError(t, err)
///
toks, err = ds.ListVPPTokens(ctx)
assert.NoError(t, err)
assert.Len(t, toks, 3)
tokTeams, err := ds.InsertVPPToken(ctx, dataToken4)
assert.NoError(t, err)
_, err = ds.UpdateVPPTokenTeams(ctx, tokTeams.ID, []uint{team.ID, team2.ID})
assert.Error(t, err)
_, err = ds.UpdateVPPTokenTeams(ctx, tokTeams.ID, []uint{team2.ID})
assert.NoError(t, err)
// make sure updating a VPP token auto-clears associated VPP install automations
t2app, err := ds.InsertVPPAppWithTeam(ctx, &fleet.VPPApp{
Name: "vpp1", BundleIdentifier: "com.app.vpp1",
VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "adam_vpp_app_1", Platform: fleet.MacOSPlatform}},
}, &team2.ID)
require.NoError(t, err)
t2meta, err := ds.GetVPPAppMetadataByTeamAndTitleID(ctx, &team2.ID, t2app.TitleID)
require.NoError(t, err)
t2Policy, err := ds.NewTeamPolicy(ctx, team2.ID, nil, fleet.PolicyPayload{
Name: "p1",
Query: "SELECT 1;",
VPPAppsTeamsID: &t2meta.VPPAppsTeamsID,
})
require.NoError(t, err)
_, err = ds.UpdateVPPTokenTeams(ctx, tokTeams.ID, []uint{team.ID, team2.ID})
assert.Error(t, err)
// errored update shouldn't have cleared anything
t2Policy, err = ds.Policy(ctx, t2Policy.ID)
assert.NoError(t, err)
assert.Equal(t, t2meta.VPPAppsTeamsID, *t2Policy.VPPAppsTeamsID)
_, err = ds.UpdateVPPTokenTeams(ctx, tokTeams.ID, []uint{team.ID, 0})
assert.Error(t, err)
_, err = ds.UpdateVPPTokenTeams(ctx, tokTeams.ID, []uint{team2.ID, 0})
assert.NoError(t, err)
// errored update should have cleared automation
t2Policy, err = ds.Policy(ctx, t2Policy.ID)
assert.NoError(t, err)
assert.Nil(t, t2Policy.VPPAppsTeamsID)
toks, err = ds.ListVPPTokens(ctx)
assert.NoError(t, err)
assert.Len(t, toks, 4)
tokTeams, err = ds.GetVPPToken(ctx, tokTeams.ID)
assert.NoError(t, err)
assert.Len(t, tokTeams.Teams, 2)
assert.Contains(t, tokTeams.Teams, fleet.TeamTuple{ID: team2.ID, Name: team2.Name})
assert.Contains(t, tokTeams.Teams, fleet.TeamTuple{ID: 0, Name: fleet.TeamNameNoTeam})
tokBadConstraint, err := ds.InsertVPPToken(ctx, dataToken5)
assert.NoError(t, err)
_, err = ds.UpdateVPPTokenTeams(ctx, tokBadConstraint.ID, []uint{})
assert.Error(t, err)
_, err = ds.UpdateVPPTokenTeams(ctx, tokBadConstraint.ID, []uint{team.ID})
assert.Error(t, err)
assert.ErrorContains(t, err, "\"Kritters\" team already has a VPP token.")
_, err = ds.UpdateVPPTokenTeams(ctx, tokBadConstraint.ID, []uint{0})
assert.Error(t, err)
assert.ErrorContains(t, err, "\"No team\" team already has a VPP token.")
toks, err = ds.ListVPPTokens(ctx)
assert.NoError(t, err)
assert.Len(t, toks, 5)
///
tokNil, err := ds.GetVPPTokenByTeamID(ctx, nil)
assert.NoError(t, err)
assert.Equal(t, tokTeams.ID, tokNil.ID)
tokTeam1, err := ds.GetVPPTokenByTeamID(ctx, &team.ID)
assert.NoError(t, err)
assert.Equal(t, tokTeam.ID, tokTeam1.ID)
tokTeam2, err := ds.GetVPPTokenByTeamID(ctx, &team2.ID)
assert.NoError(t, err)
assert.Equal(t, tokTeam2.ID, tokTeam2.ID)
assert.Len(t, tokTeam2.Teams, 2)
assert.Contains(t, tokTeam2.Teams, fleet.TeamTuple{ID: team2.ID, Name: team2.Name})
assert.Contains(t, tokTeam2.Teams, fleet.TeamTuple{ID: 0, Name: fleet.TeamNameNoTeam})
////
// make sure deleting a VPP token auto-clears associated VPP install automations
t1app, err = ds.InsertVPPAppWithTeam(ctx, &fleet.VPPApp{
Name: "vpp1", BundleIdentifier: "com.app.vpp1",
VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "adam_vpp_app_1", Platform: fleet.MacOSPlatform}},
}, &team.ID)
require.NoError(t, err)
t1meta, err = ds.GetVPPAppMetadataByTeamAndTitleID(ctx, &team.ID, t1app.TitleID)
require.NoError(t, err)
t1Policy2, err := ds.NewTeamPolicy(ctx, team.ID, nil, fleet.PolicyPayload{
Name: "t1p2",
Query: "SELECT 1;",
VPPAppsTeamsID: &t1meta.VPPAppsTeamsID,
})
require.NoError(t, err)
err = ds.DeleteVPPToken(ctx, tokTeam.ID)
assert.NoError(t, err)
t1Policy2, err = ds.Policy(ctx, t1Policy2.ID)
assert.NoError(t, err)
assert.Nil(t, t1Policy2.VPPAppsTeamsID)
tokNil, err = ds.GetVPPTokenByTeamID(ctx, nil)
assert.NoError(t, err)
assert.Equal(t, tokTeams.ID, tokNil.ID)
_, err = ds.GetVPPTokenByTeamID(ctx, &team.ID)
assert.Error(t, err)
tokTeam2, err = ds.GetVPPTokenByTeamID(ctx, &team2.ID)
assert.NoError(t, err)
assert.Equal(t, tokTeams.ID, tokTeam2.ID)
////
err = ds.DeleteVPPToken(ctx, tokTeams.ID)
assert.NoError(t, err)
_, err = ds.GetVPPTokenByTeamID(ctx, nil)
assert.Error(t, err)
assert.True(t, fleet.IsNotFound(err))
_, err = ds.GetVPPTokenByTeamID(ctx, &team.ID)
assert.Error(t, err)
assert.True(t, fleet.IsNotFound(err))
_, err = ds.GetVPPTokenByTeamID(ctx, &team2.ID)
assert.Error(t, err)
assert.True(t, fleet.IsNotFound(err))
////
tokAll, err = ds.UpdateVPPTokenTeams(ctx, tokAll.ID, []uint{})
assert.NoError(t, err)
tokNil, err = ds.GetVPPTokenByTeamID(ctx, nil)
assert.NoError(t, err)
assert.Equal(t, tokAll.ID, tokNil.ID)
tokTeam1, err = ds.GetVPPTokenByTeamID(ctx, &team.ID)
assert.NoError(t, err)
assert.Equal(t, tokAll.ID, tokTeam1.ID)
tokTeam2, err = ds.GetVPPTokenByTeamID(ctx, &team2.ID)
assert.NoError(t, err)
assert.Equal(t, tokAll.ID, tokTeam2.ID)
err = ds.DeleteVPPToken(ctx, tokAll.ID)
assert.NoError(t, err)
////
_, err = ds.UpdateVPPTokenTeams(ctx, tokNone.ID, []uint{0, team.ID, team2.ID})
assert.NoError(t, err)
tokNil, err = ds.GetVPPTokenByTeamID(ctx, nil)
assert.NoError(t, err)
assert.Equal(t, tokNone.ID, tokNil.ID)
tokTeam1, err = ds.GetVPPTokenByTeamID(ctx, &team.ID)
assert.NoError(t, err)
assert.Equal(t, tokNone.ID, tokTeam1.ID)
tokTeam2, err = ds.GetVPPTokenByTeamID(ctx, &team2.ID)
assert.NoError(t, err)
assert.Equal(t, tokNone.ID, tokTeam2.ID)
////
err = ds.DeleteVPPToken(ctx, tokNone.ID)
assert.NoError(t, err)
}
func testVPPTokenAppTeamAssociations(t *testing.T, ds *Datastore) {
ctx := context.Background()
team1, err := ds.NewTeam(ctx, &fleet.Team{Name: "Kritters"})
assert.NoError(t, err)
team2, err := ds.NewTeam(ctx, &fleet.Team{Name: "Zingers"})
assert.NoError(t, err)
dataToken, err := test.CreateVPPTokenData(time.Now().Add(24*time.Hour), "Donkey Kong", "Jungle")
require.NoError(t, err)
dataToken2, err := test.CreateVPPTokenData(time.Now().Add(24*time.Hour), "Diddy Kong", "Mines")
require.NoError(t, err)
tok1, err := ds.InsertVPPToken(ctx, dataToken)
assert.NoError(t, err)
tok2, err := ds.InsertVPPToken(ctx, dataToken2)
assert.NoError(t, err)
_, err = ds.UpdateVPPTokenTeams(ctx, tok1.ID, []uint{team1.ID})
assert.NoError(t, err)
_, err = ds.UpdateVPPTokenTeams(ctx, tok2.ID, []uint{team2.ID})
assert.NoError(t, err)
app1 := &fleet.VPPApp{
Name: "app1",
VPPAppTeam: fleet.VPPAppTeam{
VPPAppID: fleet.VPPAppID{
AdamID: "1",
Platform: fleet.MacOSPlatform,
},
},
BundleIdentifier: "app1",
}
_, err = ds.InsertVPPAppWithTeam(ctx, app1, &team1.ID)
assert.NoError(t, err)
_, err = ds.InsertVPPAppWithTeam(ctx, app1, &team2.ID)
assert.NoError(t, err)
app2 := &fleet.VPPApp{
Name: "app2",
VPPAppTeam: fleet.VPPAppTeam{
VPPAppID: fleet.VPPAppID{
AdamID: "2",
Platform: fleet.MacOSPlatform,
},
},
BundleIdentifier: "app2",
}
vppApp2, err := ds.InsertVPPAppWithTeam(ctx, app2, &team1.ID)
_ = vppApp2
assert.NoError(t, err)
// team1: token 1, app1, app2
// team2: token 2, app 1
apps, err := ds.GetAssignedVPPApps(ctx, &team1.ID)
assert.NoError(t, err)
assert.Len(t, apps, 2)
assert.Contains(t, apps, app1.VPPAppID)
assert.Contains(t, apps, app2.VPPAppID)
apps, err = ds.GetAssignedVPPApps(ctx, &team2.ID)
assert.NoError(t, err)
assert.Len(t, apps, 1)
assert.Contains(t, apps, app1.VPPAppID)
/// Try to move team 1 token to team 2
_, err = ds.UpdateVPPTokenTeams(ctx, tok1.ID, []uint{team2.ID})
assert.Error(t, err)
// team1: token 1, app1, app2
// team2: token 2, app 1
apps, err = ds.GetAssignedVPPApps(ctx, &team1.ID)
assert.NoError(t, err)
assert.Len(t, apps, 2)
apps, err = ds.GetAssignedVPPApps(ctx, &team2.ID)
assert.NoError(t, err)
assert.Len(t, apps, 1)
assert.Contains(t, apps, app1.VPPAppID)
_, err = ds.UpdateVPPTokenTeams(ctx, tok1.ID, nil)
assert.NoError(t, err)
// team1: no token, no apps
// team2: token 2, app 1
apps, err = ds.GetAssignedVPPApps(ctx, &team1.ID)
assert.NoError(t, err)
assert.Len(t, apps, 0)
apps, err = ds.GetAssignedVPPApps(ctx, &team2.ID)
assert.NoError(t, err)
assert.Len(t, apps, 1)
assert.Contains(t, apps, app1.VPPAppID)
// Move team 2 token to team 1
_, err = ds.UpdateVPPTokenTeams(ctx, tok2.ID, []uint{team1.ID})
assert.NoError(t, err)
// team1: token 2, app 1
// team2: no token, no apps
apps, err = ds.GetAssignedVPPApps(ctx, &team1.ID)
assert.NoError(t, err)
assert.Len(t, apps, 0)
apps, err = ds.GetAssignedVPPApps(ctx, &team2.ID)
assert.NoError(t, err)
assert.Len(t, apps, 0)
/// Can't assign apps with no token
_, err = ds.InsertVPPAppWithTeam(ctx, app1, &team2.ID)
assert.Error(t, err)
}
func testVPPTokenReassignTeamsToAllTeams(t *testing.T, ds *Datastore) {
ctx := context.Background()
// Set up two teams
team1, err := ds.NewTeam(ctx, &fleet.Team{Name: "All Teams Reassign Team 1"})
require.NoError(t, err)
team2, err := ds.NewTeam(ctx, &fleet.Team{Name: "All Teams Reassign Team 2"})
require.NoError(t, err)
// Insert token
tokenData, err := test.CreateVPPTokenData(time.Now().Add(24*time.Hour), "Org For Reassign", "Loc For Reassign")
require.NoError(t, err)
tok, err := ds.InsertVPPToken(ctx, tokenData)
require.NoError(t, err)
tokenID := tok.ID
// Assign token to team1 and team2
upTok, err := ds.UpdateVPPTokenTeams(ctx, tokenID, []uint{team1.ID, team2.ID})
require.NoError(t, err)
require.Len(t, upTok.Teams, 2)
// Now, reassign to ALL TEAMS (teams = [])
upTok, err = ds.UpdateVPPTokenTeams(ctx, tokenID, []uint{})
require.NoError(t, err)
require.NotNil(t, upTok.Teams)
require.Len(t, upTok.Teams, 0, "After reassigning to all teams, Teams should be zero-length (all teams)")
// Confirm that the assignment is present as "All teams"
gotTok, err := ds.GetVPPToken(ctx, tokenID)
require.NoError(t, err)
require.NotNil(t, gotTok.Teams)
require.Len(t, gotTok.Teams, 0, "After reassigning to all teams, Teams should be zero-length (all teams)")
// Now, assign back to just team1
upTok, err = ds.UpdateVPPTokenTeams(ctx, tokenID, []uint{team1.ID})
require.NoError(t, err)
require.Len(t, upTok.Teams, 1)
require.Equal(t, team1.ID, upTok.Teams[0].ID)
}
func testGetOrInsertSoftwareTitleForVPPApp(t *testing.T, ds *Datastore) {
ctx := context.Background()
host1 := test.NewHost(t, ds, "host1", "", "host1key", "host1uuid", time.Now())
host2 := test.NewHost(t, ds, "host2", "", "host2key", "host2uuid", time.Now())
software1 := []fleet.Software{
{Name: "Existing Title", Version: "0.0.1", Source: "apps", BundleIdentifier: "existing.title"},
}
software2 := []fleet.Software{
{Name: "Existing Title", Version: "v0.0.2", Source: "apps", BundleIdentifier: "existing.title"},
{Name: "Existing Title", Version: "0.0.3", Source: "apps", BundleIdentifier: "existing.title"},
{Name: "Existing Title Without Bundle", Version: "0.0.3", Source: "apps"},
{Name: "Existing Title from windows", Version: "0.0.3", Source: "programs"},
}
_, err := ds.UpdateHostSoftware(ctx, host1.ID, software1)
require.NoError(t, err)
_, err = ds.UpdateHostSoftware(ctx, host2.ID, software2)
require.NoError(t, err)
require.NoError(t, ds.SyncHostsSoftware(ctx, time.Now()))
require.NoError(t, ds.SyncHostsSoftwareTitles(ctx, time.Now()))
// get the ID of the windows title so we can validate that it is not re-used
sw, _, _, err := ds.ListSoftwareTitles(ctx, fleet.SoftwareTitleListOptions{}, fleet.TeamFilter{TeamID: host2.TeamID})
require.NoError(t, err)
require.Len(t, sw, 3)
var tid uint
for _, s := range sw {
if s.Name == "Existing Title from windows" {
tid = s.ID
}
}
tests := []struct {
name string
app *fleet.VPPApp
existingTitleID uint
expectDiffID bool
}{
{
name: "title that already exists, no bundle identifier in payload",
app: &fleet.VPPApp{
Name: "Existing Title",
LatestVersion: "0.0.1",
BundleIdentifier: "",
},
},
{
name: "title that already exists, bundle identifier in payload",
app: &fleet.VPPApp{
Name: "Existing Title",
LatestVersion: "0.0.2",
BundleIdentifier: "existing.title",
},
},
{
name: "title that already exists but doesn't have a bundle identifier",
app: &fleet.VPPApp{
Name: "Existing Title Without Bundle",
LatestVersion: "0.0.3",
BundleIdentifier: "",
},
},
{
name: "title that already exists, no bundle identifier in DB, bundle identifier in payload",
app: &fleet.VPPApp{
Name: "Existing Title Without Bundle",
LatestVersion: "0.0.3",
BundleIdentifier: "new.bundle.id",
},
},
{
name: "title that already exists for windows",
app: &fleet.VPPApp{
Name: "Existing Title from windows",
LatestVersion: "0.0.4",
BundleIdentifier: "com.bundle.id",
},
existingTitleID: tid,
expectDiffID: true,
},
{
name: "title that doesn't exist, no bundle identifier in payload",
app: &fleet.VPPApp{
Name: "New Title",
LatestVersion: "0.1.0",
BundleIdentifier: "",
},
},
{
name: "title that doesn't exist, with bundle identifier in payload",
app: &fleet.VPPApp{
Name: "New Title",
LatestVersion: "0.1.0",
BundleIdentifier: "new.title.bundle",
},
},
}
for _, platform := range fleet.AppStoreAppsPlatforms {
for _, tt := range tests {
t.Run(fmt.Sprintf("%s_%v", tt.name, platform), func(t *testing.T) {
tt.app.Platform = platform
var id uint
err := ds.withTx(ctx, func(tx sqlx.ExtContext) error {
var err error
id, err = ds.getOrInsertSoftwareTitleForVPPApp(ctx, tx, tt.app)
return err
})
require.NoError(t, err)
require.NotEmpty(t, id)
if tt.expectDiffID {
require.NotEqual(t, tt.existingTitleID, id)
}
})
}
}
}
func testDeleteVPPAssignedToPolicy(t *testing.T, ds *Datastore) {
ctx := context.Background()
test.CreateInsertGlobalVPPToken(t, ds)
va1, err := ds.InsertVPPAppWithTeam(ctx, &fleet.VPPApp{
Name: "vpp1", BundleIdentifier: "com.app.vpp1",
VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "adam_vpp_app_1", Platform: fleet.MacOSPlatform}},
}, nil)
require.NoError(t, err)
meta, err := ds.GetVPPAppMetadataByTeamAndTitleID(ctx, ptr.Uint(0), va1.TitleID)
require.NoError(t, err)
p1, err := ds.NewTeamPolicy(ctx, fleet.PolicyNoTeamID, nil, fleet.PolicyPayload{
Name: "p1",
Query: "SELECT 1;",
VPPAppsTeamsID: &meta.VPPAppsTeamsID,
})
require.NoError(t, err)
err = ds.DeleteVPPAppFromTeam(ctx, ptr.Uint(0), va1.VPPAppID)
require.Error(t, err)
require.ErrorIs(t, err, errDeleteInstallerWithAssociatedPolicy)
_, err = ds.DeleteTeamPolicies(ctx, fleet.PolicyNoTeamID, []uint{p1.ID})
require.NoError(t, err)
err = ds.DeleteVPPAppFromTeam(ctx, ptr.Uint(0), va1.VPPAppID)
require.NoError(t, err)
}
func testVPPTokenTeamAssignment(t *testing.T, ds *Datastore) {
ctx := context.Background()
// Create a couple of teams
team1, err := ds.NewTeam(ctx, &fleet.Team{Name: "team1" + t.Name()})
require.NoError(t, err)
team2, err := ds.NewTeam(ctx, &fleet.Team{Name: "team2" + t.Name()})
require.NoError(t, err)
dataToken, err := test.CreateVPPTokenData(time.Now().Add(24*time.Hour), "vpp_org"+t.Name(), "vpp location"+t.Name())
require.NoError(t, err)
tok1, err := ds.InsertVPPToken(ctx, dataToken)
assert.NoError(t, err)
_, err = ds.UpdateVPPTokenTeams(ctx, tok1.ID, []uint{})
assert.NoError(t, err)
// Insert some VPP apps for no team
app1 := &fleet.VPPApp{Name: "vpp_app_1", VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "1", Platform: fleet.MacOSPlatform}}, BundleIdentifier: "b1"}
_, err = ds.InsertVPPAppWithTeam(ctx, app1, nil)
require.NoError(t, err)
app2 := &fleet.VPPApp{Name: "vpp_app_2", VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "2", Platform: fleet.MacOSPlatform}}, BundleIdentifier: "b2"}
_, err = ds.InsertVPPAppWithTeam(ctx, app2, nil)
require.NoError(t, err)
app3 := &fleet.VPPApp{Name: "vpp_app_3", VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "3", Platform: fleet.MacOSPlatform}}, BundleIdentifier: "b3"}
_, err = ds.InsertVPPAppWithTeam(ctx, app3, nil)
require.NoError(t, err)
app4 := &fleet.VPPApp{Name: "vpp_app_4", VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "4", Platform: fleet.MacOSPlatform}}, BundleIdentifier: "b4"}
_, err = ds.InsertVPPAppWithTeam(ctx, app4, nil)
require.NoError(t, err)
assigned, err := ds.GetAssignedVPPApps(ctx, &team1.ID)
require.NoError(t, err)
require.Empty(t, assigned)
assigned, err = ds.GetAssignedVPPApps(ctx, &team2.ID)
require.NoError(t, err)
require.Empty(t, assigned)
// assign app1 and app2 to team1
_, err = ds.SetTeamVPPApps(ctx, &team1.ID, []fleet.VPPAppTeam{
{VPPAppID: app1.VPPAppID, InstallDuringSetup: ptr.Bool(true)},
{VPPAppID: app2.VPPAppID, SelfService: true},
}, nil)
require.NoError(t, err)
assigned, err = ds.GetAssignedVPPApps(ctx, &team1.ID)
require.NoError(t, err)
require.Len(t, assigned, 2)
assert.Contains(t, assigned, app1.VPPAppID)
assert.Contains(t, assigned, app2.VPPAppID)
assert.True(t, assigned[app2.VPPAppID].SelfService)
assert.True(t, *assigned[app1.VPPAppID].InstallDuringSetup)
// assign token to team1 and team 2 explicitly
_, err = ds.UpdateVPPTokenTeams(ctx, tok1.ID, []uint{team1.ID, team2.ID})
require.NoError(t, err)
// team1 should still have its apps
assigned, err = ds.GetAssignedVPPApps(ctx, &team1.ID)
require.NoError(t, err)
require.Len(t, assigned, 2)
assert.Contains(t, assigned, app1.VPPAppID)
assert.Contains(t, assigned, app2.VPPAppID)
assert.True(t, assigned[app2.VPPAppID].SelfService)
assert.True(t, *assigned[app1.VPPAppID].InstallDuringSetup)
// assign token to just team2
_, err = ds.UpdateVPPTokenTeams(ctx, tok1.ID, []uint{team2.ID})
require.NoError(t, err)
// team1 should no longer have its apps, since the token was removed
assigned, err = ds.GetAssignedVPPApps(ctx, &team1.ID)
require.NoError(t, err)
require.Empty(t, assigned)
}
func testSetTeamVPPAppsWithLabels(t *testing.T, ds *Datastore) {
ctx := context.Background()
// Create a team
team, err := ds.NewTeam(ctx, &fleet.Team{Name: "vpp_app_labels" + t.Name()})
require.NoError(t, err)
dataToken, err := test.CreateVPPTokenData(time.Now().Add(24*time.Hour), "Org"+t.Name(), "Location"+t.Name())
require.NoError(t, err)
tok1, err := ds.InsertVPPToken(ctx, dataToken)
assert.NoError(t, err)
_, err = ds.UpdateVPPTokenTeams(ctx, tok1.ID, []uint{})
assert.NoError(t, err)
// Create some labels
label1, err := ds.NewLabel(ctx, &fleet.Label{
Name: "label1" + t.Name(),
Description: "a label",
Query: "select 1 from processes;",
Platform: "darwin",
})
require.NoError(t, err)
label2, err := ds.NewLabel(ctx, &fleet.Label{
Name: "label2" + t.Name(),
Description: "a label",
Query: "select 2 from processes;",
Platform: "darwin",
})
require.NoError(t, err)
app1 := &fleet.VPPApp{Name: "vpp_app_1", VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "1", Platform: fleet.MacOSPlatform}}, BundleIdentifier: "b1"}
_, err = ds.InsertVPPAppWithTeam(ctx, app1, nil)
require.NoError(t, err)
app2 := &fleet.VPPApp{Name: "vpp_app_2", VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "2", Platform: fleet.MacOSPlatform}}, BundleIdentifier: "b2"}
_, err = ds.InsertVPPAppWithTeam(ctx, app2, nil)
require.NoError(t, err)
assigned, err := ds.GetAssignedVPPApps(ctx, &team.ID)
require.NoError(t, err)
require.Len(t, assigned, 0)
app1.VPPAppTeam = fleet.VPPAppTeam{VPPAppID: app1.VPPAppID, ValidatedLabels: &fleet.LabelIdentsWithScope{
LabelScope: fleet.LabelScopeIncludeAny,
ByName: map[string]fleet.LabelIdent{
label1.Name: {
LabelID: label1.ID,
LabelName: label1.Name,
},
label2.Name: {
LabelID: label2.ID,
LabelName: label2.Name,
},
},
}}
app2.VPPAppTeam = fleet.VPPAppTeam{VPPAppID: app2.VPPAppID, ValidatedLabels: &fleet.LabelIdentsWithScope{
LabelScope: fleet.LabelScopeExcludeAny,
ByName: map[string]fleet.LabelIdent{
label1.Name: {
LabelID: label1.ID,
LabelName: label1.Name,
},
label2.Name: {
LabelID: label2.ID,
LabelName: label2.Name,
},
},
}}
_, err = ds.SetTeamVPPApps(ctx, &team.ID, []fleet.VPPAppTeam{
app1.VPPAppTeam,
app2.VPPAppTeam,
}, nil)
require.NoError(t, err)
assigned, err = ds.GetAssignedVPPApps(ctx, &team.ID)
require.NoError(t, err)
require.Len(t, assigned, 2)
app1Meta, err := ds.GetVPPAppMetadataByTeamAndTitleID(ctx, &team.ID, app1.TitleID)
require.NoError(t, err)
app2Meta, err := ds.GetVPPAppMetadataByTeamAndTitleID(ctx, &team.ID, app2.TitleID)
require.NoError(t, err)
require.Len(t, app1Meta.LabelsIncludeAny, 2)
require.Len(t, app1Meta.LabelsExcludeAny, 0)
for _, l := range app1Meta.LabelsIncludeAny {
_, ok := app1.VPPAppTeam.ValidatedLabels.ByName[l.LabelName]
require.True(t, ok)
}
require.Len(t, app2Meta.LabelsExcludeAny, 2)
require.Len(t, app2Meta.LabelsIncludeAny, 0)
for _, l := range app2Meta.LabelsExcludeAny {
_, ok := app2.VPPAppTeam.ValidatedLabels.ByName[l.LabelName]
require.True(t, ok)
}
// switch label types
app1.VPPAppTeam = fleet.VPPAppTeam{VPPAppID: app1.VPPAppID, ValidatedLabels: &fleet.LabelIdentsWithScope{
LabelScope: fleet.LabelScopeExcludeAny,
ByName: map[string]fleet.LabelIdent{
label1.Name: {
LabelID: label1.ID,
LabelName: label1.Name,
},
label2.Name: {
LabelID: label2.ID,
LabelName: label2.Name,
},
},
}}
app2.VPPAppTeam = fleet.VPPAppTeam{VPPAppID: app2.VPPAppID, ValidatedLabels: &fleet.LabelIdentsWithScope{
LabelScope: fleet.LabelScopeIncludeAny,
ByName: map[string]fleet.LabelIdent{
label1.Name: {
LabelID: label1.ID,
LabelName: label1.Name,
},
label2.Name: {
LabelID: label2.ID,
LabelName: label2.Name,
},
},
}}
_, err = ds.SetTeamVPPApps(ctx, &team.ID, []fleet.VPPAppTeam{
app1.VPPAppTeam,
app2.VPPAppTeam,
}, nil)
require.NoError(t, err)
assigned, err = ds.GetAssignedVPPApps(ctx, &team.ID)
require.NoError(t, err)
require.Len(t, assigned, 2)
app1Meta, err = ds.GetVPPAppMetadataByTeamAndTitleID(ctx, &team.ID, app1.TitleID)
require.NoError(t, err)
app2Meta, err = ds.GetVPPAppMetadataByTeamAndTitleID(ctx, &team.ID, app2.TitleID)
require.NoError(t, err)
require.Len(t, app1Meta.LabelsIncludeAny, 0)
require.Len(t, app1Meta.LabelsExcludeAny, 2)
for _, l := range app1Meta.LabelsExcludeAny {
_, ok := app1.VPPAppTeam.ValidatedLabels.ByName[l.LabelName]
require.True(t, ok)
}
require.Len(t, app2Meta.LabelsExcludeAny, 0)
require.Len(t, app2Meta.LabelsIncludeAny, 2)
for _, l := range app2Meta.LabelsIncludeAny {
_, ok := app2.VPPAppTeam.ValidatedLabels.ByName[l.LabelName]
require.True(t, ok)
}
}
func testGetAllVPPApps(t *testing.T, ds *Datastore) {
ctx := context.Background()
dataToken, err := test.CreateVPPTokenData(time.Now().Add(24*time.Hour), "Org"+t.Name(), "Location"+t.Name())
require.NoError(t, err)
tok1, err := ds.InsertVPPToken(ctx, dataToken)
assert.NoError(t, err)
_, err = ds.UpdateVPPTokenTeams(ctx, tok1.ID, []uint{})
assert.NoError(t, err)
app1 := &fleet.VPPApp{Name: "vpp_app_1", VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "1", Platform: fleet.MacOSPlatform}}, BundleIdentifier: "b1"}
_, err = ds.InsertVPPAppWithTeam(ctx, app1, nil)
require.NoError(t, err)
app2 := &fleet.VPPApp{Name: "vpp_app_2", VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "2", Platform: fleet.MacOSPlatform}}, BundleIdentifier: "b2"}
_, err = ds.InsertVPPAppWithTeam(ctx, app2, nil)
require.NoError(t, err)
app3 := &fleet.VPPApp{Name: "vpp_app_3", VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "3", Platform: fleet.MacOSPlatform}}, BundleIdentifier: "b3"}
_, err = ds.InsertVPPAppWithTeam(ctx, app3, nil)
require.NoError(t, err)
// Include an Android app. it shouldn't show up since this is an Apple-only operation.
app4 := &fleet.VPPApp{Name: "vpp_app_4", VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "com.an.android.app", Platform: fleet.AndroidPlatform}}, BundleIdentifier: "com.an.android.app"}
_, err = ds.InsertVPPAppWithTeam(ctx, app4, nil)
require.NoError(t, err)
// this method doesn't pull the VPPAppTeamID
app1.AppTeamID = 0
app2.AppTeamID = 0
app3.AppTeamID = 0
apps, err := ds.GetAllVPPApps(ctx)
require.NoError(t, err)
require.Equal(t, apps, []*fleet.VPPApp{app1, app2, app3})
}
func testGetUnverifiedVPPInstallsForHost(t *testing.T, ds *Datastore) {
ctx := context.Background()
test.CreateInsertGlobalVPPToken(t, ds)
va1, err := ds.InsertVPPAppWithTeam(ctx, &fleet.VPPApp{
Name: "vpp1", BundleIdentifier: "com.app.vpp1",
VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "adam_vpp_app_1", Platform: fleet.MacOSPlatform}},
}, nil)
require.NoError(t, err)
vpp1 := va1.VPPAppID
va2, err := ds.InsertVPPAppWithTeam(ctx, &fleet.VPPApp{
Name: "vpp2", BundleIdentifier: "com.app.vpp2",
VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "adam_vpp_app_2", Platform: fleet.MacOSPlatform}},
}, nil)
require.NoError(t, err)
vpp2 := va2.VPPAppID
va3, err := ds.InsertVPPAppWithTeam(ctx, &fleet.VPPApp{
Name: "vpp3", BundleIdentifier: "com.app.vpp3",
VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "adam_vpp_app_3", Platform: fleet.MacOSPlatform}},
}, nil)
require.NoError(t, err)
vpp3 := va3.VPPAppID
h1, err := ds.NewHost(ctx, &fleet.Host{
Hostname: "macos-test-1",
OsqueryHostID: ptr.String("osquery-macos-1"),
NodeKey: ptr.String("node-key-macos-1"),
UUID: uuid.NewString(),
Platform: "darwin",
HardwareSerial: "654321a",
})
require.NoError(t, err)
nanoEnroll(t, ds, h1, false)
cmdUUID1 := createVPPAppInstallRequest(t, ds, h1, vpp1.AdamID, nil)
createVPPAppInstallResult(t, ds, h1, cmdUUID1, "Acknowledged")
cmdUUID2 := createVPPAppInstallRequest(t, ds, h1, vpp2.AdamID, nil)
createVPPAppInstallResult(t, ds, h1, cmdUUID2, "Acknowledged")
cmdUUID3 := createVPPAppInstallRequest(t, ds, h1, vpp3.AdamID, nil)
createVPPAppInstallResult(t, ds, h1, cmdUUID3, "Acknowledged")
for _, step := range []struct {
installUUID string
before int
after int
}{
{installUUID: cmdUUID1, before: 3, after: 2},
{installUUID: cmdUUID2, before: 2, after: 1},
{installUUID: cmdUUID3, before: 1, after: 0},
} {
x, err := ds.GetUnverifiedVPPInstallsForHost(ctx, h1.UUID)
require.NoError(t, err)
assert.Len(t, x, step.before)
err = ds.SetVPPInstallAsVerified(ctx, h1.ID, step.installUUID, fleet.VerifySoftwareInstallCommandUUID())
require.NoError(t, err)
x, err = ds.GetUnverifiedVPPInstallsForHost(ctx, h1.UUID)
require.NoError(t, err)
assert.Len(t, x, step.after)
}
}
func testSoftwareTitleDisplayNameVPP(t *testing.T, ds *Datastore) {
ctx := context.Background()
test.CreateInsertGlobalVPPToken(t, ds)
va1, err := ds.InsertVPPAppWithTeam(ctx, &fleet.VPPApp{
Name: "vpp1", BundleIdentifier: "com.app.vpp1",
VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "adam_vpp_app_1", Platform: fleet.MacOSPlatform}},
}, nil)
require.NoError(t, err)
titleID := va1.TitleID
// Display name is empty by default
titles, _, _, err := ds.ListSoftwareTitles(
ctx,
fleet.SoftwareTitleListOptions{TeamID: ptr.Uint(0)},
fleet.TeamFilter{User: &fleet.User{
GlobalRole: ptr.String(fleet.RoleAdmin),
}},
)
require.NoError(t, err)
assert.Len(t, titles, 1)
assert.Empty(t, titles[0].DisplayName)
title, err := ds.SoftwareTitleByID(ctx, titleID, ptr.Uint(0), fleet.TeamFilter{})
require.NoError(t, err)
assert.Empty(t, title.DisplayName)
_, err = ds.InsertVPPAppWithTeam(ctx, &fleet.VPPApp{
Name: "vpp1", BundleIdentifier: "com.app.vpp1",
VPPAppTeam: fleet.VPPAppTeam{
VPPAppID: fleet.VPPAppID{AdamID: "adam_vpp_app_1", Platform: fleet.MacOSPlatform},
DisplayName: ptr.String("vpp_update1"),
},
}, nil)
require.NoError(t, err)
// Display name entry should be in join table
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
type result struct {
DisplayName string `db:"display_name"`
SoftwareTitleID uint `db:"software_title_id"`
TeamID uint `db:"team_id"`
}
var r []result
err := sqlx.SelectContext(ctx, q, &r, "SELECT display_name, software_title_id, team_id FROM software_title_display_names")
require.NoError(t, err)
assert.Len(t, r, 1)
assert.Equal(t, r[0], result{"vpp_update1", titleID, 0})
return nil
})
// List contains display name
titles, _, _, err = ds.ListSoftwareTitles(
ctx,
fleet.SoftwareTitleListOptions{TeamID: ptr.Uint(0)},
fleet.TeamFilter{User: &fleet.User{
GlobalRole: ptr.String(fleet.RoleAdmin),
}},
)
require.NoError(t, err)
assert.Len(t, titles, 1)
assert.Equal(t, "vpp_update1", titles[0].DisplayName)
// Entity contains display name
title, err = ds.SoftwareTitleByID(ctx, titleID, ptr.Uint(0), fleet.TeamFilter{})
require.NoError(t, err)
assert.Equal(t, "vpp_update1", title.DisplayName)
// Update the display name again, should see the change
_, err = ds.InsertVPPAppWithTeam(ctx, &fleet.VPPApp{
Name: "vpp1", BundleIdentifier: "com.app.vpp1",
VPPAppTeam: fleet.VPPAppTeam{
VPPAppID: fleet.VPPAppID{AdamID: "adam_vpp_app_1", Platform: fleet.MacOSPlatform},
DisplayName: ptr.String("vpp_update2"),
},
}, nil)
require.NoError(t, err)
// List contains display name
titles, _, _, err = ds.ListSoftwareTitles(
ctx,
fleet.SoftwareTitleListOptions{TeamID: ptr.Uint(0)},
fleet.TeamFilter{User: &fleet.User{
GlobalRole: ptr.String(fleet.RoleAdmin),
}},
)
require.NoError(t, err)
assert.Len(t, titles, 1)
assert.Equal(t, "vpp_update2", titles[0].DisplayName)
// Entity contains display name
title, err = ds.SoftwareTitleByID(ctx, titleID, ptr.Uint(0), fleet.TeamFilter{})
require.NoError(t, err)
assert.Equal(t, "vpp_update2", title.DisplayName)
// Update display name to be empty
_, err = ds.InsertVPPAppWithTeam(ctx, &fleet.VPPApp{
Name: "vpp1", BundleIdentifier: "com.app.vpp1",
VPPAppTeam: fleet.VPPAppTeam{
DisplayName: ptr.String(""),
VPPAppID: fleet.VPPAppID{AdamID: "adam_vpp_app_1", Platform: fleet.MacOSPlatform},
},
}, nil)
require.NoError(t, err)
// List contains display name
titles, _, _, err = ds.ListSoftwareTitles(
ctx,
fleet.SoftwareTitleListOptions{TeamID: ptr.Uint(0)},
fleet.TeamFilter{User: &fleet.User{
GlobalRole: ptr.String(fleet.RoleAdmin),
}},
)
require.NoError(t, err)
assert.Len(t, titles, 1)
assert.Empty(t, titles[0].DisplayName)
// Entity contains display name
title, err = ds.SoftwareTitleByID(ctx, titleID, ptr.Uint(0), fleet.TeamFilter{})
require.NoError(t, err)
assert.Empty(t, title.DisplayName)
// Delete vpp app, display name should be deleted
err = ds.DeleteVPPAppFromTeam(ctx, ptr.Uint(0), fleet.VPPAppID{AdamID: "adam_vpp_app_1", Platform: fleet.MacOSPlatform})
require.NoError(t, err)
_, err = ds.getSoftwareTitleDisplayName(ctx, 0, titleID)
require.ErrorContains(t, err, "not found")
}
func testAndroidVPPAppStatus(t *testing.T, ds *Datastore) {
ctx := context.Background()
// create a few android devices, including one on a team
tm, err := ds.NewTeam(ctx, &fleet.Team{Name: "team 1"})
require.NoError(t, err)
host1 := createAndroidHost(uuid.NewString())
host1, err = ds.NewAndroidHost(ctx, host1)
require.NoError(t, err)
host2 := createAndroidHost(uuid.NewString())
host2, err = ds.NewAndroidHost(ctx, host2)
require.NoError(t, err)
err = ds.AddHostsToTeam(ctx, fleet.NewAddHostsToTeamParams(&tm.ID, []uint{host2.Host.ID}))
require.NoError(t, err)
host2.Host.TeamID = &tm.ID
host3 := createAndroidHost(uuid.NewString())
host3, err = ds.NewAndroidHost(ctx, host3)
require.NoError(t, err)
// create no-team app
va1, err := ds.InsertVPPAppWithTeam(ctx, &fleet.VPPApp{
Name: "vpp1", BundleIdentifier: "com.app.vpp1",
VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "com.app.vpp1", Platform: fleet.AndroidPlatform}, SelfService: true},
}, nil)
require.NoError(t, err)
vpp1, titleID1 := va1.VPPAppID, va1.TitleID
// create team app
va2, err := ds.InsertVPPAppWithTeam(ctx, &fleet.VPPApp{
Name: "vpp2", BundleIdentifier: "com.app.vpp2",
VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "com.app.vpp2", Platform: fleet.AndroidPlatform}, SelfService: true},
}, &tm.ID)
require.NoError(t, err)
vpp2, titleID2 := va2.VPPAppID, va2.TitleID
installs, err := ds.ListHostMDMAndroidVPPAppsPendingInstallWithVersion(ctx, host1.Host.UUID, 100)
require.NoError(t, err)
require.Len(t, installs, 0)
// unknown host uuid returns nothing
installs, err = ds.ListHostMDMAndroidVPPAppsPendingInstallWithVersion(ctx, "no-such-uuid", 1)
require.NoError(t, err)
require.Len(t, installs, 0)
// insert pending install for no-team app on host1
cmdVpp1 := uuid.NewString()
err = ds.InsertAndroidSetupExperienceSoftwareInstall(ctx, &fleet.HostAndroidVPPSoftwareInstall{
HostID: host1.Host.ID,
AdamID: vpp1.AdamID,
CommandUUID: cmdVpp1,
AssociatedEventID: "1",
})
require.NoError(t, err)
// requesting pending installs with any version equal or greater returns it
installs, err = ds.ListHostMDMAndroidVPPAppsPendingInstallWithVersion(ctx, host1.Host.UUID, 1)
require.NoError(t, err)
require.Len(t, installs, 1)
require.Equal(t, cmdVpp1, installs[0].CommandUUID)
installs, err = ds.ListHostMDMAndroidVPPAppsPendingInstallWithVersion(ctx, host1.Host.UUID, 3)
require.NoError(t, err)
require.Len(t, installs, 1)
require.Equal(t, cmdVpp1, installs[0].CommandUUID)
// smaller version doesn't return it
installs, err = ds.ListHostMDMAndroidVPPAppsPendingInstallWithVersion(ctx, host1.Host.UUID, 0)
require.NoError(t, err)
require.Len(t, installs, 0)
// insert pending install for team app on host2
cmdVpp2 := uuid.NewString()
err = ds.InsertAndroidSetupExperienceSoftwareInstall(ctx, &fleet.HostAndroidVPPSoftwareInstall{
HostID: host2.Host.ID,
AdamID: vpp2.AdamID,
CommandUUID: cmdVpp2,
AssociatedEventID: "123",
})
require.NoError(t, err)
installs, err = ds.ListHostMDMAndroidVPPAppsPendingInstallWithVersion(ctx, host2.Host.UUID, 3)
require.NoError(t, err)
require.Len(t, installs, 0)
installs, err = ds.ListHostMDMAndroidVPPAppsPendingInstallWithVersion(ctx, host2.Host.UUID, 123)
require.NoError(t, err)
require.Len(t, installs, 1)
require.Equal(t, cmdVpp2, installs[0].CommandUUID)
// insert pending install for no-team app on host3
cmdVpp3 := uuid.NewString()
err = ds.InsertAndroidSetupExperienceSoftwareInstall(ctx, &fleet.HostAndroidVPPSoftwareInstall{
HostID: host3.Host.ID,
AdamID: vpp1.AdamID,
CommandUUID: cmdVpp3,
AssociatedEventID: "1",
})
require.NoError(t, err)
// list software available for install on no-team
tmFilter := fleet.TeamFilter{User: test.UserAdmin}
titles, _, _, err := ds.ListSoftwareTitles(ctx, fleet.SoftwareTitleListOptions{TeamID: ptr.Uint(0), Platform: "android", AvailableForInstall: true}, tmFilter)
require.NoError(t, err)
require.Len(t, titles, 1)
require.Equal(t, titleID1, titles[0].ID)
require.NotNil(t, titles[0].AppStoreApp)
require.Equal(t, titles[0].AppStoreApp.AppStoreID, vpp1.AdamID)
// list software available for install on team
titles, _, _, err = ds.ListSoftwareTitles(ctx, fleet.SoftwareTitleListOptions{TeamID: &tm.ID, Platform: "android", AvailableForInstall: true}, tmFilter)
require.NoError(t, err)
require.Len(t, titles, 1)
require.Equal(t, titleID2, titles[0].ID)
require.NotNil(t, titles[0].AppStoreApp)
require.Equal(t, titles[0].AppStoreApp.AppStoreID, vpp2.AdamID)
// list host software, should show as pending
hostTitles, _, err := ds.ListHostSoftware(ctx, host1.Host, fleet.HostSoftwareTitleListOptions{IncludeAvailableForInstall: true})
require.NoError(t, err)
require.Len(t, hostTitles, 1)
require.NotNil(t, hostTitles[0].AppStoreApp)
require.Equal(t, vpp1.AdamID, hostTitles[0].AppStoreApp.AppStoreID)
require.NotNil(t, hostTitles[0].Status)
require.Equal(t, fleet.SoftwareInstallPending, *hostTitles[0].Status)
hostTitles, _, err = ds.ListHostSoftware(ctx, host2.Host, fleet.HostSoftwareTitleListOptions{IncludeAvailableForInstall: true})
require.NoError(t, err)
require.Len(t, hostTitles, 1)
require.NotNil(t, hostTitles[0].AppStoreApp)
require.Equal(t, vpp2.AdamID, hostTitles[0].AppStoreApp.AppStoreID)
require.NotNil(t, hostTitles[0].Status)
require.Equal(t, fleet.SoftwareInstallPending, *hostTitles[0].Status)
// bulk-set nothing as verified
err = ds.BulkSetVPPInstallsAsVerified(ctx, host1.Host.ID, []string{})
require.NoError(t, err)
summary, err := ds.GetSummaryHostVPPAppInstalls(ctx, nil, vpp1)
require.NoError(t, err)
require.Equal(t, &fleet.VPPAppStatusSummary{Pending: 2}, summary)
summary, err = ds.GetSummaryHostVPPAppInstalls(ctx, &tm.ID, vpp2)
require.NoError(t, err)
require.Equal(t, &fleet.VPPAppStatusSummary{Pending: 1}, summary)
// mark vpp1 as installed on host1
err = ds.BulkSetVPPInstallsAsVerified(ctx, host1.Host.ID, []string{cmdVpp1})
require.NoError(t, err)
summary, err = ds.GetSummaryHostVPPAppInstalls(ctx, nil, vpp1)
require.NoError(t, err)
require.Equal(t, &fleet.VPPAppStatusSummary{Pending: 1, Installed: 1}, summary)
// mark vpp2 as failed on host2
err = ds.BulkSetVPPInstallsAsFailed(ctx, host2.Host.ID, []string{cmdVpp2})
require.NoError(t, err)
summary, err = ds.GetSummaryHostVPPAppInstalls(ctx, &tm.ID, vpp2)
require.NoError(t, err)
require.Equal(t, &fleet.VPPAppStatusSummary{Failed: 1}, summary)
// get vpp command result for an android command returns nothing (but doesn't fail)
res, err := ds.GetVPPCommandResults(ctx, cmdVpp1, host1.Host.UUID)
require.NoError(t, err)
require.Nil(t, res)
// set an unknown host install or an unknown app ID as pending due to config change has no effect
err = ds.SetAndroidAppInstallPendingApplyConfig(ctx, "no-such-host", vpp1.AdamID, 123)
require.NoError(t, err)
err = ds.SetAndroidAppInstallPendingApplyConfig(ctx, host1.Host.UUID, "no-such-app-id", 123)
require.NoError(t, err)
// list hosts filtering by vpp1 installed status
hosts, err := ds.ListHosts(ctx, tmFilter, fleet.HostListOptions{SoftwareTitleIDFilter: &titleID1, SoftwareStatusFilter: ptr.T(fleet.SoftwareInstalled)})
require.NoError(t, err)
require.Len(t, hosts, 1)
require.Equal(t, host1.Host.ID, hosts[0].ID)
// list hosts filtering by vpp2 failed status
hosts, err = ds.ListHosts(ctx, tmFilter, fleet.HostListOptions{TeamFilter: &tm.ID, SoftwareTitleIDFilter: &titleID2, SoftwareStatusFilter: ptr.T(fleet.SoftwareInstallFailed)})
require.NoError(t, err)
require.Len(t, hosts, 1)
require.Equal(t, host2.Host.ID, hosts[0].ID)
// list hosts filtering by vpp2 pending status
hosts, err = ds.ListHosts(ctx, tmFilter, fleet.HostListOptions{TeamFilter: &tm.ID, SoftwareTitleIDFilter: &titleID2, SoftwareStatusFilter: ptr.T(fleet.SoftwareInstallPending)})
require.NoError(t, err)
require.Len(t, hosts, 0)
// list hosts filtering by vpp1 pending status
hosts, err = ds.ListHosts(ctx, tmFilter, fleet.HostListOptions{TeamFilter: nil, SoftwareTitleIDFilter: &titleID1, SoftwareStatusFilter: ptr.T(fleet.SoftwareInstallPending)})
require.NoError(t, err)
require.Len(t, hosts, 1)
require.Equal(t, host3.Host.ID, hosts[0].ID)
// set vpp1 as pending due to config change on host1
err = ds.SetAndroidAppInstallPendingApplyConfig(ctx, host1.Host.UUID, vpp1.AdamID, 123)
require.NoError(t, err)
// now nothing is returned for Installed for vpp1
hosts, err = ds.ListHosts(ctx, tmFilter, fleet.HostListOptions{SoftwareTitleIDFilter: &titleID1, SoftwareStatusFilter: ptr.T(fleet.SoftwareInstalled)})
require.NoError(t, err)
require.Len(t, hosts, 0)
// and host1 is returned for pending for vpp1
hosts, err = ds.ListHosts(ctx, tmFilter, fleet.HostListOptions{TeamFilter: nil, SoftwareTitleIDFilter: &titleID1, SoftwareStatusFilter: ptr.T(fleet.SoftwareInstallPending), ListOptions: fleet.ListOptions{OrderKey: "id"}})
require.NoError(t, err)
require.Len(t, hosts, 2)
require.Equal(t, host1.Host.ID, hosts[0].ID)
require.Equal(t, host3.Host.ID, hosts[1].ID)
// set vpp2 as pending due to config change on host2
err = ds.SetAndroidAppInstallPendingApplyConfig(ctx, host2.Host.UUID, vpp2.AdamID, 123)
require.NoError(t, err)
// has no effect because the install was failed, it stays failed
// list hosts filtering by vpp2 failed status
hosts, err = ds.ListHosts(ctx, tmFilter, fleet.HostListOptions{TeamFilter: &tm.ID, SoftwareTitleIDFilter: &titleID2, SoftwareStatusFilter: ptr.T(fleet.SoftwareInstallFailed)})
require.NoError(t, err)
require.Len(t, hosts, 1)
require.Equal(t, host2.Host.ID, hosts[0].ID)
}
func testGetVPPAppInstallStatusByCommandUUID(t *testing.T, ds *Datastore) {
ctx := context.Background()
// team
tm, err := ds.NewTeam(ctx, &fleet.Team{Name: "team1"})
require.NoError(t, err)
// Create VPP token for the team
dataToken, err := test.CreateVPPTokenData(time.Now().Add(24*time.Hour), "Test org"+t.Name(), "Test location"+t.Name())
require.NoError(t, err)
tok1, err := ds.InsertVPPToken(ctx, dataToken)
require.NoError(t, err)
_, err = ds.UpdateVPPTokenTeams(ctx, tok1.ID, []uint{tm.ID})
require.NoError(t, err)
// user
user := test.NewUser(t, ds, "Alice", "alice@example.com", true)
// create host
host := test.NewHost(t, ds, "host1", "", "host1key", "host1uuid", time.Now(), test.WithPlatform("darwin"))
nanoEnroll(t, ds, host, false)
err = ds.AddHostsToTeam(ctx, fleet.NewAddHostsToTeamParams(&tm.ID, []uint{host.ID}))
require.NoError(t, err)
host.TeamID = &tm.ID
// create host2 with different UUID and hostname
host2 := test.NewHost(t, ds, "host2", "", "host2key", "host2uuid", time.Now(), test.WithPlatform("darwin"))
nanoEnroll(t, ds, host2, false)
err = ds.AddHostsToTeam(ctx, fleet.NewAddHostsToTeamParams(&tm.ID, []uint{host2.ID}))
require.NoError(t, err)
host2.TeamID = &tm.ID
vPPApp := &fleet.VPPApp{
VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "adam_vpp_1", Platform: fleet.MacOSPlatform}},
Name: "vpp1",
BundleIdentifier: "com.app.vpp1",
LatestVersion: "1.0.0",
}
va1, err := ds.InsertVPPAppWithTeam(ctx, vPPApp, &tm.ID)
require.NoError(t, err)
vpp1 := va1.AdamID
// Insert software entry for vpp app
res, err := ds.writer(ctx).ExecContext(ctx, `
INSERT INTO software (name, version, source, bundle_identifier, title_id, checksum)
VALUES (?, ?, ?, ?, ?, ?)
`,
vPPApp.Name, "0.1.1", "apps", vPPApp.BundleIdentifier, vPPApp.TitleID, hex.EncodeToString([]byte("vpp1")),
)
require.NoError(t, err)
vppSoftwareID, err := res.LastInsertId()
require.NoError(t, err)
// install on host 1
vpp1CmdUUID := createVPPAppInstallRequest(t, ds, host, vpp1, user)
_, err = ds.activateNextUpcomingActivity(ctx, ds.writer(ctx), host.ID, "")
require.NoError(t, err)
createVPPAppInstallResult(t, ds, host, vpp1CmdUUID, fleet.MDMAppleStatusAcknowledged)
_, err = ds.writer(ctx).ExecContext(ctx, `
INSERT INTO host_software (host_id, software_id)
VALUES (?, ?)
`, host.ID, vppSoftwareID)
require.NoError(t, err)
// host 2 no entry in host_software
vpp2CmdUUID := createVPPAppInstallRequest(t, ds, host2, vpp1, user)
_, err = ds.activateNextUpcomingActivity(ctx, ds.writer(ctx), host2.ID, "")
require.NoError(t, err)
createVPPAppInstallResult(t, ds, host2, vpp2CmdUUID, fleet.MDMAppleStatusAcknowledged)
// Test 1: Get install status for host1 (app is installed)
isInstalled, err := ds.GetVPPAppInstallStatusByCommandUUID(ctx, vpp1CmdUUID)
require.NoError(t, err)
require.True(t, isInstalled, "app should be installed on host1")
// Test 2: Get install status for host2 (app is NOT installed)
isInstalled, err = ds.GetVPPAppInstallStatusByCommandUUID(ctx, vpp2CmdUUID)
require.NoError(t, err)
require.False(t, isInstalled, "app should NOT be installed on host2")
// Test 3: Get install status for non-existent command UUID (returns false)
isInstalled, err = ds.GetVPPAppInstallStatusByCommandUUID(ctx, "non-existent-uuid")
require.NoError(t, err)
require.False(t, isInstalled, "should return false for non-existent command")
}
func testAndroidAppConfigs(t *testing.T, ds *Datastore) {
ctx := context.Background()
// Create a team
team, err := ds.NewTeam(ctx, &fleet.Team{Name: "androids"})
require.NoError(t, err)
// Insert some VPP apps for no team
app1 := &fleet.VPPApp{Name: "android_app_1", VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "1", Platform: fleet.AndroidPlatform}}, BundleIdentifier: "b1"}
_, err = ds.InsertVPPAppWithTeam(ctx, app1, nil)
require.NoError(t, err)
app2 := &fleet.VPPApp{Name: "android_app_2", VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "2", Platform: fleet.AndroidPlatform}}, BundleIdentifier: "b2"}
_, err = ds.InsertVPPAppWithTeam(ctx, app2, nil)
require.NoError(t, err)
app3 := &fleet.VPPApp{Name: "android_app_3", VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "3", Platform: fleet.AndroidPlatform}}, BundleIdentifier: "b3"}
_, err = ds.InsertVPPAppWithTeam(ctx, app3, nil)
require.NoError(t, err)
app4 := &fleet.VPPApp{Name: "android_app_4", VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "4", Platform: fleet.AndroidPlatform}}, BundleIdentifier: "b4"}
_, err = ds.InsertVPPAppWithTeam(ctx, app4, nil)
require.NoError(t, err)
config1 := json.RawMessage(`{"workProfileWidgets":"WORK_PROFILE_WIDGETS_ALLOWED", "managedConfiguration": {"1":1}}`)
expectedConfig1 := json.RawMessage(`{"workProfileWidgets": "WORK_PROFILE_WIDGETS_ALLOWED", "managedConfiguration": {"1": 1}}`)
_, err = ds.SetTeamVPPApps(ctx, &team.ID, []fleet.VPPAppTeam{
{VPPAppID: app1.VPPAppID, SelfService: true, DisplayName: ptr.String("name 1")},
{VPPAppID: app2.VPPAppID, SelfService: true, DisplayName: ptr.String("name 2"), Configuration: json.RawMessage(nil)},
{VPPAppID: app3.VPPAppID, SelfService: true, DisplayName: ptr.String("name 3"), Configuration: json.RawMessage(`{}`)},
{VPPAppID: app4.VPPAppID, SelfService: true, DisplayName: ptr.String("name 4"), Configuration: config1},
}, map[string]uint{"1_android": 1, "2_android": 2, "3_android": 3, "4_android": 4})
require.NoError(t, err)
assigned, err := ds.GetAssignedVPPApps(ctx, &team.ID)
require.NoError(t, err)
require.Len(t, assigned, 4)
for _, a := range assigned {
config, err := ds.GetAndroidAppConfiguration(ctx, a.AdamID, team.ID)
require.True(t, err == nil || fleet.IsNotFound(err))
if config != nil {
a.Configuration = config.Configuration
assigned[a.VPPAppID] = a
}
}
require.Equal(t, json.RawMessage(nil), assigned[app1.VPPAppID].Configuration)
require.Equal(t, json.RawMessage(nil), assigned[app2.VPPAppID].Configuration)
require.Equal(t, json.RawMessage(`{}`), assigned[app3.VPPAppID].Configuration)
require.Equal(t, expectedConfig1, assigned[app4.VPPAppID].Configuration)
_, err = ds.SetTeamVPPApps(ctx, &team.ID, []fleet.VPPAppTeam{
{VPPAppID: app1.VPPAppID}, // stays nil
{VPPAppID: app2.VPPAppID, Configuration: json.RawMessage(`{"managedConfiguration": 1}`)}, // updates
{VPPAppID: app3.VPPAppID, Configuration: json.RawMessage(nil)},
{VPPAppID: app4.VPPAppID, Configuration: config1},
}, map[string]uint{"1": 1, "2": 2, "3": 3, "4": 4})
require.NoError(t, err)
assigned, err = ds.GetAssignedVPPApps(ctx, &team.ID)
require.NoError(t, err)
require.Len(t, assigned, 4)
for _, a := range assigned {
config, err := ds.GetAndroidAppConfiguration(ctx, a.AdamID, team.ID)
require.True(t, err == nil || fleet.IsNotFound(err))
if config != nil {
a.Configuration = config.Configuration
assigned[a.VPPAppID] = a
}
}
require.Equal(t, json.RawMessage(nil), assigned[app1.VPPAppID].Configuration)
require.Equal(t, json.RawMessage(`{"managedConfiguration": 1}`), assigned[app2.VPPAppID].Configuration)
require.Equal(t, json.RawMessage(`{}`), assigned[app3.VPPAppID].Configuration)
require.Equal(t, expectedConfig1, assigned[app4.VPPAppID].Configuration)
// Delete all
_, err = ds.SetTeamVPPApps(ctx, &team.ID, []fleet.VPPAppTeam{}, nil)
require.NoError(t, err)
}