Add tests for declarations

This commit is contained in:
Martin Angers 2024-04-08 14:48:11 -04:00
parent b979eddcfc
commit 7964a81828
4 changed files with 241 additions and 8 deletions

View file

@ -12,6 +12,7 @@ import (
"github.com/fleetdm/fleet/v4/server/contexts/ctxerr"
"github.com/fleetdm/fleet/v4/server/fleet"
fleetmdm "github.com/fleetdm/fleet/v4/server/mdm"
apple_mdm "github.com/fleetdm/fleet/v4/server/mdm/apple"
"github.com/fleetdm/fleet/v4/server/mdm/apple/mobileconfig"
"github.com/fleetdm/fleet/v4/server/mdm/nanodep/godep"
@ -3393,8 +3394,6 @@ WHERE h.uuid = ?
}
func (ds *Datastore) batchSetMDMAppleDeclarations(ctx context.Context, tx sqlx.ExtContext, tmID *uint, incomingDeclarations []*fleet.MDMAppleDeclaration) ([]*fleet.MDMAppleDeclaration, error) {
// TODO(mna): batch-set should not delete the reserved OS updates DDM.
const insertStmt = `
INSERT INTO mdm_apple_declarations (
declaration_uuid,
@ -3471,7 +3470,7 @@ WHERE
}
// figure out if we need to delete any declarations
keepIdents := make([]any, 0, len(incomingIdents))
keepIdents := make([]string, 0, len(incomingIdents))
for _, p := range existingDecls {
if newP := incomingDecls[p.Identifier]; newP != nil {
keepIdents = append(keepIdents, p.Identifier)
@ -3486,7 +3485,7 @@ WHERE
delArgs = []any{declTeamID}
} else {
// delete the obsolete declarations (all those that are not in keepIdents)
stmt, args, err := sqlx.In(fmt.Sprintf(fmtDeleteStmt, andIdentNotInList), declTeamID, keepIdents)
stmt, args, err := sqlx.In(fmt.Sprintf(fmtDeleteStmt, andIdentNotInList), declTeamID, append(keepIdents, fleetmdm.ListFleetReservedMacOSDeclarationNames()...))
// if err != nil || strings.HasPrefix(ds.testBatchSetMDMAppleProfilesErr, "inselect") { // TODO(JVE): do we need to create similar errors for testing decls?
// if err == nil {
// err = errors.New(ds.testBatchSetMDMAppleProfilesErr)

View file

@ -1693,6 +1693,12 @@ ON DUPLICATE KEY UPDATE
keepNames = append(keepNames, p.Name)
}
}
for n := range mdm.FleetReservedProfileNames() {
if _, ok := incomingProfs[n]; !ok {
// always keep reserved profiles even if they're not incoming
keepNames = append(keepNames, n)
}
}
var (
stmt string

View file

@ -116,10 +116,16 @@ func ListFleetReservedWindowsProfileNames() []string {
return []string{FleetWindowsOSUpdatesProfileName}
}
// ListFleetReservedAppleDDMProfileNames returns a list of profile names that
// are reserved by Fleet for Apple DDM declarations.
func ListFleetReservedAppleDDMProfileNames() []string {
// ListFleetReservedMacOSProfileNames returns a list of PayloadDisplayName strings
// that are reserved by Fleet for macOS.
func ListFleetReservedMacOSProfileNames() []string {
return []string{FleetFileVaultProfileName, FleetdConfigProfileName}
}
// ListFleetReservedMacOSDeclarationNames returns a list of declaration names
// that are reserved by Fleet for Apple DDM declarations.
func ListFleetReservedMacOSDeclarationNames() []string {
return []string{FleetMacOSUpdatesProfileName}
// TODO(mna): use this to filter-out those reserved profiles from status
// summaries/filters.
// summaries/filters. Reconcile with the previous func...
}

View file

@ -6230,6 +6230,54 @@ func (s *integrationMDMTestSuite) assertConfigProfilesByIdentifier(teamID *uint,
return profile
}
func (s *integrationMDMTestSuite) assertMacOSConfigProfilesByName(teamID *uint, profileName string, exists bool) {
t := s.T()
if teamID == nil {
teamID = ptr.Uint(0)
}
var cfgProfs []*fleet.MDMAppleConfigProfile
mysql.ExecAdhocSQL(t, s.ds, func(q sqlx.ExtContext) error {
return sqlx.SelectContext(context.Background(), q, &cfgProfs, `SELECT name FROM mdm_apple_configuration_profiles WHERE team_id = ?`, teamID)
})
label := "exist"
if !exists {
label = "not exist"
}
require.Condition(t, func() bool {
for _, p := range cfgProfs {
if p.Name == profileName {
return exists // success if we want it to exist, failure if we don't
}
}
return !exists
}, "a config profile must %s with name: %s", label, profileName)
}
func (s *integrationMDMTestSuite) assertMacOSDeclarationsByName(teamID *uint, declarationName string, exists bool) {
t := s.T()
if teamID == nil {
teamID = ptr.Uint(0)
}
var cfgProfs []*fleet.MDMAppleConfigProfile
mysql.ExecAdhocSQL(t, s.ds, func(q sqlx.ExtContext) error {
return sqlx.SelectContext(context.Background(), q, &cfgProfs, `SELECT name FROM mdm_apple_declarations WHERE team_id = ?`, teamID)
})
label := "exist"
if !exists {
label = "not exist"
}
require.Condition(t, func() bool {
for _, p := range cfgProfs {
if p.Name == declarationName {
return exists // success if we want it to exist, failure if we don't
}
}
return !exists
}, "a config profile must %s with name: %s", label, declarationName)
}
func (s *integrationMDMTestSuite) assertWindowsConfigProfilesByName(teamID *uint, profileName string, exists bool) {
t := s.T()
if teamID == nil {
@ -7423,6 +7471,7 @@ func (s *integrationMDMTestSuite) TestOrbitConfigNudgeSettings() {
Description: "desc team1_" + t.Name(),
})
require.NoError(t, err)
s.assertMacOSDeclarationsByName(&team.ID, servermdm.FleetMacOSUpdatesProfileName, false)
// add the host to the team
err = s.ds.AddHostsToTeam(context.Background(), &team.ID, []uint{h.ID})
@ -7444,6 +7493,7 @@ func (s *integrationMDMTestSuite) TestOrbitConfigNudgeSettings() {
},
},
}, http.StatusOK, &tmResp)
s.assertMacOSDeclarationsByName(&team.ID, servermdm.FleetMacOSUpdatesProfileName, true)
resp = orbitGetConfigResponse{}
s.DoJSON("POST", "/api/fleet/orbit/config", json.RawMessage(fmt.Sprintf(`{"orbit_node_key": %q}`, *h.OrbitNodeKey)), http.StatusOK, &resp)
@ -12219,3 +12269,175 @@ func (s *integrationMDMTestSuite) TestIsServerBitlockerStatus() {
require.NotNil(t, hr.Host.MDM.OSSettings.DiskEncryption.Status)
require.Equal(t, fleet.DiskEncryptionEnforcing, *hr.Host.MDM.OSSettings.DiskEncryption.Status)
}
func (s *integrationMDMTestSuite) TestMDMBatchSetProfilesKeepsReservedNames() {
t := s.T()
ctx := context.Background()
checkMacProfs := func(teamID *uint, names ...string) {
var count int
mysql.ExecAdhocSQL(t, s.ds, func(q sqlx.ExtContext) error {
var tid uint
if teamID != nil {
tid = *teamID
}
return sqlx.GetContext(ctx, q, &count, `SELECT COUNT(*) FROM mdm_apple_configuration_profiles WHERE team_id = ?`, tid)
})
require.Equal(t, len(names), count)
for _, n := range names {
s.assertMacOSConfigProfilesByName(teamID, n, true)
}
}
checkMacDecls := func(teamID *uint, names ...string) {
var count int
mysql.ExecAdhocSQL(t, s.ds, func(q sqlx.ExtContext) error {
var tid uint
if teamID != nil {
tid = *teamID
}
return sqlx.GetContext(ctx, q, &count, `SELECT COUNT(*) FROM mdm_apple_declarations WHERE team_id = ?`, tid)
})
require.Equal(t, len(names), count)
for _, n := range names {
s.assertMacOSDeclarationsByName(teamID, n, true)
}
}
checkWinProfs := func(teamID *uint, names ...string) {
var count int
mysql.ExecAdhocSQL(t, s.ds, func(q sqlx.ExtContext) error {
var tid uint
if teamID != nil {
tid = *teamID
}
return sqlx.GetContext(ctx, q, &count, `SELECT COUNT(*) FROM mdm_windows_configuration_profiles WHERE team_id = ?`, tid)
})
for _, n := range names {
s.assertWindowsConfigProfilesByName(teamID, n, true)
}
}
acResp := appConfigResponse{}
s.DoJSON("GET", "/api/latest/fleet/config", nil, http.StatusOK, &acResp)
require.True(t, acResp.MDM.EnabledAndConfigured)
require.True(t, acResp.MDM.WindowsEnabledAndConfigured)
// ensures that the fleetd profile is created
secrets, err := s.ds.GetEnrollSecrets(ctx, nil)
require.NoError(t, err)
if len(secrets) == 0 {
require.NoError(t, s.ds.ApplyEnrollSecrets(ctx, nil, []*fleet.EnrollSecret{{Secret: t.Name()}}))
}
require.NoError(t, ReconcileAppleProfiles(ctx, s.ds, s.mdmCommander, s.logger))
// turn on disk encryption and os updates
s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{
"mdm": {
"enable_disk_encryption": true,
"windows_updates": {
"deadline_days": 3,
"grace_period_days": 1
},
"macos_updates": {
"deadline": "2023-12-31",
"minimum_version": "13.3.7"
}
}
}`), http.StatusOK, &acResp)
checkMacProfs(nil, servermdm.ListFleetReservedMacOSProfileNames()...)
checkMacDecls(nil, servermdm.ListFleetReservedMacOSDeclarationNames()...)
checkWinProfs(nil, servermdm.ListFleetReservedWindowsProfileNames()...)
// batch set only windows profiles doesn't remove the reserved names
newWinProfile := syncml.ForTestWithData(map[string]string{"l1": "d1"})
var testProfiles []fleet.MDMProfileBatchPayload
testProfiles = append(testProfiles, fleet.MDMProfileBatchPayload{
Name: "n1",
Contents: newWinProfile,
})
s.Do("POST", "/api/v1/fleet/mdm/profiles/batch", batchSetMDMProfilesRequest{Profiles: testProfiles}, http.StatusNoContent)
checkMacProfs(nil, servermdm.ListFleetReservedMacOSProfileNames()...)
checkWinProfs(nil, append(servermdm.ListFleetReservedWindowsProfileNames(), "n1")...)
// batch set windows and mac profiles doesn't remove the reserved names
newMacProfile := mcBytesForTest("n2", "i2", uuid.NewString())
testProfiles = append(testProfiles, fleet.MDMProfileBatchPayload{
Name: "n2",
Contents: newMacProfile,
})
s.Do("POST", "/api/v1/fleet/mdm/profiles/batch", batchSetMDMProfilesRequest{Profiles: testProfiles}, http.StatusNoContent)
checkMacProfs(nil, append(servermdm.ListFleetReservedMacOSProfileNames(), "n2")...)
checkWinProfs(nil, append(servermdm.ListFleetReservedWindowsProfileNames(), "n1")...)
// batch set only mac profiles doesn't remove the reserved names
testProfiles = []fleet.MDMProfileBatchPayload{{
Name: "n2",
Contents: newMacProfile,
}}
s.Do("POST", "/api/v1/fleet/mdm/profiles/batch", batchSetMDMProfilesRequest{Profiles: testProfiles}, http.StatusNoContent)
checkMacProfs(nil, append(servermdm.ListFleetReservedMacOSProfileNames(), "n2")...)
checkWinProfs(nil, servermdm.ListFleetReservedWindowsProfileNames()...)
// create a team
var tmResp teamResponse
s.DoJSON("POST", "/api/v1/fleet/teams", map[string]string{"Name": t.Name()}, http.StatusOK, &tmResp)
// edit team mdm config to turn on disk encryption and os updates
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", tmResp.Team.ID), modifyTeamRequest{
TeamPayload: fleet.TeamPayload{
Name: ptr.String(t.Name()),
MDM: &fleet.TeamPayloadMDM{
EnableDiskEncryption: optjson.SetBool(true),
WindowsUpdates: &fleet.WindowsUpdates{
DeadlineDays: optjson.SetInt(4),
GracePeriodDays: optjson.SetInt(1),
},
MacOSUpdates: &fleet.MacOSUpdates{
Deadline: optjson.SetString("2023-12-31"),
MinimumVersion: optjson.SetString("13.3.8"),
},
},
},
}, http.StatusOK, &teamResponse{})
s.DoJSON("GET", fmt.Sprintf("/api/v1/fleet/teams/%d", tmResp.Team.ID), nil, http.StatusOK, &tmResp)
require.True(t, tmResp.Team.Config.MDM.EnableDiskEncryption)
require.Equal(t, 4, tmResp.Team.Config.MDM.WindowsUpdates.DeadlineDays.Value)
require.Equal(t, 1, tmResp.Team.Config.MDM.WindowsUpdates.GracePeriodDays.Value)
require.Equal(t, "2023-12-31", tmResp.Team.Config.MDM.MacOSUpdates.Deadline.Value)
require.Equal(t, "13.3.8", tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion.Value)
require.NoError(t, ReconcileAppleProfiles(ctx, s.ds, s.mdmCommander, s.logger))
checkMacProfs(&tmResp.Team.ID, servermdm.ListFleetReservedMacOSProfileNames()...)
checkWinProfs(&tmResp.Team.ID, servermdm.ListFleetReservedWindowsProfileNames()...)
// batch set only windows profiles doesn't remove the reserved names
var testTeamProfiles []fleet.MDMProfileBatchPayload
testTeamProfiles = append(testTeamProfiles, fleet.MDMProfileBatchPayload{
Name: "n1",
Contents: newWinProfile,
})
s.Do("POST", "/api/v1/fleet/mdm/profiles/batch", batchSetMDMProfilesRequest{Profiles: testTeamProfiles}, http.StatusNoContent, "team_id", strconv.Itoa(int(tmResp.Team.ID)))
checkMacProfs(&tmResp.Team.ID, servermdm.ListFleetReservedMacOSProfileNames()...)
checkWinProfs(&tmResp.Team.ID, append(servermdm.ListFleetReservedWindowsProfileNames(), "n1")...)
// batch set windows and mac profiles doesn't remove the reserved names
testTeamProfiles = append(testTeamProfiles, fleet.MDMProfileBatchPayload{
Name: "n2",
Contents: newMacProfile,
})
s.Do("POST", "/api/v1/fleet/mdm/profiles/batch", batchSetMDMProfilesRequest{Profiles: testTeamProfiles}, http.StatusNoContent, "team_id", strconv.Itoa(int(tmResp.Team.ID)))
checkMacProfs(&tmResp.Team.ID, append(servermdm.ListFleetReservedMacOSProfileNames(), "n2")...)
checkWinProfs(&tmResp.Team.ID, append(servermdm.ListFleetReservedWindowsProfileNames(), "n1")...)
// batch set only mac profiles doesn't remove the reserved names
testTeamProfiles = []fleet.MDMProfileBatchPayload{{
Name: "n2",
Contents: newMacProfile,
}}
s.Do("POST", "/api/v1/fleet/mdm/profiles/batch", batchSetMDMProfilesRequest{Profiles: testTeamProfiles}, http.StatusNoContent, "team_id", strconv.Itoa(int(tmResp.Team.ID)))
checkMacProfs(&tmResp.Team.ID, append(servermdm.ListFleetReservedMacOSProfileNames(), "n2")...)
checkWinProfs(&tmResp.Team.ID, servermdm.ListFleetReservedWindowsProfileNames()...)
}