Add MDM profiles to host detail in API responses (#10034)
Issue #9599 # Checklist for submitter If some of the following don't apply, delete the relevant line. - [ ] Changes file added for user-visible changes in `changes/` or `orbit/changes/`. See [Changes files](https://fleetdm.com/docs/contributing/committing-changes#changes-files) for more information. - [ ] Documented any API changes (docs/Using-Fleet/REST-API.md or docs/Contributing/API-for-contributors.md) - [ ] Documented any permissions changes - [ ] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements) - [ ] Added support on fleet's osquery simulator `cmd/osquery-perf` for new osquery data ingestion features. - [ ] Added/updated tests - [ ] Manual QA for all new/changed functionality - For Orbit and Fleet Desktop changes: - [ ] Manual QA must be performed in the three main OSs, macOS, Windows and Linux. - [ ] Auto-update manual QA, from released version of component to new version (see [tools/tuf/test](../tools/tuf/test/README.md)).
This commit is contained in:
@@ -265,6 +265,10 @@ func TestGetTeamsByName(t *testing.T) {
|
||||
func TestGetHosts(t *testing.T) {
|
||||
_, ds := runServerWithMockedDS(t)
|
||||
|
||||
ds.AppConfigFunc = func(ctx context.Context) (*fleet.AppConfig, error) {
|
||||
return &fleet.AppConfig{}, nil
|
||||
}
|
||||
|
||||
// this func is called when no host is specified i.e. `fleetctl get hosts --json`
|
||||
ds.ListHostsFunc = func(ctx context.Context, filter fleet.TeamFilter, opt fleet.HostListOptions) ([]*fleet.Host, error) {
|
||||
additional := json.RawMessage(`{"query1": [{"col1": "val", "col2": 42}]}`)
|
||||
|
||||
@@ -237,6 +237,7 @@ require (
|
||||
github.com/jonboulle/clockwork v0.2.2 // indirect
|
||||
github.com/kevinburke/ssh_config v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.15.0 // indirect
|
||||
github.com/lib/pq v1.10.6 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/magiconair/properties v1.8.5 // indirect
|
||||
github.com/mattn/go-colorable v0.1.11 // indirect
|
||||
@@ -287,6 +288,7 @@ require (
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
github.com/yashtewari/glob-intersection v0.1.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
github.com/ziutek/mymysql v1.5.4 // indirect
|
||||
go.elastic.co/apm v1.15.0 // indirect
|
||||
go.elastic.co/fastjson v1.1.0 // indirect
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
|
||||
@@ -914,6 +914,7 @@ github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.10.1/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs=
|
||||
github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/macadmins/osquery-extension v0.0.7 h1:iEo2eajSbG51pdXg+QH3kM5ZWhidWBBZ/YiNDEXU1lE=
|
||||
@@ -1306,6 +1307,7 @@ github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPR
|
||||
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
|
||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
||||
github.com/zwass/kit v0.0.0-20210625184505-ec5b5c5cce9c h1:TWQ2UvXPkhPxI2KmApKBOCaV6yD2N4mlvqFQ/DlPtpQ=
|
||||
github.com/zwass/kit v0.0.0-20210625184505-ec5b5c5cce9c/go.mod h1:OYYulo9tUqRadRLwB0+LE914sa1ui2yL7OrcU3Q/1XY=
|
||||
|
||||
@@ -140,6 +140,25 @@ func (ds *Datastore) DeleteMDMAppleConfigProfile(ctx context.Context, profileID
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ds *Datastore) GetHostMDMProfiles(ctx context.Context, hostUUID string) ([]fleet.HostMDMAppleProfile, error) {
|
||||
stmt := `
|
||||
SELECT
|
||||
profile_id,
|
||||
status,
|
||||
operation_type,
|
||||
detail
|
||||
FROM
|
||||
host_mdm_apple_profiles
|
||||
WHERE
|
||||
host_uuid = ?`
|
||||
|
||||
var profiles []fleet.HostMDMAppleProfile
|
||||
if err := sqlx.SelectContext(ctx, ds.reader, &profiles, stmt, hostUUID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return profiles, nil
|
||||
}
|
||||
|
||||
func (ds *Datastore) NewMDMAppleEnrollmentProfile(
|
||||
ctx context.Context,
|
||||
payload fleet.MDMAppleEnrollmentProfilePayload,
|
||||
|
||||
@@ -27,6 +27,7 @@ func TestMDMAppleConfigProfile(t *testing.T) {
|
||||
{"TestNewMDMAppleConfigProfileDuplicateIdentifier", testNewMDMAppleConfigProfileDuplicateIdentifier},
|
||||
{"TestDeleteMDMAppleConfigProfile", testDeleteMDMAppleConfigProfile},
|
||||
{"TestListMDMAppleConfigProfiles", testListMDMAppleConfigProfiles},
|
||||
{"TestHostDetailsMDMProfiles", testHostDetailsMDMProfiles},
|
||||
{"TestBatchSetMDMAppleProfiles", testBatchSetMDMAppleProfiles},
|
||||
{"TestMDMAppleProfileManagement", testMDMAppleProfileManagement},
|
||||
{"TestUpdateHostMDMAppleProfile", testGetMDMAppleProfilesContents},
|
||||
@@ -192,6 +193,124 @@ func checkConfigProfile(t *testing.T, expected fleet.MDMAppleConfigProfile, actu
|
||||
require.Equal(t, expected.Mobileconfig, actual.Mobileconfig)
|
||||
}
|
||||
|
||||
func testHostDetailsMDMProfiles(t *testing.T, ds *Datastore) {
|
||||
ctx := context.Background()
|
||||
|
||||
p0, err := ds.NewMDMAppleConfigProfile(ctx, fleet.MDMAppleConfigProfile{Name: "Name0", Identifier: "Identifier0", Mobileconfig: []byte("profile0-bytes")})
|
||||
require.NoError(t, err)
|
||||
|
||||
p1, err := ds.NewMDMAppleConfigProfile(ctx, fleet.MDMAppleConfigProfile{Name: "Name1", Identifier: "Identifier1", Mobileconfig: []byte("profile1-bytes")})
|
||||
require.NoError(t, err)
|
||||
|
||||
p2, err := ds.NewMDMAppleConfigProfile(ctx, fleet.MDMAppleConfigProfile{Name: "Name2", Identifier: "Identifier2", Mobileconfig: []byte("profile2-bytes")})
|
||||
require.NoError(t, err)
|
||||
|
||||
profiles, err := ds.ListMDMAppleConfigProfiles(ctx, ptr.Uint(0))
|
||||
require.NoError(t, err)
|
||||
require.Len(t, profiles, 3)
|
||||
|
||||
h0, err := ds.NewHost(ctx, &fleet.Host{
|
||||
DetailUpdatedAt: time.Now(),
|
||||
LabelUpdatedAt: time.Now(),
|
||||
PolicyUpdatedAt: time.Now(),
|
||||
SeenTime: time.Now(),
|
||||
OsqueryHostID: ptr.String("host0-osquery-id"),
|
||||
NodeKey: ptr.String("host0-node-key"),
|
||||
UUID: "host0-test-mdm-profiles",
|
||||
Hostname: "hostname0",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
gotHost, err := ds.Host(ctx, h0.ID)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, gotHost.MDM.Profiles)
|
||||
gotProfs, err := ds.GetHostMDMProfiles(ctx, h0.UUID)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, gotProfs)
|
||||
|
||||
h1, err := ds.NewHost(ctx, &fleet.Host{
|
||||
DetailUpdatedAt: time.Now(),
|
||||
LabelUpdatedAt: time.Now(),
|
||||
PolicyUpdatedAt: time.Now(),
|
||||
SeenTime: time.Now(),
|
||||
OsqueryHostID: ptr.String("host1-osquery-id"),
|
||||
NodeKey: ptr.String("host1-node-key"),
|
||||
UUID: "host1-test-mdm-profiles",
|
||||
Hostname: "hostname1",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
gotHost, err = ds.Host(ctx, h1.ID)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, gotHost.MDM.Profiles)
|
||||
gotProfs, err = ds.GetHostMDMProfiles(ctx, h1.UUID)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, gotProfs)
|
||||
|
||||
expectedProfiles0 := map[uint]fleet.HostMDMAppleProfile{
|
||||
p0.ProfileID: {HostUUID: h0.UUID, ProfileID: p0.ProfileID, CommandUUID: "cmd0-uuid", Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall, Detail: ""},
|
||||
p1.ProfileID: {HostUUID: h0.UUID, ProfileID: p1.ProfileID, CommandUUID: "cmd1-uuid", Status: &fleet.MDMAppleDeliveryApplied, OperationType: fleet.MDMAppleOperationTypeInstall, Detail: ""},
|
||||
p2.ProfileID: {HostUUID: h0.UUID, ProfileID: p2.ProfileID, CommandUUID: "cmd2-uuid", Status: &fleet.MDMAppleDeliveryFailed, OperationType: fleet.MDMAppleOperationTypeRemove, Detail: "Error removing profile"},
|
||||
}
|
||||
|
||||
expectedProfiles1 := map[uint]fleet.HostMDMAppleProfile{
|
||||
p0.ProfileID: {HostUUID: h1.UUID, ProfileID: p0.ProfileID, CommandUUID: "cmd0-uuid", Status: &fleet.MDMAppleDeliveryFailed, OperationType: fleet.MDMAppleOperationTypeInstall, Detail: "Error installing profile"},
|
||||
p1.ProfileID: {HostUUID: h1.UUID, ProfileID: p1.ProfileID, CommandUUID: "cmd1-uuid", Status: &fleet.MDMAppleDeliveryApplied, OperationType: fleet.MDMAppleOperationTypeInstall, Detail: ""},
|
||||
p2.ProfileID: {HostUUID: h1.UUID, ProfileID: p2.ProfileID, CommandUUID: "cmd2-uuid", Status: &fleet.MDMAppleDeliveryFailed, OperationType: fleet.MDMAppleOperationTypeRemove, Detail: "Error removing profile"},
|
||||
}
|
||||
|
||||
var args []interface{}
|
||||
for _, p := range expectedProfiles0 {
|
||||
args = append(args, p.HostUUID, p.ProfileID, p.CommandUUID, *p.Status, p.OperationType, p.Detail)
|
||||
}
|
||||
for _, p := range expectedProfiles1 {
|
||||
args = append(args, p.HostUUID, p.ProfileID, p.CommandUUID, *p.Status, p.OperationType, p.Detail)
|
||||
}
|
||||
|
||||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||||
_, err := q.ExecContext(ctx, `
|
||||
INSERT INTO host_mdm_apple_profiles (
|
||||
host_uuid, profile_id, command_uuid, status, operation_type, detail)
|
||||
VALUES (?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?)
|
||||
`, args...,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
gotHost, err = ds.Host(ctx, h1.ID)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, gotHost.MDM.Profiles) // ds.Host never returns MDM profiles
|
||||
|
||||
gotProfs, err = ds.GetHostMDMProfiles(ctx, h0.UUID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, gotProfs, 3)
|
||||
for _, gp := range gotProfs {
|
||||
ep, ok := expectedProfiles0[gp.ProfileID]
|
||||
require.True(t, ok)
|
||||
require.Equal(t, *ep.Status, *gp.Status)
|
||||
require.Equal(t, ep.OperationType, gp.OperationType)
|
||||
require.Equal(t, ep.Detail, gp.Detail)
|
||||
}
|
||||
|
||||
gotHost, err = ds.Host(ctx, h1.ID)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, gotHost.MDM.Profiles) // ds.Host never returns MDM profiles
|
||||
|
||||
gotProfs, err = ds.GetHostMDMProfiles(ctx, h1.UUID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, gotProfs, 3)
|
||||
for _, gp := range gotProfs {
|
||||
ep, ok := expectedProfiles1[gp.ProfileID]
|
||||
require.True(t, ok)
|
||||
require.Equal(t, *ep.Status, *gp.Status)
|
||||
require.Equal(t, ep.OperationType, gp.OperationType)
|
||||
require.Equal(t, ep.Detail, gp.Detail)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIngestMDMAppleDevicesFromDEPSync(t *testing.T) {
|
||||
ds := CreateMySQLDS(t)
|
||||
ctx := context.Background()
|
||||
@@ -823,7 +942,6 @@ func testMDMAppleProfileManagement(t *testing.T, ds *Datastore) {
|
||||
{ProfileID: globalPfs[1].ProfileID, ProfileIdentifier: globalPfs[1].Identifier, HostUUID: "test-uuid-1"},
|
||||
{ProfileID: globalPfs[2].ProfileID, ProfileIdentifier: globalPfs[2].Identifier, HostUUID: "test-uuid-1"},
|
||||
}, toRemove)
|
||||
|
||||
}
|
||||
|
||||
// checkMDMHostRelatedTables checks that rows are inserted for new MDM hosts in
|
||||
|
||||
@@ -476,6 +476,8 @@ LIMIT
|
||||
return &host, nil
|
||||
}
|
||||
|
||||
// hostMDMSelect is the SQL fragment used to construct the JSON object
|
||||
// of MDM host data.
|
||||
const hostMDMSelect = `,
|
||||
JSON_OBJECT(
|
||||
'enrollment_status',
|
||||
@@ -2359,7 +2361,6 @@ func (ds *Datastore) SetOrUpdateHostDiskEncryptionKey(ctx context.Context, hostI
|
||||
base64_encrypted = VALUES(base64_encrypted)
|
||||
`, hostID, encryptedBase64Key)
|
||||
return err
|
||||
|
||||
}
|
||||
|
||||
func (ds *Datastore) GetUnverifiedDiskEncryptionKeys(ctx context.Context) ([]fleet.HostDiskEncryptionKey, error) {
|
||||
@@ -2406,7 +2407,6 @@ func (ds *Datastore) GetHostDiskEncryptionKey(ctx context.Context, hostID uint)
|
||||
FROM
|
||||
host_disk_encryption_keys
|
||||
WHERE host_id = ?`, hostID)
|
||||
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
msg := fmt.Sprintf("for host %d", hostID)
|
||||
|
||||
@@ -422,12 +422,12 @@ func (cp MDMAppleConfigProfile) ScreenPayloadTypes() error {
|
||||
|
||||
// HostMDMAppleProfile represents the status of an Apple MDM profile in a host.
|
||||
type HostMDMAppleProfile struct {
|
||||
HostUUID string
|
||||
CommandUUID string
|
||||
ProfileID uint
|
||||
Status *MDMAppleDeliveryStatus
|
||||
OperationType MDMAppleOperationType
|
||||
Detail string
|
||||
HostUUID string `db:"host_uuid" json:"-"`
|
||||
CommandUUID string `db:"command_uuid" json:"-"`
|
||||
ProfileID uint `db:"profile_id" json:"profile_id"`
|
||||
Status *MDMAppleDeliveryStatus `db:"status" json:"status"`
|
||||
OperationType MDMAppleOperationType `db:"operation_type" json:"operation_type"`
|
||||
Detail string `db:"detail" json:"detail"`
|
||||
}
|
||||
|
||||
type MDMAppleProfilePayload struct {
|
||||
|
||||
@@ -716,6 +716,9 @@ type Datastore interface {
|
||||
// profile id.
|
||||
DeleteMDMAppleConfigProfile(ctx context.Context, profileID uint) error
|
||||
|
||||
// GetHostMDMProfiles returns the MDM profile information for the specified host UUID.
|
||||
GetHostMDMProfiles(ctx context.Context, hostUUID string) ([]HostMDMAppleProfile, error)
|
||||
|
||||
// NewMDMAppleEnrollmentProfile creates and returns new enrollment profile.
|
||||
// Such enrollment profiles allow devices to enroll to Fleet MDM.
|
||||
NewMDMAppleEnrollmentProfile(ctx context.Context, enrollmentPayload MDMAppleEnrollmentProfilePayload) (*MDMAppleEnrollmentProfile, error)
|
||||
|
||||
@@ -223,6 +223,14 @@ type MDMHostData struct {
|
||||
// EncryptionKeyAvailable indicates if Fleet was able to retrieve and
|
||||
// decode an encryption key for the host.
|
||||
EncryptionKeyAvailable bool `json:"encryption_key_available" db:"-" csv:"-"`
|
||||
|
||||
// Profiles is a list of HostMDMProfiles for the host. Note that as for many
|
||||
// other host fields, it is not filled in by all host-returning datastore methods.
|
||||
//
|
||||
// It is a pointer to a slice so that when set, it gets marhsaled even
|
||||
// if the slice is empty, but when unset, it doesn't get marshaled
|
||||
// (e.g. we don't return that information for the List Hosts endpoint).
|
||||
Profiles *[]HostMDMAppleProfile `json:"profiles,omitempty" db:"profiles" csv:"-"`
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface for sqlx, to support unmarshaling a
|
||||
@@ -268,7 +276,7 @@ func (h Host) AuthzType() string {
|
||||
}
|
||||
|
||||
// HostDetail provides the full host metadata along with associated labels and
|
||||
// packs.
|
||||
// packs. It also includes policies, batteries, and MDM profiles, as applicable.
|
||||
type HostDetail struct {
|
||||
Host
|
||||
// Labels is the list of labels the host is a member of.
|
||||
|
||||
@@ -513,6 +513,8 @@ type ListMDMAppleConfigProfilesFunc func(ctx context.Context, teamID *uint) ([]*
|
||||
|
||||
type DeleteMDMAppleConfigProfileFunc func(ctx context.Context, profileID uint) error
|
||||
|
||||
type GetHostMDMProfilesFunc func(ctx context.Context, hostUUID string) ([]fleet.HostMDMAppleProfile, error)
|
||||
|
||||
type NewMDMAppleEnrollmentProfileFunc func(ctx context.Context, enrollmentPayload fleet.MDMAppleEnrollmentProfilePayload) (*fleet.MDMAppleEnrollmentProfile, error)
|
||||
|
||||
type GetMDMAppleEnrollmentProfileByTokenFunc func(ctx context.Context, token string) (*fleet.MDMAppleEnrollmentProfile, error)
|
||||
@@ -1307,6 +1309,9 @@ type DataStore struct {
|
||||
DeleteMDMAppleConfigProfileFunc DeleteMDMAppleConfigProfileFunc
|
||||
DeleteMDMAppleConfigProfileFuncInvoked bool
|
||||
|
||||
GetHostMDMProfilesFunc GetHostMDMProfilesFunc
|
||||
GetHostMDMProfilesFuncInvoked bool
|
||||
|
||||
NewMDMAppleEnrollmentProfileFunc NewMDMAppleEnrollmentProfileFunc
|
||||
NewMDMAppleEnrollmentProfileFuncInvoked bool
|
||||
|
||||
@@ -3122,6 +3127,11 @@ func (s *DataStore) DeleteMDMAppleConfigProfile(ctx context.Context, profileID u
|
||||
return s.DeleteMDMAppleConfigProfileFunc(ctx, profileID)
|
||||
}
|
||||
|
||||
func (s *DataStore) GetHostMDMProfiles(ctx context.Context, hostUUID string) ([]fleet.HostMDMAppleProfile, error) {
|
||||
s.GetHostMDMProfilesFuncInvoked = true
|
||||
return s.GetHostMDMProfilesFunc(ctx, hostUUID)
|
||||
}
|
||||
|
||||
func (s *DataStore) NewMDMAppleEnrollmentProfile(ctx context.Context, enrollmentPayload fleet.MDMAppleEnrollmentProfilePayload) (*fleet.MDMAppleEnrollmentProfile, error) {
|
||||
s.mu.Lock()
|
||||
s.NewMDMAppleEnrollmentProfileFuncInvoked = true
|
||||
|
||||
@@ -367,7 +367,6 @@ func (svc *Service) ListMDMAppleConfigProfiles(ctx context.Context, teamID uint)
|
||||
return nil, ctxerr.Wrap(ctx, err)
|
||||
}
|
||||
|
||||
// TODO: record activitiy
|
||||
return cps, nil
|
||||
}
|
||||
|
||||
@@ -432,7 +431,6 @@ func (svc *Service) GetMDMAppleConfigProfile(ctx context.Context, profileID uint
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: record activitiy
|
||||
return cp, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -467,6 +467,143 @@ func mcBytesForTest(name, identifier, uuid string) []byte {
|
||||
`, name, identifier, uuid))
|
||||
}
|
||||
|
||||
func TestHostDetailsMDMProfiles(t *testing.T) {
|
||||
svc, ctx, ds := setupAppleMDMService(t)
|
||||
ctx = viewer.NewContext(ctx, viewer.Viewer{User: &fleet.User{GlobalRole: ptr.String(fleet.RoleAdmin)}})
|
||||
|
||||
expected := []fleet.HostMDMAppleProfile{
|
||||
{HostUUID: "H057-UU1D-1337", ProfileID: uint(5), CommandUUID: "CMD-UU1D-5", Status: &fleet.MDMAppleDeliveryPending, OperationType: fleet.MDMAppleOperationTypeInstall, Detail: ""},
|
||||
{HostUUID: "H057-UU1D-1337", ProfileID: uint(8), CommandUUID: "CMD-UU1D-8", Status: &fleet.MDMAppleDeliveryApplied, OperationType: fleet.MDMAppleOperationTypeInstall, Detail: ""},
|
||||
{HostUUID: "H057-UU1D-1337", ProfileID: uint(13), CommandUUID: "CMD-UU1D-13", Status: &fleet.MDMAppleDeliveryFailed, OperationType: fleet.MDMAppleOperationTypeRemove, Detail: "Error removing profile"},
|
||||
}
|
||||
expectedByProfileID := make(map[uint]fleet.HostMDMAppleProfile)
|
||||
for _, ep := range expected {
|
||||
expectedByProfileID[ep.ProfileID] = ep
|
||||
}
|
||||
|
||||
ds.GetHostMDMProfilesFunc = func(ctx context.Context, hostUUID string) ([]fleet.HostMDMAppleProfile, error) {
|
||||
if hostUUID == "H057-UU1D-1337" {
|
||||
return expected, nil
|
||||
}
|
||||
return []fleet.HostMDMAppleProfile{}, nil
|
||||
}
|
||||
ds.HostFunc = func(ctx context.Context, hostID uint) (*fleet.Host, error) {
|
||||
if hostID == uint(42) {
|
||||
return &fleet.Host{ID: uint(42), UUID: "H057-UU1D-1337"}, nil
|
||||
}
|
||||
return &fleet.Host{ID: hostID, UUID: "WR0N6-UU1D"}, nil
|
||||
}
|
||||
ds.HostByIdentifierFunc = func(ctx context.Context, identifier string) (*fleet.Host, error) {
|
||||
if identifier == "h0571d3n71f13r" {
|
||||
return &fleet.Host{ID: uint(42), UUID: "H057-UU1D-1337"}, nil
|
||||
}
|
||||
return &fleet.Host{ID: uint(21), UUID: "WR0N6-UU1D"}, nil
|
||||
}
|
||||
ds.LoadHostSoftwareFunc = func(ctx context.Context, host *fleet.Host, includeCVEScores bool) error {
|
||||
return nil
|
||||
}
|
||||
ds.ListLabelsForHostFunc = func(ctx context.Context, hid uint) ([]*fleet.Label, error) {
|
||||
return nil, nil
|
||||
}
|
||||
ds.ListPacksForHostFunc = func(ctx context.Context, hid uint) (packs []*fleet.Pack, err error) {
|
||||
return nil, nil
|
||||
}
|
||||
ds.ListHostBatteriesFunc = func(ctx context.Context, id uint) ([]*fleet.HostBattery, error) {
|
||||
return nil, nil
|
||||
}
|
||||
ds.ListPoliciesForHostFunc = func(ctx context.Context, host *fleet.Host) ([]*fleet.HostPolicy, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
mdmEnabled bool
|
||||
hostID *uint
|
||||
hostIdentifier *string
|
||||
expected *[]fleet.HostMDMAppleProfile
|
||||
}{
|
||||
{
|
||||
name: "TestGetHostMDMProfilesOK",
|
||||
mdmEnabled: true,
|
||||
hostID: ptr.Uint(42),
|
||||
hostIdentifier: nil,
|
||||
expected: &expected,
|
||||
},
|
||||
{
|
||||
name: "TestGetHostMDMProfilesEmpty",
|
||||
mdmEnabled: false,
|
||||
hostID: ptr.Uint(21),
|
||||
hostIdentifier: nil,
|
||||
expected: &[]fleet.HostMDMAppleProfile{},
|
||||
},
|
||||
{
|
||||
name: "TestGetHostMDMProfilesNil",
|
||||
mdmEnabled: false,
|
||||
hostID: ptr.Uint(42),
|
||||
hostIdentifier: nil,
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "TestHostByIdentifierMDMProfilesOK",
|
||||
mdmEnabled: true,
|
||||
hostID: nil,
|
||||
hostIdentifier: ptr.String("h0571d3n71f13r"),
|
||||
expected: &expected,
|
||||
},
|
||||
{
|
||||
name: "TestHostByIdentifierMDMProfilesNil",
|
||||
mdmEnabled: false,
|
||||
hostID: nil,
|
||||
hostIdentifier: ptr.String("h0571d3n71f13r"),
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "TestHostByIdentifierMDMProfilesEmpty",
|
||||
mdmEnabled: false,
|
||||
hostID: nil,
|
||||
hostIdentifier: ptr.String("4n07h3r1d3n71f13r"),
|
||||
expected: &[]fleet.HostMDMAppleProfile{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
ds.AppConfigFunc = func(context.Context) (*fleet.AppConfig, error) {
|
||||
return &fleet.AppConfig{MDM: fleet.MDM{EnabledAndConfigured: c.mdmEnabled}}, nil
|
||||
}
|
||||
ds.AppConfigFuncInvoked = false
|
||||
ds.HostFuncInvoked = false
|
||||
ds.HostByIdentifierFuncInvoked = false
|
||||
ds.GetHostMDMProfilesFuncInvoked = false
|
||||
|
||||
var gotHost *fleet.HostDetail
|
||||
if c.hostID != nil {
|
||||
h, err := svc.GetHost(ctx, *c.hostID, fleet.HostDetailOptions{})
|
||||
require.NoError(t, err)
|
||||
require.True(t, ds.HostFuncInvoked)
|
||||
gotHost = h
|
||||
}
|
||||
if c.hostIdentifier != nil {
|
||||
h, err := svc.HostByIdentifier(ctx, *c.hostIdentifier, fleet.HostDetailOptions{})
|
||||
require.NoError(t, err)
|
||||
require.True(t, ds.HostByIdentifierFuncInvoked)
|
||||
gotHost = h
|
||||
}
|
||||
require.NotNil(t, gotHost)
|
||||
require.True(t, ds.AppConfigFuncInvoked)
|
||||
|
||||
if !c.mdmEnabled {
|
||||
require.Nil(t, gotHost.MDM.Profiles)
|
||||
return
|
||||
}
|
||||
|
||||
require.True(t, ds.GetHostMDMProfilesFuncInvoked)
|
||||
require.NotNil(t, gotHost.MDM.Profiles)
|
||||
require.ElementsMatch(t, *c.expected, *gotHost.MDM.Profiles)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppleMDMEnrollmentProfile(t *testing.T) {
|
||||
svc, ctx, _ := setupAppleMDMService(t)
|
||||
|
||||
@@ -801,7 +938,6 @@ func TestMDMCommandAndReportResultsProfileHandling(t *testing.T) {
|
||||
require.True(t, ds.GetMDMAppleCommandRequestTypeFuncInvoked)
|
||||
require.True(t, ds.UpdateHostMDMAppleProfileFuncInvoked)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestMDMBatchSetAppleProfiles(t *testing.T) {
|
||||
@@ -1079,7 +1215,6 @@ func TestMDMAppleCommander(t *testing.T) {
|
||||
pushes := make(map[string]*mdm.Push, len(targetUUIDs))
|
||||
for _, uuid := range targetUUIDs {
|
||||
pushes[uuid] = &mdm.Push{
|
||||
|
||||
PushMagic: "magic" + uuid,
|
||||
Token: []byte("token" + uuid),
|
||||
Topic: "topic" + uuid,
|
||||
@@ -1240,7 +1375,6 @@ func TestMDMAppleReconcileProfiles(t *testing.T) {
|
||||
require.True(t, ds.ListMDMAppleProfilesToRemoveFuncInvoked)
|
||||
require.True(t, ds.GetMDMAppleProfilesContentsFuncInvoked)
|
||||
require.True(t, ds.BulkUpsertMDMAppleHostProfilesFuncInvoked)
|
||||
|
||||
}
|
||||
|
||||
func mobileconfigForTest(name, identifier string) []byte {
|
||||
|
||||
@@ -795,6 +795,19 @@ func (svc *Service) getHostDetails(ctx context.Context, host *fleet.Host, opts f
|
||||
policies = &hp
|
||||
}
|
||||
|
||||
// If Fleet MDM is enabled and configured, we want to include MDM profiles.
|
||||
ac, err := svc.ds.AppConfig(ctx)
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "get app config for host mdm profiles")
|
||||
}
|
||||
if ac.MDM.EnabledAndConfigured {
|
||||
profiles, err := svc.ds.GetHostMDMProfiles(ctx, host.UUID)
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "get host mdm profiles")
|
||||
}
|
||||
host.MDM.Profiles = &profiles
|
||||
}
|
||||
|
||||
return &fleet.HostDetail{
|
||||
Host: *host,
|
||||
Labels: labels,
|
||||
|
||||
@@ -37,6 +37,9 @@ func TestHostDetails(t *testing.T) {
|
||||
Description: "the foobar label",
|
||||
},
|
||||
}
|
||||
ds.AppConfigFunc = func(ctx context.Context) (*fleet.AppConfig, error) {
|
||||
return &fleet.AppConfig{}, nil
|
||||
}
|
||||
ds.ListLabelsForHostFunc = func(ctx context.Context, hid uint) ([]*fleet.Label, error) {
|
||||
return expectedLabels, nil
|
||||
}
|
||||
@@ -83,6 +86,10 @@ func TestHostAuth(t *testing.T) {
|
||||
teamHost := &fleet.Host{TeamID: ptr.Uint(1)}
|
||||
globalHost := &fleet.Host{}
|
||||
|
||||
ds.AppConfigFunc = func(ctx context.Context) (*fleet.AppConfig, error) {
|
||||
return &fleet.AppConfig{}, nil
|
||||
}
|
||||
|
||||
ds.DeleteHostFunc = func(ctx context.Context, hid uint) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user