diff --git a/changes/32164-block-vpp-installs-on-personal-apple-devices b/changes/32164-block-vpp-installs-on-personal-apple-devices new file mode 100644 index 0000000000..9037bca688 --- /dev/null +++ b/changes/32164-block-vpp-installs-on-personal-apple-devices @@ -0,0 +1 @@ +* Fixed a bug where blocking of VPP installs on personally enrolled apple devices was not in place \ No newline at end of file diff --git a/ee/server/service/software_installers.go b/ee/server/service/software_installers.go index a583e0bb2a..220d9200da 100644 --- a/ee/server/service/software_installers.go +++ b/ee/server/service/software_installers.go @@ -25,6 +25,7 @@ import ( "github.com/fleetdm/fleet/v4/server/fleet" "github.com/fleetdm/fleet/v4/server/mdm/apple/vpp" maintained_apps "github.com/fleetdm/fleet/v4/server/mdm/maintainedapps" + "github.com/fleetdm/fleet/v4/server/mdm/nanomdm/mdm" "github.com/fleetdm/fleet/v4/server/ptr" "github.com/go-kit/log" kitlog "github.com/go-kit/log" @@ -1085,6 +1086,16 @@ func (svc *Service) InstallSoftwareTitle(ctx context.Context, hostID uint, softw } return svc.installSoftwareTitleUsingInstaller(ctx, host, installer) } + } else { + // Get the enrollment type of the mobile apple device. + enrollment, err := svc.ds.GetNanoMDMEnrollment(ctx, host.UUID) + if err != nil { + return ctxerr.Wrap(ctx, err, "getting nano mdm enrollment") + } + + if enrollment.Type == mdm.EnrollType(mdm.UserEnrollmentDevice).String() { + return fleet.NewUserMessageError(errors.New(fleet.InstallSoftwarePersonalAppleDeviceErrMsg), http.StatusUnprocessableEntity) + } } vppApp, err := svc.ds.GetVPPAppByTeamAndTitleID(ctx, host.TeamID, softwareTitleID) diff --git a/ee/server/service/software_installers_test.go b/ee/server/service/software_installers_test.go index 9500271a41..8ea6455aca 100644 --- a/ee/server/service/software_installers_test.go +++ b/ee/server/service/software_installers_test.go @@ -16,6 +16,7 @@ import ( "github.com/fleetdm/fleet/v4/server/authz" "github.com/fleetdm/fleet/v4/server/contexts/viewer" "github.com/fleetdm/fleet/v4/server/fleet" + "github.com/fleetdm/fleet/v4/server/mdm/nanomdm/mdm" "github.com/fleetdm/fleet/v4/server/mock" "github.com/fleetdm/fleet/v4/server/ptr" "github.com/stretchr/testify/assert" @@ -212,6 +213,33 @@ func TestUninstallSoftwareTitle(t *testing.T) { require.ErrorContains(t, svc.UninstallSoftwareTitle(context.Background(), 1, 10), fleet.RunScriptsOrbitDisabledErrMsg) } +// TestInstallSoftwareTitle is mostly tested in enterprise integration test. This test hits is placed to hit some edge cases. +func TestInstallSoftwareTitle(t *testing.T) { + t.Parallel() + ds := new(mock.Store) + svc := newTestService(t, ds) + ctx := viewer.NewContext(context.Background(), viewer.Viewer{User: &fleet.User{GlobalRole: ptr.String(fleet.RoleAdmin)}}) + + host := &fleet.Host{ + UUID: "personal-ios", + OrbitNodeKey: ptr.String("orbit_key"), + Platform: "ios", + TeamID: ptr.Uint(1), + } + + ds.HostFunc = func(ctx context.Context, id uint) (*fleet.Host, error) { + return host, nil + } + + ds.GetNanoMDMEnrollmentFunc = func(ctx context.Context, id string) (*fleet.NanoEnrollment, error) { + return &fleet.NanoEnrollment{ + Type: mdm.EnrollType(mdm.UserEnrollmentDevice).String(), + }, nil + } + + require.ErrorContains(t, svc.InstallSoftwareTitle(ctx, 1, 10), fleet.InstallSoftwarePersonalAppleDeviceErrMsg) +} + func TestSoftwareInstallerPayloadFromSlug(t *testing.T) { t.Parallel() ds := new(mock.Store) diff --git a/server/fleet/errors.go b/server/fleet/errors.go index 3ecb562df1..2ff04187b8 100644 --- a/server/fleet/errors.go +++ b/server/fleet/errors.go @@ -641,6 +641,9 @@ const ( RunScripUnsavedMaxLenErrMsg = "Script is too large. It's limited to 10,000 characters (approximately 125 lines)." RunScriptGatewayTimeoutErrMsg = "Gateway timeout. Fleet didn't hear back from the host and doesn't know if the script ran. Please make sure your load balancer timeout isn't shorter than the Fleet server timeout." + // Software + InstallSoftwarePersonalAppleDeviceErrMsg = "Couldn't install. Currently, software install isn't supported on personal (BYOD) iOS and iPadOS hosts." + // End user authentication EndUserAuthDEPWebURLConfiguredErrMsg = `End user authentication can't be configured when the configured automatic enrollment (DEP) profile specifies a configuration_web_url.` // #nosec G101