From f0a5eb6dbc481efe1e9122ad8aaee102ce2da5a7 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Wed, 25 May 2022 12:03:22 +0300 Subject: [PATCH] [#251] object/address: Refactor and document package functionality Merge `address` package into `oid` one. Bring `session.Object` implementation into conformity with the NeoFS API protocol. Signed-off-by: Leonard Lyubich --- object/address/address.go | 192 -------------------------------- object/address/address_test.go | 129 --------------------- object/address/test/generate.go | 17 --- object/id/address.go | 148 ++++++++++++++++++++++++ object/id/address_test.go | 95 ++++++++++++++++ object/id/doc.go | 3 + object/id/id.go | 2 +- object/id/test/doc.go | 2 +- object/id/test/generate.go | 15 ++- pool/pool.go | 112 +++++++------------ pool/pool_test.go | 6 +- session/container.go | 8 +- session/object.go | 86 ++++++++++---- session/object_test.go | 75 +++++++++---- session/test/session.go | 7 +- 15 files changed, 429 insertions(+), 468 deletions(-) delete mode 100644 object/address/address.go delete mode 100644 object/address/address_test.go delete mode 100644 object/address/test/generate.go create mode 100644 object/id/address.go create mode 100644 object/id/address_test.go diff --git a/object/address/address.go b/object/address/address.go deleted file mode 100644 index 736b034..0000000 --- a/object/address/address.go +++ /dev/null @@ -1,192 +0,0 @@ -package address - -import ( - "errors" - "fmt" - "strings" - - "github.com/nspcc-dev/neofs-api-go/v2/refs" - cid "github.com/nspcc-dev/neofs-sdk-go/container/id" - oid "github.com/nspcc-dev/neofs-sdk-go/object/id" -) - -// Address represents v2-compatible object address. -type Address refs.Address - -var errInvalidAddressString = errors.New("incorrect format of the string object address") - -const ( - addressParts = 2 - addressSeparator = "/" -) - -// NewAddressFromV2 converts v2 Address message to Address. -// -// Nil refs.Address converts to nil. -func NewAddressFromV2(aV2 *refs.Address) *Address { - return (*Address)(aV2) -} - -// NewAddress creates and initializes blank Address. -// -// Works similar as NewAddressFromV2(new(Address)). -// -// Defaults: -// - cid: nil; -// - oid: nil. -func NewAddress() *Address { - return NewAddressFromV2(new(refs.Address)) -} - -// ToV2 converts Address to v2 Address message. -// -// Nil Address converts to nil. -func (a *Address) ToV2() *refs.Address { - return (*refs.Address)(a) -} - -// ContainerID returns container identifier. -func (a *Address) ContainerID() (v cid.ID, isSet bool) { - v2 := (*refs.Address)(a) - - cidV2 := v2.GetContainerID() - if cidV2 != nil { - _ = v.ReadFromV2(*cidV2) - isSet = true - } - - return -} - -// SetContainerID sets container identifier. -func (a *Address) SetContainerID(id cid.ID) { - var cidV2 refs.ContainerID - id.WriteToV2(&cidV2) - - (*refs.Address)(a).SetContainerID(&cidV2) -} - -// ObjectID returns object identifier. -func (a *Address) ObjectID() (v oid.ID, isSet bool) { - v2 := (*refs.Address)(a) - - oidV2 := v2.GetObjectID() - if oidV2 != nil { - _ = v.ReadFromV2(*oidV2) - isSet = true - } - - return -} - -// SetObjectID sets object identifier. -func (a *Address) SetObjectID(id oid.ID) { - var oidV2 refs.ObjectID - id.WriteToV2(&oidV2) - - (*refs.Address)(a).SetObjectID(&oidV2) -} - -// Parse converts base58 string representation into Address. -func (a *Address) Parse(s string) error { - var ( - err error - oid oid.ID - id cid.ID - parts = strings.Split(s, addressSeparator) - ) - - if len(parts) != addressParts { - return errInvalidAddressString - } else if err = id.DecodeString(parts[0]); err != nil { - return err - } else if err = oid.DecodeString(parts[1]); err != nil { - return err - } - - a.SetObjectID(oid) - a.SetContainerID(id) - - return nil -} - -// String returns string representation of Object.Address. -func (a *Address) String() string { - var cidStr, oidStr string - - if cID, set := a.ContainerID(); set { - cidStr = cID.String() - } - - if oID, set := a.ObjectID(); set { - oidStr = oID.String() - } - - return strings.Join([]string{cidStr, oidStr}, addressSeparator) -} - -// Marshal marshals Address into a protobuf binary form. -func (a *Address) Marshal() ([]byte, error) { - return (*refs.Address)(a).StableMarshal(nil) -} - -var errCIDNotSet = errors.New("container ID is not set") -var errOIDNotSet = errors.New("object ID is not set") - -// Unmarshal unmarshals protobuf binary representation of Address. -func (a *Address) Unmarshal(data []byte) error { - err := (*refs.Address)(a).Unmarshal(data) - if err != nil { - return err - } - - v2 := a.ToV2() - - return checkFormat(v2) -} - -// MarshalJSON encodes Address to protobuf JSON format. -func (a *Address) MarshalJSON() ([]byte, error) { - return (*refs.Address)(a).MarshalJSON() -} - -// UnmarshalJSON decodes Address from protobuf JSON format. -func (a *Address) UnmarshalJSON(data []byte) error { - v2 := (*refs.Address)(a) - - err := v2.UnmarshalJSON(data) - if err != nil { - return err - } - - return checkFormat(v2) -} - -func checkFormat(v2 *refs.Address) error { - var ( - cID cid.ID - oID oid.ID - ) - - cidV2 := v2.GetContainerID() - if cidV2 == nil { - return errCIDNotSet - } - - err := cID.ReadFromV2(*cidV2) - if err != nil { - return fmt.Errorf("could not convert V2 container ID: %w", err) - } - - oidV2 := v2.GetObjectID() - if oidV2 == nil { - return errOIDNotSet - } - - err = oID.ReadFromV2(*oidV2) - if err != nil { - return fmt.Errorf("could not convert V2 object ID: %w", err) - } - - return nil -} diff --git a/object/address/address_test.go b/object/address/address_test.go deleted file mode 100644 index 4d4a47f..0000000 --- a/object/address/address_test.go +++ /dev/null @@ -1,129 +0,0 @@ -package address - -import ( - "strings" - "testing" - - "github.com/nspcc-dev/neofs-api-go/v2/refs" - cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test" - oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test" - "github.com/stretchr/testify/require" -) - -func TestAddress_SetContainerID(t *testing.T) { - a := NewAddress() - - id := cidtest.ID() - - a.SetContainerID(id) - - cID, set := a.ContainerID() - require.True(t, set) - require.Equal(t, id, cID) -} - -func TestAddress_SetObjectID(t *testing.T) { - a := NewAddress() - - oid := oidtest.ID() - - a.SetObjectID(oid) - - oID, set := a.ObjectID() - require.True(t, set) - require.Equal(t, oid, oID) -} - -func TestAddress_Parse(t *testing.T) { - cid := cidtest.ID() - - oid := oidtest.ID() - - t.Run("should parse successful", func(t *testing.T) { - s := strings.Join([]string{cid.String(), oid.String()}, addressSeparator) - a := NewAddress() - - require.NoError(t, a.Parse(s)) - oID, set := a.ObjectID() - require.True(t, set) - require.Equal(t, oid, oID) - cID, set := a.ContainerID() - require.True(t, set) - require.Equal(t, cid, cID) - }) - - t.Run("should fail for bad address", func(t *testing.T) { - s := strings.Join([]string{cid.String()}, addressSeparator) - require.EqualError(t, NewAddress().Parse(s), errInvalidAddressString.Error()) - }) - - t.Run("should fail on container.ID", func(t *testing.T) { - s := strings.Join([]string{"1", "2"}, addressSeparator) - require.Error(t, NewAddress().Parse(s)) - }) - - t.Run("should fail on object.ID", func(t *testing.T) { - s := strings.Join([]string{cid.String(), "2"}, addressSeparator) - require.Error(t, NewAddress().Parse(s)) - }) -} - -func TestAddressEncoding(t *testing.T) { - a := NewAddress() - a.SetObjectID(oidtest.ID()) - a.SetContainerID(cidtest.ID()) - - t.Run("binary", func(t *testing.T) { - data, err := a.Marshal() - require.NoError(t, err) - - a2 := NewAddress() - require.NoError(t, a2.Unmarshal(data)) - - require.Equal(t, a, a2) - }) - - t.Run("json", func(t *testing.T) { - data, err := a.MarshalJSON() - require.NoError(t, err) - - a2 := NewAddress() - require.NoError(t, a2.UnmarshalJSON(data)) - - require.Equal(t, a, a2) - }) -} - -func TestNewAddressFromV2(t *testing.T) { - t.Run("from nil", func(t *testing.T) { - var x *refs.Address - - require.Nil(t, NewAddressFromV2(x)) - }) -} - -func TestAddress_ToV2(t *testing.T) { - t.Run("nil", func(t *testing.T) { - var x *Address - - require.Nil(t, x.ToV2()) - }) -} - -func TestNewAddress(t *testing.T) { - t.Run("default values", func(t *testing.T) { - a := NewAddress() - - // check initial values - _, set := a.ContainerID() - require.False(t, set) - _, set = a.ObjectID() - require.False(t, set) - - // convert to v2 message - aV2 := a.ToV2() - - require.Nil(t, aV2.GetContainerID()) - require.Nil(t, aV2.GetObjectID()) - }) -} diff --git a/object/address/test/generate.go b/object/address/test/generate.go deleted file mode 100644 index 224b5dd..0000000 --- a/object/address/test/generate.go +++ /dev/null @@ -1,17 +0,0 @@ -package address - -import ( - cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test" - "github.com/nspcc-dev/neofs-sdk-go/object/address" - oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test" -) - -// Address returns random object.Address. -func Address() *address.Address { - x := address.NewAddress() - - x.SetContainerID(cidtest.ID()) - x.SetObjectID(oidtest.ID()) - - return x -} diff --git a/object/id/address.go b/object/id/address.go new file mode 100644 index 0000000..4208a89 --- /dev/null +++ b/object/id/address.go @@ -0,0 +1,148 @@ +package oid + +import ( + "errors" + "fmt" + "strings" + + "github.com/nspcc-dev/neofs-api-go/v2/refs" + cid "github.com/nspcc-dev/neofs-sdk-go/container/id" +) + +// Address represents global object identifier in NeoFS network. Each object +// belongs to exactly one container and is uniquely addressed within the container. +// +// Address is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/refs.Address +// message. See ReadFromV2 / WriteToV2 methods. +// +// Instances can be created using built-in var declaration. +// +// Note that direct typecast is not safe and may result in loss of compatibility: +// _ = Address(refs.Address{}) // not recommended +type Address struct { + cnr cid.ID + + obj ID +} + +// ReadFromV2 reads Address from the refs.Address message. Returns an error if +// the message is malformed according to the NeoFS API V2 protocol. +// +// See also WriteToV2. +func (x *Address) ReadFromV2(m refs.Address) error { + cnr := m.GetContainerID() + if cnr == nil { + return errors.New("missing container ID") + } + + obj := m.GetObjectID() + if obj == nil { + return errors.New("missing object ID") + } + + err := x.cnr.ReadFromV2(*cnr) + if err != nil { + return fmt.Errorf("invalid container ID: %w", err) + } + + err = x.obj.ReadFromV2(*obj) + if err != nil { + return fmt.Errorf("invalid object ID: %w", err) + } + + return nil +} + +// WriteToV2 writes Address to the refs.Address message. +// The message must not be nil. +// +// See also ReadFromV2. +func (x Address) WriteToV2(m *refs.Address) { + var obj refs.ObjectID + x.obj.WriteToV2(&obj) + + var cnr refs.ContainerID + x.cnr.WriteToV2(&cnr) + + m.SetObjectID(&obj) + m.SetContainerID(&cnr) +} + +// Container sets unique identifier of the NeoFS object container. +// +// Zero Address has zero container ID, which is incorrect according to NeoFS +// API protocol. +// +// See also SetContainer. +func (x Address) Container() cid.ID { + return x.cnr +} + +// SetContainer sets unique identifier of the NeoFS object container. +// +// See also Container. +func (x *Address) SetContainer(id cid.ID) { + x.cnr = id +} + +// Object returns unique identifier of the object in the container +// identified by Container(). +// +// Zero Address has zero object ID, which is incorrect according to NeoFS +// API protocol. +// +// See also SetObject. +func (x Address) Object() ID { + return x.obj +} + +// SetObject sets unique identifier of the object in the container +// identified by Container(). +// +// See also Object. +func (x *Address) SetObject(id ID) { + x.obj = id +} + +// delimiter of container and object IDs in Address protocol string. +const idDelimiter = "/" + +// EncodeToString encodes Address into NeoFS API protocol string: concatenation +// of the string-encoded container and object IDs delimited by a slash. +// +// See also DecodeString. +func (x Address) EncodeToString() string { + return x.cnr.EncodeToString() + "/" + x.obj.EncodeToString() +} + +// DecodeString decodes string into Address according to NeoFS API protocol. Returns +// an error if s is malformed. +// +// See also DecodeString. +func (x *Address) DecodeString(s string) error { + indDelimiter := strings.Index(s, idDelimiter) + if indDelimiter < 0 { + return errors.New("missing delimiter") + } + + err := x.cnr.DecodeString(s[:indDelimiter]) + if err != nil { + return fmt.Errorf("decode container string: %w", err) + } + + err = x.obj.DecodeString(s[indDelimiter+1:]) + if err != nil { + return fmt.Errorf("decode object string: %w", err) + } + + return nil +} + +// String implements fmt.Stringer. +// +// String is designed to be human-readable, and its format MAY differ between +// SDK versions. String MAY return same result as EncodeToString. String MUST NOT +// be used to encode Address into NeoFS protocol string. +func (x Address) String() string { + return x.EncodeToString() +} diff --git a/object/id/address_test.go b/object/id/address_test.go new file mode 100644 index 0000000..322707a --- /dev/null +++ b/object/id/address_test.go @@ -0,0 +1,95 @@ +package oid_test + +import ( + "testing" + + "github.com/nspcc-dev/neofs-api-go/v2/refs" + cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test" + oid "github.com/nspcc-dev/neofs-sdk-go/object/id" + oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test" + "github.com/stretchr/testify/require" +) + +func TestAddress_SetContainer(t *testing.T) { + var x oid.Address + + require.Zero(t, x.Container()) + + cnr := cidtest.ID() + + x.SetContainer(cnr) + require.Equal(t, cnr, x.Container()) +} + +func TestAddress_SetObject(t *testing.T) { + var x oid.Address + + require.Zero(t, x.Object()) + + obj := oidtest.ID() + + x.SetObject(obj) + require.Equal(t, obj, x.Object()) +} + +func TestAddress_ReadFromV2(t *testing.T) { + var x oid.Address + var xV2 refs.Address + + require.Error(t, x.ReadFromV2(xV2)) + + var cnrV2 refs.ContainerID + xV2.SetContainerID(&cnrV2) + + require.Error(t, x.ReadFromV2(xV2)) + + cnr := cidtest.ID() + cnr.WriteToV2(&cnrV2) + + require.Error(t, x.ReadFromV2(xV2)) + + var objV2 refs.ObjectID + xV2.SetObjectID(&objV2) + + require.Error(t, x.ReadFromV2(xV2)) + + obj := oidtest.ID() + obj.WriteToV2(&objV2) + + require.NoError(t, x.ReadFromV2(xV2)) + require.Equal(t, cnr, x.Container()) + require.Equal(t, obj, x.Object()) + + var xV2To refs.Address + x.WriteToV2(&xV2To) + + require.Equal(t, xV2, xV2To) +} + +func TestAddress_DecodeString(t *testing.T) { + var x, x2 oid.Address + + require.NoError(t, x2.DecodeString(x.EncodeToString())) + require.Equal(t, x, x2) + + cnr := cidtest.ID() + obj := oidtest.ID() + + x.SetContainer(cnr) + x.SetObject(obj) + + require.NoError(t, x2.DecodeString(x.EncodeToString())) + require.Equal(t, x, x2) + + strCnr := cnr.EncodeToString() + strObj := obj.EncodeToString() + + require.Error(t, x2.DecodeString("")) + require.Error(t, x2.DecodeString("/")) + require.Error(t, x2.DecodeString(strCnr)) + require.Error(t, x2.DecodeString(strCnr+"/")) + require.Error(t, x2.DecodeString("/"+strCnr)) + require.Error(t, x2.DecodeString(strCnr+strObj)) + require.Error(t, x2.DecodeString(strCnr+"\\"+strObj)) + require.NoError(t, x2.DecodeString(strCnr+"/"+strObj)) +} diff --git a/object/id/doc.go b/object/id/doc.go index 89f1585..0117742 100644 --- a/object/id/doc.go +++ b/object/id/doc.go @@ -1,6 +1,9 @@ /* Package oid provides primitives to work with object identification in NeoFS. +Address type is used for global object identity inside the NeoFS network, +while ID represents identity within a fixed container. + Using package types in an application is recommended to potentially work with different protocol versions with which these types are compatible. diff --git a/object/id/id.go b/object/id/id.go index 7fbd8b6..f9ccbe9 100644 --- a/object/id/id.go +++ b/object/id/id.go @@ -19,7 +19,7 @@ import ( // Instances can be created using built-in var declaration. // // Note that direct typecast is not safe and may result in loss of compatibility: -// _ = ObjectID([32]byte{}) // not recommended +// _ = ID([32]byte{}) // not recommended type ID [sha256.Size]byte // ReadFromV2 reads ID from the refs.ObjectID message. Returns an error if diff --git a/object/id/test/doc.go b/object/id/test/doc.go index 22d5e2e..4aad5e7 100644 --- a/object/id/test/doc.go +++ b/object/id/test/doc.go @@ -6,7 +6,7 @@ Note that importing the package into source files is highly discouraged. Random instance generation functions can be useful when testing expects any value, e.g.: import oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test" - dec := oidtest.ID() + value := oidtest.ID() // test the value */ diff --git a/object/id/test/generate.go b/object/id/test/generate.go index 8bb8032..8a7e035 100644 --- a/object/id/test/generate.go +++ b/object/id/test/generate.go @@ -4,10 +4,11 @@ import ( "crypto/sha256" "math/rand" + cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test" oid "github.com/nspcc-dev/neofs-sdk-go/object/id" ) -// ID returns random object.ID. +// ID returns random oid.ID. func ID() oid.ID { checksum := [sha256.Size]byte{} @@ -16,7 +17,7 @@ func ID() oid.ID { return idWithChecksum(checksum) } -// idWithChecksum returns object.ID initialized +// idWithChecksum returns oid.ID initialized // with specified checksum. func idWithChecksum(cs [sha256.Size]byte) oid.ID { var id oid.ID @@ -24,3 +25,13 @@ func idWithChecksum(cs [sha256.Size]byte) oid.ID { return id } + +// Address returns random object.Address. +func Address() oid.Address { + var x oid.Address + + x.SetContainer(cidtest.ID()) + x.SetObject(ID()) + + return x +} diff --git a/pool/pool.go b/pool/pool.go index bcfda9a..10c628d 100644 --- a/pool/pool.go +++ b/pool/pool.go @@ -25,7 +25,6 @@ import ( "github.com/nspcc-dev/neofs-sdk-go/eacl" "github.com/nspcc-dev/neofs-sdk-go/netmap" "github.com/nspcc-dev/neofs-sdk-go/object" - "github.com/nspcc-dev/neofs-sdk-go/object/address" oid "github.com/nspcc-dev/neofs-sdk-go/object/id" "github.com/nspcc-dev/neofs-sdk-go/session" "github.com/nspcc-dev/neofs-sdk-go/user" @@ -293,14 +292,8 @@ func (c *clientWrapper) objectPut(ctx context.Context, prm PrmObjectPut) (*oid.I func (c *clientWrapper) objectDelete(ctx context.Context, prm PrmObjectDelete) error { var cliPrm sdkClient.PrmObjectDelete - - if cnr, set := prm.addr.ContainerID(); set { - cliPrm.FromContainer(cnr) - } - - if obj, set := prm.addr.ObjectID(); set { - cliPrm.ByID(obj) - } + cliPrm.FromContainer(prm.addr.Container()) + cliPrm.ByID(prm.addr.Object()) if prm.stoken != nil { cliPrm.WithinSession(*prm.stoken) @@ -319,22 +312,8 @@ func (c *clientWrapper) objectDelete(ctx context.Context, prm PrmObjectDelete) e func (c *clientWrapper) objectGet(ctx context.Context, prm PrmObjectGet) (*ResGetObject, error) { var cliPrm sdkClient.PrmObjectGet - - if cnr, set := prm.addr.ContainerID(); set { - cliPrm.FromContainer(cnr) - } - - if obj, set := prm.addr.ObjectID(); set { - cliPrm.ByID(obj) - } - - if cnr, set := prm.addr.ContainerID(); set { - cliPrm.FromContainer(cnr) - } - - if obj, set := prm.addr.ObjectID(); set { - cliPrm.ByID(obj) - } + cliPrm.FromContainer(prm.addr.Container()) + cliPrm.ByID(prm.addr.Object()) if prm.stoken != nil { cliPrm.WithinSession(*prm.stoken) @@ -367,14 +346,8 @@ func (c *clientWrapper) objectGet(ctx context.Context, prm PrmObjectGet) (*ResGe func (c *clientWrapper) objectHead(ctx context.Context, prm PrmObjectHead) (*object.Object, error) { var cliPrm sdkClient.PrmObjectHead - - if cnr, set := prm.addr.ContainerID(); set { - cliPrm.FromContainer(cnr) - } - - if obj, set := prm.addr.ObjectID(); set { - cliPrm.ByID(obj) - } + cliPrm.FromContainer(prm.addr.Container()) + cliPrm.ByID(prm.addr.Object()) if prm.stoken != nil { cliPrm.WithinSession(*prm.stoken) @@ -403,18 +376,11 @@ func (c *clientWrapper) objectHead(ctx context.Context, prm PrmObjectHead) (*obj func (c *clientWrapper) objectRange(ctx context.Context, prm PrmObjectRange) (*ResObjectRange, error) { var cliPrm sdkClient.PrmObjectRange - + cliPrm.FromContainer(prm.addr.Container()) + cliPrm.ByID(prm.addr.Object()) cliPrm.SetOffset(prm.off) cliPrm.SetLength(prm.ln) - if cnr, set := prm.addr.ContainerID(); set { - cliPrm.FromContainer(cnr) - } - - if obj, set := prm.addr.ObjectID(); set { - cliPrm.ByID(obj) - } - if prm.stoken != nil { cliPrm.WithinSession(*prm.stoken) } @@ -609,15 +575,24 @@ type clientPack struct { type prmContext struct { defaultSession bool verb session.ObjectVerb - addr address.Address + cnr cid.ID + + objSet bool + obj oid.ID } func (x *prmContext) useDefaultSession() { x.defaultSession = true } -func (x *prmContext) useAddress(addr address.Address) { - x.addr = addr +func (x *prmContext) useContainer(cnr cid.ID) { + x.cnr = cnr +} + +func (x *prmContext) useAddress(addr oid.Address) { + x.cnr = addr.Container() + x.obj = addr.Object() + x.objSet = true } func (x *prmContext) useVerb(verb session.ObjectVerb) { @@ -669,11 +644,11 @@ func (x *PrmObjectPut) SetPayload(payload io.Reader) { type PrmObjectDelete struct { prmCommon - addr address.Address + addr oid.Address } // SetAddress specifies NeoFS address of the object. -func (x *PrmObjectDelete) SetAddress(addr address.Address) { +func (x *PrmObjectDelete) SetAddress(addr oid.Address) { x.addr = addr } @@ -681,11 +656,11 @@ func (x *PrmObjectDelete) SetAddress(addr address.Address) { type PrmObjectGet struct { prmCommon - addr address.Address + addr oid.Address } // SetAddress specifies NeoFS address of the object. -func (x *PrmObjectGet) SetAddress(addr address.Address) { +func (x *PrmObjectGet) SetAddress(addr oid.Address) { x.addr = addr } @@ -693,11 +668,11 @@ func (x *PrmObjectGet) SetAddress(addr address.Address) { type PrmObjectHead struct { prmCommon - addr address.Address + addr oid.Address } // SetAddress specifies NeoFS address of the object. -func (x *PrmObjectHead) SetAddress(addr address.Address) { +func (x *PrmObjectHead) SetAddress(addr oid.Address) { x.addr = addr } @@ -705,12 +680,12 @@ func (x *PrmObjectHead) SetAddress(addr address.Address) { type PrmObjectRange struct { prmCommon - addr address.Address + addr oid.Address off, ln uint64 } // SetAddress specifies NeoFS address of the object. -func (x *PrmObjectRange) SetAddress(addr address.Address) { +func (x *PrmObjectRange) SetAddress(addr oid.Address) { x.addr = addr } @@ -1283,7 +1258,9 @@ type callContext struct { sessionDefault bool sessionTarget func(session.Object) sessionVerb session.ObjectVerb - sessionAddr address.Address + sessionCnr cid.ID + sessionObjSet bool + sessionObj oid.ID } func (p *Pool) initCallContext(ctx *callContext, cfg prmCommon, prmCtx prmContext) error { @@ -1309,7 +1286,9 @@ func (p *Pool) initCallContext(ctx *callContext, cfg prmCommon, prmCtx prmContex ctx.sessionDefault = cfg.stoken == nil && prmCtx.defaultSession if ctx.sessionDefault { ctx.sessionVerb = prmCtx.verb - ctx.sessionAddr = prmCtx.addr + ctx.sessionCnr = prmCtx.cnr + ctx.sessionObjSet = prmCtx.objSet + ctx.sessionObj = prmCtx.obj } return err @@ -1333,7 +1312,11 @@ func (p *Pool) openDefaultSession(ctx *callContext) error { } tok.ForVerb(ctx.sessionVerb) - tok.ApplyTo(ctx.sessionAddr) + tok.BindContainer(ctx.sessionCnr) + + if ctx.sessionObjSet { + tok.LimitByObject(ctx.sessionObj) + } // sign the token if err := tok.Sign(*ctx.key); err != nil { @@ -1372,15 +1355,12 @@ func (p *Pool) fillAppropriateKey(prm *prmCommon) { // PutObject writes an object through a remote server using NeoFS API protocol. func (p *Pool) PutObject(ctx context.Context, prm PrmObjectPut) (*oid.ID, error) { - var cIDp *cid.ID - if cID, set := prm.hdr.ContainerID(); set { - cIDp = &cID - } + cnr, _ := prm.hdr.ContainerID() var prmCtx prmContext prmCtx.useDefaultSession() prmCtx.useVerb(session.VerbObjectPut) - prmCtx.useAddress(*newAddressFromCnrID(cIDp)) + prmCtx.useContainer(cnr) p.fillAppropriateKey(&prm.prmCommon) @@ -1604,7 +1584,7 @@ func (p *Pool) SearchObjects(ctx context.Context, prm PrmObjectSearch) (*ResObje var prmCtx prmContext prmCtx.useDefaultSession() prmCtx.useVerb(session.VerbObjectSearch) - prmCtx.useAddress(*newAddressFromCnrID(&prm.cnrID)) + prmCtx.useContainer(prm.cnrID) p.fillAppropriateKey(&prm.prmCommon) @@ -1795,11 +1775,3 @@ func (p *Pool) Close() { p.cancel() <-p.closedCh } - -func newAddressFromCnrID(cnrID *cid.ID) *address.Address { - addr := address.NewAddress() - if cnrID != nil { - addr.SetContainerID(*cnrID) - } - return addr -} diff --git a/pool/pool_test.go b/pool/pool_test.go index d76d816..49dcbb5 100644 --- a/pool/pool_test.go +++ b/pool/pool_test.go @@ -17,7 +17,7 @@ import ( neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa" "github.com/nspcc-dev/neofs-sdk-go/netmap" "github.com/nspcc-dev/neofs-sdk-go/object" - "github.com/nspcc-dev/neofs-sdk-go/object/address" + oid "github.com/nspcc-dev/neofs-sdk-go/object/id" "github.com/nspcc-dev/neofs-sdk-go/session" "github.com/nspcc-dev/neofs-sdk-go/user" "github.com/stretchr/testify/require" @@ -418,7 +418,7 @@ func TestSessionCache(t *testing.T) { require.True(t, containsTokens(tokens, &st)) var prm PrmObjectGet - prm.SetAddress(address.Address{}) + prm.SetAddress(oid.Address{}) prm.UseSession(session.Object{}) _, err = pool.GetObject(ctx, prm) @@ -565,7 +565,7 @@ func TestSessionCacheWithKey(t *testing.T) { require.True(t, containsTokens(tokens, &st)) var prm PrmObjectGet - prm.SetAddress(address.Address{}) + prm.SetAddress(oid.Address{}) prm.UseKey(newPrivateKey(t)) _, err = pool.GetObject(ctx, prm) diff --git a/session/container.go b/session/container.go index 747afc3..88f3062 100644 --- a/session/container.go +++ b/session/container.go @@ -57,7 +57,7 @@ func (x *Container) ReadFromV2(m session.Token) error { } c, ok := b.GetContext().(*session.ContainerSessionContext) - if !ok { + if !ok || c == nil { return fmt.Errorf("invalid context %T", b.GetContext()) } @@ -70,11 +70,7 @@ func (x *Container) ReadFromV2(m session.Token) error { x.body = *b - if c != nil { - x.c = *c - } else { - x.c = session.ContainerSessionContext{} - } + x.c = *c lt := b.GetLifetime() if lt != nil { diff --git a/session/object.go b/session/object.go index d8a7fa1..5c5a8a6 100644 --- a/session/object.go +++ b/session/object.go @@ -9,9 +9,10 @@ import ( "github.com/google/uuid" "github.com/nspcc-dev/neofs-api-go/v2/refs" "github.com/nspcc-dev/neofs-api-go/v2/session" + cid "github.com/nspcc-dev/neofs-sdk-go/container/id" neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto" neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa" - "github.com/nspcc-dev/neofs-sdk-go/object/address" + oid "github.com/nspcc-dev/neofs-sdk-go/object/id" "github.com/nspcc-dev/neofs-sdk-go/user" ) @@ -57,26 +58,23 @@ func (x *Object) ReadFromV2(m session.Token) error { } c, ok := b.GetContext().(*session.ObjectSessionContext) - if !ok { + if !ok || c == nil { return fmt.Errorf("invalid context %T", b.GetContext()) } x.body = *b - if c != nil { - x.c = *c + x.c = *c - obj := c.GetAddress() - if obj != nil { - x.obj = *obj - } else { - x.obj = refs.Address{} - } - } else { - x.c = session.ObjectSessionContext{} - x.obj = refs.Address{} + obj := c.GetAddress() + + cnr := obj.GetContainerID() + if cnr == nil { + return errors.New("missing bound container") } + x.obj = *obj + lt := b.GetLifetime() if lt != nil { x.lt = *lt @@ -209,23 +207,63 @@ func (x Object) VerifySignature() bool { return x.sig.Verify(data) } -// ApplyTo limits session scope to a given author object. +// BindContainer binds the Object session to a given container. Each session +// MUST be bound to exactly one container. // -// See also AppliedTo. -func (x *Object) ApplyTo(a address.Address) { - x.obj = *a.ToV2() +// See also AssertContainer. +func (x *Object) BindContainer(cnr cid.ID) { + var cnrV2 refs.ContainerID + cnr.WriteToV2(&cnrV2) + + x.obj.SetContainerID(&cnrV2) } -// AppliedTo checks if session scope is limited by a given object. +// AssertContainer checks if Object session bound to a given container. // -// Zero Object isn't applied to any author's object. +// Zero Object isn't bound to any container which is incorrect according to +// NeoFS API protocol. // -// See also ApplyTo. -func (x Object) AppliedTo(obj address.Address) bool { - objv2 := *address.NewAddressFromV2(&x.obj) +// See also BindContainer. +func (x Object) AssertContainer(cnr cid.ID) bool { + cnrV2 := x.obj.GetContainerID() + if cnrV2 == nil { + return false + } - // FIXME: use Equals method - return obj.String() == objv2.String() + var cnr2 cid.ID + + err := cnr2.ReadFromV2(*cnrV2) + + return err == nil && cnr2.Equals(cnr) +} + +// LimitByObject limits session scope to a given object from the container +// to which Object session is bound. +// +// See also AssertObject. +func (x *Object) LimitByObject(obj oid.ID) { + var objV2 refs.ObjectID + obj.WriteToV2(&objV2) + + x.obj.SetObjectID(&objV2) +} + +// AssertObject checks if Object session is applied to a given object. +// +// Zero Object is applied to all objects in the container. +// +// See also LimitByObject. +func (x Object) AssertObject(obj oid.ID) bool { + objV2 := x.obj.GetObjectID() + if objV2 == nil { + return true + } + + var obj2 oid.ID + + err := obj2.ReadFromV2(*objV2) + + return err == nil && obj2.Equals(obj) } // ObjectVerb enumerates object operations. diff --git a/session/object_test.go b/session/object_test.go index c702c1c..00cc0a8 100644 --- a/session/object_test.go +++ b/session/object_test.go @@ -9,11 +9,12 @@ import ( "github.com/google/uuid" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/nspcc-dev/neofs-api-go/v2/refs" v2session "github.com/nspcc-dev/neofs-api-go/v2/session" + cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test" neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto" neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa" - "github.com/nspcc-dev/neofs-sdk-go/object/address" - addresstest "github.com/nspcc-dev/neofs-sdk-go/object/address/test" + oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test" "github.com/nspcc-dev/neofs-sdk-go/session" sessiontest "github.com/nspcc-dev/neofs-sdk-go/session/test" "github.com/nspcc-dev/neofs-sdk-go/user" @@ -41,6 +42,14 @@ func TestObject_ReadFromV2(t *testing.T) { var c v2session.ObjectSessionContext id := uuid.New() + cnr := cidtest.ID() + + var cnrV2 refs.ContainerID + cnr.WriteToV2(&cnrV2) + + var addrV2 refs.Address + addrV2.SetContainerID(&cnrV2) + t.Run("protocol violation", func(t *testing.T) { require.Error(t, x.ReadFromV2(m)) @@ -54,27 +63,32 @@ func TestObject_ReadFromV2(t *testing.T) { b.SetContext(&c) + require.Error(t, x.ReadFromV2(m)) + + c.SetAddress(&addrV2) + require.NoError(t, x.ReadFromV2(m)) }) m.SetBody(&b) + c.SetAddress(&addrV2) b.SetContext(&c) b.SetID(id[:]) t.Run("object", func(t *testing.T) { - var obj address.Address + require.NoError(t, x.ReadFromV2(m)) + require.True(t, x.AssertContainer(cnr)) + + obj := oidtest.Address() + + var objV2 refs.Address + obj.WriteToV2(&objV2) + + c.SetAddress(&objV2) require.NoError(t, x.ReadFromV2(m)) - require.True(t, x.AppliedTo(obj)) - - obj = *addresstest.Address() - - objv2 := *obj.ToV2() - - c.SetAddress(&objv2) - - require.NoError(t, x.ReadFromV2(m)) - require.True(t, x.AppliedTo(obj)) + require.True(t, x.AssertContainer(obj.Container())) + require.True(t, x.AssertObject(obj.Object())) }) t.Run("verb", func(t *testing.T) { @@ -157,16 +171,31 @@ func TestEncodingObject(t *testing.T) { }) } -func TestObjectAppliedTo(t *testing.T) { +func TestObject_BindContainer(t *testing.T) { var x session.Object - a := *addresstest.Address() + cnr := cidtest.ID() - require.False(t, x.AppliedTo(a)) + require.False(t, x.AssertContainer(cnr)) - x.ApplyTo(a) + x.BindContainer(cnr) - require.True(t, x.AppliedTo(a)) + require.True(t, x.AssertContainer(cnr)) +} + +func TestObject_LimitByObject(t *testing.T) { + var x session.Object + + obj := oidtest.ID() + obj2 := oidtest.ID() + + require.True(t, x.AssertObject(obj)) + require.True(t, x.AssertObject(obj2)) + + x.LimitByObject(obj) + + require.True(t, x.AssertObject(obj)) + require.False(t, x.AssertObject(obj2)) } func TestObjectExp(t *testing.T) { @@ -250,7 +279,8 @@ func TestObjectSignature(t *testing.T) { const exp = 33 id := uuid.New() key := randPublicKey() - obj := *addresstest.Address() + cnr := cidtest.ID() + obj := oidtest.ID() verb := session.VerbObjectDelete signer := randSigner() @@ -275,8 +305,11 @@ func TestObjectSignature(t *testing.T) { func() { x.SetAuthKey(key) }, func() { x.SetAuthKey(randPublicKey()) }, - func() { x.ApplyTo(obj) }, - func() { x.ApplyTo(*addresstest.Address()) }, + func() { x.BindContainer(cnr) }, + func() { x.BindContainer(cidtest.ID()) }, + + func() { x.LimitByObject(obj) }, + func() { x.LimitByObject(oidtest.ID()) }, func() { x.ForVerb(verb) }, func() { x.ForVerb(verb + 1) }, diff --git a/session/test/session.go b/session/test/session.go index fb2031f..fb5a8d8 100644 --- a/session/test/session.go +++ b/session/test/session.go @@ -9,7 +9,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/keys" cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test" neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa" - addresstest "github.com/nspcc-dev/neofs-sdk-go/object/address/test" + oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test" "github.com/nspcc-dev/neofs-sdk-go/session" ) @@ -71,8 +71,11 @@ func Object() *session.Object { panic(err) } + addr := oidtest.Address() + tok.ForVerb(session.VerbObjectPut) - tok.ApplyTo(*addresstest.Address()) + tok.BindContainer(addr.Container()) + tok.LimitByObject(addr.Object()) tok.SetID(uuid.New()) tok.SetAuthKey((*neofsecdsa.PublicKey)(&priv.PublicKey)) tok.SetExp(11)