SE: CLI setup experience changes (#22956)
This commit is contained in:
@@ -0,0 +1 @@
|
||||
* Added support to `fleetctl gitops` to specify a setup experience script to run and software to install, for a team or no team.
|
||||
@@ -94,7 +94,7 @@ func applyCommand() *cli.Command {
|
||||
teamsSoftwareInstallers := make(map[string][]fleet.SoftwarePackageResponse)
|
||||
teamsScripts := make(map[string][]fleet.ScriptResponse)
|
||||
|
||||
_, _, _, err = fleetClient.ApplyGroup(c.Context, specs, baseDir, logf, nil, opts, teamsSoftwareInstallers, teamsScripts)
|
||||
_, _, _, err = fleetClient.ApplyGroup(c.Context, false, specs, baseDir, logf, nil, opts, teamsSoftwareInstallers, teamsScripts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -438,6 +438,9 @@ func TestGitOpsBasicTeam(t *testing.T) {
|
||||
ds.ListSoftwareTitlesFunc = func(ctx context.Context, opt fleet.SoftwareTitleListOptions, tmFilter fleet.TeamFilter) ([]fleet.SoftwareTitleListResult, int, *fleet.PaginationMetadata, error) {
|
||||
return nil, 0, nil, nil
|
||||
}
|
||||
ds.DeleteSetupExperienceScriptFunc = func(ctx context.Context, teamID *uint) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
tmpFile, err := os.CreateTemp(t.TempDir(), "*.yml")
|
||||
require.NoError(t, err)
|
||||
@@ -903,6 +906,9 @@ func TestGitOpsFullTeam(t *testing.T) {
|
||||
ds.ListSoftwareTitlesFunc = func(ctx context.Context, opt fleet.SoftwareTitleListOptions, tmFilter fleet.TeamFilter) ([]fleet.SoftwareTitleListResult, int, *fleet.PaginationMetadata, error) {
|
||||
return nil, 0, nil, nil
|
||||
}
|
||||
ds.DeleteSetupExperienceScriptFunc = func(ctx context.Context, teamID *uint) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
startSoftwareInstallerServer(t)
|
||||
|
||||
@@ -1159,6 +1165,9 @@ func TestGitOpsBasicGlobalAndTeam(t *testing.T) {
|
||||
ds.ListABMTokensFunc = func(ctx context.Context) ([]*fleet.ABMToken, error) {
|
||||
return []*fleet.ABMToken{}, nil
|
||||
}
|
||||
ds.DeleteSetupExperienceScriptFunc = func(ctx context.Context, teamID *uint) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
globalFile, err := os.CreateTemp(t.TempDir(), "*.yml")
|
||||
require.NoError(t, err)
|
||||
@@ -1437,6 +1446,9 @@ func TestGitOpsBasicGlobalAndNoTeam(t *testing.T) {
|
||||
ds.ListABMTokensFunc = func(ctx context.Context) ([]*fleet.ABMToken, error) {
|
||||
return []*fleet.ABMToken{}, nil
|
||||
}
|
||||
ds.DeleteSetupExperienceScriptFunc = func(ctx context.Context, teamID *uint) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
globalFileBasic, err := os.CreateTemp(t.TempDir(), "*.yml")
|
||||
require.NoError(t, err)
|
||||
@@ -2354,6 +2366,9 @@ func setupFullGitOpsPremiumServer(t *testing.T) (*mock.Store, **fleet.AppConfig,
|
||||
ds.ListABMTokensFunc = func(ctx context.Context) ([]*fleet.ABMToken, error) {
|
||||
return []*fleet.ABMToken{}, nil
|
||||
}
|
||||
ds.DeleteSetupExperienceScriptFunc = func(ctx context.Context, teamID *uint) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
t.Setenv("FLEET_SERVER_URL", fleetServerURL)
|
||||
t.Setenv("ORG_NAME", orgName)
|
||||
|
||||
@@ -389,7 +389,7 @@ Use the stop and reset subcommands to manage the server and dependencies once st
|
||||
// so pass in the current working directory.
|
||||
teamsSoftwareInstallers := make(map[string][]fleet.SoftwarePackageResponse)
|
||||
teamsScripts := make(map[string][]fleet.ScriptResponse)
|
||||
_, _, _, err = client.ApplyGroup(c.Context, specs, ".", logf, nil, fleet.ApplyClientSpecOptions{}, teamsSoftwareInstallers, teamsScripts)
|
||||
_, _, _, err = client.ApplyGroup(c.Context, false, specs, ".", logf, nil, fleet.ApplyClientSpecOptions{}, teamsSoftwareInstallers, teamsScripts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -130,7 +130,9 @@
|
||||
"bootstrap_package": null,
|
||||
"enable_end_user_authentication": false,
|
||||
"macos_setup_assistant": null,
|
||||
"enable_release_device_manually": false
|
||||
"enable_release_device_manually": false,
|
||||
"script": null,
|
||||
"software": null
|
||||
},
|
||||
"windows_settings": {
|
||||
"custom_settings": null
|
||||
|
||||
@@ -50,6 +50,8 @@ spec:
|
||||
enable_end_user_authentication: false
|
||||
enable_release_device_manually: false
|
||||
macos_setup_assistant:
|
||||
script:
|
||||
software:
|
||||
windows_settings:
|
||||
custom_settings: null
|
||||
end_user_authentication:
|
||||
|
||||
@@ -82,7 +82,9 @@
|
||||
"bootstrap_package": null,
|
||||
"enable_end_user_authentication": false,
|
||||
"macos_setup_assistant": null,
|
||||
"enable_release_device_manually": false
|
||||
"enable_release_device_manually": false,
|
||||
"script": null,
|
||||
"software": null
|
||||
},
|
||||
"windows_settings": {
|
||||
"custom_settings": null
|
||||
|
||||
@@ -50,6 +50,8 @@ spec:
|
||||
enable_end_user_authentication: false
|
||||
enable_release_device_manually: false
|
||||
macos_setup_assistant:
|
||||
script:
|
||||
software:
|
||||
windows_settings:
|
||||
custom_settings:
|
||||
end_user_authentication:
|
||||
|
||||
+6
-2
@@ -54,7 +54,9 @@
|
||||
"bootstrap_package": null,
|
||||
"enable_end_user_authentication": false,
|
||||
"macos_setup_assistant": null,
|
||||
"enable_release_device_manually": false
|
||||
"enable_release_device_manually": false,
|
||||
"script": null,
|
||||
"software": null
|
||||
},
|
||||
"windows_settings": {
|
||||
"custom_settings": null
|
||||
@@ -137,7 +139,9 @@
|
||||
"bootstrap_package": null,
|
||||
"enable_end_user_authentication": false,
|
||||
"macos_setup_assistant": null,
|
||||
"enable_release_device_manually": false
|
||||
"enable_release_device_manually": false,
|
||||
"script": null,
|
||||
"software": null
|
||||
},
|
||||
"windows_settings": {
|
||||
"custom_settings": null
|
||||
|
||||
@@ -34,6 +34,8 @@ spec:
|
||||
enable_end_user_authentication: false
|
||||
enable_release_device_manually: false
|
||||
macos_setup_assistant:
|
||||
script:
|
||||
software:
|
||||
scripts: null
|
||||
secrets: null
|
||||
webhook_settings:
|
||||
@@ -84,6 +86,8 @@ spec:
|
||||
enable_end_user_authentication: false
|
||||
enable_release_device_manually: false
|
||||
macos_setup_assistant:
|
||||
script:
|
||||
software:
|
||||
scripts: null
|
||||
webhook_settings:
|
||||
host_status_webhook: null
|
||||
|
||||
@@ -40,6 +40,8 @@ spec:
|
||||
enable_end_user_authentication: false
|
||||
macos_setup_assistant: null
|
||||
enable_release_device_manually: false
|
||||
script: null
|
||||
software: null
|
||||
macos_updates:
|
||||
deadline: null
|
||||
minimum_version: null
|
||||
|
||||
@@ -40,6 +40,8 @@ spec:
|
||||
enable_end_user_authentication: false
|
||||
macos_setup_assistant: %s
|
||||
enable_release_device_manually: false
|
||||
script: null
|
||||
software: null
|
||||
macos_updates:
|
||||
deadline: null
|
||||
minimum_version: null
|
||||
|
||||
@@ -22,6 +22,8 @@ spec:
|
||||
enable_end_user_authentication: false
|
||||
macos_setup_assistant: null
|
||||
enable_release_device_manually: false
|
||||
script: null
|
||||
software: null
|
||||
macos_updates:
|
||||
deadline: null
|
||||
minimum_version: null
|
||||
@@ -62,6 +64,8 @@ spec:
|
||||
bootstrap_package: null
|
||||
macos_setup_assistant: null
|
||||
enable_release_device_manually: false
|
||||
script: null
|
||||
software: null
|
||||
macos_updates:
|
||||
deadline: null
|
||||
minimum_version: null
|
||||
|
||||
@@ -22,6 +22,8 @@ spec:
|
||||
enable_end_user_authentication: false
|
||||
macos_setup_assistant: %s
|
||||
enable_release_device_manually: false
|
||||
script: null
|
||||
software: null
|
||||
macos_updates:
|
||||
deadline: null
|
||||
minimum_version: null
|
||||
@@ -62,6 +64,8 @@ spec:
|
||||
bootstrap_package: %s
|
||||
macos_setup_assistant: %s
|
||||
enable_release_device_manually: false
|
||||
script: null
|
||||
software: null
|
||||
macos_updates:
|
||||
deadline: null
|
||||
minimum_version: null
|
||||
|
||||
@@ -20,6 +20,8 @@ spec:
|
||||
enable_end_user_authentication: false
|
||||
macos_setup_assistant: null
|
||||
enable_release_device_manually: false
|
||||
script: null
|
||||
software: null
|
||||
macos_updates:
|
||||
deadline: null
|
||||
minimum_version: null
|
||||
|
||||
@@ -21,6 +21,8 @@ spec:
|
||||
enable_end_user_authentication: false
|
||||
macos_setup_assistant: %s
|
||||
enable_release_device_manually: false
|
||||
script: null
|
||||
software: null
|
||||
macos_updates:
|
||||
deadline: null
|
||||
minimum_version: null
|
||||
|
||||
@@ -1301,15 +1301,16 @@ func (svc *Service) softwareBatchUpload(
|
||||
}
|
||||
|
||||
installer := &fleet.UploadSoftwareInstallerPayload{
|
||||
TeamID: teamID,
|
||||
InstallScript: p.InstallScript,
|
||||
PreInstallQuery: p.PreInstallQuery,
|
||||
PostInstallScript: p.PostInstallScript,
|
||||
UninstallScript: p.UninstallScript,
|
||||
InstallerFile: bytes.NewReader(bodyBytes),
|
||||
SelfService: p.SelfService,
|
||||
UserID: userID,
|
||||
URL: p.URL,
|
||||
TeamID: teamID,
|
||||
InstallScript: p.InstallScript,
|
||||
PreInstallQuery: p.PreInstallQuery,
|
||||
PostInstallScript: p.PostInstallScript,
|
||||
UninstallScript: p.UninstallScript,
|
||||
InstallerFile: bytes.NewReader(bodyBytes),
|
||||
SelfService: p.SelfService,
|
||||
UserID: userID,
|
||||
URL: p.URL,
|
||||
InstallDuringSetup: p.InstallDuringSetup,
|
||||
}
|
||||
|
||||
// set the filename before adding metadata, as it is used as fallback
|
||||
|
||||
@@ -1396,6 +1396,15 @@ func (svc *Service) editTeamFromSpec(
|
||||
}
|
||||
}
|
||||
|
||||
// if the setup experience script was cleared, remove it for that team
|
||||
if spec.MDM.MacOSSetup.Script.Set &&
|
||||
spec.MDM.MacOSSetup.Script.Value == "" &&
|
||||
oldMacOSSetup.Script.Value != "" {
|
||||
if err := svc.DeleteSetupExperienceScript(ctx, &team.ID); err != nil {
|
||||
return ctxerr.Wrapf(ctx, err, "clear setup experience script for team %d", team.ID)
|
||||
}
|
||||
}
|
||||
|
||||
if didUpdateMacOSEndUserAuth {
|
||||
if err := svc.updateMacOSSetupEnableEndUserAuth(
|
||||
ctx, spec.MDM.MacOSSetup.EnableEndUserAuthentication, &team.ID, &team.Name,
|
||||
|
||||
+21
-11
@@ -79,9 +79,10 @@ func (svc *Service) BatchAssociateVPPApps(ctx context.Context, teamName string,
|
||||
SelfService: false,
|
||||
Platform: fleet.IPadOSPlatform,
|
||||
}, {
|
||||
AppStoreID: payload.AppStoreID,
|
||||
SelfService: payload.SelfService,
|
||||
Platform: fleet.MacOSPlatform,
|
||||
AppStoreID: payload.AppStoreID,
|
||||
SelfService: payload.SelfService,
|
||||
Platform: fleet.MacOSPlatform,
|
||||
InstallDuringSetup: payload.InstallDuringSetup,
|
||||
}}...)
|
||||
}
|
||||
|
||||
@@ -101,7 +102,14 @@ func (svc *Service) BatchAssociateVPPApps(ctx context.Context, teamName string,
|
||||
return fleet.NewInvalidArgumentError("app_store_apps.platform",
|
||||
fmt.Sprintf("platform must be one of '%s', '%s', or '%s", fleet.IOSPlatform, fleet.IPadOSPlatform, fleet.MacOSPlatform))
|
||||
}
|
||||
vppAppTeams = append(vppAppTeams, fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: payload.AppStoreID, Platform: payload.Platform}, SelfService: payload.SelfService})
|
||||
vppAppTeams = append(vppAppTeams, fleet.VPPAppTeam{
|
||||
VPPAppID: fleet.VPPAppID{
|
||||
AdamID: payload.AppStoreID,
|
||||
Platform: payload.Platform,
|
||||
},
|
||||
SelfService: payload.SelfService,
|
||||
InstallDuringSetup: payload.InstallDuringSetup,
|
||||
})
|
||||
}
|
||||
|
||||
var missingAssets []string
|
||||
@@ -374,14 +382,15 @@ func (svc *Service) AddAppStoreApp(ctx context.Context, teamID *uint, appID flee
|
||||
func getVPPAppsMetadata(ctx context.Context, ids []fleet.VPPAppTeam) ([]*fleet.VPPApp, error) {
|
||||
var apps []*fleet.VPPApp
|
||||
|
||||
// Map of adamID to platform, then to whether it's available as self-service.
|
||||
adamIDMap := make(map[string]map[fleet.AppleDevicePlatform]bool)
|
||||
// Map of adamID to platform, then to whether it's available as self-service
|
||||
// and installed during setup.
|
||||
adamIDMap := make(map[string]map[fleet.AppleDevicePlatform]fleet.VPPAppTeam)
|
||||
for _, id := range ids {
|
||||
if _, ok := adamIDMap[id.AdamID]; !ok {
|
||||
adamIDMap[id.AdamID] = make(map[fleet.AppleDevicePlatform]bool, 1)
|
||||
adamIDMap[id.AdamID][id.Platform] = id.SelfService
|
||||
adamIDMap[id.AdamID] = make(map[fleet.AppleDevicePlatform]fleet.VPPAppTeam, 1)
|
||||
adamIDMap[id.AdamID][id.Platform] = fleet.VPPAppTeam{SelfService: id.SelfService, InstallDuringSetup: id.InstallDuringSetup}
|
||||
} else {
|
||||
adamIDMap[id.AdamID][id.Platform] = id.SelfService
|
||||
adamIDMap[id.AdamID][id.Platform] = fleet.VPPAppTeam{SelfService: id.SelfService, InstallDuringSetup: id.InstallDuringSetup}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -397,14 +406,15 @@ func getVPPAppsMetadata(ctx context.Context, ids []fleet.VPPAppTeam) ([]*fleet.V
|
||||
for adamID, metadata := range assetMetatada {
|
||||
platforms := getPlatformsFromSupportedDevices(metadata.SupportedDevices)
|
||||
for platform := range platforms {
|
||||
if selfService, ok := adamIDMap[adamID][platform]; ok {
|
||||
if props, ok := adamIDMap[adamID][platform]; ok {
|
||||
app := &fleet.VPPApp{
|
||||
VPPAppTeam: fleet.VPPAppTeam{
|
||||
VPPAppID: fleet.VPPAppID{
|
||||
AdamID: adamID,
|
||||
Platform: platform,
|
||||
},
|
||||
SelfService: selfService,
|
||||
SelfService: props.SelfService,
|
||||
InstallDuringSetup: props.InstallDuringSetup,
|
||||
},
|
||||
BundleIdentifier: metadata.BundleID,
|
||||
IconURL: metadata.ArtworkURL,
|
||||
|
||||
+2
-1
@@ -757,7 +757,8 @@ func parseSoftware(top map[string]json.RawMessage, result *GitOps, baseDir strin
|
||||
for _, item := range software.Packages {
|
||||
var softwarePackageSpec fleet.SoftwarePackageSpec
|
||||
if item.Path != nil {
|
||||
fileBytes, err := os.ReadFile(resolveApplyRelativePath(baseDir, *item.Path))
|
||||
softwarePackageSpec.ReferencedYamlPath = resolveApplyRelativePath(baseDir, *item.Path)
|
||||
fileBytes, err := os.ReadFile(softwarePackageSpec.ReferencedYamlPath)
|
||||
if err != nil {
|
||||
multiError = multierror.Append(multiError, fmt.Errorf("failed to read policies file %s: %v", *item.Path, err))
|
||||
continue
|
||||
|
||||
@@ -65,7 +65,7 @@ CREATE TABLE `app_config_json` (
|
||||
UNIQUE KEY `id` (`id`)
|
||||
) /*!50100 TABLESPACE `innodb_system` */ ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
INSERT INTO `app_config_json` VALUES (1,'{\"mdm\": {\"ios_updates\": {\"deadline\": null, \"minimum_version\": null}, \"macos_setup\": {\"bootstrap_package\": null, \"macos_setup_assistant\": null, \"enable_end_user_authentication\": false, \"enable_release_device_manually\": false}, \"macos_updates\": {\"deadline\": null, \"minimum_version\": null}, \"ipados_updates\": {\"deadline\": null, \"minimum_version\": null}, \"macos_settings\": {\"custom_settings\": null}, \"macos_migration\": {\"mode\": \"\", \"enable\": false, \"webhook_url\": \"\"}, \"windows_updates\": {\"deadline_days\": null, \"grace_period_days\": null}, \"apple_server_url\": \"\", \"windows_settings\": {\"custom_settings\": null}, \"apple_bm_terms_expired\": false, \"apple_business_manager\": null, \"enable_disk_encryption\": false, \"enabled_and_configured\": false, \"end_user_authentication\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"issuer_uri\": \"\", \"metadata_url\": \"\"}, \"volume_purchasing_program\": null, \"windows_enabled_and_configured\": false, \"apple_bm_enabled_and_configured\": false}, \"scripts\": null, \"features\": {\"enable_host_users\": true, \"enable_software_inventory\": false}, \"org_info\": {\"org_name\": \"\", \"contact_url\": \"\", \"org_logo_url\": \"\", \"org_logo_url_light_background\": \"\"}, \"integrations\": {\"jira\": null, \"zendesk\": null, \"google_calendar\": null, \"ndes_scep_proxy\": null}, \"sso_settings\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"enable_sso\": false, \"issuer_uri\": \"\", \"metadata_url\": \"\", \"idp_image_url\": \"\", \"enable_jit_role_sync\": false, \"enable_sso_idp_login\": false, \"enable_jit_provisioning\": false}, \"agent_options\": {\"config\": {\"options\": {\"logger_plugin\": \"tls\", \"pack_delimiter\": \"/\", \"logger_tls_period\": 10, \"distributed_plugin\": \"tls\", \"disable_distributed\": false, \"logger_tls_endpoint\": \"/api/osquery/log\", \"distributed_interval\": 10, \"distributed_tls_max_attempts\": 3}, \"decorators\": {\"load\": [\"SELECT uuid AS host_uuid FROM system_info;\", \"SELECT hostname AS hostname FROM system_info;\"]}}, \"overrides\": {}}, \"fleet_desktop\": {\"transparency_url\": \"\"}, \"smtp_settings\": {\"port\": 587, \"domain\": \"\", \"server\": \"\", \"password\": \"\", \"user_name\": \"\", \"configured\": false, \"enable_smtp\": false, \"enable_ssl_tls\": true, \"sender_address\": \"\", \"enable_start_tls\": true, \"verify_ssl_certs\": true, \"authentication_type\": \"0\", \"authentication_method\": \"0\"}, \"server_settings\": {\"server_url\": \"\", \"enable_analytics\": false, \"query_report_cap\": 0, \"scripts_disabled\": false, \"deferred_save_host\": false, \"live_query_disabled\": false, \"ai_features_disabled\": false, \"query_reports_disabled\": false}, \"webhook_settings\": {\"interval\": \"0s\", \"activities_webhook\": {\"destination_url\": \"\", \"enable_activities_webhook\": false}, \"host_status_webhook\": {\"days_count\": 0, \"destination_url\": \"\", \"host_percentage\": 0, \"enable_host_status_webhook\": false}, \"vulnerabilities_webhook\": {\"destination_url\": \"\", \"host_batch_size\": 0, \"enable_vulnerabilities_webhook\": false}, \"failing_policies_webhook\": {\"policy_ids\": null, \"destination_url\": \"\", \"host_batch_size\": 0, \"enable_failing_policies_webhook\": false}}, \"host_expiry_settings\": {\"host_expiry_window\": 0, \"host_expiry_enabled\": false}, \"vulnerability_settings\": {\"databases_path\": \"\"}, \"activity_expiry_settings\": {\"activity_expiry_window\": 0, \"activity_expiry_enabled\": false}}','2020-01-01 01:01:01','2020-01-01 01:01:01');
|
||||
INSERT INTO `app_config_json` VALUES (1,'{\"mdm\": {\"ios_updates\": {\"deadline\": null, \"minimum_version\": null}, \"macos_setup\": {\"script\": null, \"software\": null, \"bootstrap_package\": null, \"macos_setup_assistant\": null, \"enable_end_user_authentication\": false, \"enable_release_device_manually\": false}, \"macos_updates\": {\"deadline\": null, \"minimum_version\": null}, \"ipados_updates\": {\"deadline\": null, \"minimum_version\": null}, \"macos_settings\": {\"custom_settings\": null}, \"macos_migration\": {\"mode\": \"\", \"enable\": false, \"webhook_url\": \"\"}, \"windows_updates\": {\"deadline_days\": null, \"grace_period_days\": null}, \"apple_server_url\": \"\", \"windows_settings\": {\"custom_settings\": null}, \"apple_bm_terms_expired\": false, \"apple_business_manager\": null, \"enable_disk_encryption\": false, \"enabled_and_configured\": false, \"end_user_authentication\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"issuer_uri\": \"\", \"metadata_url\": \"\"}, \"volume_purchasing_program\": null, \"windows_enabled_and_configured\": false, \"apple_bm_enabled_and_configured\": false}, \"scripts\": null, \"features\": {\"enable_host_users\": true, \"enable_software_inventory\": false}, \"org_info\": {\"org_name\": \"\", \"contact_url\": \"\", \"org_logo_url\": \"\", \"org_logo_url_light_background\": \"\"}, \"integrations\": {\"jira\": null, \"zendesk\": null, \"google_calendar\": null, \"ndes_scep_proxy\": null}, \"sso_settings\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"enable_sso\": false, \"issuer_uri\": \"\", \"metadata_url\": \"\", \"idp_image_url\": \"\", \"enable_jit_role_sync\": false, \"enable_sso_idp_login\": false, \"enable_jit_provisioning\": false}, \"agent_options\": {\"config\": {\"options\": {\"logger_plugin\": \"tls\", \"pack_delimiter\": \"/\", \"logger_tls_period\": 10, \"distributed_plugin\": \"tls\", \"disable_distributed\": false, \"logger_tls_endpoint\": \"/api/osquery/log\", \"distributed_interval\": 10, \"distributed_tls_max_attempts\": 3}, \"decorators\": {\"load\": [\"SELECT uuid AS host_uuid FROM system_info;\", \"SELECT hostname AS hostname FROM system_info;\"]}}, \"overrides\": {}}, \"fleet_desktop\": {\"transparency_url\": \"\"}, \"smtp_settings\": {\"port\": 587, \"domain\": \"\", \"server\": \"\", \"password\": \"\", \"user_name\": \"\", \"configured\": false, \"enable_smtp\": false, \"enable_ssl_tls\": true, \"sender_address\": \"\", \"enable_start_tls\": true, \"verify_ssl_certs\": true, \"authentication_type\": \"0\", \"authentication_method\": \"0\"}, \"server_settings\": {\"server_url\": \"\", \"enable_analytics\": false, \"query_report_cap\": 0, \"scripts_disabled\": false, \"deferred_save_host\": false, \"live_query_disabled\": false, \"ai_features_disabled\": false, \"query_reports_disabled\": false}, \"webhook_settings\": {\"interval\": \"0s\", \"activities_webhook\": {\"destination_url\": \"\", \"enable_activities_webhook\": false}, \"host_status_webhook\": {\"days_count\": 0, \"destination_url\": \"\", \"host_percentage\": 0, \"enable_host_status_webhook\": false}, \"vulnerabilities_webhook\": {\"destination_url\": \"\", \"host_batch_size\": 0, \"enable_vulnerabilities_webhook\": false}, \"failing_policies_webhook\": {\"policy_ids\": null, \"destination_url\": \"\", \"host_batch_size\": 0, \"enable_failing_policies_webhook\": false}}, \"host_expiry_settings\": {\"host_expiry_window\": 0, \"host_expiry_enabled\": false}, \"vulnerability_settings\": {\"databases_path\": \"\"}, \"activity_expiry_settings\": {\"activity_expiry_window\": 0, \"activity_expiry_enabled\": false}}','2020-01-01 01:01:01','2020-01-01 01:01:01');
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `calendar_events` (
|
||||
|
||||
@@ -85,7 +85,7 @@ WHERE global_or_team_id = ?`
|
||||
if err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "retrieving number of inserted software installers")
|
||||
}
|
||||
totalInsertions += uint(inserts)
|
||||
totalInsertions += uint(inserts) // nolint: gosec
|
||||
|
||||
// VPP apps
|
||||
res, err = tx.ExecContext(ctx, stmtVPPApps, hostUUID, teamID)
|
||||
@@ -96,7 +96,7 @@ WHERE global_or_team_id = ?`
|
||||
if err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "retrieving number of inserted vpp apps")
|
||||
}
|
||||
totalInsertions += uint(inserts)
|
||||
totalInsertions += uint(inserts) // nolint: gosec
|
||||
|
||||
// Scripts
|
||||
res, err = tx.ExecContext(ctx, stmtSetupScripts, hostUUID, teamID)
|
||||
@@ -107,7 +107,7 @@ WHERE global_or_team_id = ?`
|
||||
if err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "retrieving number of inserted setup experience scripts")
|
||||
}
|
||||
totalInsertions += uint(inserts)
|
||||
totalInsertions += uint(inserts) // nolint: gosec
|
||||
|
||||
if err := setHostAwaitingConfiguration(ctx, tx, hostUUID, true); err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "setting host awaiting configuration to true")
|
||||
@@ -403,7 +403,7 @@ func (ds *Datastore) SetSetupExperienceScript(ctx context.Context, script *fleet
|
||||
id, _ := scRes.LastInsertId()
|
||||
|
||||
// then create the script entity
|
||||
_, err = insertSetupExperienceScript(ctx, tx, script, uint(id))
|
||||
_, err = insertSetupExperienceScript(ctx, tx, script, uint(id)) // nolint: gosec
|
||||
return err
|
||||
})
|
||||
|
||||
|
||||
@@ -186,13 +186,13 @@ func testEnqueueSetupExperienceItems(t *testing.T, ds *Datastore) {
|
||||
HostUUID: hostTeam1,
|
||||
Name: "script1",
|
||||
Status: "pending",
|
||||
ScriptID: nullableUint(uint(script1ID)),
|
||||
ScriptID: nullableUint(uint(script1ID)), // nolint: gosec
|
||||
},
|
||||
{
|
||||
HostUUID: hostTeam2,
|
||||
Name: "script2",
|
||||
Status: "pending",
|
||||
ScriptID: nullableUint(uint(script2ID)),
|
||||
ScriptID: nullableUint(uint(script2ID)), // nolint: gosec
|
||||
},
|
||||
} {
|
||||
var found bool
|
||||
@@ -267,7 +267,7 @@ func testEnqueueSetupExperienceItems(t *testing.T, ds *Datastore) {
|
||||
HostUUID: hostTeam1,
|
||||
Name: "script1",
|
||||
Status: "pending",
|
||||
ScriptID: nullableUint(uint(script1ID)),
|
||||
ScriptID: nullableUint(uint(script1ID)), // nolint: gosec
|
||||
},
|
||||
} {
|
||||
var found bool
|
||||
@@ -303,7 +303,7 @@ type setupExperienceInsertTestRows struct {
|
||||
}
|
||||
|
||||
func nullableUint(val uint) sql.NullInt64 {
|
||||
return sql.NullInt64{Int64: int64(val), Valid: true}
|
||||
return sql.NullInt64{Int64: int64(val), Valid: true} // nolint: gosec
|
||||
}
|
||||
|
||||
func testGetSetupExperienceTitles(t *testing.T, ds *Datastore) {
|
||||
@@ -668,7 +668,7 @@ func testSetupExperienceStatusResults(t *testing.T, ds *Datastore) {
|
||||
require.NoError(t, err)
|
||||
id, err := res.LastInsertId()
|
||||
require.NoError(t, err)
|
||||
scriptID = uint(id)
|
||||
scriptID = uint(id) // nolint: gosec
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -680,7 +680,7 @@ func testSetupExperienceStatusResults(t *testing.T, ds *Datastore) {
|
||||
require.NoError(t, err)
|
||||
id, err := res.LastInsertId()
|
||||
require.NoError(t, err)
|
||||
sesr.ID = uint(id)
|
||||
sesr.ID = uint(id) // nolint: gosec
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -847,16 +847,6 @@ WHERE
|
||||
team_id = ?
|
||||
`
|
||||
|
||||
const countInstallDuringSetupAllInstallersInTeam = `
|
||||
SELECT
|
||||
COUNT(*)
|
||||
FROM
|
||||
software_installers
|
||||
WHERE
|
||||
global_or_team_id = ? AND
|
||||
install_during_setup = 1
|
||||
`
|
||||
|
||||
const deleteAllInstallersInTeam = `
|
||||
DELETE FROM
|
||||
software_installers
|
||||
@@ -881,8 +871,8 @@ WHERE
|
||||
SELECT
|
||||
COUNT(*)
|
||||
FROM
|
||||
software_installers
|
||||
WHERE
|
||||
software_installers
|
||||
WHERE
|
||||
global_or_team_id = ? AND
|
||||
title_id NOT IN (?) AND
|
||||
install_during_setup = 1
|
||||
@@ -926,11 +916,12 @@ INSERT INTO software_installers (
|
||||
user_name,
|
||||
user_email,
|
||||
url,
|
||||
package_ids
|
||||
package_ids,
|
||||
install_during_setup
|
||||
) VALUES (
|
||||
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
||||
(SELECT id FROM software_titles WHERE name = ? AND source = ? AND browser = ''),
|
||||
?, (SELECT name FROM users WHERE id = ?), (SELECT email FROM users WHERE id = ?), ?, ?
|
||||
?, (SELECT name FROM users WHERE id = ?), (SELECT email FROM users WHERE id = ?), ?, ?, COALESCE(?, false)
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
install_script_content_id = VALUES(install_script_content_id),
|
||||
@@ -946,7 +937,8 @@ ON DUPLICATE KEY UPDATE
|
||||
user_id = VALUES(user_id),
|
||||
user_name = VALUES(user_name),
|
||||
user_email = VALUES(user_email),
|
||||
url = VALUES(url)
|
||||
url = VALUES(url),
|
||||
install_during_setup = COALESCE(?, install_during_setup)
|
||||
`
|
||||
|
||||
// use a team id of 0 if no-team
|
||||
@@ -955,20 +947,19 @@ ON DUPLICATE KEY UPDATE
|
||||
globalOrTeamID = *tmID
|
||||
}
|
||||
|
||||
// if we're batch-setting installers and replacing the ones installed during
|
||||
// setup in the same go, no need to validate that we don't delete one marked
|
||||
// as install during setup (since we're overwriting those). This is always
|
||||
// called from fleetctl gitops, so it should always be the case anyway.
|
||||
var replacingInstallDuringSetup bool
|
||||
if len(installers) == 0 || installers[0].InstallDuringSetup != nil {
|
||||
replacingInstallDuringSetup = true
|
||||
}
|
||||
|
||||
if err := ds.withRetryTxx(ctx, func(tx sqlx.ExtContext) error {
|
||||
// if no installers are provided, just delete whatever was in
|
||||
// the table
|
||||
if len(installers) == 0 {
|
||||
// check if any existing installer is install_during_setup, fail if there is one
|
||||
// TODO: will need to be reconciled with https://github.com/fleetdm/fleet/issues/22385
|
||||
var countInstallDuringSetup int
|
||||
if err := sqlx.GetContext(ctx, tx, &countInstallDuringSetup, countInstallDuringSetupAllInstallersInTeam, globalOrTeamID); err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "check installers installed during setup")
|
||||
}
|
||||
if countInstallDuringSetup > 0 {
|
||||
return errDeleteInstallerInstalledDuringSetup
|
||||
}
|
||||
|
||||
if _, err := tx.ExecContext(ctx, unsetAllInstallersFromPolicies, globalOrTeamID); err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "unset all obsolete installers in policies")
|
||||
}
|
||||
@@ -1005,17 +996,18 @@ ON DUPLICATE KEY UPDATE
|
||||
}
|
||||
|
||||
// check if any in the list are install_during_setup, fail if there is one
|
||||
// TODO: will need to be reconciled with https://github.com/fleetdm/fleet/issues/22385
|
||||
stmt, args, err = sqlx.In(countInstallDuringSetupNotInList, globalOrTeamID, titleIDs)
|
||||
if err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "build statement to check installers install_during_setup")
|
||||
}
|
||||
var countInstallDuringSetup int
|
||||
if err := sqlx.GetContext(ctx, tx, &countInstallDuringSetup, stmt, args...); err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "check installers installed during setup")
|
||||
}
|
||||
if countInstallDuringSetup > 0 {
|
||||
return errDeleteInstallerInstalledDuringSetup
|
||||
if !replacingInstallDuringSetup {
|
||||
stmt, args, err = sqlx.In(countInstallDuringSetupNotInList, globalOrTeamID, titleIDs)
|
||||
if err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "build statement to check installers install_during_setup")
|
||||
}
|
||||
var countInstallDuringSetup int
|
||||
if err := sqlx.GetContext(ctx, tx, &countInstallDuringSetup, stmt, args...); err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "check installers installed during setup")
|
||||
}
|
||||
if countInstallDuringSetup > 0 {
|
||||
return errDeleteInstallerInstalledDuringSetup
|
||||
}
|
||||
}
|
||||
|
||||
stmt, args, err = sqlx.In(deleteInstallersNotInList, globalOrTeamID, titleIDs)
|
||||
@@ -1100,6 +1092,8 @@ ON DUPLICATE KEY UPDATE
|
||||
installer.UserID,
|
||||
installer.URL,
|
||||
strings.Join(installer.PackageIDs, ","),
|
||||
installer.InstallDuringSetup,
|
||||
installer.InstallDuringSetup,
|
||||
}
|
||||
upsertQuery := insertNewOrEditedInstaller
|
||||
if len(existing) > 0 && existing[0].IsPackageModified { // update uploaded_at for updated installer package
|
||||
|
||||
@@ -719,21 +719,23 @@ func testBatchSetSoftwareInstallers(t *testing.T, ds *Datastore) {
|
||||
})
|
||||
|
||||
// add a new installer + ins0 installer
|
||||
// mark ins0 as install_during_setup
|
||||
ins1 := "installer1"
|
||||
ins1File := bytes.NewReader([]byte("installer1"))
|
||||
err = ds.BatchSetSoftwareInstallers(ctx, &team.ID, []*fleet.UploadSoftwareInstallerPayload{
|
||||
{
|
||||
InstallScript: "install",
|
||||
InstallerFile: ins0File,
|
||||
StorageID: ins0,
|
||||
Filename: ins0,
|
||||
Title: ins0,
|
||||
Source: "apps",
|
||||
Version: "1",
|
||||
PreInstallQuery: "select 0 from foo;",
|
||||
UserID: user1.ID,
|
||||
Platform: "darwin",
|
||||
URL: "https://example.com",
|
||||
InstallScript: "install",
|
||||
InstallerFile: ins0File,
|
||||
StorageID: ins0,
|
||||
Filename: ins0,
|
||||
Title: ins0,
|
||||
Source: "apps",
|
||||
Version: "1",
|
||||
PreInstallQuery: "select 0 from foo;",
|
||||
UserID: user1.ID,
|
||||
Platform: "darwin",
|
||||
URL: "https://example.com",
|
||||
InstallDuringSetup: ptr.Bool(true),
|
||||
},
|
||||
{
|
||||
InstallScript: "install",
|
||||
@@ -767,12 +769,6 @@ func testBatchSetSoftwareInstallers(t *testing.T, ds *Datastore) {
|
||||
{Name: ins1, Source: "apps", Browser: ""},
|
||||
})
|
||||
|
||||
// mark ins0 as install_during_setup
|
||||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||||
_, err := q.ExecContext(ctx, ` UPDATE software_installers SET install_during_setup = 1 WHERE title_id = ?`, *softwareInstallers[0].TitleID)
|
||||
return err
|
||||
})
|
||||
|
||||
// remove ins0 fails due to install_during_setup
|
||||
err = ds.BatchSetSoftwareInstallers(ctx, &team.ID, []*fleet.UploadSoftwareInstallerPayload{
|
||||
{
|
||||
@@ -791,16 +787,72 @@ func testBatchSetSoftwareInstallers(t *testing.T, ds *Datastore) {
|
||||
require.Error(t, err)
|
||||
require.ErrorIs(t, err, errDeleteInstallerInstalledDuringSetup)
|
||||
|
||||
// remove everything fails due to ins0 install_during_setup
|
||||
err = ds.BatchSetSoftwareInstallers(ctx, &team.ID, []*fleet.UploadSoftwareInstallerPayload{})
|
||||
require.Error(t, err)
|
||||
require.ErrorIs(t, err, errDeleteInstallerInstalledDuringSetup)
|
||||
// batch-set both installers again, this time with nil install_during_setup for ins0,
|
||||
// will keep it as true.
|
||||
err = ds.BatchSetSoftwareInstallers(ctx, &team.ID, []*fleet.UploadSoftwareInstallerPayload{
|
||||
{
|
||||
InstallScript: "install",
|
||||
InstallerFile: ins0File,
|
||||
StorageID: ins0,
|
||||
Filename: ins0,
|
||||
Title: ins0,
|
||||
Source: "apps",
|
||||
Version: "1",
|
||||
PreInstallQuery: "select 0 from foo;",
|
||||
UserID: user1.ID,
|
||||
Platform: "darwin",
|
||||
URL: "https://example.com",
|
||||
InstallDuringSetup: nil,
|
||||
},
|
||||
{
|
||||
InstallScript: "install",
|
||||
PostInstallScript: "post-install",
|
||||
InstallerFile: ins1File,
|
||||
StorageID: ins1,
|
||||
Filename: ins1,
|
||||
Title: ins1,
|
||||
Source: "apps",
|
||||
Version: "2",
|
||||
PreInstallQuery: "select 1 from bar;",
|
||||
UserID: user1.ID,
|
||||
Platform: "darwin",
|
||||
URL: "https://example2.com",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// mark ins0 as NOT install_during_setup
|
||||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||||
_, err := q.ExecContext(ctx, ` UPDATE software_installers SET install_during_setup = 0 WHERE title_id = ?`, *softwareInstallers[0].TitleID)
|
||||
return err
|
||||
err = ds.BatchSetSoftwareInstallers(ctx, &team.ID, []*fleet.UploadSoftwareInstallerPayload{
|
||||
{
|
||||
InstallScript: "install",
|
||||
InstallerFile: ins0File,
|
||||
StorageID: ins0,
|
||||
Filename: ins0,
|
||||
Title: ins0,
|
||||
Source: "apps",
|
||||
Version: "1",
|
||||
PreInstallQuery: "select 0 from foo;",
|
||||
UserID: user1.ID,
|
||||
Platform: "darwin",
|
||||
URL: "https://example.com",
|
||||
InstallDuringSetup: ptr.Bool(false),
|
||||
},
|
||||
{
|
||||
InstallScript: "install",
|
||||
PostInstallScript: "post-install",
|
||||
InstallerFile: ins1File,
|
||||
StorageID: ins1,
|
||||
Filename: ins1,
|
||||
Title: ins1,
|
||||
Source: "apps",
|
||||
Version: "2",
|
||||
PreInstallQuery: "select 1 from bar;",
|
||||
UserID: user1.ID,
|
||||
Platform: "darwin",
|
||||
URL: "https://example2.com",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// remove ins0
|
||||
err = ds.BatchSetSoftwareInstallers(ctx, &team.ID, []*fleet.UploadSoftwareInstallerPayload{
|
||||
|
||||
@@ -642,6 +642,8 @@ func testTeamsMDMConfig(t *testing.T, ds *Datastore) {
|
||||
BootstrapPackage: optjson.SetString("bootstrap"),
|
||||
MacOSSetupAssistant: optjson.SetString("assistant"),
|
||||
EnableReleaseDeviceManually: optjson.SetBool(false),
|
||||
Script: optjson.String{Set: true},
|
||||
Software: optjson.Slice[*fleet.MacOSSetupSoftware]{Set: true, Value: []*fleet.MacOSSetupSoftware{}},
|
||||
},
|
||||
WindowsSettings: fleet.WindowsSettings{
|
||||
CustomSettings: optjson.SetSlice([]fleet.MDMProfileSpec{{Path: "foo"}, {Path: "bar"}}),
|
||||
|
||||
@@ -169,6 +169,15 @@ func (ds *Datastore) SetTeamVPPApps(ctx context.Context, teamID *uint, appFleets
|
||||
return ctxerr.Wrap(ctx, err, "SetTeamVPPApps getting list of existing apps")
|
||||
}
|
||||
|
||||
// if we're batch-setting apps and replacing the ones installed during setup
|
||||
// in the same go, no need to validate that we don't delete one marked as
|
||||
// install during setup (since we're overwriting those). This is always
|
||||
// called from fleetctl gitops, so it should always be the case anyway.
|
||||
var replacingInstallDuringSetup bool
|
||||
if len(appFleets) == 0 || appFleets[0].InstallDuringSetup != nil {
|
||||
replacingInstallDuringSetup = true
|
||||
}
|
||||
|
||||
var toAddApps []fleet.VPPAppTeam
|
||||
var toRemoveApps []fleet.VPPAppID
|
||||
|
||||
@@ -181,9 +190,8 @@ func (ds *Datastore) SetTeamVPPApps(ctx context.Context, teamID *uint, appFleets
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
// if app is marked as install during setup, prevent deletion.
|
||||
// TODO: will need to be reconciled with https://github.com/fleetdm/fleet/issues/22385
|
||||
if appTeamInfo.InstallDuringSetup {
|
||||
// if app is marked as install during setup, prevent deletion unless we're replacing those.
|
||||
if !replacingInstallDuringSetup && appTeamInfo.InstallDuringSetup != nil && *appTeamInfo.InstallDuringSetup {
|
||||
return errDeleteInstallerInstalledDuringSetup
|
||||
}
|
||||
toRemoveApps = append(toRemoveApps, existingApp)
|
||||
@@ -191,7 +199,10 @@ func (ds *Datastore) SetTeamVPPApps(ctx context.Context, teamID *uint, appFleets
|
||||
}
|
||||
|
||||
for _, appFleet := range appFleets {
|
||||
if existingFleet, ok := existingApps[appFleet.VPPAppID]; !ok || existingFleet.SelfService != appFleet.SelfService {
|
||||
// upsert it if it does not exist or SelfService or InstallDuringSetup flags are changed
|
||||
if existingFleet, ok := existingApps[appFleet.VPPAppID]; !ok || existingFleet.SelfService != appFleet.SelfService ||
|
||||
appFleet.InstallDuringSetup != nil &&
|
||||
existingFleet.InstallDuringSetup != nil && *appFleet.InstallDuringSetup != *existingFleet.InstallDuringSetup {
|
||||
toAddApps = append(toAddApps, appFleet)
|
||||
}
|
||||
}
|
||||
@@ -310,11 +321,13 @@ ON DUPLICATE KEY UPDATE
|
||||
func insertVPPAppTeams(ctx context.Context, tx sqlx.ExtContext, appID fleet.VPPAppTeam, teamID *uint, vppTokenID uint) error {
|
||||
stmt := `
|
||||
INSERT INTO vpp_apps_teams
|
||||
(adam_id, global_or_team_id, team_id, platform, self_service, vpp_token_id)
|
||||
(adam_id, global_or_team_id, team_id, platform, self_service, vpp_token_id, install_during_setup)
|
||||
VALUES
|
||||
(?, ?, ?, ?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE self_service = VALUES(self_service)
|
||||
`
|
||||
(?, ?, ?, ?, ?, ?, COALESCE(?, false))
|
||||
ON DUPLICATE KEY UPDATE
|
||||
self_service = VALUES(self_service),
|
||||
install_during_setup = COALESCE(?, install_during_setup)
|
||||
`
|
||||
|
||||
var globalOrTmID uint
|
||||
if teamID != nil {
|
||||
@@ -325,7 +338,7 @@ ON DUPLICATE KEY UPDATE self_service = VALUES(self_service)
|
||||
}
|
||||
}
|
||||
|
||||
_, err := tx.ExecContext(ctx, stmt, appID.AdamID, globalOrTmID, teamID, appID.Platform, appID.SelfService, vppTokenID)
|
||||
_, err := tx.ExecContext(ctx, stmt, appID.AdamID, globalOrTmID, teamID, appID.Platform, appID.SelfService, vppTokenID, appID.InstallDuringSetup, appID.InstallDuringSetup)
|
||||
if IsDuplicate(err) {
|
||||
err = &existsError{
|
||||
Identifier: fmt.Sprintf("%s %s self_service: %v", appID.AdamID, appID.Platform, appID.SelfService),
|
||||
|
||||
@@ -511,11 +511,17 @@ func testVPPApps(t *testing.T, ds *Datastore) {
|
||||
// Check that getting the assigned apps works
|
||||
appSet, err := ds.GetAssignedVPPApps(ctx, &team.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, map[fleet.VPPAppID]fleet.VPPAppTeam{app1.VPPAppID: {VPPAppID: app1.VPPAppID}, app2.VPPAppID: {VPPAppID: app2.VPPAppID}}, appSet)
|
||||
assert.Equal(t, map[fleet.VPPAppID]fleet.VPPAppTeam{
|
||||
app1.VPPAppID: {VPPAppID: app1.VPPAppID, InstallDuringSetup: ptr.Bool(false)},
|
||||
app2.VPPAppID: {VPPAppID: app2.VPPAppID, InstallDuringSetup: ptr.Bool(false)},
|
||||
}, appSet)
|
||||
|
||||
appSet, err = ds.GetAssignedVPPApps(ctx, nil)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, map[fleet.VPPAppID]fleet.VPPAppTeam{appNoTeam1.VPPAppID: {VPPAppID: appNoTeam1.VPPAppID}, appNoTeam2.VPPAppID: {VPPAppID: appNoTeam2.VPPAppID}}, appSet)
|
||||
assert.Equal(t, map[fleet.VPPAppID]fleet.VPPAppTeam{
|
||||
appNoTeam1.VPPAppID: {VPPAppID: appNoTeam1.VPPAppID, InstallDuringSetup: ptr.Bool(false)},
|
||||
appNoTeam2.VPPAppID: {VPPAppID: appNoTeam2.VPPAppID, InstallDuringSetup: ptr.Bool(false)},
|
||||
}, 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)
|
||||
@@ -560,29 +566,24 @@ func testSetTeamVPPApps(t *testing.T, ds *Datastore) {
|
||||
require.Len(t, assigned, 0)
|
||||
|
||||
// Assign 2 apps
|
||||
// make app1 install_during_setup for that team
|
||||
err = ds.SetTeamVPPApps(ctx, &team.ID, []fleet.VPPAppTeam{
|
||||
{VPPAppID: app1.VPPAppID},
|
||||
{VPPAppID: app1.VPPAppID, InstallDuringSetup: ptr.Bool(true)},
|
||||
{VPPAppID: app2.VPPAppID, SelfService: true},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// make app1 install_during_setup for that team
|
||||
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 = ?`, team.ID, app1.AdamID)
|
||||
return 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)
|
||||
assert.True(t, *assigned[app1.VPPAppID].InstallDuringSetup)
|
||||
|
||||
// Assign an additional app
|
||||
err = ds.SetTeamVPPApps(ctx, &team.ID, []fleet.VPPAppTeam{
|
||||
{VPPAppID: app1.VPPAppID},
|
||||
{VPPAppID: app1.VPPAppID, InstallDuringSetup: ptr.Bool(true)},
|
||||
{VPPAppID: app2.VPPAppID},
|
||||
{VPPAppID: app3.VPPAppID},
|
||||
})
|
||||
@@ -595,11 +596,11 @@ func testSetTeamVPPApps(t *testing.T, ds *Datastore) {
|
||||
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)
|
||||
assert.True(t, *assigned[app1.VPPAppID].InstallDuringSetup)
|
||||
|
||||
// Swap one app out for another
|
||||
err = ds.SetTeamVPPApps(ctx, &team.ID, []fleet.VPPAppTeam{
|
||||
{VPPAppID: app1.VPPAppID},
|
||||
{VPPAppID: app1.VPPAppID, InstallDuringSetup: ptr.Bool(true)},
|
||||
{VPPAppID: app2.VPPAppID, SelfService: true},
|
||||
{VPPAppID: app4.VPPAppID},
|
||||
})
|
||||
@@ -612,7 +613,7 @@ func testSetTeamVPPApps(t *testing.T, ds *Datastore) {
|
||||
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)
|
||||
assert.True(t, *assigned[app1.VPPAppID].InstallDuringSetup)
|
||||
|
||||
// Remove app1 fails because it is installed during setup
|
||||
err = ds.SetTeamVPPApps(ctx, &team.ID, []fleet.VPPAppTeam{
|
||||
@@ -622,18 +623,22 @@ func testSetTeamVPPApps(t *testing.T, ds *Datastore) {
|
||||
require.Error(t, err)
|
||||
require.ErrorIs(t, err, errDeleteInstallerInstalledDuringSetup)
|
||||
|
||||
// Remove all apps fails because app1 is installed during setup
|
||||
err = ds.SetTeamVPPApps(ctx, &team.ID, []fleet.VPPAppTeam{})
|
||||
require.Error(t, err)
|
||||
require.ErrorIs(t, err, errDeleteInstallerInstalledDuringSetup)
|
||||
|
||||
// make app1 NOT install_during_setup for that team
|
||||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||||
_, err := q.ExecContext(ctx, `UPDATE vpp_apps_teams SET install_during_setup = 0 WHERE global_or_team_id = ? AND adam_id = ?`, team.ID, app1.AdamID)
|
||||
return err
|
||||
err = ds.SetTeamVPPApps(ctx, &team.ID, []fleet.VPPAppTeam{
|
||||
{VPPAppID: app1.VPPAppID, InstallDuringSetup: ptr.Bool(false)},
|
||||
{VPPAppID: app2.VPPAppID, SelfService: true},
|
||||
{VPPAppID: app4.VPPAppID},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Remove all apps now works
|
||||
// Remove app1 now works
|
||||
err = ds.SetTeamVPPApps(ctx, &team.ID, []fleet.VPPAppTeam{
|
||||
{VPPAppID: app2.VPPAppID, SelfService: true},
|
||||
{VPPAppID: app4.VPPAppID},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Remove all apps
|
||||
err = ds.SetTeamVPPApps(ctx, &team.ID, []fleet.VPPAppTeam{})
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
+22
-4
@@ -420,10 +420,19 @@ func (s *MacOSSettings) FromMap(m map[string]interface{}) (map[string]bool, erro
|
||||
|
||||
// MacOSSetup contains settings related to the setup of DEP enrolled devices.
|
||||
type MacOSSetup struct {
|
||||
BootstrapPackage optjson.String `json:"bootstrap_package"`
|
||||
EnableEndUserAuthentication bool `json:"enable_end_user_authentication"`
|
||||
MacOSSetupAssistant optjson.String `json:"macos_setup_assistant"`
|
||||
EnableReleaseDeviceManually optjson.Bool `json:"enable_release_device_manually"`
|
||||
BootstrapPackage optjson.String `json:"bootstrap_package"`
|
||||
EnableEndUserAuthentication bool `json:"enable_end_user_authentication"`
|
||||
MacOSSetupAssistant optjson.String `json:"macos_setup_assistant"`
|
||||
EnableReleaseDeviceManually optjson.Bool `json:"enable_release_device_manually"`
|
||||
Script optjson.String `json:"script"`
|
||||
Software optjson.Slice[*MacOSSetupSoftware] `json:"software"`
|
||||
}
|
||||
|
||||
// MacOSSetupSoftware represents a VPP app or a software package to install
|
||||
// during the setup experience of a macOS device.
|
||||
type MacOSSetupSoftware struct {
|
||||
AppStoreID string `json:"app_store_id"`
|
||||
PackagePath string `json:"package_path"`
|
||||
}
|
||||
|
||||
// MacOSMigration contains settings related to the MDM migration work flow.
|
||||
@@ -670,6 +679,15 @@ func (c *AppConfig) Copy() *AppConfig {
|
||||
clone.MDM.VolumePurchasingProgram = optjson.SetSlice(vpp)
|
||||
}
|
||||
|
||||
if c.MDM.MacOSSetup.Software.Set {
|
||||
sw := make([]*MacOSSetupSoftware, len(c.MDM.MacOSSetup.Software.Value))
|
||||
for i, s := range c.MDM.MacOSSetup.Software.Value {
|
||||
s := *s
|
||||
sw[i] = &s
|
||||
}
|
||||
clone.MDM.MacOSSetup.Software = optjson.SetSlice(sw)
|
||||
}
|
||||
|
||||
return &clone
|
||||
}
|
||||
|
||||
|
||||
@@ -373,14 +373,15 @@ type ScriptPayload struct {
|
||||
}
|
||||
|
||||
type SoftwareInstallerPayload struct {
|
||||
URL string `json:"url"`
|
||||
PreInstallQuery string `json:"pre_install_query"`
|
||||
InstallScript string `json:"install_script"`
|
||||
UninstallScript string `json:"uninstall_script"`
|
||||
PostInstallScript string `json:"post_install_script"`
|
||||
SelfService bool `json:"self_service"`
|
||||
FleetMaintained bool `json:"-"`
|
||||
Filename string `json:"-"`
|
||||
URL string `json:"url"`
|
||||
PreInstallQuery string `json:"pre_install_query"`
|
||||
InstallScript string `json:"install_script"`
|
||||
UninstallScript string `json:"uninstall_script"`
|
||||
PostInstallScript string `json:"post_install_script"`
|
||||
SelfService bool `json:"self_service"`
|
||||
FleetMaintained bool `json:"-"`
|
||||
Filename string `json:"-"`
|
||||
InstallDuringSetup *bool `json:"install_during_setup"` // if nil, do not change saved value, otherwise set it
|
||||
}
|
||||
|
||||
type HostLockWipeStatus struct {
|
||||
|
||||
@@ -427,12 +427,14 @@ func SoftwareFromOsqueryRow(name, version, source, vendor, installedPath, releas
|
||||
}
|
||||
|
||||
type VPPBatchPayload struct {
|
||||
AppStoreID string `json:"app_store_id"`
|
||||
SelfService bool `json:"self_service"`
|
||||
AppStoreID string `json:"app_store_id"`
|
||||
SelfService bool `json:"self_service"`
|
||||
InstallDuringSetup *bool `json:"install_during_setup"` // keep saved value if nil, otherwise set as indicated
|
||||
}
|
||||
|
||||
type VPPBatchPayloadWithPlatform struct {
|
||||
AppStoreID string `json:"app_store_id"`
|
||||
SelfService bool `json:"self_service"`
|
||||
Platform AppleDevicePlatform `json:"platform"`
|
||||
AppStoreID string `json:"app_store_id"`
|
||||
SelfService bool `json:"self_service"`
|
||||
Platform AppleDevicePlatform `json:"platform"`
|
||||
InstallDuringSetup *bool `json:"install_during_setup"` // keep saved value if nil, otherwise set as indicated
|
||||
}
|
||||
|
||||
@@ -308,25 +308,26 @@ func (s *HostSoftwareInstallerResultAuthz) AuthzType() string {
|
||||
}
|
||||
|
||||
type UploadSoftwareInstallerPayload struct {
|
||||
TeamID *uint
|
||||
InstallScript string
|
||||
PreInstallQuery string
|
||||
PostInstallScript string
|
||||
InstallerFile io.ReadSeeker // TODO: maybe pull this out of the payload and only pass it to methods that need it (e.g., won't be needed when storing metadata in the database)
|
||||
StorageID string
|
||||
Filename string
|
||||
Title string
|
||||
Version string
|
||||
Source string
|
||||
Platform string
|
||||
BundleIdentifier string
|
||||
SelfService bool
|
||||
UserID uint
|
||||
URL string
|
||||
FleetLibraryAppID *uint
|
||||
PackageIDs []string
|
||||
UninstallScript string
|
||||
Extension string
|
||||
TeamID *uint
|
||||
InstallScript string
|
||||
PreInstallQuery string
|
||||
PostInstallScript string
|
||||
InstallerFile io.ReadSeeker // TODO: maybe pull this out of the payload and only pass it to methods that need it (e.g., won't be needed when storing metadata in the database)
|
||||
StorageID string
|
||||
Filename string
|
||||
Title string
|
||||
Version string
|
||||
Source string
|
||||
Platform string
|
||||
BundleIdentifier string
|
||||
SelfService bool
|
||||
UserID uint
|
||||
URL string
|
||||
FleetLibraryAppID *uint
|
||||
PackageIDs []string
|
||||
UninstallScript string
|
||||
Extension string
|
||||
InstallDuringSetup *bool // keep saved value if nil, otherwise set as indicated
|
||||
}
|
||||
|
||||
type UpdateSoftwareInstallerPayload struct {
|
||||
@@ -438,6 +439,16 @@ type SoftwarePackageSpec struct {
|
||||
InstallScript TeamSpecSoftwareAsset `json:"install_script"`
|
||||
PostInstallScript TeamSpecSoftwareAsset `json:"post_install_script"`
|
||||
UninstallScript TeamSpecSoftwareAsset `json:"uninstall_script"`
|
||||
|
||||
// ReferencedYamlPath is the resolved path of the file used to fill the
|
||||
// software package. Only present after parsing a GitOps file on the fleetctl
|
||||
// side of processing. This is required to match a macos_setup.software to
|
||||
// its corresponding software package, as we do this matching by yaml path.
|
||||
//
|
||||
// It must be JSON-marshaled because it gets set during gitops file processing,
|
||||
// which is then re-marshaled to JSON from this struct and later re-unmarshaled
|
||||
// during ApplyGroup...
|
||||
ReferencedYamlPath string `json:"referenced_yaml_path"`
|
||||
}
|
||||
|
||||
type SoftwareSpec struct {
|
||||
|
||||
@@ -239,6 +239,14 @@ func (t *TeamMDM) Copy() *TeamMDM {
|
||||
}
|
||||
clone.WindowsSettings.CustomSettings = optjson.SetSlice(windowsSettings)
|
||||
}
|
||||
if t.MacOSSetup.Software.Set {
|
||||
sw := make([]*MacOSSetupSoftware, len(t.MacOSSetup.Software.Value))
|
||||
for i, s := range t.MacOSSetup.Software.Value {
|
||||
s := *s
|
||||
sw[i] = &s
|
||||
}
|
||||
clone.MacOSSetup.Software = optjson.SetSlice(sw)
|
||||
}
|
||||
return &clone
|
||||
}
|
||||
|
||||
@@ -518,5 +526,7 @@ func TeamSpecFromTeam(t *Team) (*TeamSpec, error) {
|
||||
HostExpirySettings: &t.Config.HostExpirySettings,
|
||||
WebhookSettings: webhookSettings,
|
||||
Integrations: integrations,
|
||||
Scripts: t.Config.Scripts,
|
||||
Software: t.Config.Software,
|
||||
}, nil
|
||||
}
|
||||
|
||||
+7
-2
@@ -16,8 +16,13 @@ type VPPAppID struct {
|
||||
type VPPAppTeam struct {
|
||||
VPPAppID
|
||||
|
||||
SelfService bool `db:"self_service" json:"self_service"`
|
||||
InstallDuringSetup bool `db:"install_during_setup" json:"-"`
|
||||
SelfService bool `db:"self_service" json:"self_service"`
|
||||
|
||||
// InstallDuringSetup is either the stored value of that flag for the VPP app
|
||||
// or the value to set to that VPP app when batch-setting it. When used to
|
||||
// set the value, if nil it will keep the currently saved value (or default
|
||||
// to false), while if not nil, it will update the flag's value in the DB.
|
||||
InstallDuringSetup *bool `db:"install_during_setup" json:"-"`
|
||||
}
|
||||
|
||||
// VPPApp represents a VPP (Volume Purchase Program) application,
|
||||
|
||||
@@ -908,8 +908,14 @@ func TestMDMAppleConfig(t *testing.T) {
|
||||
name: "nochange",
|
||||
licenseTier: "free",
|
||||
expectedMDM: fleet.MDM{
|
||||
AppleBusinessManager: optjson.Slice[fleet.MDMAppleABMAssignmentInfo]{Set: true, Value: []fleet.MDMAppleABMAssignmentInfo{}},
|
||||
MacOSSetup: fleet.MacOSSetup{BootstrapPackage: optjson.String{Set: true}, MacOSSetupAssistant: optjson.String{Set: true}, EnableReleaseDeviceManually: optjson.SetBool(false)},
|
||||
AppleBusinessManager: optjson.Slice[fleet.MDMAppleABMAssignmentInfo]{Set: true, Value: []fleet.MDMAppleABMAssignmentInfo{}},
|
||||
MacOSSetup: fleet.MacOSSetup{
|
||||
BootstrapPackage: optjson.String{Set: true},
|
||||
MacOSSetupAssistant: optjson.String{Set: true},
|
||||
EnableReleaseDeviceManually: optjson.SetBool(false),
|
||||
Software: optjson.Slice[*fleet.MacOSSetupSoftware]{Set: true, Value: []*fleet.MacOSSetupSoftware{}},
|
||||
Script: optjson.String{Set: true},
|
||||
},
|
||||
MacOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
IOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
IPadOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
@@ -943,12 +949,18 @@ func TestMDMAppleConfig(t *testing.T) {
|
||||
expectedMDM: fleet.MDM{
|
||||
AppleBusinessManager: optjson.Slice[fleet.MDMAppleABMAssignmentInfo]{Set: true, Value: []fleet.MDMAppleABMAssignmentInfo{}},
|
||||
DeprecatedAppleBMDefaultTeam: "foobar",
|
||||
MacOSSetup: fleet.MacOSSetup{BootstrapPackage: optjson.String{Set: true}, MacOSSetupAssistant: optjson.String{Set: true}, EnableReleaseDeviceManually: optjson.SetBool(false)},
|
||||
MacOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
IOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
IPadOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
VolumePurchasingProgram: optjson.Slice[fleet.MDMAppleVolumePurchasingProgramInfo]{Set: true, Value: []fleet.MDMAppleVolumePurchasingProgramInfo{}},
|
||||
WindowsUpdates: fleet.WindowsUpdates{DeadlineDays: optjson.Int{Set: true}, GracePeriodDays: optjson.Int{Set: true}},
|
||||
MacOSSetup: fleet.MacOSSetup{
|
||||
BootstrapPackage: optjson.String{Set: true},
|
||||
MacOSSetupAssistant: optjson.String{Set: true},
|
||||
EnableReleaseDeviceManually: optjson.SetBool(false),
|
||||
Software: optjson.Slice[*fleet.MacOSSetupSoftware]{Set: true, Value: []*fleet.MacOSSetupSoftware{}},
|
||||
Script: optjson.String{Set: true},
|
||||
},
|
||||
MacOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
IOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
IPadOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
VolumePurchasingProgram: optjson.Slice[fleet.MDMAppleVolumePurchasingProgramInfo]{Set: true, Value: []fleet.MDMAppleVolumePurchasingProgramInfo{}},
|
||||
WindowsUpdates: fleet.WindowsUpdates{DeadlineDays: optjson.Int{Set: true}, GracePeriodDays: optjson.Int{Set: true}},
|
||||
WindowsSettings: fleet.WindowsSettings{
|
||||
CustomSettings: optjson.Slice[fleet.MDMProfileSpec]{Set: true, Value: []fleet.MDMProfileSpec{}},
|
||||
},
|
||||
@@ -962,12 +974,18 @@ func TestMDMAppleConfig(t *testing.T) {
|
||||
expectedMDM: fleet.MDM{
|
||||
AppleBusinessManager: optjson.Slice[fleet.MDMAppleABMAssignmentInfo]{Set: true, Value: []fleet.MDMAppleABMAssignmentInfo{}},
|
||||
DeprecatedAppleBMDefaultTeam: "foobar",
|
||||
MacOSSetup: fleet.MacOSSetup{BootstrapPackage: optjson.String{Set: true}, MacOSSetupAssistant: optjson.String{Set: true}, EnableReleaseDeviceManually: optjson.SetBool(false)},
|
||||
MacOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
IOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
IPadOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
VolumePurchasingProgram: optjson.Slice[fleet.MDMAppleVolumePurchasingProgramInfo]{Set: true, Value: []fleet.MDMAppleVolumePurchasingProgramInfo{}},
|
||||
WindowsUpdates: fleet.WindowsUpdates{DeadlineDays: optjson.Int{Set: true}, GracePeriodDays: optjson.Int{Set: true}},
|
||||
MacOSSetup: fleet.MacOSSetup{
|
||||
BootstrapPackage: optjson.String{Set: true},
|
||||
MacOSSetupAssistant: optjson.String{Set: true},
|
||||
EnableReleaseDeviceManually: optjson.SetBool(false),
|
||||
Software: optjson.Slice[*fleet.MacOSSetupSoftware]{Set: true, Value: []*fleet.MacOSSetupSoftware{}},
|
||||
Script: optjson.String{Set: true},
|
||||
},
|
||||
MacOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
IOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
IPadOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
VolumePurchasingProgram: optjson.Slice[fleet.MDMAppleVolumePurchasingProgramInfo]{Set: true, Value: []fleet.MDMAppleVolumePurchasingProgramInfo{}},
|
||||
WindowsUpdates: fleet.WindowsUpdates{DeadlineDays: optjson.Int{Set: true}, GracePeriodDays: optjson.Int{Set: true}},
|
||||
WindowsSettings: fleet.WindowsSettings{
|
||||
CustomSettings: optjson.Slice[fleet.MDMProfileSpec]{Set: true, Value: []fleet.MDMProfileSpec{}},
|
||||
},
|
||||
@@ -985,9 +1003,15 @@ func TestMDMAppleConfig(t *testing.T) {
|
||||
newMDM: fleet.MDM{EndUserAuthentication: fleet.MDMEndUserAuthentication{SSOProviderSettings: fleet.SSOProviderSettings{EntityID: "foo"}}},
|
||||
oldMDM: fleet.MDM{EndUserAuthentication: fleet.MDMEndUserAuthentication{SSOProviderSettings: fleet.SSOProviderSettings{EntityID: "foo"}}},
|
||||
expectedMDM: fleet.MDM{
|
||||
AppleBusinessManager: optjson.Slice[fleet.MDMAppleABMAssignmentInfo]{Set: true, Value: []fleet.MDMAppleABMAssignmentInfo{}},
|
||||
EndUserAuthentication: fleet.MDMEndUserAuthentication{SSOProviderSettings: fleet.SSOProviderSettings{EntityID: "foo"}},
|
||||
MacOSSetup: fleet.MacOSSetup{BootstrapPackage: optjson.String{Set: true}, MacOSSetupAssistant: optjson.String{Set: true}, EnableReleaseDeviceManually: optjson.SetBool(false)},
|
||||
AppleBusinessManager: optjson.Slice[fleet.MDMAppleABMAssignmentInfo]{Set: true, Value: []fleet.MDMAppleABMAssignmentInfo{}},
|
||||
EndUserAuthentication: fleet.MDMEndUserAuthentication{SSOProviderSettings: fleet.SSOProviderSettings{EntityID: "foo"}},
|
||||
MacOSSetup: fleet.MacOSSetup{
|
||||
BootstrapPackage: optjson.String{Set: true},
|
||||
MacOSSetupAssistant: optjson.String{Set: true},
|
||||
EnableReleaseDeviceManually: optjson.SetBool(false),
|
||||
Software: optjson.Slice[*fleet.MacOSSetupSoftware]{Set: true, Value: []*fleet.MacOSSetupSoftware{}},
|
||||
Script: optjson.String{Set: true},
|
||||
},
|
||||
MacOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
IOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
IPadOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
@@ -1015,7 +1039,13 @@ func TestMDMAppleConfig(t *testing.T) {
|
||||
MetadataURL: "http://isser.metadata.com",
|
||||
IDPName: "onelogin",
|
||||
}},
|
||||
MacOSSetup: fleet.MacOSSetup{BootstrapPackage: optjson.String{Set: true}, MacOSSetupAssistant: optjson.String{Set: true}, EnableReleaseDeviceManually: optjson.SetBool(false)},
|
||||
MacOSSetup: fleet.MacOSSetup{
|
||||
BootstrapPackage: optjson.String{Set: true},
|
||||
MacOSSetupAssistant: optjson.String{Set: true},
|
||||
EnableReleaseDeviceManually: optjson.SetBool(false),
|
||||
Software: optjson.Slice[*fleet.MacOSSetupSoftware]{Set: true, Value: []*fleet.MacOSSetupSoftware{}},
|
||||
Script: optjson.String{Set: true},
|
||||
},
|
||||
MacOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
IOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
IPadOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
@@ -1075,9 +1105,15 @@ func TestMDMAppleConfig(t *testing.T) {
|
||||
EnableDiskEncryption: optjson.SetBool(false),
|
||||
},
|
||||
expectedMDM: fleet.MDM{
|
||||
AppleBusinessManager: optjson.Slice[fleet.MDMAppleABMAssignmentInfo]{Set: true, Value: []fleet.MDMAppleABMAssignmentInfo{}},
|
||||
EnableDiskEncryption: optjson.Bool{Set: true, Valid: true, Value: false},
|
||||
MacOSSetup: fleet.MacOSSetup{BootstrapPackage: optjson.String{Set: true}, MacOSSetupAssistant: optjson.String{Set: true}, EnableReleaseDeviceManually: optjson.SetBool(false)},
|
||||
AppleBusinessManager: optjson.Slice[fleet.MDMAppleABMAssignmentInfo]{Set: true, Value: []fleet.MDMAppleABMAssignmentInfo{}},
|
||||
EnableDiskEncryption: optjson.Bool{Set: true, Valid: true, Value: false},
|
||||
MacOSSetup: fleet.MacOSSetup{
|
||||
BootstrapPackage: optjson.String{Set: true},
|
||||
MacOSSetupAssistant: optjson.String{Set: true},
|
||||
EnableReleaseDeviceManually: optjson.SetBool(false),
|
||||
Software: optjson.Slice[*fleet.MacOSSetupSoftware]{Set: true, Value: []*fleet.MacOSSetupSoftware{}},
|
||||
Script: optjson.String{Set: true},
|
||||
},
|
||||
MacOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
IOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
IPadOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
|
||||
+228
-30
@@ -389,9 +389,32 @@ func getProfilesContents(baseDir string, macProfiles []fleet.MDMProfileSpec, win
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// fileContent is used to store the name of a file and its content.
|
||||
type fileContent struct {
|
||||
Filename string
|
||||
Content []byte
|
||||
}
|
||||
|
||||
// TODO: as confirmed by Noah and Marko on Slack:
|
||||
//
|
||||
// > from Noah: "We want to support existing features w/ fleetctl apply for
|
||||
// > backwards compatibility GitOps but we don’t need to add new features."
|
||||
//
|
||||
// We should deprecate ApplyGroup and use it only for `fleetctl apply` (and
|
||||
// its current minimal use in `preview`), and have a distinct implementation
|
||||
// that is `gitops`-only, because both uses have subtle differences in
|
||||
// behaviour that make it hard to reuse a single implementation (e.g. a missing
|
||||
// key in gitops means "remove what is absent" while in apply it means "leave
|
||||
// as-is").
|
||||
//
|
||||
// For now I'm just passing a "gitops" bool for a quick fix, but we should
|
||||
// properly plan that separation and refactor so that gitops can be
|
||||
// significantly cleaned up and simplified going forward.
|
||||
|
||||
// ApplyGroup applies the given spec group to Fleet.
|
||||
func (c *Client) ApplyGroup(
|
||||
ctx context.Context,
|
||||
viaGitOps bool,
|
||||
specs *spec.Group,
|
||||
baseDir string,
|
||||
logf func(format string, args ...interface{}),
|
||||
@@ -554,6 +577,15 @@ func (c *Client) ApplyGroup(
|
||||
tmMacSetup := extractTmSpecsMacOSSetup(specs.Teams)
|
||||
tmBootstrapPackages := make(map[string]*fleet.MDMAppleBootstrapPackage, len(tmMacSetup))
|
||||
tmMacSetupAssistants := make(map[string][]byte, len(tmMacSetup))
|
||||
|
||||
// those are gitops-only features
|
||||
tmMacSetupScript := make(map[string]fileContent, len(tmMacSetup))
|
||||
tmMacSetupSoftware := make(map[string][]*fleet.MacOSSetupSoftware, len(tmMacSetup))
|
||||
// this is a set of software packages or VPP apps that are configured as
|
||||
// install_during_setup, by team. This is a gitops-only setting, so it will
|
||||
// only be filled when called via this command.
|
||||
tmSoftwareMacOSSetup := make(map[string]map[fleet.MacOSSetupSoftware]struct{}, len(tmMacSetup))
|
||||
|
||||
for k, setup := range tmMacSetup {
|
||||
if setup.BootstrapPackage.Value != "" {
|
||||
bp, err := c.ValidateBootstrapPackageFromURL(setup.BootstrapPackage.Value)
|
||||
@@ -569,6 +601,21 @@ func (c *Client) ApplyGroup(
|
||||
}
|
||||
tmMacSetupAssistants[k] = b
|
||||
}
|
||||
if setup.Script.Value != "" {
|
||||
b, err := c.validateMacOSSetupScript(resolveApplyRelativePath(baseDir, setup.Script.Value))
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("applying teams: %w", err)
|
||||
}
|
||||
tmMacSetupScript[k] = fileContent{Filename: filepath.Base(setup.Script.Value), Content: b}
|
||||
}
|
||||
if viaGitOps {
|
||||
m, err := extractTeamOrNoTeamMacOSSetupSoftware(baseDir, setup.Software.Value)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
tmSoftwareMacOSSetup[k] = m
|
||||
tmMacSetupSoftware[k] = setup.Software.Value
|
||||
}
|
||||
}
|
||||
|
||||
tmScripts := extractTmSpecsScripts(specs.Teams)
|
||||
@@ -590,24 +637,59 @@ func (c *Client) ApplyGroup(
|
||||
|
||||
tmSoftwarePackages := extractTmSpecsSoftwarePackages(specs.Teams)
|
||||
tmSoftwarePackagesPayloads := make(map[string][]fleet.SoftwareInstallerPayload, len(tmSoftwarePackages))
|
||||
tmSoftwarePackageByPath := make(map[string]map[string]fleet.SoftwarePackageSpec, len(tmSoftwarePackages))
|
||||
for tmName, software := range tmSoftwarePackages {
|
||||
softwarePayloads, err := buildSoftwarePackagesPayload(baseDir, software)
|
||||
installDuringSetupKeys := tmSoftwareMacOSSetup[tmName]
|
||||
softwarePayloads, err := buildSoftwarePackagesPayload(baseDir, software, installDuringSetupKeys)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("applying software installers for team %q: %w", tmName, err)
|
||||
}
|
||||
tmSoftwarePackagesPayloads[tmName] = softwarePayloads
|
||||
for _, swSpec := range software {
|
||||
if swSpec.ReferencedYamlPath != "" {
|
||||
// can be referenced by macos_setup.software.package_path
|
||||
if tmSoftwarePackageByPath[tmName] == nil {
|
||||
tmSoftwarePackageByPath[tmName] = make(map[string]fleet.SoftwarePackageSpec, len(software))
|
||||
}
|
||||
tmSoftwarePackageByPath[tmName][swSpec.ReferencedYamlPath] = swSpec
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tmSoftwareApps := extractTmSpecsSoftwareApps(specs.Teams)
|
||||
tmSoftwareAppsPayloads := make(map[string][]fleet.VPPBatchPayload)
|
||||
tmSoftwareAppsByAppID := make(map[string]map[string]fleet.TeamSpecAppStoreApp, len(tmSoftwareApps))
|
||||
for tmName, apps := range tmSoftwareApps {
|
||||
installDuringSetupKeys := tmSoftwareMacOSSetup[tmName]
|
||||
appPayloads := make([]fleet.VPPBatchPayload, 0, len(apps))
|
||||
for _, app := range apps {
|
||||
appPayloads = append(appPayloads, fleet.VPPBatchPayload{AppStoreID: app.AppStoreID, SelfService: app.SelfService})
|
||||
var installDuringSetup *bool
|
||||
if installDuringSetupKeys != nil {
|
||||
_, ok := installDuringSetupKeys[fleet.MacOSSetupSoftware{AppStoreID: app.AppStoreID}]
|
||||
installDuringSetup = &ok
|
||||
}
|
||||
appPayloads = append(appPayloads, fleet.VPPBatchPayload{
|
||||
AppStoreID: app.AppStoreID,
|
||||
SelfService: app.SelfService,
|
||||
InstallDuringSetup: installDuringSetup,
|
||||
})
|
||||
// can be referenced by macos_setup.software.app_store_id
|
||||
if tmSoftwareAppsByAppID[tmName] == nil {
|
||||
tmSoftwareAppsByAppID[tmName] = make(map[string]fleet.TeamSpecAppStoreApp, len(apps))
|
||||
}
|
||||
tmSoftwareAppsByAppID[tmName][app.AppStoreID] = app
|
||||
}
|
||||
tmSoftwareAppsPayloads[tmName] = appPayloads
|
||||
}
|
||||
|
||||
// if macos_setup.software has some values, they must exist in the software
|
||||
// packages or vpp apps.
|
||||
for tmName, setupSw := range tmMacSetupSoftware {
|
||||
if err := validateTeamOrNoTeamMacOSSetupSoftware(tmName, setupSw, tmSoftwarePackageByPath[tmName], tmSoftwareAppsByAppID[tmName]); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Next, apply the teams specs before saving the profiles, so that any
|
||||
// non-existing team gets created.
|
||||
var err error
|
||||
@@ -675,6 +757,19 @@ func (c *Client) ApplyGroup(
|
||||
}
|
||||
}
|
||||
}
|
||||
if viaGitOps && !opts.DryRun {
|
||||
for tmName, tmID := range teamIDsByName {
|
||||
if fc, ok := tmMacSetupScript[tmName]; ok {
|
||||
if err := c.uploadMacOSSetupScript(fc.Filename, fc.Content, &tmID); err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("uploading setup experience script for team %q: %w", tmName, err)
|
||||
}
|
||||
} else {
|
||||
if err := c.deleteMacOSSetupScript(&tmID); err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("deleting setup experience script for team %q: %w", tmName, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(tmScriptsPayloads) > 0 {
|
||||
for tmName, scripts := range tmScriptsPayloads {
|
||||
// For non-dry run, currentTeamName and tmName are the same
|
||||
@@ -702,6 +797,7 @@ func (c *Client) ApplyGroup(
|
||||
for tmName, apps := range tmSoftwareAppsPayloads {
|
||||
// For non-dry run, currentTeamName and tmName are the same
|
||||
currentTeamName := getTeamName(tmName)
|
||||
logfn("[+] applying %d app store apps for team %s\n", len(apps), tmName)
|
||||
if err := c.ApplyTeamAppStoreAppsAssociation(currentTeamName, apps, opts.ApplySpecOptions); err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("applying app store apps for team: %q: %w", tmName, err)
|
||||
}
|
||||
@@ -752,7 +848,45 @@ func (c *Client) ApplyGroup(
|
||||
return teamIDsByName, teamsSoftwareInstallers, teamsScripts, nil
|
||||
}
|
||||
|
||||
func buildSoftwarePackagesPayload(baseDir string, specs []fleet.SoftwarePackageSpec) ([]fleet.SoftwareInstallerPayload, error) {
|
||||
func extractTeamOrNoTeamMacOSSetupSoftware(baseDir string, software []*fleet.MacOSSetupSoftware) (map[fleet.MacOSSetupSoftware]struct{}, error) {
|
||||
m := make(map[fleet.MacOSSetupSoftware]struct{}, len(software))
|
||||
for _, sw := range software {
|
||||
if sw.AppStoreID != "" && sw.PackagePath != "" {
|
||||
return nil, errors.New("applying teams: only one of app_store_id or package_path can be set")
|
||||
}
|
||||
if sw.PackagePath != "" {
|
||||
sw.PackagePath = resolveApplyRelativePath(baseDir, sw.PackagePath)
|
||||
}
|
||||
m[*sw] = struct{}{}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func validateTeamOrNoTeamMacOSSetupSoftware(teamName string, macOSSetupSoftware []*fleet.MacOSSetupSoftware, packagesByPath map[string]fleet.SoftwarePackageSpec, vppAppsByAppID map[string]fleet.TeamSpecAppStoreApp) error {
|
||||
// if macos_setup.software has some values, they must exist in the software
|
||||
// packages or vpp apps.
|
||||
for _, ssw := range macOSSetupSoftware {
|
||||
var valid bool
|
||||
if ssw.AppStoreID != "" {
|
||||
// check that it exists in the team's Apps
|
||||
_, valid = vppAppsByAppID[ssw.AppStoreID]
|
||||
} else if ssw.PackagePath != "" {
|
||||
// check that it exists in the team's Software installers (PackagePath is
|
||||
// already resolved to abs dir)
|
||||
_, valid = packagesByPath[ssw.PackagePath]
|
||||
}
|
||||
if !valid {
|
||||
label := ssw.AppStoreID
|
||||
if label == "" {
|
||||
label = ssw.PackagePath
|
||||
}
|
||||
return fmt.Errorf("applying macOS setup experience software for team %q: software %q does not exist for that team", teamName, label)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildSoftwarePackagesPayload(baseDir string, specs []fleet.SoftwarePackageSpec, installDuringSetupKeys map[fleet.MacOSSetupSoftware]struct{}) ([]fleet.SoftwareInstallerPayload, error) {
|
||||
softwarePayloads := make([]fleet.SoftwareInstallerPayload, len(specs))
|
||||
for i, si := range specs {
|
||||
var qc string
|
||||
@@ -838,15 +972,20 @@ func buildSoftwarePackagesPayload(baseDir string, specs []fleet.SoftwarePackageS
|
||||
}
|
||||
}
|
||||
|
||||
var installDuringSetup *bool
|
||||
if installDuringSetupKeys != nil {
|
||||
_, ok := installDuringSetupKeys[fleet.MacOSSetupSoftware{PackagePath: si.ReferencedYamlPath}]
|
||||
installDuringSetup = &ok
|
||||
}
|
||||
softwarePayloads[i] = fleet.SoftwareInstallerPayload{
|
||||
URL: si.URL,
|
||||
SelfService: si.SelfService,
|
||||
PreInstallQuery: qc,
|
||||
InstallScript: string(ic),
|
||||
PostInstallScript: string(pc),
|
||||
UninstallScript: string(us),
|
||||
URL: si.URL,
|
||||
SelfService: si.SelfService,
|
||||
PreInstallQuery: qc,
|
||||
InstallScript: string(ic),
|
||||
PostInstallScript: string(pc),
|
||||
UninstallScript: string(us),
|
||||
InstallDuringSetup: installDuringSetup,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return softwarePayloads, nil
|
||||
@@ -1478,7 +1617,7 @@ func (c *Client) DoGitOps(
|
||||
}
|
||||
|
||||
// Apply org settings, scripts, enroll secrets, team entities (software, scripts, etc.), and controls.
|
||||
teamIDsByName, teamsSoftwareInstallers, teamsScripts, err := c.ApplyGroup(ctx, &group, baseDir, logf, appConfig, fleet.ApplyClientSpecOptions{
|
||||
teamIDsByName, teamsSoftwareInstallers, teamsScripts, err := c.ApplyGroup(ctx, true, &group, baseDir, logf, appConfig, fleet.ApplyClientSpecOptions{
|
||||
ApplySpecOptions: fleet.ApplySpecOptions{
|
||||
DryRun: dryRun,
|
||||
},
|
||||
@@ -1535,30 +1674,89 @@ func (c *Client) DoGitOps(
|
||||
return teamAssumptions, nil
|
||||
}
|
||||
|
||||
func (c *Client) doGitOpsNoTeamSoftware(config *spec.GitOps, baseDir string, appconfig *fleet.EnrichedAppConfig, logFn func(format string, args ...interface{}), dryRun bool) ([]fleet.SoftwarePackageResponse, error) {
|
||||
func (c *Client) doGitOpsNoTeamSoftware(
|
||||
config *spec.GitOps,
|
||||
baseDir string,
|
||||
appconfig *fleet.EnrichedAppConfig,
|
||||
logFn func(format string, args ...interface{}),
|
||||
dryRun bool,
|
||||
) ([]fleet.SoftwarePackageResponse, error) {
|
||||
if !config.IsNoTeam() || appconfig == nil || !appconfig.License.IsPremium() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var softwareInstallers []fleet.SoftwarePackageResponse
|
||||
if config.IsNoTeam() && appconfig != nil && appconfig.License.IsPremium() {
|
||||
packages := make([]fleet.SoftwarePackageSpec, 0, len(config.Software.Packages))
|
||||
for _, software := range config.Software.Packages {
|
||||
if software != nil {
|
||||
packages = append(packages, *software)
|
||||
|
||||
packages := make([]fleet.SoftwarePackageSpec, 0, len(config.Software.Packages))
|
||||
packagesByPath := make(map[string]fleet.SoftwarePackageSpec, len(config.Software.Packages))
|
||||
for _, software := range config.Software.Packages {
|
||||
if software != nil {
|
||||
packages = append(packages, *software)
|
||||
if software.ReferencedYamlPath != "" {
|
||||
// can be referenced by macos_setup.software
|
||||
packagesByPath[software.ReferencedYamlPath] = *software
|
||||
}
|
||||
}
|
||||
payload, err := buildSoftwarePackagesPayload(baseDir, packages)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("applying software installers: %w", err)
|
||||
}
|
||||
logFn("[+] applying %d software packages for 'No team'\n", len(payload))
|
||||
softwareInstallers, err = c.ApplyNoTeamSoftwareInstallers(payload, fleet.ApplySpecOptions{DryRun: dryRun})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("applying software installers: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if dryRun {
|
||||
logFn("[+] would've applied 'No Team' software packages\n")
|
||||
} else {
|
||||
logFn("[+] applied 'No Team' software packages\n")
|
||||
// marshaling dance to get the macos_setup data - config.Controls.MacOSSetup
|
||||
// is of type any and contains a generic map[string]any. By
|
||||
// marshal-unmarshaling it into a properly typed struct, we avoid having to
|
||||
// do a bunch of error-prone and unmaintainable type-assertions to walk down
|
||||
// the untyped map.
|
||||
b, err := json.Marshal(config.Controls.MacOSSetup)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("applying software installers: json-encode controls.macos_setup: %w", err)
|
||||
}
|
||||
var macOSSetup fleet.MacOSSetup
|
||||
if err := json.Unmarshal(b, &macOSSetup); err != nil {
|
||||
return nil, fmt.Errorf("applying software installers: json-decode controls.macos_setup: %w", err)
|
||||
}
|
||||
|
||||
// load the no-team macos_setup.script if any
|
||||
var macosSetupScript *fileContent
|
||||
if macOSSetup.Script.Value != "" {
|
||||
b, err := c.validateMacOSSetupScript(resolveApplyRelativePath(baseDir, macOSSetup.Script.Value))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("applying no team macos_setup.script: %w", err)
|
||||
}
|
||||
macosSetupScript = &fileContent{Filename: filepath.Base(macOSSetup.Script.Value), Content: b}
|
||||
}
|
||||
|
||||
noTeamSoftwareMacOSSetup, err := extractTeamOrNoTeamMacOSSetupSoftware(baseDir, macOSSetup.Software.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: note that VPP apps are not validated nor taken into account at the moment,
|
||||
// tracked with issue https://github.com/fleetdm/fleet/issues/22970
|
||||
if err := validateTeamOrNoTeamMacOSSetupSoftware(*config.TeamName, macOSSetup.Software.Value, packagesByPath, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
payload, err := buildSoftwarePackagesPayload(baseDir, packages, noTeamSoftwareMacOSSetup)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("applying software installers: %w", err)
|
||||
}
|
||||
|
||||
if macosSetupScript != nil {
|
||||
logFn("[+] applying macos setup experience script for 'No team'\n")
|
||||
if err := c.uploadMacOSSetupScript(macosSetupScript.Filename, macosSetupScript.Content, nil); err != nil {
|
||||
return nil, fmt.Errorf("uploading setup experience script for No team: %w", err)
|
||||
}
|
||||
} else if err := c.deleteMacOSSetupScript(nil); err != nil {
|
||||
return nil, fmt.Errorf("deleting setup experience script for No team: %w", err)
|
||||
}
|
||||
|
||||
logFn("[+] applying %d software packages for 'No team'\n", len(payload))
|
||||
softwareInstallers, err = c.ApplyNoTeamSoftwareInstallers(payload, fleet.ApplySpecOptions{DryRun: dryRun})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("applying software installers: %w", err)
|
||||
}
|
||||
|
||||
if dryRun {
|
||||
logFn("[+] would've applied 'No Team' software packages\n")
|
||||
} else {
|
||||
logFn("[+] applied 'No Team' software packages\n")
|
||||
}
|
||||
return softwareInstallers, nil
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -139,3 +143,78 @@ func (c *Client) ApplyNoTeamScripts(scripts []fleet.ScriptPayload, opts fleet.Ap
|
||||
|
||||
return resp.Scripts, err
|
||||
}
|
||||
|
||||
func (c *Client) validateMacOSSetupScript(fileName string) ([]byte, error) {
|
||||
if err := c.CheckAppleMDMEnabled(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b, err := os.ReadFile(fileName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (c *Client) deleteMacOSSetupScript(teamID *uint) error {
|
||||
var query string
|
||||
if teamID != nil {
|
||||
query = fmt.Sprintf("team_id=%d", *teamID)
|
||||
}
|
||||
|
||||
verb, path := "DELETE", "/api/latest/fleet/setup_experience/script"
|
||||
var delResp deleteSetupExperienceScriptResponse
|
||||
return c.authenticatedRequestWithQuery(nil, verb, path, &delResp, query)
|
||||
}
|
||||
|
||||
func (c *Client) uploadMacOSSetupScript(filename string, data []byte, teamID *uint) error {
|
||||
// there is no "replace setup experience script" endpoint, and none was
|
||||
// planned, so to avoid delaying the feature I'm doing DELETE then SET, but
|
||||
// that's not ideal (will always re-create the script when apply/gitops is
|
||||
// run with the same yaml). Note though that we also redo software installers
|
||||
// downloads on each run, so the churn of this one is minor in comparison.
|
||||
if err := c.deleteMacOSSetupScript(teamID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
verb, path := "POST", "/api/latest/fleet/setup_experience/script"
|
||||
|
||||
var b bytes.Buffer
|
||||
w := multipart.NewWriter(&b)
|
||||
|
||||
fw, err := w.CreateFormFile("script", filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.Copy(fw, bytes.NewBuffer(data)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// add the team_id field
|
||||
if teamID != nil {
|
||||
if err := w.WriteField("team_id", fmt.Sprint(*teamID)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
w.Close()
|
||||
|
||||
response, err := c.doContextWithBodyAndHeaders(context.Background(), verb, path, "",
|
||||
b.Bytes(),
|
||||
map[string]string{
|
||||
"Content-Type": w.FormDataContentType(),
|
||||
"Accept": "application/json",
|
||||
"Authorization": fmt.Sprintf("Bearer %s", c.token),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("do multipart request: %w", err)
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
var resp setSetupExperienceScriptResponse
|
||||
if err := c.parseResponse(verb, path, response, &resp); err != nil {
|
||||
return fmt.Errorf("parse response: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -235,6 +235,8 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
|
||||
MacOSSetupAssistant: optjson.String{Set: true},
|
||||
BootstrapPackage: optjson.String{Set: true},
|
||||
EnableReleaseDeviceManually: optjson.SetBool(false),
|
||||
Script: optjson.String{Set: true},
|
||||
Software: optjson.Slice[*fleet.MacOSSetupSoftware]{Set: true, Value: []*fleet.MacOSSetupSoftware{}},
|
||||
},
|
||||
// because the WindowsSettings was marshalled to JSON to be saved in the DB,
|
||||
// it did get marshalled, and then when unmarshalled it was set (but
|
||||
@@ -337,6 +339,8 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
|
||||
MacOSSetupAssistant: optjson.String{Set: true},
|
||||
BootstrapPackage: optjson.String{Set: true},
|
||||
EnableReleaseDeviceManually: optjson.SetBool(false),
|
||||
Script: optjson.String{Set: true},
|
||||
Software: optjson.Slice[*fleet.MacOSSetupSoftware]{Set: true, Value: []*fleet.MacOSSetupSoftware{}},
|
||||
},
|
||||
WindowsSettings: fleet.WindowsSettings{
|
||||
CustomSettings: optjson.Slice[fleet.MDMProfileSpec]{Set: true, Value: []fleet.MDMProfileSpec{}},
|
||||
@@ -367,6 +371,8 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
|
||||
MacOSSetupAssistant: optjson.String{Set: true},
|
||||
BootstrapPackage: optjson.String{Set: true},
|
||||
EnableReleaseDeviceManually: optjson.SetBool(false),
|
||||
Script: optjson.String{Set: true},
|
||||
Software: optjson.Slice[*fleet.MacOSSetupSoftware]{Set: true, Value: []*fleet.MacOSSetupSoftware{}},
|
||||
},
|
||||
WindowsSettings: fleet.WindowsSettings{
|
||||
CustomSettings: optjson.Slice[fleet.MDMProfileSpec]{Set: true, Value: []fleet.MDMProfileSpec{}},
|
||||
@@ -399,6 +405,8 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
|
||||
MacOSSetupAssistant: optjson.String{Set: true},
|
||||
BootstrapPackage: optjson.String{Set: true},
|
||||
EnableReleaseDeviceManually: optjson.SetBool(false),
|
||||
Script: optjson.String{Set: true},
|
||||
Software: optjson.Slice[*fleet.MacOSSetupSoftware]{Set: true, Value: []*fleet.MacOSSetupSoftware{}},
|
||||
},
|
||||
WindowsSettings: fleet.WindowsSettings{
|
||||
CustomSettings: optjson.Slice[fleet.MDMProfileSpec]{Set: true, Value: []fleet.MDMProfileSpec{}},
|
||||
@@ -2365,6 +2373,8 @@ func (s *integrationEnterpriseTestSuite) TestWindowsUpdatesTeamConfig() {
|
||||
MacOSSetupAssistant: optjson.String{Set: true},
|
||||
BootstrapPackage: optjson.String{Set: true},
|
||||
EnableReleaseDeviceManually: optjson.SetBool(false),
|
||||
Script: optjson.String{Set: true},
|
||||
Software: optjson.Slice[*fleet.MacOSSetupSoftware]{Set: true, Value: []*fleet.MacOSSetupSoftware{}},
|
||||
},
|
||||
WindowsSettings: fleet.WindowsSettings{
|
||||
CustomSettings: optjson.Slice[fleet.MDMProfileSpec]{Set: true, Value: []fleet.MDMProfileSpec{}},
|
||||
|
||||
@@ -1423,18 +1423,18 @@ func (s *integrationMDMTestSuite) TestSetupExperienceFlowWithSoftwareAndScriptAu
|
||||
require.NoError(t, plist.Unmarshal(cmd.Raw, &fullCmd))
|
||||
|
||||
// Can be useful for debugging
|
||||
//switch cmd.Command.RequestType {
|
||||
//case "InstallProfile":
|
||||
// fmt.Println(">>>> device received command: ", cmd.CommandUUID, cmd.Command.RequestType, string(fullCmd.Command.InstallProfile.Payload))
|
||||
//case "InstallEnterpriseApplication":
|
||||
// if fullCmd.Command.InstallEnterpriseApplication.ManifestURL != nil {
|
||||
// fmt.Println(">>>> device received command: ", cmd.CommandUUID, cmd.Command.RequestType, *fullCmd.Command.InstallEnterpriseApplication.ManifestURL)
|
||||
// } else {
|
||||
// fmt.Println(">>>> device received command: ", cmd.CommandUUID, cmd.Command.RequestType)
|
||||
// }
|
||||
//default:
|
||||
// fmt.Println(">>>> device received command: ", cmd.Command.RequestType)
|
||||
//}
|
||||
// switch cmd.Command.RequestType {
|
||||
// case "InstallProfile":
|
||||
// fmt.Println(">>>> device received command: ", cmd.CommandUUID, cmd.Command.RequestType, string(fullCmd.Command.InstallProfile.Payload))
|
||||
// case "InstallEnterpriseApplication":
|
||||
// if fullCmd.Command.InstallEnterpriseApplication.ManifestURL != nil {
|
||||
// fmt.Println(">>>> device received command: ", cmd.CommandUUID, cmd.Command.RequestType, *fullCmd.Command.InstallEnterpriseApplication.ManifestURL)
|
||||
// } else {
|
||||
// fmt.Println(">>>> device received command: ", cmd.CommandUUID, cmd.Command.RequestType)
|
||||
// }
|
||||
// default:
|
||||
// fmt.Println(">>>> device received command: ", cmd.Command.RequestType)
|
||||
// }
|
||||
|
||||
cmds = append(cmds, &fullCmd)
|
||||
cmd, err = mdmDevice.Acknowledge(cmd.CommandUUID)
|
||||
@@ -1450,14 +1450,15 @@ func (s *integrationMDMTestSuite) TestSetupExperienceFlowWithSoftwareAndScriptAu
|
||||
switch cmd.Command.RequestType {
|
||||
case "InstallProfile":
|
||||
installProfileCount++
|
||||
if strings.Contains(string(cmd.Command.InstallProfile.Payload), "<string>I1</string>") {
|
||||
switch {
|
||||
case strings.Contains(string(cmd.Command.InstallProfile.Payload), "<string>I1</string>"):
|
||||
profileCustomSeen = true
|
||||
} else if strings.Contains(string(cmd.Command.InstallProfile.Payload), fmt.Sprintf("<string>%s</string>", mobileconfig.FleetdConfigPayloadIdentifier)) {
|
||||
case strings.Contains(string(cmd.Command.InstallProfile.Payload), fmt.Sprintf("<string>%s</string>", mobileconfig.FleetdConfigPayloadIdentifier)):
|
||||
profileFleetdSeen = true
|
||||
} else if strings.Contains(string(cmd.Command.InstallProfile.Payload), fmt.Sprintf("<string>%s</string>", mobileconfig.FleetCARootConfigPayloadIdentifier)) {
|
||||
case strings.Contains(string(cmd.Command.InstallProfile.Payload), fmt.Sprintf("<string>%s</string>", mobileconfig.FleetCARootConfigPayloadIdentifier)):
|
||||
profileFleetCASeen = true
|
||||
} else if strings.Contains(string(cmd.Command.InstallProfile.Payload), fmt.Sprintf("<string>%s</string", mobileconfig.FleetFileVaultPayloadIdentifier)) &&
|
||||
strings.Contains(string(cmd.Command.InstallProfile.Payload), "ForceEnableInSetupAssistant") {
|
||||
case strings.Contains(string(cmd.Command.InstallProfile.Payload), fmt.Sprintf("<string>%s</string", mobileconfig.FleetFileVaultPayloadIdentifier)) &&
|
||||
strings.Contains(string(cmd.Command.InstallProfile.Payload), "ForceEnableInSetupAssistant"):
|
||||
profileFileVaultSeen = true
|
||||
}
|
||||
|
||||
@@ -1612,18 +1613,18 @@ func (s *integrationMDMTestSuite) TestSetupExperienceFlowWithSoftwareAndScriptFo
|
||||
require.NoError(t, plist.Unmarshal(cmd.Raw, &fullCmd))
|
||||
|
||||
// Can be useful for debugging
|
||||
//switch cmd.Command.RequestType {
|
||||
//case "InstallProfile":
|
||||
// fmt.Println(">>>> device received command: ", cmd.CommandUUID, cmd.Command.RequestType, string(fullCmd.Command.InstallProfile.Payload))
|
||||
//case "InstallEnterpriseApplication":
|
||||
// if fullCmd.Command.InstallEnterpriseApplication.ManifestURL != nil {
|
||||
// fmt.Println(">>>> device received command: ", cmd.CommandUUID, cmd.Command.RequestType, *fullCmd.Command.InstallEnterpriseApplication.ManifestURL)
|
||||
// } else {
|
||||
// fmt.Println(">>>> device received command: ", cmd.CommandUUID, cmd.Command.RequestType)
|
||||
// }
|
||||
//default:
|
||||
// fmt.Println(">>>> device received command: ", cmd.Command.RequestType)
|
||||
//}
|
||||
// switch cmd.Command.RequestType {
|
||||
// case "InstallProfile":
|
||||
// fmt.Println(">>>> device received command: ", cmd.CommandUUID, cmd.Command.RequestType, string(fullCmd.Command.InstallProfile.Payload))
|
||||
// case "InstallEnterpriseApplication":
|
||||
// if fullCmd.Command.InstallEnterpriseApplication.ManifestURL != nil {
|
||||
// fmt.Println(">>>> device received command: ", cmd.CommandUUID, cmd.Command.RequestType, *fullCmd.Command.InstallEnterpriseApplication.ManifestURL)
|
||||
// } else {
|
||||
// fmt.Println(">>>> device received command: ", cmd.CommandUUID, cmd.Command.RequestType)
|
||||
// }
|
||||
// default:
|
||||
// fmt.Println(">>>> device received command: ", cmd.Command.RequestType)
|
||||
// }
|
||||
|
||||
cmds = append(cmds, &fullCmd)
|
||||
cmd, err = mdmDevice.Acknowledge(cmd.CommandUUID)
|
||||
@@ -1639,14 +1640,15 @@ func (s *integrationMDMTestSuite) TestSetupExperienceFlowWithSoftwareAndScriptFo
|
||||
switch cmd.Command.RequestType {
|
||||
case "InstallProfile":
|
||||
installProfileCount++
|
||||
if strings.Contains(string(cmd.Command.InstallProfile.Payload), "<string>I1</string>") {
|
||||
switch {
|
||||
case strings.Contains(string(cmd.Command.InstallProfile.Payload), "<string>I1</string>"):
|
||||
profileCustomSeen = true
|
||||
} else if strings.Contains(string(cmd.Command.InstallProfile.Payload), fmt.Sprintf("<string>%s</string>", mobileconfig.FleetdConfigPayloadIdentifier)) {
|
||||
case strings.Contains(string(cmd.Command.InstallProfile.Payload), fmt.Sprintf("<string>%s</string>", mobileconfig.FleetdConfigPayloadIdentifier)):
|
||||
profileFleetdSeen = true
|
||||
} else if strings.Contains(string(cmd.Command.InstallProfile.Payload), fmt.Sprintf("<string>%s</string>", mobileconfig.FleetCARootConfigPayloadIdentifier)) {
|
||||
case strings.Contains(string(cmd.Command.InstallProfile.Payload), fmt.Sprintf("<string>%s</string>", mobileconfig.FleetCARootConfigPayloadIdentifier)):
|
||||
profileFleetCASeen = true
|
||||
} else if strings.Contains(string(cmd.Command.InstallProfile.Payload), fmt.Sprintf("<string>%s</string", mobileconfig.FleetFileVaultPayloadIdentifier)) &&
|
||||
strings.Contains(string(cmd.Command.InstallProfile.Payload), "ForceEnableInSetupAssistant") {
|
||||
case strings.Contains(string(cmd.Command.InstallProfile.Payload), fmt.Sprintf("<string>%s</string", mobileconfig.FleetFileVaultPayloadIdentifier)) &&
|
||||
strings.Contains(string(cmd.Command.InstallProfile.Payload), "ForceEnableInSetupAssistant"):
|
||||
profileFileVaultSeen = true
|
||||
}
|
||||
|
||||
|
||||
@@ -10189,7 +10189,11 @@ func (s *integrationMDMTestSuite) TestBatchAssociateAppStoreApps() {
|
||||
assert.Contains(t, assoc, fleet.VPPAppID{AdamID: s.appleVPPConfigSrvConfig.Assets[1].AdamID, Platform: fleet.IOSPlatform})
|
||||
assert.Contains(t, assoc, fleet.VPPAppID{AdamID: s.appleVPPConfigSrvConfig.Assets[1].AdamID, Platform: fleet.IPadOSPlatform})
|
||||
// Only macOS version should be self-service
|
||||
assert.Equal(t, fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: s.appleVPPConfigSrvConfig.Assets[1].AdamID, Platform: fleet.MacOSPlatform}, SelfService: true},
|
||||
assert.Equal(t, fleet.VPPAppTeam{
|
||||
VPPAppID: fleet.VPPAppID{AdamID: s.appleVPPConfigSrvConfig.Assets[1].AdamID, Platform: fleet.MacOSPlatform},
|
||||
SelfService: true,
|
||||
InstallDuringSetup: ptr.Bool(false),
|
||||
},
|
||||
assoc[fleet.VPPAppID{AdamID: s.appleVPPConfigSrvConfig.Assets[1].AdamID, Platform: fleet.MacOSPlatform}])
|
||||
|
||||
// Reverse self-service associations
|
||||
@@ -10206,10 +10210,23 @@ func (s *integrationMDMTestSuite) TestBatchAssociateAppStoreApps() {
|
||||
assoc, err = s.ds.GetAssignedVPPApps(ctx, &tmGood.ID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, assoc, 4)
|
||||
assert.Equal(t, fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: s.appleVPPConfigSrvConfig.Assets[0].AdamID, Platform: fleet.MacOSPlatform}, SelfService: true}, assoc[fleet.VPPAppID{AdamID: s.appleVPPConfigSrvConfig.Assets[0].AdamID, Platform: fleet.MacOSPlatform}])
|
||||
assert.Equal(t, fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: s.appleVPPConfigSrvConfig.Assets[1].AdamID, Platform: fleet.IOSPlatform}}, assoc[fleet.VPPAppID{AdamID: s.appleVPPConfigSrvConfig.Assets[1].AdamID, Platform: fleet.IOSPlatform}])
|
||||
assert.Equal(t, fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: s.appleVPPConfigSrvConfig.Assets[1].AdamID, Platform: fleet.IPadOSPlatform}}, assoc[fleet.VPPAppID{AdamID: s.appleVPPConfigSrvConfig.Assets[1].AdamID, Platform: fleet.IPadOSPlatform}])
|
||||
assert.Equal(t, fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: s.appleVPPConfigSrvConfig.Assets[1].AdamID, Platform: fleet.MacOSPlatform}}, assoc[fleet.VPPAppID{AdamID: s.appleVPPConfigSrvConfig.Assets[1].AdamID, Platform: fleet.MacOSPlatform}])
|
||||
assert.Equal(t, fleet.VPPAppTeam{
|
||||
VPPAppID: fleet.VPPAppID{AdamID: s.appleVPPConfigSrvConfig.Assets[0].AdamID, Platform: fleet.MacOSPlatform},
|
||||
SelfService: true,
|
||||
InstallDuringSetup: ptr.Bool(false),
|
||||
}, assoc[fleet.VPPAppID{AdamID: s.appleVPPConfigSrvConfig.Assets[0].AdamID, Platform: fleet.MacOSPlatform}])
|
||||
assert.Equal(t, fleet.VPPAppTeam{
|
||||
VPPAppID: fleet.VPPAppID{AdamID: s.appleVPPConfigSrvConfig.Assets[1].AdamID, Platform: fleet.IOSPlatform},
|
||||
InstallDuringSetup: ptr.Bool(false),
|
||||
}, assoc[fleet.VPPAppID{AdamID: s.appleVPPConfigSrvConfig.Assets[1].AdamID, Platform: fleet.IOSPlatform}])
|
||||
assert.Equal(t, fleet.VPPAppTeam{
|
||||
VPPAppID: fleet.VPPAppID{AdamID: s.appleVPPConfigSrvConfig.Assets[1].AdamID, Platform: fleet.IPadOSPlatform},
|
||||
InstallDuringSetup: ptr.Bool(false),
|
||||
}, assoc[fleet.VPPAppID{AdamID: s.appleVPPConfigSrvConfig.Assets[1].AdamID, Platform: fleet.IPadOSPlatform}])
|
||||
assert.Equal(t, fleet.VPPAppTeam{
|
||||
VPPAppID: fleet.VPPAppID{AdamID: s.appleVPPConfigSrvConfig.Assets[1].AdamID, Platform: fleet.MacOSPlatform},
|
||||
InstallDuringSetup: ptr.Bool(false),
|
||||
}, assoc[fleet.VPPAppID{AdamID: s.appleVPPConfigSrvConfig.Assets[1].AdamID, Platform: fleet.MacOSPlatform}])
|
||||
|
||||
// Associate an app with a team with no team members
|
||||
s.Do("POST", batchURL, batchAssociateAppStoreAppsRequest{Apps: []fleet.VPPBatchPayload{{AppStoreID: s.appleVPPConfigSrvConfig.Assets[0].AdamID}}}, http.StatusNoContent, "team_name", tmEmpty.Name)
|
||||
@@ -11724,19 +11741,19 @@ func (s *integrationMDMTestSuite) TestSetupExperience() {
|
||||
"GET", "/api/latest/fleet/software/titles",
|
||||
listSoftwareTitlesRequest{},
|
||||
http.StatusOK, &respListTitles,
|
||||
"team_id", strconv.Itoa(int(team1.ID)),
|
||||
"team_id", fmt.Sprint(team1.ID),
|
||||
)
|
||||
require.Len(t, respListTitles.SoftwareTitles, 2)
|
||||
|
||||
var respGetSetupExperience getSetupExperienceSoftwareResponse
|
||||
s.DoJSON("GET", "/api/latest/fleet/setup_experience/software", getSetupExperienceSoftwareRequest{}, http.StatusOK, &respGetSetupExperience, "team_id", strconv.Itoa(int(team1.ID)))
|
||||
s.DoJSON("GET", "/api/latest/fleet/setup_experience/software", getSetupExperienceSoftwareRequest{}, http.StatusOK, &respGetSetupExperience, "team_id", fmt.Sprint(team1.ID))
|
||||
require.Len(t, respGetSetupExperience.SoftwareTitles, 2)
|
||||
|
||||
var respPutSetupExperience putSetupExperienceSoftwareResponse
|
||||
s.DoJSON("PUT", "/api/latest/fleet/setup_experience/software", putSetupExperienceSoftwareRequest{TeamID: team1.ID, TitleIDs: []uint{respListTitles.SoftwareTitles[0].ID, respListTitles.SoftwareTitles[1].ID}}, http.StatusOK, &respPutSetupExperience)
|
||||
require.Nil(t, respPutSetupExperience.error())
|
||||
|
||||
s.DoJSON("GET", "/api/latest/fleet/setup_experience/software", getSetupExperienceSoftwareRequest{}, http.StatusOK, &respGetSetupExperience, "team_id", strconv.Itoa(int(team1.ID)))
|
||||
s.DoJSON("GET", "/api/latest/fleet/setup_experience/software", getSetupExperienceSoftwareRequest{}, http.StatusOK, &respGetSetupExperience, "team_id", fmt.Sprint(team1.ID))
|
||||
require.Len(t, respGetSetupExperience.SoftwareTitles, 2)
|
||||
|
||||
desktopToken := uuid.New().String()
|
||||
|
||||
@@ -146,6 +146,13 @@ github.com/fleetdm/fleet/v4/server/fleet/MacOSSetup EnableReleaseDeviceManually
|
||||
github.com/fleetdm/fleet/v4/pkg/optjson/Bool Set bool
|
||||
github.com/fleetdm/fleet/v4/pkg/optjson/Bool Valid bool
|
||||
github.com/fleetdm/fleet/v4/pkg/optjson/Bool Value bool
|
||||
github.com/fleetdm/fleet/v4/server/fleet/MacOSSetup Script optjson.String
|
||||
github.com/fleetdm/fleet/v4/server/fleet/MacOSSetup Software optjson.Slice[*github.com/fleetdm/fleet/v4/server/fleet.MacOSSetupSoftware]
|
||||
github.com/fleetdm/fleet/v4/pkg/optjson/Slice[*github.com/fleetdm/fleet/v4/server/fleet.MacOSSetupSoftware] Set bool
|
||||
github.com/fleetdm/fleet/v4/pkg/optjson/Slice[*github.com/fleetdm/fleet/v4/server/fleet.MacOSSetupSoftware] Valid bool
|
||||
github.com/fleetdm/fleet/v4/pkg/optjson/Slice[*github.com/fleetdm/fleet/v4/server/fleet.MacOSSetupSoftware] Value []*fleet.MacOSSetupSoftware
|
||||
github.com/fleetdm/fleet/v4/server/fleet/MacOSSetupSoftware AppStoreID string
|
||||
github.com/fleetdm/fleet/v4/server/fleet/MacOSSetupSoftware PackagePath string
|
||||
github.com/fleetdm/fleet/v4/server/fleet/MDM MacOSMigration fleet.MacOSMigration
|
||||
github.com/fleetdm/fleet/v4/server/fleet/MacOSMigration Enable bool
|
||||
github.com/fleetdm/fleet/v4/server/fleet/MacOSMigration Mode fleet.MacOSMigrationMode string
|
||||
|
||||
@@ -28,6 +28,13 @@ github.com/fleetdm/fleet/v4/server/fleet/MacOSSetup EnableReleaseDeviceManually
|
||||
github.com/fleetdm/fleet/v4/pkg/optjson/Bool Set bool
|
||||
github.com/fleetdm/fleet/v4/pkg/optjson/Bool Valid bool
|
||||
github.com/fleetdm/fleet/v4/pkg/optjson/Bool Value bool
|
||||
github.com/fleetdm/fleet/v4/server/fleet/MacOSSetup Script optjson.String
|
||||
github.com/fleetdm/fleet/v4/server/fleet/MacOSSetup Software optjson.Slice[*github.com/fleetdm/fleet/v4/server/fleet.MacOSSetupSoftware]
|
||||
github.com/fleetdm/fleet/v4/pkg/optjson/Slice[*github.com/fleetdm/fleet/v4/server/fleet.MacOSSetupSoftware] Set bool
|
||||
github.com/fleetdm/fleet/v4/pkg/optjson/Slice[*github.com/fleetdm/fleet/v4/server/fleet.MacOSSetupSoftware] Valid bool
|
||||
github.com/fleetdm/fleet/v4/pkg/optjson/Slice[*github.com/fleetdm/fleet/v4/server/fleet.MacOSSetupSoftware] Value []*fleet.MacOSSetupSoftware
|
||||
github.com/fleetdm/fleet/v4/server/fleet/MacOSSetupSoftware AppStoreID string
|
||||
github.com/fleetdm/fleet/v4/server/fleet/MacOSSetupSoftware PackagePath string
|
||||
github.com/fleetdm/fleet/v4/server/fleet/TeamMDM WindowsSettings fleet.WindowsSettings
|
||||
github.com/fleetdm/fleet/v4/server/fleet/WindowsSettings CustomSettings optjson.Slice[github.com/fleetdm/fleet/v4/server/fleet.MDMProfileSpec]
|
||||
github.com/fleetdm/fleet/v4/pkg/optjson/Slice[github.com/fleetdm/fleet/v4/server/fleet.MDMProfileSpec] Set bool
|
||||
|
||||
Reference in New Issue
Block a user