Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

clean up, docs, minor refactor(s) 🧹 #38

Merged
merged 12 commits into from
Aug 1, 2023
71 changes: 39 additions & 32 deletions cmd/kmfddm/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ import (
httpddm "github.com/jessepeterson/kmfddm/http"
apihttp "github.com/jessepeterson/kmfddm/http/api"
ddmhttp "github.com/jessepeterson/kmfddm/http/ddm"
"github.com/jessepeterson/kmfddm/log/logkeys"
"github.com/jessepeterson/kmfddm/log/stdlogfmt"
"github.com/jessepeterson/kmfddm/notifier"
"github.com/jessepeterson/kmfddm/notifier/foss"
)

// overridden by -ldflags -X
Expand Down Expand Up @@ -51,24 +53,29 @@ func main() {
logger := stdlogfmt.New(stdlogfmt.WithDebugFlag(*flDebug))

if *flAPIKey == "" {
logger.Info("msg", "empty API key; API disabled")
logger.Info(logkeys.Message, "empty API key; API disabled")
}

storage, err := setupStorage(*flStorage, *flDSN)
if err != nil {
logger.Info("msg", "init storage", "name", *flStorage, "err", err)
logger.Info(logkeys.Message, "init storage", "name", *flStorage, logkeys.Error, err)
os.Exit(1)
}

nOpts := []notifier.Option{
notifier.WithLogger(logger.With("service", "notifier")),
nOpts := []foss.Option{
foss.WithLogger(logger.With("service", "notifier-foss")),
}
if *flMicro {
nOpts = append(nOpts, notifier.WithMicroMDM())
nOpts = append(nOpts, foss.WithMicroMDM())
}
nanoNotif, err := notifier.New(storage, *flEnqueueURL, *flEnqueueKey, nOpts...)
fossNotif, err := foss.NewFossMDM(*flEnqueueURL, *flEnqueueKey, nOpts...)
if err != nil {
logger.Info("msg", "creating notifier", "err", err)
logger.Info(logkeys.Message, "creating notifier", logkeys.Error, err)
os.Exit(1)
}
nanoNotif, err := notifier.New(fossNotif, storage, notifier.WithLogger(logger.With("service", "notifier")))
if err != nil {
logger.Info(logkeys.Message, "creating notifier", logkeys.Error, err)
os.Exit(1)
}

Expand All @@ -78,34 +85,34 @@ func main() {

mux.Handle(
"/declaration-items",
ddmhttp.TokensDeclarationItemsHandler(storage, false, logger.With("handler", "declaration-items")),
ddmhttp.TokensOrDeclarationItemsHandler(storage, false, logger.With(logkeys.Handler, "declaration-items")),
"GET",
)

mux.Handle(
"/tokens",
ddmhttp.TokensDeclarationItemsHandler(storage, true, logger.With("handler", "tokens")),
ddmhttp.TokensOrDeclarationItemsHandler(storage, true, logger.With(logkeys.Handler, "tokens")),
"GET",
)

mux.Handle(
"/declaration/:type/:id",
http.StripPrefix("/declaration/",
ddmhttp.DeclarationHandler(storage, logger.With("handler", "declaration")),
ddmhttp.DeclarationHandler(storage, logger.With(logkeys.Handler, "declaration")),
),
"GET",
)

var statusHandler http.Handler = ddmhttp.StatusReportHandler(storage, logger.With("handler", "status"))
var statusHandler http.Handler = ddmhttp.StatusReportHandler(storage, logger.With(logkeys.Handler, "status"))
if *flDumpStatus != "" {
f := os.Stdout
if *flDumpStatus != "-" {
if f, err = os.OpenFile(*flDumpStatus, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644); err != nil {
logger.Info("msg", "dump status", "path", *flDumpStatus, "err", err)
logger.Info(logkeys.Message, "dump status", "path", *flDumpStatus, logkeys.Error, err)
os.Exit(1)
}
defer f.Close()
logger.Debug("msg", "dump status", "path", *flDumpStatus)
logger.Debug(logkeys.Message, "dump status", "path", *flDumpStatus)
}
statusHandler = DumpHandler(statusHandler, f)
}
Expand Down Expand Up @@ -134,25 +141,25 @@ func main() {

mux.Handle(
"/v1/declarations",
apihttp.PutDeclarationHandler(storage, nanoNotif, logger.With("handler", "put-declaration")),
apihttp.PutDeclarationHandler(storage, nanoNotif, logger.With(logkeys.Handler, "put-declaration")),
"PUT",
)

mux.Handle(
"/v1/declarations/:id",
apihttp.GetDeclarationHandler(storage, logger.With("handler", "get-declaration")),
apihttp.GetDeclarationHandler(storage, logger.With(logkeys.Handler, "get-declaration")),
"GET",
)

mux.Handle(
"/v1/declarations/:id",
apihttp.DeleteDeclarationHandler(storage, logger.With("handler", "delete-declaration")),
apihttp.DeleteDeclarationHandler(storage, logger.With(logkeys.Handler, "delete-declaration")),
"DELETE",
)

mux.Handle(
"/v1/declarations/:id/touch",
apihttp.TouchDeclarationHandler(storage, nanoNotif, logger.With("handler", "touch-declaration")),
apihttp.TouchDeclarationHandler(storage, nanoNotif, logger.With(logkeys.Handler, "touch-declaration")),
"POST",
)

Expand All @@ -166,71 +173,71 @@ func main() {
// set declarations
mux.Handle(
"/v1/set-declarations/:id",
apihttp.GetSetDeclarationsHandler(storage, logger.With("handler", "get-set-declarations")),
apihttp.GetSetDeclarationsHandler(storage, logger.With(logkeys.Handler, "get-set-declarations")),
"GET",
)

mux.Handle(
"/v1/set-declarations/:id",
apihttp.PutSetDeclarationHandler(storage, nanoNotif, logger.With("handler", "put-set-declarations")),
apihttp.PutSetDeclarationHandler(storage, nanoNotif, logger.With(logkeys.Handler, "put-set-declarations")),
"PUT",
)

mux.Handle(
"/v1/set-declarations/:id",
apihttp.DeleteSetDeclarationHandler(storage, nanoNotif, logger.With("handler", "delete-set-delcarations")),
apihttp.DeleteSetDeclarationHandler(storage, nanoNotif, logger.With(logkeys.Handler, "delete-set-delcarations")),
"DELETE",
)

// enrollment sets
mux.Handle(
"/v1/enrollment-sets/:id",
apihttp.GetEnrollmentSetsHandler(storage, logger.With("handler", "get-enrollment-sets")),
apihttp.GetEnrollmentSetsHandler(storage, logger.With(logkeys.Handler, "get-enrollment-sets")),
"GET",
)

mux.Handle(
"/v1/enrollment-sets/:id",
apihttp.PutEnrollmentSetHandler(storage, nanoNotif, logger.With("handler", "put-enrollment-sets")),
apihttp.PutEnrollmentSetHandler(storage, nanoNotif, logger.With(logkeys.Handler, "put-enrollment-sets")),
"PUT",
)

mux.Handle(
"/v1/enrollment-sets/:id",
apihttp.DeleteEnrollmentSetHandler(storage, nanoNotif, logger.With("handler", "delete-enrollment-sets")),
apihttp.DeleteEnrollmentSetHandler(storage, nanoNotif, logger.With(logkeys.Handler, "delete-enrollment-sets")),
"DELETE",
)

// declarations sets
mux.Handle(
"/v1/declaration-sets/:id",
apihttp.GetDeclarationSetsHandler(storage, logger.With("handler", "get-declaration-sets")),
apihttp.GetDeclarationSetsHandler(storage, logger.With(logkeys.Handler, "get-declaration-sets")),
"GET",
)

// status queries
mux.Handle(
"/v1/declaration-status/:id",
apihttp.GetDeclarationStatusHandler(storage, logger.With("handler", "get-declaration-status")),
apihttp.GetDeclarationStatusHandler(storage, logger.With(logkeys.Handler, "get-declaration-status")),
"GET",
)

mux.Handle(
"/v1/status-errors/:id",
apihttp.GetStatusErrorsHandler(storage, logger.With("handler", "get-status-errors")),
apihttp.GetStatusErrorsHandler(storage, logger.With(logkeys.Handler, "get-status-errors")),
"GET",
)

mux.Handle(
"/v1/status-values/:id",
apihttp.GetStatusValuesHandler(storage, logger.With("handler", "get-status-values")),
apihttp.GetStatusValuesHandler(storage, logger.With(logkeys.Handler, "get-status-values")),
"GET",
)

// notifier
mux.Handle(
"/v1/notify",
apihttp.NotifyHandler(nanoNotif, logger.With("handler", "notify")),
apihttp.NotifyHandler(nanoNotif, logger.With(logkeys.Handler, "notify")),
"POST",
)
})
Expand All @@ -239,11 +246,11 @@ func main() {
// init for newTraceID()
rand.Seed(time.Now().UnixNano())

logger.Info("msg", "starting server", "listen", *flListen)
err = http.ListenAndServe(*flListen, httpddm.TraceLoggingMiddleware(mux, logger.With("handler", "log"), newTraceID))
logs := []interface{}{"msg", "server shutdown"}
logger.Info(logkeys.Message, "starting server", "listen", *flListen)
err = http.ListenAndServe(*flListen, httpddm.TraceLoggingMiddleware(mux, logger.With(logkeys.Handler, "log"), newTraceID))
logs := []interface{}{logkeys.Message, "server shutdown"}
if err != nil {
logs = append(logs, "err", err)
logs = append(logs, logkeys.Error, err)
}
logger.Info(logs...)
}
Expand Down
12 changes: 5 additions & 7 deletions cmd/kmfddm/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import (
"hash"

"github.com/cespare/xxhash"
"github.com/jessepeterson/kmfddm/http/api"
"github.com/jessepeterson/kmfddm/http/ddm"
"github.com/jessepeterson/kmfddm/storage"
"github.com/jessepeterson/kmfddm/storage/file"
"github.com/jessepeterson/kmfddm/storage/mysql"
Expand All @@ -15,14 +13,14 @@ import (
)

type allStorage interface {
api.SetAPIStorage
api.DeclarationAPIStorage
ddm.StatusStorage
api.EnrollmentAPIStorage
api.StatusAPIStorage
storage.DeclarationAPIStorage
storage.EnrollmentIDRetriever
storage.EnrollmentDeclarationStorage
storage.StatusStorer
storage.SetDeclarationStorage
storage.SetRetreiver
storage.EnrollmentSetStorage
storage.StatusAPIStorage
}

var hasher func() hash.Hash = func() hash.Hash { return xxhash.New() }
Expand Down
5 changes: 4 additions & 1 deletion ddm/declaration.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"github.com/valyala/fastjson"
)

var ErrInvalidDeclaration = errors.New("invalid declaration")

type Declaration struct {
Identifier string
Type string
Expand All @@ -33,7 +35,7 @@ func (d *Declaration) Valid() bool {
return true
}

// findIDRefs traverses the payload using IDRefs to find any dependent declarations.
// findIDRefs traverses the payload using IdentifierRefs to find any dependent declarations.
func findIDRefs(v *fastjson.Value, declarationType string) []string {
if _, ok := IdentifierRefs[declarationType]; !ok {
return nil
Expand Down Expand Up @@ -96,6 +98,7 @@ func parseDeclarationValue(v *fastjson.Value, d *Declaration) error {
return nil
}

// ParseDeclaration parses raw into a Declaration structure.
func ParseDeclaration(raw []byte) (*Declaration, error) {
v, err := fastjson.ParseBytes(raw)
if err != nil {
Expand Down
49 changes: 34 additions & 15 deletions ddm/items.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import (
"strings"
)

// ManifestDeclaration contains the identifier and server token of a declaration.
// See https://developer.apple.com/documentation/devicemanagement/manifestdeclaration
type ManifestDeclaration struct {
Identifier string
ServerToken string
}

// ManifestDeclarationItems contains a listing of manifest-type delineated declarations.
// See https://developer.apple.com/documentation/devicemanagement/declarationitemsresponse/manifestdeclarationitems
type ManifestDeclarationItems struct {
Activations []ManifestDeclaration
Expand All @@ -20,6 +22,7 @@ type ManifestDeclarationItems struct {
Management []ManifestDeclaration
}

// DeclarationItems are the set of declartions a DDM client should synchronize.
// See https://developer.apple.com/documentation/devicemanagement/declarationitemsresponse
type DeclarationItems struct {
Declarations ManifestDeclarationItems
Expand All @@ -29,6 +32,11 @@ type DeclarationItems struct {
// ManifestType returns the "type" of manifest from a declaration type.
// The result should (but is not guaranteed to) be one of "activation",
// "configuration", "asset", or "management".
//
// Generally this is the third word separated by periods in a
// declaration type. For example the manifest type of the declaration
// type "com.apple.configuration.management.test" should be
// "configuration".
func ManifestType(t string) string {
if !strings.HasPrefix(t, "com.apple.") {
return ""
Expand All @@ -41,13 +49,27 @@ func ManifestType(t string) string {
return t[0:pos]
}

// NewHash returns a newly instantiated hashing function.
type NewHash func() hash.Hash

// DIBuilder incrementally builds the DDM Declaration Items structure for later serializing.
type DIBuilder struct {
hash hash.Hash
DeclarationItems
hash.Hash
}

func NewDIBuilder(newHash func() hash.Hash) *DIBuilder {
b := &DIBuilder{
// NewDIBuilder constructs a new Declaration Items builder.
// It will panic if provided with a nil hasher.
func NewDIBuilder(newHash NewHash) *DIBuilder {
if newHash == nil {
panic("nil hasher")
}
hash := newHash()
if hash == nil {
panic("nil hash")
}
return &DIBuilder{
hash: hash,
DeclarationItems: DeclarationItems{
Declarations: ManifestDeclarationItems{
// init slices so they're non-nil for the JSON encoder.
Expand All @@ -59,20 +81,14 @@ func NewDIBuilder(newHash func() hash.Hash) *DIBuilder {
},
},
}
if newHash != nil {
b.Hash = newHash()
}
return b
}

func tokenHashWrite(h hash.Hash, d *Declaration) {
if h == nil {
return
}
h.Write([]byte(d.ServerToken))
}

func (b *DIBuilder) AddDeclarationData(d *Declaration) {
// Add adds a declaration d to the Declaration Items builder.
func (b *DIBuilder) Add(d *Declaration) {
md := ManifestDeclaration{
Identifier: d.Identifier,
ServerToken: d.ServerToken,
Expand All @@ -87,11 +103,14 @@ func (b *DIBuilder) AddDeclarationData(d *Declaration) {
case "management":
b.Declarations.Management = append(b.Declarations.Management, md)
}
tokenHashWrite(b.Hash, d)
tokenHashWrite(b.hash, d)
}

func tokenHashFinalize(h hash.Hash) string {
return fmt.Sprintf("%x", h.Sum(nil))
}

// Finalize finishes building the declarations items by computing the final Declarations Token.
func (b *DIBuilder) Finalize() {
if b.Hash != nil {
b.DeclarationItems.DeclarationsToken = fmt.Sprintf("%x", b.Hash.Sum(nil))
}
b.DeclarationItems.DeclarationsToken = tokenHashFinalize(b.hash)
}
Loading