mirror of
https://github.com/micromdm/micromdm/
synced 2026-08-14 17:41:43 +08:00
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:
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user