mirror of
https://github.com/fleetdm/fleet
synced 2026-05-23 17:08:53 +00:00
Pull decorators from config options (#1749)
Previously decorators were stored in a separate table. Now they are stored directly with the config so that they can be modified on a per-platform basis. Delete now unused decorators code.
This commit is contained in:
parent
a0d05f4e2a
commit
1d9e37b069
21 changed files with 85 additions and 1066 deletions
|
|
@ -1,39 +0,0 @@
|
|||
package datastore
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/kolide/fleet/server/kolide"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func testDecorators(t *testing.T, ds kolide.Datastore) {
|
||||
decorator := &kolide.Decorator{
|
||||
Query: "select from something",
|
||||
Type: kolide.DecoratorInterval,
|
||||
Interval: 60,
|
||||
}
|
||||
decorator, err := ds.NewDecorator(decorator)
|
||||
require.Nil(t, err)
|
||||
require.True(t, decorator.ID > 0)
|
||||
result, err := ds.Decorator(decorator.ID)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, decorator.Query, result.Query)
|
||||
results, err := ds.ListDecorators()
|
||||
require.Nil(t, err)
|
||||
assert.Len(t, results, 1)
|
||||
|
||||
decorator.Query = "select foo from bar;"
|
||||
err = ds.SaveDecorator(decorator)
|
||||
require.Nil(t, err)
|
||||
result, err = ds.Decorator(decorator.ID)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, "select foo from bar;", result.Query)
|
||||
|
||||
err = ds.DeleteDecorator(decorator.ID)
|
||||
require.Nil(t, err)
|
||||
result, err = ds.Decorator(decorator.ID)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
}
|
||||
|
|
@ -59,7 +59,6 @@ var testFunctions = [...]func(*testing.T, kolide.Datastore){
|
|||
testOptionsToConfig,
|
||||
testGetPackByName,
|
||||
testGetQueryByName,
|
||||
testDecorators,
|
||||
testFileIntegrityMonitoring,
|
||||
testYARAStore,
|
||||
testAddLabelToPackTwice,
|
||||
|
|
|
|||
|
|
@ -1,99 +0,0 @@
|
|||
package mysql
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/kolide/fleet/server/kolide"
|
||||
)
|
||||
|
||||
func (ds *Datastore) SaveDecorator(dec *kolide.Decorator, opts ...kolide.OptionalArg) error {
|
||||
db := ds.getTransaction(opts)
|
||||
sqlStatement :=
|
||||
"UPDATE decorators SET " +
|
||||
"`name` = ?, " +
|
||||
"`query` = ?, " +
|
||||
"`type` = ?, " +
|
||||
"`interval` = ? " +
|
||||
"WHERE id = ?"
|
||||
_, err := db.Exec(
|
||||
sqlStatement,
|
||||
dec.Name,
|
||||
dec.Query,
|
||||
dec.Type,
|
||||
dec.Interval,
|
||||
dec.ID,
|
||||
)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "saving decorator")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ds *Datastore) NewDecorator(decorator *kolide.Decorator, opts ...kolide.OptionalArg) (*kolide.Decorator, error) {
|
||||
db := ds.getTransaction(opts)
|
||||
sqlStatement :=
|
||||
"INSERT INTO decorators (" +
|
||||
"`name`," +
|
||||
"`query`," +
|
||||
"`type`," +
|
||||
"`interval` ) " +
|
||||
"VALUES (?, ?, ?, ?)"
|
||||
result, err := db.Exec(sqlStatement, decorator.Name, decorator.Query, decorator.Type, decorator.Interval)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "creating decorator")
|
||||
}
|
||||
id, _ := result.LastInsertId()
|
||||
decorator.ID = uint(id)
|
||||
return decorator, nil
|
||||
}
|
||||
|
||||
func (ds *Datastore) DeleteDecorator(id uint) error {
|
||||
sqlStatement := `
|
||||
DELETE FROM decorators
|
||||
WHERE id = ?
|
||||
`
|
||||
res, err := ds.db.Exec(sqlStatement, id)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "deleting decorator")
|
||||
}
|
||||
deleted, _ := res.RowsAffected()
|
||||
if deleted < 1 {
|
||||
return notFound("Decorator").WithID(id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ds *Datastore) Decorator(id uint) (*kolide.Decorator, error) {
|
||||
sqlStatement := `
|
||||
SELECT *
|
||||
FROM decorators
|
||||
WHERE id = ?
|
||||
`
|
||||
var result kolide.Decorator
|
||||
err := ds.db.Get(&result, sqlStatement, id)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, notFound("Decorator").WithID(id)
|
||||
}
|
||||
return nil, errors.Wrap(err, "retrieving decorator")
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (ds *Datastore) ListDecorators(opts ...kolide.OptionalArg) ([]*kolide.Decorator, error) {
|
||||
db := ds.getTransaction(opts)
|
||||
sqlStatement := `
|
||||
SELECT *
|
||||
FROM decorators
|
||||
ORDER by built_in DESC, name ASC
|
||||
`
|
||||
var results []*kolide.Decorator
|
||||
err := db.Select(&results, sqlStatement)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "listing decorators")
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
|
@ -3,6 +3,8 @@ package data
|
|||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/jmoiron/sqlx/reflectx"
|
||||
|
|
@ -15,21 +17,14 @@ func init() {
|
|||
}
|
||||
|
||||
type configForExport struct {
|
||||
Options map[string]interface{} `json:"options"`
|
||||
FilePaths map[string][]string `json:"file_paths,omitempty"`
|
||||
}
|
||||
|
||||
type yamlObjForExport struct {
|
||||
ApiVersion string `json:"apiVersion"`
|
||||
Kind string `json:"kind"`
|
||||
Spec specForExport `json:"spec"`
|
||||
}
|
||||
|
||||
type specForExport struct {
|
||||
Config json.RawMessage `json:"config"`
|
||||
Options map[string]interface{} `json:"options"`
|
||||
FilePaths map[string][]string `json:"file_paths,omitempty"`
|
||||
Decorators kolide.Decorators `json:"decorators"`
|
||||
}
|
||||
|
||||
func Up_20171212182458(tx *sql.Tx) error {
|
||||
// Migrate pre fleetctl osquery options to the new osquery options
|
||||
// formats.
|
||||
txx := sqlx.Tx{Tx: tx, Mapper: reflectx.NewMapperFunc("db", sqlx.NameMapper)}
|
||||
|
||||
// Get basic osquery options
|
||||
|
|
@ -70,10 +65,39 @@ func Up_20171212182458(tx *sql.Tx) error {
|
|||
fimConfig[sectionName] = append(fimConfig[sectionName], fileName)
|
||||
}
|
||||
|
||||
query = `
|
||||
SELECT *
|
||||
FROM decorators
|
||||
ORDER by built_in DESC, name ASC
|
||||
`
|
||||
var decorators []*kolide.Decorator
|
||||
err = txx.Select(&decorators, query)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "retrieving decorators")
|
||||
}
|
||||
|
||||
decConfig := kolide.Decorators{
|
||||
Interval: make(map[string][]string),
|
||||
}
|
||||
for _, dec := range decorators {
|
||||
switch dec.Type {
|
||||
case kolide.DecoratorLoad:
|
||||
decConfig.Load = append(decConfig.Load, dec.Query)
|
||||
case kolide.DecoratorAlways:
|
||||
decConfig.Always = append(decConfig.Always, dec.Query)
|
||||
case kolide.DecoratorInterval:
|
||||
key := strconv.Itoa(int(dec.Interval))
|
||||
decConfig.Interval[key] = append(decConfig.Interval[key], dec.Query)
|
||||
default:
|
||||
fmt.Printf("Unable to migrate decorator. Please migrate manually: '%s'\n", dec.Query)
|
||||
}
|
||||
}
|
||||
|
||||
// Create config JSON
|
||||
config := configForExport{
|
||||
Options: optConfig,
|
||||
FilePaths: fimConfig,
|
||||
Options: optConfig,
|
||||
FilePaths: fimConfig,
|
||||
Decorators: decConfig,
|
||||
}
|
||||
confJSON, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ type Datastore interface {
|
|||
InviteStore
|
||||
ScheduledQueryStore
|
||||
OptionStore
|
||||
DecoratorStore
|
||||
FileIntegrityMonitoringStore
|
||||
YARAStore
|
||||
OsqueryOptionsStore
|
||||
|
|
|
|||
|
|
@ -1,38 +1,13 @@
|
|||
package kolide
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// DecoratorStore methods to manipulate decorator queries.
|
||||
// See https://osquery.readthedocs.io/en/stable/deployment/configuration/
|
||||
type DecoratorStore interface {
|
||||
// NewDecorator creates a decorator query.
|
||||
NewDecorator(decorator *Decorator, opts ...OptionalArg) (*Decorator, error)
|
||||
// DeleteDecorator removes a decorator query.
|
||||
DeleteDecorator(id uint) error
|
||||
// Decorator retrieves a decorator query with supplied ID.
|
||||
Decorator(id uint) (*Decorator, error)
|
||||
// ListDecorators returns all decorator queries.
|
||||
ListDecorators(opts ...OptionalArg) ([]*Decorator, error)
|
||||
// SaveDecorator updates an existing decorator
|
||||
SaveDecorator(dec *Decorator, opts ...OptionalArg) error
|
||||
}
|
||||
|
||||
// DecoratorService exposes decorators data so it can be manipulated by
|
||||
// end users
|
||||
type DecoratorService interface {
|
||||
// ListDecorators returns decorators
|
||||
ListDecorators(ctx context.Context) ([]*Decorator, error)
|
||||
// DeleteDecorator removes an existing decorator if it is not built-in
|
||||
DeleteDecorator(ctx context.Context, id uint) error
|
||||
// NewDecorator creates a new decorator
|
||||
NewDecorator(ctx context.Context, payload DecoratorPayload) (*Decorator, error)
|
||||
// ModifyDecorator updates an existing decorator
|
||||
ModifyDecorator(ctx context.Context, payload DecoratorPayload) (*Decorator, error)
|
||||
}
|
||||
// DEPRECATED
|
||||
// Decorators are now stored as JSON in the config, so these types are only
|
||||
// useful for migrating existing Fleet installations.
|
||||
|
||||
// DecoratorType refers to the allowable types of decorator queries.
|
||||
// See https://osquery.readthedocs.io/en/stable/deployment/configuration/
|
||||
|
|
|
|||
|
|
@ -15,6 +15,5 @@ type Service interface {
|
|||
TargetService
|
||||
ScheduledQueryService
|
||||
OptionService
|
||||
DecoratorService
|
||||
FileIntegrityMonitoringService
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ package mock
|
|||
//go:generate mockimpl -o datastore_invites.go "s *InviteStore" "kolide.InviteStore"
|
||||
//go:generate mockimpl -o datastore_appconfig.go "s *AppConfigStore" "kolide.AppConfigStore"
|
||||
//go:generate mockimpl -o datastore_labels.go "s *LabelStore" "kolide.LabelStore"
|
||||
//go:generate mockimpl -o datastore_decorators.go "s *DecoratorStore" "kolide.DecoratorStore"
|
||||
//go:generate mockimpl -o datastore_options.go "s *OptionStore" "kolide.OptionStore"
|
||||
//go:generate mockimpl -o datastore_packs.go "s *PackStore" "kolide.PackStore"
|
||||
//go:generate mockimpl -o datastore_hosts.go "s *HostStore" "kolide.HostStore"
|
||||
|
|
@ -28,7 +27,6 @@ type Store struct {
|
|||
OsqueryOptionsStore
|
||||
FileIntegrityMonitoringStore
|
||||
AppConfigStore
|
||||
DecoratorStore
|
||||
HostStore
|
||||
InviteStore
|
||||
LabelStore
|
||||
|
|
|
|||
|
|
@ -1,59 +0,0 @@
|
|||
// Automatically generated by mockimpl. DO NOT EDIT!
|
||||
|
||||
package mock
|
||||
|
||||
import "github.com/kolide/fleet/server/kolide"
|
||||
|
||||
var _ kolide.DecoratorStore = (*DecoratorStore)(nil)
|
||||
|
||||
type NewDecoratorFunc func(decorator *kolide.Decorator, opts ...kolide.OptionalArg) (*kolide.Decorator, error)
|
||||
|
||||
type DeleteDecoratorFunc func(id uint) error
|
||||
|
||||
type DecoratorFunc func(id uint) (*kolide.Decorator, error)
|
||||
|
||||
type ListDecoratorsFunc func(opts ...kolide.OptionalArg) ([]*kolide.Decorator, error)
|
||||
|
||||
type SaveDecoratorFunc func(dec *kolide.Decorator, opts ...kolide.OptionalArg) error
|
||||
|
||||
type DecoratorStore struct {
|
||||
NewDecoratorFunc NewDecoratorFunc
|
||||
NewDecoratorFuncInvoked bool
|
||||
|
||||
DeleteDecoratorFunc DeleteDecoratorFunc
|
||||
DeleteDecoratorFuncInvoked bool
|
||||
|
||||
DecoratorFunc DecoratorFunc
|
||||
DecoratorFuncInvoked bool
|
||||
|
||||
ListDecoratorsFunc ListDecoratorsFunc
|
||||
ListDecoratorsFuncInvoked bool
|
||||
|
||||
SaveDecoratorFunc SaveDecoratorFunc
|
||||
SaveDecoratorFuncInvoked bool
|
||||
}
|
||||
|
||||
func (s *DecoratorStore) NewDecorator(decorator *kolide.Decorator, opts ...kolide.OptionalArg) (*kolide.Decorator, error) {
|
||||
s.NewDecoratorFuncInvoked = true
|
||||
return s.NewDecoratorFunc(decorator, opts...)
|
||||
}
|
||||
|
||||
func (s *DecoratorStore) DeleteDecorator(id uint) error {
|
||||
s.DeleteDecoratorFuncInvoked = true
|
||||
return s.DeleteDecoratorFunc(id)
|
||||
}
|
||||
|
||||
func (s *DecoratorStore) Decorator(id uint) (*kolide.Decorator, error) {
|
||||
s.DecoratorFuncInvoked = true
|
||||
return s.DecoratorFunc(id)
|
||||
}
|
||||
|
||||
func (s *DecoratorStore) ListDecorators(opts ...kolide.OptionalArg) ([]*kolide.Decorator, error) {
|
||||
s.ListDecoratorsFuncInvoked = true
|
||||
return s.ListDecoratorsFunc(opts...)
|
||||
}
|
||||
|
||||
func (s *DecoratorStore) SaveDecorator(dec *kolide.Decorator, opts ...kolide.OptionalArg) error {
|
||||
s.SaveDecoratorFuncInvoked = true
|
||||
return s.SaveDecoratorFunc(dec, opts...)
|
||||
}
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/kolide/fleet/server/kolide"
|
||||
)
|
||||
|
||||
type listDecoratorResponse struct {
|
||||
Decorators []*kolide.Decorator `json:"decorators"`
|
||||
Err error `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (r listDecoratorResponse) error() error { return r.Err }
|
||||
|
||||
func makeListDecoratorsEndpoint(svc kolide.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
decs, err := svc.ListDecorators(ctx)
|
||||
if err != nil {
|
||||
return listDecoratorResponse{Err: err}, nil
|
||||
}
|
||||
return listDecoratorResponse{Decorators: decs}, nil
|
||||
}
|
||||
}
|
||||
|
||||
type newDecoratorRequest struct {
|
||||
Payload kolide.DecoratorPayload `json:"payload"`
|
||||
}
|
||||
|
||||
type decoratorResponse struct {
|
||||
Decorator *kolide.Decorator `json:"decorator,omitempty"`
|
||||
Err error `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (r decoratorResponse) error() error { return r.Err }
|
||||
|
||||
func makeNewDecoratorEndpoint(svc kolide.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
r := request.(newDecoratorRequest)
|
||||
dec, err := svc.NewDecorator(ctx, r.Payload)
|
||||
if err != nil {
|
||||
return decoratorResponse{Err: err}, nil
|
||||
}
|
||||
return decoratorResponse{Decorator: dec}, nil
|
||||
}
|
||||
}
|
||||
|
||||
type deleteDecoratorRequest struct {
|
||||
ID uint
|
||||
}
|
||||
|
||||
type deleteDecoratorResponse struct {
|
||||
Err error `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (r deleteDecoratorResponse) error() error { return r.Err }
|
||||
|
||||
func makeDeleteDecoratorEndpoint(svc kolide.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
r := request.(deleteDecoratorRequest)
|
||||
err := svc.DeleteDecorator(ctx, r.ID)
|
||||
|
||||
if err != nil {
|
||||
return deleteDecoratorResponse{Err: err}, nil
|
||||
}
|
||||
return deleteDecoratorResponse{}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func makeModifyDecoratorEndpoint(svc kolide.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
r := request.(newDecoratorRequest)
|
||||
dec, err := svc.ModifyDecorator(ctx, r.Payload)
|
||||
if err != nil {
|
||||
return decoratorResponse{Err: err}, nil
|
||||
}
|
||||
return decoratorResponse{Decorator: dec}, nil
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,203 +0,0 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/kolide/fleet/server/kolide"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupDecoratorTest(r *testResource) {
|
||||
decs := []kolide.Decorator{
|
||||
kolide.Decorator{
|
||||
Type: kolide.DecoratorLoad,
|
||||
Query: "select something from foo;",
|
||||
},
|
||||
kolide.Decorator{
|
||||
Type: kolide.DecoratorLoad,
|
||||
Query: "select bar from foo;",
|
||||
},
|
||||
kolide.Decorator{
|
||||
Type: kolide.DecoratorAlways,
|
||||
Query: "select x from y;",
|
||||
},
|
||||
kolide.Decorator{
|
||||
Type: kolide.DecoratorInterval,
|
||||
Query: "select name from system_info;",
|
||||
Interval: 3600,
|
||||
},
|
||||
}
|
||||
for _, d := range decs {
|
||||
r.ds.NewDecorator(&d)
|
||||
}
|
||||
}
|
||||
|
||||
func testModifyDecorator(t *testing.T, r *testResource) {
|
||||
dec := &kolide.Decorator{
|
||||
Name: "foo",
|
||||
Type: kolide.DecoratorLoad,
|
||||
Query: "select foo from bar;",
|
||||
}
|
||||
dec, err := r.ds.NewDecorator(dec)
|
||||
require.Nil(t, err)
|
||||
buffer := bytes.NewBufferString(`{
|
||||
"payload": {
|
||||
"type": "always",
|
||||
"name": "bar",
|
||||
"query": "select baz from boom;"
|
||||
}
|
||||
}`)
|
||||
req, err := http.NewRequest("PATCH", r.server.URL+fmt.Sprintf("/api/v1/kolide/decorators/%d", dec.ID), buffer)
|
||||
require.Nil(t, err)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", r.adminToken))
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
require.Nil(t, err)
|
||||
|
||||
var decResp decoratorResponse
|
||||
err = json.NewDecoder(resp.Body).Decode(&decResp)
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, decResp.Decorator)
|
||||
assert.Equal(t, "select baz from boom;", decResp.Decorator.Query)
|
||||
assert.Equal(t, kolide.DecoratorAlways, decResp.Decorator.Type)
|
||||
assert.Equal(t, "bar", decResp.Decorator.Name)
|
||||
}
|
||||
|
||||
// This test verifies that we can submit the same payload twice without
|
||||
// raising an error
|
||||
func testModifyDecoratorNoChanges(t *testing.T, r *testResource) {
|
||||
dec := &kolide.Decorator{
|
||||
Type: kolide.DecoratorLoad,
|
||||
Query: "select foo from bar;",
|
||||
}
|
||||
dec, err := r.ds.NewDecorator(dec)
|
||||
require.Nil(t, err)
|
||||
buffer := bytes.NewBufferString(`{
|
||||
"payload": {
|
||||
"type": "load",
|
||||
"query": "select foo from bar;"
|
||||
}
|
||||
}`)
|
||||
req, err := http.NewRequest("PATCH", r.server.URL+fmt.Sprintf("/api/v1/kolide/decorators/%d", dec.ID), buffer)
|
||||
require.Nil(t, err)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", r.adminToken))
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
require.Nil(t, err)
|
||||
|
||||
var decResp decoratorResponse
|
||||
err = json.NewDecoder(resp.Body).Decode(&decResp)
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, decResp.Decorator)
|
||||
assert.Equal(t, "select foo from bar;", decResp.Decorator.Query)
|
||||
assert.Equal(t, kolide.DecoratorLoad, decResp.Decorator.Type)
|
||||
}
|
||||
|
||||
func testListDecorator(t *testing.T, r *testResource) {
|
||||
setupDecoratorTest(r)
|
||||
req, err := http.NewRequest("GET", r.server.URL+"/api/v1/kolide/decorators", nil)
|
||||
require.Nil(t, err)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", r.adminToken))
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
|
||||
var decs listDecoratorResponse
|
||||
err = json.NewDecoder(resp.Body).Decode(&decs)
|
||||
require.Nil(t, err)
|
||||
|
||||
assert.Len(t, decs.Decorators, 4)
|
||||
}
|
||||
|
||||
func testNewDecorator(t *testing.T, r *testResource) {
|
||||
buffer := bytes.NewBufferString(
|
||||
`{
|
||||
"payload": {
|
||||
"type": "load",
|
||||
"query": "select x from y;"
|
||||
}
|
||||
}`)
|
||||
req, err := http.NewRequest("POST", r.server.URL+"/api/v1/kolide/decorators", buffer)
|
||||
require.Nil(t, err)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", r.adminToken))
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
|
||||
var dec decoratorResponse
|
||||
err = json.NewDecoder(resp.Body).Decode(&dec)
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, dec.Decorator)
|
||||
assert.Equal(t, kolide.DecoratorLoad, dec.Decorator.Type)
|
||||
assert.Equal(t, "select x from y;", dec.Decorator.Query)
|
||||
}
|
||||
|
||||
// invalid json
|
||||
func testNewDecoratorFailType(t *testing.T, r *testResource) {
|
||||
buffer := bytes.NewBufferString(
|
||||
`{
|
||||
"payload": {
|
||||
"type": "zip",
|
||||
"query": "select x from y;"
|
||||
}
|
||||
}`)
|
||||
|
||||
req, err := http.NewRequest("POST", r.server.URL+"/api/v1/kolide/decorators", buffer)
|
||||
require.Nil(t, err)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", r.adminToken))
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, http.StatusUnprocessableEntity, resp.StatusCode)
|
||||
|
||||
var errStruct mockValidationError
|
||||
err = json.NewDecoder(resp.Body).Decode(&errStruct)
|
||||
require.Nil(t, err)
|
||||
require.Len(t, errStruct.Errors, 1)
|
||||
assert.Equal(t, "invalid value, must be load, always, or interval", errStruct.Errors[0].Reason)
|
||||
}
|
||||
|
||||
func testNewDecoratorFailValidation(t *testing.T, r *testResource) {
|
||||
buffer := bytes.NewBufferString(
|
||||
`{
|
||||
"payload": {
|
||||
"type": "interval",
|
||||
"query": "select x from y;",
|
||||
"interval": 3601
|
||||
}
|
||||
}`)
|
||||
|
||||
req, err := http.NewRequest("POST", r.server.URL+"/api/v1/kolide/decorators", buffer)
|
||||
require.Nil(t, err)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", r.adminToken))
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, http.StatusUnprocessableEntity, resp.StatusCode)
|
||||
|
||||
var errStruct mockValidationError
|
||||
err = json.NewDecoder(resp.Body).Decode(&errStruct)
|
||||
require.Nil(t, err)
|
||||
require.Len(t, errStruct.Errors, 1)
|
||||
assert.Equal(t, "must be divisible by 60", errStruct.Errors[0].Reason)
|
||||
}
|
||||
|
||||
func testDeleteDecorator(t *testing.T, r *testResource) {
|
||||
setupDecoratorTest(r)
|
||||
req, err := http.NewRequest("DELETE", r.server.URL+"/api/v1/kolide/decorators/1", nil)
|
||||
require.Nil(t, err)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", r.adminToken))
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
decs, _ := r.ds.ListDecorators()
|
||||
assert.Len(t, decs, 3)
|
||||
}
|
||||
|
|
@ -111,13 +111,6 @@ var testFunctions = [...]func(*testing.T, *testResource){
|
|||
testNonAdminUserSetAdmin,
|
||||
testAdminUserSetEnabled,
|
||||
testNonAdminUserSetEnabled,
|
||||
testModifyDecorator,
|
||||
testListDecorator,
|
||||
testNewDecorator,
|
||||
testNewDecoratorFailType,
|
||||
testNewDecoratorFailValidation,
|
||||
testDeleteDecorator,
|
||||
testModifyDecoratorNoChanges,
|
||||
}
|
||||
|
||||
func TestEndpoints(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -58,10 +58,6 @@ type KolideEndpoints struct {
|
|||
GetLabel endpoint.Endpoint
|
||||
ListLabels endpoint.Endpoint
|
||||
DeleteLabel endpoint.Endpoint
|
||||
ListDecorators endpoint.Endpoint
|
||||
NewDecorator endpoint.Endpoint
|
||||
ModifyDecorator endpoint.Endpoint
|
||||
DeleteDecorator endpoint.Endpoint
|
||||
GetHost endpoint.Endpoint
|
||||
DeleteHost endpoint.Endpoint
|
||||
ListHosts endpoint.Endpoint
|
||||
|
|
@ -137,10 +133,6 @@ func MakeKolideServerEndpoints(svc kolide.Service, jwtKey string) KolideEndpoint
|
|||
GetLabel: authenticatedUser(jwtKey, svc, makeGetLabelEndpoint(svc)),
|
||||
ListLabels: authenticatedUser(jwtKey, svc, makeListLabelsEndpoint(svc)),
|
||||
DeleteLabel: authenticatedUser(jwtKey, svc, makeDeleteLabelEndpoint(svc)),
|
||||
ListDecorators: authenticatedUser(jwtKey, svc, makeListDecoratorsEndpoint(svc)),
|
||||
NewDecorator: authenticatedUser(jwtKey, svc, makeNewDecoratorEndpoint(svc)),
|
||||
ModifyDecorator: authenticatedUser(jwtKey, svc, makeModifyDecoratorEndpoint(svc)),
|
||||
DeleteDecorator: authenticatedUser(jwtKey, svc, makeDeleteDecoratorEndpoint(svc)),
|
||||
SearchTargets: authenticatedUser(jwtKey, svc, makeSearchTargetsEndpoint(svc)),
|
||||
GetOptions: authenticatedUser(jwtKey, svc, mustBeAdmin(makeGetOptionsEndpoint(svc))),
|
||||
ModifyOptions: authenticatedUser(jwtKey, svc, mustBeAdmin(makeModifyOptionsEndpoint(svc))),
|
||||
|
|
@ -203,10 +195,6 @@ type kolideHandlers struct {
|
|||
GetLabel http.Handler
|
||||
ListLabels http.Handler
|
||||
DeleteLabel http.Handler
|
||||
ListDecorators http.Handler
|
||||
NewDecorator http.Handler
|
||||
ModifyDecorator http.Handler
|
||||
DeleteDecorator http.Handler
|
||||
GetHost http.Handler
|
||||
DeleteHost http.Handler
|
||||
ListHosts http.Handler
|
||||
|
|
@ -272,10 +260,6 @@ func makeKolideKitHandlers(e KolideEndpoints, opts []kithttp.ServerOption) *koli
|
|||
GetLabel: newServer(e.GetLabel, decodeGetLabelRequest),
|
||||
ListLabels: newServer(e.ListLabels, decodeListLabelsRequest),
|
||||
DeleteLabel: newServer(e.DeleteLabel, decodeDeleteLabelRequest),
|
||||
ListDecorators: newServer(e.ListDecorators, decodeNoParamsRequest),
|
||||
NewDecorator: newServer(e.NewDecorator, decodeNewDecoratorRequest),
|
||||
ModifyDecorator: newServer(e.ModifyDecorator, decodeModifyDecoratorRequest),
|
||||
DeleteDecorator: newServer(e.DeleteDecorator, decodeDeleteDecoratorRequest),
|
||||
GetHost: newServer(e.GetHost, decodeGetHostRequest),
|
||||
DeleteHost: newServer(e.DeleteHost, decodeDeleteHostRequest),
|
||||
ListHosts: newServer(e.ListHosts, decodeListHostsRequest),
|
||||
|
|
@ -382,11 +366,6 @@ func attachKolideAPIRoutes(r *mux.Router, h *kolideHandlers) {
|
|||
r.Handle("/api/v1/kolide/labels", h.ListLabels).Methods("GET").Name("list_labels")
|
||||
r.Handle("/api/v1/kolide/labels/{id}", h.DeleteLabel).Methods("DELETE").Name("delete_label")
|
||||
|
||||
r.Handle("/api/v1/kolide/decorators", h.ListDecorators).Methods("GET").Name("list_decorators")
|
||||
r.Handle("/api/v1/kolide/decorators", h.NewDecorator).Methods("POST").Name("create_decorator")
|
||||
r.Handle("/api/v1/kolide/decorators/{id}", h.ModifyDecorator).Methods("PATCH").Name("modify_decorator")
|
||||
r.Handle("/api/v1/kolide/decorators/{id}", h.DeleteDecorator).Methods("DELETE").Name("delete_decorator")
|
||||
|
||||
r.Handle("/api/v1/kolide/hosts", h.ListHosts).Methods("GET").Name("list_hosts")
|
||||
r.Handle("/api/v1/kolide/host_summary", h.GetHostSummary).Methods("GET").Name("get_host_summary")
|
||||
r.Handle("/api/v1/kolide/hosts/{id}", h.GetHost).Methods("GET").Name("get_host")
|
||||
|
|
|
|||
|
|
@ -1,69 +0,0 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/kolide/fleet/server/kolide"
|
||||
)
|
||||
|
||||
func (mw loggingMiddleware) ListDecorators(ctx context.Context) ([]*kolide.Decorator, error) {
|
||||
var (
|
||||
decs []*kolide.Decorator
|
||||
err error
|
||||
)
|
||||
defer func(begin time.Time) {
|
||||
mw.logger.Log(
|
||||
"method", "ListDecorators",
|
||||
"err", err,
|
||||
"took", time.Since(begin),
|
||||
)
|
||||
}(time.Now())
|
||||
decs, err = mw.Service.ListDecorators(ctx)
|
||||
return decs, err
|
||||
}
|
||||
|
||||
func (mw loggingMiddleware) NewDecorator(ctx context.Context, payload kolide.DecoratorPayload) (*kolide.Decorator, error) {
|
||||
var (
|
||||
dec *kolide.Decorator
|
||||
err error
|
||||
)
|
||||
defer func(begin time.Time) {
|
||||
mw.logger.Log(
|
||||
"method", "NewDecorator",
|
||||
"err", err,
|
||||
"took", time.Since(begin),
|
||||
)
|
||||
}(time.Now())
|
||||
dec, err = mw.Service.NewDecorator(ctx, payload)
|
||||
return dec, err
|
||||
}
|
||||
|
||||
func (mw loggingMiddleware) ModifyDecorator(ctx context.Context, payload kolide.DecoratorPayload) (*kolide.Decorator, error) {
|
||||
var (
|
||||
dec *kolide.Decorator
|
||||
err error
|
||||
)
|
||||
defer func(begin time.Time) {
|
||||
mw.logger.Log(
|
||||
"method", "ModifyDecorator",
|
||||
"err", err,
|
||||
"took", time.Since(begin),
|
||||
)
|
||||
}(time.Now())
|
||||
dec, err = mw.Service.ModifyDecorator(ctx, payload)
|
||||
return dec, err
|
||||
}
|
||||
|
||||
func (mw loggingMiddleware) DeleteDecorator(ctx context.Context, id uint) error {
|
||||
var err error
|
||||
defer func(begin time.Time) {
|
||||
mw.logger.Log(
|
||||
"method", "DeleteDecorator",
|
||||
"err", err,
|
||||
"took", time.Since(begin),
|
||||
)
|
||||
}(time.Now())
|
||||
err = mw.Service.DeleteDecorator(ctx, id)
|
||||
return err
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/kolide/fleet/server/kolide"
|
||||
)
|
||||
|
||||
func (mw metricsMiddleware) ListDecorators(ctx context.Context) ([]*kolide.Decorator, error) {
|
||||
var (
|
||||
decs []*kolide.Decorator
|
||||
err error
|
||||
)
|
||||
defer func(begin time.Time) {
|
||||
lvs := []string{"method", "ListDecorators", "error", fmt.Sprint(err != nil)}
|
||||
mw.requestCount.With(lvs...).Add(1)
|
||||
mw.requestLatency.With(lvs...).Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
decs, err = mw.Service.ListDecorators(ctx)
|
||||
return decs, err
|
||||
}
|
||||
|
||||
func (mw metricsMiddleware) NewDecorator(ctx context.Context, payload kolide.DecoratorPayload) (*kolide.Decorator, error) {
|
||||
var (
|
||||
dec *kolide.Decorator
|
||||
err error
|
||||
)
|
||||
defer func(begin time.Time) {
|
||||
lvs := []string{"method", "NewDecorator", "error", fmt.Sprint(err != nil)}
|
||||
mw.requestCount.With(lvs...).Add(1)
|
||||
mw.requestLatency.With(lvs...).Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
dec, err = mw.Service.NewDecorator(ctx, payload)
|
||||
return dec, err
|
||||
}
|
||||
|
||||
func (mw metricsMiddleware) ModifyDecorator(ctx context.Context, payload kolide.DecoratorPayload) (*kolide.Decorator, error) {
|
||||
var (
|
||||
dec *kolide.Decorator
|
||||
err error
|
||||
)
|
||||
defer func(begin time.Time) {
|
||||
lvs := []string{"method", "ModifyDecorator", "error", fmt.Sprint(err != nil)}
|
||||
mw.requestCount.With(lvs...).Add(1)
|
||||
mw.requestLatency.With(lvs...).Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
dec, err = mw.Service.ModifyDecorator(ctx, payload)
|
||||
return dec, err
|
||||
}
|
||||
|
||||
func (mw metricsMiddleware) DeleteDecorator(ctx context.Context, id uint) error {
|
||||
var err error
|
||||
defer func(begin time.Time) {
|
||||
lvs := []string{"method", "DeleteDecorator", "error", fmt.Sprint(err != nil)}
|
||||
mw.requestCount.With(lvs...).Add(1)
|
||||
mw.requestLatency.With(lvs...).Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
err = mw.Service.DeleteDecorator(ctx, id)
|
||||
return err
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/kolide/fleet/server/kolide"
|
||||
)
|
||||
|
||||
func (svc service) ListDecorators(ctx context.Context) ([]*kolide.Decorator, error) {
|
||||
return svc.ds.ListDecorators()
|
||||
}
|
||||
|
||||
func (svc service) DeleteDecorator(ctx context.Context, uid uint) error {
|
||||
return svc.ds.DeleteDecorator(uid)
|
||||
}
|
||||
|
||||
func (svc service) NewDecorator(ctx context.Context, payload kolide.DecoratorPayload) (*kolide.Decorator, error) {
|
||||
var dec kolide.Decorator
|
||||
if payload.Name != nil {
|
||||
dec.Name = *payload.Name
|
||||
}
|
||||
dec.Query = *payload.Query
|
||||
dec.Type = *payload.DecoratorType
|
||||
if payload.Interval != nil {
|
||||
dec.Interval = *payload.Interval
|
||||
}
|
||||
return svc.ds.NewDecorator(&dec)
|
||||
}
|
||||
|
||||
func (svc service) ModifyDecorator(ctx context.Context, payload kolide.DecoratorPayload) (*kolide.Decorator, error) {
|
||||
dec, err := svc.ds.Decorator(payload.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if payload.Name != nil {
|
||||
dec.Name = *payload.Name
|
||||
}
|
||||
if payload.DecoratorType != nil {
|
||||
dec.Type = *payload.DecoratorType
|
||||
}
|
||||
if payload.Query != nil {
|
||||
dec.Query = *payload.Query
|
||||
}
|
||||
if payload.Interval != nil {
|
||||
dec.Interval = *payload.Interval
|
||||
}
|
||||
err = svc.ds.SaveDecorator(dec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dec, nil
|
||||
}
|
||||
|
|
@ -106,36 +106,6 @@ func (svc service) GetClientConfig(ctx context.Context) (map[string]interface{},
|
|||
return nil, osqueryError{message: "internal error: parsing base configuration: " + err.Error()}
|
||||
}
|
||||
|
||||
decorators, err := svc.ds.ListDecorators()
|
||||
if err != nil {
|
||||
return nil, osqueryError{message: "internal error: unable to fetch decorators: " + err.Error()}
|
||||
}
|
||||
decConfig := kolide.Decorators{
|
||||
Interval: make(map[string][]string),
|
||||
}
|
||||
for _, dec := range decorators {
|
||||
switch dec.Type {
|
||||
case kolide.DecoratorLoad:
|
||||
decConfig.Load = append(decConfig.Load, dec.Query)
|
||||
case kolide.DecoratorAlways:
|
||||
decConfig.Always = append(decConfig.Always, dec.Query)
|
||||
case kolide.DecoratorInterval:
|
||||
key := strconv.Itoa(int(dec.Interval))
|
||||
decConfig.Interval[key] = append(decConfig.Interval[key], dec.Query)
|
||||
default:
|
||||
svc.logger.Log("component", "service", "method", "GetClientConfig", "err",
|
||||
"unknown decorator type")
|
||||
}
|
||||
}
|
||||
|
||||
if len(decConfig.Interval) > 0 || len(decConfig.Always) > 0 || len(decConfig.Load) > 0 {
|
||||
decJSON, err := json.Marshal(decConfig)
|
||||
if err != nil {
|
||||
return nil, osqueryError{message: "internal error: marshal decorator JSON: " + err.Error()}
|
||||
}
|
||||
config["decorators"] = json.RawMessage(decJSON)
|
||||
}
|
||||
|
||||
packs, err := svc.ds.ListPacksForHost(host.ID)
|
||||
if err != nil {
|
||||
return nil, osqueryError{message: "database error: " + err.Error()}
|
||||
|
|
|
|||
|
|
@ -326,9 +326,6 @@ func TestLabelQueries(t *testing.T) {
|
|||
|
||||
func TestGetClientConfig(t *testing.T) {
|
||||
ds := new(mock.Store)
|
||||
ds.ListDecoratorsFunc = func(opt ...kolide.OptionalArg) ([]*kolide.Decorator, error) {
|
||||
return []*kolide.Decorator{}, nil
|
||||
}
|
||||
ds.ListPacksForHostFunc = func(hid uint) ([]*kolide.Pack, error) {
|
||||
return []*kolide.Pack{}, nil
|
||||
}
|
||||
|
|
@ -351,10 +348,29 @@ func TestGetClientConfig(t *testing.T) {
|
|||
}
|
||||
}
|
||||
ds.OptionsForPlatformFunc = func(platform string) (json.RawMessage, error) {
|
||||
return json.RawMessage(`{"options":{
|
||||
"distributed_interval": 11,
|
||||
"logger_tls_period": 33
|
||||
}}`), nil
|
||||
return json.RawMessage(`
|
||||
{
|
||||
"options":{
|
||||
"distributed_interval":11,
|
||||
"logger_tls_period":33
|
||||
},
|
||||
"decorators":{
|
||||
"load":[
|
||||
"SELECT version FROM osquery_info;",
|
||||
"SELECT uuid AS host_uuid FROM system_info;"
|
||||
],
|
||||
"always":[
|
||||
"SELECT user AS username FROM logged_in_users WHERE user <> '' ORDER BY time LIMIT 1;"
|
||||
],
|
||||
"interval":{
|
||||
"3600":[
|
||||
"SELECT total_seconds AS uptime FROM uptime;"
|
||||
]
|
||||
}
|
||||
},
|
||||
"foo": "bar"
|
||||
}
|
||||
`), nil
|
||||
}
|
||||
ds.SaveHostFunc = func(host *kolide.Host) error {
|
||||
return nil
|
||||
|
|
@ -371,14 +387,33 @@ func TestGetClientConfig(t *testing.T) {
|
|||
"logger_tls_period": float64(33),
|
||||
}
|
||||
|
||||
expectedDecorators := map[string]interface{}{
|
||||
"load": []interface{}{
|
||||
"SELECT version FROM osquery_info;",
|
||||
"SELECT uuid AS host_uuid FROM system_info;",
|
||||
},
|
||||
"always": []interface{}{
|
||||
"SELECT user AS username FROM logged_in_users WHERE user <> '' ORDER BY time LIMIT 1;",
|
||||
},
|
||||
"interval": map[string]interface{}{
|
||||
"3600": []interface{}{"SELECT total_seconds AS uptime FROM uptime;"},
|
||||
},
|
||||
}
|
||||
|
||||
expectedConfig := map[string]interface{}{
|
||||
"options": expectedOptions,
|
||||
"decorators": expectedDecorators,
|
||||
"foo": "bar",
|
||||
}
|
||||
|
||||
// No packs loaded yet
|
||||
conf, err := svc.GetClientConfig(ctx1)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, map[string]interface{}{"options": expectedOptions}, conf)
|
||||
assert.Equal(t, expectedConfig, conf)
|
||||
|
||||
conf, err = svc.GetClientConfig(ctx2)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, map[string]interface{}{"options": expectedOptions}, conf)
|
||||
assert.Equal(t, expectedConfig, conf)
|
||||
|
||||
// Now add packs
|
||||
ds.ListPacksForHostFunc = func(hid uint) ([]*kolide.Pack, error) {
|
||||
|
|
@ -977,9 +1012,6 @@ func TestUpdateHostIntervals(t *testing.T) {
|
|||
svc, err := newTestService(ds, nil)
|
||||
require.Nil(t, err)
|
||||
|
||||
ds.ListDecoratorsFunc = func(opt ...kolide.OptionalArg) ([]*kolide.Decorator, error) {
|
||||
return []*kolide.Decorator{}, nil
|
||||
}
|
||||
ds.ListPacksForHostFunc = func(hid uint) ([]*kolide.Pack, error) {
|
||||
return []*kolide.Pack{}, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func decodeNewDecoratorRequest(ctx context.Context, req *http.Request) (interface{}, error) {
|
||||
var dec newDecoratorRequest
|
||||
err := json.NewDecoder(req.Body).Decode(&dec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dec, nil
|
||||
}
|
||||
|
||||
func decodeDeleteDecoratorRequest(ctx context.Context, req *http.Request) (interface{}, error) {
|
||||
id, err := idFromRequest(req, "id")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return deleteDecoratorRequest{ID: id}, nil
|
||||
}
|
||||
|
||||
func decodeModifyDecoratorRequest(ctx context.Context, req *http.Request) (interface{}, error) {
|
||||
var request newDecoratorRequest
|
||||
id, err := idFromRequest(req, "id")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.NewDecoder(req.Body).Decode(&request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
request.Payload.ID = id
|
||||
return request, nil
|
||||
}
|
||||
|
|
@ -1,104 +0,0 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/kolide/fleet/server/kolide"
|
||||
)
|
||||
|
||||
func validateNewDecoratorType(payload kolide.DecoratorPayload, invalid *invalidArgumentError) {
|
||||
if payload.DecoratorType == nil {
|
||||
invalid.Append("type", "missing required argument")
|
||||
return
|
||||
}
|
||||
if *payload.DecoratorType == kolide.DecoratorUndefined {
|
||||
invalid.Append("type", "invalid value, must be load, always, or interval")
|
||||
return
|
||||
}
|
||||
if *payload.DecoratorType == kolide.DecoratorInterval {
|
||||
if payload.Interval == nil {
|
||||
invalid.Append("interval", "missing required argument")
|
||||
return
|
||||
}
|
||||
if *payload.Interval%60 != 0 {
|
||||
invalid.Append("interval", "must be divisible by 60")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NewDecorator validator checks to make sure that a valid decorator type exists and
|
||||
// if the decorator is of an interval type, an interval value is present and is
|
||||
// divisable by 60
|
||||
// See https://osquery.readthedocs.io/en/stable/deployment/configuration/
|
||||
func (mw validationMiddleware) NewDecorator(ctx context.Context, payload kolide.DecoratorPayload) (*kolide.Decorator, error) {
|
||||
invalid := &invalidArgumentError{}
|
||||
validateNewDecoratorType(payload, invalid)
|
||||
|
||||
if payload.Query == nil {
|
||||
invalid.Append("query", "missing required argument")
|
||||
}
|
||||
|
||||
if invalid.HasErrors() {
|
||||
return nil, invalid
|
||||
}
|
||||
return mw.Service.NewDecorator(ctx, payload)
|
||||
}
|
||||
|
||||
func (mw validationMiddleware) validateModifyDecoratorType(payload kolide.DecoratorPayload, invalid *invalidArgumentError) error {
|
||||
if payload.DecoratorType != nil {
|
||||
|
||||
if *payload.DecoratorType == kolide.DecoratorUndefined {
|
||||
invalid.Append("type", "invalid value, must be load, always, or interval")
|
||||
return nil
|
||||
}
|
||||
if *payload.DecoratorType == kolide.DecoratorInterval {
|
||||
// special processing for interval type
|
||||
existingDec, err := mw.ds.Decorator(payload.ID)
|
||||
if err != nil {
|
||||
// if decorator is not present we want to return a 404 to the client
|
||||
return err
|
||||
}
|
||||
// if the type has changed from always or load to interval we need to
|
||||
// check suitability of interval value
|
||||
if existingDec.Type != kolide.DecoratorInterval {
|
||||
if payload.Interval == nil {
|
||||
invalid.Append("interval", "missing required argument")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if payload.Interval != nil {
|
||||
if *payload.Interval%60 != 0 {
|
||||
invalid.Append("interval", "value must be divisible by 60")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mw validationMiddleware) ModifyDecorator(ctx context.Context, payload kolide.DecoratorPayload) (*kolide.Decorator, error) {
|
||||
invalid := &invalidArgumentError{}
|
||||
err := mw.validateModifyDecoratorType(payload, invalid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if invalid.HasErrors() {
|
||||
return nil, invalid
|
||||
}
|
||||
return mw.Service.ModifyDecorator(ctx, payload)
|
||||
}
|
||||
|
||||
func (mw validationMiddleware) DeleteDecorator(ctx context.Context, id uint) error {
|
||||
invalid := &invalidArgumentError{}
|
||||
dec, err := mw.ds.Decorator(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dec.BuiltIn {
|
||||
invalid.Append("decorator", "read only")
|
||||
return invalid
|
||||
}
|
||||
return mw.Service.DeleteDecorator(ctx, id)
|
||||
}
|
||||
|
|
@ -1,143 +0,0 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/kolide/fleet/server/kolide"
|
||||
"github.com/kolide/fleet/server/mock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var dtPtr = func(t kolide.DecoratorType) *kolide.DecoratorType { return &t }
|
||||
|
||||
func TestDecoratorValidation(t *testing.T) {
|
||||
ds := mock.Store{}
|
||||
ds.DecoratorFunc = func(id uint) (*kolide.Decorator, error) {
|
||||
return &kolide.Decorator{
|
||||
ID: 1,
|
||||
Query: "select x from y;",
|
||||
Type: kolide.DecoratorAlways,
|
||||
}, nil
|
||||
}
|
||||
ds.SaveDecoratorFunc = func(dec *kolide.Decorator, opts ...kolide.OptionalArg) error {
|
||||
return nil
|
||||
}
|
||||
svc := &service{
|
||||
ds: &ds,
|
||||
}
|
||||
validator := validationMiddleware{
|
||||
Service: svc,
|
||||
ds: &ds,
|
||||
}
|
||||
|
||||
payload := kolide.DecoratorPayload{
|
||||
ID: uint(1),
|
||||
DecoratorType: dtPtr(kolide.DecoratorInterval),
|
||||
Interval: uintPtr(3600),
|
||||
}
|
||||
|
||||
dec, err := validator.ModifyDecorator(context.Background(), payload)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, kolide.DecoratorInterval, dec.Type)
|
||||
assert.Equal(t, uint(3600), dec.Interval)
|
||||
}
|
||||
|
||||
func TestDecoratorValidationIntervalMissing(t *testing.T) {
|
||||
ds := mock.Store{}
|
||||
ds.DecoratorFunc = func(id uint) (*kolide.Decorator, error) {
|
||||
return &kolide.Decorator{
|
||||
ID: 1,
|
||||
Query: "select x from y;",
|
||||
Type: kolide.DecoratorAlways,
|
||||
}, nil
|
||||
}
|
||||
ds.SaveDecoratorFunc = func(dec *kolide.Decorator, opts ...kolide.OptionalArg) error {
|
||||
return nil
|
||||
}
|
||||
svc := &service{
|
||||
ds: &ds,
|
||||
}
|
||||
validator := validationMiddleware{
|
||||
Service: svc,
|
||||
ds: &ds,
|
||||
}
|
||||
|
||||
payload := kolide.DecoratorPayload{
|
||||
ID: uint(1),
|
||||
DecoratorType: dtPtr(kolide.DecoratorInterval),
|
||||
}
|
||||
|
||||
_, err := validator.ModifyDecorator(context.Background(), payload)
|
||||
require.NotNil(t, err)
|
||||
r, ok := err.(*invalidArgumentError)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "missing required argument", (*r)[0].reason)
|
||||
}
|
||||
|
||||
func TestDecoratorValidationIntervalSameType(t *testing.T) {
|
||||
ds := mock.Store{}
|
||||
ds.DecoratorFunc = func(id uint) (*kolide.Decorator, error) {
|
||||
return &kolide.Decorator{
|
||||
ID: 1,
|
||||
Query: "select x from y;",
|
||||
Type: kolide.DecoratorInterval,
|
||||
Interval: 600,
|
||||
}, nil
|
||||
}
|
||||
ds.SaveDecoratorFunc = func(dec *kolide.Decorator, opts ...kolide.OptionalArg) error {
|
||||
return nil
|
||||
}
|
||||
svc := &service{
|
||||
ds: &ds,
|
||||
}
|
||||
validator := validationMiddleware{
|
||||
Service: svc,
|
||||
ds: &ds,
|
||||
}
|
||||
|
||||
payload := kolide.DecoratorPayload{
|
||||
ID: uint(1),
|
||||
DecoratorType: dtPtr(kolide.DecoratorInterval),
|
||||
Interval: uintPtr(1200),
|
||||
}
|
||||
|
||||
dec, err := validator.ModifyDecorator(context.Background(), payload)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, uint(1200), dec.Interval)
|
||||
}
|
||||
|
||||
func TestDecoratorValidationIntervalInvalid(t *testing.T) {
|
||||
ds := mock.Store{}
|
||||
ds.DecoratorFunc = func(id uint) (*kolide.Decorator, error) {
|
||||
return &kolide.Decorator{
|
||||
ID: 1,
|
||||
Query: "select x from y;",
|
||||
Type: kolide.DecoratorInterval,
|
||||
Interval: 600,
|
||||
}, nil
|
||||
}
|
||||
ds.SaveDecoratorFunc = func(dec *kolide.Decorator, opts ...kolide.OptionalArg) error {
|
||||
return nil
|
||||
}
|
||||
svc := &service{
|
||||
ds: &ds,
|
||||
}
|
||||
validator := validationMiddleware{
|
||||
Service: svc,
|
||||
ds: &ds,
|
||||
}
|
||||
|
||||
payload := kolide.DecoratorPayload{
|
||||
ID: uint(1),
|
||||
DecoratorType: dtPtr(kolide.DecoratorInterval),
|
||||
Interval: uintPtr(1203),
|
||||
}
|
||||
|
||||
_, err := validator.ModifyDecorator(context.Background(), payload)
|
||||
require.NotNil(t, err)
|
||||
r, ok := err.(*invalidArgumentError)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "value must be divisible by 60", (*r)[0].reason)
|
||||
}
|
||||
Loading…
Reference in a new issue