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 - - - - - - - + + + + + + + - - - - - - - + + + + + + + - - - - - - - + + + + + + + - + +
Aaron Raff
Aaron Raff

💻
Rodrigo Chacon
Rodrigo Chacon

💻
Christopher Diehl
Christopher Diehl

💻
Andrew Z Allen
Andrew Z Allen

📖
Sebastien Armand
Sebastien Armand

💻
Dat Tran
Dat Tran

💻
Jon Perl
Jon Perl

⚠️ 💻
Aaron Raff
Aaron Raff

💻
Rodrigo Chacon
Rodrigo Chacon

💻
Christopher Diehl
Christopher Diehl

💻
Andrew Z Allen
Andrew Z Allen

📖
Sebastien Armand
Sebastien Armand

💻
Dat Tran
Dat Tran

💻
Jon Perl
Jon Perl

⚠️ 💻
Or Elimelech
Or Elimelech

💻
giddel
giddel

💻
Eduardo
Eduardo

📖 💻
Itai Schwartz
Itai Schwartz

💻
Ikko Ashimine
Ikko Ashimine

📖
Márk Sági-Kazár
Márk Sági-Kazár

💻
Dan Piet
Dan Piet

💻
Or Elimelech
Or Elimelech

💻
giddel
giddel

💻
Eduardo
Eduardo

📖 💻
Itai Schwartz
Itai Schwartz

💻
Ikko Ashimine
Ikko Ashimine

📖
Márk Sági-Kazár
Márk Sági-Kazár

💻
Dan Piet
Dan Piet

💻
Amay Shah
Amay Shah

💻
kevin-ip
kevin-ip

💻
albertchae
albertchae

💻
Thomas Sickert
Thomas Sickert

📖
Jameel Al-Aziz
Jameel Al-Aziz

📦
George
George

💻
Chase Pierce
Chase Pierce

💻
Amay Shah
Amay Shah

💻
kevin-ip
kevin-ip

💻
albertchae
albertchae

💻
Thomas Sickert
Thomas Sickert

📖
Jameel Al-Aziz
Jameel Al-Aziz

📦
George
George

💻
Chase Pierce
Chase Pierce

💻
ITO Shogo
ITO Shogo

⚠️
ITO Shogo
ITO Shogo

⚠️
Yoofi Quansah
Yoofi Quansah

💻
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()