Apply custom setup assistants (if present) when ingesting new devices (#11563)
This commit is contained in:
@@ -252,7 +252,7 @@ ORDER BY created_at DESC
|
||||
|
||||
func (ds *Datastore) GetMDMAppleEnrollmentProfileByToken(ctx context.Context, token string) (*fleet.MDMAppleEnrollmentProfile, error) {
|
||||
var enrollment fleet.MDMAppleEnrollmentProfile
|
||||
if err := sqlx.GetContext(ctx, ds.writer,
|
||||
if err := sqlx.GetContext(ctx, ds.reader,
|
||||
&enrollment,
|
||||
`
|
||||
SELECT
|
||||
@@ -277,6 +277,33 @@ WHERE
|
||||
return &enrollment, nil
|
||||
}
|
||||
|
||||
func (ds *Datastore) GetMDMAppleEnrollmentProfileByType(ctx context.Context, typ fleet.MDMAppleEnrollmentType) (*fleet.MDMAppleEnrollmentProfile, error) {
|
||||
var enrollment fleet.MDMAppleEnrollmentProfile
|
||||
if err := sqlx.GetContext(ctx, ds.writer, // use writer as it is used just after creation in some cases
|
||||
&enrollment,
|
||||
`
|
||||
SELECT
|
||||
id,
|
||||
token,
|
||||
type,
|
||||
dep_profile,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
mdm_apple_enrollment_profiles
|
||||
WHERE
|
||||
type = ?
|
||||
`,
|
||||
typ,
|
||||
); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, ctxerr.Wrap(ctx, notFound("MDMAppleEnrollmentProfile"))
|
||||
}
|
||||
return nil, ctxerr.Wrap(ctx, err, "get enrollment profile by type")
|
||||
}
|
||||
return &enrollment, nil
|
||||
}
|
||||
|
||||
func (ds *Datastore) GetMDMAppleCommandRequestType(ctx context.Context, commandUUID string) (string, error) {
|
||||
var rt string
|
||||
err := sqlx.GetContext(ctx, ds.reader, &rt, `SELECT request_type FROM nano_commands WHERE command_uuid = ?`, commandUUID)
|
||||
@@ -599,20 +626,20 @@ func insertMDMAppleHostDB(
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ds *Datastore) IngestMDMAppleDevicesFromDEPSync(ctx context.Context, devices []godep.Device) (int64, error) {
|
||||
func (ds *Datastore) IngestMDMAppleDevicesFromDEPSync(ctx context.Context, devices []godep.Device) (createdCount int64, teamID *uint, err error) {
|
||||
if len(devices) < 1 {
|
||||
level.Debug(ds.logger).Log("msg", "ingesting devices from DEP received < 1 device, skipping", "len(devices)", len(devices))
|
||||
return 0, nil
|
||||
return 0, nil, nil
|
||||
}
|
||||
filteredDevices := filterMDMAppleDevices(devices, ds.logger)
|
||||
if len(filteredDevices) < 1 {
|
||||
level.Debug(ds.logger).Log("msg", "ingesting devices from DEP filtered all devices, skipping", "len(devices)", len(devices))
|
||||
return 0, nil
|
||||
return 0, nil, nil
|
||||
}
|
||||
|
||||
appCfg, err := ds.AppConfig(ctx)
|
||||
if err != nil {
|
||||
return 0, ctxerr.Wrap(ctx, err, "ingest mdm apple host get app config")
|
||||
return 0, nil, ctxerr.Wrap(ctx, err, "ingest mdm apple host get app config")
|
||||
}
|
||||
|
||||
args := []interface{}{nil}
|
||||
@@ -629,13 +656,13 @@ func (ds *Datastore) IngestMDMAppleDevicesFromDEPSync(ctx context.Context, devic
|
||||
// If the team doesn't exist, we still ingest the device, but it won't
|
||||
// belong to any team.
|
||||
case err != nil:
|
||||
return 0, ctxerr.Wrap(ctx, err, "ingest mdm apple host get team by name")
|
||||
return 0, nil, ctxerr.Wrap(ctx, err, "ingest mdm apple host get team by name")
|
||||
default:
|
||||
args[0] = team.ID
|
||||
teamID = &team.ID
|
||||
}
|
||||
}
|
||||
|
||||
var resCount int64
|
||||
err = ds.withRetryTxx(ctx, func(tx sqlx.ExtContext) error {
|
||||
us, unionArgs := unionSelectDevices(filteredDevices)
|
||||
args = append(args, unionArgs...)
|
||||
@@ -678,7 +705,7 @@ func (ds *Datastore) IngestMDMAppleDevicesFromDEPSync(ctx context.Context, devic
|
||||
if err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "ingest mdm apple hosts from dep sync rows affected")
|
||||
}
|
||||
resCount = n
|
||||
createdCount = n
|
||||
|
||||
// get new host ids
|
||||
args = []interface{}{}
|
||||
@@ -715,7 +742,7 @@ func (ds *Datastore) IngestMDMAppleDevicesFromDEPSync(ctx context.Context, devic
|
||||
return nil
|
||||
})
|
||||
|
||||
return resCount, err
|
||||
return createdCount, teamID, err
|
||||
}
|
||||
|
||||
func upsertMDMAppleHostDisplayNamesDB(ctx context.Context, tx sqlx.ExtContext, hosts ...fleet.Host) error {
|
||||
|
||||
@@ -56,6 +56,7 @@ func TestMDMApple(t *testing.T) {
|
||||
{"TestListMDMAppleCommands", testListMDMAppleCommands},
|
||||
{"TestMDMAppleEULA", testMDMAppleEULA},
|
||||
{"TestMDMAppleSetupAssistant", testMDMAppleSetupAssistant},
|
||||
{"TestMDMAppleEnrollmentProfile", testMDMAppleEnrollmentProfile},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
@@ -443,9 +444,10 @@ func TestIngestMDMAppleDevicesFromDEPSync(t *testing.T) {
|
||||
}
|
||||
wantSerials = append(wantSerials, "abc", "xyz", "ijk")
|
||||
|
||||
n, err := ds.IngestMDMAppleDevicesFromDEPSync(ctx, depDevices)
|
||||
n, tmID, err := ds.IngestMDMAppleDevicesFromDEPSync(ctx, depDevices)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(3), n) // 3 new hosts ("abc", "xyz", "ijk")
|
||||
require.Nil(t, tmID)
|
||||
|
||||
hosts = listHostsCheckCount(t, ds, fleet.TeamFilter{User: test.UserAdmin}, fleet.HostListOptions{}, len(wantSerials))
|
||||
gotSerials := []string{}
|
||||
@@ -468,8 +470,9 @@ func TestDEPSyncTeamAssignment(t *testing.T) {
|
||||
{SerialNumber: "def", Model: "MacBook Pro", OS: "OSX", OpType: "added"},
|
||||
}
|
||||
|
||||
n, err := ds.IngestMDMAppleDevicesFromDEPSync(ctx, depDevices)
|
||||
n, tmID, err := ds.IngestMDMAppleDevicesFromDEPSync(ctx, depDevices)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, tmID)
|
||||
require.Equal(t, int64(2), n)
|
||||
|
||||
hosts := listHostsCheckCount(t, ds, fleet.TeamFilter{User: test.UserAdmin}, fleet.HostListOptions{}, 2)
|
||||
@@ -493,9 +496,11 @@ func TestDEPSyncTeamAssignment(t *testing.T) {
|
||||
{SerialNumber: "xyz", Model: "MacBook Pro", OS: "OSX", OpType: "added"},
|
||||
}
|
||||
|
||||
n, err = ds.IngestMDMAppleDevicesFromDEPSync(ctx, depDevices)
|
||||
n, tmID, err = ds.IngestMDMAppleDevicesFromDEPSync(ctx, depDevices)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(1), n)
|
||||
require.NotNil(t, tmID)
|
||||
require.Equal(t, team.ID, *tmID)
|
||||
|
||||
hosts = listHostsCheckCount(t, ds, fleet.TeamFilter{User: test.UserAdmin}, fleet.HostListOptions{}, 3)
|
||||
for _, h := range hosts {
|
||||
@@ -514,9 +519,10 @@ func TestDEPSyncTeamAssignment(t *testing.T) {
|
||||
{SerialNumber: "jqk", Model: "MacBook Pro", OS: "OSX", OpType: "added"},
|
||||
}
|
||||
|
||||
n, err = ds.IngestMDMAppleDevicesFromDEPSync(ctx, depDevices)
|
||||
n, tmID, err = ds.IngestMDMAppleDevicesFromDEPSync(ctx, depDevices)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, n, 1)
|
||||
require.Nil(t, tmID)
|
||||
|
||||
hosts = listHostsCheckCount(t, ds, fleet.TeamFilter{User: test.UserAdmin}, fleet.HostListOptions{}, 4)
|
||||
for _, h := range hosts {
|
||||
@@ -638,11 +644,12 @@ func testIngestMDMAppleIngestAfterDEPSync(t *testing.T, ds *Datastore) {
|
||||
testModel := "MacBook Pro"
|
||||
|
||||
// simulate a host that is first ingested via DEP (e.g., the device was added via Apple Business Manager)
|
||||
n, err := ds.IngestMDMAppleDevicesFromDEPSync(ctx, []godep.Device{
|
||||
n, tmID, err := ds.IngestMDMAppleDevicesFromDEPSync(ctx, []godep.Device{
|
||||
{SerialNumber: testSerial, Model: testModel, OS: "OSX", OpType: "added"},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(1), n)
|
||||
require.Nil(t, tmID)
|
||||
|
||||
hosts := listHostsCheckCount(t, ds, fleet.TeamFilter{User: test.UserAdmin}, fleet.HostListOptions{}, 1)
|
||||
// hosts that are first ingested via DEP will have a serial number but not a UUID because UUID
|
||||
@@ -684,11 +691,12 @@ func testIngestMDMAppleCheckinBeforeDEPSync(t *testing.T, ds *Datastore) {
|
||||
checkMDMHostRelatedTables(t, ds, hosts[0].ID, testSerial, testModel)
|
||||
|
||||
// no effect if same host appears in DEP sync
|
||||
n, err := ds.IngestMDMAppleDevicesFromDEPSync(ctx, []godep.Device{
|
||||
n, tmID, err := ds.IngestMDMAppleDevicesFromDEPSync(ctx, []godep.Device{
|
||||
{SerialNumber: testSerial, Model: testModel, OS: "OSX", OpType: "added"},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(0), n)
|
||||
require.Nil(t, tmID)
|
||||
|
||||
hosts = listHostsCheckCount(t, ds, fleet.TeamFilter{User: test.UserAdmin}, fleet.HostListOptions{}, 1)
|
||||
require.Equal(t, testSerial, hosts[0].HardwareSerial)
|
||||
@@ -3407,3 +3415,56 @@ func testMDMAppleSetupAssistant(t *testing.T, ds *Datastore) {
|
||||
err = ds.DeleteMDMAppleSetupAssistant(ctx, &tm.ID)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func testMDMAppleEnrollmentProfile(t *testing.T, ds *Datastore) {
|
||||
ctx := context.Background()
|
||||
|
||||
_, err := ds.GetMDMAppleEnrollmentProfileByType(ctx, fleet.MDMAppleEnrollmentTypeAutomatic)
|
||||
require.Error(t, err)
|
||||
require.ErrorIs(t, err, sql.ErrNoRows)
|
||||
|
||||
_, err = ds.GetMDMAppleEnrollmentProfileByToken(ctx, "abcd")
|
||||
require.Error(t, err)
|
||||
require.ErrorIs(t, err, sql.ErrNoRows)
|
||||
|
||||
// add a new automatic enrollment profile
|
||||
rawMsg := json.RawMessage(`{"allow_pairing": true}`)
|
||||
profAuto, err := ds.NewMDMAppleEnrollmentProfile(ctx, fleet.MDMAppleEnrollmentProfilePayload{
|
||||
Type: "automatic",
|
||||
DEPProfile: &rawMsg,
|
||||
Token: "abcd",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NotZero(t, profAuto.ID)
|
||||
|
||||
// add a new manual enrollment profile
|
||||
profMan, err := ds.NewMDMAppleEnrollmentProfile(ctx, fleet.MDMAppleEnrollmentProfilePayload{
|
||||
Type: "manual",
|
||||
DEPProfile: &rawMsg,
|
||||
Token: "efgh",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NotZero(t, profMan.ID)
|
||||
|
||||
profs, err := ds.ListMDMAppleEnrollmentProfiles(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, profs, 2)
|
||||
|
||||
tokens := make([]string, 2)
|
||||
for i, p := range profs {
|
||||
tokens[i] = p.Token
|
||||
}
|
||||
require.ElementsMatch(t, []string{"abcd", "efgh"}, tokens)
|
||||
|
||||
// get the automatic profile by type
|
||||
getProf, err := ds.GetMDMAppleEnrollmentProfileByType(ctx, fleet.MDMAppleEnrollmentTypeAutomatic)
|
||||
require.NoError(t, err)
|
||||
getProf.UpdateCreateTimestamps = fleet.UpdateCreateTimestamps{}
|
||||
require.Equal(t, profAuto, getProf)
|
||||
|
||||
// get the manual profile by token
|
||||
getProf, err = ds.GetMDMAppleEnrollmentProfileByToken(ctx, "efgh")
|
||||
require.NoError(t, err)
|
||||
getProf.UpdateCreateTimestamps = fleet.UpdateCreateTimestamps{}
|
||||
require.Equal(t, profMan, getProf)
|
||||
}
|
||||
|
||||
@@ -1098,11 +1098,12 @@ func testHostsListMDM(t *testing.T, ds *Datastore) {
|
||||
}
|
||||
|
||||
// enrollment: pending (with Fleet mdm)
|
||||
n, err := ds.IngestMDMAppleDevicesFromDEPSync(ctx, []godep.Device{
|
||||
n, tmID, err := ds.IngestMDMAppleDevicesFromDEPSync(ctx, []godep.Device{
|
||||
{SerialNumber: "532141num832", Model: "MacBook Pro", OS: "OSX", OpType: "added"},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(1), n)
|
||||
require.Nil(t, tmID)
|
||||
|
||||
const simpleMDM, kandji, unknown = "https://simplemdm.com", "https://kandji.io", "https://url.com"
|
||||
err = ds.SetOrUpdateMDMData(ctx, hostIDs[0], false, true, simpleMDM, true, fleet.WellKnownMDMSimpleMDM) // enrollment: automatic
|
||||
|
||||
Reference in New Issue
Block a user