refactor user api (#351)

Closes #307
This commit is contained in:
Victor Vrantchan
2017-12-09 22:42:03 -05:00
committed by GitHub
parent 45aacebb02
commit c5b1e8ccce
22 changed files with 307 additions and 218 deletions

View File

@@ -63,7 +63,7 @@ func (cmd *applyCommand) applyUser(args []string) error {
manifest.PasswordHash = hashPlist
}
usr, err := cmd.applysvc.ApplyUser(context.TODO(), manifest)
usr, err := cmd.usersvc.ApplyUser(context.TODO(), manifest)
if err != nil {
return errors.Wrap(err, "apply user with mdmctl")
}

View File

@@ -7,9 +7,8 @@ import (
"os"
"text/tabwriter"
"github.com/micromdm/micromdm/platform/user"
"github.com/pkg/errors"
"github.com/micromdm/micromdm/platform/api/server/list"
)
type usersTableOutput struct{ w *tabwriter.Writer }
@@ -34,7 +33,7 @@ func (cmd *getCommand) getUsers(args []string) error {
out.BasicHeader()
defer out.BasicFooter()
users, err := cmd.list.ListUsers(context.TODO(), list.ListUsersOption{})
users, err := cmd.usersvc.ListUsers(context.TODO(), user.ListUsersOption{})
if err != nil {
return errors.Wrap(err, "list users")
}

View File

@@ -9,12 +9,14 @@ import (
"github.com/micromdm/micromdm/platform/blueprint"
"github.com/micromdm/micromdm/platform/profile"
"github.com/micromdm/micromdm/platform/remove"
"github.com/micromdm/micromdm/platform/user"
)
type remoteServices struct {
profilesvc profile.Service
blueprintsvc blueprint.Service
blocksvc remove.Service
usersvc user.Service
applysvc apply.Service
list list.Service
}
@@ -46,6 +48,13 @@ func setupClient(logger log.Logger) (*remoteServices, error) {
return nil, err
}
usersvc, err := user.NewHTTPClient(
cfg.ServerURL, cfg.APIToken, logger,
httptransport.SetClient(skipVerifyHTTPClient(cfg.SkipVerify)))
if err != nil {
return nil, err
}
applysvc, err := apply.NewClient(
cfg.ServerURL, logger, cfg.APIToken,
httptransport.SetClient(skipVerifyHTTPClient(cfg.SkipVerify)))
@@ -64,6 +73,7 @@ func setupClient(logger log.Logger) (*remoteServices, error) {
profilesvc: profilesvc,
blueprintsvc: blueprintsvc,
blocksvc: blocksvc,
usersvc: usersvc,
applysvc: applysvc,
list: listsvc,
}, nil

View File

@@ -61,6 +61,7 @@ import (
block "github.com/micromdm/micromdm/platform/remove"
blockbuiltin "github.com/micromdm/micromdm/platform/remove/builtin"
"github.com/micromdm/micromdm/platform/user"
userbuiltin "github.com/micromdm/micromdm/platform/user/builtin"
"github.com/micromdm/micromdm/workflow/webhook"
)
@@ -172,7 +173,7 @@ func serve(args []string) error {
stdlog.Fatal(err)
}
userDB, err := user.NewDB(sm.db, sm.pubclient, log.With(logger, "component", "user db"))
userDB, err := userbuiltin.NewDB(sm.db, sm.pubclient, log.With(logger, "component", "user db"))
if err != nil {
stdlog.Fatal(err)
}
@@ -284,8 +285,15 @@ func serve(args []string) error {
}
blueprintEndpoints := blueprint.MakeServerEndpoints(blueprintsvc)
blockEndpoints := block.MakeServerEndpoints(removeService)
var usersvc user.Service
{
usersvc = user.New(userDB)
}
userEndpoints := user.MakeServerEndpoints(usersvc)
var listsvc list.Service
{
l := &list.ListService{
@@ -293,7 +301,6 @@ func serve(args []string) error {
Devices: devDB,
Tokens: tokenDB,
Apps: appDB,
Users: userDB,
}
listsvc = l
@@ -313,7 +320,6 @@ func serve(args []string) error {
GetDEPProfileEndpoint: list.MakeGetDEPProfileEndpoint(listsvc),
GetDEPDeviceEndpoint: list.MakeGetDEPDeviceDetailsEndpoint(listsvc),
ListAppsEndpont: list.MakeListAppsEndpoint(listsvc),
ListUserEndpoint: list.MakeListUsersEndpoint(listsvc),
}
var applysvc apply.Service
@@ -322,7 +328,6 @@ func serve(args []string) error {
DEPClient: dc,
Tokens: tokenDB,
Apps: appDB,
Users: userDB,
}
applysvc = l
if err := l.WatchTokenUpdates(sm.pubclient); err != nil {
@@ -340,16 +345,10 @@ func serve(args []string) error {
appUploadEndpoint = apply.MakeUploadAppEndpiont(applysvc)
}
var applyUserEndpoint endpoint.Endpoint
{
applyUserEndpoint = apply.MakeApplyUserEndpoint(applysvc)
}
applyEndpoints := apply.Endpoints{
ApplyDEPTokensEndpoint: apply.MakeApplyDEPTokensEndpoint(applysvc),
DefineDEPProfileEndpoint: defineDEPProfileEndpoint,
AppUploadEndpoint: appUploadEndpoint,
ApplyUserEndpoint: applyUserEndpoint,
}
applyAPIHandlers := apply.MakeHTTPHandlers(ctx, applyEndpoints, connectOpts...)
@@ -375,11 +374,13 @@ func serve(args []string) error {
profilesHandler := profile.MakeHTTPHandler(profileEndpoints, logger)
blueprintsHandler := blueprint.MakeHTTPHandler(blueprintEndpoints, logger)
blockhandler := block.MakeHTTPHandler(blockEndpoints, logger)
userHandler := user.MakeHTTPHandler(userEndpoints, logger)
// API commands. Only handled if the user provides an api key.
if *flAPIKey != "" {
r.Handle("/v1/profiles", apiAuthMiddleware(*flAPIKey, profilesHandler))
r.Handle("/v1/blueprints", apiAuthMiddleware(*flAPIKey, blueprintsHandler))
r.Handle("/v1/users", apiAuthMiddleware(*flAPIKey, userHandler))
r.Handle("/v1/devices/{udid}/block", apiAuthMiddleware(*flAPIKey, blockhandler))
r.Handle("/v1/devices/{udid}/unblock", apiAuthMiddleware(*flAPIKey, blockhandler))
r.Handle("/push/{udid}", apiAuthMiddleware(*flAPIKey, pushHandlers.PushHandler))
@@ -393,8 +394,6 @@ func serve(args []string) error {
r.Handle("/v1/dep/profiles", apiAuthMiddleware(*flAPIKey, applyAPIHandlers.DefineDEPProfileHandler)).Methods("POST")
r.Handle("/v1/apps", apiAuthMiddleware(*flAPIKey, applyAPIHandlers.AppUploadHandler)).Methods("POST")
r.Handle("/v1/apps", apiAuthMiddleware(*flAPIKey, listAPIHandlers.ListAppsHandler)).Methods("GET")
r.Handle("/v1/users", apiAuthMiddleware(*flAPIKey, applyAPIHandlers.ApplyUserhandler)).Methods("PUT")
r.Handle("/v1/users", apiAuthMiddleware(*flAPIKey, listAPIHandlers.ListUsersHander)).Methods("GET")
r.Handle("/v1/config/certificate", apiAuthMiddleware(*flAPIKey, configHandlers.SavePushCertificateHandler)).Methods("PUT")
}

View File

@@ -49,22 +49,10 @@ func NewClient(instance string, logger log.Logger, token string, opts ...httptra
).Endpoint()
}
var applyUserEndpoint endpoint.Endpoint
{
applyUserEndpoint = httptransport.NewClient(
"PUT",
copyURL(u, "/v1/users"),
encodeRequestWithToken(token, EncodeHTTPGenericRequest),
DecodeApplyUserResponse,
opts...,
).Endpoint()
}
return Endpoints{
ApplyDEPTokensEndpoint: applyDEPTokensEndpoint,
DefineDEPProfileEndpoint: defineDEPProfileEndpoint,
AppUploadEndpoint: uploadAppEndpoint,
ApplyUserEndpoint: applyUserEndpoint,
}, nil
}

View File

@@ -6,27 +6,12 @@ import (
"github.com/go-kit/kit/endpoint"
"github.com/micromdm/dep"
"github.com/micromdm/micromdm/platform/user"
)
type Endpoints struct {
ApplyDEPTokensEndpoint endpoint.Endpoint
DefineDEPProfileEndpoint endpoint.Endpoint
AppUploadEndpoint endpoint.Endpoint
ApplyUserEndpoint endpoint.Endpoint
}
func (e Endpoints) ApplyUser(ctx context.Context, u user.User) (*user.User, error) {
request := applyUserRequest{
User: u,
}
resp, err := e.ApplyUserEndpoint(ctx, request)
if err != nil {
return nil, err
}
usr := resp.(applyUserResponse).User
return &usr, resp.(applyUserResponse).Err
}
func (e Endpoints) UploadApp(ctx context.Context, manifestName string, manifest io.Reader, pkgName string, pkg io.Reader) error {
@@ -83,17 +68,6 @@ func MakeDefineDEPProfile(svc Service) endpoint.Endpoint {
}
}
func MakeApplyUserEndpoint(svc Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
req := request.(applyUserRequest)
u, err := svc.ApplyUser(ctx, req.User)
return applyUserResponse{
User: *u,
Err: err,
}, nil
}
}
func MakeUploadAppEndpiont(svc Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
req := request.(appUploadRequest)
@@ -135,14 +109,3 @@ type depProfileResponse struct {
}
func (r *depProfileResponse) error() error { return r.Err }
type applyUserRequest struct {
User user.User `json:"user"`
}
type applyUserResponse struct {
Err error `json:"err"`
User user.User `json:"user"`
}
func (r applyUserResponse) error() error { return r.Err }

View File

@@ -14,18 +14,15 @@ import (
"github.com/fullsailor/pkcs7"
"github.com/micromdm/dep"
"github.com/pkg/errors"
"github.com/micromdm/micromdm/platform/appstore"
"github.com/micromdm/micromdm/platform/deptoken"
"github.com/micromdm/micromdm/platform/pubsub"
"github.com/micromdm/micromdm/platform/user"
)
type Service interface {
ApplyDEPToken(ctx context.Context, P7MContent []byte) error
UploadApp(ctx context.Context, manifestName string, manifest io.Reader, pkgName string, pkg io.Reader) error
ApplyUser(ctx context.Context, u user.User) (*user.User, error)
DEPService
}
@@ -35,20 +32,6 @@ type ApplyService struct {
Tokens *deptoken.DB
Apps appstore.AppStore
Users *user.DB
}
func (svc *ApplyService) ApplyUser(ctx context.Context, u user.User) (*user.User, error) {
toSave := &u
if u.UUID == "" { //newUser
usr, err := user.NewFromRequest(u)
if err != nil {
return nil, errors.Wrap(err, "create user from request")
}
toSave = usr
}
err := svc.Users.Save(toSave)
return toSave, errors.Wrap(err, "apply user")
}
func (svc *ApplyService) UploadApp(ctx context.Context, manifestName string, manifest io.Reader, pkgName string, pkg io.Reader) error {

View File

@@ -17,7 +17,6 @@ type HTTPHandlers struct {
DEPTokensHandler http.Handler
DefineDEPProfileHandler http.Handler
AppUploadHandler http.Handler
ApplyUserhandler http.Handler
}
func MakeHTTPHandlers(ctx context.Context, endpoints Endpoints, opts ...httptransport.ServerOption) HTTPHandlers {
@@ -40,12 +39,6 @@ func MakeHTTPHandlers(ctx context.Context, endpoints Endpoints, opts ...httptran
encodeResponse,
opts...,
),
ApplyUserhandler: httptransport.NewServer(
endpoints.ApplyUserEndpoint,
decodeUserRequest,
encodeResponse,
opts...,
),
}
return h
}
@@ -87,15 +80,6 @@ func decodeAppUploadRequest(ctx context.Context, r *http.Request) (interface{},
}, nil
}
func decodeUserRequest(ctx context.Context, r *http.Request) (interface{}, error) {
var req applyUserRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, errors.Wrap(err, "decode user request")
}
defer r.Body.Close()
return req, nil
}
func EncodeUploadAppRequest(_ context.Context, r *http.Request, request interface{}) error {
req := request.(appUploadRequest)
body := new(bytes.Buffer)
@@ -204,12 +188,3 @@ func DecodeUploadAppResponse(_ context.Context, r *http.Response) (interface{},
err := json.NewDecoder(r.Body).Decode(&resp)
return resp, err
}
func DecodeApplyUserResponse(_ context.Context, r *http.Response) (interface{}, error) {
if r.StatusCode != http.StatusOK {
return nil, errorDecoder(r)
}
var resp applyUserResponse
err := json.NewDecoder(r.Body).Decode(&resp)
return resp, err
}

View File

@@ -81,17 +81,6 @@ func NewClient(instance string, logger log.Logger, token string, opts ...httptra
).Endpoint()
}
var listUsersEndpoint endpoint.Endpoint
{
listUsersEndpoint = httptransport.NewClient(
"GET",
copyURL(u, "/v1/users"),
encodeRequestWithToken(token, EncodeHTTPGenericRequest),
DecodeListUsersResponse,
opts...,
).Endpoint()
}
return Endpoints{
ListDevicesEndpoint: listDevicesEndpoint,
GetDEPTokensEndpoint: getDEPTokensEndpoint,
@@ -99,7 +88,6 @@ func NewClient(instance string, logger log.Logger, token string, opts ...httptra
GetDEPDeviceEndpoint: getDEPDeviceDetailsEndpoint,
GetDEPProfileEndpoint: getDEPProfilesEndpoint,
ListAppsEndpont: listAppsEndpoint,
ListUserEndpoint: listUsersEndpoint,
}, nil
}

View File

@@ -8,7 +8,6 @@ import (
"github.com/micromdm/dep"
"github.com/micromdm/micromdm/platform/deptoken"
"github.com/micromdm/micromdm/platform/user"
)
type Endpoints struct {
@@ -18,16 +17,6 @@ type Endpoints struct {
GetDEPDeviceEndpoint endpoint.Endpoint
GetDEPProfileEndpoint endpoint.Endpoint
ListAppsEndpont endpoint.Endpoint
ListUserEndpoint endpoint.Endpoint
}
func (e Endpoints) ListUsers(ctx context.Context, opts ListUsersOption) ([]user.User, error) {
request := userRequest{opts}
response, err := e.ListUserEndpoint(ctx, request.Opts)
if err != nil {
return nil, err
}
return response.(userResponse).Users, response.(userResponse).Err
}
func (e Endpoints) ListDevices(ctx context.Context, opts ListDevicesOption) ([]DeviceDTO, error) {
@@ -74,17 +63,6 @@ func (e Endpoints) GetDEPDevice(ctx context.Context, serials []string) (*dep.Dev
return response.(depDeviceDetailsResponse).DeviceDetailsResponse, response.(depDeviceDetailsResponse).Err
}
func MakeListUsersEndpoint(svc Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
req := request.(userRequest)
dto, err := svc.ListUsers(ctx, req.Opts)
return userResponse{
Users: dto,
Err: err,
}, nil
}
}
func MakeListDevicesEndpoint(svc Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
req := request.(devicesRequest)
@@ -157,14 +135,6 @@ type DeviceDTO struct {
LastSeen time.Time `json:"last_seen"`
}
type userRequest struct{ Opts ListUsersOption }
type userResponse struct {
Users []user.User `json:"users"`
Err error `json:"err,omitempty"`
}
func (r userResponse) error() error { return r.Err }
type devicesRequest struct{ Opts ListDevicesOption }
type devicesResponse struct {
Devices []DeviceDTO `json:"devices"`

View File

@@ -14,7 +14,6 @@ import (
"github.com/micromdm/micromdm/platform/deptoken"
"github.com/micromdm/micromdm/platform/device"
"github.com/micromdm/micromdm/platform/pubsub"
"github.com/micromdm/micromdm/platform/user"
)
type ListDevicesOption struct {
@@ -25,21 +24,12 @@ type ListDevicesOption struct {
FilterUDID []string
}
type ListUsersOption struct {
Page int
PerPage int
FilterUserID []string
FilterUDID []string
}
type ListAppsOption struct {
FilterName []string `json:"filter_name"`
}
type Service interface {
ListDevices(ctx context.Context, opt ListDevicesOption) ([]DeviceDTO, error)
ListUsers(ctx context.Context, opt ListUsersOption) ([]user.User, error)
GetDEPTokens(ctx context.Context) ([]deptoken.DEPToken, []byte, error)
ListApplications(ctx context.Context, opt ListAppsOption) ([]AppDTO, error)
DEPService
@@ -52,7 +42,6 @@ type ListService struct {
Devices *device.DB
Tokens *deptoken.DB
Apps appstore.AppStore
Users *user.DB
}
func (svc *ListService) ListApplications(ctx context.Context, opts ListAppsOption) ([]AppDTO, error) {
@@ -124,11 +113,6 @@ func (svc *ListService) ListDevices(ctx context.Context, opt ListDevicesOption)
return dto, err
}
func (svc *ListService) ListUsers(ctx context.Context, opts ListUsersOption) ([]user.User, error) {
u, err := svc.Users.List()
return u, errors.Wrap(err, "list users from api request")
}
func (svc *ListService) GetDEPTokens(ctx context.Context) ([]deptoken.DEPToken, []byte, error) {
_, cert, err := svc.Tokens.DEPKeypair()
if err != nil {

View File

@@ -18,7 +18,6 @@ type HTTPHandlers struct {
GetDEPProfileHandler http.Handler
GetDEPDeviceDetailsHandler http.Handler
ListAppsHandler http.Handler
ListUsersHander http.Handler
}
func MakeHTTPHandlers(ctx context.Context, endpoints Endpoints, opts ...httptransport.ServerOption) HTTPHandlers {
@@ -58,12 +57,6 @@ func MakeHTTPHandlers(ctx context.Context, endpoints Endpoints, opts ...httptran
encodeResponse,
opts...,
),
ListUsersHander: httptransport.NewServer(
endpoints.ListUserEndpoint,
decodeListUsersRequest,
encodeResponse,
opts...,
),
}
return h
}
@@ -79,11 +72,6 @@ func decodeListDevicesRequest(ctx context.Context, r *http.Request) (interface{}
return req, nil
}
func decodeListUsersRequest(ctx context.Context, r *http.Request) (interface{}, error) {
req := userRequest{}
return req, nil
}
func decodeDepAccountInfoRequest(ctx context.Context, r *http.Request) (interface{}, error) {
return nil, nil
}
@@ -210,12 +198,3 @@ func DecodeListAppsResponse(_ context.Context, r *http.Response) (interface{}, e
err := json.NewDecoder(r.Body).Decode(&resp)
return resp, err
}
func DecodeListUsersResponse(_ context.Context, r *http.Response) (interface{}, error) {
if r.StatusCode != http.StatusOK {
return nil, errorDecoder(r)
}
var resp userResponse
err := json.NewDecoder(r.Body).Decode(&resp)
return resp, errors.Wrap(err, "decode user response")
}

View File

@@ -20,13 +20,13 @@ const (
type DB struct {
*bolt.DB
profDB profile.Store
userDB *user.DB
userDB user.Store
}
func NewDB(
db *bolt.DB,
profileDB profile.Store,
userDB *user.DB,
userDB user.Store,
) (*DB, error) {
err := db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucketIfNotExists([]byte(blueprintIndexBucket))

View File

@@ -1,11 +1,10 @@
package blueprint
import (
"net/http"
"github.com/go-kit/kit/endpoint"
"github.com/go-kit/kit/log"
httptransport "github.com/go-kit/kit/transport/http"
"github.com/gorilla/mux"
"github.com/micromdm/micromdm/pkg/httputil"
)
@@ -23,7 +22,7 @@ func MakeServerEndpoints(s Service) Endpoints {
}
}
func MakeHTTPHandler(e Endpoints, logger log.Logger) http.Handler {
func MakeHTTPHandler(e Endpoints, logger log.Logger) *mux.Router {
r, options := httputil.NewRouter(logger)
// PUT /v1/blueprints create or replace a blueprint on the server

View File

@@ -1,11 +1,10 @@
package profile
import (
"net/http"
"github.com/go-kit/kit/endpoint"
"github.com/go-kit/kit/log"
httptransport "github.com/go-kit/kit/transport/http"
"github.com/gorilla/mux"
"github.com/micromdm/micromdm/pkg/httputil"
)
@@ -23,7 +22,7 @@ func MakeServerEndpoints(s Service) Endpoints {
}
}
func MakeHTTPHandler(e Endpoints, logger log.Logger) http.Handler {
func MakeHTTPHandler(e Endpoints, logger log.Logger) *mux.Router {
r, options := httputil.NewRouter(logger)
// GET /v1/profiles get a list of profiles managed by the server

View File

@@ -1,11 +1,10 @@
package remove
import (
"net/http"
"github.com/go-kit/kit/endpoint"
"github.com/go-kit/kit/log"
httptransport "github.com/go-kit/kit/transport/http"
"github.com/gorilla/mux"
"github.com/micromdm/micromdm/pkg/httputil"
)
@@ -21,7 +20,7 @@ func MakeServerEndpoints(s Service) Endpoints {
}
}
func MakeHTTPHandler(e Endpoints, logger log.Logger) http.Handler {
func MakeHTTPHandler(e Endpoints, logger log.Logger) *mux.Router {
r, options := httputil.NewRouter(logger)
// POST /v1/devices/:udid/block force a device to unenroll next time it connects

View File

@@ -0,0 +1,69 @@
package user
import (
"context"
"net/http"
"github.com/go-kit/kit/endpoint"
"github.com/micromdm/micromdm/pkg/httputil"
"github.com/pkg/errors"
)
func (svc *UserService) ApplyUser(ctx context.Context, u User) (*User, error) {
toSave := &u
if u.UUID == "" { //newUser
usr, err := NewFromRequest(u)
if err != nil {
return nil, errors.Wrap(err, "create user from request")
}
toSave = usr
}
err := svc.store.Save(toSave)
return toSave, errors.Wrap(err, "apply user")
}
type applyUserRequest struct {
User User `json:"user"`
}
type applyUserResponse struct {
User User `json:"user"`
Err error `json:"err,omitempty"`
}
func (r applyUserResponse) Failed() error { return r.Err }
func decodeApplyUserRequest(ctx context.Context, r *http.Request) (interface{}, error) {
var req applyUserRequest
err := httputil.DecodeJSONRequest(r, &req)
return req, err
}
func decodeApplyUserResponse(_ context.Context, r *http.Response) (interface{}, error) {
var resp applyUserResponse
err := httputil.DecodeJSONResponse(r, &resp)
return resp, err
}
func MakeApplyUserEndpoint(svc Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
req := request.(applyUserRequest)
u, err := svc.ApplyUser(ctx, req.User)
return applyUserResponse{
User: *u,
Err: err,
}, nil
}
}
func (e Endpoints) ApplyUser(ctx context.Context, u User) (*User, error) {
request := applyUserRequest{
User: u,
}
resp, err := e.ApplyUserEndpoint(ctx, request)
if err != nil {
return nil, err
}
usr := resp.(applyUserResponse).User
return &usr, resp.(applyUserResponse).Err
}

View File

@@ -1,4 +1,4 @@
package user
package builtin
import (
"context"
@@ -12,6 +12,7 @@ import (
"github.com/micromdm/micromdm/mdm/checkin"
"github.com/micromdm/micromdm/platform/pubsub"
"github.com/micromdm/micromdm/platform/user"
)
const (
@@ -51,14 +52,14 @@ func NewDB(db *bolt.DB, pubsubSvc pubsub.PublishSubscriber, logger log.Logger) (
return datastore, nil
}
func (db *DB) List() ([]User, error) {
var users []User
func (db *DB) List() ([]user.User, error) {
var users []user.User
err := db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(UserBucket))
c := b.Cursor()
for k, v := c.First(); k != nil; k, v = c.Next() {
var u User
if err := UnmarshalUser(v, &u); err != nil {
var u user.User
if err := user.UnmarshalUser(v, &u); err != nil {
return err
}
users = append(users, u)
@@ -68,7 +69,7 @@ func (db *DB) List() ([]User, error) {
return users, errors.Wrap(err, "list users")
}
func (db *DB) Save(u *User) error {
func (db *DB) Save(u *user.User) error {
tx, err := db.DB.Begin(true)
if err != nil {
return errors.Wrap(err, "begin transaction")
@@ -77,7 +78,7 @@ func (db *DB) Save(u *User) error {
if bkt == nil {
return fmt.Errorf("bucket %q not found!", UserBucket)
}
userpb, err := MarshalUser(u)
userpb, err := user.MarshalUser(u)
if err != nil {
return errors.Wrap(err, "marshalling user")
}
@@ -106,15 +107,15 @@ func (db *DB) Save(u *User) error {
return tx.Commit()
}
func (db *DB) User(uuid string) (*User, error) {
var u User
func (db *DB) User(uuid string) (*user.User, error) {
var u user.User
err := db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(UserBucket))
v := b.Get([]byte(uuid))
if v == nil {
return &notFound{"User", fmt.Sprintf("uuid %s", uuid)}
}
return UnmarshalUser(v, &u)
return user.UnmarshalUser(v, &u)
})
if err != nil {
return nil, errors.Wrap(err, "get user by uuid from bolt")
@@ -122,8 +123,8 @@ func (db *DB) User(uuid string) (*User, error) {
return &u, nil
}
func (db *DB) UserByUserID(userID string) (*User, error) {
var u User
func (db *DB) UserByUserID(userID string) (*user.User, error) {
var u user.User
err := db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(UserBucket))
ib := tx.Bucket([]byte(userIndexBucket))
@@ -135,7 +136,7 @@ func (db *DB) UserByUserID(userID string) (*User, error) {
if idx == nil {
return &notFound{"User", fmt.Sprintf("uuid %s", string(idx))}
}
return UnmarshalUser(v, &u)
return user.UnmarshalUser(v, &u)
})
if err != nil {
return nil, errors.Wrap(err, "get user by user id from bolt")
@@ -143,14 +144,14 @@ func (db *DB) UserByUserID(userID string) (*User, error) {
return &u, nil
}
func (db *DB) DeviceUsers(udid string) ([]User, error) {
var users []User
func (db *DB) DeviceUsers(udid string) ([]user.User, error) {
var users []user.User
err := db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(UserBucket))
c := b.Cursor()
for k, v := c.First(); k != nil; k, v = c.Next() {
var u User
if err := UnmarshalUser(v, &u); err != nil {
var u user.User
if err := user.UnmarshalUser(v, &u); err != nil {
return errors.Wrap(err, "unmarshal user for DeviceUsers")
}
if u.UDID == udid {
@@ -170,8 +171,8 @@ func (db *DB) DeleteDeviceUsers(udid string) error {
b := tx.Bucket([]byte(UserBucket))
c := b.Cursor()
for k, v := c.First(); k != nil; k, v = c.Next() {
var u User
if err := UnmarshalUser(v, &u); err != nil {
var u user.User
if err := user.UnmarshalUser(v, &u); err != nil {
return errors.Wrap(err, "unmarshal user for DeviceUsers")
}
if u.UDID != udid {
@@ -195,6 +196,10 @@ func (e *notFound) Error() string {
return fmt.Sprintf("not found: %s %s", e.ResourceType, e.Message)
}
func (e *notFound) NotFound() bool {
return true
}
func (db *DB) pollCheckin(pubsubSvc pubsub.PublishSubscriber) error {
tokenUpdateEvents, err := pubsubSvc.Subscribe(context.TODO(), "users", checkin.TokenUpdateTopic)
if err != nil {
@@ -213,7 +218,7 @@ func (db *DB) pollCheckin(pubsubSvc pubsub.PublishSubscriber) error {
if event.Command.UserID == "" {
break // only interested in user commands
}
newUser := new(User)
newUser := new(user.User)
byGUID, err := db.UserByUserID(event.Command.UserID)
if err != nil && !isNotFound(err) {
level.Info(db.logger).Log("err", err, "msg", "get user from DB")

44
platform/user/client.go Normal file
View File

@@ -0,0 +1,44 @@
package user
import (
"net/url"
"github.com/go-kit/kit/endpoint"
"github.com/go-kit/kit/log"
httptransport "github.com/go-kit/kit/transport/http"
"github.com/micromdm/micromdm/pkg/httputil"
)
func NewHTTPClient(instance, token string, logger log.Logger, opts ...httptransport.ClientOption) (Service, error) {
u, err := url.Parse(instance)
if err != nil {
return nil, err
}
var applyUserEndpoint endpoint.Endpoint
{
applyUserEndpoint = httptransport.NewClient(
"PUT",
httputil.CopyURL(u, "/v1/users"),
httputil.EncodeRequestWithToken(token, httptransport.EncodeJSONRequest),
decodeApplyUserResponse,
opts...,
).Endpoint()
}
var listUsersEndpoint endpoint.Endpoint
{
listUsersEndpoint = httptransport.NewClient(
"GET",
httputil.CopyURL(u, "/v1/users"),
httputil.EncodeRequestWithToken(token, httptransport.EncodeJSONRequest),
decodeListUsersResponse,
opts...,
).Endpoint()
}
return Endpoints{
ApplyUserEndpoint: applyUserEndpoint,
ListUsersEndpoint: listUsersEndpoint,
}, nil
}

View File

@@ -0,0 +1,59 @@
package user
import (
"context"
"encoding/json"
"net/http"
"github.com/go-kit/kit/endpoint"
"github.com/micromdm/micromdm/pkg/httputil"
"github.com/pkg/errors"
)
func (svc *UserService) ListUsers(ctx context.Context, opts ListUsersOption) ([]User, error) {
u, err := svc.store.List()
return u, errors.Wrap(err, "list users from api request")
}
type getUsersRequest struct{ Opts ListUsersOption }
type getUsersResponse struct {
Users []User `json:"users"`
Err error `json:"err,omitempty"`
}
func (r getUsersResponse) Failed() error { return r.Err }
func decodeListUsersRequest(ctx context.Context, r *http.Request) (interface{}, error) {
var opts ListUsersOption
if err := json.NewDecoder(r.Body).Decode(&opts); err != nil {
return nil, err
}
req := getUsersRequest{Opts: opts}
return req, nil
}
func decodeListUsersResponse(_ context.Context, r *http.Response) (interface{}, error) {
var resp getUsersResponse
err := httputil.DecodeJSONResponse(r, &resp)
return resp, err
}
func MakeListUsersEndpoint(svc Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
req := request.(getUsersRequest)
users, err := svc.ListUsers(ctx, req.Opts)
return getUsersResponse{
Users: users,
Err: err,
}, nil
}
}
func (e Endpoints) ListUsers(ctx context.Context, opts ListUsersOption) ([]User, error) {
request := getUsersRequest{opts}
response, err := e.ListUsersEndpoint(ctx, request.Opts)
if err != nil {
return nil, err
}
return response.(getUsersResponse).Users, response.(getUsersResponse).Err
}

45
platform/user/server.go Normal file
View File

@@ -0,0 +1,45 @@
package user
import (
"github.com/go-kit/kit/endpoint"
"github.com/go-kit/kit/log"
httptransport "github.com/go-kit/kit/transport/http"
"github.com/gorilla/mux"
"github.com/micromdm/micromdm/pkg/httputil"
)
type Endpoints struct {
ApplyUserEndpoint endpoint.Endpoint
ListUsersEndpoint endpoint.Endpoint
}
func MakeServerEndpoints(s Service) Endpoints {
return Endpoints{
ApplyUserEndpoint: MakeApplyUserEndpoint(s),
ListUsersEndpoint: MakeListUsersEndpoint(s),
}
}
func MakeHTTPHandler(e Endpoints, logger log.Logger) *mux.Router {
r, options := httputil.NewRouter(logger)
// PUT /v1/users create or replace an user
// GET /v1/users get a list of users managed by the server
r.Methods("PUT").Path("/v1/users").Handler(httptransport.NewServer(
e.ApplyUserEndpoint,
decodeApplyUserRequest,
httputil.EncodeJSONResponse,
options...,
))
r.Methods("GET").Path("/v1/users").Handler(httptransport.NewServer(
e.ListUsersEndpoint,
decodeListUsersRequest,
httputil.EncodeJSONResponse,
options...,
))
return r
}

32
platform/user/service.go Normal file
View File

@@ -0,0 +1,32 @@
package user
import (
"context"
)
type ListUsersOption struct {
Page int `json:"page"`
PerPage int `json:"per_page"`
FilterUserID []string `json:"filter_user_id"`
FilterUDID []string `json:"filter_udid"`
}
type Service interface {
ApplyUser(ctx context.Context, u User) (*User, error)
ListUsers(ctx context.Context, opt ListUsersOption) ([]User, error)
}
type Store interface {
User(string) (*User, error)
Save(*User) error
List() ([]User, error)
}
type UserService struct {
store Store
}
func New(store Store) *UserService {
return &UserService{store: store}
}