fleet/server/service/transport_error.go
Zachary Wasserman 0b7747bef0
Fix pack and query UI issues in Fleet 2.0 (#1829)
Replaces (and appropriately refactors) a number of endpoints that were removed long ago when we decided to kill the UI with the fleetctl release. We turned out not to do this, and now need to restore these missing endpoints.

This is not a straight up replacement of the existing code because of refactoring to the DB schemas that was also done in the migration.

Most of the replaced code was removed in #1670 and #1686.

Fixes #1811, fixes #1810
2018-06-15 10:13:11 -04:00

167 lines
3.6 KiB
Go

package service
import (
"context"
"encoding/json"
"net/http"
"github.com/kolide/fleet/server/kolide"
"github.com/pkg/errors"
)
// erroer interface is implemented by response structs to encode business logic errors
type errorer interface {
error() error
}
type jsonError struct {
Message string `json:"message"`
Errors []map[string]string `json:"errors,omitempty"`
}
// use baseError to encode an jsonError.Errors field with an error that has
// a generic "name" field. The frontend client always expects errors in a
// []map[string]string format.
func baseError(err string) []map[string]string {
return []map[string]string{map[string]string{
"name": "base",
"reason": err},
}
}
// same as baseError, but replaces "base" with different name.
func namedError(name string, err string) []map[string]string {
return []map[string]string{map[string]string{
"name": name,
"reason": err},
}
}
// encode error and status header to the client
func encodeError(ctx context.Context, err error, w http.ResponseWriter) {
enc := json.NewEncoder(w)
enc.SetIndent("", " ")
type validationError interface {
error
Invalid() []map[string]string
}
if e, ok := err.(validationError); ok {
ve := jsonError{
Message: "Validation Failed",
Errors: e.Invalid(),
}
w.WriteHeader(http.StatusUnprocessableEntity)
enc.Encode(ve)
return
}
type authenticationError interface {
error
AuthError() string
}
if e, ok := err.(authenticationError); ok {
ae := jsonError{
Message: "Authentication Failed",
Errors: baseError(e.AuthError()),
}
w.WriteHeader(http.StatusUnauthorized)
enc.Encode(ae)
return
}
type permissionError interface {
PermissionError() []map[string]string
}
if e, ok := err.(permissionError); ok {
pe := jsonError{
Message: "Permission Denied",
Errors: e.PermissionError(),
}
w.WriteHeader(http.StatusForbidden)
enc.Encode(pe)
return
}
if kolide.IsForeignKey(errors.Cause(err)) {
ve := jsonError{
Message: "Validation Failed",
Errors: baseError(err.Error()),
}
w.WriteHeader(http.StatusForbidden)
enc.Encode(ve)
return
}
type mailError interface {
MailError() []map[string]string
}
if e, ok := err.(mailError); ok {
me := jsonError{
Message: "Mail Error",
Errors: e.MailError(),
}
w.WriteHeader(http.StatusInternalServerError)
enc.Encode(me)
return
}
type osqueryError interface {
error
NodeInvalid() bool
}
if e, ok := err.(osqueryError); ok {
// osquery expects to receive the node_invalid key when a TLS
// request provides an invalid node_key for authentication. It
// doesn't use the error message provided, but we provide this
// for debugging purposes (and perhaps osquery will use this
// error message in the future).
errMap := map[string]interface{}{"error": e.Error()}
if e.NodeInvalid() {
w.WriteHeader(http.StatusUnauthorized)
errMap["node_invalid"] = true
} else {
w.WriteHeader(http.StatusInternalServerError)
}
enc.Encode(errMap)
return
}
type notFoundError interface {
error
IsNotFound() bool
}
if e, ok := err.(notFoundError); ok {
je := jsonError{
Message: "Resource Not Found",
Errors: baseError(e.Error()),
}
w.WriteHeader(http.StatusNotFound)
enc.Encode(je)
return
}
type existsError interface {
error
IsExists() bool
}
if e, ok := err.(existsError); ok {
je := jsonError{
Message: "Resource Already Exists",
Errors: baseError(e.Error()),
}
w.WriteHeader(http.StatusConflict)
enc.Encode(je)
return
}
w.WriteHeader(http.StatusInternalServerError)
je := jsonError{
Message: "Unknown Error",
Errors: baseError(err.Error()),
}
enc.Encode(je)
}