refactor config api (#352)

Closes #300
Closes #303
This commit is contained in:
Victor Vrantchan
2017-12-10 00:39:04 -05:00
committed by GitHub
parent c5b1e8ccce
commit 5ff82344be
25 changed files with 421 additions and 458 deletions

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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")
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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,

View File

@@ -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

View File

@@ -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
}

View File

@@ -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)

View 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
}

View File

@@ -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 &notFound{"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")
}

View File

@@ -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?

View File

@@ -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
}

View File

@@ -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

View 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
}

View 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
}

View File

@@ -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
View 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
}

View File

@@ -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}
}

View File

@@ -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
}