Match pre-assigned profiles to a team (or create one) and assign host to team (#12127)

This commit is contained in:
Martin Angers
2023-06-05 15:08:21 -04:00
committed by GitHub
parent 166b83a461
commit 9f064acd2e
13 changed files with 620 additions and 22 deletions
+116 -2
View File
@@ -7,10 +7,13 @@ import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"sort"
"strings"
"time"
"github.com/fleetdm/fleet/v4/pkg/file"
"github.com/fleetdm/fleet/v4/server/authz"
@@ -742,7 +745,118 @@ func (svc *Service) MDMAppleMatchPreassignment(ctx context.Context, externalHost
return ctxerr.Wrap(ctx, err)
}
// TODO(mna): TBD the exact signature, must return the list of profiles for matching
//profs, err := svc.profileMatcher.RetrieveProfiles(ctx, externalHostIdentifier)
profs, err := svc.profileMatcher.RetrieveProfiles(ctx, externalHostIdentifier)
if err != nil {
return err
}
if len(profs.Profiles) == 0 || profs.HostUUID == "" {
return nil // nothing to do
}
// load the host and ensure it is enrolled in Fleet MDM
host, err := svc.ds.HostByIdentifier(ctx, profs.HostUUID)
if err != nil {
return err // will return a not found error if host does not exist
}
hostMDM, err := svc.ds.GetHostMDM(ctx, host.ID)
if err != nil || !hostMDM.IsFleetEnrolled() {
if err == nil || fleet.IsNotFound(err) {
err = errors.New("host is not enrolled in Fleet MDM")
return ctxerr.Wrap(ctx, &fleet.BadRequestError{
Message: err.Error(),
InternalErr: err,
})
}
return err
}
// Collect the profiles' hashes and look for a team with exactly that set.
// Also collect the profiles' groups in case we need to create a new team,
// and the list of raw profiles bytes.
hashes, groups, rawProfiles := make([]string, 0, len(profs.Profiles)),
make([]string, 0, len(profs.Profiles)),
make([][]byte, 0, len(profs.Profiles))
for _, prof := range profs.Profiles {
hashes = append(hashes, prof.HexMD5Hash)
rawProfiles = append(rawProfiles, prof.Profile)
if prof.Group != "" {
groups = append(groups, prof.Group)
}
}
// find a team with exactly that set of profiles
teamIDs, err := svc.ds.MatchMDMAppleConfigProfiles(ctx, hashes)
if err != nil {
return err
}
var targetTeamID uint
if len(teamIDs) > 0 {
// if the host is already in one of those valid teams, nothing to do.
if host.TeamID != nil {
for _, tmID := range teamIDs {
if *host.TeamID == tmID {
return nil
}
}
}
// else assign the host to the first valid team
targetTeamID = teamIDs[0]
} else {
// Create a new team with this set of profiles. Creating via the service
// call so that it properly assigns the agent options and creates audit
// activities, etc.
teamName := teamNameFromPreassignGroups(groups)
tm, err := svc.NewTeam(ctx, fleet.TeamPayload{Name: &teamName})
if err != nil {
return err
}
// create profiles for that team via the service call, so that uniqueness
// of profile identifier/name is verified, activity created, etc.
// NOTE: this will use the read replica to load the team, which was just
// created above, could lead to not found issues with slow replication.
if err := svc.BatchSetMDMAppleProfiles(ctx, &tm.ID, nil, rawProfiles, false); err != nil {
return err
}
targetTeamID = tm.ID
}
// assign host to that team via the service call, which will trigger
// deployment of the profiles.
if err := svc.AddHostsToTeam(ctx, &targetTeamID, []uint{host.ID}); err != nil {
return err
}
return nil
}
// teamNameFromPreassignGroups returns the team name to use for a new team
// created to match the set of profiles preassigned to a host. The team name is
// derived from the "group" field provided with each request to pre-assign a
// profile to a host (in fleet.MDMApplePreassignProfilePayload). That field is
// optional, and empty groups are ignored. The current timestamp is appended to
// the team's name to help avoid duplicates.
func teamNameFromPreassignGroups(groups []string) string {
const defaultName = "default"
dedupeGroups := make(map[string]struct{}, len(groups))
for _, group := range groups {
if group != "" {
dedupeGroups[group] = struct{}{}
}
}
groups = groups[:0]
for group := range dedupeGroups {
groups = append(groups, group)
}
sort.Strings(groups)
if len(groups) == 0 {
groups = []string{defaultName}
}
return fmt.Sprintf("%s (%s)", strings.Join(groups, " - "), time.Now().UTC().Format("2006-01-02:15:04:05"))
}