Add tool to get a profile as defined in Apple BM (#18274)

This commit is contained in:
Martin Angers 2024-04-15 15:11:46 -04:00 committed by GitHub
parent 3e8ebf84cf
commit ea0da5e2fa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 48 additions and 4 deletions

View file

@ -94,6 +94,26 @@ func (c *Client) SyncDevices(ctx context.Context, name string, opts ...DeviceReq
return resp, c.doWithAfterHook(ctx, name, http.MethodPost, "/devices/sync", req, resp)
}
// GetDevicesDetails uses the Apple "Get Device Details" API endpoint to
// retrieve the details (such as its assigned enrollment profile UUID) for the
// specified device, identified by its serial number.
// See https://developer.apple.com/documentation/devicemanagement/get_device_details
func (c *Client) GetDeviceDetails(ctx context.Context, name, serialNumber string) (*Device, error) {
type request struct {
Devices []string `json:"devices"`
}
type response struct {
Devices map[string]*Device `json:"devices"`
}
resp := new(response)
if err := c.doWithAfterHook(ctx, name, http.MethodPost, "/devices", request{
Devices: []string{serialNumber},
}, resp); err != nil {
return nil, err
}
return resp.Devices[serialNumber], nil
}
// IsCursorExhausted returns true if err is a DEP "exhausted cursor" error.
func IsCursorExhausted(err error) bool {
return httpErrorContains(err, http.StatusBadRequest, "EXHAUSTED_CURSOR")

View file

@ -2,7 +2,9 @@ package godep
import (
"context"
"encoding/json"
"net/http"
"net/url"
)
// Profile corresponds to the Apple DEP API "Profile" structure.
@ -75,3 +77,12 @@ func (c *Client) DefineProfile(ctx context.Context, name string, profile *Profil
resp := new(ProfileResponse)
return resp, c.doWithAfterHook(ctx, name, http.MethodPost, "/profile", profile, resp)
}
// GetProfile uses the Apple "Get a Profile" API endpoint to get the details
// for the specified profile UUID.
// See https://developer.apple.com/documentation/devicemanagement/get_a_profile
func (c *Client) GetProfile(ctx context.Context, name, profileUUID string) (*json.RawMessage, error) {
resp := &json.RawMessage{}
qs := url.Values{"profile_uuid": {profileUUID}}
return resp, c.doWithAfterHook(ctx, name, http.MethodGet, "/profile?"+qs.Encode(), nil, resp)
}

View file

@ -1,6 +1,6 @@
// Command getaccount takes an Apple Business Manager server token in decrypted
// Command applebmapi takes an Apple Business Manager server token in decrypted
// JSON format and calls the Apple BM API to retrieve and print the account
// information.
// information or the specified enrollment profile.
//
// Was implemented to test out https://github.com/fleetdm/fleet/issues/7515#issuecomment-1330889768,
// and can still be useful for debugging purposes.
@ -27,12 +27,18 @@ import (
func main() {
mysqlAddr := flag.String("mysql", "localhost:3306", "mysql address")
appleBMToken := flag.String("apple-bm-token", "", "path to (decrypted) Apple BM token")
profileUUID := flag.String("profile-uuid", "", "the Apple profile UUID to retrieve")
serialNum := flag.String("serial-number", "", "serial number of a device to get the device details")
flag.Parse()
if *appleBMToken == "" {
log.Fatal("must provide Apple BM token")
}
if *profileUUID != "" && *serialNum != "" {
log.Fatal("only one of -profile-uuid or -serial-number must be provided")
}
tok, err := os.ReadFile(*appleBMToken)
if err != nil {
log.Fatal(err)
@ -64,11 +70,18 @@ func main() {
if err != nil {
log.Fatal(err)
}
depClient := godep.NewClient(depStorage, fleethttp.NewClient())
ctx := context.Background()
res, err := depClient.AccountDetail(ctx, apple_mdm.DEPName)
var res any
switch {
case *profileUUID != "":
res, err = depClient.GetProfile(ctx, apple_mdm.DEPName, *profileUUID)
case *serialNum != "":
res, err = depClient.GetDeviceDetails(ctx, apple_mdm.DEPName, *serialNum)
default:
res, err = depClient.AccountDetail(ctx, apple_mdm.DEPName)
}
if err != nil {
log.Fatal(err)
}