reload dep.Client when new token is added (#184)

on initial startup the DEP Client is not configured. Previously a user had to restart the server to get things running after adding the OAuth token. With this change, a new DEP client is created in all relevant services whenever the token is updated.
This commit is contained in:
Victor Vrantchan
2017-05-29 22:45:41 -04:00
committed by GitHub
parent 8a67b37557
commit 804276eeb2
8 changed files with 334 additions and 151 deletions

View File

@@ -2,7 +2,8 @@ package apply
import (
"context"
"fmt"
"log"
"sync"
"bufio"
"bytes"
@@ -13,11 +14,11 @@ import (
"github.com/fullsailor/pkcs7"
"github.com/boltdb/bolt"
"github.com/micromdm/dep"
"github.com/micromdm/micromdm/blueprint"
"github.com/micromdm/micromdm/core/list"
"github.com/micromdm/micromdm/deptoken"
"github.com/micromdm/micromdm/profile"
"github.com/micromdm/micromdm/pubsub"
)
type Service interface {
@@ -28,10 +29,44 @@ type Service interface {
}
type ApplyService struct {
DEPClient dep.Client
mtx sync.RWMutex
DEPClient dep.Client
Blueprints *blueprint.DB
Profiles *profile.DB
DB *bolt.DB // TODO: replace with reference to DEP token svc/pkg
Tokens *deptoken.DB
}
func (svc *ApplyService) WatchTokenUpdates(pubsub pubsub.Subscriber) error {
tokenAdded, err := pubsub.Subscribe("apply-token-events", deptoken.DEPTokenTopic)
if err != nil {
return err
}
go func() {
for {
select {
case event := <-tokenAdded:
var token deptoken.DEPToken
if err := json.Unmarshal(event.Message, &token); err != nil {
log.Printf("unmarshalling tokenAdded to token: %s\n", err)
continue
}
client, err := token.Client()
if err != nil {
log.Printf("creating new DEP client: %s\n", err)
continue
}
svc.mtx.Lock()
svc.DEPClient = client
svc.mtx.Unlock()
}
}
}()
return nil
}
func (svc *ApplyService) ApplyBlueprint(ctx context.Context, bp *blueprint.Blueprint) error {
@@ -73,28 +108,12 @@ func unwrapTokenJSON(wrapped []byte) ([]byte, error) {
return tokenJSON.Bytes(), nil
}
// TODO: move into seperate svc/pkg
const (
depTokenBucket = "mdm.DEPToken"
)
func PutDEPToken(db *bolt.DB, consumerKey string, json []byte) error {
err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucketIfNotExists([]byte(depTokenBucket))
if err != nil {
return err
}
return b.Put([]byte(consumerKey), json)
})
return err
}
func (svc *ApplyService) ApplyDEPToken(ctx context.Context, P7MContent []byte) error {
unwrapped, err := unwrapSMIME(P7MContent)
if err != nil {
return err
}
key, cert, err := list.GetDEPKeypair(svc.DB)
key, cert, err := svc.Tokens.DEPKeypair()
if err != nil {
return err
}
@@ -110,16 +129,16 @@ func (svc *ApplyService) ApplyDEPToken(ctx context.Context, P7MContent []byte) e
if err != nil {
return err
}
var depToken list.DEPToken
var depToken deptoken.DEPToken
err = json.Unmarshal(tokenJSON, &depToken)
if err != nil {
return err
}
err = PutDEPToken(svc.DB, depToken.ConsumerKey, tokenJSON)
err = svc.Tokens.AddToken(depToken.ConsumerKey, tokenJSON)
if err != nil {
return err
}
fmt.Println("stored DEP token with ck", depToken.ConsumerKey)
log.Println("stored DEP token with ck", depToken.ConsumerKey)
return nil
}

View File

@@ -2,6 +2,7 @@ package apply
import (
"context"
"errors"
"github.com/micromdm/dep"
)
@@ -11,5 +12,8 @@ type DEPService interface {
}
func (svc *ApplyService) DefineDEPProfile(ctx context.Context, p *dep.Profile) (*dep.ProfileResponse, error) {
if svc.DEPClient == nil {
return nil, errors.New("DEP not configured yet. add a DEP token to enable DEP")
}
return svc.DEPClient.DefineProfile(p)
}

View File

@@ -7,6 +7,7 @@ import (
"github.com/go-kit/kit/endpoint"
"github.com/micromdm/dep"
"github.com/micromdm/micromdm/blueprint"
"github.com/micromdm/micromdm/deptoken"
"github.com/micromdm/micromdm/profile"
)
@@ -29,7 +30,7 @@ func (e Endpoints) ListDevices(ctx context.Context, opts ListDevicesOption) ([]D
return response.(devicesResponse).Devices, response.(devicesResponse).Err
}
func (e Endpoints) GetDEPTokens(ctx context.Context) ([]DEPToken, []byte, error) {
func (e Endpoints) GetDEPTokens(ctx context.Context) ([]deptoken.DEPToken, []byte, error) {
resp, err := e.GetDEPTokensEndpoint(ctx, nil)
if err != nil {
return nil, nil, err
@@ -162,18 +163,10 @@ type devicesResponse struct {
Err error `json:"err,omitempty"`
}
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"`
}
type depTokenResponse struct {
DEPTokens []DEPToken `json:"dep_tokens"`
DEPPubKey []byte `json:"public_key"`
Err error `json:"err,omitempty"`
DEPTokens []deptoken.DEPToken `json:"dep_tokens"`
DEPPubKey []byte `json:"public_key"`
Err error `json:"err,omitempty"`
}
type blueprintsRequest struct{ Opts GetBlueprintsOption }

View File

@@ -1,20 +1,17 @@
package list
import (
"bytes"
"context"
"encoding/json"
"crypto/rsa"
"crypto/x509"
"github.com/boltdb/bolt"
"log"
"sync"
"github.com/micromdm/dep"
"github.com/micromdm/micromdm/blueprint"
"github.com/micromdm/micromdm/crypto"
"github.com/micromdm/micromdm/deptoken"
"github.com/micromdm/micromdm/device"
"github.com/micromdm/micromdm/profile"
"github.com/micromdm/micromdm/pubsub"
)
type ListDevicesOption struct {
@@ -35,18 +32,52 @@ type GetProfilesOption struct {
type Service interface {
ListDevices(ctx context.Context, opt ListDevicesOption) ([]DeviceDTO, error)
GetDEPTokens(ctx context.Context) ([]DEPToken, []byte, error)
GetDEPTokens(ctx context.Context) ([]deptoken.DEPToken, []byte, error)
GetBlueprints(ctx context.Context, opt GetBlueprintsOption) ([]blueprint.Blueprint, error)
GetProfiles(ctx context.Context, opt GetProfilesOption) ([]profile.Profile, error)
DEPService
}
type ListService struct {
DEPClient dep.Client
mtx sync.RWMutex
DEPClient dep.Client
Devices *device.DB
Blueprints *blueprint.DB
Profiles *profile.DB
DB *bolt.DB // TODO: replace with reference to DEP token svc/pkg
Tokens *deptoken.DB
}
func (svc *ListService) WatchTokenUpdates(pubsub pubsub.Subscriber) error {
tokenAdded, err := pubsub.Subscribe("list-token-events", deptoken.DEPTokenTopic)
if err != nil {
return err
}
go func() {
for {
select {
case event := <-tokenAdded:
var token deptoken.DEPToken
if err := json.Unmarshal(event.Message, &token); err != nil {
log.Printf("unmarshalling tokenAdded to token: %s\n", err)
continue
}
client, err := token.Client()
if err != nil {
log.Printf("creating new DEP client: %s\n", err)
continue
}
svc.mtx.Lock()
svc.DEPClient = client
svc.mtx.Unlock()
}
}
}()
return nil
}
func (svc *ListService) ListDevices(ctx context.Context, opt ListDevicesOption) ([]DeviceDTO, error) {
@@ -63,98 +94,8 @@ func (svc *ListService) ListDevices(ctx context.Context, opt ListDevicesOption)
return dto, err
}
// TODO: move into seperate svc/pkg
const (
depTokenBucket = "mdm.DEPToken"
)
// TODO: move into seperate svc/pkg
func GetDEPTokens(db *bolt.DB) ([]DEPToken, error) {
var result []DEPToken
err := db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(depTokenBucket))
if b == nil {
return nil
}
c := b.Cursor()
prefix := []byte("CK_")
for k, v := c.Seek(prefix); k != nil && bytes.HasPrefix(k, prefix); k, v = c.Next() {
var depToken DEPToken
err := json.Unmarshal(v, &depToken)
if err != nil {
// TODO: log problematic DEP token, or remove altogether?
continue
}
result = append(result, depToken)
}
return nil
})
return result, err
}
// TODO: move into seperate svc/pkg
func generateAndStoreDEPKeypair(db *bolt.DB) (key *rsa.PrivateKey, cert *x509.Certificate, err error) {
key, cert, err = crypto.SimpleSelfSignedRSAKeypair("micromdm-dep-token", 365)
if err != nil {
return
}
pkBytes := x509.MarshalPKCS1PrivateKey(key)
certBytes := cert.Raw
err = db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucketIfNotExists([]byte(depTokenBucket))
if err != nil {
return err
}
err = b.Put([]byte("key"), pkBytes)
if err != nil {
return err
}
err = b.Put([]byte("certificate"), certBytes)
if err != nil {
return err
}
return nil
})
return
}
// TODO: move into seperate svc/pkg
func GetDEPKeypair(db *bolt.DB) (key *rsa.PrivateKey, cert *x509.Certificate, err error) {
var keyBytes, certBytes []byte
err = db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(depTokenBucket))
if b == nil {
return nil
}
keyBytes = b.Get([]byte("key"))
certBytes = b.Get([]byte("certificate"))
return nil
})
if err != nil {
return
}
if keyBytes == nil || certBytes == nil {
// if there is no certificate or private key then generate
key, cert, err = generateAndStoreDEPKeypair(db)
} else {
key, err = x509.ParsePKCS1PrivateKey(keyBytes)
if err != nil {
return
}
cert, err = x509.ParseCertificate(certBytes)
if err != nil {
return
}
}
return
}
func (svc *ListService) GetDEPTokens(ctx context.Context) ([]DEPToken, []byte, error) {
_, cert, err := GetDEPKeypair(svc.DB)
func (svc *ListService) GetDEPTokens(ctx context.Context) ([]deptoken.DEPToken, []byte, error) {
_, cert, err := svc.Tokens.DEPKeypair()
if err != nil {
return nil, nil, err
}
@@ -163,7 +104,7 @@ func (svc *ListService) GetDEPTokens(ctx context.Context) ([]DEPToken, []byte, e
certBytes = cert.Raw
}
tokens, err := GetDEPTokens(svc.DB)
tokens, err := svc.Tokens.DEPTokens()
if err != nil {
return nil, certBytes, err
}

View File

@@ -2,6 +2,7 @@ package list
import (
"context"
"errors"
"github.com/micromdm/dep"
)
@@ -13,13 +14,22 @@ type DEPService interface {
}
func (svc *ListService) GetDEPAccountInfo(ctx context.Context) (*dep.Account, error) {
if svc.DEPClient == nil {
return nil, errors.New("DEP not configured yet. add a DEP token to enable DEP")
}
return svc.DEPClient.Account()
}
func (svc *ListService) GetDEPDevice(ctx context.Context, serials []string) (*dep.DeviceDetailsResponse, error) {
if svc.DEPClient == nil {
return nil, errors.New("DEP not configured yet. add a DEP token to enable DEP")
}
return svc.DEPClient.DeviceDetails(serials)
}
func (svc *ListService) GetDEPProfile(ctx context.Context, uuid string) (*dep.Profile, error) {
if svc.DEPClient == nil {
return nil, errors.New("DEP not configured yet. add a DEP token to enable DEP")
}
return svc.DEPClient.FetchProfile(uuid)
}