From 23df1261b58bbacf3e57b2137a83b4bb2f7296a5 Mon Sep 17 00:00:00 2001 From: Mosen Date: Mon, 11 Jul 2016 18:40:12 +1000 Subject: [PATCH] Add methods RegisterAckHandler, FindAckHandler, ExecAckHandler to connect service. This allows each separate package to register to receive a specific response type and perform its own actions without introducing dependencies into the connect service. Add ack handler for device information Add ack handler for installed applications Change application struct to use sql.Null* types Register DeviceInformation and InstalledApplicationList handlers in the main file. --- applications/acknowledge.go | 31 ++++++++++++ applications/application.go | 16 ++++--- connect/service.go | 95 +++++++++++++++++-------------------- device/acknowledge.go | 58 ++++++++++++++++++++++ main.go | 12 +++++ 5 files changed, 153 insertions(+), 59 deletions(-) create mode 100644 applications/acknowledge.go create mode 100644 device/acknowledge.go diff --git a/applications/acknowledge.go b/applications/acknowledge.go new file mode 100644 index 00000000..872ca0a7 --- /dev/null +++ b/applications/acknowledge.go @@ -0,0 +1,31 @@ +package applications + +import ( + "fmt" + "github.com/micromdm/mdm" +) + +// +//func (svc service) ackInstalledApplicationList(req mdm.Response) error { +// apps := req.InstalledApplicationList +// devices, err := svc.devices.Devices( +// device.UDID{UDID: req.UDID}, +// ) +// if err != nil { +// return err +// } +// +// if len(devices) > 1 || len(devices) == 0 { +// return errors.New("expected a single query result for device, got more or less than one.") +// } +// device := devices[0] +// +// for _, app := range apps { +// +// } +//} + +func AcknowledgeInstalledApplicationListResponse(response mdm.Response, datastores map[string]interface{}) error { + fmt.Println("InstalledApplicationListResponseHandler TODO") + return nil +} diff --git a/applications/application.go b/applications/application.go index 89ffed32..f689191a 100644 --- a/applications/application.go +++ b/applications/application.go @@ -1,14 +1,16 @@ package applications +import "database/sql" + type Application struct { - Identifier string `plist:",omitempty" json:"identifier,omitempty"` - Version string `plist:",omitempty" json:"version,omitempty"` - ShortVersion string `plist:",omitempty" json:"short_version,omitempty"` - Name string `json:"name,omitempty"` - BundleSize int `plist:",omitempty" json:"bundle_size,omitempty"` + Identifier sql.NullString `plist:",omitempty" json:"identifier,omitempty"` + Version sql.NullString `plist:",omitempty" json:"version,omitempty"` + ShortVersion sql.NullString `plist:",omitempty" json:"short_version,omitempty"` + Name string `json:"name,omitempty"` + BundleSize sql.NullInt64 `plist:",omitempty" json:"bundle_size,omitempty"` // The size of the app's document, library, and other folders, in bytes. - DynamicSize int `plist:",omitempty" json:"dynamic_size,omitempty"` + DynamicSize sql.NullInt64 `plist:",omitempty" json:"dynamic_size,omitempty"` - IsValidated bool `plist:",omitempty" json:"is_validated,omitempty"` + IsValidated sql.NullBool `plist:",omitempty" json:"is_validated,omitempty"` } diff --git a/connect/service.go b/connect/service.go index a6306e52..cd8d5eb1 100644 --- a/connect/service.go +++ b/connect/service.go @@ -1,19 +1,21 @@ package connect import ( - "encoding/json" "github.com/micromdm/mdm" "github.com/micromdm/micromdm/command" "github.com/micromdm/micromdm/device" "github.com/pkg/errors" "golang.org/x/net/context" - "time" ) // Service defines methods for an MDM service type Service interface { Acknowledge(ctx context.Context, req mdm.Response) (int, error) NextCommand(ctx context.Context, req mdm.Response) ([]byte, int, error) + + RegisterAckHandler(requestType string, handler func(req mdm.Response, datastores map[string]interface{}) error, datastores map[string]interface{}) + FindAckHandler(requestType string) (func(req mdm.Response) error, bool) + ExecAckHandler(requestType string, req mdm.Response) error } // NewService creates a mdm service @@ -24,25 +26,27 @@ func NewService(devices device.Datastore, cs command.Service) Service { } } +type ackHandler struct { + requestType string + handler func(req mdm.Response) error +} + type service struct { devices device.Datastore commands command.Service + handlers []ackHandler } func (svc service) Acknowledge(ctx context.Context, req mdm.Response) (int, error) { - switch req.RequestType { - case "DeviceInformation": - if err := svc.ackQueryResponses(req); err != nil { - return 0, err - } - default: - // Need to handle the absence of RequestType in IOS8 devices - if req.QueryResponses.UDID != "" { - if err := svc.ackQueryResponses(req); err != nil { - return 0, err - } - } - } + + err := svc.ExecAckHandler(req.RequestType, req) + + //// Need to handle the absence of RequestType in IOS8 devices + //if req.QueryResponses.UDID != "" { + // if err := svc.ackQueryResponses(req); err != nil { + // return 0, err + // } + //} total, err := svc.commands.DeleteCommand(req.UDID, req.CommandUUID) if err != nil { @@ -81,42 +85,29 @@ func (svc service) checkRequeue(deviceUDID string) (int, error) { return 0, nil } -// Acknowledge Queries sent with DeviceInformation command -func (svc service) ackQueryResponses(req mdm.Response) error { - devices, err := svc.devices.Devices( - device.SerialNumber{SerialNumber: req.QueryResponses.SerialNumber}, - device.UDID{UDID: req.UDID}, - ) - - if err != nil { - return err +func (svc service) RegisterAckHandler(requestType string, handler func(req mdm.Response, datastores map[string]interface{}) error, datastores map[string]interface{}) { + datastoreInjectedHandler := func(req mdm.Response) error { + return handler(req, datastores) } - - if len(devices) > 1 { - return errors.New("expected a single query result for device, got more than one.") - } - - existing := devices[0] - - now := time.Now() - existing.LastCheckin = &now - existing.LastQueryResponse, err = json.Marshal(req.QueryResponses) - - if err != nil { - return err - } - - var serialNumber device.JsonNullString - serialNumber.Scan(req.QueryResponses.SerialNumber) - - existing.ProductName = req.QueryResponses.ProductName - existing.BuildVersion = req.QueryResponses.BuildVersion - existing.DeviceName = req.QueryResponses.DeviceName - existing.IMEI = req.QueryResponses.IMEI - existing.MEID = req.QueryResponses.MEID - existing.Model = req.QueryResponses.Model - existing.OSVersion = req.QueryResponses.OSVersion - existing.SerialNumber = serialNumber - - return svc.devices.Save("queryResponses", &existing) + svc.handlers = append(svc.handlers, ackHandler{requestType, datastoreInjectedHandler}) +} + +// If not found, second return variable is false +func (svc service) FindAckHandler(requestType string) (func(req mdm.Response) error, bool) { + for _, h := range svc.handlers { + if h.requestType == requestType { + return h.handler, true + } + } + + return nil, false +} + +func (svc service) ExecAckHandler(requestType string, req mdm.Response) error { + handler, found := svc.FindAckHandler(requestType) + if !found { + return errors.Errorf("There is no registered handler for the response type: %s", requestType) + } + + return handler(req) } diff --git a/device/acknowledge.go b/device/acknowledge.go new file mode 100644 index 00000000..f5a31bfd --- /dev/null +++ b/device/acknowledge.go @@ -0,0 +1,58 @@ +package device + +import ( + "encoding/json" + "errors" + "github.com/micromdm/mdm" + "time" +) + +// Acknowledge Queries sent with DeviceInformation command +func AcknowledgeDeviceInformationResponse(req mdm.Response, datastores map[string]interface{}) error { + store, found := datastores["devices"] + if !found { + return errors.New("Do not have access to datastore for saving device information") + } + + devicesStore, ok := store.(Datastore) + if !ok { + return errors.New("could not acknowledge device information because the given datastore isnt a device datastore.") + } + + devices, err := devicesStore.Devices( + SerialNumber{SerialNumber: req.QueryResponses.SerialNumber}, + UDID{UDID: req.UDID}, + ) + + if err != nil { + return err + } + + if len(devices) > 1 { + return errors.New("expected a single query result for device, got more than one.") + } + + existing := devices[0] + + now := time.Now() + existing.LastCheckin = &now + existing.LastQueryResponse, err = json.Marshal(req.QueryResponses) + + if err != nil { + return err + } + + var serialNumber JsonNullString + serialNumber.Scan(req.QueryResponses.SerialNumber) + + existing.ProductName = req.QueryResponses.ProductName + existing.BuildVersion = req.QueryResponses.BuildVersion + existing.DeviceName = req.QueryResponses.DeviceName + existing.IMEI = req.QueryResponses.IMEI + existing.MEID = req.QueryResponses.MEID + existing.Model = req.QueryResponses.Model + existing.OSVersion = req.QueryResponses.OSVersion + existing.SerialNumber = serialNumber + + return devicesStore.Save("queryResponses", &existing) +} diff --git a/main.go b/main.go index 42e7468f..286d8e8f 100644 --- a/main.go +++ b/main.go @@ -203,6 +203,18 @@ func main() { checkinSvc := checkin.NewService(deviceDB, mgmtSvc, commandSvc, enrollmentProfile) connectSvc := connect.NewService(deviceDB, commandSvc) + connectSvc.RegisterAckHandler( + "InstalledApplicationList", + applications.AcknowledgeInstalledApplicationListResponse, + map[string]interface{}{"applications": appsDB}, + ) + + connectSvc.RegisterAckHandler( + "DeviceInformation", + device.AcknowledgeDeviceInformationResponse, + map[string]interface{}{"devices": deviceDB}, + ) + httpLogger := log.NewContext(logger).With("component", "http") managementHandler := management.ServiceHandler(ctx, mgmtSvc, httpLogger) commandHandler := command.ServiceHandler(ctx, commandSvc, httpLogger)