From 7f421562013e48ba440ad881eb98767f16703c38 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Fri, 13 Nov 2020 14:31:53 +0300 Subject: [PATCH] [#168] acl: Implement binary/JSON encoders/decoders on HeaderFilter Signed-off-by: Leonard Lyubich --- pkg/acl/eacl/filter.go | 74 ++++++++++++++++++++++----- pkg/acl/eacl/filter_test.go | 24 +++++++++ v2/acl/json.go | 20 ++++++++ v2/acl/json_test.go | 12 +++++ v2/acl/marshal.go | 99 +++++++++++++++++++++---------------- v2/acl/marshal_test.go | 6 +-- 6 files changed, 175 insertions(+), 60 deletions(-) diff --git a/pkg/acl/eacl/filter.go b/pkg/acl/eacl/filter.go index 7608f3f9..efed7ef7 100644 --- a/pkg/acl/eacl/filter.go +++ b/pkg/acl/eacl/filter.go @@ -43,28 +43,28 @@ func (s staticStringer) String() string { return string(s) } -func (a Filter) Value() string { - return a.value.String() +func (f Filter) Value() string { + return f.value.String() } -func (a Filter) Matcher() Match { - return a.matcher +func (f Filter) Matcher() Match { + return f.matcher } -func (a Filter) Key() string { - return a.key.String() +func (f Filter) Key() string { + return f.key.String() } -func (a Filter) From() FilterHeaderType { - return a.from +func (f Filter) From() FilterHeaderType { + return f.from } -func (a *Filter) ToV2() *v2acl.HeaderFilter { +func (f *Filter) ToV2() *v2acl.HeaderFilter { filter := new(v2acl.HeaderFilter) - filter.SetValue(a.value.String()) - filter.SetKey(a.key.String()) - filter.SetMatchType(a.matcher.ToV2()) - filter.SetHeaderType(a.from.ToV2()) + filter.SetValue(f.value.String()) + filter.SetKey(f.key.String()) + filter.SetMatchType(f.matcher.ToV2()) + filter.SetHeaderType(f.from.ToV2()) return filter } @@ -94,6 +94,10 @@ func (k filterKey) String() string { } } +func NewFilter() *Filter { + return NewFilterFromV2(new(v2acl.HeaderFilter)) +} + func NewFilterFromV2(filter *v2acl.HeaderFilter) *Filter { f := new(Filter) @@ -108,3 +112,47 @@ func NewFilterFromV2(filter *v2acl.HeaderFilter) *Filter { return f } + +// Marshal marshals Filter into a protobuf binary form. +// +// Buffer is allocated when the argument is empty. +// Otherwise, the first buffer is used. +func (f *Filter) Marshal(b ...[]byte) ([]byte, error) { + var buf []byte + if len(b) > 0 { + buf = b[0] + } + + return f.ToV2(). + StableMarshal(buf) +} + +// Unmarshal unmarshals protobuf binary representation of Filter. +func (f *Filter) Unmarshal(data []byte) error { + fV2 := new(v2acl.HeaderFilter) + if err := fV2.Unmarshal(data); err != nil { + return err + } + + *f = *NewFilterFromV2(fV2) + + return nil +} + +// MarshalJSON encodes Filter to protobuf JSON format. +func (f *Filter) MarshalJSON() ([]byte, error) { + return f.ToV2(). + MarshalJSON() +} + +// UnmarshalJSON decodes Filter from protobuf JSON format. +func (f *Filter) UnmarshalJSON(data []byte) error { + fV2 := new(v2acl.HeaderFilter) + if err := fV2.UnmarshalJSON(data); err != nil { + return err + } + + *f = *NewFilterFromV2(fV2) + + return nil +} diff --git a/pkg/acl/eacl/filter_test.go b/pkg/acl/eacl/filter_test.go index f87033ca..64636ccd 100644 --- a/pkg/acl/eacl/filter_test.go +++ b/pkg/acl/eacl/filter_test.go @@ -35,3 +35,27 @@ func TestFilter(t *testing.T) { require.Equal(t, new(Filter), NewFilterFromV2(nil)) }) } + +func TestFilterEncoding(t *testing.T) { + f := newObjectFilter(MatchStringEqual, "key", "value") + + t.Run("binary", func(t *testing.T) { + data, err := f.Marshal() + require.NoError(t, err) + + f2 := NewFilter() + require.NoError(t, f2.Unmarshal(data)) + + require.Equal(t, f, f2) + }) + + t.Run("json", func(t *testing.T) { + data, err := f.MarshalJSON() + require.NoError(t, err) + + d2 := NewFilter() + require.NoError(t, d2.UnmarshalJSON(data)) + + require.Equal(t, f, d2) + }) +} diff --git a/v2/acl/json.go b/v2/acl/json.go index d9bbb45e..e8baf1c2 100644 --- a/v2/acl/json.go +++ b/v2/acl/json.go @@ -82,3 +82,23 @@ func BearerTokenFromJSON(data []byte) (*BearerToken, error) { return BearerTokenFromGRPCMessage(msg), nil } + +func (f *HeaderFilter) MarshalJSON() ([]byte, error) { + return protojson.MarshalOptions{ + EmitUnpopulated: true, + }.Marshal( + HeaderFilterToGRPCMessage(f), + ) +} + +func (f *HeaderFilter) UnmarshalJSON(data []byte) error { + msg := new(acl.EACLRecord_Filter) + + if err := protojson.Unmarshal(data, msg); err != nil { + return err + } + + *f = *HeaderFilterFromGRPCMessage(msg) + + return nil +} diff --git a/v2/acl/json_test.go b/v2/acl/json_test.go index 9d9e67ef..d2d68456 100644 --- a/v2/acl/json_test.go +++ b/v2/acl/json_test.go @@ -64,3 +64,15 @@ func TestBearerTokenJSON(t *testing.T) { require.Equal(t, exp, got) }) } + +func TestFilterJSON(t *testing.T) { + f := generateFilter(acl.HeaderTypeObject, "key", "value") + + data, err := f.MarshalJSON() + require.NoError(t, err) + + f2 := new(acl.HeaderFilter) + require.NoError(t, f2.UnmarshalJSON(data)) + + require.Equal(t, f, f2) +} diff --git a/v2/acl/marshal.go b/v2/acl/marshal.go index a87bfc8e..152d0f1f 100644 --- a/v2/acl/marshal.go +++ b/v2/acl/marshal.go @@ -1,7 +1,9 @@ package acl import ( - "github.com/nspcc-dev/neofs-api-go/util/proto" + protoutil "github.com/nspcc-dev/neofs-api-go/util/proto" + acl "github.com/nspcc-dev/neofs-api-go/v2/acl/grpc" + "google.golang.org/protobuf/proto" ) const ( @@ -50,14 +52,14 @@ func (t *Table) StableMarshal(buf []byte) ([]byte, error) { err error ) - n, err = proto.NestedStructureMarshal(tableVersionField, buf[offset:], t.version) + n, err = protoutil.NestedStructureMarshal(tableVersionField, buf[offset:], t.version) if err != nil { return nil, err } offset += n - n, err = proto.NestedStructureMarshal(tableContainerIDField, buf[offset:], t.cid) + n, err = protoutil.NestedStructureMarshal(tableContainerIDField, buf[offset:], t.cid) if err != nil { return nil, err } @@ -65,7 +67,7 @@ func (t *Table) StableMarshal(buf []byte) ([]byte, error) { offset += n for i := range t.records { - n, err = proto.NestedStructureMarshal(tableRecordsField, buf[offset:], t.records[i]) + n, err = protoutil.NestedStructureMarshal(tableRecordsField, buf[offset:], t.records[i]) if err != nil { return nil, err } @@ -82,11 +84,11 @@ func (t *Table) StableSize() (size int) { return 0 } - size += proto.NestedStructureSize(tableVersionField, t.version) - size += proto.NestedStructureSize(tableContainerIDField, t.cid) + size += protoutil.NestedStructureSize(tableVersionField, t.version) + size += protoutil.NestedStructureSize(tableContainerIDField, t.cid) for i := range t.records { - size += proto.NestedStructureSize(tableRecordsField, t.records[i]) + size += protoutil.NestedStructureSize(tableRecordsField, t.records[i]) } return size @@ -108,14 +110,14 @@ func (r *Record) StableMarshal(buf []byte) ([]byte, error) { err error ) - n, err = proto.EnumMarshal(recordOperationField, buf[offset:], int32(r.op)) + n, err = protoutil.EnumMarshal(recordOperationField, buf[offset:], int32(r.op)) if err != nil { return nil, err } offset += n - n, err = proto.EnumMarshal(recordActionField, buf[offset:], int32(r.action)) + n, err = protoutil.EnumMarshal(recordActionField, buf[offset:], int32(r.action)) if err != nil { return nil, err } @@ -123,7 +125,7 @@ func (r *Record) StableMarshal(buf []byte) ([]byte, error) { offset += n for i := range r.filters { - n, err = proto.NestedStructureMarshal(recordFiltersField, buf[offset:], r.filters[i]) + n, err = protoutil.NestedStructureMarshal(recordFiltersField, buf[offset:], r.filters[i]) if err != nil { return nil, err } @@ -132,7 +134,7 @@ func (r *Record) StableMarshal(buf []byte) ([]byte, error) { } for i := range r.targets { - n, err = proto.NestedStructureMarshal(recordTargetsField, buf[offset:], r.targets[i]) + n, err = protoutil.NestedStructureMarshal(recordTargetsField, buf[offset:], r.targets[i]) if err != nil { return nil, err } @@ -149,15 +151,15 @@ func (r *Record) StableSize() (size int) { return 0 } - size += proto.EnumSize(recordOperationField, int32(r.op)) - size += proto.EnumSize(recordActionField, int32(r.action)) + size += protoutil.EnumSize(recordOperationField, int32(r.op)) + size += protoutil.EnumSize(recordActionField, int32(r.action)) for i := range r.filters { - size += proto.NestedStructureSize(recordFiltersField, r.filters[i]) + size += protoutil.NestedStructureSize(recordFiltersField, r.filters[i]) } for i := range r.targets { - size += proto.NestedStructureSize(recordTargetsField, r.targets[i]) + size += protoutil.NestedStructureSize(recordTargetsField, r.targets[i]) } return size @@ -179,28 +181,28 @@ func (f *HeaderFilter) StableMarshal(buf []byte) ([]byte, error) { err error ) - n, err = proto.EnumMarshal(filterHeaderTypeField, buf[offset:], int32(f.hdrType)) + n, err = protoutil.EnumMarshal(filterHeaderTypeField, buf[offset:], int32(f.hdrType)) if err != nil { return nil, err } offset += n - n, err = proto.EnumMarshal(filterMatchTypeField, buf[offset:], int32(f.matchType)) + n, err = protoutil.EnumMarshal(filterMatchTypeField, buf[offset:], int32(f.matchType)) if err != nil { return nil, err } offset += n - n, err = proto.StringMarshal(filterNameField, buf[offset:], f.key) + n, err = protoutil.StringMarshal(filterNameField, buf[offset:], f.key) if err != nil { return nil, err } offset += n - _, err = proto.StringMarshal(filterValueField, buf[offset:], f.value) + _, err = protoutil.StringMarshal(filterValueField, buf[offset:], f.value) if err != nil { return nil, err } @@ -214,14 +216,25 @@ func (f *HeaderFilter) StableSize() (size int) { return 0 } - size += proto.EnumSize(filterHeaderTypeField, int32(f.hdrType)) - size += proto.EnumSize(filterMatchTypeField, int32(f.matchType)) - size += proto.StringSize(filterNameField, f.key) - size += proto.StringSize(filterValueField, f.value) + size += protoutil.EnumSize(filterHeaderTypeField, int32(f.hdrType)) + size += protoutil.EnumSize(filterMatchTypeField, int32(f.matchType)) + size += protoutil.StringSize(filterNameField, f.key) + size += protoutil.StringSize(filterValueField, f.value) return size } +func (f *HeaderFilter) Unmarshal(data []byte) error { + m := new(acl.EACLRecord_Filter) + if err := proto.Unmarshal(data, m); err != nil { + return err + } + + *f = *HeaderFilterFromGRPCMessage(m) + + return nil +} + // StableMarshal marshals unified role info structure in a protobuf // compatible way without field order shuffle. func (t *Target) StableMarshal(buf []byte) ([]byte, error) { @@ -238,14 +251,14 @@ func (t *Target) StableMarshal(buf []byte) ([]byte, error) { err error ) - n, err = proto.EnumMarshal(targetTypeField, buf[offset:], int32(t.role)) + n, err = protoutil.EnumMarshal(targetTypeField, buf[offset:], int32(t.role)) if err != nil { return nil, err } offset += n - _, err = proto.RepeatedBytesMarshal(targetKeysField, buf[offset:], t.keys) + _, err = protoutil.RepeatedBytesMarshal(targetKeysField, buf[offset:], t.keys) if err != nil { return nil, err } @@ -259,8 +272,8 @@ func (t *Target) StableSize() (size int) { return 0 } - size += proto.EnumSize(targetTypeField, int32(t.role)) - size += proto.RepeatedBytesSize(targetKeysField, t.keys) + size += protoutil.EnumSize(targetTypeField, int32(t.role)) + size += protoutil.RepeatedBytesSize(targetKeysField, t.keys) return size } @@ -279,21 +292,21 @@ func (l *TokenLifetime) StableMarshal(buf []byte) ([]byte, error) { err error ) - n, err = proto.UInt64Marshal(lifetimeExpirationField, buf[offset:], l.exp) + n, err = protoutil.UInt64Marshal(lifetimeExpirationField, buf[offset:], l.exp) if err != nil { return nil, err } offset += n - n, err = proto.UInt64Marshal(lifetimeNotValidBeforeField, buf[offset:], l.nbf) + n, err = protoutil.UInt64Marshal(lifetimeNotValidBeforeField, buf[offset:], l.nbf) if err != nil { return nil, err } offset += n - _, err = proto.UInt64Marshal(lifetimeIssuedAtField, buf[offset:], l.iat) + _, err = protoutil.UInt64Marshal(lifetimeIssuedAtField, buf[offset:], l.iat) if err != nil { return nil, err } @@ -306,9 +319,9 @@ func (l *TokenLifetime) StableSize() (size int) { return 0 } - size += proto.UInt64Size(lifetimeExpirationField, l.exp) - size += proto.UInt64Size(lifetimeNotValidBeforeField, l.nbf) - size += proto.UInt64Size(lifetimeIssuedAtField, l.iat) + size += protoutil.UInt64Size(lifetimeExpirationField, l.exp) + size += protoutil.UInt64Size(lifetimeNotValidBeforeField, l.nbf) + size += protoutil.UInt64Size(lifetimeIssuedAtField, l.iat) return size } @@ -327,21 +340,21 @@ func (bt *BearerTokenBody) StableMarshal(buf []byte) ([]byte, error) { err error ) - n, err = proto.NestedStructureMarshal(bearerTokenBodyACLField, buf[offset:], bt.eacl) + n, err = protoutil.NestedStructureMarshal(bearerTokenBodyACLField, buf[offset:], bt.eacl) if err != nil { return nil, err } offset += n - n, err = proto.NestedStructureMarshal(bearerTokenBodyOwnerField, buf[offset:], bt.ownerID) + n, err = protoutil.NestedStructureMarshal(bearerTokenBodyOwnerField, buf[offset:], bt.ownerID) if err != nil { return nil, err } offset += n - _, err = proto.NestedStructureMarshal(bearerTokenBodyLifetimeField, buf[offset:], bt.lifetime) + _, err = protoutil.NestedStructureMarshal(bearerTokenBodyLifetimeField, buf[offset:], bt.lifetime) if err != nil { return nil, err } @@ -354,9 +367,9 @@ func (bt *BearerTokenBody) StableSize() (size int) { return 0 } - size += proto.NestedStructureSize(bearerTokenBodyACLField, bt.eacl) - size += proto.NestedStructureSize(bearerTokenBodyOwnerField, bt.ownerID) - size += proto.NestedStructureSize(bearerTokenBodyLifetimeField, bt.lifetime) + size += protoutil.NestedStructureSize(bearerTokenBodyACLField, bt.eacl) + size += protoutil.NestedStructureSize(bearerTokenBodyOwnerField, bt.ownerID) + size += protoutil.NestedStructureSize(bearerTokenBodyLifetimeField, bt.lifetime) return size } @@ -375,14 +388,14 @@ func (bt *BearerToken) StableMarshal(buf []byte) ([]byte, error) { err error ) - n, err = proto.NestedStructureMarshal(bearerTokenBodyField, buf[offset:], bt.body) + n, err = protoutil.NestedStructureMarshal(bearerTokenBodyField, buf[offset:], bt.body) if err != nil { return nil, err } offset += n - _, err = proto.NestedStructureMarshal(bearerTokenSignatureField, buf[offset:], bt.sig) + _, err = protoutil.NestedStructureMarshal(bearerTokenSignatureField, buf[offset:], bt.sig) if err != nil { return nil, err } @@ -395,8 +408,8 @@ func (bt *BearerToken) StableSize() (size int) { return 0 } - size += proto.NestedStructureSize(bearerTokenBodyField, bt.body) - size += proto.NestedStructureSize(bearerTokenSignatureField, bt.sig) + size += protoutil.NestedStructureSize(bearerTokenBodyField, bt.body) + size += protoutil.NestedStructureSize(bearerTokenSignatureField, bt.sig) return size } diff --git a/v2/acl/marshal_test.go b/v2/acl/marshal_test.go index 4727a2fe..af5f56e6 100644 --- a/v2/acl/marshal_test.go +++ b/v2/acl/marshal_test.go @@ -117,7 +117,6 @@ func generateBearerToken(id string) *acl.BearerToken { func TestHeaderFilter_StableMarshal(t *testing.T) { filterFrom := generateFilter(acl.HeaderTypeObject, "CID", "Container ID Value") - transport := new(grpc.EACLRecord_Filter) t.Run("non empty", func(t *testing.T) { filterFrom.SetHeaderType(acl.HeaderTypeObject) @@ -128,10 +127,9 @@ func TestHeaderFilter_StableMarshal(t *testing.T) { wire, err := filterFrom.StableMarshal(nil) require.NoError(t, err) - err = goproto.Unmarshal(wire, transport) - require.NoError(t, err) + filterTo := new(acl.HeaderFilter) + require.NoError(t, filterTo.Unmarshal(wire)) - filterTo := acl.HeaderFilterFromGRPCMessage(transport) require.Equal(t, filterFrom, filterTo) }) }