Skip to content

Commit

Permalink
session: Add CopyTo method for structs
Browse files Browse the repository at this point in the history
closes #194

Signed-off-by: Evgenii Baidakov <[email protected]>
  • Loading branch information
smallhive committed Sep 7, 2023
1 parent e63134a commit e5f5668
Show file tree
Hide file tree
Showing 6 changed files with 490 additions and 0 deletions.
42 changes: 42 additions & 0 deletions session/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,48 @@ type commonData struct {

type contextReader func(session.TokenContext, bool) error

func (x commonData) copyTo(dst *commonData) {
dst.idSet = x.idSet
copy(dst.id[:], x.id[:])

dst.issuerSet = x.issuerSet
iss := x.issuer
dst.issuer = iss

dst.lifetimeSet = x.lifetimeSet
dst.iat = x.iat
dst.nbf = x.nbf
dst.exp = x.exp

if auth := x.authKey; auth != nil {
dst.authKey = make([]byte, len(x.authKey))
copy(dst.authKey, x.authKey)
} else {
dst.authKey = nil
}

dst.sigSet = x.sigSet
if sig := x.sig.GetKey(); sig != nil {
bts := make([]byte, len(sig))
copy(bts, sig)

dst.sig.SetKey(bts)
} else {
dst.sig.SetKey(nil)
}

dst.sig.SetScheme(x.sig.GetScheme())

if sign := x.sig.GetSign(); sign != nil {
bts := make([]byte, len(sign))
copy(bts, sign)

dst.sig.SetSign(sign)
} else {
dst.sig.SetSign(nil)
}
}

// reads commonData and custom context from the session.Token message.
// If checkFieldPresence is set, returns an error on absence of any protocol-required
// field. Verifies format of any presented field according to NeoFS API V2 protocol.
Expand Down
234 changes: 234 additions & 0 deletions session/common_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
package session

import (
"bytes"
"testing"

"github.com/google/uuid"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
"github.com/nspcc-dev/neofs-api-go/v2/session"
"github.com/nspcc-dev/neofs-sdk-go/crypto/test"
"github.com/stretchr/testify/require"
)

func Test_commonData_copyTo(t *testing.T) {
var sig refs.Signature

sig.SetKey([]byte("key"))
sig.SetSign([]byte("sign"))
sig.SetScheme(refs.ECDSA_SHA512)

signer := test.RandomSignerRFC6979(t)

data := commonData{
idSet: true,
id: uuid.New(),
issuerSet: true,
issuer: signer.UserID(),
lifetimeSet: true,
iat: 1,
nbf: 2,
exp: 3,
authKey: []byte{1, 2, 3, 4},
sigSet: true,
sig: sig,
}

t.Run("copy", func(t *testing.T) {
var dst commonData
data.copyTo(&dst)

emptyWriter := func() session.TokenContext {
return &session.ContainerSessionContext{}
}

require.Equal(t, data, dst)
require.True(t, bytes.Equal(data.marshal(emptyWriter), dst.marshal(emptyWriter)))

require.Equal(t, data.issuerSet, dst.issuerSet)
require.Equal(t, data.issuer.String(), dst.issuer.String())
})

t.Run("change id", func(t *testing.T) {
var dst commonData
data.copyTo(&dst)

require.Equal(t, data.idSet, dst.idSet)
require.Equal(t, data.id.String(), dst.id.String())

dst.SetID(uuid.New())

require.Equal(t, data.idSet, dst.idSet)
require.NotEqual(t, data.id.String(), dst.id.String())
})

t.Run("overwrite id", func(t *testing.T) {
// id is not set
local := commonData{}
require.False(t, local.idSet)

// id is set
var dst commonData
dst.SetID(uuid.New())
require.True(t, dst.idSet)

// overwrite ID data
local.copyTo(&dst)

emptyWriter := func() session.TokenContext {
return &session.ContainerSessionContext{}
}
require.True(t, bytes.Equal(local.marshal(emptyWriter), dst.marshal(emptyWriter)))

require.False(t, local.idSet)
require.False(t, dst.idSet)

// update id
dst.SetID(uuid.New())

// check that affects only dst
require.False(t, local.idSet)
require.True(t, dst.idSet)
})

t.Run("change issuer", func(t *testing.T) {
var dst commonData
data.copyTo(&dst)

require.Equal(t, data.issuerSet, dst.issuerSet)
require.True(t, data.issuer.Equals(dst.issuer))

dst.SetIssuer(test.RandomSignerRFC6979(t).UserID())

require.Equal(t, data.issuerSet, dst.issuerSet)
require.False(t, data.issuer.Equals(dst.issuer))
})

t.Run("overwrite issuer", func(t *testing.T) {
var local commonData
require.False(t, local.issuerSet)

var dst commonData
dst.SetIssuer(test.RandomSignerRFC6979(t).UserID())
require.True(t, dst.issuerSet)

local.copyTo(&dst)
require.False(t, local.issuerSet)
require.False(t, dst.issuerSet)

emptyWriter := func() session.TokenContext {
return &session.ContainerSessionContext{}
}
require.True(t, bytes.Equal(local.marshal(emptyWriter), dst.marshal(emptyWriter)))

require.Equal(t, local.issuerSet, dst.issuerSet)
require.True(t, local.issuer.Equals(dst.issuer))

dst.SetIssuer(test.RandomSignerRFC6979(t).UserID())
require.False(t, local.issuerSet)
require.True(t, dst.issuerSet)

require.False(t, local.issuer.Equals(dst.issuer))
})

t.Run("change lifetime", func(t *testing.T) {
var dst commonData
data.copyTo(&dst)

require.Equal(t, data.lifetimeSet, dst.lifetimeSet)
require.Equal(t, data.iat, dst.iat)
require.Equal(t, data.nbf, dst.nbf)
require.Equal(t, data.exp, dst.exp)

dst.SetExp(100)
dst.SetIat(200)
dst.SetNbf(300)

require.Equal(t, data.lifetimeSet, dst.lifetimeSet)
require.NotEqual(t, data.iat, dst.iat)
require.NotEqual(t, data.nbf, dst.nbf)
require.NotEqual(t, data.exp, dst.exp)
})

t.Run("overwrite lifetime", func(t *testing.T) {
// lifetime is not set
local := commonData{}
require.False(t, local.lifetimeSet)

// lifetime is set
var dst commonData
dst.SetExp(100)
dst.SetIat(200)
dst.SetNbf(300)
require.True(t, dst.lifetimeSet)

local.copyTo(&dst)
require.False(t, local.lifetimeSet)
require.False(t, dst.lifetimeSet)

emptyWriter := func() session.TokenContext {
return &session.ContainerSessionContext{}
}
require.True(t, bytes.Equal(local.marshal(emptyWriter), dst.marshal(emptyWriter)))

// check both are equal
require.Equal(t, local.lifetimeSet, dst.lifetimeSet)
require.Equal(t, local.iat, dst.iat)
require.Equal(t, local.nbf, dst.nbf)
require.Equal(t, local.exp, dst.exp)

// update lifetime
dst.SetExp(100)
dst.SetIat(200)
dst.SetNbf(300)

// check that affects only dst
require.False(t, local.lifetimeSet)
require.True(t, dst.lifetimeSet)
require.NotEqual(t, local.iat, dst.iat)
require.NotEqual(t, local.nbf, dst.nbf)
require.NotEqual(t, local.exp, dst.exp)
})

t.Run("change sig", func(t *testing.T) {
var dst commonData
data.copyTo(&dst)

require.Equal(t, data.sigSet, dst.sigSet)
require.Equal(t, data.sig.GetScheme(), dst.sig.GetScheme())
require.True(t, bytes.Equal(data.sig.GetKey(), dst.sig.GetKey()))
require.True(t, bytes.Equal(data.sig.GetSign(), dst.sig.GetSign()))

dst.sig.SetKey([]byte{1, 2, 3})
dst.sig.SetScheme(100)
dst.sig.SetSign([]byte{10, 11, 12})

require.Equal(t, data.issuerSet, dst.issuerSet)
require.NotEqual(t, data.sig.GetScheme(), dst.sig.GetScheme())
require.False(t, bytes.Equal(data.sig.GetKey(), dst.sig.GetKey()))
require.False(t, bytes.Equal(data.sig.GetSign(), dst.sig.GetSign()))
})

t.Run("overwrite sig", func(t *testing.T) {
local := commonData{}
require.False(t, local.sigSet)

emptyWriter := func() session.TokenContext {
return &session.ContainerSessionContext{}
}

var dst commonData
require.NoError(t, dst.sign(signer, emptyWriter))
require.True(t, dst.sigSet)

local.copyTo(&dst)
require.False(t, local.sigSet)
require.False(t, dst.sigSet)

require.True(t, bytes.Equal(local.marshal(emptyWriter), dst.marshal(emptyWriter)))

require.NoError(t, dst.sign(signer, emptyWriter))
require.False(t, local.sigSet)
require.True(t, dst.sigSet)
})
}
15 changes: 15 additions & 0 deletions session/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,21 @@ type Container struct {
cnr cid.ID
}

// CopyTo writes deep copy of the [Container] to dst.
func (x Container) CopyTo(dst *Container) {
if dst == nil {
return
}

x.commonData.copyTo(&dst.commonData)

dst.verb = x.verb

dst.cnrSet = x.cnrSet
contID := x.cnr
dst.cnr = contID
}

// readContext is a contextReader needed for commonData methods.
func (x *Container) readContext(c session.TokenContext, checkFieldPresence bool) error {
cCnr, ok := c.(*session.ContainerSessionContext)
Expand Down
70 changes: 70 additions & 0 deletions session/container_internal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package session

import (
"bytes"
"testing"

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

func TestContainer_CopyTo(t *testing.T) {
var container Container

containerID := cidtest.ID()

container.ForVerb(VerbContainerDelete)
container.ApplyOnlyTo(containerID)

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

emptyWriter := func() session.TokenContext {
return &session.ContainerSessionContext{}
}

require.Equal(t, container, dst)
require.True(t, bytes.Equal(container.marshal(emptyWriter), dst.marshal(emptyWriter)))
})

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

require.Equal(t, container.verb, dst.verb)
require.True(t, container.cnrSet)
require.True(t, dst.cnrSet)

container.ForVerb(VerbContainerSetEACL)

require.NotEqual(t, container.verb, dst.verb)
require.True(t, container.cnrSet)
require.True(t, dst.cnrSet)
})

t.Run("overwrite container id", func(t *testing.T) {
var local Container
require.False(t, local.cnrSet)

var dst Container
dst.ApplyOnlyTo(containerID)
require.True(t, dst.cnrSet)

local.CopyTo(&dst)
emptyWriter := func() session.TokenContext {
return &session.ContainerSessionContext{}
}

require.Equal(t, local, dst)
require.True(t, bytes.Equal(local.marshal(emptyWriter), dst.marshal(emptyWriter)))

require.False(t, local.cnrSet)
require.False(t, dst.cnrSet)

dst.ApplyOnlyTo(containerID)
require.True(t, dst.cnrSet)
require.False(t, local.cnrSet)
})
}
Loading

0 comments on commit e5f5668

Please sign in to comment.