Skip to content

Commit

Permalink
eacl: Add CopyTo method for structs
Browse files Browse the repository at this point in the history
Signed-off-by: Evgenii Baidakov <evgenii@nspcc.io>
  • Loading branch information
smallhive committed Sep 7, 2023
1 parent e2af4c3 commit b2fc773
Show file tree
Hide file tree
Showing 8 changed files with 309 additions and 0 deletions.
8 changes: 8 additions & 0 deletions eacl/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ func (u u64Stringer) EncodeToString() string {
return strconv.FormatUint(uint64(u), 10)
}

// CopyTo writes deep copy of the [Filter] to dst.
func (f Filter) CopyTo(dst *Filter) {
dst.from = f.from
dst.matcher = f.matcher
dst.key = f.key
dst.value = f.value
}

// Value returns filtered string value.
func (f Filter) Value() string {
return f.value.EncodeToString()
Expand Down
46 changes: 46 additions & 0 deletions eacl/filter_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package eacl

import (
"bytes"
"strconv"
"testing"

Expand Down Expand Up @@ -100,3 +101,48 @@ func TestFilter_ToV2(t *testing.T) {
}
})
}

func TestFilter_CopyTo(t *testing.T) {
var filter Filter
filter.value = staticStringer("value")
filter.from = 1
filter.matcher = 1
filter.key = filterKey{
typ: 1,
str: "1",
}

var dst Filter
t.Run("copy", func(t *testing.T) {
filter.CopyTo(&dst)

bts, err := filter.Marshal()
require.NoError(t, err)

bts2, err := dst.Marshal()
require.NoError(t, err)

require.Equal(t, filter, dst)
require.True(t, bytes.Equal(bts, bts2))
})

t.Run("change", func(t *testing.T) {
require.Equal(t, filter.value, dst.value)
require.Equal(t, filter.from, dst.from)
require.Equal(t, filter.matcher, dst.matcher)
require.Equal(t, filter.key.typ, dst.key.typ)
require.Equal(t, filter.key.str, dst.key.str)

dst.value = staticStringer("value2")
dst.from = 2
dst.matcher = 2
dst.key.typ = 2
dst.key.str = "2"

require.NotEqual(t, filter.value, dst.value)
require.NotEqual(t, filter.from, dst.from)
require.NotEqual(t, filter.matcher, dst.matcher)
require.NotEqual(t, filter.key.typ, dst.key.typ)
require.NotEqual(t, filter.key.str, dst.key.str)
})
}
17 changes: 17 additions & 0 deletions eacl/record.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,23 @@ type Record struct {
targets []Target
}

// CopyTo writes deep copy of the [Record] to dst.
func (r Record) CopyTo(dst *Record) {
dst.action = r.action
dst.operation = r.operation

dst.filters = make([]Filter, len(r.filters))
copy(dst.filters, r.filters)

dst.targets = make([]Target, len(r.targets))
for i, t := range r.targets {
var newTarget Target
t.CopyTo(&newTarget)

dst.targets[i] = newTarget
}
}

// Targets returns list of target subjects to apply ACL rule to.
//
// The value returned shares memory with the structure itself, so changing it can lead to data corruption.
Expand Down
69 changes: 69 additions & 0 deletions eacl/record_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package eacl

import (
"bytes"
"crypto/ecdsa"
"fmt"
"testing"
Expand Down Expand Up @@ -251,3 +252,71 @@ func randomPublicKey(t *testing.T) *ecdsa.PublicKey {
require.NoError(t, err)
return &p.PrivateKey.PublicKey
}

func TestRecord_CopyTo(t *testing.T) {
var record Record
record.action = ActionAllow
record.operation = OperationPut
record.AddObjectAttributeFilter(MatchStringEqual, "key", "value")

var target Target
target.SetRole(1)
target.SetBinaryKeys([][]byte{
{1, 2, 3},
})

record.SetTargets(target)
record.AddObjectAttributeFilter(MatchStringEqual, "key", "value")

t.Run("copy", func(t *testing.T) {
var dst Record
record.CopyTo(&dst)

bts, err := record.Marshal()
require.NoError(t, err)

bts2, err := dst.Marshal()
require.NoError(t, err)

require.Equal(t, record, dst)
require.True(t, bytes.Equal(bts, bts2))
})

t.Run("change filters", func(t *testing.T) {
var dst Record
record.CopyTo(&dst)

require.Equal(t, record.filters[0].key.str, dst.filters[0].key.str)
require.Equal(t, record.filters[0].key.typ, dst.filters[0].key.typ)
require.Equal(t, record.filters[0].matcher, dst.filters[0].matcher)
require.Equal(t, record.filters[0].value, dst.filters[0].value)
require.Equal(t, record.filters[0].from, dst.filters[0].from)

dst.filters[0].key.str = "key2"
dst.filters[0].key.typ = 12345
dst.filters[0].matcher = MatchStringNotEqual
dst.filters[0].value = staticStringer("staticStringer")
dst.filters[0].from = 12345

require.NotEqual(t, record.filters[0].key.str, dst.filters[0].key.str)
require.NotEqual(t, record.filters[0].key.typ, dst.filters[0].key.typ)
require.NotEqual(t, record.filters[0].matcher, dst.filters[0].matcher)
require.NotEqual(t, record.filters[0].value, dst.filters[0].value)
require.NotEqual(t, record.filters[0].from, dst.filters[0].from)
})

t.Run("change target", func(t *testing.T) {
var dst Record
record.CopyTo(&dst)

require.Equal(t, record.targets[0].role, dst.targets[0].role)
dst.targets[0].role = 12345
require.NotEqual(t, record.targets[0].role, dst.targets[0].role)

for i, key := range dst.targets[0].keys {
require.True(t, bytes.Equal(key, record.targets[0].keys[i]))
key[0] = 10
require.False(t, bytes.Equal(key, record.targets[0].keys[i]))
}
})
}
18 changes: 18 additions & 0 deletions eacl/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,24 @@ type Table struct {
records []Record
}

// CopyTo writes deep copy of the [Table] to dst.
func (t Table) CopyTo(dst *Table) {
ver := t.version
dst.version = ver

if t.cid != nil {
id := *t.cid
dst.cid = &id
} else {
dst.cid = nil
}

dst.records = make([]Record, len(t.records))
for i := range t.records {
t.records[i].CopyTo(&dst.records[i])
}
}

// CID returns identifier of the container that should use given access control rules.
func (t Table) CID() (cID cid.ID, isSet bool) {
if t.cid != nil {
Expand Down
102 changes: 102 additions & 0 deletions eacl/table_internal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package eacl

import (
"bytes"
"crypto/sha256"
"testing"

cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
"github.com/nspcc-dev/neofs-sdk-go/version"
"github.com/stretchr/testify/require"
)

func TestTable_CopyTo(t *testing.T) {
sha := sha256.Sum256([]byte("container id"))
id := cidtest.IDWithChecksum(sha)

var table Table
table.SetVersion(version.Current())
table.SetCID(id)

var target Target
target.SetRole(1)
target.SetBinaryKeys([][]byte{
{1, 2, 3},
})

record := CreateRecord(ActionAllow, OperationPut)
record.SetTargets(target)
record.AddObjectAttributeFilter(MatchStringEqual, "key", "value")

table.AddRecord(record)

t.Run("copy", func(t *testing.T) {
var dst Table
table.CopyTo(&dst)

bts, err := table.Marshal()
require.NoError(t, err)

bts2, err := dst.Marshal()
require.NoError(t, err)

require.Equal(t, table, dst)
require.True(t, bytes.Equal(bts, bts2))
})

t.Run("change version", func(t *testing.T) {
var dst Table
table.CopyTo(&dst)

require.True(t, table.Version().Equal(dst.Version()))

var newVersion version.Version
newVersion.SetMajor(10)
newVersion.SetMinor(100)

dst.SetVersion(newVersion)

require.False(t, table.Version().Equal(dst.Version()))
})

t.Run("change cid", func(t *testing.T) {
var dst Table
table.CopyTo(&dst)

cid1, isSet1 := table.CID()
require.True(t, isSet1)

cid2, isSet2 := dst.CID()
require.True(t, isSet2)

require.True(t, cid1.Equals(cid2))

sha = sha256.Sum256([]byte("container id 2"))
dst.SetCID(cidtest.IDWithChecksum(sha))

cid1, isSet1 = table.CID()
require.True(t, isSet1)

cid2, isSet2 = dst.CID()
require.True(t, isSet2)

require.False(t, cid1.Equals(cid2))
})

t.Run("change record", func(t *testing.T) {
var dst Table
table.CopyTo(&dst)

require.Equal(t, table.records[0].action, dst.records[0].action)
dst.records[0].SetAction(ActionDeny)
require.NotEqual(t, table.records[0].action, dst.records[0].action)

require.Equal(t, table.records[0].operation, dst.records[0].operation)
dst.records[0].SetOperation(OperationDelete)
require.NotEqual(t, table.records[0].operation, dst.records[0].operation)

require.Equal(t, table.records[0].targets[0].role, dst.records[0].targets[0].role)
table.records[0].targets[0].SetRole(1234)
require.NotEqual(t, table.records[0].targets[0].role, dst.records[0].targets[0].role)
})
}
12 changes: 12 additions & 0 deletions eacl/target.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@ func ecdsaKeysToPtrs(keys []ecdsa.PublicKey) []*ecdsa.PublicKey {
return keysPtr
}

// CopyTo writes deep copy of the [Target] to dst.
func (t Target) CopyTo(dst *Target) {
dst.role = t.role

dst.keys = make([][]byte, len(t.keys))
for i, k := range t.keys {
dst.keys[i] = make([]byte, len(k))

copy(dst.keys[i], t.keys[i])
}
}

// BinaryKeys returns list of public keys to identify
// target subject in a binary format.
//
Expand Down
37 changes: 37 additions & 0 deletions eacl/target_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package eacl

import (
"bytes"
"crypto/ecdsa"
"testing"

Expand Down Expand Up @@ -83,3 +84,39 @@ func TestTarget_ToV2(t *testing.T) {
require.Nil(t, targetV2.GetKeys())
})
}

func TestTarget_CopyTo(t *testing.T) {
var target Target
target.SetRole(1)
target.SetBinaryKeys([][]byte{
{1, 2, 3},
})

t.Run("copy", func(t *testing.T) {
var dst Target
target.CopyTo(&dst)

bts, err := target.Marshal()
require.NoError(t, err)

bts2, err := dst.Marshal()
require.NoError(t, err)

require.Equal(t, target, dst)
require.True(t, bytes.Equal(bts, bts2))
})

t.Run("change", func(t *testing.T) {
var dst Target
target.CopyTo(&dst)

require.Equal(t, target.role, dst.role)
dst.SetRole(2)
require.NotEqual(t, target.role, dst.role)

require.True(t, bytes.Equal(target.keys[0], dst.keys[0]))
// change some key data
dst.keys[0][0] = 5
require.False(t, bytes.Equal(target.keys[0], dst.keys[0]))
})
}

0 comments on commit b2fc773

Please sign in to comment.