mirror of
https://github.com/fleetdm/fleet
synced 2026-05-21 07:58:31 +00:00
Custom email device-mapping: implement the REST API changes (#15748)
This commit is contained in:
parent
6a3b7b8315
commit
235d2cf2dc
17 changed files with 430 additions and 55 deletions
1
changes/issue-15057-custom-email-device-mapping
Normal file
1
changes/issue-15057-custom-email-device-mapping
Normal file
|
|
@ -0,0 +1 @@
|
|||
* Added the `PUT /api/fleet/orbit/device_mapping` and `PUT /api/v1/fleet/hosts/{id}/device_mapping` endpoints (orbit-authenticated and user-authenticated) to set or replace the custom email address associated with a host.
|
||||
|
|
@ -425,7 +425,7 @@ func loadHostScheduledQueryStatsDB(ctx context.Context, db sqlx.QueryerContext,
|
|||
AND (q.team_id IS NULL OR q.team_id = ?)
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM query_results
|
||||
WHERE query_results.query_id = q.id
|
||||
WHERE query_results.query_id = q.id
|
||||
AND query_results.host_id = ?
|
||||
)
|
||||
GROUP BY q.id
|
||||
|
|
@ -952,14 +952,14 @@ func (ds *Datastore) applyHostFilters(
|
|||
) (string, []interface{}, error) {
|
||||
opt.OrderKey = defaultHostColumnTableAlias(opt.OrderKey)
|
||||
|
||||
deviceMappingJoin := `LEFT JOIN (
|
||||
deviceMappingJoin := fmt.Sprintf(`LEFT JOIN (
|
||||
SELECT
|
||||
host_id,
|
||||
CONCAT('[', GROUP_CONCAT(JSON_OBJECT('email', email, 'source', source)), ']') AS device_mapping
|
||||
CONCAT('[', GROUP_CONCAT(JSON_OBJECT('email', email, 'source', %s)), ']') AS device_mapping
|
||||
FROM
|
||||
host_emails
|
||||
GROUP BY
|
||||
host_id) dm ON dm.host_id = h.id`
|
||||
host_id) dm ON dm.host_id = h.id`, deviceMappingTranslateSourceColumn(""))
|
||||
if !opt.DeviceMapping {
|
||||
deviceMappingJoin = ""
|
||||
}
|
||||
|
|
@ -2856,21 +2856,35 @@ func (ds *Datastore) CleanupExpiredHosts(ctx context.Context) ([]uint, error) {
|
|||
}
|
||||
|
||||
func (ds *Datastore) ListHostDeviceMapping(ctx context.Context, id uint) ([]*fleet.HostDeviceMapping, error) {
|
||||
stmt := `
|
||||
return ds.listHostDeviceMappingDB(ctx, ds.reader(ctx), id)
|
||||
}
|
||||
|
||||
func deviceMappingTranslateSourceColumn(hostEmailsTableAlias string) string {
|
||||
if hostEmailsTableAlias != "" && !strings.HasSuffix(hostEmailsTableAlias, ".") {
|
||||
hostEmailsTableAlias += "."
|
||||
}
|
||||
// this means:
|
||||
// if source starts with "custom_" then return "custom" else return source as-is
|
||||
return fmt.Sprintf(` CASE WHEN %ssource LIKE '%s%%' THEN '%s' ELSE %[1]ssource END `,
|
||||
hostEmailsTableAlias, fleet.DeviceMappingCustomPrefix, fleet.DeviceMappingCustomReplacement)
|
||||
}
|
||||
|
||||
func (ds *Datastore) listHostDeviceMappingDB(ctx context.Context, q sqlx.QueryerContext, hostID uint) ([]*fleet.HostDeviceMapping, error) {
|
||||
stmt := fmt.Sprintf(`
|
||||
SELECT
|
||||
id,
|
||||
host_id,
|
||||
email,
|
||||
source
|
||||
%s as source
|
||||
FROM
|
||||
host_emails
|
||||
WHERE
|
||||
host_id = ?
|
||||
ORDER BY
|
||||
email, source`
|
||||
email, source`, deviceMappingTranslateSourceColumn(""))
|
||||
|
||||
var mappings []*fleet.HostDeviceMapping
|
||||
err := sqlx.SelectContext(ctx, ds.reader(ctx), &mappings, stmt, id)
|
||||
err := sqlx.SelectContext(ctx, q, &mappings, stmt, hostID)
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "select host emails by host id")
|
||||
}
|
||||
|
|
@ -2963,6 +2977,65 @@ func (ds *Datastore) ReplaceHostDeviceMapping(ctx context.Context, hid uint, map
|
|||
})
|
||||
}
|
||||
|
||||
func (ds *Datastore) SetOrUpdateCustomHostDeviceMapping(ctx context.Context, hostID uint, email, source string) ([]*fleet.HostDeviceMapping, error) {
|
||||
const (
|
||||
delStmt = `DELETE FROM host_emails WHERE host_id = ? AND source = ?`
|
||||
updStmt = `UPDATE host_emails SET email = ? WHERE host_id = ? AND source = ?`
|
||||
insStmt = `INSERT INTO host_emails (email, host_id, source) VALUES (?, ?, ?)`
|
||||
// for the custom_installer source, we insert it only if there is no
|
||||
// existing custom_override source for that host.
|
||||
insInstallerStmt = `INSERT INTO host_emails (email, host_id, source)
|
||||
(
|
||||
SELECT ?, ?, ?
|
||||
FROM DUAL
|
||||
WHERE
|
||||
NOT EXISTS (
|
||||
SELECT 1 FROM host_emails WHERE host_id = ? AND source = ?
|
||||
)
|
||||
)`
|
||||
)
|
||||
|
||||
err := ds.withRetryTxx(ctx, func(tx sqlx.ExtContext) error {
|
||||
if source == fleet.DeviceMappingCustomOverride {
|
||||
// must delete any existing custom installer
|
||||
if _, err := tx.ExecContext(ctx, delStmt, hostID, fleet.DeviceMappingCustomInstaller); err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "delete custom installer device mapping")
|
||||
}
|
||||
}
|
||||
|
||||
// first attempt an update, and if it updates nothing proceed with an
|
||||
// insert (this is the same logic as our insertOrUpdate helper function,
|
||||
// but it is not used directly because we may need a more complex insert
|
||||
// statement). We cannot use ON DUPLICATE KEY UPDATE because there is no
|
||||
// unique constraint on that table.
|
||||
res, err := tx.ExecContext(ctx, updStmt, email, hostID, source)
|
||||
if err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "update custom device mapping")
|
||||
}
|
||||
|
||||
rowsAffected, _ := res.RowsAffected() // cannot fail for mysql driver
|
||||
if rowsAffected == 0 {
|
||||
// use an insert, no existing email for that source
|
||||
stmt := insStmt
|
||||
params := []any{email, hostID, source}
|
||||
if source == fleet.DeviceMappingCustomInstaller {
|
||||
// custom installer can only insert if there's no custom override
|
||||
stmt = insInstallerStmt
|
||||
params = append(params, hostID, fleet.DeviceMappingCustomOverride)
|
||||
}
|
||||
if _, err := tx.ExecContext(ctx, stmt, params...); err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "insert custom device mapping")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ds.listHostDeviceMappingDB(ctx, ds.writer(ctx), hostID)
|
||||
}
|
||||
|
||||
func (ds *Datastore) ReplaceHostBatteries(ctx context.Context, hid uint, mappings []*fleet.HostBattery) error {
|
||||
for _, m := range mappings {
|
||||
if hid != m.HostID {
|
||||
|
|
@ -3347,8 +3420,6 @@ func (ds *Datastore) SetOrUpdateMDMData(
|
|||
)
|
||||
}
|
||||
|
||||
const hostEmailsSourceMdmIdpAccounts = "mdm_idp_accounts"
|
||||
|
||||
func (ds *Datastore) SetOrUpdateHostEmailsFromMdmIdpAccounts(
|
||||
ctx context.Context,
|
||||
hostID uint,
|
||||
|
|
@ -3367,7 +3438,7 @@ func (ds *Datastore) SetOrUpdateHostEmailsFromMdmIdpAccounts(
|
|||
ctx,
|
||||
`UPDATE host_emails SET email = ? WHERE host_id = ? AND source = ?`,
|
||||
`INSERT INTO host_emails (email, host_id, source) VALUES (?, ?, ?)`,
|
||||
email, hostID, hostEmailsSourceMdmIdpAccounts,
|
||||
email, hostID, fleet.DeviceMappingMDMIdpAccounts,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -124,6 +124,7 @@ func TestHosts(t *testing.T) {
|
|||
{"HostsNoSeenTime", testHostsNoSeenTime},
|
||||
{"HostDeviceMapping", testHostDeviceMapping},
|
||||
{"ReplaceHostDeviceMapping", testHostsReplaceHostDeviceMapping},
|
||||
{"CustomHostDeviceMapping", testHostsCustomHostDeviceMapping},
|
||||
{"HostMDMAndMunki", testHostMDMAndMunki},
|
||||
{"AggregatedHostMDMAndMunki", testAggregatedHostMDMAndMunki},
|
||||
{"MunkiIssuesBatchSize", testMunkiIssuesBatchSize},
|
||||
|
|
@ -4888,6 +4889,102 @@ func testHostsReplaceHostDeviceMapping(t *testing.T, ds *Datastore) {
|
|||
})
|
||||
}
|
||||
|
||||
func testHostsCustomHostDeviceMapping(t *testing.T, ds *Datastore) {
|
||||
ctx := context.Background()
|
||||
|
||||
h1, err := ds.NewHost(ctx, &fleet.Host{
|
||||
OsqueryHostID: ptr.String("1"),
|
||||
NodeKey: ptr.String("1"),
|
||||
Platform: "linux",
|
||||
Hostname: "host1",
|
||||
DetailUpdatedAt: time.Now(),
|
||||
LabelUpdatedAt: time.Now(),
|
||||
PolicyUpdatedAt: time.Now(),
|
||||
SeenTime: time.Now(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
h2, err := ds.NewHost(ctx, &fleet.Host{
|
||||
OsqueryHostID: ptr.String("2"),
|
||||
NodeKey: ptr.String("2"),
|
||||
Platform: "linux",
|
||||
Hostname: "host2",
|
||||
DetailUpdatedAt: time.Now(),
|
||||
LabelUpdatedAt: time.Now(),
|
||||
PolicyUpdatedAt: time.Now(),
|
||||
SeenTime: time.Now(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// create a custom installer email for h1
|
||||
dms, err := ds.SetOrUpdateCustomHostDeviceMapping(ctx, h1.ID, "a@b.c", fleet.DeviceMappingCustomInstaller)
|
||||
require.NoError(t, err)
|
||||
assertHostDeviceMapping(t, dms, []*fleet.HostDeviceMapping{{Email: "a@b.c", Source: fleet.DeviceMappingCustomReplacement}})
|
||||
|
||||
// custom installer can be updated
|
||||
dms, err = ds.SetOrUpdateCustomHostDeviceMapping(ctx, h1.ID, "b@b.c", fleet.DeviceMappingCustomInstaller)
|
||||
require.NoError(t, err)
|
||||
assertHostDeviceMapping(t, dms, []*fleet.HostDeviceMapping{{Email: "b@b.c", Source: fleet.DeviceMappingCustomReplacement}})
|
||||
|
||||
// set a custom override, custom installer is removed
|
||||
dms, err = ds.SetOrUpdateCustomHostDeviceMapping(ctx, h1.ID, "c@b.c", fleet.DeviceMappingCustomOverride)
|
||||
require.NoError(t, err)
|
||||
assertHostDeviceMapping(t, dms, []*fleet.HostDeviceMapping{{Email: "c@b.c", Source: fleet.DeviceMappingCustomReplacement}})
|
||||
|
||||
// updating the custom installer is now ignored
|
||||
dms, err = ds.SetOrUpdateCustomHostDeviceMapping(ctx, h1.ID, "d@b.c", fleet.DeviceMappingCustomInstaller)
|
||||
require.NoError(t, err)
|
||||
assertHostDeviceMapping(t, dms, []*fleet.HostDeviceMapping{{Email: "c@b.c", Source: fleet.DeviceMappingCustomReplacement}})
|
||||
|
||||
// updating the custom override works
|
||||
dms, err = ds.SetOrUpdateCustomHostDeviceMapping(ctx, h1.ID, "e@b.c", fleet.DeviceMappingCustomOverride)
|
||||
require.NoError(t, err)
|
||||
assertHostDeviceMapping(t, dms, []*fleet.HostDeviceMapping{{Email: "e@b.c", Source: fleet.DeviceMappingCustomReplacement}})
|
||||
|
||||
// set some unrelated emails for h2
|
||||
err = ds.ReplaceHostDeviceMapping(ctx, h2.ID, []*fleet.HostDeviceMapping{
|
||||
{HostID: h2.ID, Email: "a@c.d", Source: fleet.DeviceMappingGoogleChromeProfiles},
|
||||
{HostID: h2.ID, Email: "b@c.d", Source: fleet.DeviceMappingGoogleChromeProfiles},
|
||||
}, fleet.DeviceMappingGoogleChromeProfiles)
|
||||
require.NoError(t, err)
|
||||
|
||||
// create a custom override immediately, without a custom installer
|
||||
_, err = ds.SetOrUpdateCustomHostDeviceMapping(ctx, h2.ID, "c@c.d", fleet.DeviceMappingCustomOverride)
|
||||
require.NoError(t, err)
|
||||
|
||||
// adding a custom installer is ignored
|
||||
dms, err = ds.SetOrUpdateCustomHostDeviceMapping(ctx, h2.ID, "d@c.d", fleet.DeviceMappingCustomInstaller)
|
||||
require.NoError(t, err)
|
||||
|
||||
assertHostDeviceMapping(t, dms, []*fleet.HostDeviceMapping{
|
||||
{Email: "a@c.d", Source: fleet.DeviceMappingGoogleChromeProfiles},
|
||||
{Email: "b@c.d", Source: fleet.DeviceMappingGoogleChromeProfiles},
|
||||
{Email: "c@c.d", Source: fleet.DeviceMappingCustomReplacement},
|
||||
})
|
||||
|
||||
// updating the custom override works
|
||||
dms, err = ds.SetOrUpdateCustomHostDeviceMapping(ctx, h2.ID, "e@c.d", fleet.DeviceMappingCustomOverride)
|
||||
require.NoError(t, err)
|
||||
|
||||
assertHostDeviceMapping(t, dms, []*fleet.HostDeviceMapping{
|
||||
{Email: "a@c.d", Source: fleet.DeviceMappingGoogleChromeProfiles},
|
||||
{Email: "b@c.d", Source: fleet.DeviceMappingGoogleChromeProfiles},
|
||||
{Email: "e@c.d", Source: fleet.DeviceMappingCustomReplacement},
|
||||
})
|
||||
|
||||
// deleting the host deletes the mappings
|
||||
err = ds.DeleteHost(ctx, h2.ID)
|
||||
require.NoError(t, err)
|
||||
dms, err = ds.ListHostDeviceMapping(ctx, h2.ID)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, dms)
|
||||
|
||||
// other host was left untouched
|
||||
dms, err = ds.ListHostDeviceMapping(ctx, h1.ID)
|
||||
require.NoError(t, err)
|
||||
assertHostDeviceMapping(t, dms, []*fleet.HostDeviceMapping{{Email: "e@b.c", Source: fleet.DeviceMappingCustomReplacement}})
|
||||
}
|
||||
|
||||
func assertHostDeviceMapping(t *testing.T, got, want []*fleet.HostDeviceMapping) {
|
||||
t.Helper()
|
||||
|
||||
|
|
|
|||
|
|
@ -535,14 +535,14 @@ func (ds *Datastore) ListHostsInLabel(ctx context.Context, filter fleet.TeamFilt
|
|||
failingPoliciesJoin = ""
|
||||
}
|
||||
|
||||
deviceMappingJoin := `LEFT JOIN (
|
||||
deviceMappingJoin := fmt.Sprintf(`LEFT JOIN (
|
||||
SELECT
|
||||
host_id,
|
||||
CONCAT('[', GROUP_CONCAT(JSON_OBJECT('email', email, 'source', source)), ']') AS device_mapping
|
||||
CONCAT('[', GROUP_CONCAT(JSON_OBJECT('email', email, 'source', %s)), ']') AS device_mapping
|
||||
FROM
|
||||
host_emails
|
||||
GROUP BY
|
||||
host_id) dm ON dm.host_id = h.id`
|
||||
host_id) dm ON dm.host_id = h.id`, deviceMappingTranslateSourceColumn(""))
|
||||
if !opt.DeviceMapping {
|
||||
deviceMappingJoin = ""
|
||||
}
|
||||
|
|
@ -605,7 +605,7 @@ func (ds *Datastore) CountHostsInLabel(ctx context.Context, filter fleet.TeamFil
|
|||
query := `SELECT count(*) FROM label_membership lm
|
||||
JOIN hosts h ON (lm.host_id = h.id)
|
||||
LEFT JOIN host_seen_times hst ON (h.id=hst.host_id)
|
||||
LEFT JOIN host_disks hd ON (h.id=hd.host_id)
|
||||
LEFT JOIN host_disks hd ON (h.id=hd.host_id)
|
||||
`
|
||||
|
||||
query += hostMDMJoin
|
||||
|
|
|
|||
|
|
@ -269,6 +269,9 @@ type Datastore interface {
|
|||
CountHosts(ctx context.Context, filter TeamFilter, opt HostListOptions) (int, error)
|
||||
CountHostsInLabel(ctx context.Context, filter TeamFilter, lid uint, opt HostListOptions) (int, error)
|
||||
ListHostDeviceMapping(ctx context.Context, id uint) ([]*HostDeviceMapping, error)
|
||||
// SetOrUpdateCustomHostDeviceMapping replaces the custom email address
|
||||
// associated with the host with the provided one.
|
||||
SetOrUpdateCustomHostDeviceMapping(ctx context.Context, hostID uint, email, source string) ([]*HostDeviceMapping, error)
|
||||
// ListHostBatteries returns the list of batteries for the given host ID.
|
||||
ListHostBatteries(ctx context.Context, id uint) ([]*HostBattery, error)
|
||||
|
||||
|
|
|
|||
|
|
@ -836,6 +836,18 @@ func ExpandPlatform(platform string) []string {
|
|||
}
|
||||
}
|
||||
|
||||
// List of valid sources for HostDeviceMapping (host_emails table in the
|
||||
// database).
|
||||
const (
|
||||
DeviceMappingGoogleChromeProfiles = "google_chrome_profiles"
|
||||
DeviceMappingMDMIdpAccounts = "mdm_idp_accounts"
|
||||
DeviceMappingCustomInstaller = "custom_installer" // set by fleetd via device-authenticated API
|
||||
DeviceMappingCustomOverride = "custom_override" // set by user via user-authenticated API
|
||||
|
||||
DeviceMappingCustomPrefix = "custom_" // if host_emails.source starts with this, replace with DeviceMappingCustomReplacement
|
||||
DeviceMappingCustomReplacement = "custom" // replaces a source that starts with CustomPrefix - in the UI, we want to display those as only "custom"
|
||||
)
|
||||
|
||||
// HostDeviceMapping represents a mapping of a user email address to a host,
|
||||
// as reported by the specified source (e.g. Google Chrome Profiles).
|
||||
type HostDeviceMapping struct {
|
||||
|
|
|
|||
|
|
@ -359,6 +359,11 @@ type Service interface {
|
|||
// ListHostDeviceMapping returns the list of device-mapping of user's email address
|
||||
// for the host.
|
||||
ListHostDeviceMapping(ctx context.Context, id uint) ([]*HostDeviceMapping, error)
|
||||
// SetCustomHostDeviceMapping sets the custom email address associated with
|
||||
// the host, which is either set by the fleetd installer at startup (via a
|
||||
// device-authenticated API), or manually by the user (via the
|
||||
// user-authenticated API).
|
||||
SetCustomHostDeviceMapping(ctx context.Context, hostID uint, email string) ([]*HostDeviceMapping, error)
|
||||
|
||||
// ListDevicePolicies lists all policies for the given host, including passing / failing summaries
|
||||
ListDevicePolicies(ctx context.Context, host *Host) ([]*HostPolicy, error)
|
||||
|
|
|
|||
|
|
@ -204,6 +204,8 @@ type CountHostsInLabelFunc func(ctx context.Context, filter fleet.TeamFilter, li
|
|||
|
||||
type ListHostDeviceMappingFunc func(ctx context.Context, id uint) ([]*fleet.HostDeviceMapping, error)
|
||||
|
||||
type SetOrUpdateCustomHostDeviceMappingFunc func(ctx context.Context, hostID uint, email string, source string) ([]*fleet.HostDeviceMapping, error)
|
||||
|
||||
type ListHostBatteriesFunc func(ctx context.Context, id uint) ([]*fleet.HostBattery, error)
|
||||
|
||||
type LoadHostByDeviceAuthTokenFunc func(ctx context.Context, authToken string, tokenTTL time.Duration) (*fleet.Host, error)
|
||||
|
|
@ -1064,6 +1066,9 @@ type DataStore struct {
|
|||
ListHostDeviceMappingFunc ListHostDeviceMappingFunc
|
||||
ListHostDeviceMappingFuncInvoked bool
|
||||
|
||||
SetOrUpdateCustomHostDeviceMappingFunc SetOrUpdateCustomHostDeviceMappingFunc
|
||||
SetOrUpdateCustomHostDeviceMappingFuncInvoked bool
|
||||
|
||||
ListHostBatteriesFunc ListHostBatteriesFunc
|
||||
ListHostBatteriesFuncInvoked bool
|
||||
|
||||
|
|
@ -2588,6 +2593,13 @@ func (s *DataStore) ListHostDeviceMapping(ctx context.Context, id uint) ([]*flee
|
|||
return s.ListHostDeviceMappingFunc(ctx, id)
|
||||
}
|
||||
|
||||
func (s *DataStore) SetOrUpdateCustomHostDeviceMapping(ctx context.Context, hostID uint, email string, source string) ([]*fleet.HostDeviceMapping, error) {
|
||||
s.mu.Lock()
|
||||
s.SetOrUpdateCustomHostDeviceMappingFuncInvoked = true
|
||||
s.mu.Unlock()
|
||||
return s.SetOrUpdateCustomHostDeviceMappingFunc(ctx, hostID, email, source)
|
||||
}
|
||||
|
||||
func (s *DataStore) ListHostBatteries(ctx context.Context, id uint) ([]*fleet.HostBattery, error) {
|
||||
s.mu.Lock()
|
||||
s.ListHostBatteriesFuncInvoked = true
|
||||
|
|
|
|||
|
|
@ -495,6 +495,10 @@ func (e *authEndpointer) GET(path string, f handlerFunc, v interface{}) {
|
|||
e.handleEndpoint(path, f, v, "GET")
|
||||
}
|
||||
|
||||
func (e *authEndpointer) PUT(path string, f handlerFunc, v interface{}) {
|
||||
e.handleEndpoint(path, f, v, "PUT")
|
||||
}
|
||||
|
||||
func (e *authEndpointer) PATCH(path string, f handlerFunc, v interface{}) {
|
||||
e.handleEndpoint(path, f, v, "PATCH")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -383,6 +383,7 @@ func attachFleetAPIRoutes(r *mux.Router, svc fleet.Service, config config.FleetC
|
|||
ue.POST("/api/_version_/fleet/hosts/transfer/filter", addHostsToTeamByFilterEndpoint, addHostsToTeamByFilterRequest{})
|
||||
ue.POST("/api/_version_/fleet/hosts/{id:[0-9]+}/refetch", refetchHostEndpoint, refetchHostRequest{})
|
||||
ue.GET("/api/_version_/fleet/hosts/{id:[0-9]+}/device_mapping", listHostDeviceMappingEndpoint, listHostDeviceMappingRequest{})
|
||||
ue.PUT("/api/_version_/fleet/hosts/{id:[0-9]+}/device_mapping", putHostDeviceMappingEndpoint, putHostDeviceMappingRequest{})
|
||||
ue.GET("/api/_version_/fleet/hosts/report", hostsReportEndpoint, hostsReportRequest{})
|
||||
ue.GET("/api/_version_/fleet/os_versions", osVersionsEndpoint, osVersionsRequest{})
|
||||
ue.GET("/api/_version_/fleet/hosts/{id:[0-9]+}/queries/{query_id:[0-9]+}", getHostQueryReportEndpoint, getHostQueryReportRequest{})
|
||||
|
|
@ -652,6 +653,7 @@ func attachFleetAPIRoutes(r *mux.Router, svc fleet.Service, config config.FleetC
|
|||
// endpoints are POST due to passing the device token in the JSON body.
|
||||
oe.POST("/api/fleet/orbit/scripts/request", getOrbitScriptEndpoint, orbitGetScriptRequest{})
|
||||
oe.POST("/api/fleet/orbit/scripts/result", postOrbitScriptResultEndpoint, orbitPostScriptResultRequest{})
|
||||
oe.PUT("/api/fleet/orbit/device_mapping", putOrbitDeviceMappingEndpoint, orbitPutDeviceMappingRequest{})
|
||||
|
||||
oeWindowsMDM := oe.WithCustomMiddleware(mdmConfiguredMiddleware.VerifyWindowsMDM())
|
||||
oeWindowsMDM.POST("/api/fleet/orbit/disk_encryption_key", postOrbitDiskEncryptionKeyEndpoint, orbitPostDiskEncryptionKeyRequest{})
|
||||
|
|
|
|||
|
|
@ -1278,6 +1278,57 @@ func (svc *Service) ListHostDeviceMapping(ctx context.Context, id uint) ([]*flee
|
|||
return svc.ds.ListHostDeviceMapping(ctx, id)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Put Custom Host Device Mapping
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
type putHostDeviceMappingRequest struct {
|
||||
ID uint `url:"id"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
type putHostDeviceMappingResponse struct {
|
||||
HostID uint `json:"host_id"`
|
||||
DeviceMapping []*fleet.HostDeviceMapping `json:"device_mapping"`
|
||||
Err error `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (r putHostDeviceMappingResponse) error() error { return r.Err }
|
||||
|
||||
func putHostDeviceMappingEndpoint(ctx context.Context, request interface{}, svc fleet.Service) (errorer, error) {
|
||||
req := request.(*putHostDeviceMappingRequest)
|
||||
dms, err := svc.SetCustomHostDeviceMapping(ctx, req.ID, req.Email)
|
||||
if err != nil {
|
||||
return putHostDeviceMappingResponse{Err: err}, nil
|
||||
}
|
||||
return putHostDeviceMappingResponse{HostID: req.ID, DeviceMapping: dms}, nil
|
||||
}
|
||||
|
||||
func (svc *Service) SetCustomHostDeviceMapping(ctx context.Context, hostID uint, email string) ([]*fleet.HostDeviceMapping, error) {
|
||||
isInstallerSource := svc.authz.IsAuthenticatedWith(ctx, authzctx.AuthnOrbitToken)
|
||||
if !isInstallerSource {
|
||||
if err := svc.authz.Authorize(ctx, &fleet.Host{}, fleet.ActionList); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
host, err := svc.ds.HostLite(ctx, hostID)
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "get host")
|
||||
}
|
||||
|
||||
// Authorize again with team loaded now that we have team_id
|
||||
if err := svc.authz.Authorize(ctx, host, fleet.ActionWrite); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
source := fleet.DeviceMappingCustomOverride
|
||||
if isInstallerSource {
|
||||
source = fleet.DeviceMappingCustomInstaller
|
||||
}
|
||||
return svc.ds.SetOrUpdateCustomHostDeviceMapping(ctx, hostID, email, source)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// MDM
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -582,6 +582,9 @@ func TestHostAuth(t *testing.T) {
|
|||
ds.ListHostsLiteByIDsFunc = func(ctx context.Context, ids []uint) ([]*fleet.Host, error) {
|
||||
return nil, nil
|
||||
}
|
||||
ds.SetOrUpdateCustomHostDeviceMappingFunc = func(ctx context.Context, hostID uint, email, source string) ([]*fleet.HostDeviceMapping, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
|
|
@ -615,6 +618,14 @@ func TestHostAuth(t *testing.T) {
|
|||
true,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"team admin, belongs to team",
|
||||
&fleet.User{Teams: []fleet.UserTeam{{Team: fleet.Team{ID: 1}, Role: fleet.RoleAdmin}}},
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"team maintainer, belongs to team",
|
||||
&fleet.User{Teams: []fleet.UserTeam{{Team: fleet.Team{ID: 1}, Role: fleet.RoleMaintainer}}},
|
||||
|
|
@ -631,6 +642,14 @@ func TestHostAuth(t *testing.T) {
|
|||
true,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"team admin, DOES NOT belong to team",
|
||||
&fleet.User{Teams: []fleet.UserTeam{{Team: fleet.Team{ID: 2}, Role: fleet.RoleAdmin}}},
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"team maintainer, DOES NOT belong to team",
|
||||
&fleet.User{Teams: []fleet.UserTeam{{Team: fleet.Team{ID: 2}, Role: fleet.RoleMaintainer}}},
|
||||
|
|
@ -694,6 +713,12 @@ func TestHostAuth(t *testing.T) {
|
|||
|
||||
err = svc.RefetchHost(ctx, 1)
|
||||
checkAuthErr(t, tt.shouldFailTeamRead, err)
|
||||
|
||||
_, err = svc.SetCustomHostDeviceMapping(ctx, 1, "a@b.c")
|
||||
checkAuthErr(t, tt.shouldFailTeamWrite, err)
|
||||
|
||||
_, err = svc.SetCustomHostDeviceMapping(ctx, 2, "a@b.c")
|
||||
checkAuthErr(t, tt.shouldFailGlobalWrite, err)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3023,6 +3023,7 @@ func (s *integrationTestSuite) TestHostDeviceMapping() {
|
|||
t := s.T()
|
||||
ctx := context.Background()
|
||||
|
||||
orbitHost := createOrbitEnrolledHost(t, "windows", "device_mapping", s.ds)
|
||||
hosts := s.createHosts(t)
|
||||
|
||||
// get host device mappings of invalid host
|
||||
|
|
@ -3033,21 +3034,32 @@ func (s *integrationTestSuite) TestHostDeviceMapping() {
|
|||
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/device_mapping", hosts[0].ID), nil, http.StatusOK, &listResp)
|
||||
require.Len(t, listResp.DeviceMapping, 0)
|
||||
|
||||
// create some mappings
|
||||
// create a custom mapping of a non-existing host
|
||||
var putResp putHostDeviceMappingResponse
|
||||
s.DoJSON("PUT", fmt.Sprintf("/api/latest/fleet/hosts/%d/device_mapping", hosts[2].ID+1), nil, http.StatusNotFound, &putResp)
|
||||
|
||||
// create some google mappings
|
||||
require.NoError(t, s.ds.ReplaceHostDeviceMapping(ctx, hosts[0].ID, []*fleet.HostDeviceMapping{
|
||||
{HostID: hosts[0].ID, Email: "a@b.c", Source: "google_chrome_profiles"},
|
||||
{HostID: hosts[0].ID, Email: "b@b.c", Source: "google_chrome_profiles"},
|
||||
}, "google_chrome_profiles"))
|
||||
{HostID: hosts[0].ID, Email: "a@b.c", Source: fleet.DeviceMappingGoogleChromeProfiles},
|
||||
{HostID: hosts[0].ID, Email: "b@b.c", Source: fleet.DeviceMappingGoogleChromeProfiles},
|
||||
}, fleet.DeviceMappingGoogleChromeProfiles))
|
||||
|
||||
// create a custom mapping
|
||||
s.DoJSON("PUT", fmt.Sprintf("/api/latest/fleet/hosts/%d/device_mapping", hosts[0].ID), putHostDeviceMappingRequest{Email: "c@b.c"}, http.StatusOK, &putResp)
|
||||
require.Equal(t, hosts[0].ID, putResp.HostID)
|
||||
require.ElementsMatch(t, putResp.DeviceMapping, []*fleet.HostDeviceMapping{
|
||||
{Email: "a@b.c", Source: fleet.DeviceMappingGoogleChromeProfiles},
|
||||
{Email: "b@b.c", Source: fleet.DeviceMappingGoogleChromeProfiles},
|
||||
{Email: "c@b.c", Source: fleet.DeviceMappingCustomReplacement},
|
||||
})
|
||||
|
||||
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/device_mapping", hosts[0].ID), nil, http.StatusOK, &listResp)
|
||||
require.Len(t, listResp.DeviceMapping, 2)
|
||||
require.Equal(t, "a@b.c", listResp.DeviceMapping[0].Email)
|
||||
require.Equal(t, "google_chrome_profiles", listResp.DeviceMapping[0].Source)
|
||||
require.Zero(t, listResp.DeviceMapping[0].HostID)
|
||||
require.Equal(t, "b@b.c", listResp.DeviceMapping[1].Email)
|
||||
require.Equal(t, "google_chrome_profiles", listResp.DeviceMapping[1].Source)
|
||||
require.Zero(t, listResp.DeviceMapping[1].HostID)
|
||||
require.Equal(t, hosts[0].ID, listResp.HostID)
|
||||
require.ElementsMatch(t, listResp.DeviceMapping, []*fleet.HostDeviceMapping{
|
||||
{Email: "a@b.c", Source: fleet.DeviceMappingGoogleChromeProfiles},
|
||||
{Email: "b@b.c", Source: fleet.DeviceMappingGoogleChromeProfiles},
|
||||
{Email: "c@b.c", Source: fleet.DeviceMappingCustomReplacement},
|
||||
})
|
||||
|
||||
// other host still has none
|
||||
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/device_mapping", hosts[1].ID), nil, http.StatusOK, &listResp)
|
||||
|
|
@ -3056,7 +3068,7 @@ func (s *integrationTestSuite) TestHostDeviceMapping() {
|
|||
var listHosts listHostsResponse
|
||||
// list hosts response includes device mappings
|
||||
s.DoJSON("GET", "/api/latest/fleet/hosts?device_mapping=true", nil, http.StatusOK, &listHosts)
|
||||
require.Len(t, listHosts.Hosts, 3)
|
||||
require.Len(t, listHosts.Hosts, len(hosts)+1)
|
||||
hostsByID := make(map[uint]fleet.HostResponse)
|
||||
for _, h := range listHosts.Hosts {
|
||||
hostsByID[h.ID] = h
|
||||
|
|
@ -3069,20 +3081,31 @@ func (s *integrationTestSuite) TestHostDeviceMapping() {
|
|||
|
||||
err := json.Unmarshal(*hostsByID[host1.ID].DeviceMapping, &dm)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, dm, 2)
|
||||
|
||||
var emails []string
|
||||
for _, e := range dm {
|
||||
emails = append(emails, e.Email)
|
||||
}
|
||||
assert.Contains(t, emails, "a@b.c")
|
||||
assert.Contains(t, emails, "b@b.c")
|
||||
assert.Equal(t, "google_chrome_profiles", dm[0].Source)
|
||||
assert.Equal(t, "google_chrome_profiles", dm[1].Source)
|
||||
require.ElementsMatch(t, dm, []*fleet.HostDeviceMapping{
|
||||
{Email: "a@b.c", Source: fleet.DeviceMappingGoogleChromeProfiles},
|
||||
{Email: "b@b.c", Source: fleet.DeviceMappingGoogleChromeProfiles},
|
||||
{Email: "c@b.c", Source: fleet.DeviceMappingCustomReplacement},
|
||||
})
|
||||
|
||||
// no device mapping for other hosts
|
||||
assert.Nil(t, hostsByID[hosts[1].ID].DeviceMapping)
|
||||
assert.Nil(t, hostsByID[hosts[2].ID].DeviceMapping)
|
||||
assert.Nil(t, hostsByID[orbitHost.ID].DeviceMapping)
|
||||
|
||||
// update custom email for hosts[0]
|
||||
s.DoJSON("PUT", fmt.Sprintf("/api/latest/fleet/hosts/%d/device_mapping", hosts[0].ID), putHostDeviceMappingRequest{Email: "d@b.c"}, http.StatusOK, &putResp)
|
||||
require.Equal(t, hosts[0].ID, putResp.HostID)
|
||||
require.ElementsMatch(t, putResp.DeviceMapping, []*fleet.HostDeviceMapping{
|
||||
{Email: "a@b.c", Source: fleet.DeviceMappingGoogleChromeProfiles},
|
||||
{Email: "b@b.c", Source: fleet.DeviceMappingGoogleChromeProfiles},
|
||||
{Email: "d@b.c", Source: fleet.DeviceMappingCustomReplacement},
|
||||
})
|
||||
|
||||
// create a custom_installer email for orbit host
|
||||
s.Do("PUT", "/api/fleet/orbit/device_mapping", orbitPutDeviceMappingRequest{
|
||||
OrbitNodeKey: *orbitHost.OrbitNodeKey,
|
||||
Email: "e@b.c",
|
||||
}, http.StatusOK)
|
||||
|
||||
// search host by email address finds the corresponding host
|
||||
s.DoJSON("GET", "/api/latest/fleet/hosts?device_mapping=true", nil, http.StatusOK, &listHosts, "query", "a@b.c")
|
||||
|
|
@ -3092,17 +3115,41 @@ func (s *integrationTestSuite) TestHostDeviceMapping() {
|
|||
|
||||
err = json.Unmarshal(*listHosts.Hosts[0].DeviceMapping, &dm)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, dm, 2)
|
||||
require.ElementsMatch(t, putResp.DeviceMapping, []*fleet.HostDeviceMapping{
|
||||
{Email: "a@b.c", Source: fleet.DeviceMappingGoogleChromeProfiles},
|
||||
{Email: "b@b.c", Source: fleet.DeviceMappingGoogleChromeProfiles},
|
||||
{Email: "d@b.c", Source: fleet.DeviceMappingCustomReplacement},
|
||||
})
|
||||
|
||||
for _, e := range dm {
|
||||
emails = append(emails, e.Email)
|
||||
}
|
||||
assert.Contains(t, emails, "a@b.c")
|
||||
assert.Contains(t, emails, "b@b.c")
|
||||
assert.Equal(t, "google_chrome_profiles", dm[0].Source)
|
||||
assert.Equal(t, "google_chrome_profiles", dm[1].Source)
|
||||
// search host by the custom email address finds the corresponding host
|
||||
s.DoJSON("GET", "/api/latest/fleet/hosts?device_mapping=true", nil, http.StatusOK, &listHosts, "query", "d@b.c")
|
||||
require.Len(t, listHosts.Hosts, 1)
|
||||
require.Equal(t, hosts[0].ID, listHosts.Hosts[0].ID)
|
||||
|
||||
s.DoJSON("GET", "/api/latest/fleet/hosts?device_mapping=true", nil, http.StatusOK, &listHosts, "query", "c@b.c")
|
||||
s.DoJSON("GET", "/api/latest/fleet/hosts?device_mapping=true", nil, http.StatusOK, &listHosts, "query", "e@b.c")
|
||||
require.Len(t, listHosts.Hosts, 1)
|
||||
require.Equal(t, orbitHost.ID, listHosts.Hosts[0].ID)
|
||||
|
||||
// override the custom email for the orbit host
|
||||
s.DoJSON("PUT", fmt.Sprintf("/api/latest/fleet/hosts/%d/device_mapping", orbitHost.ID), putHostDeviceMappingRequest{Email: "f@b.c"}, http.StatusOK, &putResp)
|
||||
|
||||
// update the custom_installer email for orbit host, will get ignored (because a custom_override exists)
|
||||
s.Do("PUT", "/api/fleet/orbit/device_mapping", orbitPutDeviceMappingRequest{
|
||||
OrbitNodeKey: *orbitHost.OrbitNodeKey,
|
||||
Email: "g@b.c",
|
||||
}, http.StatusOK)
|
||||
|
||||
// searching by the old custom installer email doesn't work anymore
|
||||
s.DoJSON("GET", "/api/latest/fleet/hosts?device_mapping=true", nil, http.StatusOK, &listHosts, "query", "e@b.c")
|
||||
require.Len(t, listHosts.Hosts, 0)
|
||||
|
||||
// searching by the new custom email address finds it
|
||||
s.DoJSON("GET", "/api/latest/fleet/hosts?device_mapping=true", nil, http.StatusOK, &listHosts, "query", "f@b.c")
|
||||
require.Len(t, listHosts.Hosts, 1)
|
||||
require.Equal(t, orbitHost.ID, listHosts.Hosts[0].ID)
|
||||
|
||||
// searching by a never-used email returns nothing
|
||||
s.DoJSON("GET", "/api/latest/fleet/hosts?device_mapping=true", nil, http.StatusOK, &listHosts, "query", "Z@b.c")
|
||||
require.Len(t, listHosts.Hosts, 0)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,9 +27,11 @@ func (s *integrationTestSuite) TestDeviceAuthenticatedEndpoints() {
|
|||
|
||||
// create some mappings and MDM/Munki data
|
||||
require.NoError(t, s.ds.ReplaceHostDeviceMapping(context.Background(), hosts[0].ID, []*fleet.HostDeviceMapping{
|
||||
{HostID: hosts[0].ID, Email: "a@b.c", Source: "google_chrome_profiles"},
|
||||
{HostID: hosts[0].ID, Email: "b@b.c", Source: "google_chrome_profiles"},
|
||||
}, "google_chrome_profiles"))
|
||||
{HostID: hosts[0].ID, Email: "a@b.c", Source: fleet.DeviceMappingGoogleChromeProfiles},
|
||||
{HostID: hosts[0].ID, Email: "b@b.c", Source: fleet.DeviceMappingGoogleChromeProfiles},
|
||||
}, fleet.DeviceMappingGoogleChromeProfiles))
|
||||
_, err = s.ds.SetOrUpdateCustomHostDeviceMapping(context.Background(), hosts[0].ID, "c@b.c", fleet.DeviceMappingCustomInstaller)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, s.ds.SetOrUpdateMDMData(context.Background(), hosts[0].ID, false, true, "url", false, "", ""))
|
||||
require.NoError(t, s.ds.SetOrUpdateMunkiInfo(context.Background(), hosts[0].ID, "1.3.0", nil, nil))
|
||||
// create a battery for hosts[0]
|
||||
|
|
@ -107,7 +109,12 @@ func (s *integrationTestSuite) TestDeviceAuthenticatedEndpoints() {
|
|||
require.NoError(t, json.NewDecoder(res.Body).Decode(&listDMResp))
|
||||
require.NoError(t, res.Body.Close())
|
||||
require.Equal(t, hosts[0].ID, listDMResp.HostID)
|
||||
require.Len(t, listDMResp.DeviceMapping, 2)
|
||||
require.Len(t, listDMResp.DeviceMapping, 3)
|
||||
require.ElementsMatch(t, listDMResp.DeviceMapping, []*fleet.HostDeviceMapping{
|
||||
{Email: "a@b.c", Source: fleet.DeviceMappingGoogleChromeProfiles},
|
||||
{Email: "b@b.c", Source: fleet.DeviceMappingGoogleChromeProfiles},
|
||||
{Email: "c@b.c", Source: fleet.DeviceMappingCustomReplacement},
|
||||
})
|
||||
devDMs := listDMResp.DeviceMapping
|
||||
|
||||
// compare response with standard list device mapping API for that same host
|
||||
|
|
|
|||
|
|
@ -6730,7 +6730,7 @@ func (s *integrationMDMTestSuite) TestSSO() {
|
|||
}
|
||||
source, ok := sourceByEmail["sso_user@example.com"]
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "mdm_idp_accounts", source)
|
||||
require.Equal(t, fleet.DeviceMappingMDMIdpAccounts, source)
|
||||
source, ok = sourceByEmail["g1@example.com"]
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "google_chrome_profiles", source)
|
||||
|
|
@ -6762,7 +6762,7 @@ func (s *integrationMDMTestSuite) TestSSO() {
|
|||
s.DoJSON("GET", fmt.Sprintf("/api/v1/fleet/hosts/%d/device_mapping", hostResp.Host.ID), nil, http.StatusOK, &dmResp)
|
||||
require.Len(t, dmResp.DeviceMapping, 1)
|
||||
require.Equal(t, "sso_user@example.com", dmResp.DeviceMapping[0].Email)
|
||||
require.Equal(t, "mdm_idp_accounts", dmResp.DeviceMapping[0].Source)
|
||||
require.Equal(t, fleet.DeviceMappingMDMIdpAccounts, dmResp.DeviceMapping[0].Source)
|
||||
hostsResp = listHostsResponse{}
|
||||
s.DoJSON("GET", fmt.Sprintf("/api/v1/fleet/hosts?query=%s&device_mapping=true", url.QueryEscape("sso_user@example.com")), nil, http.StatusOK, &hostsResp)
|
||||
require.Len(t, hostsResp.Hosts, 1)
|
||||
|
|
@ -6773,7 +6773,7 @@ func (s *integrationMDMTestSuite) TestSSO() {
|
|||
require.NoError(t, json.Unmarshal(*gotHost.DeviceMapping, &dm))
|
||||
require.Len(t, dm, 1)
|
||||
require.Equal(t, "sso_user@example.com", dm[0].Email)
|
||||
require.Equal(t, "mdm_idp_accounts", dm[0].Source)
|
||||
require.Equal(t, fleet.DeviceMappingMDMIdpAccounts, dm[0].Source)
|
||||
|
||||
// enrolling a different user works without problems
|
||||
res = s.LoginMDMSSOUser("sso_user2", "user123#")
|
||||
|
|
|
|||
|
|
@ -519,6 +519,44 @@ func (svc *Service) SaveHostScriptResult(ctx context.Context, result *fleet.Host
|
|||
return fleet.ErrMissingLicense
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// Post Orbit device mapping (custom email)
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
type orbitPutDeviceMappingRequest struct {
|
||||
OrbitNodeKey string `json:"orbit_node_key"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
// interface implementation required by the OrbitClient
|
||||
func (r *orbitPutDeviceMappingRequest) setOrbitNodeKey(nodeKey string) {
|
||||
r.OrbitNodeKey = nodeKey
|
||||
}
|
||||
|
||||
// interface implementation required by orbit authentication
|
||||
func (r *orbitPutDeviceMappingRequest) orbitHostNodeKey() string {
|
||||
return r.OrbitNodeKey
|
||||
}
|
||||
|
||||
type orbitPutDeviceMappingResponse struct {
|
||||
Err error `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (r orbitPutDeviceMappingResponse) error() error { return r.Err }
|
||||
|
||||
func putOrbitDeviceMappingEndpoint(ctx context.Context, request interface{}, svc fleet.Service) (errorer, error) {
|
||||
req := request.(*orbitPutDeviceMappingRequest)
|
||||
|
||||
host, ok := hostctx.FromContext(ctx)
|
||||
if !ok {
|
||||
err := newOsqueryError("internal error: missing host from request context")
|
||||
return orbitPutDeviceMappingResponse{Err: err}, nil
|
||||
}
|
||||
|
||||
_, err := svc.SetCustomHostDeviceMapping(ctx, host.ID, req.Email)
|
||||
return orbitPutDeviceMappingResponse{Err: err}, nil
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// Post Orbit disk encryption key
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -1079,10 +1079,10 @@ func directIngestChromeProfiles(ctx context.Context, logger log.Logger, host *fl
|
|||
mapping = append(mapping, &fleet.HostDeviceMapping{
|
||||
HostID: host.ID,
|
||||
Email: row["email"],
|
||||
Source: "google_chrome_profiles",
|
||||
Source: fleet.DeviceMappingGoogleChromeProfiles,
|
||||
})
|
||||
}
|
||||
return ds.ReplaceHostDeviceMapping(ctx, host.ID, mapping, "google_chrome_profiles")
|
||||
return ds.ReplaceHostDeviceMapping(ctx, host.ID, mapping, fleet.DeviceMappingGoogleChromeProfiles)
|
||||
}
|
||||
|
||||
func directIngestBattery(ctx context.Context, logger log.Logger, host *fleet.Host, ds fleet.Datastore, rows []map[string]string) error {
|
||||
|
|
|
|||
Loading…
Reference in a new issue