mirror of
https://github.com/micromdm/micromdm/
synced 2026-08-07 10:05:48 +08:00
Add device uuid to certificate, as there will never be a normalised form of the certificate data.
Change statments to reflect table change from certificates to devices_certificates Add the CertificateList request type to the connect service's Acknowledge method Add certificates datastore to the management service, and add endpoints and request/responses for retrieving certificates by device uuid to the management endpoujnt.
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
type Certificate struct {
|
||||
UUID string `db:"certificate_uuid" json:"uuid"`
|
||||
DeviceUUID string `db:"device_uuid" json:"device_uuid"`
|
||||
Data x509.Certificate `db:"data" json:"data"`
|
||||
CommonName string `db:"common_name" json:"common_nane"`
|
||||
IsIdentity bool `db:"is_identity" json:"is_identity"`
|
||||
|
||||
@@ -11,25 +11,38 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
insertCertificateStmt = `INSERT INTO certificates (
|
||||
insertCertificateStmt = `INSERT INTO devices_certificates (
|
||||
device_uuid,
|
||||
common_name,
|
||||
data,
|
||||
is_identity
|
||||
) VALUES ($1, $2, $3)
|
||||
) VALUES ($1, $2, $3, $4)
|
||||
RETURNING certificate_uuid;`
|
||||
|
||||
selectCertificatesStmt = `SELECT
|
||||
certificate_uuid,
|
||||
device_uuid
|
||||
common_name,
|
||||
data,
|
||||
is_identity
|
||||
FROM certificates`
|
||||
|
||||
selectCertificatesByDeviceStmt = `SELECT
|
||||
certificate_uuid,
|
||||
certificates.device_uuid device_uuid
|
||||
common_name,
|
||||
data,
|
||||
is_identity
|
||||
FROM certificates
|
||||
INNER JOIN devices ON certificates.device_uuid = devices.device_uuid
|
||||
WHERE devices.udid = $1`
|
||||
)
|
||||
|
||||
// This Datastore manages a list of certificates assigned to devices.
|
||||
type Datastore interface {
|
||||
New(crt *Certificate) (string, error)
|
||||
Certificates(params ...interface{}) ([]Certificate, error)
|
||||
GetCertificatesByDeviceUDID(udid string) ([]Certificate, error)
|
||||
}
|
||||
|
||||
type pgStore struct {
|
||||
@@ -63,7 +76,7 @@ func NewDB(driver, conn string, logger kitlog.Logger) (Datastore, error) {
|
||||
}
|
||||
|
||||
func (store pgStore) New(c *Certificate) (string, error) {
|
||||
if err := store.QueryRow(insertCertificateStmt, c.CommonName, "", c.IsIdentity).Scan(&c.UUID); err != nil {
|
||||
if err := store.QueryRow(insertCertificateStmt, c.DeviceUUID, c.CommonName, "", c.IsIdentity).Scan(&c.UUID); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -81,6 +94,15 @@ func (store pgStore) Certificates(params ...interface{}) ([]Certificate, error)
|
||||
return certificates, nil
|
||||
}
|
||||
|
||||
func (store pgStore) GetCertificatesByDeviceUDID(udid string) ([]Certificate, error) {
|
||||
var certificates []Certificate
|
||||
err := store.Select(&certificates, selectCertificatesByDeviceStmt, udid)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "pgStore GetCertificatesByDeviceUDID")
|
||||
}
|
||||
return certificates, nil
|
||||
}
|
||||
|
||||
// UUID is a filter that can be added as a parameter to narrow down the list of returned results
|
||||
type UUID struct {
|
||||
UUID string
|
||||
@@ -90,6 +112,15 @@ func (p UUID) where() string {
|
||||
return fmt.Sprintf("certificate_uuid = '%s'", p.UUID)
|
||||
}
|
||||
|
||||
// Filter by a device uuid
|
||||
type DeviceUUID struct {
|
||||
UUID string
|
||||
}
|
||||
|
||||
func (p DeviceUUID) where() string {
|
||||
return fmt.Sprintf("device_uuid = '%s'", p.UUID)
|
||||
}
|
||||
|
||||
// whereer is for building args passed into a method which finds resources
|
||||
type whereer interface {
|
||||
where() string
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/micromdm/mdm"
|
||||
apps "github.com/micromdm/micromdm/applications"
|
||||
"github.com/micromdm/micromdm/certificates"
|
||||
"github.com/micromdm/micromdm/command"
|
||||
"github.com/micromdm/micromdm/device"
|
||||
"github.com/pkg/errors"
|
||||
@@ -21,11 +22,12 @@ type Service interface {
|
||||
}
|
||||
|
||||
// NewService creates a mdm service
|
||||
func NewService(devices device.Datastore, apps apps.Datastore, cs command.Service) Service {
|
||||
func NewService(devices device.Datastore, apps apps.Datastore, certs certificates.Datastore, cs command.Service) Service {
|
||||
return &service{
|
||||
commands: cs,
|
||||
devices: devices,
|
||||
apps: apps,
|
||||
certs: certs,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +35,7 @@ type service struct {
|
||||
devices device.Datastore
|
||||
apps apps.Datastore
|
||||
commands command.Service
|
||||
certs certificates.Datastore
|
||||
}
|
||||
|
||||
// Acknowledge a response from a device.
|
||||
@@ -46,11 +49,12 @@ func (svc service) Acknowledge(ctx context.Context, req mdm.Response) (int, erro
|
||||
}
|
||||
case "InstalledApplicationList":
|
||||
if err := svc.ackInstalledApplicationList(req); err != nil {
|
||||
fmt.Printf("Got an error acknowledging InstalledApplicationList: %v\n", err)
|
||||
return 0, err
|
||||
}
|
||||
case "CertificateList":
|
||||
return 0, nil
|
||||
if err := svc.ackCertificateList(req); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
default:
|
||||
// Need to handle the absence of RequestType in IOS8 devices
|
||||
if req.QueryResponses.UDID != "" {
|
||||
@@ -64,6 +68,12 @@ func (svc service) Acknowledge(ctx context.Context, req mdm.Response) (int, erro
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
if req.CertificateList != nil {
|
||||
if err := svc.ackCertificateList(req); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
total, err := svc.commands.DeleteCommand(req.UDID, req.CommandUUID)
|
||||
@@ -237,10 +247,24 @@ skip:
|
||||
|
||||
// Acknowledge a response to `CertificateList`.
|
||||
func (svc service) ackCertificateList(req mdm.Response) error {
|
||||
_, err := svc.devices.GetDeviceByUDID(req.UDID, "device_uuid")
|
||||
device, err := svc.devices.GetDeviceByUDID(req.UDID, "device_uuid")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "getting a device record by udid")
|
||||
}
|
||||
|
||||
for _, cert := range req.CertificateList {
|
||||
newCert := certificates.Certificate{
|
||||
CommonName: cert.CommonName,
|
||||
IsIdentity: cert.IsIdentity,
|
||||
//Data: cert.Data,
|
||||
DeviceUUID: device.UUID,
|
||||
}
|
||||
|
||||
_, err := svc.certs.New(&newCert)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "persisting a device certificate")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
6
main.go
6
main.go
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/micromdm/dep"
|
||||
"github.com/micromdm/micromdm/applications"
|
||||
"github.com/micromdm/micromdm/certificates"
|
||||
"github.com/micromdm/micromdm/checkin"
|
||||
"github.com/micromdm/micromdm/command"
|
||||
"github.com/micromdm/micromdm/connect"
|
||||
@@ -200,11 +201,10 @@ func main() {
|
||||
}
|
||||
|
||||
dc := depClient(logger, *flDEPCK, *flDEPCS, *flDEPAT, *flDEPAS, *flDEPServerURL, *flDEPsim)
|
||||
mgmtSvc := management.NewService(deviceDB, workflowDB, dc, pushSvc, appsDB)
|
||||
mgmtSvc := management.NewService(deviceDB, workflowDB, dc, pushSvc, appsDB, certsDB)
|
||||
commandSvc := command.NewService(commandDB)
|
||||
checkinSvc := checkin.NewService(deviceDB, mgmtSvc, commandSvc, enrollmentProfile)
|
||||
connectSvc := connect.NewService(deviceDB, commandSvc)
|
||||
connectSvc := connect.NewService(deviceDB, appsDB, commandSvc)
|
||||
connectSvc := connect.NewService(deviceDB, appsDB, certsDB, commandSvc)
|
||||
enrollSvc, _ := enroll.NewService(*flPushCert, *flPushPass, *flTLSCACert, *flSCEPURL, *flURL)
|
||||
|
||||
httpLogger := log.NewContext(logger).With("component", "http")
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"github.com/RobotsAndPencils/buford/push"
|
||||
"github.com/micromdm/dep"
|
||||
"github.com/micromdm/micromdm/applications"
|
||||
"github.com/micromdm/micromdm/certificates"
|
||||
"github.com/micromdm/micromdm/device"
|
||||
"github.com/micromdm/micromdm/workflow"
|
||||
"github.com/pkg/errors"
|
||||
@@ -31,6 +32,9 @@ type Service interface {
|
||||
// Installed Applications
|
||||
InstalledApps(deviceUUID string) ([]applications.Application, error)
|
||||
|
||||
// Installed Certificates
|
||||
Certificates(deviceUUID string) ([]certificates.Certificate, error)
|
||||
|
||||
// AssignWorkflow assigns a workflow to a device
|
||||
AssignWorkflow(deviceUUID, workflowUUID string) error
|
||||
|
||||
@@ -43,13 +47,14 @@ type Service interface {
|
||||
}
|
||||
|
||||
// NewService creates a management service
|
||||
func NewService(ds device.Datastore, ws workflow.Datastore, dc dep.Client, ps *push.Service, as applications.Datastore) Service {
|
||||
func NewService(ds device.Datastore, ws workflow.Datastore, dc dep.Client, ps *push.Service, as applications.Datastore, cs certificates.Datastore) Service {
|
||||
return &service{
|
||||
devices: ds,
|
||||
depClient: dc,
|
||||
workflows: ws,
|
||||
pushsvc: ps,
|
||||
applications: as,
|
||||
certificates: cs,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +64,7 @@ type service struct {
|
||||
workflows workflow.Datastore
|
||||
pushsvc *push.Service
|
||||
applications applications.Datastore
|
||||
certificates certificates.Datastore
|
||||
}
|
||||
|
||||
func (svc service) Push(deviceUDID string) (string, error) {
|
||||
@@ -173,3 +179,12 @@ func (svc service) InstalledApps(deviceUUID string) ([]applications.Application,
|
||||
|
||||
return apps, nil
|
||||
}
|
||||
|
||||
func (svc service) Certificates(deviceUUID string) ([]certificates.Certificate, error) {
|
||||
certs, err := svc.certificates.GetCertificatesByDeviceUDID(deviceUUID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "management: certificates")
|
||||
}
|
||||
|
||||
return certs, nil
|
||||
}
|
||||
|
||||
@@ -108,6 +108,13 @@ func ServiceHandler(ctx context.Context, svc Service, logger kitlog.Logger) http
|
||||
encodeResponse,
|
||||
opts...,
|
||||
)
|
||||
certificatesHandler := kithttp.NewServer(
|
||||
ctx,
|
||||
makeCertificatesEndpoint(svc),
|
||||
decodeCertificatesRequest,
|
||||
encodeResponse,
|
||||
opts...,
|
||||
)
|
||||
|
||||
r := mux.NewRouter()
|
||||
|
||||
@@ -119,6 +126,7 @@ func ServiceHandler(ctx context.Context, svc Service, logger kitlog.Logger) http
|
||||
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")
|
||||
r.Handle("/management/v1/devices/{uuid}/certificates", certificatesHandler).Methods("GET")
|
||||
// profiles
|
||||
r.Handle("/management/v1/profiles", addProfileHandler).Methods("POST")
|
||||
r.Handle("/management/v1/profiles", listProfilesHandler).Methods("GET")
|
||||
@@ -246,6 +254,21 @@ func decodeInstalledAppsRequest(_ context.Context, r *http.Request) (interface{}
|
||||
return request, nil
|
||||
}
|
||||
|
||||
func decodeCertificatesRequest(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
vars := mux.Vars(r)
|
||||
deviceUUID, ok := vars["uuid"]
|
||||
if !ok {
|
||||
return nil, errBadRouting
|
||||
}
|
||||
|
||||
var request = certificatesRequest{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)
|
||||
|
||||
Reference in New Issue
Block a user