mirror of
https://github.com/micromdm/micromdm/
synced 2026-08-09 11:05:59 +08:00
@@ -184,7 +184,7 @@ func (cmd *applyCommand) applyDEPTokens(args []string) error {
|
||||
return err
|
||||
}
|
||||
ctx := context.Background()
|
||||
err = cmd.applysvc.ApplyDEPToken(ctx, p7mBytes)
|
||||
err = cmd.configsvc.ApplyDEPToken(ctx, p7mBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ func (cmd *getCommand) getDepTokens(args []string) error {
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 4, 2, ' ', 0)
|
||||
fmt.Fprintf(w, "ConsumerKey\tAccessTokenExpiry\n")
|
||||
ctx := context.Background()
|
||||
tokens, certBytes, err := cmd.list.GetDEPTokens(ctx)
|
||||
tokens, certBytes, err := cmd.configsvc.GetDEPTokens(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -13,16 +13,26 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
httptransport "github.com/go-kit/kit/transport/http"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/crypto/pkcs12"
|
||||
|
||||
"github.com/micromdm/micromdm/pkg/crypto"
|
||||
"github.com/micromdm/micromdm/pkg/crypto/mdmcertutil"
|
||||
"github.com/micromdm/micromdm/platform/config"
|
||||
)
|
||||
|
||||
type mdmcertCommand struct{}
|
||||
type mdmcertCommand struct {
|
||||
*remoteServices
|
||||
}
|
||||
|
||||
func (cmd *mdmcertCommand) setup() error {
|
||||
logger := log.NewLogfmtLogger(os.Stderr)
|
||||
remote, err := setupClient(logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.remoteServices = remote
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cmd *mdmcertCommand) Usage() error {
|
||||
const usageText = `
|
||||
@@ -62,6 +72,10 @@ func (cmd *mdmcertCommand) Run(args []string) error {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err := cmd.setup(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var run func([]string) error
|
||||
switch strings.ToLower(args[0]) {
|
||||
case "vendor":
|
||||
@@ -197,27 +211,12 @@ func (cmd *mdmcertCommand) runUpload(args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg, err := LoadServerConfig()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "load mdmctl client config")
|
||||
}
|
||||
logger := log.NewLogfmtLogger(os.Stderr)
|
||||
configsvc, err := config.NewClient(
|
||||
cfg.ServerURL,
|
||||
logger,
|
||||
cfg.APIToken,
|
||||
httptransport.SetClient(skipVerifyHTTPClient(cfg.SkipVerify)),
|
||||
)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "create config service from mdmctl config")
|
||||
}
|
||||
|
||||
cert, key, err := loadPushCerts(*flCertPath, *flKeyPath, *flKeyPass)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "load push certificate")
|
||||
}
|
||||
|
||||
if err := configsvc.SavePushCertificate(context.Background(), cert, key); err != nil {
|
||||
if err := cmd.configsvc.SavePushCertificate(context.Background(), cert, key); err != nil {
|
||||
return errors.Wrap(err, "upload push certificate and key to server")
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/micromdm/micromdm/platform/api/server/apply"
|
||||
"github.com/micromdm/micromdm/platform/api/server/list"
|
||||
"github.com/micromdm/micromdm/platform/blueprint"
|
||||
"github.com/micromdm/micromdm/platform/config"
|
||||
"github.com/micromdm/micromdm/platform/profile"
|
||||
"github.com/micromdm/micromdm/platform/remove"
|
||||
"github.com/micromdm/micromdm/platform/user"
|
||||
@@ -17,6 +18,7 @@ type remoteServices struct {
|
||||
blueprintsvc blueprint.Service
|
||||
blocksvc remove.Service
|
||||
usersvc user.Service
|
||||
configsvc config.Service
|
||||
applysvc apply.Service
|
||||
list list.Service
|
||||
}
|
||||
@@ -55,6 +57,13 @@ func setupClient(logger log.Logger) (*remoteServices, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
configsvc, err := config.NewHTTPClient(
|
||||
cfg.ServerURL, cfg.APIToken, logger,
|
||||
httptransport.SetClient(skipVerifyHTTPClient(cfg.SkipVerify)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
applysvc, err := apply.NewClient(
|
||||
cfg.ServerURL, logger, cfg.APIToken,
|
||||
httptransport.SetClient(skipVerifyHTTPClient(cfg.SkipVerify)))
|
||||
@@ -74,6 +83,7 @@ func setupClient(logger log.Logger) (*remoteServices, error) {
|
||||
blueprintsvc: blueprintsvc,
|
||||
blocksvc: blocksvc,
|
||||
usersvc: usersvc,
|
||||
configsvc: configsvc,
|
||||
applysvc: applysvc,
|
||||
list: listsvc,
|
||||
}, nil
|
||||
|
||||
@@ -51,7 +51,7 @@ import (
|
||||
blueprintbuiltin "github.com/micromdm/micromdm/platform/blueprint/builtin"
|
||||
"github.com/micromdm/micromdm/platform/command"
|
||||
"github.com/micromdm/micromdm/platform/config"
|
||||
"github.com/micromdm/micromdm/platform/deptoken"
|
||||
configbuiltin "github.com/micromdm/micromdm/platform/config/builtin"
|
||||
"github.com/micromdm/micromdm/platform/device"
|
||||
"github.com/micromdm/micromdm/platform/profile"
|
||||
profilebuiltin "github.com/micromdm/micromdm/platform/profile/builtin"
|
||||
@@ -202,19 +202,6 @@ func serve(args []string) error {
|
||||
ctx := context.Background()
|
||||
httpLogger := log.With(logger, "transport", "http")
|
||||
|
||||
var configHandlers config.HTTPHandlers
|
||||
{
|
||||
pushCertEndpoint := config.MakeSavePushCertificateEndpoint(sm.configService)
|
||||
configEndpoints := config.Endpoints{
|
||||
SavePushCertificateEndpoint: pushCertEndpoint,
|
||||
}
|
||||
configOpts := []httptransport.ServerOption{
|
||||
httptransport.ServerErrorLogger(httpLogger),
|
||||
httptransport.ServerErrorEncoder(checkin.EncodeError),
|
||||
}
|
||||
configHandlers = config.MakeHTTPHandlers(ctx, configEndpoints, configOpts...)
|
||||
}
|
||||
|
||||
var checkinHandlers checkin.HTTPHandlers
|
||||
{
|
||||
e := checkin.Endpoints{
|
||||
@@ -269,7 +256,6 @@ func serve(args []string) error {
|
||||
if err != nil {
|
||||
stdlog.Fatalf("creating DEP client: %s\n", err)
|
||||
}
|
||||
tokenDB := &deptoken.DB{DB: sm.db, Publisher: sm.pubclient}
|
||||
appDB := &appstore.Repo{Path: *flRepoPath}
|
||||
|
||||
var profilesvc profile.Service
|
||||
@@ -294,12 +280,18 @@ func serve(args []string) error {
|
||||
|
||||
userEndpoints := user.MakeServerEndpoints(usersvc)
|
||||
|
||||
var configsvc config.Service
|
||||
{
|
||||
configsvc = config.New(sm.configDB)
|
||||
}
|
||||
|
||||
configEndpoints := config.MakeServerEndpoints(configsvc)
|
||||
|
||||
var listsvc list.Service
|
||||
{
|
||||
l := &list.ListService{
|
||||
DEPClient: dc,
|
||||
Devices: devDB,
|
||||
Tokens: tokenDB,
|
||||
Apps: appDB,
|
||||
}
|
||||
listsvc = l
|
||||
@@ -315,7 +307,6 @@ func serve(args []string) error {
|
||||
}
|
||||
listEndpoints := list.Endpoints{
|
||||
ListDevicesEndpoint: listDevicesEndpoint,
|
||||
GetDEPTokensEndpoint: list.MakeGetDEPTokensEndpoint(listsvc),
|
||||
GetDEPAccountInfoEndpoint: list.MakeGetDEPAccountInfoEndpoint(listsvc),
|
||||
GetDEPProfileEndpoint: list.MakeGetDEPProfileEndpoint(listsvc),
|
||||
GetDEPDeviceEndpoint: list.MakeGetDEPDeviceDetailsEndpoint(listsvc),
|
||||
@@ -326,7 +317,6 @@ func serve(args []string) error {
|
||||
{
|
||||
l := &apply.ApplyService{
|
||||
DEPClient: dc,
|
||||
Tokens: tokenDB,
|
||||
Apps: appDB,
|
||||
}
|
||||
applysvc = l
|
||||
@@ -346,7 +336,6 @@ func serve(args []string) error {
|
||||
}
|
||||
|
||||
applyEndpoints := apply.Endpoints{
|
||||
ApplyDEPTokensEndpoint: apply.MakeApplyDEPTokensEndpoint(applysvc),
|
||||
DefineDEPProfileEndpoint: defineDEPProfileEndpoint,
|
||||
AppUploadEndpoint: appUploadEndpoint,
|
||||
}
|
||||
@@ -375,6 +364,7 @@ func serve(args []string) error {
|
||||
blueprintsHandler := blueprint.MakeHTTPHandler(blueprintEndpoints, logger)
|
||||
blockhandler := block.MakeHTTPHandler(blockEndpoints, logger)
|
||||
userHandler := user.MakeHTTPHandler(userEndpoints, logger)
|
||||
configHandler := config.MakeHTTPHandler(configEndpoints, logger)
|
||||
|
||||
// API commands. Only handled if the user provides an api key.
|
||||
if *flAPIKey != "" {
|
||||
@@ -383,18 +373,18 @@ func serve(args []string) error {
|
||||
r.Handle("/v1/users", apiAuthMiddleware(*flAPIKey, userHandler))
|
||||
r.Handle("/v1/devices/{udid}/block", apiAuthMiddleware(*flAPIKey, blockhandler))
|
||||
r.Handle("/v1/devices/{udid}/unblock", apiAuthMiddleware(*flAPIKey, blockhandler))
|
||||
r.Handle("/v1/dep-tokens", apiAuthMiddleware(*flAPIKey, configHandler))
|
||||
r.Handle("/v1/dep-tokens", apiAuthMiddleware(*flAPIKey, configHandler))
|
||||
r.Handle("/v1/config/certificate", apiAuthMiddleware(*flAPIKey, configHandler))
|
||||
r.Handle("/push/{udid}", apiAuthMiddleware(*flAPIKey, pushHandlers.PushHandler))
|
||||
r.Handle("/v1/commands", apiAuthMiddleware(*flAPIKey, commandHandlers.NewCommandHandler)).Methods("POST")
|
||||
r.Handle("/v1/devices", apiAuthMiddleware(*flAPIKey, listAPIHandlers.ListDevicesHandler)).Methods("GET")
|
||||
r.Handle("/v1/dep-tokens", apiAuthMiddleware(*flAPIKey, listAPIHandlers.GetDEPTokensHandler)).Methods("GET")
|
||||
r.Handle("/v1/dep-tokens", apiAuthMiddleware(*flAPIKey, applyAPIHandlers.DEPTokensHandler)).Methods("PUT")
|
||||
r.Handle("/v1/dep/devices", apiAuthMiddleware(*flAPIKey, listAPIHandlers.GetDEPDeviceDetailsHandler)).Methods("GET")
|
||||
r.Handle("/v1/dep/account", apiAuthMiddleware(*flAPIKey, listAPIHandlers.GetDEPAccountInfoHandler)).Methods("GET")
|
||||
r.Handle("/v1/dep/profiles", apiAuthMiddleware(*flAPIKey, listAPIHandlers.GetDEPProfileHandler)).Methods("GET")
|
||||
r.Handle("/v1/dep/profiles", apiAuthMiddleware(*flAPIKey, applyAPIHandlers.DefineDEPProfileHandler)).Methods("POST")
|
||||
r.Handle("/v1/apps", apiAuthMiddleware(*flAPIKey, applyAPIHandlers.AppUploadHandler)).Methods("POST")
|
||||
r.Handle("/v1/apps", apiAuthMiddleware(*flAPIKey, listAPIHandlers.ListAppsHandler)).Methods("GET")
|
||||
r.Handle("/v1/config/certificate", apiAuthMiddleware(*flAPIKey, configHandlers.SavePushCertificateHandler)).Methods("PUT")
|
||||
}
|
||||
|
||||
if *flRepoPath != "" {
|
||||
@@ -490,7 +480,7 @@ type server struct {
|
||||
tlsCertPath string
|
||||
scepDepot *boltdepot.Depot
|
||||
profileDB profile.Store
|
||||
configDB *config.DB
|
||||
configDB config.Store
|
||||
removeDB block.Store
|
||||
CommandWebhookURL string
|
||||
|
||||
@@ -678,13 +668,13 @@ func (c *server) setupConfigStore() {
|
||||
if c.err != nil {
|
||||
return
|
||||
}
|
||||
db, err := config.NewDB(c.db, c.pubclient)
|
||||
db, err := configbuiltin.NewDB(c.db, c.pubclient)
|
||||
if err != nil {
|
||||
c.err = err
|
||||
return
|
||||
}
|
||||
c.configDB = db
|
||||
c.configService = config.NewService(db)
|
||||
c.configService = config.New(db)
|
||||
|
||||
}
|
||||
|
||||
@@ -781,9 +771,8 @@ func (c *server) depClient() (dep.Client, error) {
|
||||
depsim := c.depsim
|
||||
var conf *dep.Config
|
||||
|
||||
tokenDB := &deptoken.DB{DB: c.db}
|
||||
// try getting the oauth config from bolt
|
||||
tokens, err := tokenDB.DEPTokens()
|
||||
tokens, err := c.configDB.DEPTokens()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/micromdm/dep"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/micromdm/micromdm/platform/deptoken"
|
||||
conf "github.com/micromdm/micromdm/platform/config"
|
||||
"github.com/micromdm/micromdm/platform/pubsub"
|
||||
)
|
||||
|
||||
@@ -105,7 +105,7 @@ func New(pub pubsub.PublishSubscriber, db *bolt.DB, opts ...Option) (Syncer, err
|
||||
}
|
||||
|
||||
func (w *watcher) updateClient(pubsub pubsub.Subscriber) error {
|
||||
tokenAdded, err := pubsub.Subscribe(context.TODO(), "token-events", deptoken.DEPTokenTopic)
|
||||
tokenAdded, err := pubsub.Subscribe(context.TODO(), "token-events", conf.DEPTokenTopic)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -114,7 +114,7 @@ func (w *watcher) updateClient(pubsub pubsub.Subscriber) error {
|
||||
for {
|
||||
select {
|
||||
case event := <-tokenAdded:
|
||||
var token deptoken.DEPToken
|
||||
var token conf.DEPToken
|
||||
if err := json.Unmarshal(event.Message, &token); err != nil {
|
||||
log.Printf("unmarshalling tokenAdded to token: %s\n", err)
|
||||
continue
|
||||
|
||||
@@ -16,17 +16,6 @@ func NewClient(instance string, logger log.Logger, token string, opts ...httptra
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var applyDEPTokensEndpoint endpoint.Endpoint
|
||||
{
|
||||
applyDEPTokensEndpoint = httptransport.NewClient(
|
||||
"PUT",
|
||||
copyURL(u, "/v1/dep-tokens"),
|
||||
encodeRequestWithToken(token, EncodeHTTPGenericRequest),
|
||||
DecodeDEPTokensResponse,
|
||||
opts...,
|
||||
).Endpoint()
|
||||
}
|
||||
|
||||
var defineDEPProfileEndpoint endpoint.Endpoint
|
||||
{
|
||||
defineDEPProfileEndpoint = httptransport.NewClient(
|
||||
@@ -50,7 +39,6 @@ func NewClient(instance string, logger log.Logger, token string, opts ...httptra
|
||||
}
|
||||
|
||||
return Endpoints{
|
||||
ApplyDEPTokensEndpoint: applyDEPTokensEndpoint,
|
||||
DefineDEPProfileEndpoint: defineDEPProfileEndpoint,
|
||||
AppUploadEndpoint: uploadAppEndpoint,
|
||||
}, nil
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
)
|
||||
|
||||
type Endpoints struct {
|
||||
ApplyDEPTokensEndpoint endpoint.Endpoint
|
||||
DefineDEPProfileEndpoint endpoint.Endpoint
|
||||
AppUploadEndpoint endpoint.Endpoint
|
||||
}
|
||||
@@ -38,25 +37,6 @@ func (e Endpoints) DefineDEPProfile(ctx context.Context, p *dep.Profile) (*dep.P
|
||||
return response.ProfileResponse, response.Err
|
||||
}
|
||||
|
||||
func (e Endpoints) ApplyDEPToken(ctx context.Context, P7MContent []byte) error {
|
||||
req := depTokensRequest{P7MContent: P7MContent}
|
||||
resp, err := e.ApplyDEPTokensEndpoint(ctx, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return resp.(depTokensResponse).Err
|
||||
}
|
||||
|
||||
func MakeApplyDEPTokensEndpoint(svc Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
|
||||
req := request.(depTokensRequest)
|
||||
err = svc.ApplyDEPToken(ctx, req.P7MContent)
|
||||
return depTokensResponse{
|
||||
Err: err,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func MakeDefineDEPProfile(svc Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
|
||||
req := request.(depProfileRequest)
|
||||
@@ -92,16 +72,6 @@ type appUploadResponse struct {
|
||||
|
||||
func (r appUploadResponse) error() error { return r.Err }
|
||||
|
||||
type depTokensRequest struct {
|
||||
P7MContent []byte `json:"p7m_content"`
|
||||
}
|
||||
|
||||
type depTokensResponse struct {
|
||||
Err error `json:"err,omitempty"`
|
||||
}
|
||||
|
||||
func (r depTokensResponse) error() error { return r.Err }
|
||||
|
||||
type depProfileRequest struct{ *dep.Profile }
|
||||
type depProfileResponse struct {
|
||||
*dep.ProfileResponse
|
||||
|
||||
@@ -2,26 +2,19 @@ package apply
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/textproto"
|
||||
|
||||
"github.com/fullsailor/pkcs7"
|
||||
"github.com/micromdm/dep"
|
||||
|
||||
"github.com/micromdm/micromdm/platform/appstore"
|
||||
"github.com/micromdm/micromdm/platform/deptoken"
|
||||
"github.com/micromdm/micromdm/platform/config"
|
||||
"github.com/micromdm/micromdm/platform/pubsub"
|
||||
)
|
||||
|
||||
type Service interface {
|
||||
ApplyDEPToken(ctx context.Context, P7MContent []byte) error
|
||||
UploadApp(ctx context.Context, manifestName string, manifest io.Reader, pkgName string, pkg io.Reader) error
|
||||
DEPService
|
||||
}
|
||||
@@ -30,8 +23,7 @@ type ApplyService struct {
|
||||
mtx sync.RWMutex
|
||||
DEPClient dep.Client
|
||||
|
||||
Tokens *deptoken.DB
|
||||
Apps appstore.AppStore
|
||||
Apps appstore.AppStore
|
||||
}
|
||||
|
||||
func (svc *ApplyService) UploadApp(ctx context.Context, manifestName string, manifest io.Reader, pkgName string, pkg io.Reader) error {
|
||||
@@ -51,7 +43,7 @@ func (svc *ApplyService) UploadApp(ctx context.Context, manifestName string, man
|
||||
}
|
||||
|
||||
func (svc *ApplyService) WatchTokenUpdates(pubsub pubsub.Subscriber) error {
|
||||
tokenAdded, err := pubsub.Subscribe(context.TODO(), "apply-token-events", deptoken.DEPTokenTopic)
|
||||
tokenAdded, err := pubsub.Subscribe(context.TODO(), "apply-token-events", config.DEPTokenTopic)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -60,7 +52,7 @@ func (svc *ApplyService) WatchTokenUpdates(pubsub pubsub.Subscriber) error {
|
||||
for {
|
||||
select {
|
||||
case event := <-tokenAdded:
|
||||
var token deptoken.DEPToken
|
||||
var token config.DEPToken
|
||||
if err := json.Unmarshal(event.Message, &token); err != nil {
|
||||
log.Printf("unmarshalling tokenAdded to token: %s\n", err)
|
||||
continue
|
||||
@@ -81,72 +73,3 @@ func (svc *ApplyService) WatchTokenUpdates(pubsub pubsub.Subscriber) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// unwrapSMIME removes the S/MIME-like wrapper around raw CMS/PKCS7 data
|
||||
func unwrapSMIME(smime []byte) ([]byte, error) {
|
||||
tr := textproto.NewReader(bufio.NewReader(bytes.NewReader(smime)))
|
||||
if _, err := tr.ReadMIMEHeader(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dec := base64.NewDecoder(base64.StdEncoding, tr.DotReader())
|
||||
buf := new(bytes.Buffer)
|
||||
io.Copy(buf, dec)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// unwrapTokenJSON removes the MIME-like headers and text surrounding the DEP token JSON
|
||||
func unwrapTokenJSON(wrapped []byte) ([]byte, error) {
|
||||
tr := textproto.NewReader(bufio.NewReader(bytes.NewReader(wrapped)))
|
||||
if _, err := tr.ReadMIMEHeader(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tokenJSON := new(bytes.Buffer)
|
||||
for {
|
||||
line, err := tr.ReadLineBytes()
|
||||
if err != nil && err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
line = bytes.Trim(line, "-----BEGIN MESSAGE-----")
|
||||
line = bytes.Trim(line, "-----END MESSAGE-----")
|
||||
if _, err := tokenJSON.Write(line); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return tokenJSON.Bytes(), nil
|
||||
}
|
||||
|
||||
func (svc *ApplyService) ApplyDEPToken(ctx context.Context, P7MContent []byte) error {
|
||||
unwrapped, err := unwrapSMIME(P7MContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key, cert, err := svc.Tokens.DEPKeypair()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p7, err := pkcs7.Parse(unwrapped)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
decrypted, err := p7.Decrypt(cert, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tokenJSON, err := unwrapTokenJSON(decrypted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var depToken deptoken.DEPToken
|
||||
err = json.Unmarshal(tokenJSON, &depToken)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = svc.Tokens.AddToken(depToken.ConsumerKey, tokenJSON)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Println("stored DEP token with ck", depToken.ConsumerKey)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -14,19 +14,12 @@ import (
|
||||
)
|
||||
|
||||
type HTTPHandlers struct {
|
||||
DEPTokensHandler http.Handler
|
||||
DefineDEPProfileHandler http.Handler
|
||||
AppUploadHandler http.Handler
|
||||
}
|
||||
|
||||
func MakeHTTPHandlers(ctx context.Context, endpoints Endpoints, opts ...httptransport.ServerOption) HTTPHandlers {
|
||||
h := HTTPHandlers{
|
||||
DEPTokensHandler: httptransport.NewServer(
|
||||
endpoints.ApplyDEPTokensEndpoint,
|
||||
decodeDEPTokensRequest,
|
||||
encodeResponse,
|
||||
opts...,
|
||||
),
|
||||
DefineDEPProfileHandler: httptransport.NewServer(
|
||||
endpoints.DefineDEPProfileEndpoint,
|
||||
decodeDEPProfileRequest,
|
||||
@@ -43,14 +36,6 @@ func MakeHTTPHandlers(ctx context.Context, endpoints Endpoints, opts ...httptran
|
||||
return h
|
||||
}
|
||||
|
||||
func decodeDEPTokensRequest(ctx context.Context, r *http.Request) (interface{}, error) {
|
||||
var req depTokensRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func decodeDEPProfileRequest(ctx context.Context, r *http.Request) (interface{}, error) {
|
||||
var req depProfileRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
@@ -162,15 +147,6 @@ func EncodeHTTPGenericRequest(_ context.Context, r *http.Request, request interf
|
||||
return nil
|
||||
}
|
||||
|
||||
func DecodeDEPTokensResponse(_ context.Context, r *http.Response) (interface{}, error) {
|
||||
if r.StatusCode != http.StatusOK {
|
||||
return nil, errorDecoder(r)
|
||||
}
|
||||
var resp depTokensResponse
|
||||
err := json.NewDecoder(r.Body).Decode(&resp)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func DecodeDEPProfileResponse(_ context.Context, r *http.Response) (interface{}, error) {
|
||||
if r.StatusCode != http.StatusOK {
|
||||
return nil, errorDecoder(r)
|
||||
|
||||
@@ -26,16 +26,6 @@ func NewClient(instance string, logger log.Logger, token string, opts ...httptra
|
||||
opts...,
|
||||
).Endpoint()
|
||||
}
|
||||
var getDEPTokensEndpoint endpoint.Endpoint
|
||||
{
|
||||
getDEPTokensEndpoint = httptransport.NewClient(
|
||||
"GET",
|
||||
copyURL(u, "/v1/dep-tokens"),
|
||||
encodeRequestWithToken(token, EncodeHTTPGenericRequest),
|
||||
DecodeGetDEPTokensResponse,
|
||||
opts...,
|
||||
).Endpoint()
|
||||
}
|
||||
|
||||
var getDEPAccountInfoEndpoint endpoint.Endpoint
|
||||
{
|
||||
@@ -83,7 +73,6 @@ func NewClient(instance string, logger log.Logger, token string, opts ...httptra
|
||||
|
||||
return Endpoints{
|
||||
ListDevicesEndpoint: listDevicesEndpoint,
|
||||
GetDEPTokensEndpoint: getDEPTokensEndpoint,
|
||||
GetDEPAccountInfoEndpoint: getDEPAccountInfoEndpoint,
|
||||
GetDEPDeviceEndpoint: getDEPDeviceDetailsEndpoint,
|
||||
GetDEPProfileEndpoint: getDEPProfilesEndpoint,
|
||||
|
||||
@@ -6,13 +6,10 @@ import (
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/micromdm/dep"
|
||||
|
||||
"github.com/micromdm/micromdm/platform/deptoken"
|
||||
)
|
||||
|
||||
type Endpoints struct {
|
||||
ListDevicesEndpoint endpoint.Endpoint
|
||||
GetDEPTokensEndpoint endpoint.Endpoint
|
||||
GetDEPAccountInfoEndpoint endpoint.Endpoint
|
||||
GetDEPDeviceEndpoint endpoint.Endpoint
|
||||
GetDEPProfileEndpoint endpoint.Endpoint
|
||||
@@ -37,14 +34,6 @@ func (e Endpoints) ListApplications(ctx context.Context, opts ListAppsOption) ([
|
||||
return response.(appListResponse).Apps, response.(appListResponse).Err
|
||||
}
|
||||
|
||||
func (e Endpoints) GetDEPTokens(ctx context.Context) ([]deptoken.DEPToken, []byte, error) {
|
||||
resp, err := e.GetDEPTokensEndpoint(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return resp.(depTokenResponse).DEPTokens, resp.(depTokenResponse).DEPPubKey, nil
|
||||
}
|
||||
|
||||
func (e Endpoints) GetDEPAccountInfo(ctx context.Context) (*dep.Account, error) {
|
||||
request := depAccountInforequest{}
|
||||
response, err := e.GetDEPAccountInfoEndpoint(ctx, request)
|
||||
@@ -94,17 +83,6 @@ func (e Endpoints) GetDEPProfile(ctx context.Context, uuid string) (*dep.Profile
|
||||
return response.(depProfileResponse).Profile, response.(depProfileResponse).Err
|
||||
}
|
||||
|
||||
func MakeGetDEPTokensEndpoint(svc Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
|
||||
tokens, pubkey, err := svc.GetDEPTokens(ctx)
|
||||
return depTokenResponse{
|
||||
DEPTokens: tokens,
|
||||
DEPPubKey: pubkey,
|
||||
Err: err,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func MakeGetDEPAccountInfoEndpoint(svc Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
|
||||
account, err := svc.GetDEPAccountInfo(ctx)
|
||||
@@ -141,12 +119,6 @@ type devicesResponse struct {
|
||||
Err error `json:"err,omitempty"`
|
||||
}
|
||||
|
||||
type depTokenResponse struct {
|
||||
DEPTokens []deptoken.DEPToken `json:"dep_tokens"`
|
||||
DEPPubKey []byte `json:"public_key"`
|
||||
Err error `json:"err,omitempty"`
|
||||
}
|
||||
|
||||
type depAccountInforequest struct{}
|
||||
type depAccountInfoResponse struct {
|
||||
*dep.Account
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/micromdm/micromdm/platform/appstore"
|
||||
"github.com/micromdm/micromdm/platform/deptoken"
|
||||
"github.com/micromdm/micromdm/platform/config"
|
||||
"github.com/micromdm/micromdm/platform/device"
|
||||
"github.com/micromdm/micromdm/platform/pubsub"
|
||||
)
|
||||
@@ -30,7 +30,6 @@ type ListAppsOption struct {
|
||||
|
||||
type Service interface {
|
||||
ListDevices(ctx context.Context, opt ListDevicesOption) ([]DeviceDTO, error)
|
||||
GetDEPTokens(ctx context.Context) ([]deptoken.DEPToken, []byte, error)
|
||||
ListApplications(ctx context.Context, opt ListAppsOption) ([]AppDTO, error)
|
||||
DEPService
|
||||
}
|
||||
@@ -40,7 +39,6 @@ type ListService struct {
|
||||
DEPClient dep.Client
|
||||
|
||||
Devices *device.DB
|
||||
Tokens *deptoken.DB
|
||||
Apps appstore.AppStore
|
||||
}
|
||||
|
||||
@@ -68,7 +66,7 @@ func (svc *ListService) ListApplications(ctx context.Context, opts ListAppsOptio
|
||||
}
|
||||
|
||||
func (svc *ListService) WatchTokenUpdates(pubsub pubsub.Subscriber) error {
|
||||
tokenAdded, err := pubsub.Subscribe(context.TODO(), "list-token-events", deptoken.DEPTokenTopic)
|
||||
tokenAdded, err := pubsub.Subscribe(context.TODO(), "list-token-events", config.DEPTokenTopic)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -77,7 +75,7 @@ func (svc *ListService) WatchTokenUpdates(pubsub pubsub.Subscriber) error {
|
||||
for {
|
||||
select {
|
||||
case event := <-tokenAdded:
|
||||
var token deptoken.DEPToken
|
||||
var token config.DEPToken
|
||||
if err := json.Unmarshal(event.Message, &token); err != nil {
|
||||
log.Printf("unmarshalling tokenAdded to token: %s\n", err)
|
||||
continue
|
||||
@@ -112,21 +110,3 @@ func (svc *ListService) ListDevices(ctx context.Context, opt ListDevicesOption)
|
||||
}
|
||||
return dto, err
|
||||
}
|
||||
|
||||
func (svc *ListService) GetDEPTokens(ctx context.Context) ([]deptoken.DEPToken, []byte, error) {
|
||||
_, cert, err := svc.Tokens.DEPKeypair()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
var certBytes []byte
|
||||
if cert != nil {
|
||||
certBytes = cert.Raw
|
||||
}
|
||||
|
||||
tokens, err := svc.Tokens.DEPTokens()
|
||||
if err != nil {
|
||||
return nil, certBytes, err
|
||||
}
|
||||
|
||||
return tokens, certBytes, nil
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
|
||||
type HTTPHandlers struct {
|
||||
ListDevicesHandler http.Handler
|
||||
GetDEPTokensHandler http.Handler
|
||||
GetDEPAccountInfoHandler http.Handler
|
||||
GetDEPProfileHandler http.Handler
|
||||
GetDEPDeviceDetailsHandler http.Handler
|
||||
@@ -28,11 +27,6 @@ func MakeHTTPHandlers(ctx context.Context, endpoints Endpoints, opts ...httptran
|
||||
encodeResponse,
|
||||
opts...,
|
||||
),
|
||||
GetDEPTokensHandler: httptransport.NewServer(
|
||||
endpoints.GetDEPTokensEndpoint,
|
||||
decodeGetDEPTokensRequest,
|
||||
encodeResponse,
|
||||
opts...),
|
||||
GetDEPAccountInfoHandler: httptransport.NewServer(
|
||||
endpoints.GetDEPAccountInfoEndpoint,
|
||||
decodeDepAccountInfoRequest,
|
||||
@@ -61,10 +55,6 @@ func MakeHTTPHandlers(ctx context.Context, endpoints Endpoints, opts ...httptran
|
||||
return h
|
||||
}
|
||||
|
||||
func decodeGetDEPTokensRequest(ctx context.Context, r *http.Request) (interface{}, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func decodeListDevicesRequest(ctx context.Context, r *http.Request) (interface{}, error) {
|
||||
req := devicesRequest{
|
||||
Opts: ListDevicesOption{},
|
||||
@@ -154,15 +144,6 @@ func DecodeDevicesResponse(_ context.Context, r *http.Response) (interface{}, er
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func DecodeGetDEPTokensResponse(_ context.Context, r *http.Response) (interface{}, error) {
|
||||
if r.StatusCode != http.StatusOK {
|
||||
return nil, errorDecoder(r)
|
||||
}
|
||||
var resp depTokenResponse
|
||||
err := json.NewDecoder(r.Body).Decode(&resp)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func DecodeDEPAccountInfoResponse(_ context.Context, r *http.Response) (interface{}, error) {
|
||||
if r.StatusCode != http.StatusOK {
|
||||
return nil, errorDecoder(r)
|
||||
|
||||
127
platform/config/apply_deptoken.go
Normal file
127
platform/config/apply_deptoken.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
|
||||
"github.com/fullsailor/pkcs7"
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/micromdm/micromdm/pkg/httputil"
|
||||
)
|
||||
|
||||
func (svc *ConfigService) ApplyDEPToken(ctx context.Context, P7MContent []byte) error {
|
||||
unwrapped, err := unwrapSMIME(P7MContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key, cert, err := svc.store.DEPKeypair()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p7, err := pkcs7.Parse(unwrapped)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
decrypted, err := p7.Decrypt(cert, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tokenJSON, err := unwrapTokenJSON(decrypted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var depToken DEPToken
|
||||
err = json.Unmarshal(tokenJSON, &depToken)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = svc.store.AddToken(depToken.ConsumerKey, tokenJSON)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("stored DEP token with ck", depToken.ConsumerKey)
|
||||
return nil
|
||||
}
|
||||
|
||||
type applyDEPTokenRequest struct {
|
||||
P7MContent []byte `json:"p7m_content"`
|
||||
}
|
||||
|
||||
type applyDEPTokenResponse struct {
|
||||
Err error `json:"err,omitempty"`
|
||||
}
|
||||
|
||||
func (r applyDEPTokenResponse) Failed() error { return r.Err }
|
||||
|
||||
func decodeApplyDEPTokensRequest(ctx context.Context, r *http.Request) (interface{}, error) {
|
||||
var req applyDEPTokenRequest
|
||||
err := httputil.DecodeJSONRequest(r, &req)
|
||||
return req, err
|
||||
}
|
||||
|
||||
func decodeApplyDEPTokensResponse(_ context.Context, r *http.Response) (interface{}, error) {
|
||||
var resp applyDEPTokenResponse
|
||||
err := httputil.DecodeJSONResponse(r, &resp)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func MakeApplyDEPTokensEndpoint(svc Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
|
||||
req := request.(applyDEPTokenRequest)
|
||||
err = svc.ApplyDEPToken(ctx, req.P7MContent)
|
||||
return applyDEPTokenResponse{
|
||||
Err: err,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (e Endpoints) ApplyDEPToken(ctx context.Context, P7MContent []byte) error {
|
||||
req := applyDEPTokenRequest{P7MContent: P7MContent}
|
||||
resp, err := e.ApplyDEPTokensEndpoint(ctx, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return resp.(applyDEPTokenResponse).Err
|
||||
}
|
||||
|
||||
// unwrapSMIME removes the S/MIME-like wrapper around raw CMS/PKCS7 data
|
||||
func unwrapSMIME(smime []byte) ([]byte, error) {
|
||||
tr := textproto.NewReader(bufio.NewReader(bytes.NewReader(smime)))
|
||||
if _, err := tr.ReadMIMEHeader(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dec := base64.NewDecoder(base64.StdEncoding, tr.DotReader())
|
||||
buf := new(bytes.Buffer)
|
||||
io.Copy(buf, dec)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// unwrapTokenJSON removes the MIME-like headers and text surrounding the DEP token JSON
|
||||
func unwrapTokenJSON(wrapped []byte) ([]byte, error) {
|
||||
tr := textproto.NewReader(bufio.NewReader(bytes.NewReader(wrapped)))
|
||||
if _, err := tr.ReadMIMEHeader(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tokenJSON := new(bytes.Buffer)
|
||||
for {
|
||||
line, err := tr.ReadLineBytes()
|
||||
if err != nil && err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
line = bytes.Trim(line, "-----BEGIN MESSAGE-----")
|
||||
line = bytes.Trim(line, "-----END MESSAGE-----")
|
||||
if _, err := tokenJSON.Write(line); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return tokenJSON.Bytes(), nil
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
// Package config provides an internal store for the configuration of the MDM server.
|
||||
package config
|
||||
package builtin
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -12,12 +11,12 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/micromdm/micromdm/pkg/crypto"
|
||||
"github.com/micromdm/micromdm/platform/config"
|
||||
"github.com/micromdm/micromdm/platform/pubsub"
|
||||
)
|
||||
|
||||
const (
|
||||
ConfigBucket = "mdm.ServerConfig"
|
||||
ConfigTopic = "mdm.ServerConfigUpdated"
|
||||
)
|
||||
|
||||
// DB stores server configuration in BoltDB
|
||||
@@ -44,7 +43,7 @@ func (db *DB) SavePushCertificate(cert, key []byte) error {
|
||||
if bkt == nil {
|
||||
return fmt.Errorf("config: bucket %q not found", ConfigBucket)
|
||||
}
|
||||
pb, err := MarshalServerConfig(&ServerConfig{
|
||||
pb, err := config.MarshalServerConfig(&config.ServerConfig{
|
||||
PushCertificate: cert,
|
||||
PrivateKey: key,
|
||||
})
|
||||
@@ -59,21 +58,21 @@ func (db *DB) SavePushCertificate(cert, key []byte) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := db.Publisher.Publish(context.TODO(), ConfigTopic, []byte("updated")); err != nil {
|
||||
if err := db.Publisher.Publish(context.TODO(), config.ConfigTopic, []byte("updated")); err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (db *DB) serverConfig() (*ServerConfig, error) {
|
||||
var conf ServerConfig
|
||||
func (db *DB) serverConfig() (*config.ServerConfig, error) {
|
||||
var conf config.ServerConfig
|
||||
err := db.View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket([]byte(ConfigBucket))
|
||||
data := bkt.Get([]byte("config"))
|
||||
if data == nil {
|
||||
return ¬Found{"ServerConfig", "no config found in boltdb"}
|
||||
}
|
||||
return UnmarshalServerConfig(data, &conf)
|
||||
return config.UnmarshalServerConfig(data, &conf)
|
||||
})
|
||||
return &conf, errors.Wrap(err, "get server config from bolt")
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package deptoken
|
||||
package builtin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -6,47 +6,16 @@ import (
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/boltdb/bolt"
|
||||
"github.com/micromdm/dep"
|
||||
|
||||
"github.com/micromdm/micromdm/pkg/crypto"
|
||||
"github.com/micromdm/micromdm/platform/pubsub"
|
||||
"github.com/micromdm/micromdm/platform/config"
|
||||
)
|
||||
|
||||
const (
|
||||
depTokenBucket = "mdm.DEPToken"
|
||||
|
||||
DEPTokenTopic = "mdm.TokenAdded"
|
||||
)
|
||||
|
||||
type DB struct {
|
||||
*bolt.DB
|
||||
Publisher pubsub.Publisher
|
||||
}
|
||||
|
||||
type DEPToken struct {
|
||||
ConsumerKey string `json:"consumer_key"`
|
||||
ConsumerSecret string `json:"consumer_secret"`
|
||||
AccessToken string `json:"access_token"`
|
||||
AccessSecret string `json:"access_secret"`
|
||||
AccessTokenExpiry time.Time `json:"access_token_expiry"`
|
||||
}
|
||||
|
||||
// create a DEP client from token.
|
||||
func (tok DEPToken) Client() (dep.Client, error) {
|
||||
conf := &dep.Config{
|
||||
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
|
||||
}
|
||||
|
||||
func (db *DB) AddToken(consumerKey string, json []byte) error {
|
||||
err := db.Update(func(tx *bolt.Tx) error {
|
||||
b, err := tx.CreateBucketIfNotExists([]byte(depTokenBucket))
|
||||
@@ -58,14 +27,14 @@ func (db *DB) AddToken(consumerKey string, json []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Publisher.Publish(context.TODO(), DEPTokenTopic, json); err != nil {
|
||||
if err := db.Publisher.Publish(context.TODO(), config.DEPTokenTopic, json); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) DEPTokens() ([]DEPToken, error) {
|
||||
var result []DEPToken
|
||||
func (db *DB) DEPTokens() ([]config.DEPToken, error) {
|
||||
var result []config.DEPToken
|
||||
err := db.View(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte(depTokenBucket))
|
||||
if b == nil {
|
||||
@@ -75,7 +44,7 @@ func (db *DB) DEPTokens() ([]DEPToken, error) {
|
||||
|
||||
prefix := []byte("CK_")
|
||||
for k, v := c.Seek(prefix); k != nil && bytes.HasPrefix(k, prefix); k, v = c.Next() {
|
||||
var depToken DEPToken
|
||||
var depToken config.DEPToken
|
||||
err := json.Unmarshal(v, &depToken)
|
||||
if err != nil {
|
||||
// TODO: log problematic DEP token, or remove altogether?
|
||||
@@ -1,16 +1,15 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/go-kit/kit/log"
|
||||
httptransport "github.com/go-kit/kit/transport/http"
|
||||
"github.com/micromdm/micromdm/pkg/httputil"
|
||||
)
|
||||
|
||||
func NewClient(instance string, logger log.Logger, token string, opts ...httptransport.ClientOption) (Service, error) {
|
||||
func NewHTTPClient(instance, token string, logger log.Logger, opts ...httptransport.ClientOption) (Service, error) {
|
||||
u, err := url.Parse(instance)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -18,31 +17,40 @@ func NewClient(instance string, logger log.Logger, token string, opts ...httptra
|
||||
|
||||
var saveEndpoint endpoint.Endpoint
|
||||
{
|
||||
|
||||
saveEndpoint = httptransport.NewClient(
|
||||
"PUT",
|
||||
copyURL(u, "/v1/config/certificate"),
|
||||
encodeRequestWithToken(token, EncodeHTTPGenericRequest),
|
||||
DecodeSavePushCertificateResponse,
|
||||
httputil.CopyURL(u, "/v1/config/certificate"),
|
||||
httputil.EncodeRequestWithToken(token, httptransport.EncodeJSONRequest),
|
||||
decodeSavePushCertificateResponse,
|
||||
opts...,
|
||||
).Endpoint()
|
||||
}
|
||||
|
||||
var applyDEPTokensEndpoint endpoint.Endpoint
|
||||
{
|
||||
applyDEPTokensEndpoint = httptransport.NewClient(
|
||||
"PUT",
|
||||
httputil.CopyURL(u, "/v1/dep-tokens"),
|
||||
httputil.EncodeRequestWithToken(token, httptransport.EncodeJSONRequest),
|
||||
decodeApplyDEPTokensResponse,
|
||||
opts...,
|
||||
).Endpoint()
|
||||
}
|
||||
|
||||
var getDEPTokensEndpoint endpoint.Endpoint
|
||||
{
|
||||
getDEPTokensEndpoint = httptransport.NewClient(
|
||||
"GET",
|
||||
httputil.CopyURL(u, "/v1/dep-tokens"),
|
||||
httputil.EncodeRequestWithToken(token, httptransport.EncodeJSONRequest),
|
||||
decodeGetDEPTokensResponse,
|
||||
opts...,
|
||||
).Endpoint()
|
||||
}
|
||||
|
||||
return Endpoints{
|
||||
SavePushCertificateEndpoint: saveEndpoint,
|
||||
ApplyDEPTokensEndpoint: applyDEPTokensEndpoint,
|
||||
GetDEPTokensEndpoint: getDEPTokensEndpoint,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func encodeRequestWithToken(token string, next httptransport.EncodeRequestFunc) httptransport.EncodeRequestFunc {
|
||||
return func(ctx context.Context, r *http.Request, request interface{}) error {
|
||||
r.SetBasicAuth("micromdm", token)
|
||||
return next(ctx, r, request)
|
||||
}
|
||||
}
|
||||
|
||||
func copyURL(base *url.URL, path string) *url.URL {
|
||||
next := *base
|
||||
next.Path = path
|
||||
return &next
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"github.com/micromdm/micromdm/platform/config/internal/configproto"
|
||||
)
|
||||
|
||||
const ConfigTopic = "mdm.ServerConfigUpdated"
|
||||
|
||||
// ServerConfig holds the configuration of the MDM Server.
|
||||
type ServerConfig struct {
|
||||
PushCertificate []byte
|
||||
|
||||
30
platform/config/deptoken.go
Normal file
30
platform/config/deptoken.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/micromdm/dep"
|
||||
)
|
||||
|
||||
const DEPTokenTopic = "mdm.TokenAdded"
|
||||
|
||||
type DEPToken struct {
|
||||
ConsumerKey string `json:"consumer_key"`
|
||||
ConsumerSecret string `json:"consumer_secret"`
|
||||
AccessToken string `json:"access_token"`
|
||||
AccessSecret string `json:"access_secret"`
|
||||
AccessTokenExpiry time.Time `json:"access_token_expiry"`
|
||||
}
|
||||
|
||||
// create a DEP client from token.
|
||||
func (tok DEPToken) Client() (dep.Client, error) {
|
||||
conf := &dep.Config{
|
||||
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
|
||||
}
|
||||
64
platform/config/get_deptoken.go
Normal file
64
platform/config/get_deptoken.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/micromdm/micromdm/pkg/httputil"
|
||||
)
|
||||
|
||||
func (svc *ConfigService) GetDEPTokens(ctx context.Context) ([]DEPToken, []byte, error) {
|
||||
_, cert, err := svc.store.DEPKeypair()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
var certBytes []byte
|
||||
if cert != nil {
|
||||
certBytes = cert.Raw
|
||||
}
|
||||
|
||||
tokens, err := svc.store.DEPTokens()
|
||||
if err != nil {
|
||||
return nil, certBytes, err
|
||||
}
|
||||
|
||||
return tokens, certBytes, nil
|
||||
}
|
||||
|
||||
type getDEPTokenResponse struct {
|
||||
DEPTokens []DEPToken `json:"dep_tokens"`
|
||||
DEPPubKey []byte `json:"public_key"`
|
||||
Err error `json:"err,omitempty"`
|
||||
}
|
||||
|
||||
func (r getDEPTokenResponse) Failed() error { return r.Err }
|
||||
|
||||
func decodeGetDEPTokensRequest(ctx context.Context, r *http.Request) (interface{}, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func decodeGetDEPTokensResponse(_ context.Context, r *http.Response) (interface{}, error) {
|
||||
var resp getDEPTokenResponse
|
||||
err := httputil.DecodeJSONResponse(r, &resp)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func MakeGetDEPTokensEndpoint(svc Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
|
||||
tokens, pubkey, err := svc.GetDEPTokens(ctx)
|
||||
return getDEPTokenResponse{
|
||||
DEPTokens: tokens,
|
||||
DEPPubKey: pubkey,
|
||||
Err: err,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (e Endpoints) GetDEPTokens(ctx context.Context) ([]DEPToken, []byte, error) {
|
||||
resp, err := e.GetDEPTokensEndpoint(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return resp.(getDEPTokenResponse).DEPTokens, resp.(getDEPTokenResponse).DEPPubKey, nil
|
||||
}
|
||||
@@ -2,12 +2,16 @@ package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/micromdm/micromdm/pkg/httputil"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type Endpoints struct {
|
||||
SavePushCertificateEndpoint endpoint.Endpoint
|
||||
func (svc *ConfigService) SavePushCertificate(ctx context.Context, cert, key []byte) error {
|
||||
err := svc.store.SavePushCertificate(cert, key)
|
||||
return errors.Wrap(err, "save push certificate")
|
||||
}
|
||||
|
||||
type saveRequest struct {
|
||||
@@ -16,10 +20,30 @@ type saveRequest struct {
|
||||
}
|
||||
|
||||
type saveResponse struct {
|
||||
Err error
|
||||
Err error `json:"err,omitempty"`
|
||||
}
|
||||
|
||||
func (r saveResponse) error() error { return r.Err }
|
||||
func (r saveResponse) Failed() error { return r.Err }
|
||||
|
||||
func decodeSavePushCertificateRequest(ctx context.Context, r *http.Request) (interface{}, error) {
|
||||
var req saveRequest
|
||||
err := httputil.DecodeJSONRequest(r, &req)
|
||||
return req, err
|
||||
}
|
||||
|
||||
func decodeSavePushCertificateResponse(_ context.Context, r *http.Response) (interface{}, error) {
|
||||
var resp saveResponse
|
||||
err := httputil.DecodeJSONResponse(r, &resp)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func MakeSavePushCertificateEndpoint(svc Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
|
||||
req := request.(saveRequest)
|
||||
err = svc.SavePushCertificate(ctx, req.Cert, req.Key)
|
||||
return saveResponse{Err: err}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (e Endpoints) SavePushCertificate(ctx context.Context, cert, key []byte) error {
|
||||
request := saveRequest{
|
||||
@@ -34,11 +58,3 @@ func (e Endpoints) SavePushCertificate(ctx context.Context, cert, key []byte) er
|
||||
|
||||
return response.(saveResponse).Err
|
||||
}
|
||||
|
||||
func MakeSavePushCertificateEndpoint(svc Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
|
||||
req := request.(saveRequest)
|
||||
err = svc.SavePushCertificate(ctx, req.Cert, req.Key)
|
||||
return saveResponse{Err: err}, nil
|
||||
}
|
||||
}
|
||||
54
platform/config/server.go
Normal file
54
platform/config/server.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/go-kit/kit/log"
|
||||
httptransport "github.com/go-kit/kit/transport/http"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/micromdm/micromdm/pkg/httputil"
|
||||
)
|
||||
|
||||
type Endpoints struct {
|
||||
ApplyDEPTokensEndpoint endpoint.Endpoint
|
||||
SavePushCertificateEndpoint endpoint.Endpoint
|
||||
GetDEPTokensEndpoint endpoint.Endpoint
|
||||
}
|
||||
|
||||
func MakeServerEndpoints(s Service) Endpoints {
|
||||
return Endpoints{
|
||||
ApplyDEPTokensEndpoint: MakeApplyDEPTokensEndpoint(s),
|
||||
SavePushCertificateEndpoint: MakeSavePushCertificateEndpoint(s),
|
||||
GetDEPTokensEndpoint: MakeGetDEPTokensEndpoint(s),
|
||||
}
|
||||
}
|
||||
|
||||
func MakeHTTPHandler(e Endpoints, logger log.Logger) *mux.Router {
|
||||
r, options := httputil.NewRouter(logger)
|
||||
|
||||
// PUT /v1/config/certificate create or replace the MDM Push Certificate
|
||||
// PUT /v1/dep-tokens create or replace a DEP OAuth token
|
||||
// GET /v1/dep-tokens get the OAuth Token used for the DEP client
|
||||
|
||||
r.Methods("PUT").Path("/v1/config/certificate").Handler(httptransport.NewServer(
|
||||
e.SavePushCertificateEndpoint,
|
||||
decodeSavePushCertificateRequest,
|
||||
httputil.EncodeJSONResponse,
|
||||
options...,
|
||||
))
|
||||
|
||||
r.Methods("PUT").Path("/v1/dep-tokens").Handler(httptransport.NewServer(
|
||||
e.ApplyDEPTokensEndpoint,
|
||||
decodeApplyDEPTokensRequest,
|
||||
httputil.EncodeJSONResponse,
|
||||
options...,
|
||||
))
|
||||
|
||||
r.Methods("GET").Path("/v1/dep-tokens").Handler(httptransport.NewServer(
|
||||
e.GetDEPTokensEndpoint,
|
||||
decodeGetDEPTokensRequest,
|
||||
httputil.EncodeJSONResponse,
|
||||
options...,
|
||||
))
|
||||
|
||||
return r
|
||||
}
|
||||
@@ -2,23 +2,30 @@ package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
)
|
||||
|
||||
type Service interface {
|
||||
SavePushCertificate(ctx context.Context, cert, key []byte) error
|
||||
ApplyDEPToken(ctx context.Context, P7MContent []byte) error
|
||||
GetDEPTokens(ctx context.Context) ([]DEPToken, []byte, error)
|
||||
}
|
||||
|
||||
type Store interface {
|
||||
SavePushCertificate(cert, key []byte) error
|
||||
PushCertificate() (*tls.Certificate, error)
|
||||
PushTopic() (string, error)
|
||||
DEPKeypair() (key *rsa.PrivateKey, cert *x509.Certificate, err error)
|
||||
AddToken(consumerKey string, json []byte) error
|
||||
DEPTokens() ([]DEPToken, error)
|
||||
}
|
||||
|
||||
type ConfigService struct {
|
||||
store *DB
|
||||
store Store
|
||||
}
|
||||
|
||||
func NewService(db *DB) *ConfigService {
|
||||
return &ConfigService{store: db}
|
||||
}
|
||||
|
||||
func (svc *ConfigService) SavePushCertificate(ctx context.Context, cert, key []byte) error {
|
||||
err := svc.store.SavePushCertificate(cert, key)
|
||||
return errors.Wrap(err, "save push certificate")
|
||||
func New(store Store) *ConfigService {
|
||||
return &ConfigService{store: store}
|
||||
}
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
httptransport "github.com/go-kit/kit/transport/http"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type HTTPHandlers struct {
|
||||
SavePushCertificateHandler http.Handler
|
||||
}
|
||||
|
||||
func MakeHTTPHandlers(ctx context.Context, endpoints Endpoints, opts ...httptransport.ServerOption) HTTPHandlers {
|
||||
h := HTTPHandlers{
|
||||
SavePushCertificateHandler: httptransport.NewServer(
|
||||
endpoints.SavePushCertificateEndpoint,
|
||||
decodeSavePushCertificateRequest,
|
||||
encodeResponse,
|
||||
opts...,
|
||||
),
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
func decodeSavePushCertificateRequest(ctx context.Context, r *http.Request) (interface{}, error) {
|
||||
var req saveRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return req, nil
|
||||
}
|
||||
|
||||
type errorWrapper struct {
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
type errorer interface {
|
||||
error() error
|
||||
}
|
||||
|
||||
func errorDecoder(r *http.Response) error {
|
||||
var w errorWrapper
|
||||
if err := json.NewDecoder(r.Body).Decode(&w); err != nil {
|
||||
return err
|
||||
}
|
||||
return errors.New(w.Error)
|
||||
}
|
||||
|
||||
func encodeResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error {
|
||||
if e, ok := response.(errorer); ok && e.error() != nil {
|
||||
EncodeError(ctx, e.error(), w)
|
||||
return nil
|
||||
}
|
||||
|
||||
enc := json.NewEncoder(w)
|
||||
enc.SetIndent("", " ")
|
||||
return enc.Encode(response)
|
||||
}
|
||||
|
||||
func EncodeError(ctx context.Context, err error, w http.ResponseWriter) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
enc := json.NewEncoder(w)
|
||||
enc.SetIndent("", " ")
|
||||
enc.Encode(errorWrapper{Error: err.Error()})
|
||||
}
|
||||
|
||||
// EncodeHTTPGenericRequest is a transport/http.EncodeRequestFunc that
|
||||
// JSON-encodes any request to the request body. Primarily useful in a client.
|
||||
func EncodeHTTPGenericRequest(_ context.Context, r *http.Request, request interface{}) error {
|
||||
var buf bytes.Buffer
|
||||
if err := json.NewEncoder(&buf).Encode(request); err != nil {
|
||||
return err
|
||||
}
|
||||
r.Body = ioutil.NopCloser(&buf)
|
||||
return nil
|
||||
}
|
||||
|
||||
func DecodeSavePushCertificateResponse(_ context.Context, r *http.Response) (interface{}, error) {
|
||||
if r.StatusCode != http.StatusOK {
|
||||
return nil, errorDecoder(r)
|
||||
}
|
||||
var resp saveResponse
|
||||
err := json.NewDecoder(r.Body).Decode(&resp)
|
||||
return resp, err
|
||||
}
|
||||
Reference in New Issue
Block a user