Files
micromdm/core/list/service.go
Jesse Peterson 670b4399db Profile service/store, API, and mdmctl management (#156)
* Profile service/store, API, and mdmctl management
* Group methods with their types
* Centralize error checking and remove profile ID specification on upload (unnecessary)
* Move more error checking into methods
2017-05-17 00:19:26 -07:00

198 lines
4.5 KiB
Go

package list
import (
"bytes"
"context"
"encoding/json"
"crypto/rsa"
"crypto/x509"
"github.com/boltdb/bolt"
"github.com/micromdm/micromdm/blueprint"
"github.com/micromdm/micromdm/crypto"
"github.com/micromdm/micromdm/device"
"github.com/micromdm/micromdm/profile"
)
type ListDevicesOption struct {
Page int
PerPage int
FilterSerial []string
FilterUDID []string
}
type GetBlueprintsOption struct {
FilterName string
}
type GetProfilesOption struct {
Identifier string `json:"id"`
}
type Service interface {
ListDevices(ctx context.Context, opt ListDevicesOption) ([]DeviceDTO, error)
GetDEPTokens(ctx context.Context) ([]DEPToken, []byte, error)
GetBlueprints(ctx context.Context, opt GetBlueprintsOption) ([]blueprint.Blueprint, error)
GetProfiles(ctx context.Context, opt GetProfilesOption) ([]profile.Profile, error)
}
type ListService struct {
Devices *device.DB
Blueprints *blueprint.DB
Profiles *profile.DB
DB *bolt.DB // TODO: replace with reference to DEP token svc/pkg
}
func (svc *ListService) ListDevices(ctx context.Context, opt ListDevicesOption) ([]DeviceDTO, error) {
devices, err := svc.Devices.List()
dto := []DeviceDTO{}
for _, d := range devices {
dto = append(dto, DeviceDTO{
SerialNumber: d.SerialNumber,
UDID: d.UDID,
EnrollmentStatus: d.Enrolled,
LastSeen: d.LastCheckin,
})
}
return dto, err
}
// TODO: move into seperate svc/pkg
const (
depTokenBucket = "mdm.DEPToken"
)
// TODO: move into seperate svc/pkg
func GetDEPTokens(db *bolt.DB) ([]DEPToken, error) {
var result []DEPToken
err := db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(depTokenBucket))
if b == nil {
return nil
}
c := b.Cursor()
prefix := []byte("CK_")
for k, v := c.Seek(prefix); k != nil && bytes.HasPrefix(k, prefix); k, v = c.Next() {
var depToken DEPToken
err := json.Unmarshal(v, &depToken)
if err != nil {
// TODO: log problematic DEP token, or remove altogether?
continue
}
result = append(result, depToken)
}
return nil
})
return result, err
}
// TODO: move into seperate svc/pkg
func generateAndStoreDEPKeypair(db *bolt.DB) (key *rsa.PrivateKey, cert *x509.Certificate, err error) {
key, cert, err = crypto.SimpleSelfSignedRSAKeypair("micromdm-dep-token", 365)
if err != nil {
return
}
pkBytes := x509.MarshalPKCS1PrivateKey(key)
certBytes := cert.Raw
err = db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucketIfNotExists([]byte(depTokenBucket))
if err != nil {
return err
}
err = b.Put([]byte("key"), pkBytes)
if err != nil {
return err
}
err = b.Put([]byte("certificate"), certBytes)
if err != nil {
return err
}
return nil
})
return
}
// TODO: move into seperate svc/pkg
func GetDEPKeypair(db *bolt.DB) (key *rsa.PrivateKey, cert *x509.Certificate, err error) {
var keyBytes, certBytes []byte
err = db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(depTokenBucket))
if b == nil {
return nil
}
keyBytes = b.Get([]byte("key"))
certBytes = b.Get([]byte("certificate"))
return nil
})
if err != nil {
return
}
if keyBytes == nil || certBytes == nil {
// if there is no certificate or private key then generate
key, cert, err = generateAndStoreDEPKeypair(db)
} else {
key, err = x509.ParsePKCS1PrivateKey(keyBytes)
if err != nil {
return
}
cert, err = x509.ParseCertificate(certBytes)
if err != nil {
return
}
}
return
}
func (svc *ListService) GetDEPTokens(ctx context.Context) ([]DEPToken, []byte, error) {
_, cert, err := GetDEPKeypair(svc.DB)
if err != nil {
return nil, nil, err
}
var certBytes []byte
if cert != nil {
certBytes = cert.Raw
}
tokens, err := GetDEPTokens(svc.DB)
if err != nil {
return nil, certBytes, err
}
return tokens, certBytes, nil
}
func (svc *ListService) GetBlueprints(ctx context.Context, opt GetBlueprintsOption) ([]blueprint.Blueprint, error) {
if opt.FilterName != "" {
bp, err := svc.Blueprints.BlueprintByName(opt.FilterName)
if err != nil {
return nil, err
}
return []blueprint.Blueprint{*bp}, err
} else {
bps, err := svc.Blueprints.List()
if err != nil {
return nil, err
}
return bps, nil
}
}
func (svc *ListService) GetProfiles(ctx context.Context, opt GetProfilesOption) ([]profile.Profile, error) {
if opt.Identifier != "" {
foundProf, err := svc.Profiles.ProfileById(opt.Identifier)
if err != nil {
return nil, err
}
return []profile.Profile{*foundProf}, nil
} else {
return svc.Profiles.List()
}
}