ISII: Make setup_experience gitops flag on app_store_apps enable setup for all platforms an app supports (#34461)

<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #30890 

Unreleased bug(or requirements miss) in iOS/iPadOS setup experience. Per
@noahtalerman we want setup_experience on VPP apps to apply to all
platforms an app is available for:
https://github.com/fleetdm/fleet/issues/30890#issuecomment-3408445511

This will change with the upcoming inclusion of the `platform` field and
it will be possible to specify settings for each individual OS

# Checklist for submitter

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

- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)
- [x] If paths of existing endpoints are modified without backwards
compatibility, checked the frontend/CLI for any necessary changes

## Testing

- [x] Added/updated automated tests

- [x] QA'd all new/changed functionality manually
This commit is contained in:
Jordan Montgomery 2025-10-17 15:16:34 -04:00 committed by GitHub
parent 1c0b004676
commit 23a4b781fd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 97 additions and 13 deletions

View file

@ -72,19 +72,21 @@ func (svc *Service) BatchAssociateVPPApps(ctx context.Context, teamName string,
// Currently only macOS is supported for self-service. Don't
// import vpp apps as self-service for ios or ipados
payloadsWithPlatform = append(payloadsWithPlatform, []fleet.VPPBatchPayloadWithPlatform{{
AppStoreID: payload.AppStoreID,
SelfService: false,
Platform: fleet.IOSPlatform,
LabelsExcludeAny: payload.LabelsExcludeAny,
LabelsIncludeAny: payload.LabelsIncludeAny,
Categories: payload.Categories,
AppStoreID: payload.AppStoreID,
SelfService: false,
InstallDuringSetup: payload.InstallDuringSetup,
Platform: fleet.IOSPlatform,
LabelsExcludeAny: payload.LabelsExcludeAny,
LabelsIncludeAny: payload.LabelsIncludeAny,
Categories: payload.Categories,
}, {
AppStoreID: payload.AppStoreID,
SelfService: false,
Platform: fleet.IPadOSPlatform,
LabelsExcludeAny: payload.LabelsExcludeAny,
LabelsIncludeAny: payload.LabelsIncludeAny,
Categories: payload.Categories,
AppStoreID: payload.AppStoreID,
SelfService: false,
InstallDuringSetup: payload.InstallDuringSetup,
Platform: fleet.IPadOSPlatform,
LabelsExcludeAny: payload.LabelsExcludeAny,
LabelsIncludeAny: payload.LabelsIncludeAny,
Categories: payload.Categories,
}, {
AppStoreID: payload.AppStoreID,
SelfService: payload.SelfService,
@ -418,7 +420,8 @@ func (svc *Service) AddAppStoreApp(ctx context.Context, teamID *uint, appID flee
if exists {
return 0, ctxerr.Wrap(ctx, fleet.ConflictError{
Message: fmt.Sprintf(fleet.CantAddSoftwareConflictMessage,
assetMD.TrackName, teamName)}, "vpp app conflicts with existing software installer")
assetMD.TrackName, teamName),
}, "vpp app conflicts with existing software installer")
}
}

View file

@ -11152,6 +11152,87 @@ func (s *integrationMDMTestSuite) TestBatchAssociateAppStoreApps() {
assoc, err = s.ds.GetAssignedVPPApps(ctx, &tmGood.ID)
require.NoError(t, err)
require.Len(t, assoc, 0)
// Test InstallDuringSetup
// Helper functions for verifying what's enabled for setup experience
getReturnedSetupExperienceAdamIDs := func(titles []fleet.SoftwareTitleListResult) []string {
var adamIDs []string
for _, title := range titles {
if (title.AppStoreApp != nil && title.AppStoreApp.InstallDuringSetup != nil && *title.AppStoreApp.InstallDuringSetup == true) ||
(title.SoftwarePackage != nil && title.SoftwarePackage.InstallDuringSetup != nil && *title.SoftwarePackage.InstallDuringSetup == true) {
adamIDs = append(adamIDs, title.AppStoreApp.AppStoreID)
}
}
return adamIDs
}
checkSetupExperienceVPP := func(t *testing.T, platform string, teamID uint, expectedAdamIDs []string) {
var respGetSetupExperience getSetupExperienceSoftwareResponse
s.DoJSON("GET", "/api/latest/fleet/setup_experience/software", getSetupExperienceSoftwareRequest{},
http.StatusOK,
&respGetSetupExperience,
"platform", platform,
"team_id", fmt.Sprint(teamID),
)
assert.ElementsMatch(t, getReturnedSetupExperienceAdamIDs(respGetSetupExperience.SoftwareTitles), expectedAdamIDs)
}
// Associate with the SetupExperience flag set to true
s.DoJSON("POST",
batchURL,
batchAssociateAppStoreAppsRequest{
Apps: []fleet.VPPBatchPayload{
// macOS only
{AppStoreID: s.appleVPPConfigSrvConfig.Assets[0].AdamID, InstallDuringSetup: ptr.Bool(true)},
// macOS, iOS, iPadOS
{AppStoreID: s.appleVPPConfigSrvConfig.Assets[1].AdamID, SelfService: true, InstallDuringSetup: ptr.Bool(true)},
// iPadOS only
{AppStoreID: s.appleVPPConfigSrvConfig.Assets[2].AdamID, SelfService: true, InstallDuringSetup: ptr.Bool(true)},
},
}, http.StatusOK, &batchAssociateResponse, "team_name", tmGood.Name,
)
checkSetupExperienceVPP(t, "macos", tmGood.ID, []string{s.appleVPPConfigSrvConfig.Assets[0].AdamID, s.appleVPPConfigSrvConfig.Assets[1].AdamID})
checkSetupExperienceVPP(t, string(fleet.IPadOSPlatform), tmGood.ID, []string{s.appleVPPConfigSrvConfig.Assets[1].AdamID, s.appleVPPConfigSrvConfig.Assets[2].AdamID})
checkSetupExperienceVPP(t, string(fleet.IOSPlatform), tmGood.ID, []string{s.appleVPPConfigSrvConfig.Assets[1].AdamID})
// Associate with the SetupExperience flag set to nil = no change
s.DoJSON("POST",
batchURL,
batchAssociateAppStoreAppsRequest{
Apps: []fleet.VPPBatchPayload{
// macOS only
{AppStoreID: s.appleVPPConfigSrvConfig.Assets[0].AdamID, InstallDuringSetup: nil},
// macOS, iOS, iPadOS
{AppStoreID: s.appleVPPConfigSrvConfig.Assets[1].AdamID, SelfService: true, InstallDuringSetup: nil},
// iPadOS only
{AppStoreID: s.appleVPPConfigSrvConfig.Assets[2].AdamID, SelfService: true, InstallDuringSetup: nil},
},
}, http.StatusOK, &batchAssociateResponse, "team_name", tmGood.Name,
)
checkSetupExperienceVPP(t, "macos", tmGood.ID, []string{s.appleVPPConfigSrvConfig.Assets[0].AdamID, s.appleVPPConfigSrvConfig.Assets[1].AdamID})
checkSetupExperienceVPP(t, string(fleet.IPadOSPlatform), tmGood.ID, []string{s.appleVPPConfigSrvConfig.Assets[1].AdamID, s.appleVPPConfigSrvConfig.Assets[2].AdamID})
checkSetupExperienceVPP(t, string(fleet.IOSPlatform), tmGood.ID, []string{s.appleVPPConfigSrvConfig.Assets[1].AdamID})
// Associate with the SetupExperience flag set to false
s.DoJSON("POST",
batchURL,
batchAssociateAppStoreAppsRequest{
Apps: []fleet.VPPBatchPayload{
// macOS only
{AppStoreID: s.appleVPPConfigSrvConfig.Assets[0].AdamID, InstallDuringSetup: ptr.Bool(false)},
// macOS, iOS, iPadOS
{AppStoreID: s.appleVPPConfigSrvConfig.Assets[1].AdamID, SelfService: true, InstallDuringSetup: ptr.Bool(false)},
// iPadOS only
{AppStoreID: s.appleVPPConfigSrvConfig.Assets[2].AdamID, SelfService: true, InstallDuringSetup: ptr.Bool(false)},
},
}, http.StatusOK, &batchAssociateResponse, "team_name", tmGood.Name,
)
checkSetupExperienceVPP(t, "macos", tmGood.ID, []string{})
checkSetupExperienceVPP(t, string(fleet.IPadOSPlatform), tmGood.ID, []string{})
checkSetupExperienceVPP(t, string(fleet.IOSPlatform), tmGood.ID, []string{})
}
func (s *integrationMDMTestSuite) TestInvalidCommandUUID() {