fleet/server/kolide/import_config_unmarshaler.go
John Murphy 2945031a86 Fix bug in Osquery import and updated import docs (#1478)
This PR contains a fix for a bug that turned up when I was testing configimporter. If the platform field is not specified, its supposed to default to all per the osquery configuration spec. The default was not properly implemented, and if the platform value was missing from the imported configuration it failed. The PR also added instructions to the api documentation describing how to import an osquery configuration.
2017-04-23 19:41:27 -05:00

201 lines
4.3 KiB
Go

package kolide
import (
"encoding/json"
"strconv"
"github.com/pkg/errors"
"github.com/spf13/cast"
)
var wrongTypeError = errors.New("argument missing or unexpected type")
// UnmarshalJSON custom unmarshaling for PackNameMap will determine whether
// the pack section of an osquery config file refers to a file path, or
// pack details. Pack details are unmarshalled into into PackDetails structure
// as oppossed to nested map[string]interface{}
func (pnm PackNameMap) UnmarshalJSON(b []byte) error {
var temp map[string]interface{}
err := json.Unmarshal(b, &temp)
if err != nil {
return err
}
for key, val := range temp {
switch t := val.(type) {
case string:
pnm[key] = t
case map[string]interface{}:
val, err := unmarshalPackDetails(t)
if err != nil {
return err
}
pnm[key] = val
default:
return errors.Errorf("can't unmarshal %s %v", key, val)
}
}
return nil
}
func strptr(v interface{}) (*string, error) {
if v == nil {
return nil, nil
}
s, ok := v.(string)
if !ok {
return nil, wrongTypeError
}
return &s, nil
}
func boolptr(v interface{}) (*bool, error) {
if v == nil {
return nil, nil
}
b, ok := v.(bool)
if !ok {
return nil, wrongTypeError
}
return &b, nil
}
// We expect a float64 here because of the way JSON represents numbers
func uintptr(v interface{}) (*OsQueryConfigInt, error) {
if v == nil {
return nil, nil
}
i, err := unmarshalInteger(v)
if err != nil {
return nil, err
}
return &i, nil
}
func unmarshalPackDetails(v map[string]interface{}) (PackDetails, error) {
var result PackDetails
queries, err := unmarshalQueryDetails(v["queries"])
if err != nil {
return result, err
}
discovery, err := unmarshalDiscovery(v["discovery"])
if err != nil {
return result, err
}
platform := cast.ToString(v["platform"])
shard, err := uintptr(v["shard"])
if err != nil {
return result, err
}
version, err := strptr(v["version"])
if err != nil {
return result, err
}
result = PackDetails{
Queries: queries,
Shard: shard,
Version: version,
Platform: platform,
Discovery: discovery,
}
return result, nil
}
func unmarshalDiscovery(val interface{}) ([]string, error) {
var result []string
if val == nil {
return result, nil
}
v, ok := val.([]interface{})
if !ok {
return result, wrongTypeError
}
for _, val := range v {
query, err := cast.ToStringE(val)
if err != nil {
return result, err
}
result = append(result, query)
}
return result, nil
}
func unmarshalQueryDetails(v interface{}) (QueryNameToQueryDetailsMap, error) {
var err error
result := make(QueryNameToQueryDetailsMap)
if v == nil {
return result, nil
}
for qn, details := range v.(map[string]interface{}) {
result[qn], err = unmarshalQueryDetail(details)
if err != nil {
return nil, err
}
}
return result, nil
}
func unmarshalQueryDetail(val interface{}) (QueryDetails, error) {
var result QueryDetails
v, ok := val.(map[string]interface{})
if !ok {
return result, errors.New("argument was missing or the wrong type")
}
interval, err := unmarshalInteger(v["interval"])
if err != nil {
return result, err
}
query, err := cast.ToStringE(v["query"])
if err != nil {
return result, err
}
removed, err := boolptr(v["removed"])
if err != nil {
return result, err
}
platform, err := strptr(v["platform"])
if err != nil {
return result, err
}
version, err := strptr(v["version"])
if err != nil {
return result, err
}
shard, err := uintptr(v["shard"])
if err != nil {
return result, err
}
snapshot, err := boolptr(v["snapshot"])
if err != nil {
return result, nil
}
result = QueryDetails{
Query: query,
Interval: OsQueryConfigInt(interval),
Removed: removed,
Platform: platform,
Version: version,
Shard: shard,
Snapshot: snapshot,
}
return result, nil
}
// It is valid for the interval can be a string that is convertable to an int,
// or an float64. The float64 is how all numbers in JSON are represented, so
// we need to convert to uint
func unmarshalInteger(val interface{}) (OsQueryConfigInt, error) {
// if interval is nil return zero value
if val == nil {
return OsQueryConfigInt(0), nil
}
switch v := val.(type) {
case string:
i, err := strconv.ParseUint(v, 10, 64)
return OsQueryConfigInt(i), err
case float64:
return OsQueryConfigInt(v), nil
default:
return OsQueryConfigInt(0), wrongTypeError
}
}