From 07b2debc98ae7edfde49512891e7eb86bcc5beaf Mon Sep 17 00:00:00 2001 From: Mosen Date: Mon, 11 Jul 2016 17:14:07 +1000 Subject: [PATCH] Define new datastore for retrieving installed applications by device uuid Add InstalledApps method to management service Add endpoint for installed applications at /management/v1/devices/uuid/applications --- applications/application.go | 14 +++++++ applications/datastore.go | 60 ++++++++++++++++++++++++++++ applications/datastore_test.go | 1 + main.go | 13 +++++- management/endpoint_installedapps.go | 27 +++++++++++++ management/service.go | 34 +++++++++++----- management/transport.go | 23 +++++++++++ 7 files changed, 162 insertions(+), 10 deletions(-) create mode 100644 applications/application.go create mode 100644 applications/datastore.go create mode 100644 applications/datastore_test.go create mode 100644 management/endpoint_installedapps.go diff --git a/applications/application.go b/applications/application.go new file mode 100644 index 00000000..89ffed32 --- /dev/null +++ b/applications/application.go @@ -0,0 +1,14 @@ +package applications + +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"` + + // The size of the app's document, library, and other folders, in bytes. + DynamicSize int `plist:",omitempty" json:"dynamic_size,omitempty"` + + IsValidated bool `plist:",omitempty" json:"is_validated,omitempty"` +} diff --git a/applications/datastore.go b/applications/datastore.go new file mode 100644 index 00000000..be517787 --- /dev/null +++ b/applications/datastore.go @@ -0,0 +1,60 @@ +package applications + +import ( + "fmt" + kitlog "github.com/go-kit/kit/log" + "github.com/jmoiron/sqlx" + _ "github.com/lib/pq" // postgres driver + "github.com/pkg/errors" + "time" +) + +// Datastore manages devices in a database +type Datastore interface { + GetApplicationsByDeviceUUID(deviceUUID string) (*[]Application, error) +} + +type pgStore struct { + *sqlx.DB +} + +func NewDB(driver, conn string, logger kitlog.Logger) (Datastore, error) { + switch driver { + case "postgres": + db, err := sqlx.Open(driver, conn) + if err != nil { + return nil, errors.Wrap(err, "device datastore") + } + var dbError error + maxAttempts := 20 + for attempts := 1; attempts <= maxAttempts; attempts++ { + dbError = db.Ping() + if dbError == nil { + break + } + logger.Log("msg", fmt.Sprintf("could not connect to postgres: %v", dbError)) + time.Sleep(time.Duration(attempts) * time.Second) + } + if dbError != nil { + return nil, errors.Wrap(dbError, "device datastore") + } + return pgStore{DB: db}, nil + default: + return nil, errors.New("unknown driver") + } +} + +func (store pgStore) GetApplicationsByDeviceUUID(deviceUUID string) (*[]Application, error) { + apps := []Application{} + query := `SELECT * FROM applications + RIGHT JOIN devices_applications ON applications.application_uuid = devices_applications.application_uuid + WHERE devices_applications.device_uuid=$1` + + err := store.Select(&apps, query, deviceUUID) + + if err != nil { + return nil, err + } + + return &apps, nil +} diff --git a/applications/datastore_test.go b/applications/datastore_test.go new file mode 100644 index 00000000..b0580a35 --- /dev/null +++ b/applications/datastore_test.go @@ -0,0 +1 @@ +package applications diff --git a/main.go b/main.go index 92304985..401b28d6 100644 --- a/main.go +++ b/main.go @@ -14,6 +14,7 @@ import ( "github.com/RobotsAndPencils/buford/push" "github.com/go-kit/kit/log" "github.com/micromdm/dep" + "github.com/micromdm/micromdm/applications" "github.com/micromdm/micromdm/checkin" "github.com/micromdm/micromdm/command" "github.com/micromdm/micromdm/connect" @@ -184,8 +185,18 @@ func main() { os.Exit(1) } + appsDB, err := applications.NewDB( + "postgres", + *flPGconn, + logger, + ) + if err != nil { + logger.Log("err", err) + os.Exit(1) + } + dc := depClient(logger, *flDEPCK, *flDEPCS, *flDEPAT, *flDEPAS, *flDEPServerURL, *flDEPsim) - mgmtSvc := management.NewService(deviceDB, workflowDB, dc, pushSvc) + mgmtSvc := management.NewService(deviceDB, workflowDB, dc, pushSvc, appsDB) commandSvc := command.NewService(commandDB) checkinSvc := checkin.NewService(deviceDB, mgmtSvc, commandSvc, enrollmentProfile) connectSvc := connect.NewService(deviceDB, commandSvc) diff --git a/management/endpoint_installedapps.go b/management/endpoint_installedapps.go new file mode 100644 index 00000000..3661e003 --- /dev/null +++ b/management/endpoint_installedapps.go @@ -0,0 +1,27 @@ +package management + +import ( + "github.com/go-kit/kit/endpoint" + "github.com/micromdm/micromdm/applications" + "golang.org/x/net/context" +) + +type installedAppsRequest struct { + UUID string +} + +type installedAppsResponse struct { + applications []applications.Application + Err error `json:"error,omitempty"` +} + +func makeInstalledAppsEndpoint(svc Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(installedAppsRequest) + apps, err := svc.InstalledApps(req.UUID) + if err != nil { + return installedAppsResponse{Err: err}, nil + } + return installedAppsResponse{applications: *apps}, nil + } +} diff --git a/management/service.go b/management/service.go index 60f656ba..1c5f4a96 100644 --- a/management/service.go +++ b/management/service.go @@ -4,6 +4,7 @@ import ( "github.com/RobotsAndPencils/buford/payload" "github.com/RobotsAndPencils/buford/push" "github.com/micromdm/dep" + "github.com/micromdm/micromdm/applications" "github.com/micromdm/micromdm/device" "github.com/micromdm/micromdm/workflow" "github.com/pkg/errors" @@ -26,6 +27,10 @@ type Service interface { // Devices Devices() ([]device.Device, error) Device(uuid string) (*device.Device, error) + + // Installed Applications + InstalledApps(deviceUUID string) (*[]applications.Application, error) + // AssignWorkflow assigns a workflow to a device AssignWorkflow(deviceUUID, workflowUUID string) error @@ -38,20 +43,22 @@ type Service interface { } // NewService creates a management service -func NewService(ds device.Datastore, ws workflow.Datastore, dc dep.Client, ps *push.Service) Service { +func NewService(ds device.Datastore, ws workflow.Datastore, dc dep.Client, ps *push.Service, as applications.Datastore) Service { return &service{ - devices: ds, - depClient: dc, - workflows: ws, - pushsvc: ps, + devices: ds, + depClient: dc, + workflows: ws, + pushsvc: ps, + applications: as, } } type service struct { - depClient dep.Client - devices device.Datastore - workflows workflow.Datastore - pushsvc *push.Service + depClient dep.Client + devices device.Datastore + workflows workflow.Datastore + pushsvc *push.Service + applications applications.Datastore } func (svc service) Push(deviceUDID string) (string, error) { @@ -157,3 +164,12 @@ func (svc service) AssignWorkflow(deviceUUID, workflowUUID string) error { dev.Workflow = workflowUUID return svc.devices.Save("assignWorkflow", dev) } + +func (svc service) InstalledApps(deviceUUID string) (*[]applications.Application, error) { + apps, err := svc.applications.GetApplicationsByDeviceUUID(deviceUUID) + if err != nil { + return nil, errors.Wrap(err, "management: installed apps") + } + + return apps, nil +} diff --git a/management/transport.go b/management/transport.go index 2f52a004..2eae74e1 100644 --- a/management/transport.go +++ b/management/transport.go @@ -101,6 +101,13 @@ func ServiceHandler(ctx context.Context, svc Service, logger kitlog.Logger) http encodeResponse, opts..., ) + installedAppsHandler := kithttp.NewServer( + ctx, + makeInstalledAppsEndpoint(svc), + decodeInstalledAppsRequest, + encodeResponse, + opts..., + ) r := mux.NewRouter() @@ -111,6 +118,7 @@ func ServiceHandler(ctx context.Context, svc Service, logger kitlog.Logger) http r.Handle("/management/v1/devices/{uuid}", showDeviceHandler).Methods("GET") r.Handle("/management/v1/devices/{uuid}", updateDeviceHandler).Methods("PATCH") r.Handle("/management/v1/devices/{udid}/push", pushHandler).Methods("POST") + r.Handle("/management/v1/devices/{uuid}/applications", installedAppsHandler).Methods("GET") // profiles r.Handle("/management/v1/profiles", addProfileHandler).Methods("POST") r.Handle("/management/v1/profiles", listProfilesHandler).Methods("GET") @@ -223,6 +231,21 @@ func decodeUpdateDeviceRequest(_ context.Context, r *http.Request) (interface{}, return request, nil } +func decodeInstalledAppsRequest(_ context.Context, r *http.Request) (interface{}, error) { + vars := mux.Vars(r) + deviceUUID, ok := vars["uuid"] + if !ok { + return nil, errBadRouting + } + + var request = installedAppsRequest{UUID: deviceUUID} + err := json.NewDecoder(r.Body).Decode(&request) + if err == io.EOF { + return nil, errEmptyRequest + } + return request, nil +} + 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)