From ef127f9aadbeefb29b422776950dea874e84721a Mon Sep 17 00:00:00 2001 From: Victor Vrantchan Date: Sat, 8 Sep 2018 18:39:31 +0000 Subject: [PATCH] move the dep library into the core repo (#504) The dep library was initially a self contained project, but doesn't have a community of users. Keeping it in the main repo should reduce the maintenance burden and simplify dependency management for the project. --- Makefile | 3 +- cmd/mdmctl/apply_dep_profile.go | 4 +- dep/account.go | 32 ++++ dep/client.go | 218 ++++++++++++++++++++++ dep/depsync/depsync.go | 14 +- dep/depsync/event.go | 2 +- dep/devices.go | 121 ++++++++++++ dep/profile.go | 92 +++++++++ dep/testdata/account_response.json | 136 ++++++++++++++ dep/testdata/device_details_response.json | 18 ++ dep/testdata/device_response.json | 20 ++ dep/testdata/empty_sync_response.json | 6 + dep/testdata/fetch_profile_response.json | 20 ++ go.mod | 3 +- go.sum | 2 - platform/config/deptoken.go | 11 +- platform/dep/define_profile.go | 3 +- platform/dep/get_account_info.go | 3 +- platform/dep/get_dep_device.go | 5 +- platform/dep/get_dep_profile.go | 3 +- platform/dep/service.go | 14 +- server/server.go | 38 ++-- 22 files changed, 723 insertions(+), 45 deletions(-) create mode 100644 dep/account.go create mode 100644 dep/client.go create mode 100644 dep/devices.go create mode 100644 dep/profile.go create mode 100644 dep/testdata/account_response.json create mode 100644 dep/testdata/device_details_response.json create mode 100644 dep/testdata/device_response.json create mode 100644 dep/testdata/empty_sync_response.json create mode 100644 dep/testdata/fetch_profile_response.json diff --git a/Makefile b/Makefile index 1bd395ff..4b1dd3b4 100644 --- a/Makefile +++ b/Makefile @@ -41,7 +41,8 @@ BUILD_VERSION = "\ -X github.com/micromdm/go4/version.buildUser=${USER} \ -X github.com/micromdm/go4/version.buildDate=${NOW} \ -X github.com/micromdm/go4/version.revision=${REVISION} \ - -X github.com/micromdm/go4/version.goVersion=${GOVERSION}" + -X github.com/micromdm/go4/version.goVersion=${GOVERSION} \ + -X github.com/micromdm/micromdm/dep.version=${VERSION}" gomodcheck: @go help mod > /dev/null || (@echo micromdm requires Go version 1.11 or higher && exit 1) diff --git a/cmd/mdmctl/apply_dep_profile.go b/cmd/mdmctl/apply_dep_profile.go index 913fbf07..e08fe122 100644 --- a/cmd/mdmctl/apply_dep_profile.go +++ b/cmd/mdmctl/apply_dep_profile.go @@ -10,10 +10,10 @@ import ( "net/url" "os" - "github.com/micromdm/dep" - "github.com/micromdm/micromdm/dep/depsync" "github.com/pkg/errors" + "github.com/micromdm/micromdm/dep" + "github.com/micromdm/micromdm/dep/depsync" "github.com/micromdm/micromdm/pkg/crypto" ) diff --git a/dep/account.go b/dep/account.go new file mode 100644 index 00000000..a59ab020 --- /dev/null +++ b/dep/account.go @@ -0,0 +1,32 @@ +package dep + +import "github.com/pkg/errors" + +const accountBasePath = "account" + +type Account struct { + ServerName string `json:"server_name"` + ServerUUID string `json:"server_uuid"` + AdminID string `json:"admin_id"` + FacilitatorID string `json:"facilitator_id,omitempty"` //deprecated + OrgName string `json:"org_name"` + OrgEmail string `json:"org_email"` + OrgPhone string `json:"org_phone"` + OrgAddress string `json:"org_address"` + OrgID string `json"org_id"` + OrgIDHash string `json"org_id_hash"` + URLs []string `json:"urls"` + OrgType string `json:"org_type"` + OrgVersion string `json:"org_version"` +} + +func (c *Client) Account() (*Account, error) { + var account Account + req, err := c.newRequest("GET", accountBasePath, nil) + if err != nil { + return nil, errors.Wrap(err, "create account request") + } + + err = c.do(req, &account) + return &account, errors.Wrap(err, "account request") +} diff --git a/dep/client.go b/dep/client.go new file mode 100644 index 00000000..e18cfc50 --- /dev/null +++ b/dep/client.go @@ -0,0 +1,218 @@ +package dep + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/http/httputil" + "net/url" + "path" + "strings" + "time" + + "github.com/garyburd/go-oauth/oauth" + "github.com/pkg/errors" +) + +var version = "dev" + +const ( + defaultBaseURL = "https://mdmenrollment.apple.com" + mediaType = "application/json;charset=UTF8" + XServerProtocolVersionHeader = "X-Server-Protocol-Version" + XServerProtocolVersion = "3" +) + +type HTTPClient interface { + Do(req *http.Request) (*http.Response, error) +} + +type Option func(*Client) + +func WithServerURL(baseURL *url.URL) Option { + return func(c *Client) { + c.baseURL = baseURL + } +} + +func WithHTTPClient(client HTTPClient) Option { + return func(c *Client) { + c.client = client + } +} + +type Client struct { + consumerKey string //given by apple + consumerSecret string //given by apple + accessToken string //given by apple + accessSecret string //given by apple + authSessionToken string //requested from DEP using above credentials + sessionExpires time.Time + + userAgent string + client HTTPClient + + baseURL *url.URL +} + +type OAuthParameters struct { + ConsumerKey string `json:"consumer_key"` + ConsumerSecret string `json:"consumer_secret"` + AccessToken string `json:"access_token"` + AccessSecret string `json:"access_secret"` +} + +func NewClient(p OAuthParameters, opts ...Option) *Client { + baseURL, _ := url.Parse(defaultBaseURL) + client := Client{ + consumerKey: p.ConsumerKey, + consumerSecret: p.ConsumerSecret, + accessToken: p.AccessToken, + accessSecret: p.AccessSecret, + client: http.DefaultClient, + userAgent: path.Join("micromdm", version), + baseURL: baseURL, + } + for _, optFn := range opts { + optFn(&client) + } + return &client +} + +func (c *Client) session() error { + if c.authSessionToken == "" { + if err := c.newSession(); err != nil { + return errors.Wrap(err, "creating new auth session for dep") + } + } + + if time.Now().After(c.sessionExpires) { + if err := c.newSession(); err != nil { + return errors.Wrap(err, "refreshing expired dep session") + } + } + return nil +} + +func (c *Client) newSession() error { + var authSessionToken struct { + AuthSessionToken string `json:"auth_session_token"` + } + consumerCredentials := oauth.Credentials{ + Token: c.consumerKey, + Secret: c.consumerSecret, + } + + accessCredentials := &oauth.Credentials{ + Token: c.accessToken, + Secret: c.accessSecret, + } + form := url.Values{} + + rel, err := url.Parse("/session") + if err != nil { + return err + } + sessionURL := c.baseURL.ResolveReference(rel) + + oauthClient := oauth.Client{ + SignatureMethod: oauth.HMACSHA1, + TokenRequestURI: sessionURL.String(), + Credentials: consumerCredentials, + } + + // create request + req, err := http.NewRequest("GET", oauthClient.TokenRequestURI, strings.NewReader(form.Encode())) + if err != nil { + return err + } + + // set Authorization Header + if err := oauthClient.SetAuthorizationHeader( + req.Header, + accessCredentials, + "GET", + req.URL, + form, + ); err != nil { + return err + } + // add headers + req.Header.Add("User-Agent", c.userAgent) + req.Header.Add("Content-Type", mediaType) + req.Header.Add("Accept", mediaType) + req.Header.Add(XServerProtocolVersionHeader, XServerProtocolVersion) + + // get Authorization Header + resp, err := c.client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + // check resp statuscode + if resp.StatusCode != http.StatusOK { + return errors.Errorf("establishing DEP session: %v", resp.Status) + } + + if err := json.NewDecoder(resp.Body).Decode(&authSessionToken); err != nil { + return errors.Wrap(err, "decode authSessionToken from response") + } + + // set token and expiration value + c.authSessionToken = authSessionToken.AuthSessionToken + c.sessionExpires = time.Now().Add(time.Minute * 3) + return nil +} + +// NewRequest creates a DEP request +func (c *Client) newRequest(method, urlStr string, body interface{}) (*http.Request, error) { + rel, err := url.Parse(urlStr) + if err != nil { + return nil, errors.Wrapf(err, "parse dep request url %s", urlStr) + } + + u := c.baseURL.ResolveReference(rel) + var buf bytes.Buffer + if body != nil { + if err := json.NewEncoder(&buf).Encode(body); err != nil { + return nil, errors.Wrap(err, "encode http body for DEP request") + } + } + + req, err := http.NewRequest(method, u.String(), &buf) + if err != nil { + return nil, errors.Wrapf(err, "creating %s request to dep %s", method, u.String()) + } + + req.Header.Add("User-Agent", c.userAgent) + req.Header.Add("Content-Type", mediaType) + req.Header.Add("Accept", mediaType) + req.Header.Add(XServerProtocolVersionHeader, XServerProtocolVersion) + return req, nil +} + +func (c *Client) do(req *http.Request, into interface{}) error { + if err := c.session(); err != nil { + return errors.Wrapf(err, "get session for request to %s", c.baseURL.String()) + } + req.Header.Add("X-ADM-Auth-Session", c.authSessionToken) + + out, _ := httputil.DumpRequestOut(req, false) + fmt.Println(string(out)) + resp, err := c.client.Do(req) + if err != nil { + return errors.Wrap(err, "perform dep request") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + body, _ := ioutil.ReadAll(resp.Body) + return errors.Errorf("unexpected dep response. status=%d DEP API Error: %s", resp.StatusCode, string(body)) + } + err = json.NewDecoder(resp.Body).Decode(into) + return errors.Wrap(err, "decode DEP response body") + +} diff --git a/dep/depsync/depsync.go b/dep/depsync/depsync.go index bd9f4904..2a59299c 100644 --- a/dep/depsync/depsync.go +++ b/dep/depsync/depsync.go @@ -10,9 +10,9 @@ import ( "github.com/boltdb/bolt" "github.com/go-kit/kit/log" "github.com/go-kit/kit/log/level" - "github.com/micromdm/dep" "github.com/pkg/errors" + "github.com/micromdm/micromdm/dep" conf "github.com/micromdm/micromdm/platform/config" "github.com/micromdm/micromdm/platform/pubsub" ) @@ -39,7 +39,7 @@ type AutoAssigner struct { type watcher struct { mtx sync.RWMutex logger log.Logger - client dep.Client + client Client publisher pubsub.Publisher conf *config @@ -61,9 +61,15 @@ func (c cursor) Valid() bool { return true } +type Client interface { + FetchDevices(...dep.DeviceRequestOption) (*dep.DeviceResponse, error) + SyncDevices(string, ...dep.DeviceRequestOption) (*dep.DeviceResponse, error) + AssignProfile(string, ...string) (*dep.ProfileResponse, error) +} + type Option func(*watcher) -func WithClient(client dep.Client) Option { +func WithClient(client Client) Option { return func(w *watcher) { w.client = client } @@ -226,7 +232,7 @@ func (w *watcher) processAutoAssign(devices []dep.Device) error { } for profileUUID, serials := range assignments { - resp, err := w.client.AssignProfile(profileUUID, serials) + resp, err := w.client.AssignProfile(profileUUID, serials...) if err != nil { level.Info(w.logger).Log( "err", err, diff --git a/dep/depsync/event.go b/dep/depsync/event.go index 9e32163a..0792e85f 100644 --- a/dep/depsync/event.go +++ b/dep/depsync/event.go @@ -4,9 +4,9 @@ import ( "time" "github.com/gogo/protobuf/proto" - "github.com/micromdm/dep" uuid "github.com/satori/go.uuid" + "github.com/micromdm/micromdm/dep" "github.com/micromdm/micromdm/dep/depsync/internal/depsyncproto" ) diff --git a/dep/devices.go b/dep/devices.go new file mode 100644 index 00000000..437391b1 --- /dev/null +++ b/dep/devices.go @@ -0,0 +1,121 @@ +package dep + +import ( + "time" + + "github.com/pkg/errors" +) + +const ( + fetchDevicesPath = "server/devices" + syncDevicesPath = "devices/sync" + deviceDetailsPath = "devices" +) + +type Device struct { + SerialNumber string `json:"serial_number"` + Model string `json:"model"` + Description string `json:"description"` + Color string `json:"color"` + AssetTag string `json:"asset_tag,omitempty"` + ProfileStatus string `json:"profile_status"` + ProfileUUID string `json:"profile_uuid,omitempty"` + ProfileAssignTime time.Time `json:"profile_assign_time,omitempty"` + ProfilePushTime time.Time `json:"profile_push_time,omitempty"` + DeviceAssignedDate time.Time `json:"device_assigned_date,omitempty"` + DeviceAssignedBy string `json:"device_assigned_by,omitempty"` + OS string `json:"os,omitempty"` + DeviceFamily string `json:"device_family,omitempty"` + // sync fields + OpType string `json:"op_type,omitempty"` + OpDate time.Time `json:"op_date,omitempty"` + // details fields + ResponseStatus string `json:"response_status,omitempty"` +} + +// DeviceRequestOption is an optional parameter for the DeviceService API. +// The option can be used to set Cursor or Limit options for the request. +type DeviceRequestOption func(*deviceRequestOpts) error + +type deviceRequestOpts struct { + Cursor string `json:"cursor,omitempty"` + Limit int `json:"limit,omitempty"` +} + +// Cursor is an optional argument that can be added to FetchDevices +func Cursor(cursor string) DeviceRequestOption { + return func(opts *deviceRequestOpts) error { + opts.Cursor = cursor + return nil + } +} + +// Limit is an optional argument that can be passed to FetchDevices and SyncDevices +func Limit(limit int) DeviceRequestOption { + return func(opts *deviceRequestOpts) error { + if limit > 1000 { + return errors.New("limit must not be higher than 1000") + } + opts.Limit = limit + return nil + } +} + +type DeviceResponse struct { + Devices []Device `json:"devices"` + Cursor string `json:"cursor"` + FetchedUntil time.Time `json:"fetched_until"` + MoreToFollow bool `json:"more_to_follow"` +} + +func (c *Client) FetchDevices(opts ...DeviceRequestOption) (*DeviceResponse, error) { + request := &deviceRequestOpts{} + for _, option := range opts { + if err := option(request); err != nil { + return nil, err + } + } + var response DeviceResponse + req, err := c.newRequest("POST", fetchDevicesPath, request) + if err != nil { + return nil, errors.Wrap(err, "create fetch devices request") + } + err = c.do(req, &response) + return &response, errors.Wrap(err, "fetch devices") +} + +func (c *Client) SyncDevices(cursor string, opts ...DeviceRequestOption) (*DeviceResponse, error) { + request := &deviceRequestOpts{Cursor: cursor} + for _, option := range opts { + if err := option(request); err != nil { + return nil, err + } + } + var response DeviceResponse + req, err := c.newRequest("POST", syncDevicesPath, request) + if err != nil { + return nil, errors.Wrap(err, "create sync devices request") + } + err = c.do(req, &response) + return &response, errors.Wrap(err, "sync devices") +} + +type DeviceDetailsResponse struct { + Devices map[string]Device `json:"devices"` +} + +func (c *Client) DeviceDetails(serials ...string) (*DeviceDetailsResponse, error) { + request := struct { + Devices []string `json:"devices"` + }{ + Devices: serials, + } + + var response DeviceDetailsResponse + req, err := c.newRequest("POST", deviceDetailsPath, request) + if err != nil { + return nil, errors.Wrap(err, "create device details request") + } + err = c.do(req, &response) + return &response, errors.Wrap(err, "get device details") +} diff --git a/dep/profile.go b/dep/profile.go new file mode 100644 index 00000000..570d2b95 --- /dev/null +++ b/dep/profile.go @@ -0,0 +1,92 @@ +package dep + +import "github.com/pkg/errors" + +const ( + defineProfilePath = "profile" + assignProfilePath = "profile/devices" +) + +type Profile struct { + ProfileName string `json:"profile_name"` + ProfileUUID string `json:"profile_uuid,omitempty"` + URL string `json:"url"` + AllowPairing bool `json:"allow_pairing,omitempty"` + IsSupervised bool `json:"is_supervised,omitempty"` + IsMultiUser bool `json:"is_multi_user,omitempty"` + IsMandatory bool `json:"is_mandatory,omitempty"` + AwaitDeviceConfigured bool `json:"await_device_configured,omitempty"` + IsMDMRemovable bool `json:"is_mdm_removable"` + SupportPhoneNumber string `json:"support_phone_number,omitempty"` + AutoAdvanceSetup bool `json:"auto_advance_setup,omitempty"` + SupportEmailAddress string `json:"support_email_address,omitempty"` + OrgMagic string `json:"org_magic"` + AnchorCerts []string `json:"anchor_certs,omitempty"` + SupervisingHostCerts []string `json:"supervising_host_certs,omitempty"` + SkipSetupItems []string `json:"skip_setup_items,omitempty"` + Department string `json:"department,omitempty"` + Devices []string `json:"devices"` + Language string `json:"language,omitempty"` + Region string `json:"region,omitempty"` +} + +type ProfileResponse struct { + ProfileUUID string `json:"profile_uuid"` + Devices map[string]string `json:"devices"` +} + +func (c *Client) DefineProfile(request *Profile) (*ProfileResponse, error) { + var response ProfileResponse + req, err := c.newRequest("POST", defineProfilePath, request) + if err != nil { + return nil, errors.Wrap(err, "create define profile request") + } + err = c.do(req, &response) + return &response, errors.Wrap(err, "define profile") +} + +func (c *Client) AssignProfile(uuid string, serials ...string) (*ProfileResponse, error) { + var response ProfileResponse + var request = struct { + ProfileUUID string `json:"profile_uuid"` + Devices []string `json:"devices"` + }{ + ProfileUUID: uuid, + Devices: serials, + } + + req, err := c.newRequest("PUT", assignProfilePath, request) + if err != nil { + return nil, errors.Wrap(err, "create assign profile request") + } + err = c.do(req, &response) + return &response, errors.Wrap(err, "assign profile") +} + +func (c *Client) FetchProfile(uuid string) (*Profile, error) { + var response Profile + req, err := c.newRequest("GET", defineProfilePath, nil) + if err != nil { + return nil, errors.Wrap(err, "create fetch profile request") + } + query := req.URL.Query() + query.Add("profile_uuid", uuid) + req.URL.RawQuery = query.Encode() + + err = c.do(req, &response) + return &response, errors.Wrap(err, "fetch profile") +} + +func (c *Client) RemoveProfile(serials ...string) (map[string]string, error) { + var response map[string]string + var request = struct { + Devices []string `json:"devices"` + }{Devices: serials} + + req, err := c.newRequest("DELETE", assignProfilePath, &request) + if err != nil { + return nil, errors.Wrap(err, "create fetch profile request") + } + err = c.do(req, &response) + return response, errors.Wrap(err, "remove profile") +} diff --git a/dep/testdata/account_response.json b/dep/testdata/account_response.json new file mode 100644 index 00000000..f6a0170b --- /dev/null +++ b/dep/testdata/account_response.json @@ -0,0 +1,136 @@ +{ + "server_name": "dev", + "server_uuid": "6666ED66666666666666666666666666", + "facilitator_id": "apple-dep@acme.co", + "admin_id": "apple-dep@acme.co", + "org_name": "Acme, Inc", + "org_email": "apple-dep@acme.co", + "org_phone": "8775277454", + "org_address": "1600 Pennsylvania Ave NW, Washington, DC 20500", + "org_id": "6666666", + "org_id_hash": "01cpx35ceg4bt5jzrvs6s0h18201cpx35ceg4bt5jzrvs6s0h182", + "urls": [ + { + "uri": "/session", + "http_method": [ + "GET" + ] + }, + { + "uri": "/devices", + "http_method": [ + "POST" + ] + }, + { + "uri": "/roster/class/person/sync", + "http_method": [ + "POST" + ] + }, + { + "uri": "/server/devices", + "http_method": [ + "POST" + ], + "limit": { + "default": 1000, + "maximum": 5000 + } + }, + { + "uri": "/roster/class", + "http_method": [ + "POST" + ] + }, + { + "uri": "/roster/course/sync", + "http_method": [ + "POST" + ] + }, + { + "uri": "/devices/sync", + "http_method": [ + "POST" + ], + "limit": { + "default": 1000, + "maximum": 5000 + } + }, + { + "uri": "/roster/class/person", + "http_method": [ + "POST" + ] + }, + { + "uri": "/profile/devices", + "http_method": [ + "DELETE", + "POST", + "PUT" + ] + }, + { + "uri": "/roster/class/location", + "http_method": [ + "POST" + ] + }, + { + "uri": "/roster/class/sync", + "http_method": [ + "POST" + ] + }, + { + "uri": "/account", + "http_method": [ + "GET" + ] + }, + { + "uri": "/roster/class/location/sync", + "http_method": [ + "POST" + ] + }, + { + "uri": "/devices/disown", + "http_method": [ + "POST" + ] + }, + { + "uri": "/roster/course", + "http_method": [ + "POST" + ] + }, + { + "uri": "/profile/tvprovider", + "http_method": [ + "POST", + "GET" + ] + }, + { + "uri": "/device/activationlock", + "http_method": [ + "POST" + ] + }, + { + "uri": "/profile", + "http_method": [ + "POST", + "GET" + ] + } + ], + "org_type": "org", + "org_version": "v2" +} diff --git a/dep/testdata/device_details_response.json b/dep/testdata/device_details_response.json new file mode 100644 index 00000000..89aeab7b --- /dev/null +++ b/dep/testdata/device_details_response.json @@ -0,0 +1,18 @@ +{ + "devices": { + "C02T0HL0GGC0": { + "serial_number": "C02T0HL0GGC0", + "description": "MBP 13.3 SPACE GRAY/2.0GHZ/8GB/256GB-USA", + "model": "MacBook Pro", + "os": "OSX", + "device_family": "Mac", + "color": "SPACE GRAY", + "profile_uuid": "C1F123098EB5074580041E69883004C3", + "profile_assign_time": "2018-09-08T16:49:09Z", + "profile_status": "assigned", + "device_assigned_by": "apple-dep@acme.co", + "device_assigned_date": "2017-10-16T01:55:30Z", + "response_status": "SUCCESS" + } + } +} diff --git a/dep/testdata/device_response.json b/dep/testdata/device_response.json new file mode 100644 index 00000000..e6833aef --- /dev/null +++ b/dep/testdata/device_response.json @@ -0,0 +1,20 @@ +{ + "devices": [ + { + "serial_number": "C02T0HL0GGC0", + "description": "MBP 13.3 SPACE GRAY/2.0GHZ/8GB/256GB-USA", + "model": "MacBook Pro", + "os": "OSX", + "device_family": "Mac", + "color": "SPACE GRAY", + "profile_uuid": "C1F123098EB5074580041E69883004C3", + "profile_assign_time": "2018-09-08T16:49:09Z", + "profile_status": "assigned", + "device_assigned_by": "apple-dep@acme.co", + "device_assigned_date": "2017-10-16T01:55:30Z" + } + ], + "fetched_until": "2018-09-08T17:28:18Z", + "more_to_follow": false, + "cursor": "MDowOjE1MzY0Mjc2OTg5MTQ6MTUzNjQyNzY5ODkxNDp0cnVlOjE1MzY0Mjc2OTg5MTQ" +} diff --git a/dep/testdata/empty_sync_response.json b/dep/testdata/empty_sync_response.json new file mode 100644 index 00000000..02828dff --- /dev/null +++ b/dep/testdata/empty_sync_response.json @@ -0,0 +1,6 @@ +{ + "devices": [], + "fetched_until": "2018-09-08T17:41:29Z", + "more_to_follow": false, + "cursor": "MDowOjE1MzY0Mjg0ODkzMDc6MTUzNjQyODQ4OTMwNzp0cnVlOjE1MzY0Mjg0ODkzMDc" +} diff --git a/dep/testdata/fetch_profile_response.json b/dep/testdata/fetch_profile_response.json new file mode 100644 index 00000000..62612fcb --- /dev/null +++ b/dep/testdata/fetch_profile_response.json @@ -0,0 +1,20 @@ +{ + "support_phone_number": "(Optional) +1 877 527 7454", + "support_email_address": "(Optional) support@example.com", + "department": "(Optional) support@example.com", + "org_magic": "0729093D-D02C-4D8B-89DC-4EF900AED5B2", + "url": "https://mdm.acme.co/mdm/enroll", + "is_supervised": false, + "allow_pairing": true, + "is_mandatory": false, + "is_mdm_removable": true, + "await_device_configured": true, + "is_multi_user": false, + "auto_advance_setup": false, + "skip_setup_items": [ + "AppleID", + "Android" + ], + "profile_uuid": "C1F123098EB5074580041E69883004C3", + "profile_name": "default" +} diff --git a/go.mod b/go.mod index 47b7a9e4..672456d4 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ require ( github.com/RobotsAndPencils/buford v0.12.0 github.com/boltdb/bolt v1.3.1 github.com/fullsailor/pkcs7 v0.0.0-20180824154052-36585635cb64 - github.com/garyburd/go-oauth v0.0.0-20180319155456-bca2e7f09a17 // indirect + github.com/garyburd/go-oauth v0.0.0-20180319155456-bca2e7f09a17 github.com/go-kit/kit v0.7.0 github.com/go-logfmt/logfmt v0.3.0 // indirect github.com/go-stack/stack v1.7.0 // indirect @@ -16,7 +16,6 @@ require ( github.com/groob/plist v0.0.0-20180203051248-dd56909aee38 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 // indirect github.com/kr/pretty v0.1.0 // indirect - github.com/micromdm/dep v0.0.0-20180512233915-03ff93db459f github.com/micromdm/go4 v0.0.0-20171021081444-deded5397014 github.com/micromdm/scep v1.0.1-0.20180906231441-a136542b4bc9 github.com/pkg/errors v0.8.0 diff --git a/go.sum b/go.sum index 5c98a5b3..b543fbe6 100644 --- a/go.sum +++ b/go.sum @@ -32,8 +32,6 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/micromdm/dep v0.0.0-20180512233915-03ff93db459f h1:WOeg7O6A5okKr5YOy6Sg+kCU3TS5TIg+djrRhBc3cd0= -github.com/micromdm/dep v0.0.0-20180512233915-03ff93db459f/go.mod h1:lDmU6NHThG8AWOQIVTnbifE/uHU8iLnGDR8W32I6OCM= github.com/micromdm/go4 v0.0.0-20171021081444-deded5397014 h1:8Za9WLoGTSU96EAWFziRCiFmQuBAC6xj9/dKiN45LHw= github.com/micromdm/go4 v0.0.0-20171021081444-deded5397014/go.mod h1:8EzTEgA3q2ZdZotWXs1bWnFCXuaFHU0+jDNZbHlwduM= github.com/micromdm/scep v1.0.1-0.20180906231441-a136542b4bc9 h1:LGgQOrgBGOoye+MmS1YT1XpdlOEEkqc/1c0XBPBELnw= diff --git a/platform/config/deptoken.go b/platform/config/deptoken.go index 897b6c88..b19a38fd 100644 --- a/platform/config/deptoken.go +++ b/platform/config/deptoken.go @@ -3,7 +3,7 @@ package config import ( "time" - "github.com/micromdm/dep" + "github.com/micromdm/micromdm/dep" ) const DEPTokenTopic = "mdm.TokenAdded" @@ -17,14 +17,13 @@ type DEPToken struct { } // create a DEP client from token. -func (tok DEPToken) Client() (dep.Client, error) { - conf := &dep.Config{ +func (tok DEPToken) Client() (*dep.Client, error) { + conf := dep.OAuthParameters{ ConsumerKey: tok.ConsumerKey, ConsumerSecret: tok.ConsumerSecret, AccessSecret: tok.AccessSecret, AccessToken: tok.AccessToken, } - depServerURL := "https://mdmenrollment.apple.com" - client, err := dep.NewClient(conf, dep.ServerURL(depServerURL)) - return client, err + client := dep.NewClient(conf) + return client, nil } diff --git a/platform/dep/define_profile.go b/platform/dep/define_profile.go index 046a1851..50d3cdc8 100644 --- a/platform/dep/define_profile.go +++ b/platform/dep/define_profile.go @@ -6,7 +6,8 @@ import ( "net/http" "github.com/go-kit/kit/endpoint" - "github.com/micromdm/dep" + + "github.com/micromdm/micromdm/dep" "github.com/micromdm/micromdm/pkg/httputil" ) diff --git a/platform/dep/get_account_info.go b/platform/dep/get_account_info.go index b5457100..a71477be 100644 --- a/platform/dep/get_account_info.go +++ b/platform/dep/get_account_info.go @@ -6,7 +6,8 @@ import ( "net/http" "github.com/go-kit/kit/endpoint" - "github.com/micromdm/dep" + + "github.com/micromdm/micromdm/dep" "github.com/micromdm/micromdm/pkg/httputil" ) diff --git a/platform/dep/get_dep_device.go b/platform/dep/get_dep_device.go index f22e70c8..0c67bb54 100644 --- a/platform/dep/get_dep_device.go +++ b/platform/dep/get_dep_device.go @@ -6,7 +6,8 @@ import ( "net/http" "github.com/go-kit/kit/endpoint" - "github.com/micromdm/dep" + + "github.com/micromdm/micromdm/dep" "github.com/micromdm/micromdm/pkg/httputil" ) @@ -14,7 +15,7 @@ func (svc *DEPService) GetDeviceDetails(ctx context.Context, serials []string) ( if svc.client == nil { return nil, errors.New("DEP not configured yet. add a DEP token to enable DEP") } - return svc.client.DeviceDetails(serials) + return svc.client.DeviceDetails(serials...) } type deviceDetailsRequest struct { diff --git a/platform/dep/get_dep_profile.go b/platform/dep/get_dep_profile.go index 56bd0989..b2c0c1dc 100644 --- a/platform/dep/get_dep_profile.go +++ b/platform/dep/get_dep_profile.go @@ -6,7 +6,8 @@ import ( "net/http" "github.com/go-kit/kit/endpoint" - "github.com/micromdm/dep" + + "github.com/micromdm/micromdm/dep" "github.com/micromdm/micromdm/pkg/httputil" ) diff --git a/platform/dep/service.go b/platform/dep/service.go index 630080ae..33adf95d 100644 --- a/platform/dep/service.go +++ b/platform/dep/service.go @@ -4,7 +4,7 @@ import ( "context" "sync" - "github.com/micromdm/dep" + "github.com/micromdm/micromdm/dep" "github.com/micromdm/micromdm/platform/pubsub" ) @@ -15,9 +15,17 @@ type Service interface { FetchProfile(ctx context.Context, uuid string) (*dep.Profile, error) } +type DEPClient interface { + DefineProfile(*dep.Profile) (*dep.ProfileResponse, error) + AssignProfile(string, ...string) (*dep.ProfileResponse, error) + FetchProfile(string) (*dep.Profile, error) + Account() (*dep.Account, error) + DeviceDetails(...string) (*dep.DeviceDetailsResponse, error) +} + type DEPService struct { mtx sync.RWMutex - client dep.Client + client DEPClient subscriber pubsub.Subscriber } @@ -25,6 +33,6 @@ func (svc *DEPService) Run() error { return svc.watchTokenUpdates(svc.subscriber) } -func New(client dep.Client, subscriber pubsub.Subscriber) *DEPService { +func New(client DEPClient, subscriber pubsub.Subscriber) *DEPService { return &DEPService{client: client, subscriber: subscriber} } diff --git a/server/server.go b/server/server.go index 69441413..3b520fdd 100644 --- a/server/server.go +++ b/server/server.go @@ -9,6 +9,7 @@ import ( "fmt" "io/ioutil" "net/http" + "net/url" "path/filepath" "time" @@ -16,12 +17,12 @@ import ( "github.com/boltdb/bolt" "github.com/go-kit/kit/log" "github.com/go-kit/kit/log/level" - "github.com/micromdm/dep" boltdepot "github.com/micromdm/scep/depot/bolt" scep "github.com/micromdm/scep/server" "github.com/pkg/errors" "golang.org/x/crypto/pkcs12" + "github.com/micromdm/micromdm/dep" "github.com/micromdm/micromdm/dep/depsync" "github.com/micromdm/micromdm/mdm" "github.com/micromdm/micromdm/mdm/enroll" @@ -58,7 +59,7 @@ type Server struct { ConfigDB config.Store RemoveDB block.Store CommandWebhookURL string - DEPClient dep.Client + DEPClient *dep.Client PushService *push.Service // bufford push APNSPushService apns.Service @@ -352,9 +353,12 @@ func (p staticTopicProvider) PushTopic() (string, error) { } func (c *Server) setupDepClient() error { - // depsim config - depsim := c.Depsim - var conf *dep.Config + var ( + conf dep.OAuthParameters + depsim = c.Depsim + hasTokenConfig bool + opts []dep.Option + ) // try getting the oauth config from bolt tokens, err := c.ConfigDB.DEPTokens() @@ -362,7 +366,7 @@ func (c *Server) setupDepClient() error { return err } if len(tokens) >= 1 { - conf = new(dep.Config) + hasTokenConfig = true conf.ConsumerSecret = tokens[0].ConsumerSecret conf.ConsumerKey = tokens[0].ConsumerKey conf.AccessSecret = tokens[0].AccessSecret @@ -372,29 +376,25 @@ func (c *Server) setupDepClient() error { // override with depsim keys if specified on CLI if depsim != "" { - conf = &dep.Config{ + hasTokenConfig = true + conf = dep.OAuthParameters{ ConsumerKey: "CK_48dd68d198350f51258e885ce9a5c37ab7f98543c4a697323d75682a6c10a32501cb247e3db08105db868f73f2c972bdb6ae77112aea803b9219eb52689d42e6", ConsumerSecret: "CS_34c7b2b531a600d99a0e4edcf4a78ded79b86ef318118c2f5bcfee1b011108c32d5302df801adbe29d446eb78f02b13144e323eb9aad51c79f01e50cb45c3a68", AccessToken: "AT_927696831c59ba510cfe4ec1a69e5267c19881257d4bca2906a99d0785b785a6f6fdeb09774954fdd5e2d0ad952e3af52c6d8d2f21c924ba0caf4a031c158b89", AccessSecret: "AS_c31afd7a09691d83548489336e8ff1cb11b82b6bca13f793344496a556b1f4972eaff4dde6deb5ac9cf076fdfa97ec97699c34d515947b9cf9ed31c99dded6ba", } + depsimurl, err := url.Parse(depsim) + if err != nil { + return err + } + opts = append(opts, dep.WithServerURL(depsimurl)) } - if conf == nil { + if !hasTokenConfig { return nil } - depServerURL := "https://mdmenrollment.apple.com" - if depsim != "" { - depServerURL = depsim - } - client, err := dep.NewClient(conf, dep.ServerURL(depServerURL)) - if err != nil { - return err - } - - c.DEPClient = client - + c.DEPClient = dep.NewClient(conf, opts...) return nil }