diff --git a/server/datastore/datastore_decorators_test.go b/server/datastore/datastore_decorators_test.go deleted file mode 100644 index d125e748b1..0000000000 --- a/server/datastore/datastore_decorators_test.go +++ /dev/null @@ -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) - -} diff --git a/server/datastore/datastore_test.go b/server/datastore/datastore_test.go index b71f3b42cb..c886564200 100644 --- a/server/datastore/datastore_test.go +++ b/server/datastore/datastore_test.go @@ -59,7 +59,6 @@ var testFunctions = [...]func(*testing.T, kolide.Datastore){ testOptionsToConfig, testGetPackByName, testGetQueryByName, - testDecorators, testFileIntegrityMonitoring, testYARAStore, testAddLabelToPackTwice, diff --git a/server/datastore/mysql/decorators.go b/server/datastore/mysql/decorators.go deleted file mode 100644 index 455b59b077..0000000000 --- a/server/datastore/mysql/decorators.go +++ /dev/null @@ -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 -} diff --git a/server/datastore/mysql/migrations/data/20171212182458_MigrateOsqueryOptions.go b/server/datastore/mysql/migrations/data/20171212182458_MigrateOsqueryOptions.go index fcd267de34..2030b28fb4 100644 --- a/server/datastore/mysql/migrations/data/20171212182458_MigrateOsqueryOptions.go +++ b/server/datastore/mysql/migrations/data/20171212182458_MigrateOsqueryOptions.go @@ -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 { diff --git a/server/kolide/datastore.go b/server/kolide/datastore.go index 1f1652ba1c..0d83a3e5e6 100644 --- a/server/kolide/datastore.go +++ b/server/kolide/datastore.go @@ -15,7 +15,6 @@ type Datastore interface { InviteStore ScheduledQueryStore OptionStore - DecoratorStore FileIntegrityMonitoringStore YARAStore OsqueryOptionsStore diff --git a/server/kolide/decorators.go b/server/kolide/decorators.go index c958cd780c..a664edfbcc 100644 --- a/server/kolide/decorators.go +++ b/server/kolide/decorators.go @@ -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/ diff --git a/server/kolide/service.go b/server/kolide/service.go index 253d8efcac..2245bfe1ea 100644 --- a/server/kolide/service.go +++ b/server/kolide/service.go @@ -15,6 +15,5 @@ type Service interface { TargetService ScheduledQueryService OptionService - DecoratorService FileIntegrityMonitoringService } diff --git a/server/mock/datastore.go b/server/mock/datastore.go index 28c65d6128..0b7e97e317 100644 --- a/server/mock/datastore.go +++ b/server/mock/datastore.go @@ -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 diff --git a/server/mock/datastore_decorators.go b/server/mock/datastore_decorators.go deleted file mode 100644 index deb43e0107..0000000000 --- a/server/mock/datastore_decorators.go +++ /dev/null @@ -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...) -} diff --git a/server/service/endpoint_decorators.go b/server/service/endpoint_decorators.go deleted file mode 100644 index a5d8dad7ce..0000000000 --- a/server/service/endpoint_decorators.go +++ /dev/null @@ -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 - - } -} diff --git a/server/service/endpoint_decorators_test.go b/server/service/endpoint_decorators_test.go deleted file mode 100644 index ab156f5ab8..0000000000 --- a/server/service/endpoint_decorators_test.go +++ /dev/null @@ -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) -} diff --git a/server/service/endpoint_test.go b/server/service/endpoint_test.go index 9653521c46..7784c9756a 100644 --- a/server/service/endpoint_test.go +++ b/server/service/endpoint_test.go @@ -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) { diff --git a/server/service/handler.go b/server/service/handler.go index 8aea2aec70..16ad62282c 100644 --- a/server/service/handler.go +++ b/server/service/handler.go @@ -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") diff --git a/server/service/logging_decorators.go b/server/service/logging_decorators.go deleted file mode 100644 index bd370a0ce1..0000000000 --- a/server/service/logging_decorators.go +++ /dev/null @@ -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 -} diff --git a/server/service/metrics_decorators.go b/server/service/metrics_decorators.go deleted file mode 100644 index 60b44ffafd..0000000000 --- a/server/service/metrics_decorators.go +++ /dev/null @@ -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 -} diff --git a/server/service/service_decorators.go b/server/service/service_decorators.go deleted file mode 100644 index 403970649f..0000000000 --- a/server/service/service_decorators.go +++ /dev/null @@ -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 -} diff --git a/server/service/service_osquery.go b/server/service/service_osquery.go index 4b773907e6..d8cf5c41e2 100644 --- a/server/service/service_osquery.go +++ b/server/service/service_osquery.go @@ -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()} diff --git a/server/service/service_osquery_test.go b/server/service/service_osquery_test.go index d465b2c11f..a260f2b4f8 100644 --- a/server/service/service_osquery_test.go +++ b/server/service/service_osquery_test.go @@ -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 } diff --git a/server/service/transport_decorators.go b/server/service/transport_decorators.go deleted file mode 100644 index 8672edb857..0000000000 --- a/server/service/transport_decorators.go +++ /dev/null @@ -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 -} diff --git a/server/service/validation_decorators.go b/server/service/validation_decorators.go deleted file mode 100644 index 70bb11909a..0000000000 --- a/server/service/validation_decorators.go +++ /dev/null @@ -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) -} diff --git a/server/service/validation_decorators_test.go b/server/service/validation_decorators_test.go deleted file mode 100644 index b1e09361fa..0000000000 --- a/server/service/validation_decorators_test.go +++ /dev/null @@ -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) -}