diff --git a/ee/server/service/teams.go b/ee/server/service/teams.go index 35acae7c0d7..b1ac0d92f4f 100644 --- a/ee/server/service/teams.go +++ b/ee/server/service/teams.go @@ -383,6 +383,20 @@ func (svc *Service) DeleteTeam(ctx context.Context, teamID uint) error { } name := team.Name + vc, ok := viewer.FromContext(ctx) + if !ok { + return fleet.ErrNoContext + } + filter := fleet.TeamFilter{User: vc.User, IncludeObserver: true} + hosts, err := svc.ds.ListHosts(ctx, filter, fleet.HostListOptions{TeamFilter: &teamID}) + if err != nil { + return ctxerr.Wrap(ctx, err, "list hosts for reconcile profiles on team change") + } + hostIDs := make([]uint, 0, len(hosts)) + for _, host := range hosts { + hostIDs = append(hostIDs, host.ID) + } + if err := svc.ds.DeleteTeam(ctx, teamID); err != nil { return err } @@ -391,6 +405,10 @@ func (svc *Service) DeleteTeam(ctx context.Context, teamID uint) error { return ctxerr.Wrap(ctx, err, "bulk set pending host profiles") } + if err := svc.ds.CleanupDiskEncryptionKeysOnTeamChange(ctx, hostIDs, ptr.Uint(0)); err != nil { + return ctxerr.Wrap(ctx, err, "reconcile profiles on team change cleanup disk encryption keys") + } + logging.WithExtras(ctx, "id", teamID) if err := svc.ds.NewActivity( diff --git a/server/datastore/mysql/apple_mdm.go b/server/datastore/mysql/apple_mdm.go index af7a1cbdd91..bfefc6c79ca 100644 --- a/server/datastore/mysql/apple_mdm.go +++ b/server/datastore/mysql/apple_mdm.go @@ -1,7 +1,6 @@ package mysql import ( - "bytes" "context" "database/sql" "errors" @@ -22,8 +21,8 @@ import ( func (ds *Datastore) NewMDMAppleConfigProfile(ctx context.Context, cp fleet.MDMAppleConfigProfile) (*fleet.MDMAppleConfigProfile, error) { stmt := ` INSERT INTO - mdm_apple_configuration_profiles (team_id, identifier, name, mobileconfig) -VALUES (?, ?, ?, ?)` + mdm_apple_configuration_profiles (team_id, identifier, name, mobileconfig, checksum) +VALUES (?, ?, ?, ?, UNHEX(MD5(mobileconfig)))` var teamID uint if cp.TeamID != nil { @@ -174,14 +173,20 @@ SELECT profile_id, profile_name AS name, profile_identifier AS identifier, - status, + -- internally, a NULL status implies that the cron needs to pick up + -- this profile, for the user that difference doesn't exist, the + -- profile is effectively pending. This is consistent with all our + -- aggregation functions. + COALESCE(status, '%s') AS status, COALESCE(operation_type, '') AS operation_type, COALESCE(detail, '') AS detail FROM host_mdm_apple_profiles WHERE - host_uuid = ? AND NOT (operation_type = '%s' AND COALESCE(status, '') = '%s')`, + host_uuid = ? AND NOT (operation_type = '%s' AND COALESCE(status, '%s') = '%s')`, + fleet.MDMAppleDeliveryPending, fleet.MDMAppleOperationTypeRemove, + fleet.MDMAppleDeliveryPending, fleet.MDMAppleDeliveryApplied, ) @@ -891,10 +896,14 @@ WHERE const insertNewOrEditedProfile = ` INSERT INTO mdm_apple_configuration_profiles ( - team_id, identifier, name, mobileconfig + team_id, identifier, name, mobileconfig, checksum ) VALUES - ( ?, ?, ?, ? ) + ( ?, ?, ?, ?, UNHEX(MD5(mobileconfig)) ) +ON DUPLICATE KEY UPDATE + name = VALUES(name), + mobileconfig = VALUES(mobileconfig), + checksum = UNHEX(MD5(VALUES(mobileconfig))) ` // use a profile team id of 0 if no-team @@ -928,16 +937,11 @@ VALUES } } - // match the existing profiles to the incoming ones and keep the existing - // ones that have not changed + // figure out if we need to delete any profiles keepIdents := make([]string, 0, len(incomingIdents)) for _, p := range existingProfiles { if newP := incomingProfs[p.Identifier]; newP != nil { - if bytes.Equal(newP.Mobileconfig, p.Mobileconfig) { - // the profile has not changed, keep the existing one - keepIdents = append(keepIdents, p.Identifier) - delete(incomingProfs, p.Identifier) - } + keepIdents = append(keepIdents, p.Identifier) } } @@ -975,117 +979,97 @@ VALUES // (i.e. pass 0 in that case as part of the teamIDs slice). Only one of the // slice arguments can have values. func (ds *Datastore) BulkSetPendingMDMAppleHostProfiles(ctx context.Context, hostIDs, teamIDs, profileIDs []uint, hostUUIDs []string) error { - var countArgs int - if len(hostIDs) > 0 { - countArgs++ - } - if len(teamIDs) > 0 { - countArgs++ - } - if len(profileIDs) > 0 { - countArgs++ - } - if len(hostUUIDs) > 0 { - countArgs++ - } - if countArgs > 1 { - return errors.New("only one of hostIDs, teamIDs, profileIDs or hostUUIDs can be provided") - } - if countArgs == 0 { - return nil - } + return ds.withTx(ctx, func(tx sqlx.ExtContext) error { + var countArgs int + if len(hostIDs) > 0 { + countArgs++ + } + if len(teamIDs) > 0 { + countArgs++ + } + if len(profileIDs) > 0 { + countArgs++ + } + if len(hostUUIDs) > 0 { + countArgs++ + } + if countArgs > 1 { + return errors.New("only one of hostIDs, teamIDs, profileIDs or hostUUIDs can be provided") + } + if countArgs == 0 { + return nil + } - var ( - uuids []string - args []any - uuidStmt string - ) + var ( + uuids []string + args []any + uuidStmt string + ) - switch { - case len(hostUUIDs) > 0: - // no need to run a query to load host UUIDs, that's what we received - // directly. - uuids = hostUUIDs - - case len(hostIDs) > 0: - uuidStmt = `SELECT uuid FROM hosts WHERE id IN (?)` - args = append(args, hostIDs) - - case len(teamIDs) > 0: - uuidStmt = `SELECT uuid FROM hosts WHERE ` - if len(teamIDs) == 1 && teamIDs[0] == 0 { - uuidStmt += `team_id IS NULL` - } else { - uuidStmt += `team_id IN (?)` - args = append(args, teamIDs) - for _, tmID := range teamIDs { - if tmID == 0 { - uuidStmt += ` OR team_id IS NULL` - break + switch { + case len(hostUUIDs) > 0: + // no need to run a query to load host UUIDs, that's what we received + // directly. + uuids = hostUUIDs + + case len(hostIDs) > 0: + uuidStmt = `SELECT uuid FROM hosts WHERE id IN (?)` + args = append(args, hostIDs) + + case len(teamIDs) > 0: + uuidStmt = `SELECT uuid FROM hosts WHERE ` + if len(teamIDs) == 1 && teamIDs[0] == 0 { + uuidStmt += `team_id IS NULL` + } else { + uuidStmt += `team_id IN (?)` + args = append(args, teamIDs) + for _, tmID := range teamIDs { + if tmID == 0 { + uuidStmt += ` OR team_id IS NULL` + break + } } } - } - case len(profileIDs) > 0: - uuidStmt = ` + case len(profileIDs) > 0: + uuidStmt = ` SELECT DISTINCT h.uuid FROM hosts h JOIN mdm_apple_configuration_profiles macp ON h.team_id = macp.team_id OR (h.team_id IS NULL AND macp.team_id = 0) WHERE macp.profile_id IN (?)` - args = append(args, profileIDs) - } - - if len(uuids) == 0 { - uuidStmt, args, err := sqlx.In(uuidStmt, args...) - if err != nil { - return ctxerr.Wrap(ctx, err, "prepare query to load host UUIDs") + args = append(args, profileIDs) } - if err := sqlx.SelectContext(ctx, ds.writer, &uuids, uuidStmt, args...); err != nil { - return ctxerr.Wrap(ctx, err, "execute query to load host UUIDs") + + if len(uuids) == 0 { + uuidStmt, args, err := sqlx.In(uuidStmt, args...) + if err != nil { + return ctxerr.Wrap(ctx, err, "prepare query to load host UUIDs") + } + if err := sqlx.SelectContext(ctx, tx, &uuids, uuidStmt, args...); err != nil { + return ctxerr.Wrap(ctx, err, "execute query to load host UUIDs") + } } - } - if len(uuids) == 0 { - return nil - } + if len(uuids) == 0 { + return nil + } - const baseStmt = ` -INSERT INTO host_mdm_apple_profiles ( - profile_id, - host_uuid, - profile_identifier, - profile_name, - operation_type, - status, - command_uuid -) - -- NOTE: from https://dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html - -- In an ON DUPLICATE KEY UPDATE clause: - -- References to columns from a UNION do not work reliably. To work - -- around this restriction, rewrite the UNION as a derived table so that its - -- rows can be treated as a single-table result set. - SELECT * FROM ( - -- profiles to install, i.e. those part of the host's team/no team. - -- Except for the SELECT list, filtering on specific target IDs - -- (host/team/profile) and ignoring hmap rows that are already - -- "install" and NULL status, this is the same as - -- ListMDMAppleProfilesToInstall. + const profilesToInstallStmt = ` SELECT ds.profile_id as profile_id, ds.host_uuid as host_uuid, ds.profile_identifier as profile_identifier, ds.profile_name as profile_name, - ? as operation_type, - NULL as status, - '' as command_uuid + ds.checksum as checksum FROM ( SELECT macp.profile_id, h.uuid as host_uuid, macp.identifier as profile_identifier, - macp.name as profile_name + macp.name as profile_name, + macp.checksum as checksum FROM mdm_apple_configuration_profiles macp JOIN hosts h ON h.team_id = macp.team_id OR (h.team_id IS NULL AND macp.team_id = 0) JOIN nano_enrollments ne ON ne.device_id = h.uuid @@ -1094,25 +1078,42 @@ INSERT INTO host_mdm_apple_profiles ( LEFT JOIN host_mdm_apple_profiles hmap ON hmap.profile_id = ds.profile_id AND hmap.host_uuid = ds.host_uuid WHERE + -- profile has been updated + ( hmap.checksum != ds.checksum ) OR -- profiles in A but not in B ( hmap.profile_id IS NULL AND hmap.host_uuid IS NULL ) OR -- profiles in A and B but with operation type "remove" - ( hmap.host_uuid IS NOT NULL AND ( hmap.operation_type = ? OR hmap.operation_type IS NULL ) ) + ( hmap.host_uuid IS NOT NULL AND ( hmap.operation_type = ? OR hmap.operation_type IS NULL ) )` + + stmt, args, err := sqlx.In(profilesToInstallStmt, + uuids, fleet.MDMAppleOperationTypeRemove, + ) + if err != nil { + return ctxerr.Wrap(ctx, err, "building profiles to install statement") + } - UNION + var profilesToInstall []*fleet.MDMAppleProfilePayload + err = sqlx.SelectContext(ctx, tx, &profilesToInstall, stmt, args...) + if err != nil { + return ctxerr.Wrap(ctx, err, "bulk set pending profile status execute") + } + installIdentifiers := []string{} + identifierToHosts := map[string][]string{} + for _, p := range profilesToInstall { + installIdentifiers = append(installIdentifiers, p.ProfileIdentifier) + if _, ok := identifierToHosts[p.ProfileIdentifier]; !ok { + identifierToHosts[p.ProfileIdentifier] = []string{} + } + identifierToHosts[p.ProfileIdentifier] = append(identifierToHosts[p.ProfileIdentifier], p.HostUUID) + } - -- profiles to remove, i.e. those not part of the host's team/no team. - -- Except for the SELECT list, filtering on specific target IDs and ignoring - -- hmap rows that are already "remove" in any status, this is the same - -- as ListMDMAppleProfilesToRemove. + profilesToRemoveStmt := ` SELECT hmap.profile_id as profile_id, hmap.host_uuid as host_uuid, hmap.profile_identifier as profile_identifier, hmap.profile_name as profile_name, - ? as operation_type, - NULL as status, - '' as command_uuid + hmap.checksum as checksum FROM ( SELECT h.uuid, macp.profile_id @@ -1129,26 +1130,83 @@ INSERT INTO host_mdm_apple_profiles ( AND ds.profile_id IS NULL AND ds.uuid IS NULL -- except "remove" operations in any state AND ( hmap.operation_type IS NULL OR hmap.operation_type != ? ) - ) AS dt + -- profiles that are being installed + ` + + inArgs := []any{uuids, uuids, fleet.MDMAppleOperationTypeRemove} + if len(installIdentifiers) > 0 { + profilesToRemoveStmt += `AND hmap.profile_identifier NOT IN (?)` + inArgs = append(inArgs, installIdentifiers) + + } + + stmt, args, err = sqlx.In(profilesToRemoveStmt, inArgs...) + if err != nil { + return ctxerr.Wrap(ctx, err, "building profiles to remove statement") + } + var profilesToRemove []*fleet.MDMAppleProfilePayload + err = sqlx.SelectContext(ctx, tx, &profilesToRemove, stmt, args...) + if err != nil { + return ctxerr.Wrap(ctx, err, "bulk set pending profile status execute") + } + if len(profilesToInstall) == 0 && len(profilesToRemove) == 0 { + return nil + } + + // before doing the inserts, remove profiles with identifiers that will be re-sent + if len(profilesToInstall) > 0 { + var dargs []any + var dsb strings.Builder + for identifier, hostUUIDs := range identifierToHosts { + for _, hostUUID := range hostUUIDs { + dargs = append(dargs, hostUUID, identifier) + dsb.WriteString("(?,?),") + } + } + stmt = fmt.Sprintf(`DELETE FROM host_mdm_apple_profiles WHERE (host_uuid, profile_identifier) IN(%s)`, strings.TrimSuffix(dsb.String(), ",")) + _, err = tx.ExecContext(ctx, stmt, dargs...) + if err != nil { + return ctxerr.Wrap(ctx, err, "bulk set pending profile status execute") + } + } + + var pargs []any + var psb strings.Builder + for _, p := range profilesToInstall { + pargs = append(pargs, p.ProfileID, p.HostUUID, p.ProfileIdentifier, p.ProfileName, p.Checksum, fleet.MDMAppleOperationTypeInstall, nil, "") + psb.WriteString("(?, ?, ?, ?, ?, ?, ?, ?),") + + } + for _, p := range profilesToRemove { + pargs = append(pargs, p.ProfileID, p.HostUUID, p.ProfileIdentifier, p.ProfileName, p.Checksum, fleet.MDMAppleOperationTypeRemove, nil, "") + psb.WriteString("(?, ?, ?, ?, ?, ?, ?, ?),") + + } + + baseStmt := fmt.Sprintf(` +INSERT INTO host_mdm_apple_profiles ( + profile_id, + host_uuid, + profile_identifier, + profile_name, + checksum, + operation_type, + status, + command_uuid +) +VALUES %s ON DUPLICATE KEY UPDATE operation_type = VALUES(operation_type), status = VALUES(status), command_uuid = VALUES(command_uuid), + checksum = VALUES(checksum), detail = '' -` +`, strings.TrimSuffix(psb.String(), ",")) - stmt, args, err := sqlx.In(baseStmt, - // to install parameters: - fleet.MDMAppleOperationTypeInstall, uuids, fleet.MDMAppleOperationTypeRemove, - // to remove parameters: - fleet.MDMAppleOperationTypeRemove, uuids, uuids, fleet.MDMAppleOperationTypeRemove, - ) - if err != nil { - return ctxerr.Wrap(ctx, err, "bulk set pending profile status build args") - } - _, err = ds.writer.ExecContext(ctx, stmt, args...) - return ctxerr.Wrap(ctx, err, "bulk set pending profile status execute") + _, err = tx.ExecContext(ctx, baseStmt, pargs...) + return ctxerr.Wrap(ctx, err, "bulk set pending profile status execute") + }) } func (ds *Datastore) ListMDMAppleProfilesToInstall(ctx context.Context) ([]*fleet.MDMAppleProfilePayload, error) { @@ -1162,6 +1220,9 @@ func (ds *Datastore) ListMDMAppleProfilesToInstall(ctx context.Context) ([]*flee // // - profiles that are in A but not in B // + // - profiles which contents have changed, but their identifier are + // the same (by matching checksums) + // // - profiles that are in A and in B, but with an operation type of // "remove", regardless of the status. (technically, if status is NULL then // the profile should be already installed - it has not been queued for @@ -1178,13 +1239,14 @@ func (ds *Datastore) ListMDMAppleProfilesToInstall(ctx context.Context) ([]*flee // profile's content is edited, all relevant hosts will be marked as status // NULL so that it gets re-installed. query := ` - SELECT ds.profile_id, ds.host_uuid, ds.profile_identifier, ds.profile_name + SELECT ds.profile_id, ds.host_uuid, ds.profile_identifier, ds.profile_name, ds.checksum FROM ( SELECT macp.profile_id, h.uuid as host_uuid, macp.identifier as profile_identifier, - macp.name as profile_name + macp.name as profile_name, + macp.checksum as checksum FROM mdm_apple_configuration_profiles macp JOIN hosts h ON h.team_id = macp.team_id OR (h.team_id IS NULL AND macp.team_id = 0) JOIN nano_enrollments ne ON ne.device_id = h.uuid @@ -1193,6 +1255,8 @@ func (ds *Datastore) ListMDMAppleProfilesToInstall(ctx context.Context) ([]*flee LEFT JOIN host_mdm_apple_profiles hmap ON hmap.profile_id = ds.profile_id AND hmap.host_uuid = ds.host_uuid WHERE + -- profile has been updated + ( hmap.checksum != ds.checksum ) OR -- profiles in A but not in B ( hmap.profile_id IS NULL AND hmap.host_uuid IS NULL ) OR -- profiles in A and B but with operation type "remove" @@ -1223,7 +1287,7 @@ func (ds *Datastore) ListMDMAppleProfilesToRemove(ctx context.Context) ([]*fleet // processed by the ListMDMAppleProfilesToInstall method (since they are in // both, their desired state is necessarily to be installed). query := ` - SELECT hmap.profile_id, hmap.profile_identifier, hmap.profile_name, hmap.host_uuid + SELECT hmap.profile_id, hmap.profile_identifier, hmap.profile_name, hmap.host_uuid, hmap.checksum FROM ( SELECT h.uuid, macp.profile_id FROM mdm_apple_configuration_profiles macp @@ -1283,8 +1347,8 @@ func (ds *Datastore) BulkUpsertMDMAppleHostProfiles(ctx context.Context, payload var sb strings.Builder for _, p := range payload { - args = append(args, p.ProfileID, p.ProfileIdentifier, p.ProfileName, p.HostUUID, p.Status, p.OperationType, p.CommandUUID) - sb.WriteString("(?, ?, ?, ?, ?, ?, ?),") + args = append(args, p.ProfileID, p.ProfileIdentifier, p.ProfileName, p.HostUUID, p.Status, p.OperationType, p.CommandUUID, p.Checksum) + sb.WriteString("(?, ?, ?, ?, ?, ?, ?, ?),") } stmt := fmt.Sprintf(` @@ -1295,7 +1359,8 @@ func (ds *Datastore) BulkUpsertMDMAppleHostProfiles(ctx context.Context, payload host_uuid, status, operation_type, - command_uuid + command_uuid, + checksum ) VALUES %s ON DUPLICATE KEY UPDATE @@ -1516,15 +1581,16 @@ func (ds *Datastore) BulkUpsertMDMAppleConfigProfiles(ctx context.Context, paylo } args = append(args, teamID, cp.Identifier, cp.Name, cp.Mobileconfig) - sb.WriteString("(?, ?, ?, ?),") + sb.WriteString("(?, ?, ?, ?, UNHEX(MD5(mobileconfig))),") } stmt := fmt.Sprintf(` INSERT INTO - mdm_apple_configuration_profiles (team_id, identifier, name, mobileconfig) + mdm_apple_configuration_profiles (team_id, identifier, name, mobileconfig, checksum) VALUES %s ON DUPLICATE KEY UPDATE - mobileconfig = VALUES(mobileconfig)`, strings.TrimSuffix(sb.String(), ",")) + mobileconfig = VALUES(mobileconfig), + checksum = UNHEX(MD5(VALUES(mobileconfig)))`, strings.TrimSuffix(sb.String(), ",")) if _, err := ds.writer.ExecContext(ctx, stmt, args...); err != nil { return ctxerr.Wrapf(ctx, err, "upsert mdm config profiles") @@ -1587,3 +1653,71 @@ func (ds *Datastore) GetMDMAppleBootstrapPackageMeta(ctx context.Context, teamID } return &bp, nil } + +func (ds *Datastore) CleanupDiskEncryptionKeysOnTeamChange(ctx context.Context, hostIDs []uint, newTeamID *uint) error { + return ds.withTx(ctx, func(tx sqlx.ExtContext) error { + return cleanupDiskEncryptionKeysOnTeamChangeDB(ctx, tx, hostIDs, newTeamID) + }) +} + +func cleanupDiskEncryptionKeysOnTeamChangeDB(ctx context.Context, tx sqlx.ExtContext, hostIDs []uint, newTeamID *uint) error { + _, err := getMDMAppleConfigProfileByTeamAndIdentifierDB(ctx, tx, newTeamID, mobileconfig.FleetFileVaultPayloadIdentifier) + if err != nil { + if fleet.IsNotFound(err) { + // the new team does not have a filevault profile so we need to delete the existing ones + if err := bulkDeleteHostDiskEncryptionKeysDB(ctx, tx, hostIDs); err != nil { + return ctxerr.Wrap(ctx, err, "reconcile filevault profiles on team change bulk delete host disk encryption keys") + } + } else { + return ctxerr.Wrap(ctx, err, "reconcile filevault profiles on team change get profile") + } + } + return nil +} + +func getMDMAppleConfigProfileByTeamAndIdentifierDB(ctx context.Context, tx sqlx.QueryerContext, teamID *uint, profileIdentifier string) (*fleet.MDMAppleConfigProfile, error) { + if teamID == nil { + teamID = ptr.Uint(0) + } + + stmt := ` +SELECT + profile_id, + team_id, + name, + identifier, + mobileconfig, + created_at, + updated_at +FROM + mdm_apple_configuration_profiles +WHERE + team_id=? AND identifier=?` + + var profile fleet.MDMAppleConfigProfile + err := sqlx.GetContext(ctx, tx, &profile, stmt, teamID, profileIdentifier) + if err != nil { + if err == sql.ErrNoRows { + return &fleet.MDMAppleConfigProfile{}, ctxerr.Wrap(ctx, notFound("MDMAppleConfigProfile").WithName(profileIdentifier)) + } + return &fleet.MDMAppleConfigProfile{}, ctxerr.Wrap(ctx, err, "get mdm apple config profile by team and identifier") + } + return &profile, nil +} + +func bulkDeleteHostDiskEncryptionKeysDB(ctx context.Context, tx sqlx.ExtContext, hostIDs []uint) error { + if len(hostIDs) == 0 { + return nil + } + + query, args, err := sqlx.In( + "DELETE FROM host_disk_encryption_keys WHERE host_id IN (?)", + hostIDs, + ) + if err != nil { + return ctxerr.Wrap(ctx, err, "building query") + } + + _, err = tx.ExecContext(ctx, query, args...) + return err +} diff --git a/server/datastore/mysql/apple_mdm_test.go b/server/datastore/mysql/apple_mdm_test.go index 662a3fb89a3..ee4c9a9155c 100644 --- a/server/datastore/mysql/apple_mdm_test.go +++ b/server/datastore/mysql/apple_mdm_test.go @@ -2,6 +2,7 @@ package mysql import ( "context" + "crypto/md5" // nolint:gosec // used only to hash for efficient comparisons "crypto/sha256" "database/sql" "fmt" @@ -740,6 +741,7 @@ func testUpdateHostTablesOnMDMUnenroll(t *testing.T, ds *Datastore) { Status: &fleet.MDMAppleDeliveryApplied, OperationType: fleet.MDMAppleOperationTypeInstall, CommandUUID: "command-uuid", + Checksum: []byte("csum"), }, }, ) @@ -854,7 +856,7 @@ func testBatchSetMDMAppleProfiles(t *testing.T, ds *Datastore) { }, nil, []*fleet.MDMAppleConfigProfile{ configProfileForTest(t, "N2", "I1", "b"), }) - require.NotEqual(t, mNoTm["I1"], mNoTmb["I1"]) + require.Equal(t, mNoTm["I1"], mNoTmb["I1"]) // apply edited profile (by content only), unchanged profile and new profile // for tm1 @@ -867,8 +869,8 @@ func testBatchSetMDMAppleProfiles(t *testing.T, ds *Datastore) { withTeamID(configProfileForTest(t, "N2", "I2", "b"), 1), withTeamID(configProfileForTest(t, "N3", "I3", "c"), 1), }) - // identifier for N1-I1 is changed - require.NotEqual(t, mTm1b["I1"], mTm1c["I1"]) + // identifier for N1-I1 is unchanged + require.Equal(t, mTm1b["I1"], mTm1c["I1"]) // identifier for N2-I2 is unchanged require.Equal(t, mTm1b["I2"], mTm1c["I2"]) @@ -937,12 +939,24 @@ func configProfileForTest(t *testing.T, name, identifier, uuid string) *fleet.MD `, name, identifier, uuid)) cp, err := fleet.NewMDMAppleConfigProfile(prof, nil) + sum := md5.Sum(prof) // nolint:gosec // used only to hash for efficient comparisons + cp.Checksum = sum[:] require.NoError(t, err) return cp } func testMDMAppleProfileManagement(t *testing.T, ds *Datastore) { ctx := context.Background() + + matchProfiles := func(want, got []*fleet.MDMAppleProfilePayload) { + // match only the fields we care about + for _, p := range got { + require.NotEmpty(t, p.Checksum) + p.Checksum = nil + } + require.ElementsMatch(t, want, got) + } + globalProfiles := []*fleet.MDMAppleConfigProfile{ configProfileForTest(t, "N1", "I1", "z"), configProfileForTest(t, "N2", "I2", "b"), @@ -1004,7 +1018,7 @@ func testMDMAppleProfileManagement(t *testing.T, ds *Datastore) { // global profiles to install on the newly added host profiles, err = ds.ListMDMAppleProfilesToInstall(ctx) require.NoError(t, err) - require.ElementsMatch(t, []*fleet.MDMAppleProfilePayload{ + matchProfiles([]*fleet.MDMAppleProfilePayload{ {ProfileID: globalPfs[0].ProfileID, ProfileIdentifier: globalPfs[0].Identifier, ProfileName: globalPfs[0].Name, HostUUID: "test-uuid-1"}, {ProfileID: globalPfs[1].ProfileID, ProfileIdentifier: globalPfs[1].Identifier, ProfileName: globalPfs[1].Name, HostUUID: "test-uuid-1"}, {ProfileID: globalPfs[2].ProfileID, ProfileIdentifier: globalPfs[2].Identifier, ProfileName: globalPfs[2].Name, HostUUID: "test-uuid-1"}, @@ -1027,17 +1041,18 @@ func testMDMAppleProfileManagement(t *testing.T, ds *Datastore) { // still the same profiles to assign as there are no profiles for team 1 profiles, err = ds.ListMDMAppleProfilesToInstall(ctx) require.NoError(t, err) - require.ElementsMatch(t, []*fleet.MDMAppleProfilePayload{ + matchProfiles([]*fleet.MDMAppleProfilePayload{ {ProfileID: globalPfs[0].ProfileID, ProfileIdentifier: globalPfs[0].Identifier, ProfileName: globalPfs[0].Name, HostUUID: "test-uuid-1"}, {ProfileID: globalPfs[1].ProfileID, ProfileIdentifier: globalPfs[1].Identifier, ProfileName: globalPfs[1].Name, HostUUID: "test-uuid-1"}, {ProfileID: globalPfs[2].ProfileID, ProfileIdentifier: globalPfs[2].Identifier, ProfileName: globalPfs[2].Name, HostUUID: "test-uuid-1"}, }, profiles) // assign profiles to team 1 - err = ds.BatchSetMDMAppleProfiles(ctx, &team.ID, []*fleet.MDMAppleConfigProfile{ + teamProfiles := []*fleet.MDMAppleConfigProfile{ configProfileForTest(t, "N4", "I4", "x"), configProfileForTest(t, "N5", "I5", "y"), - }) + } + err = ds.BatchSetMDMAppleProfiles(ctx, &team.ID, teamProfiles) require.NoError(t, err) globalPfs, err = ds.ListMDMAppleConfigProfiles(ctx, ptr.Uint(0)) @@ -1050,7 +1065,7 @@ func testMDMAppleProfileManagement(t *testing.T, ds *Datastore) { // new profiles, this time for the new host belonging to team 1 profiles, err = ds.ListMDMAppleProfilesToInstall(ctx) require.NoError(t, err) - require.ElementsMatch(t, []*fleet.MDMAppleProfilePayload{ + matchProfiles([]*fleet.MDMAppleProfilePayload{ {ProfileID: globalPfs[0].ProfileID, ProfileIdentifier: globalPfs[0].Identifier, ProfileName: globalPfs[0].Name, HostUUID: "test-uuid-1"}, {ProfileID: globalPfs[1].ProfileID, ProfileIdentifier: globalPfs[1].Identifier, ProfileName: globalPfs[1].Name, HostUUID: "test-uuid-1"}, {ProfileID: globalPfs[2].ProfileID, ProfileIdentifier: globalPfs[2].Identifier, ProfileName: globalPfs[2].Name, HostUUID: "test-uuid-1"}, @@ -1073,7 +1088,7 @@ func testMDMAppleProfileManagement(t *testing.T, ds *Datastore) { // more profiles, this time for both global hosts and the team profiles, err = ds.ListMDMAppleProfilesToInstall(ctx) require.NoError(t, err) - require.ElementsMatch(t, []*fleet.MDMAppleProfilePayload{ + matchProfiles([]*fleet.MDMAppleProfilePayload{ {ProfileID: globalPfs[0].ProfileID, ProfileIdentifier: globalPfs[0].Identifier, ProfileName: globalPfs[0].Name, HostUUID: "test-uuid-1"}, {ProfileID: globalPfs[1].ProfileID, ProfileIdentifier: globalPfs[1].Identifier, ProfileName: globalPfs[1].Name, HostUUID: "test-uuid-1"}, {ProfileID: globalPfs[2].ProfileID, ProfileIdentifier: globalPfs[2].Identifier, ProfileName: globalPfs[2].Name, HostUUID: "test-uuid-1"}, @@ -1091,6 +1106,7 @@ func testMDMAppleProfileManagement(t *testing.T, ds *Datastore) { ProfileID: globalPfs[0].ProfileID, ProfileIdentifier: globalPfs[0].Identifier, ProfileName: globalPfs[0].Name, + Checksum: globalProfiles[0].Checksum, HostUUID: "test-uuid-1", Status: &fleet.MDMAppleDeliveryApplied, OperationType: fleet.MDMAppleOperationTypeInstall, @@ -1100,6 +1116,7 @@ func testMDMAppleProfileManagement(t *testing.T, ds *Datastore) { ProfileID: globalPfs[0].ProfileID, ProfileIdentifier: globalPfs[0].Identifier, ProfileName: globalPfs[0].Name, + Checksum: globalProfiles[0].Checksum, HostUUID: "test-uuid-3", Status: &fleet.MDMAppleDeliveryApplied, OperationType: fleet.MDMAppleOperationTypeInstall, @@ -1109,6 +1126,7 @@ func testMDMAppleProfileManagement(t *testing.T, ds *Datastore) { ProfileID: globalPfs[1].ProfileID, ProfileIdentifier: globalPfs[1].Identifier, ProfileName: globalPfs[1].Name, + Checksum: globalProfiles[1].Checksum, HostUUID: "test-uuid-1", Status: &fleet.MDMAppleDeliveryApplied, OperationType: fleet.MDMAppleOperationTypeInstall, @@ -1118,6 +1136,7 @@ func testMDMAppleProfileManagement(t *testing.T, ds *Datastore) { ProfileID: globalPfs[1].ProfileID, ProfileIdentifier: globalPfs[1].Identifier, ProfileName: globalPfs[1].Name, + Checksum: globalProfiles[1].Checksum, HostUUID: "test-uuid-3", Status: &fleet.MDMAppleDeliveryApplied, OperationType: fleet.MDMAppleOperationTypeInstall, @@ -1127,6 +1146,7 @@ func testMDMAppleProfileManagement(t *testing.T, ds *Datastore) { ProfileID: globalPfs[2].ProfileID, ProfileIdentifier: globalPfs[2].Identifier, ProfileName: globalPfs[2].Name, + Checksum: globalProfiles[2].Checksum, HostUUID: "test-uuid-1", Status: &fleet.MDMAppleDeliveryApplied, OperationType: fleet.MDMAppleOperationTypeInstall, @@ -1136,6 +1156,7 @@ func testMDMAppleProfileManagement(t *testing.T, ds *Datastore) { ProfileID: globalPfs[2].ProfileID, ProfileIdentifier: globalPfs[2].Identifier, ProfileName: globalPfs[2].Name, + Checksum: globalProfiles[2].Checksum, HostUUID: "test-uuid-3", Status: &fleet.MDMAppleDeliveryApplied, OperationType: fleet.MDMAppleOperationTypeInstall, @@ -1145,6 +1166,7 @@ func testMDMAppleProfileManagement(t *testing.T, ds *Datastore) { ProfileID: teamPfs[0].ProfileID, ProfileIdentifier: teamPfs[0].Identifier, ProfileName: teamPfs[0].Name, + Checksum: teamProfiles[0].Checksum, HostUUID: "test-uuid-2", Status: &fleet.MDMAppleDeliveryApplied, OperationType: fleet.MDMAppleOperationTypeInstall, @@ -1154,6 +1176,7 @@ func testMDMAppleProfileManagement(t *testing.T, ds *Datastore) { ProfileID: teamPfs[1].ProfileID, ProfileIdentifier: teamPfs[1].Identifier, ProfileName: teamPfs[1].Name, + Checksum: teamProfiles[1].Checksum, HostUUID: "test-uuid-2", Status: &fleet.MDMAppleDeliveryApplied, OperationType: fleet.MDMAppleOperationTypeInstall, @@ -1180,7 +1203,7 @@ func testMDMAppleProfileManagement(t *testing.T, ds *Datastore) { // profiles to be added for host1 are now related to the team profiles, err = ds.ListMDMAppleProfilesToInstall(ctx) require.NoError(t, err) - require.ElementsMatch(t, []*fleet.MDMAppleProfilePayload{ + matchProfiles([]*fleet.MDMAppleProfilePayload{ {ProfileID: teamPfs[0].ProfileID, ProfileIdentifier: teamPfs[0].Identifier, ProfileName: teamPfs[0].Name, HostUUID: "test-uuid-1"}, {ProfileID: teamPfs[1].ProfileID, ProfileIdentifier: teamPfs[1].Identifier, ProfileName: teamPfs[1].Name, HostUUID: "test-uuid-1"}, }, profiles) @@ -1188,7 +1211,7 @@ func testMDMAppleProfileManagement(t *testing.T, ds *Datastore) { // profiles to be removed includes host1's old profiles toRemove, err = ds.ListMDMAppleProfilesToRemove(ctx) require.NoError(t, err) - require.ElementsMatch(t, []*fleet.MDMAppleProfilePayload{ + matchProfiles([]*fleet.MDMAppleProfilePayload{ {ProfileID: globalPfs[0].ProfileID, ProfileIdentifier: globalPfs[0].Identifier, ProfileName: globalPfs[0].Name, HostUUID: "test-uuid-1"}, {ProfileID: globalPfs[1].ProfileID, ProfileIdentifier: globalPfs[1].Identifier, ProfileName: globalPfs[1].Name, HostUUID: "test-uuid-1"}, {ProfileID: globalPfs[2].ProfileID, ProfileIdentifier: globalPfs[2].Identifier, ProfileName: globalPfs[2].Name, HostUUID: "test-uuid-1"}, @@ -1350,6 +1373,7 @@ func upsertHostCPs( CommandUUID: "", OperationType: opType, Status: status, + Checksum: []byte("csum"), } upserts = append(upserts, &payload) } @@ -1649,6 +1673,7 @@ func testIgnoreMDMClientError(t *testing.T, ds *Datastore) { CommandUUID: "c1", OperationType: fleet.MDMAppleOperationTypeRemove, Status: &fleet.MDMAppleDeliveryPending, + Checksum: []byte("csum"), }})) cps, err := ds.GetHostMDMProfiles(ctx, "h1") require.NoError(t, err) @@ -1679,6 +1704,7 @@ func testIgnoreMDMClientError(t *testing.T, ds *Datastore) { CommandUUID: "c2", OperationType: fleet.MDMAppleOperationTypeRemove, Status: &fleet.MDMAppleDeliveryPending, + Checksum: []byte("csum"), }})) cps, err = ds.GetHostMDMProfiles(ctx, "h2") require.NoError(t, err) @@ -1728,6 +1754,7 @@ func testDeleteMDMAppleProfilesForHost(t *testing.T, ds *Datastore) { CommandUUID: "c1", OperationType: fleet.MDMAppleOperationTypeRemove, Status: &fleet.MDMAppleDeliveryPending, + Checksum: []byte("csum"), }})) gotProfs, err := ds.GetHostMDMProfiles(ctx, h.UUID) @@ -2026,19 +2053,19 @@ func testBulkSetPendingMDMAppleHostProfiles(t *testing.T, ds *Datastore) { require.NoError(t, err) assertHostProfiles(map[*fleet.Host][]fleet.HostMDMAppleProfile{ enrolledHosts[0]: { - {ProfileID: globalProfiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: globalProfiles[1].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: globalProfiles[2].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, }, enrolledHosts[1]: { - {ProfileID: globalProfiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: globalProfiles[1].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: globalProfiles[2].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, }, enrolledHosts[2]: { - {ProfileID: globalProfiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: globalProfiles[1].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: globalProfiles[2].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, }, unenrolledHost: {}, linuxHost: {}, @@ -2067,19 +2094,19 @@ func testBulkSetPendingMDMAppleHostProfiles(t *testing.T, ds *Datastore) { require.NoError(t, err) assertHostProfiles(map[*fleet.Host][]fleet.HostMDMAppleProfile{ enrolledHosts[0]: { - {ProfileID: globalProfiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: globalProfiles[1].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: globalProfiles[2].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, }, enrolledHosts[1]: { - {ProfileID: globalProfiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: globalProfiles[1].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: globalProfiles[2].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, }, enrolledHosts[2]: { - {ProfileID: globalProfiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: globalProfiles[1].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: globalProfiles[2].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, }, unenrolledHost: {}, linuxHost: {}, @@ -2108,19 +2135,19 @@ func testBulkSetPendingMDMAppleHostProfiles(t *testing.T, ds *Datastore) { require.NoError(t, err) assertHostProfiles(map[*fleet.Host][]fleet.HostMDMAppleProfile{ enrolledHosts[0]: { - {ProfileID: globalProfiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: globalProfiles[1].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: globalProfiles[2].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, }, enrolledHosts[1]: { - {ProfileID: globalProfiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: globalProfiles[1].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: globalProfiles[2].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, }, enrolledHosts[2]: { - {ProfileID: globalProfiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: globalProfiles[1].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: globalProfiles[2].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, }, unenrolledHost: {}, linuxHost: {}, @@ -2152,21 +2179,21 @@ func testBulkSetPendingMDMAppleHostProfiles(t *testing.T, ds *Datastore) { require.NoError(t, err) assertHostProfiles(map[*fleet.Host][]fleet.HostMDMAppleProfile{ enrolledHosts[0]: { - {ProfileID: globalProfiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: globalProfiles[1].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: globalProfiles[2].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: tm1Profiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: tm1Profiles[1].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: tm1Profiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: tm1Profiles[1].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, }, enrolledHosts[1]: { - {ProfileID: globalProfiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: globalProfiles[1].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: globalProfiles[2].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, }, enrolledHosts[2]: { - {ProfileID: globalProfiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: globalProfiles[1].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: globalProfiles[2].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, }, unenrolledHost: {}, linuxHost: {}, @@ -2178,11 +2205,11 @@ func testBulkSetPendingMDMAppleHostProfiles(t *testing.T, ds *Datastore) { // all rows in this test since we don't have command uuids. err = ds.BulkUpsertMDMAppleHostProfiles(ctx, []*fleet.MDMAppleBulkUpsertHostProfilePayload{ {HostUUID: enrolledHosts[0].UUID, ProfileID: globalProfiles[0].ProfileID, - Status: &fleet.MDMAppleDeliveryApplied, OperationType: fleet.MDMAppleOperationTypeRemove}, + Status: &fleet.MDMAppleDeliveryApplied, OperationType: fleet.MDMAppleOperationTypeRemove, Checksum: []byte("csum")}, {HostUUID: enrolledHosts[0].UUID, ProfileID: globalProfiles[1].ProfileID, - Status: &fleet.MDMAppleDeliveryApplied, OperationType: fleet.MDMAppleOperationTypeRemove}, + Status: &fleet.MDMAppleDeliveryApplied, OperationType: fleet.MDMAppleOperationTypeRemove, Checksum: []byte("csum")}, {HostUUID: enrolledHosts[0].UUID, ProfileID: globalProfiles[2].ProfileID, - Status: &fleet.MDMAppleDeliveryFailed, OperationType: fleet.MDMAppleOperationTypeRemove}, + Status: &fleet.MDMAppleDeliveryFailed, OperationType: fleet.MDMAppleOperationTypeRemove, Checksum: []byte("csum")}, }) require.NoError(t, err) @@ -2203,19 +2230,19 @@ func testBulkSetPendingMDMAppleHostProfiles(t *testing.T, ds *Datastore) { assertHostProfiles(map[*fleet.Host][]fleet.HostMDMAppleProfile{ enrolledHosts[0]: { {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryFailed, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: tm1Profiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: newTm1Profiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: newTm1Profiles[1].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: tm1Profiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: newTm1Profiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: newTm1Profiles[1].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, }, enrolledHosts[1]: { - {ProfileID: globalProfiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: globalProfiles[1].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: globalProfiles[2].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, }, enrolledHosts[2]: { - {ProfileID: globalProfiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: globalProfiles[1].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: globalProfiles[2].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, }, unenrolledHost: {}, linuxHost: {}, @@ -2242,20 +2269,19 @@ func testBulkSetPendingMDMAppleHostProfiles(t *testing.T, ds *Datastore) { assertHostProfiles(map[*fleet.Host][]fleet.HostMDMAppleProfile{ enrolledHosts[0]: { {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryFailed, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: tm1Profiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: newTm1Profiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: newTm1Profiles[1].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: newTm1Profiles[2].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: newTm1Profiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: newTm1Profiles[1].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: newTm1Profiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, }, enrolledHosts[1]: { - {ProfileID: globalProfiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: globalProfiles[1].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: globalProfiles[2].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, }, enrolledHosts[2]: { - {ProfileID: globalProfiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: globalProfiles[1].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: globalProfiles[2].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, }, unenrolledHost: {}, linuxHost: {}, @@ -2279,21 +2305,20 @@ func testBulkSetPendingMDMAppleHostProfiles(t *testing.T, ds *Datastore) { assertHostProfiles(map[*fleet.Host][]fleet.HostMDMAppleProfile{ enrolledHosts[0]: { {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryFailed, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: tm1Profiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: newTm1Profiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: newTm1Profiles[1].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: newTm1Profiles[2].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: newTm1Profiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: newTm1Profiles[1].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: newTm1Profiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, }, enrolledHosts[1]: { - {ProfileID: globalProfiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: globalProfiles[1].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: globalProfiles[2].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, }, enrolledHosts[2]: { - {ProfileID: globalProfiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: newGlobalProfiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: newGlobalProfiles[1].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: newGlobalProfiles[2].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: newGlobalProfiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: newGlobalProfiles[1].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: newGlobalProfiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, }, unenrolledHost: {}, linuxHost: {}, @@ -2318,22 +2343,21 @@ func testBulkSetPendingMDMAppleHostProfiles(t *testing.T, ds *Datastore) { assertHostProfiles(map[*fleet.Host][]fleet.HostMDMAppleProfile{ enrolledHosts[0]: { {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryFailed, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: tm1Profiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: newTm1Profiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: newTm1Profiles[1].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: newTm1Profiles[2].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: newTm1Profiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: newTm1Profiles[1].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: newTm1Profiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, }, enrolledHosts[1]: { - {ProfileID: globalProfiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: globalProfiles[1].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: globalProfiles[2].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, }, enrolledHosts[2]: { - {ProfileID: globalProfiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: newGlobalProfiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: newGlobalProfiles[1].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: newGlobalProfiles[2].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: newGlobalProfiles[3].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: newGlobalProfiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: newGlobalProfiles[1].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: newGlobalProfiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: newGlobalProfiles[3].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, }, unenrolledHost: {}, linuxHost: {}, @@ -2355,23 +2379,22 @@ func testBulkSetPendingMDMAppleHostProfiles(t *testing.T, ds *Datastore) { assertHostProfiles(map[*fleet.Host][]fleet.HostMDMAppleProfile{ enrolledHosts[0]: { {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryFailed, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: tm1Profiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: newTm1Profiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: newTm1Profiles[1].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: newTm1Profiles[2].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: newTm1Profiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: newTm1Profiles[1].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: newTm1Profiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, }, enrolledHosts[1]: { - {ProfileID: globalProfiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: globalProfiles[1].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: globalProfiles[2].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: tm2Profiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: tm2Profiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, }, enrolledHosts[2]: { - {ProfileID: globalProfiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: newGlobalProfiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: newGlobalProfiles[1].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: newGlobalProfiles[2].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: newGlobalProfiles[3].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: newGlobalProfiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: newGlobalProfiles[1].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: newGlobalProfiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: newGlobalProfiles[3].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, }, unenrolledHost: {}, linuxHost: {}, @@ -2383,23 +2406,22 @@ func testBulkSetPendingMDMAppleHostProfiles(t *testing.T, ds *Datastore) { assertHostProfiles(map[*fleet.Host][]fleet.HostMDMAppleProfile{ enrolledHosts[0]: { {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryFailed, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: tm1Profiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: newTm1Profiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: newTm1Profiles[1].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: newTm1Profiles[2].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: newTm1Profiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: newTm1Profiles[1].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: newTm1Profiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, }, enrolledHosts[1]: { - {ProfileID: globalProfiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: globalProfiles[1].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: globalProfiles[2].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: tm2Profiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: tm2Profiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, }, enrolledHosts[2]: { - {ProfileID: globalProfiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeRemove}, - {ProfileID: newGlobalProfiles[0].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: newGlobalProfiles[1].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: newGlobalProfiles[2].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, - {ProfileID: newGlobalProfiles[3].ProfileID, Status: nil, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeRemove}, + {ProfileID: newGlobalProfiles[0].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: newGlobalProfiles[1].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: newGlobalProfiles[2].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: newGlobalProfiles[3].ProfileID, Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall}, }, unenrolledHost: {}, linuxHost: {}, diff --git a/server/datastore/mysql/hosts.go b/server/datastore/mysql/hosts.go index 66600b31cf7..d4d0629907f 100644 --- a/server/datastore/mysql/hosts.go +++ b/server/datastore/mysql/hosts.go @@ -1945,6 +1945,10 @@ func (ds *Datastore) AddHostsToTeam(ctx context.Context, teamID *uint, hostIDs [ return ctxerr.Wrap(ctx, err, "exec AddHostsToTeam") } + if err := cleanupDiskEncryptionKeysOnTeamChangeDB(ctx, tx, hostIDs, teamID); err != nil { + return ctxerr.Wrap(ctx, err, "AddHostsToTeam cleanup disk encryption keys") + } + return nil }) } diff --git a/server/datastore/mysql/hosts_test.go b/server/datastore/mysql/hosts_test.go index 019b3d4d557..f54208d8792 100644 --- a/server/datastore/mysql/hosts_test.go +++ b/server/datastore/mysql/hosts_test.go @@ -712,6 +712,7 @@ func testHostListOptionsTeamFilter(t *testing.T, ds *Datastore) { CommandUUID: "command-uuid-1", OperationType: fleet.MDMAppleOperationTypeInstall, Status: &fleet.MDMAppleDeliveryApplied, + Checksum: []byte("csum"), }, })) listHostsCheckCount(t, ds, userFilter, fleet.HostListOptions{TeamFilter: &team1.ID, MacOSSettingsFilter: fleet.MacOSSettingsStatusLatest}, 1) // hosts[0] @@ -729,6 +730,7 @@ func testHostListOptionsTeamFilter(t *testing.T, ds *Datastore) { CommandUUID: "command-uuid-2", OperationType: fleet.MDMAppleOperationTypeInstall, Status: &fleet.MDMAppleDeliveryApplied, + Checksum: []byte("csum"), }, })) listHostsCheckCount(t, ds, userFilter, fleet.HostListOptions{TeamFilter: &team1.ID, MacOSSettingsFilter: fleet.MacOSSettingsStatusLatest}, 1) // hosts[0] @@ -5523,7 +5525,7 @@ func testHostsDeleteHosts(t *testing.T, ds *Datastore) { prof, err := ds.NewMDMAppleConfigProfile(context.Background(), *configProfileForTest(t, "N1", "I1", "U1")) require.NoError(t, err) err = ds.BulkUpsertMDMAppleHostProfiles(context.Background(), []*fleet.MDMAppleBulkUpsertHostProfilePayload{ - {ProfileID: prof.ProfileID, ProfileIdentifier: prof.Identifier, ProfileName: prof.Name, HostUUID: host.UUID, OperationType: fleet.MDMAppleOperationTypeInstall}, + {ProfileID: prof.ProfileID, ProfileIdentifier: prof.Identifier, ProfileName: prof.Name, HostUUID: host.UUID, OperationType: fleet.MDMAppleOperationTypeInstall, Checksum: []byte("csum")}, }) require.NoError(t, err) diff --git a/server/datastore/mysql/labels_test.go b/server/datastore/mysql/labels_test.go index b134d09c791..00b33f96083 100644 --- a/server/datastore/mysql/labels_test.go +++ b/server/datastore/mysql/labels_test.go @@ -485,6 +485,7 @@ func testLabelsListHostsInLabelAndTeamFilter(deferred bool, t *testing.T, db *Da CommandUUID: "command-uuid-1", OperationType: fleet.MDMAppleOperationTypeInstall, Status: &fleet.MDMAppleDeliveryApplied, + Checksum: []byte("csum"), }, })) listHostsInLabelCheckCount(t, db, userFilter, l1.ID, fleet.HostListOptions{TeamFilter: &team1.ID, MacOSSettingsFilter: fleet.MacOSSettingsStatusLatest}, 1) // h1 @@ -502,6 +503,7 @@ func testLabelsListHostsInLabelAndTeamFilter(deferred bool, t *testing.T, db *Da CommandUUID: "command-uuid-2", OperationType: fleet.MDMAppleOperationTypeInstall, Status: &fleet.MDMAppleDeliveryApplied, + Checksum: []byte("csum"), }, })) listHostsInLabelCheckCount(t, db, userFilter, l1.ID, fleet.HostListOptions{TeamFilter: &team1.ID, MacOSSettingsFilter: fleet.MacOSSettingsStatusLatest}, 1) // h1 diff --git a/server/datastore/mysql/migrations/tables/20230408084104_AddChecksumToProfiles.go b/server/datastore/mysql/migrations/tables/20230408084104_AddChecksumToProfiles.go new file mode 100644 index 00000000000..1096ec485e6 --- /dev/null +++ b/server/datastore/mysql/migrations/tables/20230408084104_AddChecksumToProfiles.go @@ -0,0 +1,24 @@ +package tables + +import ( + "database/sql" + + "github.com/pkg/errors" +) + +func init() { + MigrationClient.AddMigration(Up_20230408084104, Down_20230408084104) +} + +func Up_20230408084104(tx *sql.Tx) error { + _, err := tx.Exec( + `ALTER TABLE mdm_apple_configuration_profiles ADD COLUMN checksum BINARY(16) NOT NULL; + ALTER TABLE host_mdm_apple_profiles ADD COLUMN checksum BINARY(16) NOT NULL; + UPDATE mdm_apple_configuration_profiles SET checksum = UNHEX(MD5(mobileconfig)); + UPDATE host_mdm_apple_profiles hmap SET checksum = (SELECT checksum FROM mdm_apple_configuration_profiles macp WHERE macp.profile_id = hmap.profile_id);`) + return errors.Wrap(err, "add checksum column") +} + +func Down_20230408084104(tx *sql.Tx) error { + return nil +} diff --git a/server/datastore/mysql/migrations/tables/20230408084104_AddChecksumToProfiles_test.go b/server/datastore/mysql/migrations/tables/20230408084104_AddChecksumToProfiles_test.go new file mode 100644 index 00000000000..9c75397dcc3 --- /dev/null +++ b/server/datastore/mysql/migrations/tables/20230408084104_AddChecksumToProfiles_test.go @@ -0,0 +1,87 @@ +package tables + +import ( + "crypto/md5" // nolint:gosec // used only to hash for efficient comparisons + "fmt" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestUp_20230408084104(t *testing.T) { + db := applyUpToPrev(t) + stmt := ` +INSERT INTO + mdm_apple_configuration_profiles (team_id, identifier, name, mobileconfig) +VALUES (?, ?, ?, ?)` + + mcBytes := []byte(` + + + + PayloadContent + + PayloadDisplayName + TestPayloadName + PayloadIdentifier + TestPayloadIdentifier + PayloadType + Configuration + PayloadUUID + TestPayloadUUID + PayloadVersion + 1 + + +`) + + r, err := db.Exec(stmt, 0, "TestPayloadIdentifier", "TestPayloadName", mcBytes) + profileID, _ := r.LastInsertId() + require.NoError(t, err) + + var ( + identifier string + mobileconfig []byte + ) + err = db.QueryRow(`SELECT identifier, mobileconfig FROM mdm_apple_configuration_profiles WHERE name = ? AND team_id = ?`, "TestPayloadName", 0).Scan(&identifier, &mobileconfig) + require.NoError(t, err) + require.Equal(t, "TestPayloadIdentifier", identifier) + require.Equal(t, mcBytes, mobileconfig) + + var status []string + err = db.Select(&status, `SELECT status FROM mdm_apple_delivery_status`) + require.NoError(t, err) + require.ElementsMatch(t, []string{"failed", "applied", "pending"}, status) + + var opTypes []string + err = db.Select(&opTypes, `SELECT operation_type FROM mdm_apple_operation_types`) + require.NoError(t, err) + require.ElementsMatch(t, []string{"install", "remove"}, opTypes) + + _, err = db.Exec(` + INSERT INTO nano_commands (command_uuid, request_type, command) + VALUES ('command-uuid', 'foo', '