mirror of
https://github.com/micromdm/micromdm/
synced 2026-08-12 04:55:39 +08:00
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.
This commit is contained in:
3
Makefile
3
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)
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
32
dep/account.go
Normal file
32
dep/account.go
Normal file
@@ -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")
|
||||
}
|
||||
218
dep/client.go
Normal file
218
dep/client.go
Normal file
@@ -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")
|
||||
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
121
dep/devices.go
Normal file
121
dep/devices.go
Normal file
@@ -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")
|
||||
}
|
||||
92
dep/profile.go
Normal file
92
dep/profile.go
Normal file
@@ -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")
|
||||
}
|
||||
136
dep/testdata/account_response.json
vendored
Normal file
136
dep/testdata/account_response.json
vendored
Normal file
@@ -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"
|
||||
}
|
||||
18
dep/testdata/device_details_response.json
vendored
Normal file
18
dep/testdata/device_details_response.json
vendored
Normal file
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
20
dep/testdata/device_response.json
vendored
Normal file
20
dep/testdata/device_response.json
vendored
Normal file
@@ -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"
|
||||
}
|
||||
6
dep/testdata/empty_sync_response.json
vendored
Normal file
6
dep/testdata/empty_sync_response.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"devices": [],
|
||||
"fetched_until": "2018-09-08T17:41:29Z",
|
||||
"more_to_follow": false,
|
||||
"cursor": "MDowOjE1MzY0Mjg0ODkzMDc6MTUzNjQyODQ4OTMwNzp0cnVlOjE1MzY0Mjg0ODkzMDc"
|
||||
}
|
||||
20
dep/testdata/fetch_profile_response.json
vendored
Normal file
20
dep/testdata/fetch_profile_response.json
vendored
Normal file
@@ -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"
|
||||
}
|
||||
3
go.mod
3
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
|
||||
|
||||
2
go.sum
2
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=
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
@@ -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}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user