diff --git a/Gopkg.lock b/Gopkg.lock index bd8ac64f..b91d4da8 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -32,10 +32,10 @@ [[projects]] name = "github.com/go-kit/kit" packages = [ + "auth/basic", "endpoint", "log", "log/level", - "metrics", "transport/http" ] revision = "4dc7be5d2d12881735283bcab7352178e190fc71" @@ -60,10 +60,10 @@ version = "v0.5" [[projects]] - branch = "master" name = "github.com/golang/protobuf" packages = ["proto"] - revision = "130e6b02ab059e7b717a096f397c5b60111cae74" + revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265" + version = "v1.1.0" [[projects]] name = "github.com/gorilla/context" @@ -207,6 +207,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "3d247c7533ff5f494f214cccd89d30e7eb426b30218ac166eac3ae75220a21d2" + inputs-digest = "5f587f612f4f5f3dbad6cf659cb4eadfee35f886ea4dd7b795cf9fe9bfeb99e0" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 580e61a6..73190c18 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -1,4 +1,3 @@ - [[constraint]] name = "github.com/boltdb/bolt" version = "v1.3.0" @@ -26,3 +25,7 @@ [[constraint]] name = "github.com/groob/plist" branch = "master" + +[[constraint]] + name = "github.com/golang/protobuf" + version = "1.1.0" diff --git a/cmd/micromdm/serve.go b/cmd/micromdm/serve.go index 2f150a5d..952342bf 100644 --- a/cmd/micromdm/serve.go +++ b/cmd/micromdm/serve.go @@ -23,7 +23,6 @@ import ( "github.com/boltdb/bolt" "github.com/fullsailor/pkcs7" "github.com/go-kit/kit/auth/basic" - "github.com/go-kit/kit/endpoint" "github.com/go-kit/kit/log" "github.com/go-kit/kit/log/level" httptransport "github.com/go-kit/kit/transport/http" @@ -39,8 +38,7 @@ import ( "golang.org/x/crypto/pkcs12" "github.com/micromdm/micromdm/dep/depsync" - "github.com/micromdm/micromdm/mdm/checkin" - "github.com/micromdm/micromdm/mdm/connect" + "github.com/micromdm/micromdm/mdm" "github.com/micromdm/micromdm/mdm/enroll" "github.com/micromdm/micromdm/pkg/crypto" httputil2 "github.com/micromdm/micromdm/pkg/httputil" @@ -163,7 +161,6 @@ func serve(args []string) error { sm.setupConfigStore() sm.loadPushCerts() sm.setupSCEP(logger) - sm.setupCheckinService() sm.setupPushService(logger) sm.setupCommandService() sm.setupWebhooks() @@ -213,42 +210,15 @@ func serve(args []string) error { ctx := context.Background() httpLogger := log.With(logger, "transport", "http") - var checkinHandlers checkin.HTTPHandlers - { - e := checkin.Endpoints{ - CheckinEndpoint: checkin.MakeCheckinEndpoint(sm.checkinService), - } - opts := []httptransport.ServerOption{ - httptransport.ServerErrorLogger(httpLogger), - httptransport.ServerErrorEncoder(checkin.EncodeError), - } - checkinHandlers = checkin.MakeHTTPHandlers(ctx, e, opts...) - } - - connectOpts := []httptransport.ServerOption{ - httptransport.ServerErrorLogger(httpLogger), - httptransport.ServerErrorEncoder(connect.EncodeError), - } - - var connectEndpoint endpoint.Endpoint - { - connectEndpoint = connect.MakeConnectEndpoint(sm.connectService) - } - connectEndpoints := connect.Endpoints{ - ConnectEndpoint: connectEndpoint, - } - dc := sm.depClient appDB := &appsbuiltin.Repo{Path: *flRepoPath} - connectHandlers := connect.MakeHTTPHandlers(ctx, connectEndpoints, connectOpts...) scepHandler := scep.ServiceHandler(ctx, sm.scepService, httpLogger) enrollHandlers := enroll.MakeHTTPHandlers(ctx, enroll.MakeServerEndpoints(sm.enrollService, sm.scepDepot), httptransport.ServerErrorLogger(httpLogger)) r, options := httputil2.NewRouter(logger) + r.Handle("/version", version.Handler()) - r.Handle("/mdm/checkin", mdmAuthSignMessageMiddleware(sm.scepDepot, checkinHandlers.CheckinHandler)).Methods("PUT") - r.Handle("/mdm/connect", mdmAuthSignMessageMiddleware(sm.scepDepot, connectHandlers.ConnectHandler)).Methods("PUT") r.Handle("/mdm/enroll", enrollHandlers.EnrollHandler).Methods("GET", "POST") r.Handle("/ota/enroll", enrollHandlers.OTAEnrollHandler) r.Handle("/ota/phase23", enrollHandlers.OTAPhase2Phase3Handler).Methods("POST") @@ -259,6 +229,10 @@ func serve(args []string) error { }) } + signatureVerifier := &mdmSignatureVerifier{db: sm.scepDepot} + mdmEndpoints := mdm.MakeServerEndpoints(sm.mdmService) + mdm.RegisterHTTPHandlers(r, mdmEndpoints, signatureVerifier, logger) + // API commands. Only handled if the user provides an api key. if *flAPIKey != "" { basicAuthEndpointMiddleware := basic.AuthMiddleware("micromdm", *flAPIKey, "micromdm") @@ -411,8 +385,7 @@ type server struct { PushService *push.Service // bufford push pushService apns.Service - checkinService checkin.Service - connectService connect.Service + mdmService mdm.Service enrollService enroll.Service scepService scep.Service commandService command.Service @@ -447,7 +420,7 @@ func (c *server) setupWebhooks() { return } - h, err := webhook.NewCommandWebhook(c.webhooksHTTPClient, connect.ConnectTopic, c.CommandWebhookURL) + h, err := webhook.NewCommandWebhook(c.webhooksHTTPClient, mdm.ConnectTopic, c.CommandWebhookURL) if err != nil { c.err = err return @@ -488,25 +461,13 @@ func (c *server) setupCommandQueue(logger log.Logger) { return } - var connectService connect.Service + var mdmService mdm.Service { - svc, err := connect.New(q, c.pubclient) - if err != nil { - c.err = err - return - } - connectService = svc - connectService = connect.LoggingMiddleware(log.With(level.Info(logger), "component", "connect"))(svc) - connectService = block.RemoveMiddleware(c.removeDB)(connectService) + svc := mdm.NewService(c.pubclient, q) + mdmService = svc + mdmService = block.RemoveMiddleware(c.removeDB)(mdmService) } - c.connectService = connectService -} - -func (c *server) setupCheckinService() { - if c.err != nil { - return - } - c.checkinService, c.err = checkin.New(c.db, c.pubclient) + c.mdmService = mdmService } func (c *server) setupBolt() { @@ -794,66 +755,38 @@ func (c *server) setupSCEP(logger log.Logger) { } } -// TODO: move to separate package/library -func mdmAuthSignMessageMiddleware(db *boltdepot.Depot, next http.Handler) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - b64sig := r.Header.Get("Mdm-Signature") - if b64sig == "" { - http.Error(w, "Signature missing", http.StatusBadRequest) - return - } - sig, err := base64.StdEncoding.DecodeString(b64sig) - if err != nil { - http.Error(w, "Signature decoding error", http.StatusBadRequest) - return - } - p7, err := pkcs7.Parse(sig) - if err != nil { - http.Error(w, "Signature parsing error", http.StatusBadRequest) - return - } - bodyBuf, err := ioutil.ReadAll(r.Body) - if err != nil { - fmt.Println(err) - http.Error(w, "Problem reading request", http.StatusInternalServerError) - return - } +type mdmSignatureVerifier struct { + db *boltdepot.Depot +} - // the signed data is the HTTP body message - p7.Content = bodyBuf - - // reassign body to our already-read buffer - r.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBuf)) - // TODO: r.Body.Close() as we've ReadAll()'d it? - - err = p7.Verify() - if err != nil { - http.Error(w, "Signature verification error", http.StatusBadRequest) - return - } - - cert := p7.GetOnlySigner() - if cert == nil { - http.Error(w, "Invalid signer", http.StatusBadRequest) - return - } - - hasCN, err := HasCN(db, cert.Subject.CommonName, 0, cert, false) - if err != nil { - fmt.Println(err) - http.Error(w, "Unable to validate signature", http.StatusInternalServerError) - return - } - if !hasCN { - fmt.Println("Unauthorized client signature from:", cert.Subject.CommonName) - // NOTE: We're not returning 401 Unauthorized to avoid unenrolling a device - // this may change in the future - http.Error(w, "Unauthorized", http.StatusBadRequest) - return - } - - next.ServeHTTP(w, r) +func (v *mdmSignatureVerifier) VerifySignature(b64sig string, message []byte) error { + if b64sig == "" { + return errors.New("signature missing") } + sig, err := base64.StdEncoding.DecodeString(b64sig) + if err != nil { + return errors.Wrap(err, "decode MDM SignMessage header") + } + p7, err := pkcs7.Parse(sig) + if err != nil { + return errors.Wrap(err, "parse MDM SignMessage signature") + } + p7.Content = message + if err := p7.Verify(); err != nil { + return errors.Wrap(err, "verify MDM Signed Message") + } + cert := p7.GetOnlySigner() + if cert == nil { + return errors.New("invalid signer") + } + hasCN, err := HasCN(v.db, cert.Subject.CommonName, 0, cert, false) + if err != nil { + return errors.Wrap(err, "unable to validate signature") + } + if !hasCN { + return errors.Wrap(err, "Unauthorized client") + } + return nil } // implement HasCN function that belongs in micromdm/scep/depot/bolt diff --git a/mdm/acknowledge.go b/mdm/acknowledge.go new file mode 100644 index 00000000..3e1783df --- /dev/null +++ b/mdm/acknowledge.go @@ -0,0 +1,73 @@ +package mdm + +import ( + "context" + "net/http" + "time" + + "github.com/go-kit/kit/endpoint" + "github.com/gorilla/mux" + "github.com/groob/plist" + "github.com/pkg/errors" + uuid "github.com/satori/go.uuid" +) + +func (svc *MDMService) Acknowledge(ctx context.Context, req AcknowledgeEvent) (payload []byte, err error) { + msg, err := MarshalAcknowledgeEvent(&req) + if err != nil { + return nil, errors.Wrap(err, "marshal acknowledge response to proto") + } + + if err := svc.pub.Publish(ctx, ConnectTopic, msg); err != nil { + return nil, errors.Wrap(err, "publish connect Response on pubsub") + } + + payload, err = svc.queue.Next(ctx, req.Response) + return payload, errors.Wrap(err, "calling Next with mdm response") + +} + +type acknowledgeRequest struct { + Event AcknowledgeEvent +} + +type acknowledgeResponse struct { + Payload []byte + Err error `plist:"error,omitempty"` +} + +func (r acknowledgeResponse) Response() []byte { return r.Payload } +func (r acknowledgeResponse) Failed() error { return r.Err } + +func (d *requestDecoder) decodeAcknowledgeRequest(ctx context.Context, r *http.Request) (interface{}, error) { + body, err := d.readBody(r) + if err != nil { + return nil, errors.Wrap(err, "read acknowledge request body") + } + + var res Response + err = plist.Unmarshal(body, &res) + if err != nil { + return nil, errors.Wrap(err, "unmarshal MDM Response plist") + } + + params := mux.Vars(r) + + event := AcknowledgeEvent{ + ID: uuid.NewV4().String(), + Time: time.Now().UTC(), + Response: res, + Params: params, + Raw: body, + } + req := acknowledgeRequest{Event: event} + return req, nil +} + +func MakeAcknowledgeEndpoint(svc Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(acknowledgeRequest) + payload, err := svc.Acknowledge(ctx, req.Event) + return acknowledgeResponse{Payload: payload, Err: err}, nil + } +} diff --git a/mdm/connect/event.go b/mdm/acknowledge_event.go similarity index 50% rename from mdm/connect/event.go rename to mdm/acknowledge_event.go index 8546a1fb..e29ab21a 100644 --- a/mdm/connect/event.go +++ b/mdm/acknowledge_event.go @@ -1,33 +1,37 @@ -package connect +package mdm import ( "time" "github.com/gogo/protobuf/proto" - "github.com/micromdm/mdm" - uuid "github.com/satori/go.uuid" - - "github.com/micromdm/micromdm/mdm/connect/internal/connectproto" + "github.com/micromdm/micromdm/mdm/internal/connectproto" ) -type Event struct { +type AcknowledgeEvent struct { ID string Time time.Time - Response mdm.Response + Response Response + Params map[string]string Raw []byte } -func NewEvent(resp MDMConnectRequest) *Event { - event := Event{ - ID: uuid.NewV4().String(), - Time: time.Now().UTC(), - Response: resp.MDMResponse, - Raw: resp.Raw, - } - return &event +type Response struct { + RequestType string `json:"request_type,omitempty" plist:",omitempty"` + UDID string + UserID *string `json:"user_id,omitempty" plist:"UserID,omitempty"` + Status string + CommandUUID string + ErrorChain []ErrorChainItem `json:"error_chain" plist:",omitempty"` } -func MarshalEvent(e *Event) ([]byte, error) { +type ErrorChainItem struct { + ErrorCode int `json:"error_code,omitempty"` + ErrorDomain string `json:"error_domain,omitempty"` + LocalizedDescription string `json:"localized_description,omitempty"` + USEnglishDescription string `json:"us_english_description,omitempty"` +} + +func MarshalAcknowledgeEvent(e *AcknowledgeEvent) ([]byte, error) { response := &connectproto.Response{ CommandUuid: e.Response.CommandUUID, Udid: e.Response.UDID, @@ -42,11 +46,12 @@ func MarshalEvent(e *Event) ([]byte, error) { Id: e.ID, Time: e.Time.UnixNano(), Response: response, + Params: e.Params, Raw: e.Raw, }) } -func UnmarshalEvent(data []byte, e *Event) error { +func UnmarshalAcknowledgeEvent(data []byte, e *AcknowledgeEvent) error { var pb connectproto.Event if err := proto.Unmarshal(data, &pb); err != nil { return err @@ -57,14 +62,15 @@ func UnmarshalEvent(data []byte, e *Event) error { return nil } r := pb.GetResponse() - e.Response = mdm.Response{ + e.Response = Response{ UDID: r.GetUdid(), UserID: strPtr(r.GetUserId()), Status: r.GetStatus(), RequestType: r.GetRequestType(), CommandUUID: r.GetCommandUuid(), } - e.Raw = pb.Raw + e.Raw = pb.GetRaw() + e.Params = pb.GetParams() return nil } diff --git a/mdm/checkin.go b/mdm/checkin.go new file mode 100644 index 00000000..7eae930b --- /dev/null +++ b/mdm/checkin.go @@ -0,0 +1,105 @@ +package mdm + +import ( + "context" + "net/http" + "time" + + "github.com/go-kit/kit/endpoint" + "github.com/gorilla/mux" + "github.com/groob/plist" + "github.com/pkg/errors" + uuid "github.com/satori/go.uuid" +) + +func (svc *MDMService) Checkin(ctx context.Context, event CheckinEvent) error { + if event.Command.MessageType == "UserAuthenticate" { + return &rejectUserAuth{} + } + + msg, err := MarshalCheckinEvent(&event) + if err != nil { + return errors.Wrap(err, "marshal checkin event") + } + + topic, err := topicFromMessage(event.Command.MessageType) + if err != nil { + return errors.Wrap(err, "get checkin topic from message") + } + + err = svc.pub.Publish(ctx, topic, msg) + return errors.Wrapf(err, "publish checkin on topic: %s", topic) +} + +func topicFromMessage(messageType string) (string, error) { + switch messageType { + case "Authenticate": + return AuthenticateTopic, nil + case "TokenUpdate": + return TokenUpdateTopic, nil + case "CheckOut": + return CheckoutTopic, nil + default: + return "", errors.Errorf("unknown checkin message type %s", messageType) + } +} + +type rejectUserAuth struct{} + +func (e *rejectUserAuth) Error() string { + return "reject user auth" +} +func (e *rejectUserAuth) UserAuthReject() bool { + return true +} + +func isRejectedUserAuth(err error) bool { + type rejectUserAuthError interface { + error + UserAuthReject() bool + } + + _, ok := errors.Cause(err).(rejectUserAuthError) + return ok +} + +type checkinRequest struct { + Event CheckinEvent +} + +type checkinResponse struct { + Err error `plist:"error,omitempty"` +} + +func (r checkinResponse) Failed() error { return r.Err } + +func (d *requestDecoder) decodeCheckinRequest(ctx context.Context, r *http.Request) (interface{}, error) { + body, err := d.readBody(r) + if err != nil { + return nil, errors.Wrap(err, "read checkin request body") + } + + var cmd CheckinCommand + if err := plist.Unmarshal(body, &cmd); err != nil { + return nil, errors.Wrap(err, "unmarshal MDM Checkin Request plist") + } + + params := mux.Vars(r) + event := CheckinEvent{ + ID: uuid.NewV4().String(), + Time: time.Now().UTC(), + Command: cmd, + Params: params, + Raw: body, + } + req := checkinRequest{Event: event} + return req, nil +} + +func MakeCheckinEndpoint(svc Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(checkinRequest) + err := svc.Checkin(ctx, req.Event) + return checkinResponse{Err: err}, nil + } +} diff --git a/mdm/checkin/checkin.go b/mdm/checkin/checkin.go deleted file mode 100644 index f2c6d0f0..00000000 --- a/mdm/checkin/checkin.go +++ /dev/null @@ -1,99 +0,0 @@ -package checkin - -import ( - "fmt" - - "github.com/boltdb/bolt" - "github.com/micromdm/mdm" - "github.com/pkg/errors" - "golang.org/x/net/context" - - "github.com/micromdm/micromdm/platform/pubsub" -) - -// CheckinBucket is the *bolt.DB bucket where checkins are archived. -const CheckinBucket = "mdm.Checkin.ARCHIVE" - -// PubSub Topics where MDM Checkin events are published to. -const ( - AuthenticateTopic = "mdm.Authenticate" - TokenUpdateTopic = "mdm.TokenUpdate" - CheckoutTopic = "mdm.CheckOut" -) - -type Checkin struct { - db *bolt.DB - publisher pubsub.Publisher - archiveFn func(int64, []byte) error -} - -func New(db *bolt.DB, pub pubsub.Publisher) (*Checkin, error) { - err := db.Update(func(tx *bolt.Tx) error { - _, err := tx.CreateBucketIfNotExists([]byte(CheckinBucket)) - return err - }) - if err != nil { - return nil, errors.Wrapf(err, "creating %s bucket", CheckinBucket) - } - svc := Checkin{ - db: db, - publisher: pub, - } - svc.archiveFn = svc.archive - return &svc, nil -} - -func (svc *Checkin) Authenticate(ctx context.Context, cmd mdm.CheckinCommand) error { - if cmd.MessageType != "Authenticate" { - return fmt.Errorf("expected Authenticate, got %s MessageType", cmd.MessageType) - } - return svc.archiveAndPublish(AuthenticateTopic, cmd) -} - -func (svc *Checkin) TokenUpdate(ctx context.Context, cmd mdm.CheckinCommand) error { - if cmd.MessageType != "TokenUpdate" { - return fmt.Errorf("expected TokenUpdate, got %s MessageType", cmd.MessageType) - } - return svc.archiveAndPublish(TokenUpdateTopic, cmd) -} - -func (svc *Checkin) CheckOut(ctx context.Context, cmd mdm.CheckinCommand) error { - if cmd.MessageType != "CheckOut" { - return fmt.Errorf("expected CheckOut, but got %s MessageType", cmd.MessageType) - } - return svc.archiveAndPublish(CheckoutTopic, cmd) -} - -// archive events to BoltDB bucket using timestamp as key to preserve order. -func (svc *Checkin) archive(nano int64, msg []byte) error { - tx, err := svc.db.Begin(true) - if err != nil { - return errors.Wrap(err, "begin transaction") - } - defer tx.Rollback() - - bkt := tx.Bucket([]byte(CheckinBucket)) - if bkt == nil { - return fmt.Errorf("bucket %q not found!", CheckinBucket) - } - key := []byte(fmt.Sprintf("%d", nano)) - if err := bkt.Put(key, msg); err != nil { - return errors.Wrap(err, "put checkin event to boltdb") - } - return tx.Commit() -} - -func (svc *Checkin) archiveAndPublish(topic string, cmd mdm.CheckinCommand) error { - event := NewEvent(cmd) - msg, err := MarshalEvent(event) - if err != nil { - return errors.Wrap(err, "marshal checkin event") - } - if err := svc.archiveFn(event.Time.UnixNano(), msg); err != nil { - return errors.Wrap(err, "archive checkin") - } - if err := svc.publisher.Publish(context.TODO(), topic, msg); err != nil { - return errors.Wrapf(err, "publish checkin on topic: %s", topic) - } - return nil -} diff --git a/mdm/checkin/checkin_test.go b/mdm/checkin/checkin_test.go deleted file mode 100644 index 20ffa262..00000000 --- a/mdm/checkin/checkin_test.go +++ /dev/null @@ -1,299 +0,0 @@ -package checkin - -import ( - "context" - "errors" - "fmt" - "io/ioutil" - "os" - "reflect" - "testing" - - "github.com/boltdb/bolt" - "github.com/groob/plist" - "github.com/micromdm/mdm" -) - -func setupDB(t *testing.T) *Checkin { - f, _ := ioutil.TempFile("", "bolt-") - f.Close() - os.Remove(f.Name()) - - db, err := bolt.Open(f.Name(), 0777, nil) - if err != nil { - t.Fatalf("couldn't open bolt, err %s\n", err) - } - svc, err := New(db, nil) - if err != nil { - t.Fatalf("couldn't create service, err %s\n", err) - } - return svc -} - -func mustLoadCommand(t *testing.T, name string) mdm.CheckinCommand { - var payload mdm.CheckinCommand - data, err := ioutil.ReadFile("testdata/" + name + ".plist") - if err != nil { - t.Fatalf("failed to open test file %q.plist, err: %s", name, err) - } - if err := plist.Unmarshal(data, &payload); err != nil { - t.Fatalf("failed to unmarshal plist %q, err: %s", name, err) - } - return payload -} - -type mockPublisher struct { - Invoked bool - PublishFn func(string, []byte) error -} - -func (m *mockPublisher) Publish(ctx context.Context, s string, b []byte) error { - m.Invoked = true - return m.PublishFn(s, b) -} - -var passPublisher = func(string, []byte) error { return nil } -var failPublisher = func(string, []byte) error { - return errors.New("failed") -} - -// archiveFunc is the function signature for archiving events in BoltDB. -type archiveFunc func(int64, []byte) error - -// override the timestamp with a custom value when saving to BoltDB. -func archiveAt(timestamp int64, svc *Checkin) archiveFunc { - return func(nano int64, event []byte) error { - return svc.archive(timestamp, event) - } -} - -func archiveFail() archiveFunc { - return func(nano int64, event []byte) error { - return errors.New("archive failed") - } -} - -// load a specific event from the bolt bucket. -func loadEvent(t *testing.T, db *bolt.DB, nano int64) *Event { - var event Event - err := db.View(func(tx *bolt.Tx) error { - bkt := tx.Bucket([]byte(CheckinBucket)) - if bkt == nil { - return fmt.Errorf("no such bucket: CheckinBucket") - } - key := []byte(fmt.Sprintf("%d", nano)) - ev := bkt.Get(key) - if ev == nil { - return fmt.Errorf("no event at %d timestamp", nano) - } - return UnmarshalEvent(ev, &event) - }) - if err != nil { - t.Fatalf("error loading event: err = %q", err) - } - return &event -} - -func TestService_Authenticate(t *testing.T) { - svc := setupDB(t) - mock := &mockPublisher{} - svc.publisher = mock - tests := []struct { - name string - publisher func(string, []byte) error - archiveFn archiveFunc - request mdm.CheckinCommand - timestamp int64 - wantErr bool - }{ - { - name: "happy_path", - publisher: passPublisher, - request: mustLoadCommand(t, "Authenticate"), - archiveFn: archiveAt(1111, svc), - timestamp: 1111, - }, - { - name: "archive_fail", - publisher: passPublisher, - request: mustLoadCommand(t, "Authenticate"), - archiveFn: archiveFail(), - wantErr: true, - }, - { - name: "publisher_fail", - publisher: failPublisher, - request: mustLoadCommand(t, "Authenticate"), - archiveFn: svc.archive, - wantErr: true, - }, - { - name: "messageType_fail", - publisher: passPublisher, - request: mustLoadCommand(t, "CheckOut"), - archiveFn: svc.archive, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - mock.PublishFn = tt.publisher - mock.Invoked = false - svc.archiveFn = tt.archiveFn - err := svc.Authenticate(context.Background(), tt.request) - if (err != nil) != tt.wantErr { - t.Errorf("%q. Authenticate error = %v, wantErr %v", - tt.name, err, tt.wantErr) - return - } - if tt.wantErr { - return - } - - event := loadEvent(t, svc.db, tt.timestamp) - if !reflect.DeepEqual(event.Command, tt.request) { - t.Errorf("\nwant: %#v\n,\nhave: %#v\n", tt.request, event.Command) - } - - if !mock.Invoked { - t.Errorf("publisher not invoked") - } - }) - } -} - -func TestService_TokenUpdate(t *testing.T) { - svc := setupDB(t) - mock := &mockPublisher{} - svc.publisher = mock - tests := []struct { - name string - publisher func(string, []byte) error - archiveFn archiveFunc - request mdm.CheckinCommand - timestamp int64 - wantErr bool - }{ - { - name: "happy_path", - publisher: passPublisher, - request: mustLoadCommand(t, "TokenUpdate"), - archiveFn: archiveAt(2222, svc), - timestamp: 2222, - }, - { - name: "archive_fail", - publisher: passPublisher, - request: mustLoadCommand(t, "TokenUpdate"), - archiveFn: archiveFail(), - wantErr: true, - }, - { - name: "publisher_fail", - publisher: failPublisher, - request: mustLoadCommand(t, "TokenUpdate"), - archiveFn: svc.archive, - wantErr: true, - }, - { - name: "messageType_fail", - publisher: passPublisher, - request: mustLoadCommand(t, "CheckOut"), - archiveFn: svc.archive, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - mock.PublishFn = tt.publisher - mock.Invoked = false - svc.archiveFn = tt.archiveFn - err := svc.TokenUpdate(context.Background(), tt.request) - if (err != nil) != tt.wantErr { - t.Errorf("%q. TokenUpdate error = %v, wantErr %v", - tt.name, err, tt.wantErr) - return - } - if tt.wantErr { - return - } - - event := loadEvent(t, svc.db, tt.timestamp) - if !reflect.DeepEqual(event.Command, tt.request) { - t.Errorf("\nwant: %#v\n,\nhave: %#v\n", tt.request, event.Command) - } - - if !mock.Invoked { - t.Errorf("publisher not invoked") - } - }) - } -} - -func TestService_CheckOut(t *testing.T) { - svc := setupDB(t) - mock := &mockPublisher{} - svc.publisher = mock - tests := []struct { - name string - publisher func(string, []byte) error - archiveFn archiveFunc - request mdm.CheckinCommand - timestamp int64 - wantErr bool - }{ - { - name: "happy_path", - publisher: passPublisher, - request: mustLoadCommand(t, "CheckOut"), - archiveFn: archiveAt(1111, svc), - timestamp: 1111, - }, - { - name: "archive_fail", - publisher: passPublisher, - request: mustLoadCommand(t, "CheckOut"), - archiveFn: archiveFail(), - wantErr: true, - }, - { - name: "publisher_fail", - publisher: failPublisher, - request: mustLoadCommand(t, "CheckOut"), - archiveFn: svc.archive, - wantErr: true, - }, - { - name: "messageType_fail", - publisher: passPublisher, - request: mustLoadCommand(t, "Authenticate"), - archiveFn: svc.archive, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - mock.PublishFn = tt.publisher - mock.Invoked = false - svc.archiveFn = tt.archiveFn - err := svc.CheckOut(context.Background(), tt.request) - if (err != nil) != tt.wantErr { - t.Errorf("%q. CheckOut error = %v, wantErr %v", - tt.name, err, tt.wantErr) - return - } - if tt.wantErr { - return - } - - event := loadEvent(t, svc.db, tt.timestamp) - if !reflect.DeepEqual(event.Command, tt.request) { - t.Errorf("\nwant: %#v\n,\nhave: %#v\n", tt.request, event.Command) - } - - if !mock.Invoked { - t.Errorf("publisher not invoked") - } - }) - } -} diff --git a/mdm/checkin/endpoint.go b/mdm/checkin/endpoint.go deleted file mode 100644 index d5ed9622..00000000 --- a/mdm/checkin/endpoint.go +++ /dev/null @@ -1,67 +0,0 @@ -package checkin - -import ( - "context" - - "github.com/go-kit/kit/endpoint" - "github.com/micromdm/mdm" - "github.com/pkg/errors" -) - -// errInvalidMessageType is an invalid checking command. -var errInvalidMessageType = errors.New("invalid message type") - -type Endpoints struct { - CheckinEndpoint endpoint.Endpoint -} - -func MakeCheckinEndpoint(svc Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(checkinRequest) - var err error - switch req.MessageType { - case "Authenticate": - err = svc.Authenticate(ctx, req.CheckinCommand) - case "TokenUpdate": - err = svc.TokenUpdate(ctx, req.CheckinCommand) - case "CheckOut": - err = svc.CheckOut(ctx, req.CheckinCommand) - case "UserAuthenticate": - // TODO: to support per-user MDM. See #293 - err = &rejectUserAuth{} - default: - err = errInvalidMessageType - } - return checkinResponse{Err: err}, nil - } -} - -type checkinRequest struct { - mdm.CheckinCommand -} - -type checkinResponse struct { - Err error `plist:"error,omitempty"` -} - -func (r checkinResponse) error() error { return r.Err } - -type rejectUserAuth struct{} - -func (e *rejectUserAuth) Error() string { - return "reject user auth" -} - -func (e *rejectUserAuth) UserAuthReject() bool { - return true -} - -func isRejectedUserAuth(err error) bool { - type rejectUserAuthError interface { - error - UserAuthReject() bool - } - - _, ok := errors.Cause(err).(rejectUserAuthError) - return ok -} diff --git a/mdm/checkin/event_test.go b/mdm/checkin/event_test.go deleted file mode 100644 index 83599452..00000000 --- a/mdm/checkin/event_test.go +++ /dev/null @@ -1,49 +0,0 @@ -package checkin_test - -import ( - "io/ioutil" - "reflect" - "testing" - - "github.com/groob/plist" - "github.com/micromdm/mdm" - - "github.com/micromdm/micromdm/mdm/checkin" -) - -var marshalTests = []string{ - "Authenticate", - "TokenUpdate", - "CheckOut", -} - -func TestMarshalEvent(t *testing.T) { - for _, tt := range marshalTests { - name := tt - t.Run(name, func(t *testing.T) { - t.Parallel() - v := checkin.NewEvent(mustLoadCommand(t, name)) - var other checkin.Event - if buf, err := checkin.MarshalEvent(v); err != nil { - t.Fatal(err) - } else if err := checkin.UnmarshalEvent(buf, &other); err != nil { - t.Fatal(err) - } else if !reflect.DeepEqual(v, &other) { - t.Fatalf("\nwant: %#v\n \nhave: %#v\n", v, &other) - } - }) - } - -} - -func mustLoadCommand(t *testing.T, name string) mdm.CheckinCommand { - var payload mdm.CheckinCommand - data, err := ioutil.ReadFile("testdata/" + name + ".plist") - if err != nil { - t.Fatalf("failed to open test file %q.plist, err: %s", name, err) - } - if err := plist.Unmarshal(data, &payload); err != nil { - t.Fatalf("failed to unmarshal plist %q, err: %s", name, err) - } - return payload -} diff --git a/mdm/checkin/internal/checkinproto/checkin.pb.go b/mdm/checkin/internal/checkinproto/checkin.pb.go deleted file mode 100644 index a37231bb..00000000 --- a/mdm/checkin/internal/checkinproto/checkin.pb.go +++ /dev/null @@ -1,319 +0,0 @@ -// Code generated by protoc-gen-go. -// source: checkin.proto -// DO NOT EDIT! - -/* -Package checkinproto is a generated protocol buffer package. - -It is generated from these files: - checkin.proto - -It has these top-level messages: - Event - Command - Authenticate - TokenUpdate -*/ -package checkinproto - -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package - -type Event struct { - Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` - Time int64 `protobuf:"varint,2,opt,name=time" json:"time,omitempty"` - Command *Command `protobuf:"bytes,3,opt,name=command" json:"command,omitempty"` -} - -func (m *Event) Reset() { *m = Event{} } -func (m *Event) String() string { return proto.CompactTextString(m) } -func (*Event) ProtoMessage() {} -func (*Event) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } - -func (m *Event) GetId() string { - if m != nil { - return m.Id - } - return "" -} - -func (m *Event) GetTime() int64 { - if m != nil { - return m.Time - } - return 0 -} - -func (m *Event) GetCommand() *Command { - if m != nil { - return m.Command - } - return nil -} - -type Command struct { - MessageType string `protobuf:"bytes,1,opt,name=message_type,json=messageType" json:"message_type,omitempty"` - Topic string `protobuf:"bytes,2,opt,name=topic" json:"topic,omitempty"` - Udid string `protobuf:"bytes,3,opt,name=udid" json:"udid,omitempty"` - Authenticate *Authenticate `protobuf:"bytes,4,opt,name=authenticate" json:"authenticate,omitempty"` - TokenUpdate *TokenUpdate `protobuf:"bytes,5,opt,name=token_update,json=tokenUpdate" json:"token_update,omitempty"` -} - -func (m *Command) Reset() { *m = Command{} } -func (m *Command) String() string { return proto.CompactTextString(m) } -func (*Command) ProtoMessage() {} -func (*Command) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } - -func (m *Command) GetMessageType() string { - if m != nil { - return m.MessageType - } - return "" -} - -func (m *Command) GetTopic() string { - if m != nil { - return m.Topic - } - return "" -} - -func (m *Command) GetUdid() string { - if m != nil { - return m.Udid - } - return "" -} - -func (m *Command) GetAuthenticate() *Authenticate { - if m != nil { - return m.Authenticate - } - return nil -} - -func (m *Command) GetTokenUpdate() *TokenUpdate { - if m != nil { - return m.TokenUpdate - } - return nil -} - -type Authenticate struct { - OsVersion string `protobuf:"bytes,1,opt,name=os_version,json=osVersion" json:"os_version,omitempty"` - BuildVersion string `protobuf:"bytes,2,opt,name=build_version,json=buildVersion" json:"build_version,omitempty"` - ProductName string `protobuf:"bytes,3,opt,name=product_name,json=productName" json:"product_name,omitempty"` - SerialNumber string `protobuf:"bytes,4,opt,name=serial_number,json=serialNumber" json:"serial_number,omitempty"` - Imei string `protobuf:"bytes,5,opt,name=imei" json:"imei,omitempty"` - Meid string `protobuf:"bytes,6,opt,name=meid" json:"meid,omitempty"` - DeviceName string `protobuf:"bytes,7,opt,name=device_name,json=deviceName" json:"device_name,omitempty"` - Challenge []byte `protobuf:"bytes,8,opt,name=challenge,proto3" json:"challenge,omitempty"` - Model string `protobuf:"bytes,9,opt,name=model" json:"model,omitempty"` - ModelName string `protobuf:"bytes,10,opt,name=model_name,json=modelName" json:"model_name,omitempty"` -} - -func (m *Authenticate) Reset() { *m = Authenticate{} } -func (m *Authenticate) String() string { return proto.CompactTextString(m) } -func (*Authenticate) ProtoMessage() {} -func (*Authenticate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } - -func (m *Authenticate) GetOsVersion() string { - if m != nil { - return m.OsVersion - } - return "" -} - -func (m *Authenticate) GetBuildVersion() string { - if m != nil { - return m.BuildVersion - } - return "" -} - -func (m *Authenticate) GetProductName() string { - if m != nil { - return m.ProductName - } - return "" -} - -func (m *Authenticate) GetSerialNumber() string { - if m != nil { - return m.SerialNumber - } - return "" -} - -func (m *Authenticate) GetImei() string { - if m != nil { - return m.Imei - } - return "" -} - -func (m *Authenticate) GetMeid() string { - if m != nil { - return m.Meid - } - return "" -} - -func (m *Authenticate) GetDeviceName() string { - if m != nil { - return m.DeviceName - } - return "" -} - -func (m *Authenticate) GetChallenge() []byte { - if m != nil { - return m.Challenge - } - return nil -} - -func (m *Authenticate) GetModel() string { - if m != nil { - return m.Model - } - return "" -} - -func (m *Authenticate) GetModelName() string { - if m != nil { - return m.ModelName - } - return "" -} - -type TokenUpdate struct { - Token []byte `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"` - PushMagic string `protobuf:"bytes,2,opt,name=push_magic,json=pushMagic" json:"push_magic,omitempty"` - UnlockToken []byte `protobuf:"bytes,3,opt,name=unlock_token,json=unlockToken,proto3" json:"unlock_token,omitempty"` - AwaitingConfiguration bool `protobuf:"varint,4,opt,name=awaiting_configuration,json=awaitingConfiguration" json:"awaiting_configuration,omitempty"` - UserId string `protobuf:"bytes,5,opt,name=user_id,json=userId" json:"user_id,omitempty"` - UserLongName string `protobuf:"bytes,6,opt,name=user_long_name,json=userLongName" json:"user_long_name,omitempty"` - UserShortName string `protobuf:"bytes,7,opt,name=user_short_name,json=userShortName" json:"user_short_name,omitempty"` - NotOnConsole bool `protobuf:"varint,8,opt,name=not_on_console,json=notOnConsole" json:"not_on_console,omitempty"` -} - -func (m *TokenUpdate) Reset() { *m = TokenUpdate{} } -func (m *TokenUpdate) String() string { return proto.CompactTextString(m) } -func (*TokenUpdate) ProtoMessage() {} -func (*TokenUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } - -func (m *TokenUpdate) GetToken() []byte { - if m != nil { - return m.Token - } - return nil -} - -func (m *TokenUpdate) GetPushMagic() string { - if m != nil { - return m.PushMagic - } - return "" -} - -func (m *TokenUpdate) GetUnlockToken() []byte { - if m != nil { - return m.UnlockToken - } - return nil -} - -func (m *TokenUpdate) GetAwaitingConfiguration() bool { - if m != nil { - return m.AwaitingConfiguration - } - return false -} - -func (m *TokenUpdate) GetUserId() string { - if m != nil { - return m.UserId - } - return "" -} - -func (m *TokenUpdate) GetUserLongName() string { - if m != nil { - return m.UserLongName - } - return "" -} - -func (m *TokenUpdate) GetUserShortName() string { - if m != nil { - return m.UserShortName - } - return "" -} - -func (m *TokenUpdate) GetNotOnConsole() bool { - if m != nil { - return m.NotOnConsole - } - return false -} - -func init() { - proto.RegisterType((*Event)(nil), "checkinproto.Event") - proto.RegisterType((*Command)(nil), "checkinproto.Command") - proto.RegisterType((*Authenticate)(nil), "checkinproto.Authenticate") - proto.RegisterType((*TokenUpdate)(nil), "checkinproto.TokenUpdate") -} - -func init() { proto.RegisterFile("checkin.proto", fileDescriptor0) } - -var fileDescriptor0 = []byte{ - // 536 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x54, 0x92, 0x6f, 0x8b, 0xd3, 0x4e, - 0x10, 0xc7, 0x49, 0xef, 0x4f, 0x9b, 0xc9, 0xb6, 0x3f, 0x58, 0x7e, 0xa7, 0x51, 0x14, 0x7b, 0xf5, - 0x90, 0x3e, 0xaa, 0xa0, 0xf8, 0x4c, 0x04, 0x29, 0x3e, 0x10, 0xf4, 0x84, 0xf5, 0xf4, 0x91, 0x10, - 0xb6, 0xd9, 0x31, 0x5d, 0x9a, 0xec, 0x86, 0x64, 0x53, 0xb9, 0x97, 0xe4, 0x1b, 0xf0, 0x95, 0xf8, - 0x82, 0x64, 0x67, 0xd3, 0xbb, 0xf4, 0xd9, 0xcc, 0x67, 0xbe, 0x33, 0xb3, 0xf3, 0x4d, 0x60, 0x9a, - 0x6f, 0x31, 0xdf, 0x69, 0xb3, 0xaa, 0x1b, 0xeb, 0x2c, 0x67, 0x7d, 0x4a, 0xd9, 0xe2, 0x07, 0x9c, - 0x7d, 0xd8, 0xa3, 0x71, 0x7c, 0x06, 0x23, 0xad, 0xd2, 0x68, 0x1e, 0x2d, 0x63, 0x31, 0xd2, 0x8a, - 0x73, 0x38, 0x75, 0xba, 0xc2, 0x74, 0x34, 0x8f, 0x96, 0x27, 0x82, 0x62, 0xfe, 0x12, 0xc6, 0xb9, - 0xad, 0x2a, 0x69, 0x54, 0x7a, 0x32, 0x8f, 0x96, 0xc9, 0xab, 0x8b, 0xd5, 0x70, 0xd8, 0x6a, 0x1d, - 0x8a, 0xe2, 0xa0, 0x5a, 0xfc, 0x8d, 0x60, 0xdc, 0x43, 0x7e, 0x09, 0xac, 0xc2, 0xb6, 0x95, 0x05, - 0x66, 0xee, 0xb6, 0xc6, 0x7e, 0x55, 0xd2, 0xb3, 0x9b, 0xdb, 0x1a, 0xf9, 0xff, 0x70, 0xe6, 0x6c, - 0xad, 0x73, 0x5a, 0x1a, 0x8b, 0x90, 0xf8, 0x97, 0x74, 0x4a, 0x87, 0x95, 0xb1, 0xa0, 0x98, 0xbf, - 0x03, 0x26, 0x3b, 0xb7, 0x45, 0xe3, 0x74, 0x2e, 0x1d, 0xa6, 0xa7, 0xf4, 0x9c, 0xc7, 0xc7, 0xcf, - 0x79, 0x3f, 0x50, 0x88, 0x23, 0x3d, 0x7f, 0x0b, 0xcc, 0xd9, 0x1d, 0x9a, 0xac, 0xab, 0x95, 0xef, - 0x3f, 0xa3, 0xfe, 0x47, 0xc7, 0xfd, 0x37, 0x5e, 0xf1, 0x8d, 0x04, 0x22, 0x71, 0xf7, 0xc9, 0xe2, - 0xcf, 0x08, 0xd8, 0x70, 0x38, 0x7f, 0x0a, 0x60, 0xdb, 0x6c, 0x8f, 0x4d, 0xab, 0xad, 0xe9, 0x2f, - 0x8b, 0x6d, 0xfb, 0x3d, 0x00, 0xfe, 0x1c, 0xa6, 0x9b, 0x4e, 0x97, 0xea, 0x4e, 0x11, 0xee, 0x63, - 0x04, 0x0f, 0xa2, 0x4b, 0x60, 0x75, 0x63, 0x55, 0x97, 0xbb, 0xcc, 0xc8, 0x0a, 0xfb, 0x73, 0x93, - 0x9e, 0x5d, 0xcb, 0x0a, 0xfd, 0x9c, 0x16, 0x1b, 0x2d, 0xcb, 0xcc, 0x74, 0xd5, 0x06, 0x1b, 0x3a, - 0x3b, 0x16, 0x2c, 0xc0, 0x6b, 0x62, 0xde, 0x2e, 0x5d, 0xa1, 0xa6, 0x93, 0x62, 0x41, 0xb1, 0x67, - 0x15, 0x6a, 0x95, 0x9e, 0x07, 0xe6, 0x63, 0xfe, 0x0c, 0x12, 0x85, 0x7b, 0x9d, 0x63, 0x58, 0x37, - 0xa6, 0x12, 0x04, 0x44, 0xdb, 0x9e, 0x40, 0x9c, 0x6f, 0x65, 0x59, 0xa2, 0x29, 0x30, 0x9d, 0xcc, - 0xa3, 0x25, 0x13, 0xf7, 0xc0, 0x7f, 0xab, 0xca, 0x2a, 0x2c, 0xd3, 0x38, 0x7c, 0x2b, 0x4a, 0xbc, - 0x11, 0x14, 0x84, 0x99, 0x10, 0x8c, 0x20, 0xe2, 0x47, 0x2e, 0x7e, 0x8f, 0x20, 0x19, 0xb8, 0x1a, - 0x3e, 0xf8, 0x0e, 0x83, 0x65, 0x4c, 0x84, 0xc4, 0x0f, 0xa9, 0xbb, 0x76, 0x9b, 0x55, 0xb2, 0xb8, - 0xfb, 0x17, 0x62, 0x4f, 0x3e, 0x7b, 0xe0, 0x8d, 0xea, 0x4c, 0x69, 0xf3, 0x5d, 0x16, 0x7a, 0x4f, - 0xa8, 0x37, 0x09, 0x8c, 0xa6, 0xf3, 0x37, 0xf0, 0x40, 0xfe, 0x92, 0xda, 0x69, 0x53, 0x64, 0xb9, - 0x35, 0x3f, 0x75, 0xd1, 0x35, 0xd2, 0x79, 0xe7, 0xbd, 0x63, 0x13, 0x71, 0x71, 0xa8, 0xae, 0x87, - 0x45, 0xfe, 0x10, 0xc6, 0x5d, 0x8b, 0x4d, 0xa6, 0x55, 0xef, 0xde, 0xb9, 0x4f, 0x3f, 0x2a, 0x7e, - 0x05, 0x33, 0x2a, 0x94, 0xd6, 0x14, 0xe1, 0xb4, 0xe0, 0x24, 0xf3, 0xf4, 0x93, 0x35, 0x05, 0x19, - 0xf6, 0x02, 0xfe, 0x23, 0x55, 0xbb, 0xb5, 0x8d, 0x1b, 0xba, 0x3a, 0xf5, 0xf8, 0xab, 0xa7, 0xa4, - 0xbb, 0x82, 0x99, 0xb1, 0x2e, 0xb3, 0xc6, 0xbf, 0xad, 0xb5, 0x65, 0x70, 0x77, 0x22, 0x98, 0xb1, - 0xee, 0x8b, 0x59, 0x07, 0xb6, 0x39, 0xa7, 0x9f, 0xf0, 0xf5, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, - 0xac, 0x19, 0x1f, 0x55, 0xbf, 0x03, 0x00, 0x00, -} diff --git a/mdm/checkin/service.go b/mdm/checkin/service.go deleted file mode 100644 index b20a0665..00000000 --- a/mdm/checkin/service.go +++ /dev/null @@ -1,13 +0,0 @@ -package checkin - -import ( - "github.com/micromdm/mdm" - "golang.org/x/net/context" -) - -// Service defines methods for and MDM Check-in service. -type Service interface { - Authenticate(ctx context.Context, cmd mdm.CheckinCommand) error - TokenUpdate(ctx context.Context, cmd mdm.CheckinCommand) error - CheckOut(ctx context.Context, cmd mdm.CheckinCommand) error -} diff --git a/mdm/checkin/testdata/Authenticate.plist b/mdm/checkin/testdata/Authenticate.plist deleted file mode 100644 index fa926816..00000000 --- a/mdm/checkin/testdata/Authenticate.plist +++ /dev/null @@ -1,3 +0,0 @@ - - -AwaitingConfigurationBuildVersion15G31ChallengeYXBwbGU=DeviceNameHonest MistakeIMEIMEIDMessageTypeAuthenticateModelMacBookPro11,5ModelNameMacBook ProOSVersion10.11.6ProductNameMacBookPro11,5PushMagicSerialNumberC02RX6G8G8WPTokenTopiccom.apple.mgmt.XServer.8b4034c3-8cd9-4121-9999-cd2ddbf9a5b1UDIDFA01680E-98CA-5557-8F59-7716ECFEE964UnlockToken diff --git a/mdm/checkin/testdata/CheckOut.plist b/mdm/checkin/testdata/CheckOut.plist deleted file mode 100644 index d2be7150..00000000 --- a/mdm/checkin/testdata/CheckOut.plist +++ /dev/null @@ -1,3 +0,0 @@ - - -AwaitingConfigurationBuildVersionIMEIMEIDMessageTypeCheckOutModelOSVersionProductNamePushMagicSerialNumberTokenTopiccom.apple.mgmt.XServer.8b4034c3-8cd9-4121-9999-cd2ddbf9a5b1UDIDFA01680E-98CA-5557-8F59-7716ECFEE964UnlockToken diff --git a/mdm/checkin/testdata/TokenUpdate.plist b/mdm/checkin/testdata/TokenUpdate.plist deleted file mode 100644 index fa2296ff..00000000 --- a/mdm/checkin/testdata/TokenUpdate.plist +++ /dev/null @@ -1,3 +0,0 @@ - - -AwaitingConfigurationBuildVersionIMEIMEIDMessageTypeTokenUpdateModelOSVersionProductNamePushMagic5002D9F7-2FCC-45D0-BDFD-5CB0597F18B5SerialNumberTokenxEiNXIa4XVQOs+90kApgE8gTOAfEVQHPIURHRnJOv5Y=Topiccom.apple.mgmt.XServer.8b4034c3-8cd9-4121-9999-cd2ddbf9a5b1UDIDFA01680E-98CA-5557-8F59-7716ECFEE964UnlockToken diff --git a/mdm/checkin/transport_http.go b/mdm/checkin/transport_http.go deleted file mode 100644 index 5fc8484a..00000000 --- a/mdm/checkin/transport_http.go +++ /dev/null @@ -1,64 +0,0 @@ -package checkin - -import ( - "context" - "io" - "net/http" - - httptransport "github.com/go-kit/kit/transport/http" - "github.com/groob/plist" -) - -type HTTPHandlers struct { - // The CheckinHandler should accept PUT requests - CheckinHandler http.Handler -} - -func MakeHTTPHandlers(ctx context.Context, endpoints Endpoints, opts ...httptransport.ServerOption) HTTPHandlers { - h := HTTPHandlers{ - CheckinHandler: httptransport.NewServer( - endpoints.CheckinEndpoint, - decodeRequest, - encodeResponse, - opts..., - ), - } - return h -} - -type errorer interface { - error() error -} - -func decodeRequest(ctx context.Context, r *http.Request) (interface{}, error) { - var req checkinRequest - err := plist.NewDecoder(io.LimitReader(r.Body, 10000)).Decode(&req) - return req, err -} - -// According to the MDM Check-in protocol, the server must respond with 200 OK -// to successful Check-in requests. -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 - } - - w.WriteHeader(http.StatusOK) - return nil -} - -// EncodeError is used by the HTTP transport to encode service errors in HTTP. -// The EncodeError should be passed to the Go-Kit httptransport as the -// ServerErrorEncoder to encode error responses. -// According to the MDM Check-in protocol specification, the device only needs -// a 401 (Unauthorized) response in case of failure. -// In the case of a UserAuthenticate message we can also respond with 410 to -// reject user authentication attempts -func EncodeError(ctx context.Context, err error, w http.ResponseWriter) { - if isRejectedUserAuth(err) { - w.WriteHeader(http.StatusGone) - return - } - w.WriteHeader(http.StatusUnauthorized) -} diff --git a/mdm/checkin/event.go b/mdm/checkin_event.go similarity index 60% rename from mdm/checkin/event.go rename to mdm/checkin_event.go index 28839c80..6322e7b0 100644 --- a/mdm/checkin/event.go +++ b/mdm/checkin_event.go @@ -1,33 +1,75 @@ -package checkin +package mdm import ( + "encoding/hex" "time" "github.com/gogo/protobuf/proto" - "github.com/micromdm/mdm" - uuid "github.com/satori/go.uuid" - "github.com/micromdm/micromdm/mdm/checkin/internal/checkinproto" + "github.com/micromdm/micromdm/mdm/internal/checkinproto" ) -type Event struct { +type CheckinEvent struct { ID string Time time.Time - Command mdm.CheckinCommand + Command CheckinCommand + Params map[string]string + Raw []byte } -// NewEvent returns an Event with a unique ID and the current time. -func NewEvent(cmd mdm.CheckinCommand) *Event { - event := Event{ - ID: uuid.NewV4().String(), - Time: time.Now().UTC(), - Command: cmd, - } - return &event +// CheckinRequest represents an MDM checkin command struct. +type CheckinCommand struct { + // MessageType can be either Authenticate, + // TokenUpdate or CheckOut + MessageType string + Topic string + UDID string + auth + update } -// MarshalEvent serializes an event to a protocol buffer wire format. -func MarshalEvent(e *Event) ([]byte, error) { +// Authenticate Message Type +type auth struct { + OSVersion string + BuildVersion string + ProductName string + SerialNumber string + IMEI string + MEID string + DeviceName string `plist:"DeviceName,omitempty"` + Challenge []byte `plist:"Challenge,omitempty"` + Model string `plist:"Model,omitpempty"` + ModelName string `plist:"ModelName,omitempty"` +} + +// TokenUpdate Mesage Type +type update struct { + Token hexData + PushMagic string + UnlockToken hexData + AwaitingConfiguration bool + userTokenUpdate +} + +// TokenUpdate with user keys +type userTokenUpdate struct { + UserID string `plist:",omitempty"` + UserLongName string `plist:",omitempty"` + UserShortName string `plist:",omitempty"` + NotOnConsole bool `plist:",omitempty"` +} + +// data decodes to []byte, +// we can then attach a string method to the type +// Tokens are encoded as Hex Strings +type hexData []byte + +func (d hexData) String() string { + return hex.EncodeToString(d) +} + +// MarshalCheckinEvent serializes an event to a protocol buffer wire format. +func MarshalCheckinEvent(e *CheckinEvent) ([]byte, error) { command := &checkinproto.Command{ MessageType: e.Command.MessageType, Topic: e.Command.Topic, @@ -63,12 +105,14 @@ func MarshalEvent(e *Event) ([]byte, error) { Id: e.ID, Time: e.Time.UnixNano(), Command: command, + Params: e.Params, + Raw: e.Raw, }) } -// UnmarshalEvent parses a protocol buffer representation of data into +// UnmarshalCheckinEvent parses a protocol buffer representation of data into // the Event. -func UnmarshalEvent(data []byte, e *Event) error { +func UnmarshalCheckinEvent(data []byte, e *CheckinEvent) error { var pb checkinproto.Event if err := proto.Unmarshal(data, &pb); err != nil { return err @@ -78,7 +122,7 @@ func UnmarshalEvent(data []byte, e *Event) error { if pb.Command == nil { return nil } - e.Command = mdm.CheckinCommand{ + e.Command = CheckinCommand{ MessageType: pb.Command.MessageType, Topic: pb.Command.Topic, UDID: pb.Command.Udid, @@ -105,5 +149,7 @@ func UnmarshalEvent(data []byte, e *Event) error { e.Command.UserShortName = pb.Command.TokenUpdate.UserShortName e.Command.NotOnConsole = pb.Command.TokenUpdate.NotOnConsole } + e.Raw = pb.GetRaw() + e.Params = pb.GetParams() return nil } diff --git a/mdm/connect/connect.go b/mdm/connect/connect.go deleted file mode 100644 index ad6b9fea..00000000 --- a/mdm/connect/connect.go +++ /dev/null @@ -1,59 +0,0 @@ -package connect - -import ( - "context" - - "github.com/micromdm/mdm" - "github.com/pkg/errors" - - "github.com/micromdm/micromdm/platform/pubsub" - "github.com/micromdm/micromdm/platform/queue" -) - -const ConnectTopic = "mdm.Connect" - -// The Service accepts responses sent to an MDM server by an enrolled -// device. -type Service interface { - - // Acknowledge acknowledges a response sent by a device and returns - // the next payload if one is available. - Acknowledge(ctx context.Context, req MDMConnectRequest) (payload []byte, err error) -} - -type ConnectService struct { - queue Queue - pub pubsub.Publisher -} - -type Queue interface { - Next(context.Context, mdm.Response) (*queue.Command, error) -} - -func New(queue Queue, pub pubsub.Publisher) (*ConnectService, error) { - return &ConnectService{ - queue: queue, - pub: pub, - }, nil -} - -func (svc *ConnectService) Acknowledge(ctx context.Context, req MDMConnectRequest) (payload []byte, err error) { - event := NewEvent(req) - msg, err := MarshalEvent(event) - if err != nil { - return nil, errors.Wrap(err, "marshal connect response to proto") - } - if err := svc.pub.Publish(context.TODO(), ConnectTopic, msg); err != nil { - return nil, errors.Wrap(err, "publish connect Response on pubsub") - } - - cmd, err := svc.queue.Next(ctx, req.MDMResponse) - if err != nil { - return nil, errors.Wrap(err, "calling Next with mdm response") - } - // next can return no errors and no payload. - if cmd == nil { - return nil, nil - } - return cmd.Payload, nil -} diff --git a/mdm/connect/endpoint.go b/mdm/connect/endpoint.go deleted file mode 100644 index 19869f41..00000000 --- a/mdm/connect/endpoint.go +++ /dev/null @@ -1,32 +0,0 @@ -package connect - -import ( - "context" - - "github.com/go-kit/kit/endpoint" - "github.com/micromdm/mdm" -) - -type MDMConnectRequest struct { - Raw []byte - MDMResponse mdm.Response -} - -type mdmConnectResponse struct { - payload []byte - Err error `plist:"error,omitempty"` -} - -func (r mdmConnectResponse) error() error { return r.Err } - -type Endpoints struct { - ConnectEndpoint endpoint.Endpoint -} - -func MakeConnectEndpoint(svc Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(MDMConnectRequest) - payload, err := svc.Acknowledge(ctx, req) - return mdmConnectResponse{payload: payload, Err: err}, nil - } -} diff --git a/mdm/connect/internal/connectproto/connect.pb.go b/mdm/connect/internal/connectproto/connect.pb.go deleted file mode 100644 index a8cd321e..00000000 --- a/mdm/connect/internal/connectproto/connect.pb.go +++ /dev/null @@ -1,143 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: connect.proto - -/* -Package connectproto is a generated protocol buffer package. - -It is generated from these files: - connect.proto - -It has these top-level messages: - Event - Response -*/ -package connectproto - -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package - -type Event struct { - Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` - Time int64 `protobuf:"varint,2,opt,name=time" json:"time,omitempty"` - Response *Response `protobuf:"bytes,3,opt,name=response" json:"response,omitempty"` - Raw []byte `protobuf:"bytes,4,opt,name=raw,proto3" json:"raw,omitempty"` -} - -func (m *Event) Reset() { *m = Event{} } -func (m *Event) String() string { return proto.CompactTextString(m) } -func (*Event) ProtoMessage() {} -func (*Event) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } - -func (m *Event) GetId() string { - if m != nil { - return m.Id - } - return "" -} - -func (m *Event) GetTime() int64 { - if m != nil { - return m.Time - } - return 0 -} - -func (m *Event) GetResponse() *Response { - if m != nil { - return m.Response - } - return nil -} - -func (m *Event) GetRaw() []byte { - if m != nil { - return m.Raw - } - return nil -} - -type Response struct { - Udid string `protobuf:"bytes,1,opt,name=udid" json:"udid,omitempty"` - UserId string `protobuf:"bytes,2,opt,name=user_id,json=userId" json:"user_id,omitempty"` - Status string `protobuf:"bytes,3,opt,name=status" json:"status,omitempty"` - RequestType string `protobuf:"bytes,4,opt,name=request_type,json=requestType" json:"request_type,omitempty"` - CommandUuid string `protobuf:"bytes,5,opt,name=command_uuid,json=commandUuid" json:"command_uuid,omitempty"` -} - -func (m *Response) Reset() { *m = Response{} } -func (m *Response) String() string { return proto.CompactTextString(m) } -func (*Response) ProtoMessage() {} -func (*Response) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } - -func (m *Response) GetUdid() string { - if m != nil { - return m.Udid - } - return "" -} - -func (m *Response) GetUserId() string { - if m != nil { - return m.UserId - } - return "" -} - -func (m *Response) GetStatus() string { - if m != nil { - return m.Status - } - return "" -} - -func (m *Response) GetRequestType() string { - if m != nil { - return m.RequestType - } - return "" -} - -func (m *Response) GetCommandUuid() string { - if m != nil { - return m.CommandUuid - } - return "" -} - -func init() { - proto.RegisterType((*Event)(nil), "connectproto.Event") - proto.RegisterType((*Response)(nil), "connectproto.Response") -} - -func init() { proto.RegisterFile("connect.proto", fileDescriptor0) } - -var fileDescriptor0 = []byte{ - // 225 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0x8f, 0xb1, 0x4e, 0xc3, 0x30, - 0x10, 0x86, 0xe5, 0xa4, 0x0d, 0xcd, 0x35, 0x20, 0x74, 0x43, 0xf1, 0x18, 0x3a, 0x65, 0xca, 0x50, - 0x9e, 0x81, 0x81, 0xd5, 0x82, 0x39, 0x0a, 0xf1, 0x0d, 0x1e, 0x62, 0xa7, 0xf6, 0x19, 0xd4, 0x07, - 0xe1, 0x7d, 0x51, 0x8c, 0xa9, 0xba, 0xfd, 0xff, 0xef, 0x4f, 0xfe, 0x74, 0x70, 0x3f, 0x39, 0x6b, - 0x69, 0xe2, 0x7e, 0xf1, 0x8e, 0x1d, 0x36, 0xb9, 0xa6, 0x76, 0x3c, 0xc3, 0xf6, 0xf5, 0x8b, 0x2c, - 0xe3, 0x03, 0x14, 0x46, 0x4b, 0xd1, 0x8a, 0xae, 0x56, 0x85, 0xd1, 0x88, 0xb0, 0x61, 0x33, 0x93, - 0x2c, 0x5a, 0xd1, 0x95, 0x2a, 0x65, 0x3c, 0xc1, 0xce, 0x53, 0x58, 0x9c, 0x0d, 0x24, 0xcb, 0x56, - 0x74, 0xfb, 0xd3, 0xa1, 0xbf, 0xfd, 0xad, 0x57, 0xf9, 0x55, 0x5d, 0x39, 0x7c, 0x84, 0xd2, 0x8f, - 0xdf, 0x72, 0xd3, 0x8a, 0xae, 0x51, 0x6b, 0x3c, 0xfe, 0x08, 0xd8, 0xfd, 0x83, 0xab, 0x26, 0xea, - 0xab, 0x38, 0x65, 0x7c, 0x82, 0xbb, 0x18, 0xc8, 0x0f, 0x46, 0x27, 0x7b, 0xad, 0xaa, 0xb5, 0xbe, - 0x69, 0x3c, 0x40, 0x15, 0x78, 0xe4, 0x18, 0x92, 0xbd, 0x56, 0xb9, 0xe1, 0x33, 0x34, 0x9e, 0xce, - 0x91, 0x02, 0x0f, 0x7c, 0x59, 0x28, 0xc9, 0x6a, 0xb5, 0xcf, 0xdb, 0xfb, 0x65, 0xa1, 0x15, 0x99, - 0xdc, 0x3c, 0x8f, 0x56, 0x0f, 0x31, 0x1a, 0x2d, 0xb7, 0x7f, 0x48, 0xde, 0x3e, 0xa2, 0xd1, 0x9f, - 0x55, 0xba, 0xe1, 0xe5, 0x37, 0x00, 0x00, 0xff, 0xff, 0x0e, 0x78, 0x53, 0x38, 0x30, 0x01, 0x00, - 0x00, -} diff --git a/mdm/connect/middleware.go b/mdm/connect/middleware.go deleted file mode 100644 index 45870d56..00000000 --- a/mdm/connect/middleware.go +++ /dev/null @@ -1,41 +0,0 @@ -package connect - -import ( - "context" - "time" - - "github.com/go-kit/kit/log" -) - -type Middleware func(Service) Service - -func LoggingMiddleware(logger log.Logger) Middleware { - return func(next Service) Service { - return &loggingMiddleware{ - next: next, - logger: logger, - } - } -} - -type loggingMiddleware struct { - next Service - logger log.Logger -} - -func (mw loggingMiddleware) Acknowledge(ctx context.Context, req MDMConnectRequest) (payload []byte, err error) { - defer func(begin time.Time) { - _ = mw.logger.Log( - "method", "Acknowledge", - "udid", req.MDMResponse.UDID, - "command_uuid", req.MDMResponse.CommandUUID, - "status", req.MDMResponse.Status, - "request_type", req.MDMResponse.RequestType, - "err", err, - "took", time.Since(begin), - ) - }(time.Now()) - - payload, err = mw.next.Acknowledge(ctx, req) - return -} diff --git a/mdm/connect/transport_http.go b/mdm/connect/transport_http.go deleted file mode 100644 index fc59bf5e..00000000 --- a/mdm/connect/transport_http.go +++ /dev/null @@ -1,84 +0,0 @@ -package connect - -import ( - "context" - "io/ioutil" - "log" - "net/http" - - httptransport "github.com/go-kit/kit/transport/http" - "github.com/groob/plist" - "github.com/micromdm/mdm" -) - -type HTTPHandlers struct { - ConnectHandler http.Handler -} - -func MakeHTTPHandlers(ctx context.Context, endpoints Endpoints, opts ...httptransport.ServerOption) HTTPHandlers { - h := HTTPHandlers{ - ConnectHandler: httptransport.NewServer( - endpoints.ConnectEndpoint, - decodeRequest, - encodeResponse, - opts..., - ), - } - return h -} - -type errorer interface { - error() error -} - -func decodeRequest(ctx context.Context, r *http.Request) (interface{}, error) { - var res mdm.Response - - body, err := ioutil.ReadAll(r.Body) - if err != nil { - return nil, err - } - defer r.Body.Close() - - err = plist.Unmarshal(body, &res) - if err != nil { - return nil, err - } - - req := MDMConnectRequest{MDMResponse: res, Raw: body} - return req, nil -} - -// According to the MDM Check-in protocol, the server must respond with 200 OK -// to successful Check-in requests. -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 - } - - resp := response.(mdmConnectResponse) - - w.WriteHeader(http.StatusOK) - w.Write(resp.payload) - return nil -} - -// EncodeError is used by the HTTP transport to encode service errors in HTTP. -// The EncodeError should be passed to the Go-Kit httptransport as the -// ServerErrorEncoder to encode error responses. -func EncodeError(ctx context.Context, err error, w http.ResponseWriter) { - type checkoutErr interface { - error - Checkout() bool - } - if e, ok := err.(checkoutErr); ok { - if e.Checkout() { - log.Printf("connect: forced checkout error: %s\n", err) - w.WriteHeader(http.StatusUnauthorized) - return - } - } - log.Printf("connect error: %s\n", err) - w.WriteHeader(http.StatusInternalServerError) -} diff --git a/mdm/checkin/internal/checkinproto/checkin.go b/mdm/internal/checkinproto/checkin.go similarity index 100% rename from mdm/checkin/internal/checkinproto/checkin.go rename to mdm/internal/checkinproto/checkin.go diff --git a/mdm/internal/checkinproto/checkin.pb.go b/mdm/internal/checkinproto/checkin.pb.go new file mode 100644 index 00000000..4273c9b7 --- /dev/null +++ b/mdm/internal/checkinproto/checkin.pb.go @@ -0,0 +1,415 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: checkin.proto + +package checkinproto + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type Event struct { + Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` + Time int64 `protobuf:"varint,2,opt,name=time" json:"time,omitempty"` + Command *Command `protobuf:"bytes,3,opt,name=command" json:"command,omitempty"` + Raw []byte `protobuf:"bytes,4,opt,name=raw,proto3" json:"raw,omitempty"` + Params map[string]string `protobuf:"bytes,5,rep,name=params" json:"params,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Event) Reset() { *m = Event{} } +func (m *Event) String() string { return proto.CompactTextString(m) } +func (*Event) ProtoMessage() {} +func (*Event) Descriptor() ([]byte, []int) { + return fileDescriptor_checkin_7e1d11c144d2ce7a, []int{0} +} +func (m *Event) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Event.Unmarshal(m, b) +} +func (m *Event) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Event.Marshal(b, m, deterministic) +} +func (dst *Event) XXX_Merge(src proto.Message) { + xxx_messageInfo_Event.Merge(dst, src) +} +func (m *Event) XXX_Size() int { + return xxx_messageInfo_Event.Size(m) +} +func (m *Event) XXX_DiscardUnknown() { + xxx_messageInfo_Event.DiscardUnknown(m) +} + +var xxx_messageInfo_Event proto.InternalMessageInfo + +func (m *Event) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *Event) GetTime() int64 { + if m != nil { + return m.Time + } + return 0 +} + +func (m *Event) GetCommand() *Command { + if m != nil { + return m.Command + } + return nil +} + +func (m *Event) GetRaw() []byte { + if m != nil { + return m.Raw + } + return nil +} + +func (m *Event) GetParams() map[string]string { + if m != nil { + return m.Params + } + return nil +} + +type Command struct { + MessageType string `protobuf:"bytes,1,opt,name=message_type,json=messageType" json:"message_type,omitempty"` + Topic string `protobuf:"bytes,2,opt,name=topic" json:"topic,omitempty"` + Udid string `protobuf:"bytes,3,opt,name=udid" json:"udid,omitempty"` + Authenticate *Authenticate `protobuf:"bytes,4,opt,name=authenticate" json:"authenticate,omitempty"` + TokenUpdate *TokenUpdate `protobuf:"bytes,5,opt,name=token_update,json=tokenUpdate" json:"token_update,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Command) Reset() { *m = Command{} } +func (m *Command) String() string { return proto.CompactTextString(m) } +func (*Command) ProtoMessage() {} +func (*Command) Descriptor() ([]byte, []int) { + return fileDescriptor_checkin_7e1d11c144d2ce7a, []int{1} +} +func (m *Command) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Command.Unmarshal(m, b) +} +func (m *Command) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Command.Marshal(b, m, deterministic) +} +func (dst *Command) XXX_Merge(src proto.Message) { + xxx_messageInfo_Command.Merge(dst, src) +} +func (m *Command) XXX_Size() int { + return xxx_messageInfo_Command.Size(m) +} +func (m *Command) XXX_DiscardUnknown() { + xxx_messageInfo_Command.DiscardUnknown(m) +} + +var xxx_messageInfo_Command proto.InternalMessageInfo + +func (m *Command) GetMessageType() string { + if m != nil { + return m.MessageType + } + return "" +} + +func (m *Command) GetTopic() string { + if m != nil { + return m.Topic + } + return "" +} + +func (m *Command) GetUdid() string { + if m != nil { + return m.Udid + } + return "" +} + +func (m *Command) GetAuthenticate() *Authenticate { + if m != nil { + return m.Authenticate + } + return nil +} + +func (m *Command) GetTokenUpdate() *TokenUpdate { + if m != nil { + return m.TokenUpdate + } + return nil +} + +type Authenticate struct { + OsVersion string `protobuf:"bytes,1,opt,name=os_version,json=osVersion" json:"os_version,omitempty"` + BuildVersion string `protobuf:"bytes,2,opt,name=build_version,json=buildVersion" json:"build_version,omitempty"` + ProductName string `protobuf:"bytes,3,opt,name=product_name,json=productName" json:"product_name,omitempty"` + SerialNumber string `protobuf:"bytes,4,opt,name=serial_number,json=serialNumber" json:"serial_number,omitempty"` + Imei string `protobuf:"bytes,5,opt,name=imei" json:"imei,omitempty"` + Meid string `protobuf:"bytes,6,opt,name=meid" json:"meid,omitempty"` + DeviceName string `protobuf:"bytes,7,opt,name=device_name,json=deviceName" json:"device_name,omitempty"` + Challenge []byte `protobuf:"bytes,8,opt,name=challenge,proto3" json:"challenge,omitempty"` + Model string `protobuf:"bytes,9,opt,name=model" json:"model,omitempty"` + ModelName string `protobuf:"bytes,10,opt,name=model_name,json=modelName" json:"model_name,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Authenticate) Reset() { *m = Authenticate{} } +func (m *Authenticate) String() string { return proto.CompactTextString(m) } +func (*Authenticate) ProtoMessage() {} +func (*Authenticate) Descriptor() ([]byte, []int) { + return fileDescriptor_checkin_7e1d11c144d2ce7a, []int{2} +} +func (m *Authenticate) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Authenticate.Unmarshal(m, b) +} +func (m *Authenticate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Authenticate.Marshal(b, m, deterministic) +} +func (dst *Authenticate) XXX_Merge(src proto.Message) { + xxx_messageInfo_Authenticate.Merge(dst, src) +} +func (m *Authenticate) XXX_Size() int { + return xxx_messageInfo_Authenticate.Size(m) +} +func (m *Authenticate) XXX_DiscardUnknown() { + xxx_messageInfo_Authenticate.DiscardUnknown(m) +} + +var xxx_messageInfo_Authenticate proto.InternalMessageInfo + +func (m *Authenticate) GetOsVersion() string { + if m != nil { + return m.OsVersion + } + return "" +} + +func (m *Authenticate) GetBuildVersion() string { + if m != nil { + return m.BuildVersion + } + return "" +} + +func (m *Authenticate) GetProductName() string { + if m != nil { + return m.ProductName + } + return "" +} + +func (m *Authenticate) GetSerialNumber() string { + if m != nil { + return m.SerialNumber + } + return "" +} + +func (m *Authenticate) GetImei() string { + if m != nil { + return m.Imei + } + return "" +} + +func (m *Authenticate) GetMeid() string { + if m != nil { + return m.Meid + } + return "" +} + +func (m *Authenticate) GetDeviceName() string { + if m != nil { + return m.DeviceName + } + return "" +} + +func (m *Authenticate) GetChallenge() []byte { + if m != nil { + return m.Challenge + } + return nil +} + +func (m *Authenticate) GetModel() string { + if m != nil { + return m.Model + } + return "" +} + +func (m *Authenticate) GetModelName() string { + if m != nil { + return m.ModelName + } + return "" +} + +type TokenUpdate struct { + Token []byte `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"` + PushMagic string `protobuf:"bytes,2,opt,name=push_magic,json=pushMagic" json:"push_magic,omitempty"` + UnlockToken []byte `protobuf:"bytes,3,opt,name=unlock_token,json=unlockToken,proto3" json:"unlock_token,omitempty"` + AwaitingConfiguration bool `protobuf:"varint,4,opt,name=awaiting_configuration,json=awaitingConfiguration" json:"awaiting_configuration,omitempty"` + UserId string `protobuf:"bytes,5,opt,name=user_id,json=userId" json:"user_id,omitempty"` + UserLongName string `protobuf:"bytes,6,opt,name=user_long_name,json=userLongName" json:"user_long_name,omitempty"` + UserShortName string `protobuf:"bytes,7,opt,name=user_short_name,json=userShortName" json:"user_short_name,omitempty"` + NotOnConsole bool `protobuf:"varint,8,opt,name=not_on_console,json=notOnConsole" json:"not_on_console,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *TokenUpdate) Reset() { *m = TokenUpdate{} } +func (m *TokenUpdate) String() string { return proto.CompactTextString(m) } +func (*TokenUpdate) ProtoMessage() {} +func (*TokenUpdate) Descriptor() ([]byte, []int) { + return fileDescriptor_checkin_7e1d11c144d2ce7a, []int{3} +} +func (m *TokenUpdate) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_TokenUpdate.Unmarshal(m, b) +} +func (m *TokenUpdate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_TokenUpdate.Marshal(b, m, deterministic) +} +func (dst *TokenUpdate) XXX_Merge(src proto.Message) { + xxx_messageInfo_TokenUpdate.Merge(dst, src) +} +func (m *TokenUpdate) XXX_Size() int { + return xxx_messageInfo_TokenUpdate.Size(m) +} +func (m *TokenUpdate) XXX_DiscardUnknown() { + xxx_messageInfo_TokenUpdate.DiscardUnknown(m) +} + +var xxx_messageInfo_TokenUpdate proto.InternalMessageInfo + +func (m *TokenUpdate) GetToken() []byte { + if m != nil { + return m.Token + } + return nil +} + +func (m *TokenUpdate) GetPushMagic() string { + if m != nil { + return m.PushMagic + } + return "" +} + +func (m *TokenUpdate) GetUnlockToken() []byte { + if m != nil { + return m.UnlockToken + } + return nil +} + +func (m *TokenUpdate) GetAwaitingConfiguration() bool { + if m != nil { + return m.AwaitingConfiguration + } + return false +} + +func (m *TokenUpdate) GetUserId() string { + if m != nil { + return m.UserId + } + return "" +} + +func (m *TokenUpdate) GetUserLongName() string { + if m != nil { + return m.UserLongName + } + return "" +} + +func (m *TokenUpdate) GetUserShortName() string { + if m != nil { + return m.UserShortName + } + return "" +} + +func (m *TokenUpdate) GetNotOnConsole() bool { + if m != nil { + return m.NotOnConsole + } + return false +} + +func init() { + proto.RegisterType((*Event)(nil), "checkinproto.Event") + proto.RegisterMapType((map[string]string)(nil), "checkinproto.Event.ParamsEntry") + proto.RegisterType((*Command)(nil), "checkinproto.Command") + proto.RegisterType((*Authenticate)(nil), "checkinproto.Authenticate") + proto.RegisterType((*TokenUpdate)(nil), "checkinproto.TokenUpdate") +} + +func init() { proto.RegisterFile("checkin.proto", fileDescriptor_checkin_7e1d11c144d2ce7a) } + +var fileDescriptor_checkin_7e1d11c144d2ce7a = []byte{ + // 602 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x54, 0x92, 0xdf, 0x6e, 0xd3, 0x30, + 0x14, 0xc6, 0x95, 0x74, 0x6d, 0x97, 0x93, 0x6c, 0x20, 0x8b, 0x41, 0x98, 0x40, 0xeb, 0xca, 0x84, + 0x7a, 0x55, 0xa4, 0x21, 0xc4, 0x1f, 0x21, 0x24, 0x34, 0xed, 0x02, 0x09, 0x06, 0x32, 0x83, 0xdb, + 0xc8, 0x4d, 0x4c, 0x6a, 0x35, 0xb1, 0xa3, 0xc4, 0xe9, 0xd4, 0x47, 0xe2, 0x05, 0x78, 0x12, 0x9e, + 0x80, 0x27, 0x41, 0xe7, 0x38, 0xdd, 0xd2, 0xbb, 0x73, 0x7e, 0xfe, 0xfc, 0xe5, 0x9c, 0x2f, 0x86, + 0x83, 0x74, 0x29, 0xd3, 0x95, 0xd2, 0xf3, 0xaa, 0x36, 0xd6, 0xb0, 0xa8, 0x6b, 0xa9, 0x9b, 0xfe, + 0xf3, 0x60, 0x78, 0xb9, 0x96, 0xda, 0xb2, 0x43, 0xf0, 0x55, 0x16, 0x7b, 0x13, 0x6f, 0x16, 0x70, + 0x5f, 0x65, 0x8c, 0xc1, 0x9e, 0x55, 0xa5, 0x8c, 0xfd, 0x89, 0x37, 0x1b, 0x70, 0xaa, 0xd9, 0x0b, + 0x18, 0xa7, 0xa6, 0x2c, 0x85, 0xce, 0xe2, 0xc1, 0xc4, 0x9b, 0x85, 0xe7, 0x47, 0xf3, 0xbe, 0xdb, + 0xfc, 0xc2, 0x1d, 0xf2, 0xad, 0x8a, 0xdd, 0x87, 0x41, 0x2d, 0x6e, 0xe2, 0xbd, 0x89, 0x37, 0x8b, + 0x38, 0x96, 0xec, 0x35, 0x8c, 0x2a, 0x51, 0x8b, 0xb2, 0x89, 0x87, 0x93, 0xc1, 0x2c, 0x3c, 0x3f, + 0xd9, 0x75, 0xa0, 0x59, 0xe6, 0xdf, 0x48, 0x71, 0xa9, 0x6d, 0xbd, 0xe1, 0x9d, 0xfc, 0xf8, 0x2d, + 0x84, 0x3d, 0x8c, 0xce, 0x2b, 0xb9, 0xe9, 0xe6, 0xc5, 0x92, 0x3d, 0x80, 0xe1, 0x5a, 0x14, 0xad, + 0x9b, 0x38, 0xe0, 0xae, 0x79, 0xe7, 0xbf, 0xf1, 0xa6, 0x7f, 0x3d, 0x18, 0x77, 0xa3, 0xb1, 0x53, + 0x88, 0x4a, 0xd9, 0x34, 0x22, 0x97, 0x89, 0xdd, 0x54, 0xb2, 0x33, 0x08, 0x3b, 0x76, 0xbd, 0xa9, + 0x24, 0x1a, 0x59, 0x53, 0xa9, 0x74, 0x6b, 0x44, 0x0d, 0xe6, 0xd1, 0x66, 0xca, 0x2d, 0x1e, 0x70, + 0xaa, 0xd9, 0x07, 0x88, 0x44, 0x6b, 0x97, 0x52, 0x5b, 0x95, 0x0a, 0x2b, 0x69, 0xcf, 0xf0, 0xfc, + 0x78, 0x77, 0xa5, 0x8f, 0x3d, 0x05, 0xdf, 0xd1, 0xb3, 0xf7, 0x10, 0x59, 0xb3, 0x92, 0x3a, 0x69, + 0xab, 0x0c, 0xef, 0x0f, 0xe9, 0xfe, 0xe3, 0xdd, 0xfb, 0xd7, 0xa8, 0xf8, 0x41, 0x02, 0x1e, 0xda, + 0xbb, 0x66, 0xfa, 0xc7, 0x87, 0xa8, 0x6f, 0xce, 0x9e, 0x02, 0x98, 0x26, 0x59, 0xcb, 0xba, 0x51, + 0x46, 0x77, 0x9b, 0x05, 0xa6, 0xf9, 0xe9, 0x00, 0x7b, 0x06, 0x07, 0x8b, 0x56, 0x15, 0xd9, 0xad, + 0xc2, 0xed, 0x17, 0x11, 0xdc, 0x8a, 0x4e, 0x21, 0xaa, 0x6a, 0x93, 0xb5, 0xa9, 0x4d, 0xb4, 0x28, + 0x65, 0xb7, 0x6e, 0xd8, 0xb1, 0x2b, 0x51, 0x4a, 0xf4, 0x69, 0x64, 0xad, 0x44, 0x91, 0xe8, 0xb6, + 0x5c, 0xc8, 0x9a, 0xd6, 0x0e, 0x78, 0xe4, 0xe0, 0x15, 0x31, 0x8c, 0x4b, 0x95, 0x52, 0xd1, 0x4a, + 0x01, 0xa7, 0x1a, 0x59, 0x29, 0x55, 0x16, 0x8f, 0x1c, 0xc3, 0x9a, 0x9d, 0x40, 0x98, 0xc9, 0xb5, + 0x4a, 0xa5, 0xfb, 0xdc, 0x98, 0x8e, 0xc0, 0x21, 0xfa, 0xda, 0x13, 0x08, 0xd2, 0xa5, 0x28, 0x0a, + 0xa9, 0x73, 0x19, 0xef, 0xd3, 0x43, 0xba, 0x03, 0xf8, 0xaf, 0x4a, 0x93, 0xc9, 0x22, 0x0e, 0xdc, + 0xbf, 0xa2, 0x06, 0x83, 0xa0, 0xc2, 0x79, 0x82, 0x0b, 0x82, 0x08, 0x5a, 0x4e, 0x7f, 0xfb, 0x10, + 0xf6, 0x52, 0x75, 0x3f, 0x7c, 0x25, 0x5d, 0x64, 0x11, 0x77, 0x0d, 0x9a, 0x54, 0x6d, 0xb3, 0x4c, + 0x4a, 0x91, 0xdf, 0xbe, 0x85, 0x00, 0xc9, 0x17, 0x04, 0x18, 0x54, 0xab, 0x0b, 0x93, 0xae, 0x12, + 0x77, 0x77, 0x40, 0x77, 0x43, 0xc7, 0xc8, 0x9d, 0xbd, 0x82, 0x87, 0xe2, 0x46, 0x28, 0xab, 0x74, + 0x9e, 0xa4, 0x46, 0xff, 0x52, 0x79, 0x5b, 0x0b, 0x8b, 0xc9, 0x63, 0x62, 0xfb, 0xfc, 0x68, 0x7b, + 0x7a, 0xd1, 0x3f, 0x64, 0x8f, 0x60, 0xdc, 0x36, 0xb2, 0x4e, 0x54, 0xd6, 0xa5, 0x37, 0xc2, 0xf6, + 0x53, 0xc6, 0xce, 0xe0, 0x90, 0x0e, 0x0a, 0xa3, 0x73, 0xb7, 0x9a, 0x4b, 0x32, 0x42, 0xfa, 0xd9, + 0xe8, 0x9c, 0x02, 0x7b, 0x0e, 0xf7, 0x48, 0xd5, 0x2c, 0x4d, 0x6d, 0xfb, 0xa9, 0x1e, 0x20, 0xfe, + 0x8e, 0x94, 0x74, 0x67, 0x70, 0xa8, 0x8d, 0x4d, 0x8c, 0xc6, 0xd9, 0x1a, 0x53, 0xb8, 0x74, 0xf7, + 0x79, 0xa4, 0x8d, 0xfd, 0xaa, 0x2f, 0x1c, 0x5b, 0x8c, 0xe8, 0x11, 0xbe, 0xfc, 0x1f, 0x00, 0x00, + 0xff, 0xff, 0x87, 0xd1, 0x6a, 0xe1, 0x46, 0x04, 0x00, 0x00, +} diff --git a/mdm/checkin/internal/checkinproto/checkin.proto b/mdm/internal/checkinproto/checkin.proto similarity index 93% rename from mdm/checkin/internal/checkinproto/checkin.proto rename to mdm/internal/checkinproto/checkin.proto index 357b1bbe..bd3f4caf 100644 --- a/mdm/checkin/internal/checkinproto/checkin.proto +++ b/mdm/internal/checkinproto/checkin.proto @@ -6,6 +6,8 @@ message Event { string id = 1; int64 time = 2; Command command = 3; + bytes raw = 4; + map params = 5; } message Command { diff --git a/mdm/connect/internal/connectproto/connect.go b/mdm/internal/connectproto/connect.go similarity index 100% rename from mdm/connect/internal/connectproto/connect.go rename to mdm/internal/connectproto/connect.go diff --git a/mdm/internal/connectproto/connect.pb.go b/mdm/internal/connectproto/connect.pb.go new file mode 100644 index 00000000..09e496c8 --- /dev/null +++ b/mdm/internal/connectproto/connect.pb.go @@ -0,0 +1,189 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: connect.proto + +package connectproto + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type Event struct { + Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` + Time int64 `protobuf:"varint,2,opt,name=time" json:"time,omitempty"` + Response *Response `protobuf:"bytes,3,opt,name=response" json:"response,omitempty"` + Raw []byte `protobuf:"bytes,4,opt,name=raw,proto3" json:"raw,omitempty"` + Params map[string]string `protobuf:"bytes,5,rep,name=params" json:"params,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Event) Reset() { *m = Event{} } +func (m *Event) String() string { return proto.CompactTextString(m) } +func (*Event) ProtoMessage() {} +func (*Event) Descriptor() ([]byte, []int) { + return fileDescriptor_connect_0a9da89ca45acccc, []int{0} +} +func (m *Event) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Event.Unmarshal(m, b) +} +func (m *Event) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Event.Marshal(b, m, deterministic) +} +func (dst *Event) XXX_Merge(src proto.Message) { + xxx_messageInfo_Event.Merge(dst, src) +} +func (m *Event) XXX_Size() int { + return xxx_messageInfo_Event.Size(m) +} +func (m *Event) XXX_DiscardUnknown() { + xxx_messageInfo_Event.DiscardUnknown(m) +} + +var xxx_messageInfo_Event proto.InternalMessageInfo + +func (m *Event) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *Event) GetTime() int64 { + if m != nil { + return m.Time + } + return 0 +} + +func (m *Event) GetResponse() *Response { + if m != nil { + return m.Response + } + return nil +} + +func (m *Event) GetRaw() []byte { + if m != nil { + return m.Raw + } + return nil +} + +func (m *Event) GetParams() map[string]string { + if m != nil { + return m.Params + } + return nil +} + +type Response struct { + Udid string `protobuf:"bytes,1,opt,name=udid" json:"udid,omitempty"` + UserId string `protobuf:"bytes,2,opt,name=user_id,json=userId" json:"user_id,omitempty"` + Status string `protobuf:"bytes,3,opt,name=status" json:"status,omitempty"` + RequestType string `protobuf:"bytes,4,opt,name=request_type,json=requestType" json:"request_type,omitempty"` + CommandUuid string `protobuf:"bytes,5,opt,name=command_uuid,json=commandUuid" json:"command_uuid,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Response) Reset() { *m = Response{} } +func (m *Response) String() string { return proto.CompactTextString(m) } +func (*Response) ProtoMessage() {} +func (*Response) Descriptor() ([]byte, []int) { + return fileDescriptor_connect_0a9da89ca45acccc, []int{1} +} +func (m *Response) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Response.Unmarshal(m, b) +} +func (m *Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Response.Marshal(b, m, deterministic) +} +func (dst *Response) XXX_Merge(src proto.Message) { + xxx_messageInfo_Response.Merge(dst, src) +} +func (m *Response) XXX_Size() int { + return xxx_messageInfo_Response.Size(m) +} +func (m *Response) XXX_DiscardUnknown() { + xxx_messageInfo_Response.DiscardUnknown(m) +} + +var xxx_messageInfo_Response proto.InternalMessageInfo + +func (m *Response) GetUdid() string { + if m != nil { + return m.Udid + } + return "" +} + +func (m *Response) GetUserId() string { + if m != nil { + return m.UserId + } + return "" +} + +func (m *Response) GetStatus() string { + if m != nil { + return m.Status + } + return "" +} + +func (m *Response) GetRequestType() string { + if m != nil { + return m.RequestType + } + return "" +} + +func (m *Response) GetCommandUuid() string { + if m != nil { + return m.CommandUuid + } + return "" +} + +func init() { + proto.RegisterType((*Event)(nil), "connectproto.Event") + proto.RegisterMapType((map[string]string)(nil), "connectproto.Event.ParamsEntry") + proto.RegisterType((*Response)(nil), "connectproto.Response") +} + +func init() { proto.RegisterFile("connect.proto", fileDescriptor_connect_0a9da89ca45acccc) } + +var fileDescriptor_connect_0a9da89ca45acccc = []byte{ + // 285 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x54, 0x8f, 0xbd, 0x4e, 0xc3, 0x30, + 0x1c, 0xc4, 0xe5, 0xa4, 0x09, 0xcd, 0x3f, 0x05, 0x21, 0x0b, 0x15, 0x8b, 0x85, 0xd0, 0x29, 0x53, + 0x86, 0x32, 0xf0, 0xb1, 0x77, 0x60, 0x43, 0x16, 0xcc, 0x91, 0x89, 0x3d, 0x58, 0x10, 0x27, 0xf8, + 0xa3, 0x28, 0x0f, 0xc2, 0x23, 0xf2, 0x1e, 0xc8, 0x8e, 0xa9, 0xca, 0x76, 0xf7, 0xf7, 0xf9, 0x74, + 0x3f, 0x38, 0xed, 0x06, 0xa5, 0x44, 0x67, 0x9b, 0x51, 0x0f, 0x76, 0xc0, 0xab, 0x68, 0x83, 0xdb, + 0xfc, 0x20, 0xc8, 0x76, 0x7b, 0xa1, 0x2c, 0x3e, 0x83, 0x44, 0x72, 0x82, 0x2a, 0x54, 0x17, 0x34, + 0x91, 0x1c, 0x63, 0x58, 0x58, 0xd9, 0x0b, 0x92, 0x54, 0xa8, 0x4e, 0x69, 0xd0, 0x78, 0x0b, 0x4b, + 0x2d, 0xcc, 0x38, 0x28, 0x23, 0x48, 0x5a, 0xa1, 0xba, 0xdc, 0xae, 0x9b, 0xe3, 0xba, 0x86, 0xc6, + 0x57, 0x7a, 0xc8, 0xe1, 0x73, 0x48, 0x35, 0xfb, 0x22, 0x8b, 0x0a, 0xd5, 0x2b, 0xea, 0x25, 0xbe, + 0x83, 0x7c, 0x64, 0x9a, 0xf5, 0x86, 0x64, 0x55, 0x5a, 0x97, 0xdb, 0xeb, 0xff, 0x1d, 0x61, 0x4e, + 0xf3, 0x1c, 0x12, 0x3b, 0x65, 0xf5, 0x44, 0x63, 0xfc, 0xea, 0x01, 0xca, 0xa3, 0xb3, 0x6f, 0x7e, + 0x17, 0x53, 0x9c, 0xec, 0x25, 0xbe, 0x80, 0x6c, 0xcf, 0x3e, 0xdc, 0x3c, 0xba, 0xa0, 0xb3, 0x79, + 0x4c, 0xee, 0xd1, 0xe6, 0x1b, 0xc1, 0xf2, 0x6f, 0x9c, 0x47, 0x73, 0xfc, 0x00, 0x1b, 0x34, 0xbe, + 0x84, 0x13, 0x67, 0x84, 0x6e, 0x25, 0x8f, 0x9f, 0x73, 0x6f, 0x9f, 0x38, 0x5e, 0x43, 0x6e, 0x2c, + 0xb3, 0xce, 0x04, 0xe2, 0x82, 0x46, 0x87, 0x6f, 0x60, 0xa5, 0xc5, 0xa7, 0x13, 0xc6, 0xb6, 0x76, + 0x1a, 0x45, 0x00, 0x2c, 0x68, 0x19, 0x6f, 0x2f, 0xd3, 0x28, 0x7c, 0xa4, 0x1b, 0xfa, 0x9e, 0x29, + 0xde, 0x3a, 0x27, 0x39, 0xc9, 0xe6, 0x48, 0xbc, 0xbd, 0x3a, 0xc9, 0xdf, 0xf2, 0xc0, 0x7c, 0xfb, + 0x1b, 0x00, 0x00, 0xff, 0xff, 0x78, 0xb5, 0xce, 0x2a, 0xa5, 0x01, 0x00, 0x00, +} diff --git a/mdm/connect/internal/connectproto/connect.proto b/mdm/internal/connectproto/connect.proto similarity index 89% rename from mdm/connect/internal/connectproto/connect.proto rename to mdm/internal/connectproto/connect.proto index ca7555a4..5a2eddb5 100644 --- a/mdm/connect/internal/connectproto/connect.proto +++ b/mdm/internal/connectproto/connect.proto @@ -7,6 +7,7 @@ message Event { int64 time = 2; Response response = 3; bytes raw = 4; + map params = 5; } message Response { diff --git a/mdm/server.go b/mdm/server.go new file mode 100644 index 00000000..7a7293d0 --- /dev/null +++ b/mdm/server.go @@ -0,0 +1,122 @@ +package mdm + +import ( + "context" + "io/ioutil" + "net/http" + + "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/pkg/errors" +) + +type Endpoints struct { + CheckinEndpoint endpoint.Endpoint + AcknowledgeEndpoint endpoint.Endpoint +} + +func MakeServerEndpoints(s Service) Endpoints { + return Endpoints{ + CheckinEndpoint: MakeCheckinEndpoint(s), + AcknowledgeEndpoint: MakeAcknowledgeEndpoint(s), + } +} + +func RegisterHTTPHandlers(r *mux.Router, e Endpoints, verifier SignatureVerifier, logger log.Logger) { + decoder := &requestDecoder{verifier: verifier} + options := []httptransport.ServerOption{ + httptransport.ServerErrorEncoder(encodeError), + httptransport.ServerErrorLogger(logger), + httptransport.ServerBefore(httptransport.PopulateRequestContext), + } + + r.Methods(http.MethodPut).Path("/mdm/checkin").Handler(httptransport.NewServer( + e.CheckinEndpoint, + decoder.decodeCheckinRequest, + encodeResponse, + options..., + )) + + r.Methods(http.MethodPut).Path("/mdm/connect").Handler(httptransport.NewServer( + e.AcknowledgeEndpoint, + decoder.decodeAcknowledgeRequest, + encodeResponse, + options..., + )) +} + +type SignatureVerifier interface { + VerifySignature(sig string, message []byte) error +} + +type requestDecoder struct { + verifier SignatureVerifier +} + +func (d *requestDecoder) readBody(r *http.Request) ([]byte, error) { + defer r.Body.Close() + + body, err := ioutil.ReadAll(r.Body) + if err != nil { + return nil, errors.Wrap(err, "reading MDM Response HTTP Body") + } + + if d.verifier != nil { + b64sig := r.Header.Get("Mdm-Signature") + if err := d.verifier.VerifySignature(b64sig, body); err != nil { + return nil, errors.Wrap(err, "verify signature") + } + } + + return body, nil +} + +// According to the MDM Check-in protocol, the server must respond with 200 OK +// to successful Check-in requests. +func encodeResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { + type failer interface { + Failed() error + } + + if e, ok := response.(failer); ok && e.Failed() != nil { + encodeError(ctx, e.Failed(), w) + return nil + } + + w.WriteHeader(http.StatusOK) + + type payloader interface { + Response() []byte + } + + var err error + if r, ok := response.(payloader); ok { + _, err = w.Write(r.Response()) + } + return errors.Wrap(err, "write acknowledge response") +} + +func encodeError(ctx context.Context, err error, w http.ResponseWriter) { + err = errors.Cause(err) + type rejectUserAuthError interface { + error + UserAuthReject() bool + } + if e, ok := err.(rejectUserAuthError); ok && e.UserAuthReject() { + w.WriteHeader(http.StatusGone) + return + } + + type checkoutErr interface { + error + Checkout() bool + } + if e, ok := err.(checkoutErr); ok && e.Checkout() { + w.WriteHeader(http.StatusUnauthorized) + return + } + + w.WriteHeader(http.StatusInternalServerError) +} diff --git a/mdm/service.go b/mdm/service.go new file mode 100644 index 00000000..d71fa3c5 --- /dev/null +++ b/mdm/service.go @@ -0,0 +1,38 @@ +package mdm + +import ( + "context" + + "github.com/micromdm/micromdm/platform/pubsub" +) + +type Service interface { + Checkin(ctx context.Context, event CheckinEvent) error + Acknowledge(ctx context.Context, event AcknowledgeEvent) (payload []byte, err error) +} + +type Middleware func(Service) Service + +const ( + ConnectTopic = "mdm.Connect" + AuthenticateTopic = "mdm.Authenticate" + TokenUpdateTopic = "mdm.TokenUpdate" + CheckoutTopic = "mdm.CheckOut" +) + +// Queue is an MDM Command Queue. +type Queue interface { + Next(context.Context, Response) ([]byte, error) +} + +type MDMService struct { + pub pubsub.Publisher + queue Queue +} + +func NewService(pub pubsub.Publisher, queue Queue) *MDMService { + return &MDMService{ + pub: pub, + queue: queue, + } +} diff --git a/platform/apns/builtin/db.go b/platform/apns/builtin/db.go index 4dfc705d..e5584b20 100644 --- a/platform/apns/builtin/db.go +++ b/platform/apns/builtin/db.go @@ -7,7 +7,7 @@ import ( "github.com/boltdb/bolt" "github.com/pkg/errors" - "github.com/micromdm/micromdm/mdm/checkin" + "github.com/micromdm/micromdm/mdm" "github.com/micromdm/micromdm/platform/apns" "github.com/micromdm/micromdm/platform/pubsub" ) @@ -81,17 +81,17 @@ func (db *DB) Save(info *apns.PushInfo) error { } func (db *DB) pollCheckin(sub pubsub.Subscriber) error { - tokenUpdateEvents, err := sub.Subscribe(context.TODO(), "push-info", checkin.TokenUpdateTopic) + tokenUpdateEvents, err := sub.Subscribe(context.TODO(), "push-info", mdm.TokenUpdateTopic) if err != nil { return errors.Wrapf(err, - "subscribing push to %s topic", checkin.TokenUpdateTopic) + "subscribing push to %s topic", mdm.TokenUpdateTopic) } go func() { for { select { case event := <-tokenUpdateEvents: - var ev checkin.Event - if err := checkin.UnmarshalEvent(event.Message, &ev); err != nil { + var ev mdm.CheckinEvent + if err := mdm.UnmarshalCheckinEvent(event.Message, &ev); err != nil { fmt.Println(err) continue } diff --git a/platform/blueprint/builtin/listener.go b/platform/blueprint/builtin/listener.go index 6b0c0991..d7716888 100644 --- a/platform/blueprint/builtin/listener.go +++ b/platform/blueprint/builtin/listener.go @@ -6,7 +6,7 @@ import ( "github.com/pkg/errors" - "github.com/micromdm/micromdm/mdm/checkin" + mdmsvc "github.com/micromdm/micromdm/mdm" "github.com/micromdm/micromdm/mdm/mdm" "github.com/micromdm/micromdm/platform/blueprint" "github.com/micromdm/micromdm/platform/command" @@ -99,8 +99,8 @@ func (db *DB) StartListener(sub pubsub.Subscriber, cmdSvc command.Service) error for { select { case event := <-tokenUpdateEvents: - var ev checkin.Event - if err := checkin.UnmarshalEvent(event.Message, &ev); err != nil { + var ev mdmsvc.CheckinEvent + if err := mdmsvc.UnmarshalCheckinEvent(event.Message, &ev); err != nil { fmt.Println(err) continue } diff --git a/platform/device/builtin/db.go b/platform/device/builtin/db.go index 8abc2b78..3e66fb80 100644 --- a/platform/device/builtin/db.go +++ b/platform/device/builtin/db.go @@ -10,8 +10,7 @@ import ( uuid "github.com/satori/go.uuid" "github.com/micromdm/micromdm/dep/depsync" - "github.com/micromdm/micromdm/mdm/checkin" - "github.com/micromdm/micromdm/mdm/connect" + "github.com/micromdm/micromdm/mdm" "github.com/micromdm/micromdm/platform/device" "github.com/micromdm/micromdm/platform/pubsub" ) @@ -175,37 +174,37 @@ func isNotFound(err error) bool { } func (db *DB) pollCheckin(pubsubSvc pubsub.PublishSubscriber) error { - authenticateEvents, err := pubsubSvc.Subscribe(context.TODO(), "devices", checkin.AuthenticateTopic) + authenticateEvents, err := pubsubSvc.Subscribe(context.TODO(), "devices", mdm.AuthenticateTopic) if err != nil { return errors.Wrapf(err, - "subscribing devices to %s topic", checkin.AuthenticateTopic) + "subscribing devices to %s topic", mdm.AuthenticateTopic) } - tokenUpdateEvents, err := pubsubSvc.Subscribe(context.TODO(), "devices", checkin.TokenUpdateTopic) + tokenUpdateEvents, err := pubsubSvc.Subscribe(context.TODO(), "devices", mdm.TokenUpdateTopic) if err != nil { return errors.Wrapf(err, - "subscribing devices to %s topic", checkin.TokenUpdateTopic) + "subscribing devices to %s topic", mdm.TokenUpdateTopic) } - checkoutEvents, err := pubsubSvc.Subscribe(context.TODO(), "devices", checkin.CheckoutTopic) + checkoutEvents, err := pubsubSvc.Subscribe(context.TODO(), "devices", mdm.CheckoutTopic) if err != nil { return errors.Wrapf(err, - "subscribing devices to %s topic", checkin.CheckoutTopic) + "subscribing devices to %s topic", mdm.CheckoutTopic) } depSyncEvents, err := pubsubSvc.Subscribe(context.TODO(), "devices", depsync.SyncTopic) if err != nil { return errors.Wrapf(err, "subscribing devices to %s topic", depsync.SyncTopic) } - connectEvents, err := pubsubSvc.Subscribe(context.TODO(), "devices", connect.ConnectTopic) + connectEvents, err := pubsubSvc.Subscribe(context.TODO(), "devices", mdm.ConnectTopic) if err != nil { return errors.Wrapf(err, - "subscribing devices to %s topic", connect.ConnectTopic) + "subscribing devices to %s topic", mdm.ConnectTopic) } go func() { for { select { case event := <-authenticateEvents: - var ev checkin.Event - if err := checkin.UnmarshalEvent(event.Message, &ev); err != nil { + var ev mdm.CheckinEvent + if err := mdm.UnmarshalCheckinEvent(event.Message, &ev); err != nil { fmt.Println(err) continue } @@ -252,8 +251,8 @@ func (db *DB) pollCheckin(pubsubSvc pubsub.PublishSubscriber) error { continue } case event := <-tokenUpdateEvents: - var ev checkin.Event - if err := checkin.UnmarshalEvent(event.Message, &ev); err != nil { + var ev mdm.CheckinEvent + if err := mdm.UnmarshalCheckinEvent(event.Message, &ev); err != nil { fmt.Println(err) continue } @@ -339,8 +338,8 @@ func (db *DB) pollCheckin(pubsubSvc pubsub.PublishSubscriber) error { } } case event := <-connectEvents: - var ev connect.Event - if err := connect.UnmarshalEvent(event.Message, &ev); err != nil { + var ev mdm.AcknowledgeEvent + if err := mdm.UnmarshalAcknowledgeEvent(event.Message, &ev); err != nil { fmt.Println(err) continue } @@ -355,8 +354,8 @@ func (db *DB) pollCheckin(pubsubSvc pubsub.PublishSubscriber) error { continue } case event := <-checkoutEvents: - var ev checkin.Event - if err := checkin.UnmarshalEvent(event.Message, &ev); err != nil { + var ev mdm.CheckinEvent + if err := mdm.UnmarshalCheckinEvent(event.Message, &ev); err != nil { fmt.Println(err) continue } diff --git a/platform/queue/queue.go b/platform/queue/queue.go index 3341c555..49afdf40 100644 --- a/platform/queue/queue.go +++ b/platform/queue/queue.go @@ -9,7 +9,7 @@ import ( "github.com/groob/plist" "github.com/pkg/errors" - "github.com/micromdm/mdm" + "github.com/micromdm/micromdm/mdm" "github.com/micromdm/micromdm/platform/command" "github.com/micromdm/micromdm/platform/pubsub" ) @@ -24,7 +24,18 @@ type Store struct { *bolt.DB } -func (db *Store) Next(ctx context.Context, resp mdm.Response) (*Command, error) { +func (db *Store) Next(ctx context.Context, resp mdm.Response) ([]byte, error) { + cmd, err := db.nextCommand(ctx, resp) + if err != nil { + return nil, err + } + if cmd == nil { + return nil, nil + } + return cmd.Payload, nil +} + +func (db *Store) nextCommand(ctx context.Context, resp mdm.Response) (*Command, error) { udid := resp.UDID if resp.UserID != nil { // use the user id for user level commands diff --git a/platform/queue/queue_test.go b/platform/queue/queue_test.go index 7f15f57e..1839cb1d 100644 --- a/platform/queue/queue_test.go +++ b/platform/queue/queue_test.go @@ -7,7 +7,7 @@ import ( "testing" "github.com/boltdb/bolt" - "github.com/micromdm/mdm" + "github.com/micromdm/micromdm/mdm" ) func TestNext_Error(t *testing.T) { @@ -29,7 +29,7 @@ func TestNext_Error(t *testing.T) { Status: "Error", } for range dc.Commands { - cmd, err := store.Next(ctx, resp) + cmd, err := store.nextCommand(ctx, resp) if err != nil { t.Fatalf("expected nil, but got err: %s", err) } @@ -57,16 +57,15 @@ func TestNext_NotNow(t *testing.T) { ctx := context.Background() tf := func(t *testing.T) { - resp := mdm.Response{ UDID: dc.DeviceUDID, CommandUUID: "yCmd", Status: "NotNow", } - cmd, err := store.Next(ctx, resp) + cmd, err := store.nextCommand(ctx, resp) if err != nil { - t.Fatalf("expected nil, but got err: %s", err) + t.Fatalf("expected nil, but got err: %s", err) } resp = mdm.Response{ @@ -75,7 +74,7 @@ func TestNext_NotNow(t *testing.T) { Status: "NotNow", } - cmd, err = store.Next(ctx, resp) + cmd, err = store.nextCommand(ctx, resp) if err != nil { t.Fatalf("expected nil, but got err: %s", err) } @@ -111,7 +110,7 @@ func TestNext_Idle(t *testing.T) { Status: "Idle", } for i, _ := range dc.Commands { - cmd, err := store.Next(ctx, resp) + cmd, err := store.nextCommand(ctx, resp) if err != nil { t.Fatalf("expected nil, but got err: %s", err) } @@ -143,7 +142,7 @@ func TestNext_zeroCommands(t *testing.T) { for _, s := range allStatuses { t.Run(s, func(t *testing.T) { resp := mdm.Response{CommandUUID: s, Status: s} - cmd, err := store.Next(ctx, resp) + cmd, err := store.nextCommand(ctx, resp) if err != nil { t.Errorf("expected nil, but got err: %s", err) } diff --git a/platform/remove/remove.go b/platform/remove/remove.go index ff36e6ca..b2b6e78a 100644 --- a/platform/remove/remove.go +++ b/platform/remove/remove.go @@ -6,7 +6,7 @@ import ( "github.com/gogo/protobuf/proto" "github.com/pkg/errors" - "github.com/micromdm/micromdm/mdm/connect" + "github.com/micromdm/micromdm/mdm" "github.com/micromdm/micromdm/platform/remove/internal/removeproto" ) @@ -30,8 +30,8 @@ func UnmarshalDevice(data []byte, dev *Device) error { return nil } -func RemoveMiddleware(store Store) connect.Middleware { - return func(next connect.Service) connect.Service { +func RemoveMiddleware(store Store) mdm.Middleware { + return func(next mdm.Service) mdm.Service { return &removeMiddleware{ store: store, next: next, @@ -41,11 +41,11 @@ func RemoveMiddleware(store Store) connect.Middleware { type removeMiddleware struct { store Store - next connect.Service + next mdm.Service } -func (mw removeMiddleware) Acknowledge(ctx context.Context, req connect.MDMConnectRequest) ([]byte, error) { - udid := req.MDMResponse.UDID +func (mw removeMiddleware) Acknowledge(ctx context.Context, req mdm.AcknowledgeEvent) ([]byte, error) { + udid := req.Response.UDID _, err := mw.store.DeviceByUDID(udid) if err != nil { if !isNotFound(err) { @@ -58,6 +58,10 @@ func (mw removeMiddleware) Acknowledge(ctx context.Context, req connect.MDMConne return mw.next.Acknowledge(ctx, req) } +func (mw removeMiddleware) Checkin(ctx context.Context, req mdm.CheckinEvent) error { + return mw.next.Checkin(ctx, req) +} + type checkoutErr struct{} func (checkoutErr) Error() string { diff --git a/platform/user/builtin/db.go b/platform/user/builtin/db.go index 96cfbdbd..0999b35e 100644 --- a/platform/user/builtin/db.go +++ b/platform/user/builtin/db.go @@ -10,7 +10,7 @@ import ( "github.com/pkg/errors" uuid "github.com/satori/go.uuid" - "github.com/micromdm/micromdm/mdm/checkin" + "github.com/micromdm/micromdm/mdm" "github.com/micromdm/micromdm/platform/pubsub" "github.com/micromdm/micromdm/platform/user" ) @@ -201,10 +201,10 @@ func (e *notFound) NotFound() bool { } func (db *DB) pollCheckin(pubsubSvc pubsub.PublishSubscriber) error { - tokenUpdateEvents, err := pubsubSvc.Subscribe(context.TODO(), "users", checkin.TokenUpdateTopic) + tokenUpdateEvents, err := pubsubSvc.Subscribe(context.TODO(), "users", mdm.TokenUpdateTopic) if err != nil { return errors.Wrapf(err, - "subscribing devices to %s topic", checkin.TokenUpdateTopic) + "subscribing devices to %s topic", mdm.TokenUpdateTopic) } go func() { for { @@ -252,10 +252,10 @@ func (db *DB) pollCheckin(pubsubSvc pubsub.PublishSubscriber) error { return nil } -func unmarshalCheckin(event pubsub.Event) (checkin.Event, error) { - var ev checkin.Event - if err := checkin.UnmarshalEvent(event.Message, &ev); err != nil { - return checkin.Event{}, err +func unmarshalCheckin(event pubsub.Event) (mdm.CheckinEvent, error) { + var ev mdm.CheckinEvent + if err := mdm.UnmarshalCheckinEvent(event.Message, &ev); err != nil { + return mdm.CheckinEvent{}, err } return ev, nil } diff --git a/workflow/webhook/command.go b/workflow/webhook/command.go index 849a4097..0aba772f 100644 --- a/workflow/webhook/command.go +++ b/workflow/webhook/command.go @@ -6,7 +6,7 @@ import ( "fmt" "net/http" - "github.com/micromdm/micromdm/mdm/connect" + "github.com/micromdm/micromdm/mdm" "github.com/micromdm/micromdm/platform/pubsub" "github.com/pkg/errors" ) @@ -42,8 +42,8 @@ func (cw CommandWebhook) StartListener(sub pubsub.Subscriber) error { for { select { case event := <-connectEvents: - var ev connect.Event - if err := connect.UnmarshalEvent(event.Message, &ev); err != nil { + var ev mdm.AcknowledgeEvent + if err := mdm.UnmarshalAcknowledgeEvent(event.Message, &ev); err != nil { fmt.Println(err) continue }