From eb52d52eb9b2652bae9d31fbebce0beffd376689 Mon Sep 17 00:00:00 2001 From: Elliot Jackson <13633636+elliotmjackson@users.noreply.github.com> Date: Tue, 11 Jul 2023 23:28:55 +0200 Subject: [PATCH] Support extensions using indexes reserved for internal organization (#17) Supplies a fallback mechanism for the deprecated extension index. Essentially we search for the index that we want, if its not found we look for the old one. depends on bufbuild/protovalidate#43 --- .golangci.yml | 4 ++ go.mod | 5 ++- go.sum | 14 +++++-- internal/evaluator/resolver.go | 76 +++++++++++++++++++++++++++------- 4 files changed, 81 insertions(+), 18 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 727ffde..4e38e07 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -78,3 +78,7 @@ issues: - path: internal/constraints/lookups.go linters: - gochecknoglobals + - path: internal/evaluator/resolver.go + linters: + # uses deprecated fields on protoimpl.ExtensionInfo but its the only way + - staticcheck diff --git a/go.mod b/go.mod index 7d9ce31..ee318e5 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/bufbuild/protovalidate-go go 1.18 require ( - buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20230609233005-3757a25ff0b9.1 + buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20230711201221-890d7e3584ce.1 github.com/envoyproxy/protoc-gen-validate v1.0.2 github.com/google/cel-go v0.16.0 github.com/stretchr/testify v1.8.4 @@ -13,11 +13,14 @@ require ( require ( github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230512164433-5d1fd1a340c9 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/kr/pretty v0.1.0 // indirect + github.com/kr/text v0.2.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect golang.org/x/text v0.10.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect + gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 11a55e0..9c495dd 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,8 @@ -buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20230609233005-3757a25ff0b9.1 h1:0nKm28ysQfpk1VP76OOcv33yyI92L2EQMtvhzCd7EXE= -buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20230609233005-3757a25ff0b9.1/go.mod h1:xafc+XIsTxTy76GJQ1TKgvJWsSugFBqMaN27WhUblew= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20230711201221-890d7e3584ce.1 h1:LtMTVxNFGQ/AkswvA3AQgSMBjZw9bvu642Mww5rn5Tg= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20230711201221-890d7e3584ce.1/go.mod h1:xafc+XIsTxTy76GJQ1TKgvJWsSugFBqMaN27WhUblew= github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230512164433-5d1fd1a340c9 h1:goHVqTbFX3AIo0tzGr14pgfAW2ZfPChKO21Z9MGf/gk= github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230512164433-5d1fd1a340c9/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -12,6 +13,12 @@ github.com/google/cel-go v0.16.0 h1:DG9YQ8nFCFXAs/FDDwBxmL1tpKNrdlGUM9U3537bX/Y= github.com/google/cel-go v0.16.0/go.mod h1:HXZKzB0LXqer5lHHgfWAnlYwJaQBDKMjxjulNQzhwhY= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= @@ -36,8 +43,9 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go. google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/evaluator/resolver.go b/internal/evaluator/resolver.go index aa73cbb..777cdcc 100644 --- a/internal/evaluator/resolver.go +++ b/internal/evaluator/resolver.go @@ -15,9 +15,18 @@ package evaluator import ( + "strings" + "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/reflect/protoregistry" + "google.golang.org/protobuf/runtime/protoimpl" +) + +const ( + newExtensionIndex = "1159" + previousExtensionIndex = "51071" ) type StandardConstraintResolver interface { @@ -29,15 +38,27 @@ type StandardConstraintResolver interface { type DefaultResolver struct{} func (r DefaultResolver) ResolveMessageConstraints(desc protoreflect.MessageDescriptor) *validate.MessageConstraints { - return resolveExt[protoreflect.MessageDescriptor, *validate.MessageConstraints](desc, validate.E_Message) + constraints := resolveExt[protoreflect.MessageDescriptor, *validate.MessageConstraints](desc, validate.E_Message) + if constraints == nil { + constraints = resolveDeprecatedIndex[protoreflect.MessageDescriptor, *validate.MessageConstraints](desc, validate.E_Message) + } + return constraints } func (r DefaultResolver) ResolveOneofConstraints(desc protoreflect.OneofDescriptor) *validate.OneofConstraints { - return resolveExt[protoreflect.OneofDescriptor, *validate.OneofConstraints](desc, validate.E_Oneof) + constraints := resolveExt[protoreflect.OneofDescriptor, *validate.OneofConstraints](desc, validate.E_Oneof) + if constraints == nil { + constraints = resolveDeprecatedIndex[protoreflect.OneofDescriptor, *validate.OneofConstraints](desc, validate.E_Oneof) + } + return constraints } func (r DefaultResolver) ResolveFieldConstraints(desc protoreflect.FieldDescriptor) *validate.FieldConstraints { - return resolveExt[protoreflect.FieldDescriptor, *validate.FieldConstraints](desc, validate.E_Field) + constraints := resolveExt[protoreflect.FieldDescriptor, *validate.FieldConstraints](desc, validate.E_Field) + if constraints == nil { + constraints = resolveDeprecatedIndex[protoreflect.FieldDescriptor, *validate.FieldConstraints](desc, validate.E_Field) + } + return constraints } // resolveExt does not use proto.GetExtension in the event the underlying type @@ -45,30 +66,57 @@ func (r DefaultResolver) ResolveFieldConstraints(desc protoreflect.FieldDescript // circumstances, particularly in dynamic or runtime contexts, the underlying // extension value's type may be a dynamicpb.Message. In this case, we fall back // through a proto.[Un]Marshal cycle to get it into the concrete type we expect. -func resolveExt[ - D protoreflect.Descriptor, - C proto.Message, -]( +func resolveExt[D protoreflect.Descriptor, C proto.Message]( desc D, extType protoreflect.ExtensionType, ) (constraints C) { opts := desc.Options().ProtoReflect() fDesc := extType.TypeDescriptor() - if !opts.Has(fDesc) { + if opts.Has(fDesc) { + msg := opts.Get(fDesc).Message().Interface() + if m, ok := msg.(C); ok { + return m + } + } + + unknown := opts.GetUnknown() + if len(unknown) == 0 { return constraints } + opts = opts.Type().New() + var resolver protoregistry.Types + if err := resolver.RegisterExtension(extType); err != nil { + return constraints + } + _ = proto.UnmarshalOptions{Resolver: &resolver}.Unmarshal(unknown, opts.Interface()) + + if opts.Has(fDesc) { + msg := opts.Get(fDesc).Message().Interface() + if m, ok := msg.(C); ok { + return m + } + } msg := opts.Get(fDesc).Message().Interface() if m, ok := msg.(C); ok { return m } - b, _ := proto.Marshal(msg) - constraints, ok := extType.New().Message().Interface().(C) - if !ok { - return constraints - } - _ = proto.Unmarshal(b, constraints) return constraints } + +// resolveDeprecatedIndex is a fallback for the deprecated extension index. +func resolveDeprecatedIndex[D protoreflect.Descriptor, C proto.Message]( + desc D, + ext *protoimpl.ExtensionInfo, +) C { + return resolveExt[D, C](desc, &protoimpl.ExtensionInfo{ + ExtendedType: ext.ExtendedType, + ExtensionType: ext.ExtensionType, + Field: 51071, + Name: ext.Name, + Tag: strings.Replace(ext.Tag, newExtensionIndex, previousExtensionIndex, 1), + Filename: ext.Filename, + }) +}