mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 08:55:24 +00:00
App config api (#223)
Add GET and PATCH endpoints for /kolide/config to get/update current app configuration
This commit is contained in:
parent
a3e26b6b9a
commit
26b1e70ac3
11
cli/serve.go
11
cli/serve.go
@ -115,6 +115,17 @@ the way that the kolide server works.
|
||||
initFatal(err, "creating bootstrap user")
|
||||
}
|
||||
}
|
||||
devOrgInfo := &kolide.OrgInfo{
|
||||
OrgName: "Kolide",
|
||||
OrgLogoURL: fmt.Sprintf("%s/logo.png", config.Server.Address),
|
||||
}
|
||||
_, err := svc.NewOrgInfo(ctx, kolide.OrgInfoPayload{
|
||||
OrgName: &devOrgInfo.OrgName,
|
||||
OrgLogoURL: &devOrgInfo.OrgLogoURL,
|
||||
})
|
||||
if err != nil {
|
||||
initFatal(err, "creating fake org info")
|
||||
}
|
||||
}
|
||||
|
||||
svcLogger := kitlog.NewContext(logger).With("component", "service")
|
||||
|
@ -34,6 +34,7 @@ var tables = [...]interface{}{
|
||||
&kolide.DistributedQueryCampaignTarget{},
|
||||
&kolide.Query{},
|
||||
&kolide.DistributedQueryExecution{},
|
||||
&kolide.OrgInfo{},
|
||||
}
|
||||
|
||||
type gormDB struct {
|
||||
|
35
datastore/gorm_app.go
Normal file
35
datastore/gorm_app.go
Normal file
@ -0,0 +1,35 @@
|
||||
package datastore
|
||||
|
||||
import (
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/kolide/kolide-ose/kolide"
|
||||
)
|
||||
|
||||
func (orm gormDB) NewOrgInfo(info *kolide.OrgInfo) (*kolide.OrgInfo, error) {
|
||||
err := orm.DB.First(info).Error
|
||||
switch err {
|
||||
case gorm.ErrRecordNotFound:
|
||||
err = orm.DB.Create(info).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return info, nil
|
||||
case nil:
|
||||
return info, orm.SaveOrgInfo(info)
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (orm gormDB) OrgInfo() (*kolide.OrgInfo, error) {
|
||||
info := &kolide.OrgInfo{}
|
||||
err := orm.DB.First(info).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func (orm gormDB) SaveOrgInfo(info *kolide.OrgInfo) error {
|
||||
return orm.DB.Save(info).Error
|
||||
}
|
50
datastore/gorm_app_test.go
Normal file
50
datastore/gorm_app_test.go
Normal file
@ -0,0 +1,50 @@
|
||||
package datastore
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/kolide/kolide-ose/kolide"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestOrgInfo(t *testing.T) {
|
||||
var ds kolide.Datastore
|
||||
address := os.Getenv("MYSQL_ADDR")
|
||||
if address == "" {
|
||||
ds = setup(t)
|
||||
} else {
|
||||
ds = setupMySQLGORM(t)
|
||||
defer teardownMySQLGORM(t, ds)
|
||||
}
|
||||
|
||||
testOrgInfo(t, ds)
|
||||
}
|
||||
|
||||
func testOrgInfo(t *testing.T, db kolide.Datastore) {
|
||||
info := &kolide.OrgInfo{
|
||||
OrgName: "Kolide",
|
||||
OrgLogoURL: "localhost:8080/logo.png",
|
||||
}
|
||||
|
||||
info, err := db.NewOrgInfo(info)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, info.ID, uint(1))
|
||||
|
||||
info2, err := db.OrgInfo()
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, info2.ID, uint(1))
|
||||
assert.Equal(t, info2.OrgName, info.OrgName)
|
||||
|
||||
info2.OrgName = "koolide"
|
||||
err = db.SaveOrgInfo(info2)
|
||||
assert.Nil(t, err)
|
||||
|
||||
info3, err := db.OrgInfo()
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, info3.OrgName, info2.OrgName)
|
||||
|
||||
info4, err := db.NewOrgInfo(info3)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, info4.ID, uint(1))
|
||||
}
|
@ -13,6 +13,7 @@ type inmem struct {
|
||||
users map[uint]*kolide.User
|
||||
sessions map[uint]*kolide.Session
|
||||
passwordResets map[uint]*kolide.PasswordResetRequest
|
||||
orginfo *kolide.OrgInfo
|
||||
}
|
||||
|
||||
func (orm *inmem) Name() string {
|
||||
|
30
datastore/inmem_app.go
Normal file
30
datastore/inmem_app.go
Normal file
@ -0,0 +1,30 @@
|
||||
package datastore
|
||||
|
||||
import "github.com/kolide/kolide-ose/kolide"
|
||||
|
||||
func (orm *inmem) NewOrgInfo(info *kolide.OrgInfo) (*kolide.OrgInfo, error) {
|
||||
orm.mtx.Lock()
|
||||
defer orm.mtx.Unlock()
|
||||
|
||||
orm.orginfo = info
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func (orm *inmem) OrgInfo() (*kolide.OrgInfo, error) {
|
||||
orm.mtx.Lock()
|
||||
defer orm.mtx.Unlock()
|
||||
|
||||
if orm.orginfo != nil {
|
||||
return orm.orginfo, nil
|
||||
}
|
||||
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
|
||||
func (orm *inmem) SaveOrgInfo(info *kolide.OrgInfo) error {
|
||||
orm.mtx.Lock()
|
||||
defer orm.mtx.Unlock()
|
||||
|
||||
orm.orginfo = info
|
||||
return nil
|
||||
}
|
34
kolide/app.go
Normal file
34
kolide/app.go
Normal file
@ -0,0 +1,34 @@
|
||||
package kolide
|
||||
|
||||
import "context"
|
||||
|
||||
// AppConfigStore contains method for saving and retrieving
|
||||
// application configuration
|
||||
type AppConfigStore interface {
|
||||
NewOrgInfo(info *OrgInfo) (*OrgInfo, error)
|
||||
OrgInfo() (*OrgInfo, error)
|
||||
SaveOrgInfo(info *OrgInfo) error
|
||||
}
|
||||
|
||||
// AppConfigService provides methods for configuring
|
||||
// the Kolide application
|
||||
type AppConfigService interface {
|
||||
NewOrgInfo(ctx context.Context, p OrgInfoPayload) (*OrgInfo, error)
|
||||
OrgInfo(ctx context.Context) (*OrgInfo, error)
|
||||
ModifyOrgInfo(ctx context.Context, p OrgInfoPayload) (*OrgInfo, error)
|
||||
}
|
||||
|
||||
// OrgInfo holds information about the current
|
||||
// organization using Kolide
|
||||
type OrgInfo struct {
|
||||
ID uint `gorm:"primary_key"`
|
||||
OrgName string
|
||||
OrgLogoURL string
|
||||
}
|
||||
|
||||
// OrgInfoPayload is used to accept
|
||||
// OrgInfo modifications by a client
|
||||
type OrgInfoPayload struct {
|
||||
OrgName *string `json:"org_name"`
|
||||
OrgLogoURL *string `json:"org_logo_url"`
|
||||
}
|
@ -9,6 +9,7 @@ type Datastore interface {
|
||||
HostStore
|
||||
PasswordResetStore
|
||||
SessionStore
|
||||
AppConfigStore
|
||||
Name() string
|
||||
Drop() error
|
||||
Migrate() error
|
||||
|
@ -8,4 +8,5 @@ type Service interface {
|
||||
QueryService
|
||||
OsqueryService
|
||||
HostService
|
||||
AppConfigService
|
||||
}
|
||||
|
54
server/endpoint_appconfig.go
Normal file
54
server/endpoint_appconfig.go
Normal file
@ -0,0 +1,54 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/kolide/kolide-ose/kolide"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// getAppConfig is used to return
|
||||
// current configuration data to the client
|
||||
type getAppConfigResponse struct {
|
||||
Err error `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (r getAppConfigResponse) error() error { return r.Err }
|
||||
|
||||
type appConfig map[string]map[string]string
|
||||
|
||||
func makeGetAppConfigEndpoint(svc kolide.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
info, err := svc.OrgInfo(ctx)
|
||||
if err != nil {
|
||||
return getAppConfigResponse{Err: err}, nil
|
||||
}
|
||||
config := appConfig{
|
||||
"org_info": map[string]string{
|
||||
"org_name": info.OrgName,
|
||||
"org_logo_url": info.OrgLogoURL,
|
||||
},
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
}
|
||||
|
||||
type modifyAppConfigRequest struct {
|
||||
OrgPayload kolide.OrgInfoPayload `json:"org_info"`
|
||||
}
|
||||
|
||||
func makeModifyAppConfigRequest(svc kolide.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(modifyAppConfigRequest)
|
||||
info, err := svc.ModifyOrgInfo(ctx, req.OrgPayload)
|
||||
if err != nil {
|
||||
return getAppConfigResponse{Err: err}, nil
|
||||
}
|
||||
config := appConfig{
|
||||
"org_info": map[string]string{
|
||||
"org_name": info.OrgName,
|
||||
"org_logo_url": info.OrgLogoURL,
|
||||
},
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
}
|
@ -141,6 +141,26 @@ func attachAPIRoutes(router *mux.Router, ctx context.Context, svc kolide.Service
|
||||
),
|
||||
).Methods("DELETE")
|
||||
|
||||
router.Handle("/api/v1/kolide/config",
|
||||
kithttp.NewServer(
|
||||
ctx,
|
||||
authenticated(makeGetAppConfigEndpoint(svc)),
|
||||
decodeNoParamsRequest,
|
||||
encodeResponse,
|
||||
opts...,
|
||||
),
|
||||
).Methods("GET")
|
||||
|
||||
router.Handle("/api/v1/kolide/config",
|
||||
kithttp.NewServer(
|
||||
ctx,
|
||||
authenticated(mustBeAdmin(makeModifyAppConfigRequest(svc))),
|
||||
decodeModifyAppConfigRequest,
|
||||
encodeResponse,
|
||||
opts...,
|
||||
),
|
||||
).Methods("PATCH")
|
||||
|
||||
router.Handle("/api/v1/kolide/queries/{id}",
|
||||
kithttp.NewServer(
|
||||
ctx,
|
||||
|
@ -61,6 +61,14 @@ func TestAPIRoutes(t *testing.T) {
|
||||
verb: "GET",
|
||||
uri: "/api/v1/kolide/me",
|
||||
},
|
||||
{
|
||||
verb: "GET",
|
||||
uri: "/api/v1/kolide/config",
|
||||
},
|
||||
{
|
||||
verb: "PATCH",
|
||||
uri: "/api/v1/kolide/config",
|
||||
},
|
||||
{
|
||||
verb: "GET",
|
||||
uri: "/api/v1/kolide/queries/1",
|
||||
|
46
server/service_appconfig.go
Normal file
46
server/service_appconfig.go
Normal file
@ -0,0 +1,46 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/kolide/kolide-ose/kolide"
|
||||
)
|
||||
|
||||
func (svc service) NewOrgInfo(ctx context.Context, p kolide.OrgInfoPayload) (*kolide.OrgInfo, error) {
|
||||
info := &kolide.OrgInfo{}
|
||||
if p.OrgName != nil {
|
||||
info.OrgName = *p.OrgName
|
||||
}
|
||||
if p.OrgLogoURL != nil {
|
||||
info.OrgLogoURL = *p.OrgLogoURL
|
||||
}
|
||||
info, err := svc.ds.NewOrgInfo(info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func (svc service) OrgInfo(ctx context.Context) (*kolide.OrgInfo, error) {
|
||||
return svc.ds.OrgInfo()
|
||||
}
|
||||
|
||||
func (svc service) ModifyOrgInfo(ctx context.Context, p kolide.OrgInfoPayload) (*kolide.OrgInfo, error) {
|
||||
info, err := svc.ds.OrgInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if p.OrgName != nil {
|
||||
info.OrgName = *p.OrgName
|
||||
}
|
||||
if p.OrgLogoURL != nil {
|
||||
info.OrgLogoURL = *p.OrgLogoURL
|
||||
}
|
||||
|
||||
err = svc.ds.SaveOrgInfo(info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return info, nil
|
||||
}
|
16
server/transport_appconfig.go
Normal file
16
server/transport_appconfig.go
Normal file
@ -0,0 +1,16 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func decodeModifyAppConfigRequest(ctx context.Context, r *http.Request) (interface{}, error) {
|
||||
var req modifyAppConfigRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return req, nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user