mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 08:55:24 +00:00
Add macOS DDM protocol endpoints for tokens
, declaration-items
, and declaration/.../...
(#17679)
This commit is contained in:
parent
93f040f7da
commit
3d73174e90
@ -390,7 +390,7 @@ func (c *TestAppleMDMClient) TokenUpdate() error {
|
||||
// The endpoint argument is used as the value for the `Endpoint` key in the request payload.
|
||||
//
|
||||
// For more details check https://developer.apple.com/documentation/devicemanagement/declarativemanagementrequest
|
||||
func (c *TestAppleMDMClient) DeclarativeManagement(endpoint string) error {
|
||||
func (c *TestAppleMDMClient) DeclarativeManagement(endpoint string) (*http.Response, error) {
|
||||
payload := map[string]any{
|
||||
"MessageType": "DeclarativeManagement",
|
||||
"UDID": c.UUID,
|
||||
@ -398,8 +398,8 @@ func (c *TestAppleMDMClient) DeclarativeManagement(endpoint string) error {
|
||||
"EnrollmentID": "testenrollmentid-" + c.UUID,
|
||||
"Endpoint": endpoint,
|
||||
}
|
||||
_, err := c.request("application/x-apple-aspen-mdm-checkin", payload)
|
||||
return err
|
||||
r, err := c.request("application/x-apple-aspen-mdm-checkin", payload)
|
||||
return r, err
|
||||
}
|
||||
|
||||
// Checkout sends the CheckOut message to the MDM server.
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
@ -3081,6 +3082,64 @@ WHERE h.uuid = ?
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ds *Datastore) MDMAppleDDMSynchronizationTokens(ctx context.Context, teamID uint) (*fleet.MDMAppleDDMSyncTokens, error) {
|
||||
const stmt = `
|
||||
SELECT
|
||||
md5_checksum,
|
||||
latest_created_timestamp
|
||||
FROM
|
||||
team_declaration_checksum_view
|
||||
WHERE
|
||||
team_id = ?`
|
||||
|
||||
var res fleet.MDMAppleDDMSyncTokens
|
||||
if err := sqlx.GetContext(ctx, ds.reader(ctx), &res, stmt, teamID); err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "get DDM checksum by team id")
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (ds *Datastore) MDMAppleDDMDeclarationItems(ctx context.Context, teamID uint) ([]fleet.MDMAppleDDMDeclarationItem, error) {
|
||||
// TODO: Confirm whether we can use JSON functions in the query (if 5.7 officially unsupported
|
||||
// by Fleet)
|
||||
const stmt = `
|
||||
SELECT
|
||||
mad.md5_checksum as server_token,
|
||||
identifier,
|
||||
declaration_type,
|
||||
tv.md5_checksum as declarations_token
|
||||
FROM
|
||||
mdm_apple_declarations mad
|
||||
JOIN team_declaration_checksum_view tv ON mad.team_id = tv.team_id
|
||||
WHERE
|
||||
mad.team_id = ?`
|
||||
|
||||
var res []fleet.MDMAppleDDMDeclarationItem
|
||||
if err := sqlx.SelectContext(ctx, ds.reader(ctx), &res, stmt, teamID); err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "get DDM checksum by team id")
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (ds *Datastore) MDMAppleDDMDeclarationPayload(ctx context.Context, declarationType fleet.MDMAppleDeclarationType, identifier string, teamID uint) (json.RawMessage, error) {
|
||||
const stmt = `
|
||||
SELECT
|
||||
declaration
|
||||
FROM
|
||||
mdm_apple_declarations
|
||||
WHERE
|
||||
team_id = ? AND identifier = ? AND declaration_type = ?`
|
||||
|
||||
var res json.RawMessage
|
||||
if err := sqlx.GetContext(ctx, ds.reader(ctx), &res, stmt, teamID, identifier, declarationType); err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "get ddm declaration")
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (ds *Datastore) MDMAppleRecordDeclarativeCheckIn(ctx context.Context, hostUUID string, result []byte) error {
|
||||
err := ds.withRetryTxx(ctx, func(tx sqlx.ExtContext) error {
|
||||
res, err := tx.ExecContext(
|
||||
@ -3150,6 +3209,9 @@ UPDATE
|
||||
|
||||
return ctxerr.Wrap(ctx, err, "updating nano_command_results")
|
||||
})
|
||||
if err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "saving declarative management response")
|
||||
}
|
||||
|
||||
return ctxerr.Wrap(ctx, err, "saving declarative management response")
|
||||
return nil
|
||||
}
|
||||
|
@ -609,3 +609,67 @@ type MDMAppleHostDeclaration struct {
|
||||
// either by the MDM protocol or the Fleet server.
|
||||
Detail string `db:"detail" json:"detail"`
|
||||
}
|
||||
|
||||
// MDMAppleDDMTokensResponse is the response from the DDM tokens endpoint.
|
||||
//
|
||||
// https://developer.apple.com/documentation/devicemanagement/tokensresponse
|
||||
type MDMAppleDDMTokensResponse struct {
|
||||
SyncTokens MDMAppleDDMSyncTokens
|
||||
}
|
||||
|
||||
// MDMAppleDDMSyncTokens is dictionary describes the state of declarations on the server.
|
||||
//
|
||||
// https://developer.apple.com/documentation/devicemanagement/synchronizationtokens
|
||||
type MDMAppleDDMSyncTokens struct {
|
||||
DeclarationsToken string `db:"md5_checksum"`
|
||||
Timestamp time.Time `db:"latest_created_timestamp"`
|
||||
}
|
||||
|
||||
// MDMAppleDDMDeclarationItemsResponse is the response from the DDM declaration items endpoint.
|
||||
//
|
||||
// https://developer.apple.com/documentation/devicemanagement/declarationitemsresponse
|
||||
type MDMAppleDDMDeclarationItemsResponse struct {
|
||||
Declarations MDMAppleDDMManifestItems
|
||||
DeclarationsToken string
|
||||
}
|
||||
|
||||
// MDMAppleDDMManifestItems is a dictionary that contains the lists of declarations available on the
|
||||
// server.
|
||||
//
|
||||
// https://developer.apple.com/documentation/devicemanagement/declarationitemsresponse/manifestdeclarationitems
|
||||
type MDMAppleDDMManifestItems struct {
|
||||
Activations []MDMAppleDDMManifest
|
||||
Assets []MDMAppleDDMManifest
|
||||
Configurations []MDMAppleDDMManifest
|
||||
Management []MDMAppleDDMManifest
|
||||
}
|
||||
|
||||
// MDMAppleDDMManifest is a dictionary that describes a declaration.
|
||||
//
|
||||
// https://developer.apple.com/documentation/devicemanagement/declarationitemsresponse/manifestdeclarationitems
|
||||
type MDMAppleDDMManifest struct {
|
||||
Identifier string
|
||||
ServerToken string
|
||||
}
|
||||
|
||||
// MDMAppleDDMDeclarationItem represents a declaration item in the datastore. It is used to
|
||||
// construct the DDM `declaration-items` endpoint response.
|
||||
//
|
||||
// https://developer.apple.com/documentation/devicemanagement/declarationitemsresponse
|
||||
type MDMAppleDDMDeclarationItem struct {
|
||||
Identifier string `db:"identifier"`
|
||||
DeclarationType string `db:"declaration_type"`
|
||||
DeclarationsToken string `db:"declarations_token"`
|
||||
ServerToken string `db:"server_token"`
|
||||
}
|
||||
|
||||
// MDMAppleDDMDeclarationResponse represents a declaration in the datastore. It is used for the DDM
|
||||
// `declaration/.../...` enpoint response.
|
||||
//
|
||||
// https://developer.apple.com/documentation/devicemanagement/declarationresponse
|
||||
type MDMAppleDDMDeclarationResponse struct {
|
||||
Identifier string `db:"identifier"`
|
||||
Type string `db:"type"`
|
||||
Payload json.RawMessage `db:"payload"`
|
||||
ServerToken string `db:"server_token"`
|
||||
}
|
||||
|
@ -1157,6 +1157,14 @@ type Datastore interface {
|
||||
// serials.
|
||||
UpdateDEPAssignProfileRetryPending(ctx context.Context, jobID uint, serials []string) error
|
||||
|
||||
// MDMAppleDDMSynchronizationTokens returns the token used to synchronize declarations for the
|
||||
// specified team or no team.
|
||||
MDMAppleDDMSynchronizationTokens(ctx context.Context, teamID uint) (*MDMAppleDDMSyncTokens, error)
|
||||
// MDMAppleDDMDeclarationItems returns the declaration items for the specified team or no team.
|
||||
MDMAppleDDMDeclarationItems(ctx context.Context, teamID uint) ([]MDMAppleDDMDeclarationItem, error)
|
||||
// MDMAppleDDMDeclarationPayload returns the declaration payload for the specified identifier and team.
|
||||
MDMAppleDDMDeclarationPayload(ctx context.Context, declarationType MDMAppleDeclarationType, identifier string, teamID uint) (json.RawMessage, error)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Microsoft MDM
|
||||
|
||||
|
@ -758,6 +758,12 @@ type GetDEPAssignProfileExpiredCooldownsFunc func(ctx context.Context) (map[uint
|
||||
|
||||
type UpdateDEPAssignProfileRetryPendingFunc func(ctx context.Context, jobID uint, serials []string) error
|
||||
|
||||
type MDMAppleDDMSynchronizationTokensFunc func(ctx context.Context, teamID uint) (*fleet.MDMAppleDDMSyncTokens, error)
|
||||
|
||||
type MDMAppleDDMDeclarationItemsFunc func(ctx context.Context, teamID uint) ([]fleet.MDMAppleDDMDeclarationItem, error)
|
||||
|
||||
type MDMAppleDDMDeclarationPayloadFunc func(ctx context.Context, declarationType fleet.MDMAppleDeclarationType, identifier string, teamID uint) (json.RawMessage, error)
|
||||
|
||||
type WSTEPStoreCertificateFunc func(ctx context.Context, name string, crt *x509.Certificate) error
|
||||
|
||||
type WSTEPNewSerialFunc func(ctx context.Context) (*big.Int, error)
|
||||
@ -1971,6 +1977,15 @@ type DataStore struct {
|
||||
UpdateDEPAssignProfileRetryPendingFunc UpdateDEPAssignProfileRetryPendingFunc
|
||||
UpdateDEPAssignProfileRetryPendingFuncInvoked bool
|
||||
|
||||
MDMAppleDDMSynchronizationTokensFunc MDMAppleDDMSynchronizationTokensFunc
|
||||
MDMAppleDDMSynchronizationTokensFuncInvoked bool
|
||||
|
||||
MDMAppleDDMDeclarationItemsFunc MDMAppleDDMDeclarationItemsFunc
|
||||
MDMAppleDDMDeclarationItemsFuncInvoked bool
|
||||
|
||||
MDMAppleDDMDeclarationPayloadFunc MDMAppleDDMDeclarationPayloadFunc
|
||||
MDMAppleDDMDeclarationPayloadFuncInvoked bool
|
||||
|
||||
WSTEPStoreCertificateFunc WSTEPStoreCertificateFunc
|
||||
WSTEPStoreCertificateFuncInvoked bool
|
||||
|
||||
@ -4717,6 +4732,27 @@ func (s *DataStore) UpdateDEPAssignProfileRetryPending(ctx context.Context, jobI
|
||||
return s.UpdateDEPAssignProfileRetryPendingFunc(ctx, jobID, serials)
|
||||
}
|
||||
|
||||
func (s *DataStore) MDMAppleDDMSynchronizationTokens(ctx context.Context, teamID uint) (*fleet.MDMAppleDDMSyncTokens, error) {
|
||||
s.mu.Lock()
|
||||
s.MDMAppleDDMSynchronizationTokensFuncInvoked = true
|
||||
s.mu.Unlock()
|
||||
return s.MDMAppleDDMSynchronizationTokensFunc(ctx, teamID)
|
||||
}
|
||||
|
||||
func (s *DataStore) MDMAppleDDMDeclarationItems(ctx context.Context, teamID uint) ([]fleet.MDMAppleDDMDeclarationItem, error) {
|
||||
s.mu.Lock()
|
||||
s.MDMAppleDDMDeclarationItemsFuncInvoked = true
|
||||
s.mu.Unlock()
|
||||
return s.MDMAppleDDMDeclarationItemsFunc(ctx, teamID)
|
||||
}
|
||||
|
||||
func (s *DataStore) MDMAppleDDMDeclarationPayload(ctx context.Context, declarationType fleet.MDMAppleDeclarationType, identifier string, teamID uint) (json.RawMessage, error) {
|
||||
s.mu.Lock()
|
||||
s.MDMAppleDDMDeclarationPayloadFuncInvoked = true
|
||||
s.mu.Unlock()
|
||||
return s.MDMAppleDDMDeclarationPayloadFunc(ctx, declarationType, identifier, teamID)
|
||||
}
|
||||
|
||||
func (s *DataStore) WSTEPStoreCertificate(ctx context.Context, name string, crt *x509.Certificate) error {
|
||||
s.mu.Lock()
|
||||
s.WSTEPStoreCertificateFuncInvoked = true
|
||||
|
@ -2974,27 +2974,40 @@ func (svc *MDMAppleDDMService) DeclarativeManagement(r *mdm.Request, dm *mdm.Dec
|
||||
}
|
||||
level.Debug(svc.logger).Log("msg", "ddm request received", "endpoint", dm.Endpoint)
|
||||
|
||||
if dm.UDID == "" {
|
||||
return nil, ctxerr.New(r.Context, "missing device id")
|
||||
}
|
||||
|
||||
h, err := svc.ds.HostLiteByIdentifier(r.Context, dm.UDID)
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(r.Context, err, "getting host by identifier")
|
||||
}
|
||||
var tid uint
|
||||
if h.TeamID != nil {
|
||||
tid = *h.TeamID
|
||||
}
|
||||
|
||||
switch {
|
||||
case dm.Endpoint == "tokens":
|
||||
level.Debug(svc.logger).Log("msg", "received tokens request")
|
||||
// TODO: Should we record the checkin for all endpoints or just tokens?
|
||||
if err := svc.ds.MDMAppleRecordDeclarativeCheckIn(r.Context, dm.UDID, dm.Raw); err != nil {
|
||||
return nil, ctxerr.Wrap(r.Context, err, "recording declarative checkin")
|
||||
}
|
||||
// TODO(sarah): handle tokens
|
||||
level.Debug(svc.logger).Log("msg", "received tokens request")
|
||||
return nil, nil
|
||||
|
||||
return svc.handleTokens(r.Context, tid)
|
||||
|
||||
case dm.Endpoint == "declaration-items":
|
||||
// TODO(sarah): handle declaration-items
|
||||
level.Debug(svc.logger).Log("msg", "received declaration-items request")
|
||||
return nil, nil
|
||||
return svc.handleDeclarationItems(r.Context, tid)
|
||||
|
||||
case dm.Endpoint == "status":
|
||||
// TODO(roberto): handle status
|
||||
level.Debug(svc.logger).Log("msg", "received status request")
|
||||
// TODO(roberto): handle status
|
||||
|
||||
return nil, nil
|
||||
|
||||
case strings.HasPrefix(dm.Endpoint, "declarations"):
|
||||
// TODO(sarah): handle declarations
|
||||
level.Debug(svc.logger).Log("msg", "received declarations request")
|
||||
parts := strings.Split(dm.Endpoint, "/")
|
||||
if len(parts) != 3 {
|
||||
@ -3003,9 +3016,79 @@ func (svc *MDMAppleDDMService) DeclarativeManagement(r *mdm.Request, dm *mdm.Dec
|
||||
declarationType := parts[1]
|
||||
declarationIdentifier := parts[2]
|
||||
level.Debug(svc.logger).Log("msg", "parsed declarations request", "type", declarationType, "identifier", declarationIdentifier)
|
||||
return nil, nil
|
||||
|
||||
// TODO: Validate declarationType?
|
||||
d, err := svc.ds.MDMAppleDDMDeclarationPayload(r.Context, fleet.MDMAppleDeclarationType("com.apple."+declarationType), declarationIdentifier, tid)
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(r.Context, err, "getting declaration")
|
||||
}
|
||||
b, err := json.Marshal(d)
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(r.Context, err, "marshaling declaration")
|
||||
}
|
||||
return b, nil
|
||||
|
||||
default:
|
||||
return nil, ctxerr.New(r.Context, "unrecognized ddm endpoint")
|
||||
}
|
||||
}
|
||||
|
||||
func (svc *MDMAppleDDMService) handleTokens(ctx context.Context, teamID uint) ([]byte, error) {
|
||||
tok, err := svc.ds.MDMAppleDDMSynchronizationTokens(ctx, teamID)
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "getting synchronization tokens")
|
||||
}
|
||||
|
||||
b, err := json.Marshal(fleet.MDMAppleDDMTokensResponse{
|
||||
SyncTokens: *tok,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "marshaling synchronization tokens")
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (svc *MDMAppleDDMService) handleDeclarationItems(ctx context.Context, teamID uint) ([]byte, error) {
|
||||
di, err := svc.ds.MDMAppleDDMDeclarationItems(ctx, teamID)
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "getting synchronization tokens")
|
||||
}
|
||||
|
||||
var dTok string
|
||||
activations := []fleet.MDMAppleDDMManifest{}
|
||||
configurations := []fleet.MDMAppleDDMManifest{}
|
||||
for _, d := range di {
|
||||
if dTok == "" {
|
||||
dTok = d.DeclarationsToken
|
||||
} else if dTok != d.DeclarationsToken {
|
||||
level.Debug(svc.logger).Log("msg", "inconsistent declarations token", "expected", dTok, "got", d.DeclarationsToken)
|
||||
}
|
||||
|
||||
manifest := fleet.MDMAppleDDMManifest{Identifier: d.Identifier, ServerToken: d.ServerToken}
|
||||
switch d.DeclarationType {
|
||||
case string(fleet.MDMAppleDeclarativeActivation):
|
||||
activations = append(activations, manifest)
|
||||
case string(fleet.MDMAppleDeclarativeConfiguration):
|
||||
configurations = append(configurations, manifest)
|
||||
default:
|
||||
level.Debug(svc.logger).Log("msg", "unrecognized declaration type", "type", d.DeclarationType)
|
||||
return nil, ctxerr.New(ctx, "unrecognized declaration type")
|
||||
}
|
||||
}
|
||||
|
||||
b, err := json.Marshal(fleet.MDMAppleDDMDeclarationItemsResponse{
|
||||
Declarations: fleet.MDMAppleDDMManifestItems{
|
||||
Activations: activations,
|
||||
Configurations: configurations,
|
||||
Assets: []fleet.MDMAppleDDMManifest{},
|
||||
Management: []fleet.MDMAppleDDMManifest{},
|
||||
},
|
||||
DeclarationsToken: dTok,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "marshaling synchronization tokens")
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
@ -12522,23 +12522,246 @@ func (s *integrationMDMTestSuite) TestIsServerBitlockerStatus() {
|
||||
// TODO(sarah): Build out this test
|
||||
func (s *integrationMDMTestSuite) TestMDMAppleDeviceManagementRequests() {
|
||||
t := s.T()
|
||||
mdmDevice := mdmtest.NewTestMDMClientAppleDirect(mdmtest.AppleEnrollInfo{
|
||||
SCEPChallenge: s.fleetCfg.MDM.AppleSCEPChallenge,
|
||||
SCEPURL: s.server.URL + apple_mdm.SCEPPath,
|
||||
MDMURL: s.server.URL + apple_mdm.MDMPath,
|
||||
_, mdmDevice := createHostThenEnrollMDM(s.ds, s.server.URL, t)
|
||||
|
||||
insertDeclaration := func(t *testing.T, decl fleet.MDMAppleDeclaration) {
|
||||
stmt := `
|
||||
INSERT INTO mdm_apple_declarations (
|
||||
declaration_uuid,
|
||||
team_id,
|
||||
identifier,
|
||||
name,
|
||||
declaration_type,
|
||||
declaration,
|
||||
md5_checksum,
|
||||
created_at,
|
||||
uploaded_at
|
||||
) VALUES (?,?,?,?,?,?,?,?,?)`
|
||||
|
||||
mysql.ExecAdhocSQL(t, s.ds, func(q sqlx.ExtContext) error {
|
||||
_, err := q.ExecContext(context.Background(), stmt,
|
||||
decl.DeclarationUUID,
|
||||
decl.TeamID,
|
||||
decl.Identifier,
|
||||
decl.Name,
|
||||
decl.DeclarationType,
|
||||
decl.Declaration,
|
||||
decl.MD5Checksum,
|
||||
decl.CreatedAt,
|
||||
decl.UploadedAt,
|
||||
)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// initialize a time to use for our first declaration, subsequent declarations will be
|
||||
// incremented by a minute
|
||||
then := time.Now().UTC().Truncate(time.Second).Add(-1 * time.Hour)
|
||||
|
||||
// insert a declaration with no team
|
||||
noTeamDeclsByUUID := map[string]fleet.MDMAppleDeclaration{
|
||||
"123": {
|
||||
DeclarationUUID: "123",
|
||||
TeamID: ptr.Uint(0),
|
||||
Identifier: "com.example",
|
||||
Name: "Example",
|
||||
DeclarationType: fleet.MDMAppleDeclarativeConfiguration,
|
||||
Declaration: json.RawMessage(`{"foo": "bar"}`),
|
||||
MD5Checksum: "csum123",
|
||||
CreatedAt: then,
|
||||
UploadedAt: then,
|
||||
},
|
||||
}
|
||||
insertDeclaration(t, noTeamDeclsByUUID["123"])
|
||||
|
||||
mapDeclsByChecksum := func(byUUID map[string]fleet.MDMAppleDeclaration) map[string]fleet.MDMAppleDeclaration {
|
||||
byChecksum := make(map[string]fleet.MDMAppleDeclaration)
|
||||
for _, d := range byUUID {
|
||||
byChecksum[d.MD5Checksum] = byUUID[d.DeclarationUUID]
|
||||
}
|
||||
return byChecksum
|
||||
}
|
||||
|
||||
parseTokensResp := func(r *http.Response) fleet.MDMAppleDDMTokensResponse {
|
||||
require.NotNil(t, r)
|
||||
b, err := io.ReadAll(r.Body)
|
||||
require.NoError(t, err)
|
||||
defer r.Body.Close()
|
||||
r.Body = io.NopCloser(bytes.NewBuffer(b))
|
||||
// t.Log("body", string(b))
|
||||
|
||||
// unmarsal the response to make sure it's valid
|
||||
var tok fleet.MDMAppleDDMTokensResponse
|
||||
err = json.NewDecoder(r.Body).Decode(&tok)
|
||||
require.NoError(t, err)
|
||||
// t.Log("decoded", tok)
|
||||
|
||||
return tok
|
||||
}
|
||||
|
||||
parseDeclarationItemsResp := func(r *http.Response) fleet.MDMAppleDDMDeclarationItemsResponse {
|
||||
require.NotNil(t, r)
|
||||
b, err := io.ReadAll(r.Body)
|
||||
require.NoError(t, err)
|
||||
defer r.Body.Close()
|
||||
r.Body = io.NopCloser(bytes.NewBuffer(b))
|
||||
// t.Log("body", string(b))
|
||||
|
||||
// unmarsal the response to make sure it's valid
|
||||
var di fleet.MDMAppleDDMDeclarationItemsResponse
|
||||
err = json.NewDecoder(r.Body).Decode(&di)
|
||||
require.NoError(t, err)
|
||||
// t.Log("decoded", di)
|
||||
|
||||
return di
|
||||
}
|
||||
|
||||
parseDeclarationResp := func(r *http.Response, expectedBytes []byte) fleet.MDMAppleDDMDeclarationResponse {
|
||||
require.NotNil(t, r)
|
||||
b, err := io.ReadAll(r.Body)
|
||||
require.NoError(t, err)
|
||||
defer r.Body.Close()
|
||||
if expectedBytes != nil {
|
||||
require.Equal(t, expectedBytes, b)
|
||||
}
|
||||
r.Body = io.NopCloser(bytes.NewBuffer(b))
|
||||
// t.Log("body", string(b))
|
||||
|
||||
// unmarsal the response to make sure it's valid
|
||||
var d fleet.MDMAppleDDMDeclarationResponse
|
||||
err = json.NewDecoder(r.Body).Decode(&d)
|
||||
require.NoError(t, err)
|
||||
// t.Logf("decoded: %+v", d)
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
checkTokensResp := func(t *testing.T, r fleet.MDMAppleDDMTokensResponse, expectedTimestamp time.Time, prevToken string) {
|
||||
require.Equal(t, expectedTimestamp, r.SyncTokens.Timestamp)
|
||||
require.NotEmpty(t, r.SyncTokens.DeclarationsToken)
|
||||
require.NotEqual(t, prevToken, r.SyncTokens.DeclarationsToken)
|
||||
}
|
||||
|
||||
checkDeclarationItemsResp := func(t *testing.T, r fleet.MDMAppleDDMDeclarationItemsResponse, expectedDeclTok string, expectedDeclsByChecksum map[string]fleet.MDMAppleDeclaration) {
|
||||
require.Equal(t, expectedDeclTok, r.DeclarationsToken)
|
||||
require.Empty(t, r.Declarations.Activations)
|
||||
require.Empty(t, r.Declarations.Assets)
|
||||
require.Empty(t, r.Declarations.Management)
|
||||
require.Len(t, r.Declarations.Configurations, len(expectedDeclsByChecksum))
|
||||
for _, m := range r.Declarations.Configurations {
|
||||
// look up the declaration by the server token (we trim the token to the first seven
|
||||
// chars to match our keys because response is padded to length 16 with "\u000")
|
||||
d, ok := expectedDeclsByChecksum[m.ServerToken[0:7]]
|
||||
require.True(t, ok)
|
||||
require.Equal(t, d.Identifier, m.Identifier)
|
||||
}
|
||||
}
|
||||
|
||||
var currDeclToken string // we'll use this to track the expected token across tests
|
||||
|
||||
t.Run("Tokens", func(t *testing.T) {
|
||||
// get tokens, timestamp should be the same as the declaration and token should be non-empty
|
||||
r, err := mdmDevice.DeclarativeManagement("tokens")
|
||||
require.NoError(t, err)
|
||||
parsed := parseTokensResp(r)
|
||||
checkTokensResp(t, parsed, then, "")
|
||||
currDeclToken = parsed.SyncTokens.DeclarationsToken
|
||||
|
||||
// insert a new declaration
|
||||
noTeamDeclsByUUID["456"] = fleet.MDMAppleDeclaration{
|
||||
DeclarationUUID: "456",
|
||||
TeamID: ptr.Uint(0),
|
||||
Identifier: "com.example2",
|
||||
Name: "Example2",
|
||||
DeclarationType: fleet.MDMAppleDeclarativeConfiguration,
|
||||
Declaration: json.RawMessage(`{"foo": "baz"}`),
|
||||
MD5Checksum: "csum456",
|
||||
CreatedAt: then.Add(1 * time.Minute),
|
||||
UploadedAt: then.Add(1 * time.Minute),
|
||||
}
|
||||
insertDeclaration(t, noTeamDeclsByUUID["456"])
|
||||
|
||||
// get tokens again, timestamp and token should have changed
|
||||
r, err = mdmDevice.DeclarativeManagement("tokens")
|
||||
require.NoError(t, err)
|
||||
parsed = parseTokensResp(r)
|
||||
checkTokensResp(t, parsed, then.Add(1*time.Minute), currDeclToken)
|
||||
currDeclToken = parsed.SyncTokens.DeclarationsToken
|
||||
})
|
||||
err := mdmDevice.Enroll()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = mdmDevice.DeclarativeManagement("tokens")
|
||||
require.NoError(t, err)
|
||||
t.Run("DeclarationItems", func(t *testing.T) {
|
||||
r, err := mdmDevice.DeclarativeManagement("declaration-items")
|
||||
require.NoError(t, err)
|
||||
checkDeclarationItemsResp(t, parseDeclarationItemsResp(r), currDeclToken, mapDeclsByChecksum(noTeamDeclsByUUID))
|
||||
|
||||
err = mdmDevice.DeclarativeManagement("declaration-items")
|
||||
require.NoError(t, err)
|
||||
// insert a new declaration
|
||||
noTeamDeclsByUUID["789"] = fleet.MDMAppleDeclaration{
|
||||
DeclarationUUID: "789",
|
||||
TeamID: ptr.Uint(0),
|
||||
Identifier: "com.example3",
|
||||
Name: "Example3",
|
||||
DeclarationType: fleet.MDMAppleDeclarativeConfiguration,
|
||||
Declaration: json.RawMessage(`{"foo": "bang"}`),
|
||||
MD5Checksum: "csum789",
|
||||
CreatedAt: then.Add(2 * time.Minute),
|
||||
UploadedAt: then.Add(2 * time.Minute),
|
||||
}
|
||||
insertDeclaration(t, noTeamDeclsByUUID["789"])
|
||||
|
||||
err = mdmDevice.DeclarativeManagement("status")
|
||||
require.NoError(t, err)
|
||||
// get tokens again, timestamp and token should have changed
|
||||
r, err = mdmDevice.DeclarativeManagement("tokens")
|
||||
require.NoError(t, err)
|
||||
toks := parseTokensResp(r)
|
||||
checkTokensResp(t, toks, then.Add(2*time.Minute), currDeclToken)
|
||||
currDeclToken = toks.SyncTokens.DeclarationsToken
|
||||
|
||||
err = mdmDevice.DeclarativeManagement("declarations/foo/bar")
|
||||
require.NoError(t, err)
|
||||
r, err = mdmDevice.DeclarativeManagement("declaration-items")
|
||||
require.NoError(t, err)
|
||||
checkDeclarationItemsResp(t, parseDeclarationItemsResp(r), currDeclToken, mapDeclsByChecksum(noTeamDeclsByUUID))
|
||||
})
|
||||
|
||||
t.Run("Status", func(t *testing.T) {
|
||||
_, err := mdmDevice.DeclarativeManagement("status")
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("Declaration", func(t *testing.T) {
|
||||
want := noTeamDeclsByUUID["123"]
|
||||
wantBytes, err := json.Marshal(want.Declaration)
|
||||
require.NoError(t, err)
|
||||
r, err := mdmDevice.DeclarativeManagement(fmt.Sprintf("declarations/%s/%s", "configuration", want.Identifier))
|
||||
require.NoError(t, err)
|
||||
|
||||
_ = parseDeclarationResp(r, wantBytes)
|
||||
|
||||
// insert a new declaration
|
||||
noTeamDeclsByUUID["abc"] = fleet.MDMAppleDeclaration{
|
||||
DeclarationUUID: "abc",
|
||||
TeamID: ptr.Uint(0),
|
||||
Identifier: "com.example4",
|
||||
Name: "Example4",
|
||||
DeclarationType: fleet.MDMAppleDeclarativeConfiguration,
|
||||
Declaration: json.RawMessage(`{
|
||||
"Type": "com.apple.configuration.test",
|
||||
"Payload": {"foo":"bar"},
|
||||
"Identifier": "com.example4",
|
||||
"ServerToken": "csumabc"
|
||||
}`),
|
||||
MD5Checksum: "csumabc",
|
||||
CreatedAt: then.Add(3 * time.Minute),
|
||||
UploadedAt: then.Add(3 * time.Minute),
|
||||
}
|
||||
insertDeclaration(t, noTeamDeclsByUUID["abc"])
|
||||
want = noTeamDeclsByUUID["abc"]
|
||||
wantBytes, err = json.Marshal(want.Declaration)
|
||||
require.NoError(t, err)
|
||||
r, err = mdmDevice.DeclarativeManagement(fmt.Sprintf("declarations/%s/%s", "configuration", want.Identifier))
|
||||
require.NoError(t, err)
|
||||
|
||||
d := parseDeclarationResp(r, wantBytes)
|
||||
require.Equal(t, want.Identifier, d.Identifier)
|
||||
require.Equal(t, "com.apple.configuration.test", d.Type)
|
||||
require.Equal(t, json.RawMessage(`{"foo":"bar"}`), d.Payload)
|
||||
require.Equal(t, want.MD5Checksum, d.ServerToken)
|
||||
})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user