mdmctl can upload/replace push certificate (#238)

Closes #236
This commit is contained in:
Victor Vrantchan
2017-10-15 21:08:48 -04:00
committed by GitHub
parent aacac39945
commit dfc8cca3d8
18 changed files with 889 additions and 65 deletions

View File

@@ -1,39 +1,77 @@
package push
import (
"crypto/tls"
"encoding/json"
"fmt"
"log"
"strings"
"sync"
"github.com/RobotsAndPencils/buford/payload"
"github.com/RobotsAndPencils/buford/push"
"github.com/pkg/errors"
"golang.org/x/net/context"
"github.com/micromdm/micromdm/config"
"github.com/micromdm/micromdm/pubsub"
"github.com/micromdm/micromdm/queue"
)
type Push struct {
db *DB
db *DB
start chan struct{}
provider PushCertificateProvider
mu sync.RWMutex
pushsvc *push.Service
}
func New(db *DB, push *push.Service, sub pubsub.Subscriber) (*Push, error) {
pushSvc := Push{db, push}
if err := pushSvc.startQueuedSubscriber(push, sub); err != nil {
type PushCertificateProvider interface {
PushCertificate() (*tls.Certificate, error)
}
type Option func(*Push)
func WithPushService(svc *push.Service) Option {
return func(p *Push) {
p.pushsvc = svc
}
}
func New(db *DB, provider PushCertificateProvider, sub pubsub.Subscriber, opts ...Option) (*Push, error) {
pushSvc := Push{
db: db,
provider: provider,
start: make(chan struct{}),
}
for _, opt := range opts {
opt(&pushSvc)
}
// if there is no push service, the push certificate hasn't been provided.
// start a goroutine that delays the run of this service.
if err := updateClient(&pushSvc, sub); err != nil {
return nil, errors.Wrap(err, "wait for push service config")
}
if err := pushSvc.startQueuedSubscriber(sub); err != nil {
return &pushSvc, err
}
return &pushSvc, nil
}
func (svc *Push) startQueuedSubscriber(push *push.Service, sub pubsub.Subscriber) error {
func (svc *Push) startQueuedSubscriber(sub pubsub.Subscriber) error {
commandQueuedEvents, err := sub.Subscribe(context.TODO(), "push-info", queue.CommandQueuedTopic)
if err != nil {
return errors.Wrapf(err,
"subscribing push to %s topic", queue.CommandQueuedTopic)
}
go func() {
if svc.pushsvc == nil {
log.Println("push: waiting for push certificate before enabling APNS service provider")
<-svc.start
log.Println("push: service started")
}
for {
select {
case event := <-commandQueuedEvents:
@@ -54,6 +92,45 @@ func (svc *Push) startQueuedSubscriber(push *push.Service, sub pubsub.Subscriber
return nil
}
func updateClient(svc *Push, sub pubsub.Subscriber) error {
configEvents, err := sub.Subscribe(context.TODO(), "push-server-configs", config.ConfigTopic)
if err != nil {
return errors.Wrap(err, "update push service client")
}
go func() {
for {
select {
case <-configEvents:
pushsvc, err := NewPushService(svc.provider)
if err != nil {
log.Println("push: could not get push certificate %s", err)
continue
}
svc.mu.Lock()
svc.pushsvc = pushsvc
svc.mu.Unlock()
go func() { svc.start <- struct{}{} }() // unblock queue
}
}
}()
return nil
}
func NewPushService(provider PushCertificateProvider) (*push.Service, error) {
cert, err := provider.PushCertificate()
if err != nil {
return nil, errors.Wrap(err, "get push certificate from store")
}
client, err := push.NewClient(*cert)
if err != nil {
return nil, errors.Wrap(err, "create push service client")
}
svc := push.NewService(client, push.Production)
return svc, nil
}
func (svc *Push) Push(ctx context.Context, deviceUDID string) (string, error) {
info, err := svc.db.PushInfo(deviceUDID)
if err != nil {