fleet/server/service/transport_error.go
Victor Lyuboslavsky c88cc953fb
Refactor endpoint_utils for modularization (#36484)
Resolves #37192

Separating generic endpoint_utils middleware logic from domain-specific
business logic. New bounded contexts would share the generic logic and
implement their own domain-specific logic. The two approaches used in
this PR are:
- Use common `platform` types
- Use interfaces

In the next PR we will move `endpointer_utils`, `authzcheck` and
`ratelimit` into `platform` directory.

# Checklist for submitter

- [x] Added changes file

## Testing

- [x] Added/updated tests
- [x] QA'd all new/changed functionality manually



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Refactor**
* Restructured internal error handling and context management to support
bounded context architecture.
* Improved error context collection and telemetry observability through
a provider-based mechanism.
* Decoupled licensing and authentication concerns into interfaces for
better modularity.

* **Chores**
* Updated internal package dependencies to align with new architectural
boundaries.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-12-31 09:12:00 -06:00

110 lines
3.2 KiB
Go

package service
import (
"context"
"encoding/json"
"fmt"
"net/http"
platform_http "github.com/fleetdm/fleet/v4/server/platform/http"
"github.com/fleetdm/fleet/v4/server/service/middleware/endpoint_utils"
)
// FleetErrorEncoder handles fleet-specific error encoding for MailError
// and OsqueryError.
func FleetErrorEncoder(ctx context.Context, err error, w http.ResponseWriter, enc *json.Encoder, jsonErr *endpoint_utils.JsonError) bool {
switch e := err.(type) {
case MailError:
jsonErr.Message = "Mail Error"
jsonErr.Errors = []map[string]string{
{
"name": "base",
"reason": e.Message,
},
}
w.WriteHeader(http.StatusInternalServerError)
enc.Encode(jsonErr) //nolint:errcheck
return true
case *OsqueryError:
// 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]any{
"error": e.Error(),
"uuid": jsonErr.UUID,
}
if e.NodeInvalid() { //nolint:gocritic // ignore ifElseChain
w.WriteHeader(http.StatusUnauthorized)
errMap["node_invalid"] = true
} else if e.Status() != 0 {
w.WriteHeader(e.Status())
} else {
// TODO: osqueryError is not always the result of an internal error on
// our side, it is also used to represent a client error (invalid data,
// e.g. malformed json, carve too large, etc., so 4xx), are we returning
// a 500 because of some osquery-specific requirement?
w.WriteHeader(http.StatusInternalServerError)
}
enc.Encode(errMap) //nolint:errcheck
return true
}
return false
}
// MailError is set when an error performing mail operations
type MailError struct {
Message string
}
func (e MailError) Error() string {
return fmt.Sprintf("a mail error occurred: %s", e.Message)
}
// OsqueryError is the error returned to osquery agents.
type OsqueryError struct {
message string
nodeInvalid bool
StatusCode int
platform_http.ErrorWithUUID
}
var _ platform_http.ErrorUUIDer = (*OsqueryError)(nil)
// Error implements the error interface.
func (e *OsqueryError) Error() string {
return e.message
}
// NodeInvalid returns whether the error returned to osquery
// should contain the node_invalid property.
func (e *OsqueryError) NodeInvalid() bool {
return e.nodeInvalid
}
func (e *OsqueryError) Status() int {
return e.StatusCode
}
func NewOsqueryError(message string, nodeInvalid bool) *OsqueryError {
return &OsqueryError{
message: message,
nodeInvalid: nodeInvalid,
}
}
// encodeError is a convenience function that calls endpoint_utils.EncodeError
// with the FleetErrorEncoder. Use this for direct error encoding in handlers.
func encodeError(ctx context.Context, err error, w http.ResponseWriter) {
endpoint_utils.EncodeError(ctx, err, w, FleetErrorEncoder)
}
// fleetErrorEncoder is an adapter that wraps endpoint_utils.EncodeError with
// FleetErrorEncoder for use as a kithttp.ErrorEncoder.
func fleetErrorEncoder(ctx context.Context, err error, w http.ResponseWriter) {
endpoint_utils.EncodeError(ctx, err, w, FleetErrorEncoder)
}