mirror of
https://github.com/micromdm/micromdm/
synced 2026-08-13 05:45:41 +08:00
add profile enrollment
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
package checkin
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/fullsailor/pkcs7"
|
||||
"github.com/groob/plist"
|
||||
)
|
||||
|
||||
@@ -15,6 +17,26 @@ func decodeMDMCheckinRequest(r *http.Request) (interface{}, error) {
|
||||
return request, nil
|
||||
}
|
||||
|
||||
// The enrollment request is PkCS7 signed.
|
||||
// We'll ignore everything but the content for now
|
||||
func decodeMDMEnrollmentRequest(r *http.Request) (interface{}, error) {
|
||||
data, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p7, err := pkcs7.Parse(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// TODO: We should verify but not currently possible. Apple
|
||||
// does no provide a cert for the CA.
|
||||
var request depEnrollmentRequest
|
||||
if err := plist.Unmarshal(p7.Content, &request); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return request, nil
|
||||
}
|
||||
|
||||
// errorer is implemented by all concrete response types. It allows us to
|
||||
// change the HTTP response code without needing to trigger an endpoint
|
||||
// (transport-level) error. For more information, read the big comment in
|
||||
@@ -39,6 +61,18 @@ func encodeResponse(w http.ResponseWriter, response interface{}) error {
|
||||
return enc.Encode(response)
|
||||
}
|
||||
|
||||
func enrollResponse(w http.ResponseWriter, response interface{}) error {
|
||||
if e, ok := response.(errorer); ok && e.error() != nil {
|
||||
// Not a Go kit transport error, but a business-logic error.
|
||||
// Provide those as HTTP errors.
|
||||
encodeError(w, e.error())
|
||||
return nil
|
||||
}
|
||||
resp := response.(depEnrollmentResponse)
|
||||
w.Write(resp.Profile)
|
||||
return nil
|
||||
}
|
||||
|
||||
func encodeError(w http.ResponseWriter, err error) {
|
||||
w.WriteHeader(codeFrom(err))
|
||||
response := map[string]interface{}{
|
||||
|
||||
@@ -25,3 +25,14 @@ func makeCheckinEndpoint(svc MDMCheckinService) endpoint.Endpoint {
|
||||
return mdmCheckinResponse{}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func makeEnrollmentEndpoint(svc MDMCheckinService) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(depEnrollmentRequest)
|
||||
profile, err := svc.Enroll(req.DEPEnrollmentRequest.UDID)
|
||||
if err != nil {
|
||||
return depEnrollmentResponse{Err: err}, nil
|
||||
}
|
||||
return depEnrollmentResponse{Profile: []byte(*profile)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,3 +11,14 @@ type mdmCheckinResponse struct {
|
||||
}
|
||||
|
||||
func (r mdmCheckinResponse) error() error { return r.Err }
|
||||
|
||||
type depEnrollmentRequest struct {
|
||||
mdm.DEPEnrollmentRequest
|
||||
}
|
||||
|
||||
type depEnrollmentResponse struct {
|
||||
Profile []byte // MDM Enrollment Profile
|
||||
Err error `plist:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (r depEnrollmentResponse) error() error { return r.Err }
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/go-kit/kit/metrics"
|
||||
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
|
||||
httptransport "github.com/go-kit/kit/transport/http"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/micromdm/mdm"
|
||||
"github.com/micromdm/micromdm/device"
|
||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||
@@ -25,6 +26,7 @@ type MDMCheckinService interface {
|
||||
Authenticate(mdm.CheckinCommand) error
|
||||
TokenUpdate(mdm.CheckinCommand) error
|
||||
Checkout(mdm.CheckinCommand) error
|
||||
Enroll(udid string) (*device.Profile, error)
|
||||
}
|
||||
|
||||
// NewCheckinService creates a new MDM Checkin Service
|
||||
@@ -121,9 +123,21 @@ func (svc mdmCheckinService) Checkout(cmd mdm.CheckinCommand) error {
|
||||
return err
|
||||
}
|
||||
existing.Enrolled = boolPtr(false)
|
||||
err = svc.db.SaveDevice(existing)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc mdmCheckinService) Enroll(udid string) (*device.Profile, error) {
|
||||
profile, err := svc.db.GetProfileForDevice(udid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return profile, nil
|
||||
}
|
||||
|
||||
// return a pointer to a boolean
|
||||
func boolPtr(b bool) *bool {
|
||||
return &b
|
||||
@@ -141,5 +155,18 @@ func ServiceHandler(ctx context.Context, svc MDMCheckinService) http.Handler {
|
||||
decodeMDMCheckinRequest,
|
||||
encodeResponse,
|
||||
)
|
||||
return checkinHandler
|
||||
|
||||
enroll := makeEnrollmentEndpoint(svc)
|
||||
|
||||
enrollmentHandler := httptransport.NewServer(
|
||||
ctx,
|
||||
enroll,
|
||||
decodeMDMEnrollmentRequest,
|
||||
enrollResponse,
|
||||
)
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.Methods("PUT").Path("/mdm/checkin").Handler(checkinHandler)
|
||||
r.Methods("POST").Path("/mdm/checkin").Handler(enrollmentHandler)
|
||||
return r
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package device
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
@@ -36,6 +37,10 @@ type Device struct {
|
||||
Enrolled *bool `json:"enrolled,omitempty" db:"mdm_enrolled,omitempty"`
|
||||
}
|
||||
|
||||
// Profile is an Enrollment profile.
|
||||
// For now we just have one.
|
||||
type Profile []byte
|
||||
|
||||
// Datastore manages interactions of devices in a database
|
||||
type Datastore interface {
|
||||
AddDevice(*Device) error
|
||||
@@ -43,6 +48,7 @@ type Datastore interface {
|
||||
SaveDevice(*Device) error
|
||||
// RemoveDevice() error
|
||||
// AllDevices() DeviceList,error
|
||||
GetProfileForDevice(udid string) (*Profile, error)
|
||||
}
|
||||
|
||||
type config struct {
|
||||
@@ -173,6 +179,17 @@ func (db pgDatastore) SaveDevice(dev *Device) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetProfileForDevice returns an enrollment profile for a specific device
|
||||
// For now there's a single profile stored on disk
|
||||
func (db pgDatastore) GetProfileForDevice(uuid string) (*Profile, error) {
|
||||
data, err := ioutil.ReadFile("data/profiles/Enrollment.mobileconfig")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
profile := Profile(data)
|
||||
return &profile, nil
|
||||
}
|
||||
|
||||
func migrate(db *sqlx.DB) {
|
||||
schema := `
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
|
||||
2
main.go
2
main.go
@@ -119,7 +119,7 @@ func main() {
|
||||
|
||||
// router
|
||||
r := mux.NewRouter()
|
||||
r.Methods("PUT").Path("/mdm/checkin").Handler(checkinHandler)
|
||||
r.Methods("PUT", "POST").Path("/mdm/checkin").Handler(checkinHandler)
|
||||
r.Methods("PUT").Path("/mdm/connect").Handler(connectHandler)
|
||||
r.Handle("/mdm/commands", commandHandler)
|
||||
r.Methods("POST").Path("/mdm/commands").Handler(commandHandler)
|
||||
|
||||
Reference in New Issue
Block a user