mirror of
https://github.com/fleetdm/fleet
synced 2026-05-24 01:18:42 +00:00
Update authorization for MDM profiles to be platform-independent (#15023)
This commit is contained in:
parent
c602a1a2d3
commit
c7d5e5e618
13 changed files with 224 additions and 31 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue