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)