Skip to content

Commit

Permalink
Initial implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
gillespi314 committed Oct 31, 2024
1 parent ccbdf46 commit 22ffecd
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 37 deletions.
25 changes: 22 additions & 3 deletions server/datastore/mysql/apple_mdm.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,23 @@ INSERT INTO
// filled in.
profileID, _ = res.LastInsertId()

labels := make([]fleet.ConfigurationProfileLabel, 0, len(cp.LabelsIncludeAll)+len(cp.LabelsExcludeAny))
labels := make([]fleet.ConfigurationProfileLabel, 0, len(cp.LabelsIncludeAll)+len(cp.LabelsIncludeAny)+len(cp.LabelsExcludeAny))
for i := range cp.LabelsIncludeAll {
cp.LabelsIncludeAll[i].ProfileUUID = profUUID
cp.LabelsIncludeAll[i].Exclude = false
cp.LabelsIncludeAll[i].RequireAll = true
labels = append(labels, cp.LabelsIncludeAll[i])
}
for i := range cp.LabelsIncludeAny {
cp.LabelsIncludeAny[i].ProfileUUID = profUUID
cp.LabelsIncludeAny[i].Exclude = false
cp.LabelsIncludeAny[i].RequireAll = false
labels = append(labels, cp.LabelsIncludeAny[i])
}
for i := range cp.LabelsExcludeAny {
cp.LabelsExcludeAny[i].ProfileUUID = profUUID
cp.LabelsExcludeAny[i].Exclude = true
cp.LabelsExcludeAny[i].RequireAll = false
labels = append(labels, cp.LabelsExcludeAny[i])
}
if _, err := batchSetProfileLabelAssociationsDB(ctx, tx, labels, "darwin"); err != nil {
Expand Down Expand Up @@ -3808,7 +3817,8 @@ func (ds *Datastore) updateHostDEPAssignProfileResponses(ctx context.Context, pa
}

func updateHostDEPAssignProfileResponses(ctx context.Context, tx sqlx.ExtContext, logger log.Logger, profileUUID string, serials []string,
status string, abmTokenID *uint) error {
status string, abmTokenID *uint,
) error {
if len(serials) == 0 {
return nil
}
Expand Down Expand Up @@ -4320,14 +4330,23 @@ func (ds *Datastore) insertOrUpsertMDMAppleDeclaration(ctx context.Context, insO
}

labels := make([]fleet.ConfigurationProfileLabel, 0,
len(declaration.LabelsIncludeAll)+len(declaration.LabelsExcludeAny))
len(declaration.LabelsIncludeAll)+len(declaration.LabelsIncludeAny)+len(declaration.LabelsExcludeAny))
for i := range declaration.LabelsIncludeAll {
declaration.LabelsIncludeAll[i].ProfileUUID = declUUID
declaration.LabelsIncludeAll[i].Exclude = false
declaration.LabelsIncludeAll[i].RequireAll = true
labels = append(labels, declaration.LabelsIncludeAll[i])
}
for i := range declaration.LabelsIncludeAny {
declaration.LabelsIncludeAny[i].ProfileUUID = declUUID
declaration.LabelsIncludeAny[i].Exclude = false
declaration.LabelsIncludeAny[i].RequireAll = false
labels = append(labels, declaration.LabelsIncludeAny[i])
}
for i := range declaration.LabelsExcludeAny {
declaration.LabelsExcludeAny[i].ProfileUUID = declUUID
declaration.LabelsExcludeAny[i].Exclude = true
declaration.LabelsExcludeAny[i].RequireAll = false
labels = append(labels, declaration.LabelsExcludeAny[i])
}
if _, err := batchSetDeclarationLabelAssociationsDB(ctx, tx, labels); err != nil {
Expand Down
11 changes: 10 additions & 1 deletion server/datastore/mysql/microsoft_mdm.go
Original file line number Diff line number Diff line change
Expand Up @@ -1571,13 +1571,22 @@ INSERT INTO
}
}

labels := make([]fleet.ConfigurationProfileLabel, 0, len(cp.LabelsIncludeAll)+len(cp.LabelsExcludeAny))
labels := make([]fleet.ConfigurationProfileLabel, 0, len(cp.LabelsIncludeAll)+len(cp.LabelsIncludeAny)+len(cp.LabelsExcludeAny))
for i := range cp.LabelsIncludeAll {
cp.LabelsIncludeAll[i].ProfileUUID = profileUUID
cp.LabelsIncludeAll[i].RequireAll = true
cp.LabelsIncludeAll[i].Exclude = false
labels = append(labels, cp.LabelsIncludeAll[i])
}
for i := range cp.LabelsIncludeAny {
cp.LabelsIncludeAny[i].ProfileUUID = profileUUID
cp.LabelsIncludeAny[i].RequireAll = false
cp.LabelsIncludeAny[i].Exclude = false
labels = append(labels, cp.LabelsIncludeAll[i])
}
for i := range cp.LabelsExcludeAny {
cp.LabelsExcludeAny[i].ProfileUUID = profileUUID
cp.LabelsExcludeAny[i].RequireAll = false
cp.LabelsExcludeAny[i].Exclude = true
labels = append(labels, cp.LabelsExcludeAny[i])
}
Expand Down
5 changes: 4 additions & 1 deletion server/fleet/apple_mdm.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ type MDMAppleConfigProfile struct {
// Checksum is an MD5 hash of the Mobileconfig bytes
Checksum []byte `db:"checksum" json:"checksum,omitempty"`
LabelsIncludeAll []ConfigurationProfileLabel `db:"-" json:"labels_include_all,omitempty"`
LabelsIncludeAny []ConfigurationProfileLabel `db:"-" json:"labels_include_any,omitempty"`
LabelsExcludeAny []ConfigurationProfileLabel `db:"-" json:"labels_exclude_any,omitempty"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UploadedAt time.Time `db:"uploaded_at" json:"updated_at"` // NOTE: JSON field is still `updated_at` for historical reasons, would be an API breaking change
Expand All @@ -223,7 +224,8 @@ type ConfigurationProfileLabel struct {
LabelName string `db:"label_name" json:"name"`
LabelID uint `db:"label_id" json:"id,omitempty"` // omitted if 0 (which is impossible if the label is not broken)
Broken bool `db:"broken" json:"broken,omitempty"` // omitted (not rendered to JSON) if false
Exclude bool `db:"exclude" json:"-"` // not rendered in JSON, used to store the profile in LabelsIncludeAll or LabelsExcludeAny on the parent profile
Exclude bool `db:"exclude" json:"-"` // not rendered in JSON, used to store the profile in LabelsIncludeAll, LabelsIncludeAny, or LabelsExcludeAny on the parent profile
RequireAll bool `db:"require_all" json:"-"` // not rendered in JSON, used to store the profile in LabelsIncludeAll, LabelsIncludeAny, or LabelsIncludeAny on the parent profile
}

func NewMDMAppleConfigProfile(raw []byte, teamID *uint) (*MDMAppleConfigProfile, error) {
Expand Down Expand Up @@ -599,6 +601,7 @@ type MDMAppleDeclaration struct {

// labels associated with this Declaration
LabelsIncludeAll []ConfigurationProfileLabel `db:"-" json:"labels_include_all,omitempty"`
LabelsIncludeAny []ConfigurationProfileLabel `db:"-" json:"labels_include_any,omitempty"`
LabelsExcludeAny []ConfigurationProfileLabel `db:"-" json:"labels_exclude_any,omitempty"`

CreatedAt time.Time `db:"created_at" json:"created_at"`
Expand Down
16 changes: 13 additions & 3 deletions server/fleet/mdm.go
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,14 @@ func MDMProfileSpecsMatch(a, b []MDMProfileSpec) bool {
return len(pathLabelIncludeCounts) == 0 && len(pathLabelExcludeCounts) == 0
}

type MDMLabelsMode string

const (
LabelsIncludeAll MDMLabelsMode = "labels_include_all"
LabelsIncludeAny MDMLabelsMode = "labels_include_any"
LabelsExcludeAny MDMLabelsMode = "labels_exclude_any"
)

type MDMAssetName string

const (
Expand Down Expand Up @@ -741,9 +749,11 @@ func FilterMacOSOnlyProfilesFromIOSIPadOS(profiles []*MDMAppleProfilePayload) []
}

// RefetchBaseCommandUUIDPrefix and below command prefixes are the prefixes used for MDM commands used to refetch information from iOS/iPadOS devices.
const RefetchBaseCommandUUIDPrefix = "REFETCH-"
const RefetchDeviceCommandUUIDPrefix = RefetchBaseCommandUUIDPrefix + "DEVICE-"
const RefetchAppsCommandUUIDPrefix = RefetchBaseCommandUUIDPrefix + "APPS-"
const (
RefetchBaseCommandUUIDPrefix = "REFETCH-"
RefetchDeviceCommandUUIDPrefix = RefetchBaseCommandUUIDPrefix + "DEVICE-"
RefetchAppsCommandUUIDPrefix = RefetchBaseCommandUUIDPrefix + "APPS-"
)

// VPPTokenInfo is the representation of the VPP token that we send out via API.
type VPPTokenInfo struct {
Expand Down
6 changes: 3 additions & 3 deletions server/fleet/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -758,9 +758,9 @@ type Service interface {
GetHostDEPAssignment(ctx context.Context, host *Host) (*HostDEPAssignment, error)

// NewMDMAppleConfigProfile creates a new configuration profile for the specified team.
NewMDMAppleConfigProfile(ctx context.Context, teamID uint, r io.Reader, labels []string, labelsExcludeMode bool) (*MDMAppleConfigProfile, error)
NewMDMAppleConfigProfile(ctx context.Context, teamID uint, r io.Reader, labels []string, labelsMembershipMode MDMLabelsMode) (*MDMAppleConfigProfile, error)
// NewMDMAppleConfigProfileWithPayload creates a new declaration for the specified team.
NewMDMAppleDeclaration(ctx context.Context, teamID uint, r io.Reader, labels []string, name string, labelsExcludeMode bool) (*MDMAppleDeclaration, error)
NewMDMAppleDeclaration(ctx context.Context, teamID uint, r io.Reader, labels []string, name string, labelsMembershipMode MDMLabelsMode) (*MDMAppleDeclaration, error)

// GetMDMAppleConfigProfileByDeprecatedID retrieves the specified Apple
// configuration profile via its numeric ID. This method is deprecated and
Expand Down Expand Up @@ -1032,7 +1032,7 @@ type Service interface {

// NewMDMWindowsConfigProfile creates a new Windows configuration profile for
// the specified team.
NewMDMWindowsConfigProfile(ctx context.Context, teamID uint, profileName string, r io.Reader, labels []string, labelsExcludeMode bool) (*MDMWindowsConfigProfile, error)
NewMDMWindowsConfigProfile(ctx context.Context, teamID uint, profileName string, r io.Reader, labels []string, labelsMembershipMode MDMLabelsMode) (*MDMWindowsConfigProfile, error)

// NewMDMUnsupportedConfigProfile is called when a profile with an
// unsupported extension is uploaded.
Expand Down
1 change: 1 addition & 0 deletions server/fleet/windows_mdm.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type MDMWindowsConfigProfile struct {
Name string `db:"name" json:"name"`
SyncML []byte `db:"syncml" json:"-"`
LabelsIncludeAll []ConfigurationProfileLabel `db:"-" json:"labels_include_all,omitempty"`
LabelsIncludeAny []ConfigurationProfileLabel `db:"-" json:"labels_include_any,omitempty"`
LabelsExcludeAny []ConfigurationProfileLabel `db:"-" json:"labels_exclude_any,omitempty"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UploadedAt time.Time `db:"uploaded_at" json:"updated_at"` // NOTE: JSON field is still `updated_at` for historical reasons, would be an API breaking change
Expand Down
28 changes: 19 additions & 9 deletions server/service/apple_mdm.go
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ func newMDMAppleConfigProfileEndpoint(ctx context.Context, request interface{},
}
defer ff.Close()
// providing an empty set of labels since this endpoint is only maintained for backwards compat
cp, err := svc.NewMDMAppleConfigProfile(ctx, req.TeamID, ff, nil, false)
cp, err := svc.NewMDMAppleConfigProfile(ctx, req.TeamID, ff, nil, fleet.LabelsIncludeAll)
if err != nil {
return &newMDMAppleConfigProfileResponse{Err: err}, nil
}
Expand All @@ -351,7 +351,7 @@ func newMDMAppleConfigProfileEndpoint(ctx context.Context, request interface{},
}, nil
}

func (svc *Service) NewMDMAppleConfigProfile(ctx context.Context, teamID uint, r io.Reader, labels []string, labelsExcludeMode bool) (*fleet.MDMAppleConfigProfile, error) {
func (svc *Service) NewMDMAppleConfigProfile(ctx context.Context, teamID uint, r io.Reader, labels []string, labelsMembershipMode fleet.MDMLabelsMode) (*fleet.MDMAppleConfigProfile, error) {
if err := svc.authz.Authorize(ctx, &fleet.MDMConfigProfileAuthz{TeamID: &teamID}, fleet.ActionWrite); err != nil {
return nil, ctxerr.Wrap(ctx, err)
}
Expand Down Expand Up @@ -395,10 +395,15 @@ func (svc *Service) NewMDMAppleConfigProfile(ctx context.Context, teamID uint, r
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "validating labels")
}
if labelsExcludeMode {
cp.LabelsExcludeAny = labelMap
} else {
switch labelsMembershipMode {
case fleet.LabelsIncludeAll:
cp.LabelsIncludeAll = labelMap
case fleet.LabelsIncludeAny:
cp.LabelsIncludeAny = labelMap
case fleet.LabelsExcludeAny:
cp.LabelsExcludeAny = labelMap
default:
// TODO what happens if mode is not set?s
}
err = validateConfigProfileFleetVariables(string(cp.Mobileconfig))
if err != nil {
Expand Down Expand Up @@ -456,7 +461,7 @@ func validateConfigProfileFleetVariables(contents string) error {
return nil
}

func (svc *Service) NewMDMAppleDeclaration(ctx context.Context, teamID uint, r io.Reader, labels []string, name string, labelsExcludeMode bool) (*fleet.MDMAppleDeclaration, error) {
func (svc *Service) NewMDMAppleDeclaration(ctx context.Context, teamID uint, r io.Reader, labels []string, name string, labelsMembershipMode fleet.MDMLabelsMode) (*fleet.MDMAppleDeclaration, error) {
if err := svc.authz.Authorize(ctx, &fleet.MDMConfigProfileAuthz{TeamID: &teamID}, fleet.ActionWrite); err != nil {
return nil, ctxerr.Wrap(ctx, err)
}
Expand Down Expand Up @@ -514,10 +519,15 @@ func (svc *Service) NewMDMAppleDeclaration(ctx context.Context, teamID uint, r i

d := fleet.NewMDMAppleDeclaration(data, tmID, name, rawDecl.Type, rawDecl.Identifier)

if labelsExcludeMode {
d.LabelsExcludeAny = validatedLabels
} else {
switch labelsMembershipMode {
case fleet.LabelsIncludeAll:
d.LabelsIncludeAll = validatedLabels
case fleet.LabelsIncludeAny:
d.LabelsIncludeAny = validatedLabels
case fleet.LabelsExcludeAny:
d.LabelsExcludeAny = validatedLabels
default:
// TODO what happens if mode is not set?s
}

decl, err := svc.ds.NewMDMAppleDeclaration(ctx, d)
Expand Down
51 changes: 34 additions & 17 deletions server/service/mdm.go
Original file line number Diff line number Diff line change
Expand Up @@ -1209,6 +1209,7 @@ type newMDMConfigProfileRequest struct {
TeamID uint
Profile *multipart.FileHeader
LabelsIncludeAll []string
LabelsIncludeAny []string
LabelsExcludeAny []string
}

Expand Down Expand Up @@ -1248,21 +1249,22 @@ func (newMDMConfigProfileRequest) DecodeRequest(ctx context.Context, r *http.Req
}

// add labels
var existsIncl, existsExcl, existsDepr bool
var existsInclAll, existsInclAny, existsExclAny, existsDepr bool
var deprecatedLabels []string
decoded.LabelsIncludeAll, existsIncl = r.MultipartForm.Value["labels_include_all"]
decoded.LabelsExcludeAny, existsExcl = r.MultipartForm.Value["labels_exclude_any"]
decoded.LabelsIncludeAll, existsInclAll = r.MultipartForm.Value[string(fleet.LabelsIncludeAll)]
decoded.LabelsIncludeAny, existsInclAny = r.MultipartForm.Value[string(fleet.LabelsIncludeAny)]
decoded.LabelsExcludeAny, existsExclAny = r.MultipartForm.Value[string(fleet.LabelsExcludeAny)]
deprecatedLabels, existsDepr = r.MultipartForm.Value["labels"]

// validate that only one of the labels type is provided
var count int
for _, b := range []bool{existsIncl, existsExcl, existsDepr} {
for _, b := range []bool{existsInclAll, existsInclAny, existsExclAny, existsDepr} {
if b {
count++
}
}
if count > 1 {
return nil, &fleet.BadRequestError{Message: `Only one of "labels_exclude_any", "labels_include_all" or "labels" can be included.`}
return nil, &fleet.BadRequestError{Message: `Only one of "labels_exclude_any", "labels_include_all", "labels_include_any", or "labels" can be included.`}
}
if existsDepr {
decoded.LabelsIncludeAll = deprecatedLabels
Expand Down Expand Up @@ -1292,17 +1294,25 @@ func newMDMConfigProfileEndpoint(ctx context.Context, request interface{}, svc f
isMobileConfig := strings.EqualFold(fileExt, ".mobileconfig")
isJSON := strings.EqualFold(fileExt, ".json")

labels := req.LabelsIncludeAll
excludeMode := false
if len(req.LabelsExcludeAny) > 0 {
var labels []string
var labelsMode fleet.MDMLabelsMode
switch {
case len(req.LabelsIncludeAny) > 0:
labels = req.LabelsIncludeAny
labelsMode = fleet.LabelsIncludeAny
case len(req.LabelsExcludeAny) > 0:
labels = req.LabelsExcludeAny
excludeMode = true
labelsMode = fleet.LabelsExcludeAny
default:
// TODO: should this be the default?
labels = req.LabelsIncludeAll
labelsMode = fleet.LabelsIncludeAll
}

if isMobileConfig || isJSON {
// Then it's an Apple configuration file
if isJSON {
decl, err := svc.NewMDMAppleDeclaration(ctx, req.TeamID, ff, labels, profileName, excludeMode)
decl, err := svc.NewMDMAppleDeclaration(ctx, req.TeamID, ff, labels, profileName, labelsMode)
if err != nil {
return &newMDMConfigProfileResponse{Err: err}, nil
}
Expand All @@ -1313,7 +1323,7 @@ func newMDMConfigProfileEndpoint(ctx context.Context, request interface{}, svc f

}

cp, err := svc.NewMDMAppleConfigProfile(ctx, req.TeamID, ff, labels, excludeMode)
cp, err := svc.NewMDMAppleConfigProfile(ctx, req.TeamID, ff, labels, labelsMode)
if err != nil {
return &newMDMConfigProfileResponse{Err: err}, nil
}
Expand All @@ -1323,7 +1333,7 @@ func newMDMConfigProfileEndpoint(ctx context.Context, request interface{}, svc f
}

if isWindows := strings.EqualFold(fileExt, ".xml"); isWindows {
cp, err := svc.NewMDMWindowsConfigProfile(ctx, req.TeamID, profileName, ff, labels, excludeMode)
cp, err := svc.NewMDMWindowsConfigProfile(ctx, req.TeamID, profileName, ff, labels, labelsMode)
if err != nil {
return &newMDMConfigProfileResponse{Err: err}, nil
}
Expand All @@ -1347,7 +1357,7 @@ func (svc *Service) NewMDMUnsupportedConfigProfile(ctx context.Context, teamID u
return &fleet.BadRequestError{Message: "Couldn't add profile. The file should be a .mobileconfig, XML, or JSON file."}
}

func (svc *Service) NewMDMWindowsConfigProfile(ctx context.Context, teamID uint, profileName string, r io.Reader, labels []string, labelsExcludeMode bool) (*fleet.MDMWindowsConfigProfile, error) {
func (svc *Service) NewMDMWindowsConfigProfile(ctx context.Context, teamID uint, profileName string, r io.Reader, labels []string, labelsMembershipMode fleet.MDMLabelsMode) (*fleet.MDMWindowsConfigProfile, error) {
if err := svc.authz.Authorize(ctx, &fleet.MDMConfigProfileAuthz{TeamID: &teamID}, fleet.ActionWrite); err != nil {
return nil, ctxerr.Wrap(ctx, err)
}
Expand Down Expand Up @@ -1396,11 +1406,17 @@ func (svc *Service) NewMDMWindowsConfigProfile(ctx context.Context, teamID uint,
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "validating labels")
}
if labelsExcludeMode {
cp.LabelsExcludeAny = labelMap
} else {
switch labelsMembershipMode {
case fleet.LabelsIncludeAll:
cp.LabelsIncludeAll = labelMap
case fleet.LabelsIncludeAny:
cp.LabelsIncludeAny = labelMap
case fleet.LabelsExcludeAny:
cp.LabelsExcludeAny = labelMap
default:
// TODO what happens if mode is not set?s
}

err = validateWindowsProfileFleetVariables(string(cp.SyncML))
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "validating Windows profile")
Expand Down Expand Up @@ -1678,7 +1694,8 @@ func (svc *Service) BatchSetMDMProfiles(
}

func validateFleetVariables(ctx context.Context, appleProfiles []*fleet.MDMAppleConfigProfile,
windowsProfiles []*fleet.MDMWindowsConfigProfile, appleDecls []*fleet.MDMAppleDeclaration) error {
windowsProfiles []*fleet.MDMWindowsConfigProfile, appleDecls []*fleet.MDMAppleDeclaration,
) error {
var err error
for _, p := range appleProfiles {
err = validateConfigProfileFleetVariables(string(p.Mobileconfig))
Expand Down

0 comments on commit 22ffecd

Please sign in to comment.