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.
This commit is contained in:
Mosen
2016-07-11 18:40:12 +10:00
parent 43412971d8
commit 23df1261b5
5 changed files with 153 additions and 59 deletions

View File

@@ -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
}

View File

@@ -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"`
}

View File

@@ -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)
}

58
device/acknowledge.go Normal file
View File

@@ -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)
}

12
main.go
View File

@@ -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)