mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 08:55:24 +00:00
HTTP Controller updates for label queries (#96)
Add controller methods for: * Retrieving label queries * Storing results of label queries
This commit is contained in:
parent
3f81dda638
commit
41fe404ef1
@ -202,6 +202,13 @@ func (orm gormDB) AuthenticateHost(nodeKey string) (*kolide.Host, error) {
|
||||
return &host, nil
|
||||
}
|
||||
|
||||
func (orm gormDB) SaveHost(host *kolide.Host) error {
|
||||
if err := orm.DB.Save(host).Error; err != nil {
|
||||
return errors.DatabaseError(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (orm gormDB) MarkHostSeen(host *kolide.Host, t time.Time) error {
|
||||
updateTime := time.Now()
|
||||
err := orm.DB.Exec("UPDATE hosts SET updated_at=? WHERE node_key=?", t, host.NodeKey).Error
|
||||
|
8
glide.lock
generated
8
glide.lock
generated
@ -1,5 +1,5 @@
|
||||
hash: 4ffa52b45a47295720fcf4dfcc0cc85c9f47a35a3bd43bdc6e0c2355bdd76109
|
||||
updated: 2016-08-17T10:01:54.299187864-07:00
|
||||
hash: 082be2432c000297d8d76bd1dc0f2be4e98954d0f49682603469e1b3056790f5
|
||||
updated: 2016-08-24T14:33:02.991120417-07:00
|
||||
imports:
|
||||
- name: github.com/alecthomas/template
|
||||
version: a0175ee3bccc567396460bf5acd36800cb10c49c
|
||||
@ -32,6 +32,10 @@ imports:
|
||||
- render
|
||||
- name: github.com/go-sql-driver/mysql
|
||||
version: 3654d25ec346ee8ce71a68431025458d52a38ac0
|
||||
- name: github.com/golang/mock
|
||||
version: bd3c8e81be01eef76d4b503f5e687d2d1354d2d9
|
||||
subpackages:
|
||||
- gomock
|
||||
- name: github.com/golang/protobuf
|
||||
version: c3cefd437628a0b7d31b34fe44b3a7a540e98527
|
||||
subpackages:
|
||||
|
@ -76,3 +76,4 @@ import:
|
||||
- package: github.com/spf13/viper
|
||||
- package: gopkg.in/natefinch/lumberjack.v2
|
||||
version: v2.0
|
||||
- package: github.com/golang/mock
|
||||
|
238
kolide/mock_osquery.go
Normal file
238
kolide/mock_osquery.go
Normal file
@ -0,0 +1,238 @@
|
||||
// Automatically generated by MockGen. DO NOT EDIT!
|
||||
// Source: kolide/osquery.go
|
||||
|
||||
package kolide
|
||||
|
||||
import (
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
time "time"
|
||||
)
|
||||
|
||||
// Mock of OsqueryStore interface
|
||||
type MockOsqueryStore struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *_MockOsqueryStoreRecorder
|
||||
}
|
||||
|
||||
// Recorder for MockOsqueryStore (not exported)
|
||||
type _MockOsqueryStoreRecorder struct {
|
||||
mock *MockOsqueryStore
|
||||
}
|
||||
|
||||
func NewMockOsqueryStore(ctrl *gomock.Controller) *MockOsqueryStore {
|
||||
mock := &MockOsqueryStore{ctrl: ctrl}
|
||||
mock.recorder = &_MockOsqueryStoreRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
func (_m *MockOsqueryStore) EXPECT() *_MockOsqueryStoreRecorder {
|
||||
return _m.recorder
|
||||
}
|
||||
|
||||
func (_m *MockOsqueryStore) EnrollHost(uuid string, hostname string, ip string, platform string, nodeKeySize int) (*Host, error) {
|
||||
ret := _m.ctrl.Call(_m, "EnrollHost", uuid, hostname, ip, platform, nodeKeySize)
|
||||
ret0, _ := ret[0].(*Host)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (_mr *_MockOsqueryStoreRecorder) EnrollHost(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "EnrollHost", arg0, arg1, arg2, arg3, arg4)
|
||||
}
|
||||
|
||||
func (_m *MockOsqueryStore) AuthenticateHost(nodeKey string) (*Host, error) {
|
||||
ret := _m.ctrl.Call(_m, "AuthenticateHost", nodeKey)
|
||||
ret0, _ := ret[0].(*Host)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (_mr *_MockOsqueryStoreRecorder) AuthenticateHost(arg0 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "AuthenticateHost", arg0)
|
||||
}
|
||||
|
||||
func (_m *MockOsqueryStore) SaveHost(host *Host) error {
|
||||
ret := _m.ctrl.Call(_m, "SaveHost", host)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_mr *_MockOsqueryStoreRecorder) SaveHost(arg0 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "SaveHost", arg0)
|
||||
}
|
||||
|
||||
func (_m *MockOsqueryStore) MarkHostSeen(host *Host, t time.Time) error {
|
||||
ret := _m.ctrl.Call(_m, "MarkHostSeen", host, t)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_mr *_MockOsqueryStoreRecorder) MarkHostSeen(arg0, arg1 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "MarkHostSeen", arg0, arg1)
|
||||
}
|
||||
|
||||
func (_m *MockOsqueryStore) LabelQueriesForHost(host *Host, cutoff time.Time) (map[string]string, error) {
|
||||
ret := _m.ctrl.Call(_m, "LabelQueriesForHost", host, cutoff)
|
||||
ret0, _ := ret[0].(map[string]string)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (_mr *_MockOsqueryStoreRecorder) LabelQueriesForHost(arg0, arg1 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "LabelQueriesForHost", arg0, arg1)
|
||||
}
|
||||
|
||||
func (_m *MockOsqueryStore) RecordLabelQueryExecutions(host *Host, results map[string]bool, t time.Time) error {
|
||||
ret := _m.ctrl.Call(_m, "RecordLabelQueryExecutions", host, results, t)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_mr *_MockOsqueryStoreRecorder) RecordLabelQueryExecutions(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "RecordLabelQueryExecutions", arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
func (_m *MockOsqueryStore) NewQuery(query *Query) error {
|
||||
ret := _m.ctrl.Call(_m, "NewQuery", query)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_mr *_MockOsqueryStoreRecorder) NewQuery(arg0 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "NewQuery", arg0)
|
||||
}
|
||||
|
||||
func (_m *MockOsqueryStore) SaveQuery(query *Query) error {
|
||||
ret := _m.ctrl.Call(_m, "SaveQuery", query)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_mr *_MockOsqueryStoreRecorder) SaveQuery(arg0 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "SaveQuery", arg0)
|
||||
}
|
||||
|
||||
func (_m *MockOsqueryStore) DeleteQuery(query *Query) error {
|
||||
ret := _m.ctrl.Call(_m, "DeleteQuery", query)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_mr *_MockOsqueryStoreRecorder) DeleteQuery(arg0 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "DeleteQuery", arg0)
|
||||
}
|
||||
|
||||
func (_m *MockOsqueryStore) Query(id uint) (*Query, error) {
|
||||
ret := _m.ctrl.Call(_m, "Query", id)
|
||||
ret0, _ := ret[0].(*Query)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (_mr *_MockOsqueryStoreRecorder) Query(arg0 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "Query", arg0)
|
||||
}
|
||||
|
||||
func (_m *MockOsqueryStore) Queries() ([]*Query, error) {
|
||||
ret := _m.ctrl.Call(_m, "Queries")
|
||||
ret0, _ := ret[0].([]*Query)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (_mr *_MockOsqueryStoreRecorder) Queries() *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "Queries")
|
||||
}
|
||||
|
||||
func (_m *MockOsqueryStore) NewLabel(label *Label) error {
|
||||
ret := _m.ctrl.Call(_m, "NewLabel", label)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_mr *_MockOsqueryStoreRecorder) NewLabel(arg0 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "NewLabel", arg0)
|
||||
}
|
||||
|
||||
func (_m *MockOsqueryStore) NewPack(pack *Pack) error {
|
||||
ret := _m.ctrl.Call(_m, "NewPack", pack)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_mr *_MockOsqueryStoreRecorder) NewPack(arg0 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "NewPack", arg0)
|
||||
}
|
||||
|
||||
func (_m *MockOsqueryStore) SavePack(pack *Pack) error {
|
||||
ret := _m.ctrl.Call(_m, "SavePack", pack)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_mr *_MockOsqueryStoreRecorder) SavePack(arg0 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "SavePack", arg0)
|
||||
}
|
||||
|
||||
func (_m *MockOsqueryStore) DeletePack(pack *Pack) error {
|
||||
ret := _m.ctrl.Call(_m, "DeletePack", pack)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_mr *_MockOsqueryStoreRecorder) DeletePack(arg0 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "DeletePack", arg0)
|
||||
}
|
||||
|
||||
func (_m *MockOsqueryStore) Pack(id uint) (*Pack, error) {
|
||||
ret := _m.ctrl.Call(_m, "Pack", id)
|
||||
ret0, _ := ret[0].(*Pack)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (_mr *_MockOsqueryStoreRecorder) Pack(arg0 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "Pack", arg0)
|
||||
}
|
||||
|
||||
func (_m *MockOsqueryStore) Packs() ([]*Pack, error) {
|
||||
ret := _m.ctrl.Call(_m, "Packs")
|
||||
ret0, _ := ret[0].([]*Pack)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (_mr *_MockOsqueryStoreRecorder) Packs() *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "Packs")
|
||||
}
|
||||
|
||||
func (_m *MockOsqueryStore) AddQueryToPack(query *Query, pack *Pack) error {
|
||||
ret := _m.ctrl.Call(_m, "AddQueryToPack", query, pack)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_mr *_MockOsqueryStoreRecorder) AddQueryToPack(arg0, arg1 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "AddQueryToPack", arg0, arg1)
|
||||
}
|
||||
|
||||
func (_m *MockOsqueryStore) GetQueriesInPack(pack *Pack) ([]*Query, error) {
|
||||
ret := _m.ctrl.Call(_m, "GetQueriesInPack", pack)
|
||||
ret0, _ := ret[0].([]*Query)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (_mr *_MockOsqueryStoreRecorder) GetQueriesInPack(arg0 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "GetQueriesInPack", arg0)
|
||||
}
|
||||
|
||||
func (_m *MockOsqueryStore) RemoveQueryFromPack(query *Query, pack *Pack) error {
|
||||
ret := _m.ctrl.Call(_m, "RemoveQueryFromPack", query, pack)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_mr *_MockOsqueryStoreRecorder) RemoveQueryFromPack(arg0, arg1 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "RemoveQueryFromPack", arg0, arg1)
|
||||
}
|
@ -7,6 +7,7 @@ type OsqueryStore interface {
|
||||
// Host methods
|
||||
EnrollHost(uuid, hostname, ip, platform string, nodeKeySize int) (*Host, error)
|
||||
AuthenticateHost(nodeKey string) (*Host, error)
|
||||
SaveHost(host *Host) error
|
||||
MarkHostSeen(host *Host, t time.Time) error
|
||||
LabelQueriesForHost(host *Host, cutoff time.Time) (map[string]string, error)
|
||||
RecordLabelQueryExecutions(host *Host, results map[string]bool, t time.Time) error
|
||||
|
@ -32,6 +32,7 @@ type OsqueryStatusHandler interface {
|
||||
// It can be configured in a `main` function to bind the appropriate handlers
|
||||
// and it's methods can be attached to routes.
|
||||
type OsqueryHandler struct {
|
||||
LabelQueryInterval time.Duration
|
||||
ResultHandler OsqueryResultHandler
|
||||
StatusHandler OsqueryStatusHandler
|
||||
}
|
||||
@ -64,8 +65,27 @@ type OsqueryEnrollPostBody struct {
|
||||
HostIdentifier string `json:"host_identifier" validate:"required"`
|
||||
}
|
||||
|
||||
// OsqueryConfigPostBody is the generic osquery config endpoint request body
|
||||
// structure. Typically the node key and action will be parsed, and then the
|
||||
// Data JSON will be unmarshalled into one of the more specific OsqueryConfig*
|
||||
// structs.
|
||||
type OsqueryConfigPostBody struct {
|
||||
NodeKey string `json:"node_key" validate:"required"`
|
||||
Action string `json:"action" validate:"required"`
|
||||
Data *json.RawMessage `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
// OsqueryConfigDetail is the expected extra information provided with an
|
||||
// initial config request. This data will be used to determine which label
|
||||
// queries are supplied to the host.
|
||||
type OsqueryConfigDetail struct {
|
||||
Platform string `json:"platform" validate:"required"`
|
||||
}
|
||||
|
||||
// OsqueryConfigQueryResults contains the final information needed to generate a
|
||||
// config for the host. It containst the results of the label queries.
|
||||
type OsqueryConfigQueryResults struct {
|
||||
Results map[string]bool `json:"results" validate:"required"`
|
||||
}
|
||||
|
||||
type OsqueryLogPostBody struct {
|
||||
@ -141,31 +161,94 @@ func OsqueryEnroll(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
func OsqueryConfig(c *gin.Context) {
|
||||
func (h *OsqueryHandler) handleConfigDetail(db kolide.OsqueryStore, host *kolide.Host, data json.RawMessage) (map[string]string, error) {
|
||||
var detail OsqueryConfigDetail
|
||||
if err := json.Unmarshal(data, &detail); err != nil {
|
||||
return nil, errors.NewFromError(err, http.StatusBadRequest, "JSON parse error")
|
||||
}
|
||||
if err := validateStruct(&detail); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Update fields from detail (only save if there are changes)
|
||||
if host.Platform != detail.Platform {
|
||||
host.Platform = detail.Platform
|
||||
if err := db.SaveHost(host); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return kolide.LabelQueriesForHost(db, host, h.LabelQueryInterval)
|
||||
}
|
||||
|
||||
func (h *OsqueryHandler) handleConfigQueryResults(db kolide.OsqueryStore, host *kolide.Host, data json.RawMessage) error {
|
||||
var results OsqueryConfigQueryResults
|
||||
if err := json.Unmarshal(data, &results); err != nil {
|
||||
return errors.NewFromError(err, http.StatusBadRequest, "JSON parse error")
|
||||
}
|
||||
if err := validateStruct(&results); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return db.RecordLabelQueryExecutions(host, results.Results, time.Now())
|
||||
}
|
||||
|
||||
// Endpoint used by the osqueryd TLS logger plugin
|
||||
func (h *OsqueryHandler) OsqueryConfig(c *gin.Context) {
|
||||
var body OsqueryConfigPostBody
|
||||
err := ParseAndValidateJSON(c, &body)
|
||||
if err != nil {
|
||||
errors.ReturnOsqueryError(c, err)
|
||||
return
|
||||
}
|
||||
logrus.Debugf("OsqueryConfig: %s", body.NodeKey)
|
||||
|
||||
c.JSON(http.StatusOK,
|
||||
gin.H{
|
||||
"schedule": map[string]map[string]interface{}{
|
||||
"time": {
|
||||
"query": "select * from time;",
|
||||
"interval": 1,
|
||||
},
|
||||
},
|
||||
"node_invalid": false,
|
||||
})
|
||||
db := GetDB(c)
|
||||
|
||||
host, err := db.AuthenticateHost(body.NodeKey)
|
||||
if err != nil {
|
||||
errors.ReturnOsqueryError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if body.Data == nil {
|
||||
errors.ReturnOsqueryError(c,
|
||||
errors.New("Missing body data", "Got nil pointer for data in config"),
|
||||
)
|
||||
}
|
||||
|
||||
var res map[string]string
|
||||
switch body.Action {
|
||||
case "request":
|
||||
res, err = h.handleConfigDetail(db, host, *body.Data)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, res)
|
||||
return
|
||||
}
|
||||
|
||||
case "results":
|
||||
err = h.handleConfigQueryResults(db, host, *body.Data)
|
||||
// Now we should be able to calculate the appropriate config
|
||||
|
||||
default:
|
||||
err = errors.NewWithStatus(
|
||||
errors.StatusUnprocessableEntity,
|
||||
"Unknown config request action",
|
||||
fmt.Sprintf("Unknown config request action: %s", body.Action),
|
||||
)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
errors.ReturnOsqueryError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Generate the config and return it here!
|
||||
}
|
||||
|
||||
// Unmarshal the status logs before sending them to the status log handler
|
||||
func (h *OsqueryHandler) handleStatusLogs(data *json.RawMessage, nodeKey string) error {
|
||||
func (h *OsqueryHandler) handleStatusLogs(data json.RawMessage, nodeKey string) error {
|
||||
var statuses []OsqueryStatusLog
|
||||
if err := json.Unmarshal(*data, &statuses); err != nil {
|
||||
if err := json.Unmarshal(data, &statuses); err != nil {
|
||||
return errors.NewFromError(err, http.StatusBadRequest, "JSON parse error")
|
||||
}
|
||||
// Perhaps we should validate the unmarshalled status log
|
||||
@ -181,9 +264,9 @@ func (h *OsqueryHandler) handleStatusLogs(data *json.RawMessage, nodeKey string)
|
||||
}
|
||||
|
||||
// Unmarshal the result logs before sending them to the result log handler
|
||||
func (h *OsqueryHandler) handleResultLogs(data *json.RawMessage, nodeKey string) error {
|
||||
func (h *OsqueryHandler) handleResultLogs(data json.RawMessage, nodeKey string) error {
|
||||
var results []OsqueryResultLog
|
||||
if err := json.Unmarshal(*data, &results); err != nil {
|
||||
if err := json.Unmarshal(data, &results); err != nil {
|
||||
return errors.NewFromError(err, http.StatusBadRequest, "JSON parse error")
|
||||
}
|
||||
// Perhaps we should validate the unmarshalled result log
|
||||
@ -215,12 +298,18 @@ func (h *OsqueryHandler) OsqueryLog(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if body.Data == nil {
|
||||
errors.ReturnOsqueryError(c,
|
||||
errors.New("Missing body data", "Got nil pointer for data in log"),
|
||||
)
|
||||
}
|
||||
|
||||
switch body.LogType {
|
||||
case "status":
|
||||
err = h.handleStatusLogs(body.Data, body.NodeKey)
|
||||
err = h.handleStatusLogs(*body.Data, body.NodeKey)
|
||||
|
||||
case "result":
|
||||
err = h.handleResultLogs(body.Data, body.NodeKey)
|
||||
err = h.handleResultLogs(*body.Data, body.NodeKey)
|
||||
|
||||
default:
|
||||
err = errors.NewWithStatus(
|
||||
|
@ -5,7 +5,10 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/kolide/kolide-ose/errors"
|
||||
"github.com/kolide/kolide-ose/kolide"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@ -842,3 +845,216 @@ func TestDeleteQueryFromPack(t *testing.T) {
|
||||
assert.Nil(t, err)
|
||||
assert.Len(t, p.Queries, len(queriesInPack)-1)
|
||||
}
|
||||
|
||||
func TestHandleConfigDetail(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
db := kolide.NewMockOsqueryStore(ctrl)
|
||||
|
||||
detail := OsqueryConfigDetail{Platform: "darwin"}
|
||||
|
||||
detailBytes, err := json.Marshal(detail)
|
||||
assert.NoError(t, err)
|
||||
detailJSON := json.RawMessage(detailBytes)
|
||||
|
||||
host := &kolide.Host{
|
||||
NodeKey: "fake_key",
|
||||
}
|
||||
|
||||
expectHost := &kolide.Host{
|
||||
NodeKey: "fake_key",
|
||||
Platform: "darwin",
|
||||
}
|
||||
|
||||
handler := OsqueryHandler{
|
||||
LabelQueryInterval: time.Minute,
|
||||
}
|
||||
|
||||
expectQueries := map[string]string{
|
||||
"1": "query1",
|
||||
"3": "query3",
|
||||
}
|
||||
|
||||
db.EXPECT().SaveHost(expectHost)
|
||||
db.EXPECT().LabelQueriesForHost(expectHost, gomock.Any()).
|
||||
Return(expectQueries, nil).
|
||||
Do(func(_ *kolide.Host, cutoff time.Time) {
|
||||
// Check that the cutoff is in the correct interval
|
||||
expectCutoff := time.Now().Add(-handler.LabelQueryInterval)
|
||||
allowedDelta := 5 * time.Second
|
||||
assert.WithinDuration(t, expectCutoff, cutoff, allowedDelta)
|
||||
})
|
||||
|
||||
res, err := handler.handleConfigDetail(db, host, detailJSON)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expectQueries, res)
|
||||
|
||||
}
|
||||
|
||||
func TestHandleConfigDetailNoSave(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
db := kolide.NewMockOsqueryStore(ctrl)
|
||||
|
||||
detail := OsqueryConfigDetail{Platform: "darwin"}
|
||||
|
||||
detailBytes, err := json.Marshal(detail)
|
||||
assert.NoError(t, err)
|
||||
detailJSON := json.RawMessage(detailBytes)
|
||||
|
||||
host := &kolide.Host{
|
||||
NodeKey: "fake_key",
|
||||
Platform: "darwin",
|
||||
}
|
||||
|
||||
expectHost := &kolide.Host{
|
||||
NodeKey: "fake_key",
|
||||
Platform: "darwin",
|
||||
}
|
||||
|
||||
handler := OsqueryHandler{
|
||||
LabelQueryInterval: time.Hour,
|
||||
}
|
||||
|
||||
expectQueries := map[string]string{}
|
||||
|
||||
// Note that we don't expect a call to save because the platform did
|
||||
// not change
|
||||
db.EXPECT().LabelQueriesForHost(expectHost, gomock.Any()).
|
||||
Return(expectQueries, nil).
|
||||
Do(func(_ *kolide.Host, cutoff time.Time) {
|
||||
// Check that the cutoff is in the correct interval
|
||||
expectCutoff := time.Now().Add(-handler.LabelQueryInterval)
|
||||
allowedDelta := 5 * time.Second
|
||||
assert.WithinDuration(t, expectCutoff, cutoff, allowedDelta)
|
||||
})
|
||||
|
||||
res, err := handler.handleConfigDetail(db, host, detailJSON)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expectQueries, res)
|
||||
|
||||
}
|
||||
|
||||
func marshalRawMessage(t *testing.T, obj interface{}) json.RawMessage {
|
||||
objBytes, err := json.Marshal(obj)
|
||||
assert.NoError(t, err)
|
||||
objJSON := json.RawMessage(objBytes)
|
||||
return objJSON
|
||||
}
|
||||
|
||||
func TestHandleConfigDetailError(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
db := kolide.NewMockOsqueryStore(ctrl)
|
||||
|
||||
detail := OsqueryConfigDetail{Platform: "darwin"}
|
||||
|
||||
detailBytes, err := json.Marshal(detail)
|
||||
assert.NoError(t, err)
|
||||
detailJSON := json.RawMessage(detailBytes)
|
||||
|
||||
host := &kolide.Host{
|
||||
NodeKey: "fake_key",
|
||||
Platform: "darwin",
|
||||
}
|
||||
|
||||
expectHost := &kolide.Host{
|
||||
NodeKey: "fake_key",
|
||||
Platform: "darwin",
|
||||
}
|
||||
|
||||
handler := OsqueryHandler{
|
||||
LabelQueryInterval: time.Hour,
|
||||
}
|
||||
|
||||
// The DB call should error in this test
|
||||
db.EXPECT().LabelQueriesForHost(expectHost, gomock.Any()).
|
||||
Return(nil, errors.New("public", "private")).
|
||||
Do(func(_ *kolide.Host, cutoff time.Time) {
|
||||
// Check that the cutoff is in the correct interval
|
||||
expectCutoff := time.Now().Add(-handler.LabelQueryInterval)
|
||||
allowedDelta := 5 * time.Second
|
||||
assert.WithinDuration(t, expectCutoff, cutoff, allowedDelta)
|
||||
})
|
||||
|
||||
res, err := handler.handleConfigDetail(db, host, detailJSON)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, res)
|
||||
|
||||
}
|
||||
|
||||
func TestHandleConfigQueryResults(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
db := kolide.NewMockOsqueryStore(ctrl)
|
||||
|
||||
results := OsqueryConfigQueryResults{
|
||||
Results: map[string]bool{
|
||||
"1": true,
|
||||
"3": false,
|
||||
"4": true,
|
||||
},
|
||||
}
|
||||
|
||||
resultsJSON := marshalRawMessage(t, results)
|
||||
|
||||
host := &kolide.Host{
|
||||
NodeKey: "fake_key",
|
||||
Platform: "darwin",
|
||||
}
|
||||
|
||||
handler := OsqueryHandler{
|
||||
LabelQueryInterval: time.Hour,
|
||||
}
|
||||
|
||||
db.EXPECT().RecordLabelQueryExecutions(host, results.Results, gomock.Any()).
|
||||
Return(nil).
|
||||
Do(func(_ *kolide.Host, _ map[string]bool, recordTime time.Time) {
|
||||
// Check that the cutoff is in the correct interval
|
||||
allowedDelta := 5 * time.Second
|
||||
assert.WithinDuration(t, time.Now(), recordTime, allowedDelta)
|
||||
})
|
||||
|
||||
assert.NoError(t, handler.handleConfigQueryResults(db, host, resultsJSON))
|
||||
}
|
||||
|
||||
func TestHandleConfigQueryResultsError(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
db := kolide.NewMockOsqueryStore(ctrl)
|
||||
|
||||
results := OsqueryConfigQueryResults{
|
||||
Results: map[string]bool{
|
||||
"1": true,
|
||||
"3": false,
|
||||
"4": true,
|
||||
},
|
||||
}
|
||||
|
||||
resultsJSON := marshalRawMessage(t, results)
|
||||
|
||||
host := &kolide.Host{
|
||||
NodeKey: "fake_key",
|
||||
Platform: "darwin",
|
||||
}
|
||||
|
||||
handler := OsqueryHandler{
|
||||
LabelQueryInterval: time.Hour,
|
||||
}
|
||||
|
||||
// DB errors this time
|
||||
db.EXPECT().RecordLabelQueryExecutions(host, results.Results, gomock.Any()).
|
||||
Return(errors.New("public", "private")).
|
||||
Do(func(_ *kolide.Host, _ map[string]bool, recordTime time.Time) {
|
||||
// Check that the cutoff is in the correct interval
|
||||
allowedDelta := 5 * time.Second
|
||||
assert.WithinDuration(t, time.Now(), recordTime, allowedDelta)
|
||||
})
|
||||
|
||||
assert.Error(t, handler.handleConfigQueryResults(db, host, resultsJSON))
|
||||
}
|
||||
|
@ -100,6 +100,10 @@ func ParseAndValidateJSON(c *gin.Context, obj interface{}) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return validateStruct(obj)
|
||||
}
|
||||
|
||||
func validateStruct(obj interface{}) error {
|
||||
return validate.Struct(obj)
|
||||
}
|
||||
|
||||
@ -209,7 +213,7 @@ func CreateServer(ds datastore.Datastore, pool kolide.SMTPConnectionPool, w io.W
|
||||
}
|
||||
|
||||
osq.POST("/enroll", OsqueryEnroll)
|
||||
osq.POST("/config", OsqueryConfig)
|
||||
osq.POST("/config", osqueryHandler.OsqueryConfig)
|
||||
osq.POST("/log", osqueryHandler.OsqueryLog)
|
||||
osq.POST("/distributed/read", OsqueryDistributedRead)
|
||||
osq.POST("/distributed/write", OsqueryDistributedWrite)
|
||||
|
Loading…
Reference in New Issue
Block a user