From c7d5e5e6182c5676c2492a5e9669091ce8b094bf Mon Sep 17 00:00:00 2001 From: Martin Angers Date: Wed, 8 Nov 2023 11:36:57 -0500 Subject: [PATCH] Update authorization for MDM profiles to be platform-independent (#15023) --- ee/server/service/mdm.go | 8 +-- server/authz/policy.rego | 18 ++--- server/authz/policy_test.go | 6 +- server/datastore/mysql/microsoft_mdm.go | 39 ++++++++++ server/fleet/apple_mdm.go | 5 -- server/fleet/datastore.go | 8 +++ server/fleet/mdm.go | 11 +++ server/fleet/service.go | 3 + server/fleet/windows_mdm.go | 14 ++++ server/mock/datastore_mock.go | 24 +++++++ server/service/apple_mdm.go | 14 ++-- server/service/handler.go | 9 ++- server/service/mdm.go | 96 +++++++++++++++++++++++++ 13 files changed, 224 insertions(+), 31 deletions(-) diff --git a/ee/server/service/mdm.go b/ee/server/service/mdm.go index 78d0f27594..a8611da681 100644 --- a/ee/server/service/mdm.go +++ b/ee/server/service/mdm.go @@ -789,7 +789,7 @@ func (svc *Service) MDMApplePreassignProfile(ctx context.Context, payload fleet. // for the preassign and match features, we don't know yet what team(s) will // be affected, so we authorize only users with write-access to the no-team // config profiles and with team-write access. - if err := svc.authz.Authorize(ctx, &fleet.MDMAppleConfigProfile{}, fleet.ActionWrite); err != nil { + if err := svc.authz.Authorize(ctx, &fleet.MDMConfigProfileAuthz{}, fleet.ActionWrite); err != nil { return ctxerr.Wrap(ctx, err) } if err := svc.authz.Authorize(ctx, &fleet.Team{}, fleet.ActionWrite); err != nil { @@ -805,7 +805,7 @@ func (svc *Service) MDMAppleMatchPreassignment(ctx context.Context, externalHost // for the preassign and match features, we don't know yet what team(s) will // be affected, so we authorize only users with write-access to the no-team // config profiles and with team-write access. - if err := svc.authz.Authorize(ctx, &fleet.MDMAppleConfigProfile{}, fleet.ActionWrite); err != nil { + if err := svc.authz.Authorize(ctx, &fleet.MDMConfigProfileAuthz{}, fleet.ActionWrite); err != nil { return ctxerr.Wrap(ctx, err) } if err := svc.authz.Authorize(ctx, &fleet.Team{}, fleet.ActionWrite); err != nil { @@ -976,9 +976,7 @@ func teamNameFromPreassignGroups(groups []string) string { } func (svc *Service) GetMDMDiskEncryptionSummary(ctx context.Context, teamID *uint) (*fleet.MDMDiskEncryptionSummary, error) { - // TODO: Consider adding a new generic OSSetting type or Windows-specific type for authz checks - // like this. - if err := svc.authz.Authorize(ctx, fleet.MDMAppleConfigProfile{TeamID: teamID}, fleet.ActionRead); err != nil { + if err := svc.authz.Authorize(ctx, fleet.MDMConfigProfileAuthz{TeamID: teamID}, fleet.ActionRead); err != nil { return nil, ctxerr.Wrap(ctx, err) } var macOS fleet.MDMAppleFileVaultSummary diff --git a/server/authz/policy.rego b/server/authz/policy.rego index 965e42f461..41ce8c4959 100644 --- a/server/authz/policy.rego +++ b/server/authz/policy.rego @@ -595,37 +595,37 @@ allow { } ## -# Apple MDM +# Apple and Windows MDM ## -# Global admins and maintainers can read and write Apple MDM config profiles. +# Global admins and maintainers can read and write MDM config profiles. allow { - object.type == "mdm_apple_config_profile" + object.type == "mdm_config_profile" subject.global_role == [admin, maintainer][_] action == [read, write][_] } -# Global gitops can write Apple MDM config profiles. +# Global gitops can write MDM config profiles. allow { - object.type == "mdm_apple_config_profile" + object.type == "mdm_config_profile" subject.global_role == gitops action == write } -# Team admins and maintainers can read and write Apple MDM config profiles on their teams. +# Team admins and maintainers can read and write MDM config profiles on their teams. allow { not is_null(object.team_id) object.team_id != 0 - object.type == "mdm_apple_config_profile" + object.type == "mdm_config_profile" team_role(subject, object.team_id) == [admin, maintainer][_] action == [read, write][_] } -# Team gitops can write Apple MDM config profiles on their teams. +# Team gitops can write MDM config profiles on their teams. allow { not is_null(object.team_id) object.team_id != 0 - object.type == "mdm_apple_config_profile" + object.type == "mdm_config_profile" team_role(subject, object.team_id) == gitops action == write } diff --git a/server/authz/policy_test.go b/server/authz/policy_test.go index 9ea33f4c62..83fbe9587d 100644 --- a/server/authz/policy_test.go +++ b/server/authz/policy_test.go @@ -1287,11 +1287,11 @@ func TestAuthorizeTeamPolicy(t *testing.T) { }) } -func TestAuthorizeMDMAppleConfigProfile(t *testing.T) { +func TestAuthorizeMDMConfigProfile(t *testing.T) { t.Parallel() - globalProfile := &fleet.MDMAppleConfigProfile{} - team1Profile := &fleet.MDMAppleConfigProfile{ + globalProfile := &fleet.MDMConfigProfileAuthz{} + team1Profile := &fleet.MDMConfigProfileAuthz{ TeamID: ptr.Uint(1), } runTestCases(t, []authTestCase{ diff --git a/server/datastore/mysql/microsoft_mdm.go b/server/datastore/mysql/microsoft_mdm.go index f2546add00..c3683a9c68 100644 --- a/server/datastore/mysql/microsoft_mdm.go +++ b/server/datastore/mysql/microsoft_mdm.go @@ -576,3 +576,42 @@ WHERE Detail: dest.Detail, }, nil } + +func (ds *Datastore) GetMDMWindowsProfile(ctx context.Context, profileUUID string) (*fleet.MDMWindowsConfigProfile, error) { + stmt := ` +SELECT + profile_uuid, + team_id, + name, + syncml, + created_at, + updated_at +FROM + mdm_windows_configuration_profiles +WHERE + profile_uuid=?` + + var res fleet.MDMWindowsConfigProfile + err := sqlx.GetContext(ctx, ds.reader(ctx), &res, stmt, profileUUID) + if err != nil { + if err == sql.ErrNoRows { + return nil, ctxerr.Wrap(ctx, notFound("MDMWindowsProfile").WithName(profileUUID)) + } + return nil, ctxerr.Wrap(ctx, err, "get mdm windows config profile") + } + + return &res, nil +} + +func (ds *Datastore) DeleteMDMWindowsProfile(ctx context.Context, profileUUID string) error { + res, err := ds.writer(ctx).ExecContext(ctx, `DELETE FROM mdm_windows_configuration_profiles WHERE profile_uuid=?`, profileUUID) + if err != nil { + return ctxerr.Wrap(ctx, err) + } + + deleted, _ := res.RowsAffected() // cannot fail for mysql + if deleted != 1 { + return ctxerr.Wrap(ctx, notFound("MDMWindowsProfile").WithName(profileUUID)) + } + return nil +} diff --git a/server/fleet/apple_mdm.go b/server/fleet/apple_mdm.go index f68ffdc886..d5badf1a3b 100644 --- a/server/fleet/apple_mdm.go +++ b/server/fleet/apple_mdm.go @@ -208,11 +208,6 @@ func NewMDMAppleConfigProfile(raw []byte, teamID *uint) (*MDMAppleConfigProfile, }, nil } -// AuthzType implements authz.AuthzTyper. -func (cp MDMAppleConfigProfile) AuthzType() string { - return "mdm_apple_config_profile" -} - func (cp MDMAppleConfigProfile) ValidateUserProvided() error { if _, ok := mobileconfig.FleetPayloadIdentifiers()[cp.Identifier]; ok { return fmt.Errorf("payload identifier %s is not allowed", cp.Identifier) diff --git a/server/fleet/datastore.go b/server/fleet/datastore.go index d19bf531a1..4d97192232 100644 --- a/server/fleet/datastore.go +++ b/server/fleet/datastore.go @@ -1068,6 +1068,14 @@ type Datastore interface { // UpdateMDMWindowsEnrollmentsHostUUID updates the host UUID for a given MDM device ID. UpdateMDMWindowsEnrollmentsHostUUID(ctx context.Context, hostUUID string, mdmDeviceID string) error + // GetMDMWindowsProfile returns the Windows MDM profile corresponding to the + // specified profile uuid. + GetMDMWindowsProfile(ctx context.Context, profileUUID string) (*MDMWindowsConfigProfile, error) + + // DeleteMDMWindowsProfile deletes the Windows MDM profile corresponding to + // the specified profile uuid. + DeleteMDMWindowsProfile(ctx context.Context, profileUUID string) error + /////////////////////////////////////////////////////////////////////////////// // MDM Commands diff --git a/server/fleet/mdm.go b/server/fleet/mdm.go index 2aafee6962..1950f18946 100644 --- a/server/fleet/mdm.go +++ b/server/fleet/mdm.go @@ -294,3 +294,14 @@ const ( MDMOperationTypeInstall MDMOperationType = "install" MDMOperationTypeRemove MDMOperationType = "remove" ) + +// MDMConfigProfileAuthz is used to check user authorization to read/write an +// MDM configuration profile. +type MDMConfigProfileAuthz struct { + TeamID *uint `json:"team_id"` // required for authorization by team +} + +// AuthzType implements authz.AuthzTyper. +func (m MDMConfigProfileAuthz) AuthzType() string { + return "mdm_config_profile" +} diff --git a/server/fleet/service.go b/server/fleet/service.go index 219c1eeda4..c2b442f43f 100644 --- a/server/fleet/service.go +++ b/server/fleet/service.go @@ -810,6 +810,9 @@ type Service interface { // Set or update the disk encryption key for a host. SetOrUpdateDiskEncryptionKey(ctx context.Context, encryptionKey, clientError string) error + // DeleteMDMWindowsProfile deletes the specified windows profile. + DeleteMDMWindowsProfile(ctx context.Context, profileUUID string) error + /////////////////////////////////////////////////////////////////////////////// // Common MDM diff --git a/server/fleet/windows_mdm.go b/server/fleet/windows_mdm.go index 0a72f5aea7..270f6370a0 100644 --- a/server/fleet/windows_mdm.go +++ b/server/fleet/windows_mdm.go @@ -1,5 +1,9 @@ package fleet +import ( + "time" +) + // MDMWindowsBitLockerSummary reports the number of Windows hosts being managed by Fleet with // BitLocker. Each host may be counted in only one of six mutually-exclusive categories: // Verified, Verifying, ActionRequired, Enforcing, Failed, RemovingEnforcement. @@ -14,3 +18,13 @@ type MDMWindowsBitLockerSummary struct { Failed uint `json:"failed" db:"failed"` RemovingEnforcement uint `json:"removing_enforcement" db:"removing_enforcement"` } + +// MDMWindowsConfigProfile represents a Windows MDM profile in Fleet. +type MDMWindowsConfigProfile struct { + ProfileUUID string `db:"profile_uuid" json:"profile_uuid"` + TeamID *uint `db:"team_id" json:"team_id"` + Name string `db:"name" json:"name"` + SyncML []byte `db:"syncml" json:"-"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` +} diff --git a/server/mock/datastore_mock.go b/server/mock/datastore_mock.go index fa5e37ee8e..4a2bcaa56c 100644 --- a/server/mock/datastore_mock.go +++ b/server/mock/datastore_mock.go @@ -698,6 +698,10 @@ type GetMDMWindowsCommandResultsFunc func(ctx context.Context, commandUUID strin type UpdateMDMWindowsEnrollmentsHostUUIDFunc func(ctx context.Context, hostUUID string, mdmDeviceID string) error +type GetMDMWindowsProfileFunc func(ctx context.Context, profileUUID string) (*fleet.MDMWindowsConfigProfile, error) + +type DeleteMDMWindowsProfileFunc func(ctx context.Context, profileUUID string) error + type GetMDMCommandPlatformFunc func(ctx context.Context, commandUUID string) (string, error) type ListMDMCommandsFunc func(ctx context.Context, tmFilter fleet.TeamFilter, listOpts *fleet.MDMCommandListOptions) ([]*fleet.MDMCommand, error) @@ -1749,6 +1753,12 @@ type DataStore struct { UpdateMDMWindowsEnrollmentsHostUUIDFunc UpdateMDMWindowsEnrollmentsHostUUIDFunc UpdateMDMWindowsEnrollmentsHostUUIDFuncInvoked bool + GetMDMWindowsProfileFunc GetMDMWindowsProfileFunc + GetMDMWindowsProfileFuncInvoked bool + + DeleteMDMWindowsProfileFunc DeleteMDMWindowsProfileFunc + DeleteMDMWindowsProfileFuncInvoked bool + GetMDMCommandPlatformFunc GetMDMCommandPlatformFunc GetMDMCommandPlatformFuncInvoked bool @@ -4177,6 +4187,20 @@ func (s *DataStore) UpdateMDMWindowsEnrollmentsHostUUID(ctx context.Context, hos return s.UpdateMDMWindowsEnrollmentsHostUUIDFunc(ctx, hostUUID, mdmDeviceID) } +func (s *DataStore) GetMDMWindowsProfile(ctx context.Context, profileUUID string) (*fleet.MDMWindowsConfigProfile, error) { + s.mu.Lock() + s.GetMDMWindowsProfileFuncInvoked = true + s.mu.Unlock() + return s.GetMDMWindowsProfileFunc(ctx, profileUUID) +} + +func (s *DataStore) DeleteMDMWindowsProfile(ctx context.Context, profileUUID string) error { + s.mu.Lock() + s.DeleteMDMWindowsProfileFuncInvoked = true + s.mu.Unlock() + return s.DeleteMDMWindowsProfileFunc(ctx, profileUUID) +} + func (s *DataStore) GetMDMCommandPlatform(ctx context.Context, commandUUID string) (string, error) { s.mu.Lock() s.GetMDMCommandPlatformFuncInvoked = true diff --git a/server/service/apple_mdm.go b/server/service/apple_mdm.go index a4b9572d09..8532235bd6 100644 --- a/server/service/apple_mdm.go +++ b/server/service/apple_mdm.go @@ -307,7 +307,7 @@ func newMDMAppleConfigProfileEndpoint(ctx context.Context, request interface{}, } func (svc *Service) NewMDMAppleConfigProfile(ctx context.Context, teamID uint, r io.Reader, size int64) (*fleet.MDMAppleConfigProfile, error) { - if err := svc.authz.Authorize(ctx, &fleet.MDMAppleConfigProfile{TeamID: &teamID}, fleet.ActionWrite); err != nil { + if err := svc.authz.Authorize(ctx, &fleet.MDMConfigProfileAuthz{TeamID: &teamID}, fleet.ActionWrite); err != nil { return nil, ctxerr.Wrap(ctx, err) } var teamName string @@ -386,7 +386,7 @@ func listMDMAppleConfigProfilesEndpoint(ctx context.Context, request interface{} } func (svc *Service) ListMDMAppleConfigProfiles(ctx context.Context, teamID uint) ([]*fleet.MDMAppleConfigProfile, error) { - if err := svc.authz.Authorize(ctx, &fleet.MDMAppleConfigProfile{TeamID: &teamID}, fleet.ActionRead); err != nil { + if err := svc.authz.Authorize(ctx, &fleet.MDMConfigProfileAuthz{TeamID: &teamID}, fleet.ActionRead); err != nil { return nil, ctxerr.Wrap(ctx, err) } @@ -462,7 +462,7 @@ func (svc *Service) GetMDMAppleConfigProfile(ctx context.Context, profileID uint } // now we can do a specific authz check based on team id of profile before we return the profile - if err := svc.authz.Authorize(ctx, cp, fleet.ActionRead); err != nil { + if err := svc.authz.Authorize(ctx, &fleet.MDMConfigProfileAuthz{TeamID: cp.TeamID}, fleet.ActionRead); err != nil { return nil, err } @@ -511,7 +511,7 @@ func (svc *Service) DeleteMDMAppleConfigProfile(ctx context.Context, profileID u } // now we can do a specific authz check based on team id of profile before we delete the profile - if err := svc.authz.Authorize(ctx, cp, fleet.ActionWrite); err != nil { + if err := svc.authz.Authorize(ctx, &fleet.MDMConfigProfileAuthz{TeamID: cp.TeamID}, fleet.ActionWrite); err != nil { return ctxerr.Wrap(ctx, err) } @@ -572,7 +572,7 @@ func getMDMAppleProfilesSummaryEndpoint(ctx context.Context, request interface{} } func (svc *Service) GetMDMAppleProfilesSummary(ctx context.Context, teamID *uint) (*fleet.MDMAppleConfigProfilesSummary, error) { - if err := svc.authz.Authorize(ctx, fleet.MDMAppleConfigProfile{TeamID: teamID}, fleet.ActionRead); err != nil { + if err := svc.authz.Authorize(ctx, fleet.MDMConfigProfileAuthz{TeamID: teamID}, fleet.ActionRead); err != nil { return nil, ctxerr.Wrap(ctx, err) } @@ -609,7 +609,7 @@ func getMdmAppleFileVaultSummaryEndpoint(ctx context.Context, request interface{ } func (svc *Service) GetMDMAppleFileVaultSummary(ctx context.Context, teamID *uint) (*fleet.MDMAppleFileVaultSummary, error) { - if err := svc.authz.Authorize(ctx, fleet.MDMAppleConfigProfile{TeamID: teamID}, fleet.ActionRead); err != nil { + if err := svc.authz.Authorize(ctx, fleet.MDMConfigProfileAuthz{TeamID: teamID}, fleet.ActionRead); err != nil { return nil, ctxerr.Wrap(ctx, err) } @@ -1417,7 +1417,7 @@ func (svc *Service) BatchSetMDMAppleProfiles(ctx context.Context, tmID *uint, tm } } - if err := svc.authz.Authorize(ctx, &fleet.MDMAppleConfigProfile{TeamID: tmID}, fleet.ActionWrite); err != nil { + if err := svc.authz.Authorize(ctx, &fleet.MDMConfigProfileAuthz{TeamID: tmID}, fleet.ActionWrite); err != nil { return ctxerr.Wrap(ctx, err) } diff --git a/server/service/handler.go b/server/service/handler.go index 44f3599549..f9427e9032 100644 --- a/server/service/handler.go +++ b/server/service/handler.go @@ -475,12 +475,16 @@ func attachFleetAPIRoutes(r *mux.Router, svc fleet.Service, config config.FleetC // platform-agnostic POST /mdm/commands/commands. It is still supported // indefinitely for backwards compatibility. mdmAppleMW.GET("/api/_version_/fleet/mdm/apple/commands", listMDMAppleCommandsEndpoint, listMDMAppleCommandsRequest{}) + // Deprecated: GET and DELETE /mdm/apple/profiles/{profile_id} are now + // deprecated, replaced by the platform-agnostic GET/DELETE + // /mdm/profiles/{profile_id}. It is still supported indefinitely for + // backwards compatibility. + mdmAppleMW.GET("/api/_version_/fleet/mdm/apple/profiles/{profile_id:[0-9]+}", getMDMAppleConfigProfileEndpoint, getMDMAppleConfigProfileRequest{}) + mdmAppleMW.DELETE("/api/_version_/fleet/mdm/apple/profiles/{profile_id:[0-9]+}", deleteMDMAppleConfigProfileEndpoint, deleteMDMAppleConfigProfileRequest{}) mdmAppleMW.GET("/api/_version_/fleet/mdm/apple/filevault/summary", getMdmAppleFileVaultSummaryEndpoint, getMDMAppleFileVaultSummaryRequest{}) mdmAppleMW.POST("/api/_version_/fleet/mdm/apple/profiles", newMDMAppleConfigProfileEndpoint, newMDMAppleConfigProfileRequest{}) mdmAppleMW.GET("/api/_version_/fleet/mdm/apple/profiles", listMDMAppleConfigProfilesEndpoint, listMDMAppleConfigProfilesRequest{}) - mdmAppleMW.GET("/api/_version_/fleet/mdm/apple/profiles/{profile_id:[0-9]+}", getMDMAppleConfigProfileEndpoint, getMDMAppleConfigProfileRequest{}) - mdmAppleMW.DELETE("/api/_version_/fleet/mdm/apple/profiles/{profile_id:[0-9]+}", deleteMDMAppleConfigProfileEndpoint, deleteMDMAppleConfigProfileRequest{}) mdmAppleMW.GET("/api/_version_/fleet/mdm/apple/profiles/summary", getMDMAppleProfilesSummaryEndpoint, getMDMAppleProfilesSummaryRequest{}) mdmAppleMW.POST("/api/_version_/fleet/mdm/apple/enrollment_profile", createMDMAppleSetupAssistantEndpoint, createMDMAppleSetupAssistantRequest{}) mdmAppleMW.GET("/api/_version_/fleet/mdm/apple/enrollment_profile", getMDMAppleSetupAssistantEndpoint, getMDMAppleSetupAssistantRequest{}) @@ -525,6 +529,7 @@ func attachFleetAPIRoutes(r *mux.Router, svc fleet.Service, config config.FleetC mdmAnyMW.GET("/api/_version_/fleet/mdm/commands", listMDMCommandsEndpoint, listMDMCommandsRequest{}) mdmAnyMW.GET("/api/_version_/fleet/mdm/disk_encryption/summary", getMDMDiskEncryptionSummaryEndpoint, getMDMDiskEncryptionSummaryRequest{}) mdmAnyMW.GET("/api/_version_/fleet/mdm/hosts/{id:[0-9]+}/encryption_key", getHostEncryptionKey, getHostEncryptionKeyRequest{}) + //mdmAnyMW.DELETE("/api/_version_/fleet/mdm/profiles/{profile_id_or_uuid}", deleteMDMProfileEndpoint, deleteMDMProfileRequest{}) // the following set of mdm endpoints must always be accessible (even // if MDM is not configured) as it bootstraps the setup of MDM diff --git a/server/service/mdm.go b/server/service/mdm.go index d85555ca82..419930efef 100644 --- a/server/service/mdm.go +++ b/server/service/mdm.go @@ -935,3 +935,99 @@ func (svc *Service) authorizeAllHostsTeams(ctx context.Context, hostUUIDs []stri } return hosts, nil } + +//////////////////////////////////////////////////////////////////////////////// +// DELETE /mdm/profiles/{id_or_uuid} +//////////////////////////////////////////////////////////////////////////////// + +type deleteMDMProfileRequest struct { + ProfileIDOrUUID string `url:"profile_id_or_uuid"` +} + +type deleteMDMProfileResponse struct { + Err error `json:"error,omitempty"` +} + +func (r deleteMDMProfileResponse) error() error { return r.Err } + +var _ = deleteMDMProfileEndpoint // Temporary, to ensure it is used. + +func deleteMDMProfileEndpoint(ctx context.Context, request interface{}, svc fleet.Service) (errorer, error) { + req := request.(*deleteMDMProfileRequest) + + appleID, isApple := isAppleProfileID(req.ProfileIDOrUUID) + var err error + if isApple { + err = svc.DeleteMDMAppleConfigProfile(ctx, appleID) + } else { + err = svc.DeleteMDMWindowsProfile(ctx, req.ProfileIDOrUUID) + } + return &deleteMDMProfileResponse{Err: err}, nil +} + +func (svc *Service) DeleteMDMWindowsProfile(ctx context.Context, profileUUID string) error { + // first we perform a perform basic authz check + if err := svc.authz.Authorize(ctx, &fleet.Team{}, fleet.ActionRead); err != nil { + return ctxerr.Wrap(ctx, err) + } + + prof, err := svc.ds.GetMDMWindowsProfile(ctx, profileUUID) + if err != nil { + return ctxerr.Wrap(ctx, err) + } + + var teamName string + teamID := *prof.TeamID + if teamID >= 1 { + tm, err := svc.EnterpriseOverrides.TeamByIDOrName(ctx, &teamID, nil) + if err != nil { + return ctxerr.Wrap(ctx, err) + } + teamName = tm.Name + } + + // now we can do a specific authz check based on team id of profile before we delete the profile + if err := svc.authz.Authorize(ctx, &fleet.MDMConfigProfileAuthz{TeamID: prof.TeamID}, fleet.ActionWrite); err != nil { + return ctxerr.Wrap(ctx, err) + } + + // TODO: prevent deleting profiles that are managed by Fleet + //if _, ok := mobileconfig.FleetPayloadIdentifiers()[cp.Identifier]; ok { + // return &fleet.BadRequestError{ + // Message: "profiles managed by Fleet can't be deleted using this endpoint.", + // InternalErr: fmt.Errorf("deleting profile %s for team %s not allowed because it's managed by Fleet", cp.Identifier, teamName), + // } + //} + + if err := svc.ds.DeleteMDMWindowsProfile(ctx, profileUUID); err != nil { + return ctxerr.Wrap(ctx, err) + } + //// cannot use the profile ID as it is now deleted + //if err := svc.ds.BulkSetPendingMDMAppleHostProfiles(ctx, nil, []uint{teamID}, nil, nil); err != nil { + // return ctxerr.Wrap(ctx, err, "bulk set pending host profiles") + //} + + _ = teamName + //if err := svc.ds.NewActivity(ctx, authz.UserFromContext(ctx), &fleet.ActivityTypeDeletedMacosProfile{ + // TeamID: &teamID, + // TeamName: &teamName, + // ProfileName: cp.Name, + // ProfileIdentifier: cp.Identifier, + //}); err != nil { + // return ctxerr.Wrap(ctx, err, "logging activity for delete mdm apple config profile") + //} + + return nil +} + +// returns the numeric Apple profile ID and true if it is an Apple identifier, +// or 0 and false otherwise. +func isAppleProfileID(profileIDOrUUID string) (uint, bool) { + // parsing as 32 bits as that's the maximum value of the DB column (and can + // be safely converted to uint). + id, err := strconv.ParseUint(profileIDOrUUID, 10, 32) + if err != nil { + return 0, false + } + return uint(id), true +}