diff --git a/.all-contributorsrc b/.all-contributorsrc
index d5a9a0d6c3..ba7cead836 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -204,6 +204,15 @@
"contributions": [
"test"
]
+ },
+ {
+ "login": "yquansah",
+ "name": "Yoofi Quansah",
+ "avatar_url": "https://avatars.githubusercontent.com/u/13950726?v=4",
+ "profile": "https://github.com/yquansah",
+ "contributions": [
+ "code"
+ ]
}
],
"contributorsPerLine": 7,
diff --git a/README.md b/README.md
index b1829177f0..748a63b69a 100644
--- a/README.md
+++ b/README.md
@@ -272,34 +272,35 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
diff --git a/_tools/go.mod b/_tools/go.mod
index b5ca2a8bd2..e216543ce1 100644
--- a/_tools/go.mod
+++ b/_tools/go.mod
@@ -9,7 +9,7 @@ require (
golang.org/x/tools v0.7.0
golang.org/x/tools/cmd/cover v0.1.0-deprecated
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0
- google.golang.org/protobuf v1.29.0
+ google.golang.org/protobuf v1.29.1
)
require (
diff --git a/_tools/go.sum b/_tools/go.sum
index 20a56af574..b16ec68a70 100644
--- a/_tools/go.sum
+++ b/_tools/go.sum
@@ -1048,8 +1048,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.29.0 h1:44S3JjaKmLEE4YIkjzexaP+NzZsudE3Zin5Njn/pYX0=
-google.golang.org/protobuf v1.29.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+google.golang.org/protobuf v1.29.1 h1:7QBf+IK2gx70Ap/hDsOmam3GE0v9HicjfEdAxE62UoM=
+google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/go.mod b/go.mod
index f4e814a4c2..2fc45c144d 100644
--- a/go.mod
+++ b/go.mod
@@ -55,7 +55,7 @@ require (
golang.org/x/sync v0.1.0
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4
google.golang.org/grpc v1.53.0
- google.golang.org/protobuf v1.29.0
+ google.golang.org/protobuf v1.29.1
gopkg.in/segmentio/analytics-go.v3 v3.1.0
gopkg.in/yaml.v2 v2.4.0
)
diff --git a/go.sum b/go.sum
index a8626d59d1..5af828a6f8 100644
--- a/go.sum
+++ b/go.sum
@@ -31,6 +31,7 @@ cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW
cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM=
cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
+cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
@@ -1944,6 +1945,7 @@ google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ6
google.golang.org/genproto v0.0.0-20220111164026-67b88f271998/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA=
+google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s=
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
@@ -1994,8 +1996,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-google.golang.org/protobuf v1.29.0 h1:44S3JjaKmLEE4YIkjzexaP+NzZsudE3Zin5Njn/pYX0=
-google.golang.org/protobuf v1.29.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+google.golang.org/protobuf v1.29.1 h1:7QBf+IK2gx70Ap/hDsOmam3GE0v9HicjfEdAxE62UoM=
+google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/internal/cmd/auth.go b/internal/cmd/auth.go
index b834b06b88..63c6e323aa 100644
--- a/internal/cmd/auth.go
+++ b/internal/cmd/auth.go
@@ -137,6 +137,7 @@ func registerFunc(ctx context.Context, conn *grpc.ClientConn, fn func(context.Co
func authenticationHTTPMount(
ctx context.Context,
+ logger *zap.Logger,
cfg config.AuthenticationConfig,
r chi.Router,
conn *grpc.ClientConn,
@@ -172,6 +173,6 @@ func authenticationHTTPMount(
r.Group(func(r chi.Router) {
r.Use(middleware...)
- r.Mount("/auth/v1", gateway.NewGatewayServeMux(muxOpts...))
+ r.Mount("/auth/v1", gateway.NewGatewayServeMux(logger, muxOpts...))
})
}
diff --git a/internal/cmd/http.go b/internal/cmd/http.go
index 81beffbbb9..c60c76df88 100644
--- a/internal/cmd/http.go
+++ b/internal/cmd/http.go
@@ -55,7 +55,7 @@ func NewHTTPServer(
isConsole = cfg.Log.Encoding == config.LogEncodingConsole
r = chi.NewRouter()
- api = gateway.NewGatewayServeMux()
+ api = gateway.NewGatewayServeMux(logger)
httpPort = cfg.Server.HTTPPort
)
@@ -130,7 +130,7 @@ func NewHTTPServer(
// mount all authentication related HTTP components
// to the chi router.
- authenticationHTTPMount(ctx, cfg.Authentication, r, conn)
+ authenticationHTTPMount(ctx, logger, cfg.Authentication, r, conn)
r.Group(func(r chi.Router) {
r.Use(func(handler http.Handler) http.Handler {
diff --git a/internal/gateway/gateway.go b/internal/gateway/gateway.go
index cbbc7bcecf..c25b820c5d 100644
--- a/internal/gateway/gateway.go
+++ b/internal/gateway/gateway.go
@@ -1,8 +1,11 @@
package gateway
import (
+ "sync"
+
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"go.flipt.io/flipt/rpc/flipt"
+ "go.uber.org/zap"
"google.golang.org/protobuf/encoding/protojson"
)
@@ -13,20 +16,26 @@ import (
// See: rpc/flipt/marshal.go
//
// See: https://github.com/flipt-io/flipt/issues/664
-var commonMuxOptions = []runtime.ServeMuxOption{
- runtime.WithMarshalerOption(runtime.MIMEWildcard, flipt.NewV1toV2MarshallerAdapter()),
- runtime.WithMarshalerOption("application/json+pretty", &runtime.JSONPb{
- MarshalOptions: protojson.MarshalOptions{
- Indent: " ",
- Multiline: true, // Optional, implied by presence of "Indent".
- },
- UnmarshalOptions: protojson.UnmarshalOptions{
- DiscardUnknown: true,
- },
- }),
-}
+var commonMuxOptions []runtime.ServeMuxOption
+var once sync.Once
// NewGatewayServeMux builds a new gateway serve mux with common options.
-func NewGatewayServeMux(opts ...runtime.ServeMuxOption) *runtime.ServeMux {
+func NewGatewayServeMux(logger *zap.Logger, opts ...runtime.ServeMuxOption) *runtime.ServeMux {
+ once.Do(func() {
+ commonMuxOptions = []runtime.ServeMuxOption{
+ runtime.WithMarshalerOption(runtime.MIMEWildcard, flipt.NewV1toV2MarshallerAdapter(logger)),
+ runtime.WithMarshalerOption("application/json+pretty", &runtime.JSONPb{
+ MarshalOptions: protojson.MarshalOptions{
+ Indent: " ",
+ Multiline: true, // Optional, implied by presence of "Indent".
+ },
+ UnmarshalOptions: protojson.UnmarshalOptions{
+ DiscardUnknown: true,
+ },
+ }),
+ }
+
+ })
+
return runtime.NewServeMux(append(commonMuxOptions, opts...)...)
}
diff --git a/internal/server/auth/method/kubernetes/testing/http.go b/internal/server/auth/method/kubernetes/testing/http.go
index 3737a3e03c..1403a794ad 100644
--- a/internal/server/auth/method/kubernetes/testing/http.go
+++ b/internal/server/auth/method/kubernetes/testing/http.go
@@ -26,7 +26,7 @@ func StartHTTPServer(
t.Helper()
var (
- mux = gateway.NewGatewayServeMux()
+ mux = gateway.NewGatewayServeMux(logger)
httpServer = &HTTPServer{
GRPCServer: StartGRPCServer(t, ctx, logger, conf),
}
diff --git a/internal/server/auth/method/oidc/testing/http.go b/internal/server/auth/method/oidc/testing/http.go
index 1cdaa0048f..ab4ae20c39 100644
--- a/internal/server/auth/method/oidc/testing/http.go
+++ b/internal/server/auth/method/oidc/testing/http.go
@@ -34,6 +34,7 @@ func StartHTTPServer(
oidcmiddleware = oidc.NewHTTPMiddleware(conf.Session)
mux = gateway.NewGatewayServeMux(
+ logger,
runtime.WithMetadata(oidc.ForwardCookies),
runtime.WithForwardResponseOption(oidcmiddleware.ForwardResponseOption),
)
diff --git a/rpc/flipt/marshaller.go b/rpc/flipt/marshaller.go
index 391a83548c..1086411bbe 100644
--- a/rpc/flipt/marshaller.go
+++ b/rpc/flipt/marshaller.go
@@ -1,10 +1,13 @@
package flipt
import (
+ "encoding/json"
+ "errors"
"io"
grpc_gateway_v1 "github.com/grpc-ecosystem/grpc-gateway/runtime"
grpc_gateway_v2 "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
+ "go.uber.org/zap"
)
var _ grpc_gateway_v2.Marshaler = &V1toV2MarshallerAdapter{}
@@ -20,10 +23,11 @@ var _ grpc_gateway_v2.Marshaler = &V1toV2MarshallerAdapter{}
// TODO: remove this custom marshaller for Flipt API v2 as we want to use the default v2 marshaller directly.
type V1toV2MarshallerAdapter struct {
*grpc_gateway_v1.JSONPb
+ logger *zap.Logger
}
-func NewV1toV2MarshallerAdapter() *V1toV2MarshallerAdapter {
- return &V1toV2MarshallerAdapter{&grpc_gateway_v1.JSONPb{OrigName: false, EmitDefaults: true}}
+func NewV1toV2MarshallerAdapter(logger *zap.Logger) *V1toV2MarshallerAdapter {
+ return &V1toV2MarshallerAdapter{&grpc_gateway_v1.JSONPb{OrigName: false, EmitDefaults: true}, logger}
}
func (m *V1toV2MarshallerAdapter) ContentType(_ interface{}) string {
@@ -34,8 +38,30 @@ func (m *V1toV2MarshallerAdapter) Marshal(v interface{}) ([]byte, error) {
return m.JSONPb.Marshal(v)
}
+// decoderInterceptor intercepts and modifies the outbound error return value for
+// inputs that fail to unmarshal against the protobuf.
+type decoderInterceptor struct {
+ grpc_gateway_v1.Decoder
+ logger *zap.Logger
+}
+
+func (c *decoderInterceptor) Decode(v interface{}) error {
+ err := c.Decoder.Decode(v)
+ if err != nil {
+ c.logger.Debug("JSON decoding failed for inputs", zap.Error(err))
+
+ if _, ok := err.(*json.UnmarshalTypeError); ok {
+ return errors.New("invalid values for key(s) in json body")
+ }
+
+ return err
+ }
+
+ return nil
+}
+
func (m *V1toV2MarshallerAdapter) NewDecoder(r io.Reader) grpc_gateway_v2.Decoder {
- return m.JSONPb.NewDecoder(r)
+ return &decoderInterceptor{Decoder: m.JSONPb.NewDecoder(r), logger: m.logger}
}
func (m *V1toV2MarshallerAdapter) NewEncoder(w io.Writer) grpc_gateway_v2.Encoder {
diff --git a/test/api.sh b/test/api.sh
index 49ce4d3aaa..bd6b07c4b5 100755
--- a/test/api.sh
+++ b/test/api.sh
@@ -263,6 +263,12 @@ step_5_test_evaluation()
status 200
matches "\"flagKey\":\"$flag_key\""
matches "\"match\":false"
+
+ # evaluate returns 400 plus user friendly error message
+ authedShakedown POST "/api/v1/evaluate" -H 'Content-Type:application/json' -d "{\"flag_key\":\"$flag_key\",\"entity_id\":\"$(uuid_str)\",\"context\":\"hello\"}"
+ status 400
+ matches "\"code\":3"
+ contains "\"message\":\"invalid values for key(s) in json body\""
}
step_6_test_batch_evaluation()