mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-08 14:39:09 +02:00
* use libopenapi OpenAPI generator Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io> * added a handler for v1alpha1/peers Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io> * wire up request validator Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io> * wired up spec-based validation Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io> * use sync validation; set base url to v1alpha1 Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io> * moved stuff around, extracted runtime libopenapu deps into runtime_tooling Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io> * testing /peers path params Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io> * fixed tests Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io> * added user schemas and paths Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io> * use api validation in tests Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io> * post-merge fixes Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io> * deleted tmp command used to test validator integration Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io> * require request body in post/put requests in order to force validation Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io> * making linter happy Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io> * switch to api/v1alpha1 Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io> * go mod tidy Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io> * keep the original order of middleware: metrics, cors, then auth Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io> * return 422 on validation errors Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io> * cleanups Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io> * more cleanups Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io> * v1alpha1 spec cleanups Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io> * no need for a double-pointer Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io> * more linter fixes Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io> * removed more double pointers Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io> * use di to inject api_v0 and api_v1 http routers Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io> * do not re-add middleware on repeated call to ApiHandler Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io> * calling ApiRouter() now also calls ApiV1Router() Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io> --------- Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io>
94 lines
2.8 KiB
Go
94 lines
2.8 KiB
Go
package apiv1alpha1
|
|
|
|
import (
|
|
_ "embed"
|
|
"fmt"
|
|
"log/slog"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/netbirdio/netbird/shared/management/http/util"
|
|
"github.com/netbirdio/netbird/shared/management/status"
|
|
"github.com/pb33f/libopenapi"
|
|
validator "github.com/pb33f/libopenapi-validator"
|
|
"github.com/pb33f/libopenapi-validator/config"
|
|
"github.com/pb33f/libopenapi-validator/errors"
|
|
"github.com/pb33f/libopenapi/datamodel"
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// TODO (dmitri) this needs to be extracted, as it will grow to 500Kb
|
|
//
|
|
//go:embed bundle.yaml
|
|
var bundle []byte
|
|
|
|
func CreateV1ApiValidatingMiddleware() (*V1ValidatorMiddleware, error) {
|
|
doc, err := libopenapi.NewDocumentWithConfiguration(bundle, &datamodel.DocumentConfiguration{
|
|
Logger: slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
|
|
Level: slog.LevelInfo,
|
|
})),
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
model, err := doc.BuildV3Model()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// TODO figure out document validation: rn it's possible to have a spec
|
|
// that's not entirely correct -- parts of it fail to parse, but silently
|
|
v := validator.NewValidatorFromV3Model(&model.Model,
|
|
config.WithoutSecurityValidation(),
|
|
config.WithStandardBodyDecoders(),
|
|
config.WithRejectUnsupportedBodyContent(),
|
|
config.WithRequestDefaults())
|
|
// v.SetDocument(doc)
|
|
// v.ValidatePathParams()
|
|
// if valid, errs := v.ValidateDocument(); !valid {
|
|
// return nil, fmt.Errorf("error validating OpenAPI doc, %s", errs)
|
|
// }
|
|
|
|
return &V1ValidatorMiddleware{Validator: v}, nil
|
|
}
|
|
|
|
type V1ValidatorMiddleware struct {
|
|
Validator validator.Validator
|
|
}
|
|
|
|
func (v *V1ValidatorMiddleware) Handler(h http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
valid, errs := v.Validator.ValidateHttpRequestSync(r)
|
|
if !valid {
|
|
validationErrs := make([]string, 0, len(errs))
|
|
for _, err := range errs {
|
|
validationErrs = append(validationErrs, validationError(err))
|
|
}
|
|
|
|
log.WithContext(r.Context()).Debugf("error validating request: %s", strings.Join(validationErrs, ", "))
|
|
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "invalid request: %s", strings.Join(validationErrs, ", ")), w)
|
|
|
|
return
|
|
}
|
|
h.ServeHTTP(w, r)
|
|
})
|
|
}
|
|
|
|
func validationError(err *errors.ValidationError) string {
|
|
if err.SchemaValidationErrors != nil {
|
|
errs := make([]string, 0, len(err.SchemaValidationErrors))
|
|
for _, e := range err.SchemaValidationErrors {
|
|
errs = append(errs, fmt.Sprintf("field %s: %s", e.FieldPath, e.Reason))
|
|
}
|
|
return fmt.Sprintf("%s: %s", err.Message, strings.Join(errs, ", "))
|
|
} else {
|
|
if err.SpecLine > 0 && err.SpecCol > 0 {
|
|
return fmt.Sprintf("%s, Line: %d, Column: %d", err.Message, err.SpecLine, err.SpecCol)
|
|
} else {
|
|
return fmt.Sprint(err.Message)
|
|
}
|
|
}
|
|
}
|