[#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 <leonard@nspcc.ru>
This commit is contained in:
Leonard Lyubich 2022-05-25 12:03:22 +03:00 committed by LeL
parent bef4618cd6
commit f0a5eb6dbc
15 changed files with 429 additions and 468 deletions

View file

@ -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
}

View file

@ -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())
})
}

View file

@ -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
}

148
object/id/address.go Normal file
View file

@ -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()
}

95
object/id/address_test.go Normal file
View file

@ -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))
}

View file

@ -1,6 +1,9 @@
/* /*
Package oid provides primitives to work with object identification in NeoFS. 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 Using package types in an application is recommended to potentially work with
different protocol versions with which these types are compatible. different protocol versions with which these types are compatible.

View file

@ -19,7 +19,7 @@ import (
// Instances can be created using built-in var declaration. // Instances can be created using built-in var declaration.
// //
// Note that direct typecast is not safe and may result in loss of compatibility: // 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 type ID [sha256.Size]byte
// ReadFromV2 reads ID from the refs.ObjectID message. Returns an error if // ReadFromV2 reads ID from the refs.ObjectID message. Returns an error if

View file

@ -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.: 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" import oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test"
dec := oidtest.ID() value := oidtest.ID()
// test the value // test the value
*/ */

View file

@ -4,10 +4,11 @@ import (
"crypto/sha256" "crypto/sha256"
"math/rand" "math/rand"
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id" oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
) )
// ID returns random object.ID. // ID returns random oid.ID.
func ID() oid.ID { func ID() oid.ID {
checksum := [sha256.Size]byte{} checksum := [sha256.Size]byte{}
@ -16,7 +17,7 @@ func ID() oid.ID {
return idWithChecksum(checksum) return idWithChecksum(checksum)
} }
// idWithChecksum returns object.ID initialized // idWithChecksum returns oid.ID initialized
// with specified checksum. // with specified checksum.
func idWithChecksum(cs [sha256.Size]byte) oid.ID { func idWithChecksum(cs [sha256.Size]byte) oid.ID {
var id oid.ID var id oid.ID
@ -24,3 +25,13 @@ func idWithChecksum(cs [sha256.Size]byte) oid.ID {
return 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
}

View file

@ -25,7 +25,6 @@ import (
"github.com/nspcc-dev/neofs-sdk-go/eacl" "github.com/nspcc-dev/neofs-sdk-go/eacl"
"github.com/nspcc-dev/neofs-sdk-go/netmap" "github.com/nspcc-dev/neofs-sdk-go/netmap"
"github.com/nspcc-dev/neofs-sdk-go/object" "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" 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/session"
"github.com/nspcc-dev/neofs-sdk-go/user" "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 { func (c *clientWrapper) objectDelete(ctx context.Context, prm PrmObjectDelete) error {
var cliPrm sdkClient.PrmObjectDelete var cliPrm sdkClient.PrmObjectDelete
cliPrm.FromContainer(prm.addr.Container())
if cnr, set := prm.addr.ContainerID(); set { cliPrm.ByID(prm.addr.Object())
cliPrm.FromContainer(cnr)
}
if obj, set := prm.addr.ObjectID(); set {
cliPrm.ByID(obj)
}
if prm.stoken != nil { if prm.stoken != nil {
cliPrm.WithinSession(*prm.stoken) 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) { func (c *clientWrapper) objectGet(ctx context.Context, prm PrmObjectGet) (*ResGetObject, error) {
var cliPrm sdkClient.PrmObjectGet var cliPrm sdkClient.PrmObjectGet
cliPrm.FromContainer(prm.addr.Container())
if cnr, set := prm.addr.ContainerID(); set { cliPrm.ByID(prm.addr.Object())
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)
}
if prm.stoken != nil { if prm.stoken != nil {
cliPrm.WithinSession(*prm.stoken) 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) { func (c *clientWrapper) objectHead(ctx context.Context, prm PrmObjectHead) (*object.Object, error) {
var cliPrm sdkClient.PrmObjectHead var cliPrm sdkClient.PrmObjectHead
cliPrm.FromContainer(prm.addr.Container())
if cnr, set := prm.addr.ContainerID(); set { cliPrm.ByID(prm.addr.Object())
cliPrm.FromContainer(cnr)
}
if obj, set := prm.addr.ObjectID(); set {
cliPrm.ByID(obj)
}
if prm.stoken != nil { if prm.stoken != nil {
cliPrm.WithinSession(*prm.stoken) 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) { func (c *clientWrapper) objectRange(ctx context.Context, prm PrmObjectRange) (*ResObjectRange, error) {
var cliPrm sdkClient.PrmObjectRange var cliPrm sdkClient.PrmObjectRange
cliPrm.FromContainer(prm.addr.Container())
cliPrm.ByID(prm.addr.Object())
cliPrm.SetOffset(prm.off) cliPrm.SetOffset(prm.off)
cliPrm.SetLength(prm.ln) 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 { if prm.stoken != nil {
cliPrm.WithinSession(*prm.stoken) cliPrm.WithinSession(*prm.stoken)
} }
@ -609,15 +575,24 @@ type clientPack struct {
type prmContext struct { type prmContext struct {
defaultSession bool defaultSession bool
verb session.ObjectVerb verb session.ObjectVerb
addr address.Address cnr cid.ID
objSet bool
obj oid.ID
} }
func (x *prmContext) useDefaultSession() { func (x *prmContext) useDefaultSession() {
x.defaultSession = true x.defaultSession = true
} }
func (x *prmContext) useAddress(addr address.Address) { func (x *prmContext) useContainer(cnr cid.ID) {
x.addr = addr 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) { func (x *prmContext) useVerb(verb session.ObjectVerb) {
@ -669,11 +644,11 @@ func (x *PrmObjectPut) SetPayload(payload io.Reader) {
type PrmObjectDelete struct { type PrmObjectDelete struct {
prmCommon prmCommon
addr address.Address addr oid.Address
} }
// SetAddress specifies NeoFS address of the object. // SetAddress specifies NeoFS address of the object.
func (x *PrmObjectDelete) SetAddress(addr address.Address) { func (x *PrmObjectDelete) SetAddress(addr oid.Address) {
x.addr = addr x.addr = addr
} }
@ -681,11 +656,11 @@ func (x *PrmObjectDelete) SetAddress(addr address.Address) {
type PrmObjectGet struct { type PrmObjectGet struct {
prmCommon prmCommon
addr address.Address addr oid.Address
} }
// SetAddress specifies NeoFS address of the object. // SetAddress specifies NeoFS address of the object.
func (x *PrmObjectGet) SetAddress(addr address.Address) { func (x *PrmObjectGet) SetAddress(addr oid.Address) {
x.addr = addr x.addr = addr
} }
@ -693,11 +668,11 @@ func (x *PrmObjectGet) SetAddress(addr address.Address) {
type PrmObjectHead struct { type PrmObjectHead struct {
prmCommon prmCommon
addr address.Address addr oid.Address
} }
// SetAddress specifies NeoFS address of the object. // SetAddress specifies NeoFS address of the object.
func (x *PrmObjectHead) SetAddress(addr address.Address) { func (x *PrmObjectHead) SetAddress(addr oid.Address) {
x.addr = addr x.addr = addr
} }
@ -705,12 +680,12 @@ func (x *PrmObjectHead) SetAddress(addr address.Address) {
type PrmObjectRange struct { type PrmObjectRange struct {
prmCommon prmCommon
addr address.Address addr oid.Address
off, ln uint64 off, ln uint64
} }
// SetAddress specifies NeoFS address of the object. // SetAddress specifies NeoFS address of the object.
func (x *PrmObjectRange) SetAddress(addr address.Address) { func (x *PrmObjectRange) SetAddress(addr oid.Address) {
x.addr = addr x.addr = addr
} }
@ -1283,7 +1258,9 @@ type callContext struct {
sessionDefault bool sessionDefault bool
sessionTarget func(session.Object) sessionTarget func(session.Object)
sessionVerb session.ObjectVerb 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 { 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 ctx.sessionDefault = cfg.stoken == nil && prmCtx.defaultSession
if ctx.sessionDefault { if ctx.sessionDefault {
ctx.sessionVerb = prmCtx.verb ctx.sessionVerb = prmCtx.verb
ctx.sessionAddr = prmCtx.addr ctx.sessionCnr = prmCtx.cnr
ctx.sessionObjSet = prmCtx.objSet
ctx.sessionObj = prmCtx.obj
} }
return err return err
@ -1333,7 +1312,11 @@ func (p *Pool) openDefaultSession(ctx *callContext) error {
} }
tok.ForVerb(ctx.sessionVerb) tok.ForVerb(ctx.sessionVerb)
tok.ApplyTo(ctx.sessionAddr) tok.BindContainer(ctx.sessionCnr)
if ctx.sessionObjSet {
tok.LimitByObject(ctx.sessionObj)
}
// sign the token // sign the token
if err := tok.Sign(*ctx.key); err != nil { 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. // PutObject writes an object through a remote server using NeoFS API protocol.
func (p *Pool) PutObject(ctx context.Context, prm PrmObjectPut) (*oid.ID, error) { func (p *Pool) PutObject(ctx context.Context, prm PrmObjectPut) (*oid.ID, error) {
var cIDp *cid.ID cnr, _ := prm.hdr.ContainerID()
if cID, set := prm.hdr.ContainerID(); set {
cIDp = &cID
}
var prmCtx prmContext var prmCtx prmContext
prmCtx.useDefaultSession() prmCtx.useDefaultSession()
prmCtx.useVerb(session.VerbObjectPut) prmCtx.useVerb(session.VerbObjectPut)
prmCtx.useAddress(*newAddressFromCnrID(cIDp)) prmCtx.useContainer(cnr)
p.fillAppropriateKey(&prm.prmCommon) p.fillAppropriateKey(&prm.prmCommon)
@ -1604,7 +1584,7 @@ func (p *Pool) SearchObjects(ctx context.Context, prm PrmObjectSearch) (*ResObje
var prmCtx prmContext var prmCtx prmContext
prmCtx.useDefaultSession() prmCtx.useDefaultSession()
prmCtx.useVerb(session.VerbObjectSearch) prmCtx.useVerb(session.VerbObjectSearch)
prmCtx.useAddress(*newAddressFromCnrID(&prm.cnrID)) prmCtx.useContainer(prm.cnrID)
p.fillAppropriateKey(&prm.prmCommon) p.fillAppropriateKey(&prm.prmCommon)
@ -1795,11 +1775,3 @@ func (p *Pool) Close() {
p.cancel() p.cancel()
<-p.closedCh <-p.closedCh
} }
func newAddressFromCnrID(cnrID *cid.ID) *address.Address {
addr := address.NewAddress()
if cnrID != nil {
addr.SetContainerID(*cnrID)
}
return addr
}

View file

@ -17,7 +17,7 @@ import (
neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa" 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/netmap"
"github.com/nspcc-dev/neofs-sdk-go/object" "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/session"
"github.com/nspcc-dev/neofs-sdk-go/user" "github.com/nspcc-dev/neofs-sdk-go/user"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -418,7 +418,7 @@ func TestSessionCache(t *testing.T) {
require.True(t, containsTokens(tokens, &st)) require.True(t, containsTokens(tokens, &st))
var prm PrmObjectGet var prm PrmObjectGet
prm.SetAddress(address.Address{}) prm.SetAddress(oid.Address{})
prm.UseSession(session.Object{}) prm.UseSession(session.Object{})
_, err = pool.GetObject(ctx, prm) _, err = pool.GetObject(ctx, prm)
@ -565,7 +565,7 @@ func TestSessionCacheWithKey(t *testing.T) {
require.True(t, containsTokens(tokens, &st)) require.True(t, containsTokens(tokens, &st))
var prm PrmObjectGet var prm PrmObjectGet
prm.SetAddress(address.Address{}) prm.SetAddress(oid.Address{})
prm.UseKey(newPrivateKey(t)) prm.UseKey(newPrivateKey(t))
_, err = pool.GetObject(ctx, prm) _, err = pool.GetObject(ctx, prm)

View file

@ -57,7 +57,7 @@ func (x *Container) ReadFromV2(m session.Token) error {
} }
c, ok := b.GetContext().(*session.ContainerSessionContext) c, ok := b.GetContext().(*session.ContainerSessionContext)
if !ok { if !ok || c == nil {
return fmt.Errorf("invalid context %T", b.GetContext()) return fmt.Errorf("invalid context %T", b.GetContext())
} }
@ -70,11 +70,7 @@ func (x *Container) ReadFromV2(m session.Token) error {
x.body = *b x.body = *b
if c != nil { x.c = *c
x.c = *c
} else {
x.c = session.ContainerSessionContext{}
}
lt := b.GetLifetime() lt := b.GetLifetime()
if lt != nil { if lt != nil {

View file

@ -9,9 +9,10 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
"github.com/nspcc-dev/neofs-api-go/v2/refs" "github.com/nspcc-dev/neofs-api-go/v2/refs"
"github.com/nspcc-dev/neofs-api-go/v2/session" "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" neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa" 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" "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) c, ok := b.GetContext().(*session.ObjectSessionContext)
if !ok { if !ok || c == nil {
return fmt.Errorf("invalid context %T", b.GetContext()) return fmt.Errorf("invalid context %T", b.GetContext())
} }
x.body = *b x.body = *b
if c != nil { x.c = *c
x.c = *c
obj := c.GetAddress() obj := c.GetAddress()
if obj != nil {
x.obj = *obj cnr := obj.GetContainerID()
} else { if cnr == nil {
x.obj = refs.Address{} return errors.New("missing bound container")
}
} else {
x.c = session.ObjectSessionContext{}
x.obj = refs.Address{}
} }
x.obj = *obj
lt := b.GetLifetime() lt := b.GetLifetime()
if lt != nil { if lt != nil {
x.lt = *lt x.lt = *lt
@ -209,23 +207,63 @@ func (x Object) VerifySignature() bool {
return x.sig.Verify(data) 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. // See also AssertContainer.
func (x *Object) ApplyTo(a address.Address) { func (x *Object) BindContainer(cnr cid.ID) {
x.obj = *a.ToV2() 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. // See also BindContainer.
func (x Object) AppliedTo(obj address.Address) bool { func (x Object) AssertContainer(cnr cid.ID) bool {
objv2 := *address.NewAddressFromV2(&x.obj) cnrV2 := x.obj.GetContainerID()
if cnrV2 == nil {
return false
}
// FIXME: use Equals method var cnr2 cid.ID
return obj.String() == objv2.String()
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. // ObjectVerb enumerates object operations.

View file

@ -9,11 +9,12 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "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" 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" neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa" neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa"
"github.com/nspcc-dev/neofs-sdk-go/object/address" oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test"
addresstest "github.com/nspcc-dev/neofs-sdk-go/object/address/test"
"github.com/nspcc-dev/neofs-sdk-go/session" "github.com/nspcc-dev/neofs-sdk-go/session"
sessiontest "github.com/nspcc-dev/neofs-sdk-go/session/test" sessiontest "github.com/nspcc-dev/neofs-sdk-go/session/test"
"github.com/nspcc-dev/neofs-sdk-go/user" "github.com/nspcc-dev/neofs-sdk-go/user"
@ -41,6 +42,14 @@ func TestObject_ReadFromV2(t *testing.T) {
var c v2session.ObjectSessionContext var c v2session.ObjectSessionContext
id := uuid.New() 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) { t.Run("protocol violation", func(t *testing.T) {
require.Error(t, x.ReadFromV2(m)) require.Error(t, x.ReadFromV2(m))
@ -54,27 +63,32 @@ func TestObject_ReadFromV2(t *testing.T) {
b.SetContext(&c) b.SetContext(&c)
require.Error(t, x.ReadFromV2(m))
c.SetAddress(&addrV2)
require.NoError(t, x.ReadFromV2(m)) require.NoError(t, x.ReadFromV2(m))
}) })
m.SetBody(&b) m.SetBody(&b)
c.SetAddress(&addrV2)
b.SetContext(&c) b.SetContext(&c)
b.SetID(id[:]) b.SetID(id[:])
t.Run("object", func(t *testing.T) { 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.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()))
obj = *addresstest.Address()
objv2 := *obj.ToV2()
c.SetAddress(&objv2)
require.NoError(t, x.ReadFromV2(m))
require.True(t, x.AppliedTo(obj))
}) })
t.Run("verb", func(t *testing.T) { 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 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) { func TestObjectExp(t *testing.T) {
@ -250,7 +279,8 @@ func TestObjectSignature(t *testing.T) {
const exp = 33 const exp = 33
id := uuid.New() id := uuid.New()
key := randPublicKey() key := randPublicKey()
obj := *addresstest.Address() cnr := cidtest.ID()
obj := oidtest.ID()
verb := session.VerbObjectDelete verb := session.VerbObjectDelete
signer := randSigner() signer := randSigner()
@ -275,8 +305,11 @@ func TestObjectSignature(t *testing.T) {
func() { x.SetAuthKey(key) }, func() { x.SetAuthKey(key) },
func() { x.SetAuthKey(randPublicKey()) }, func() { x.SetAuthKey(randPublicKey()) },
func() { x.ApplyTo(obj) }, func() { x.BindContainer(cnr) },
func() { x.ApplyTo(*addresstest.Address()) }, func() { x.BindContainer(cidtest.ID()) },
func() { x.LimitByObject(obj) },
func() { x.LimitByObject(oidtest.ID()) },
func() { x.ForVerb(verb) }, func() { x.ForVerb(verb) },
func() { x.ForVerb(verb + 1) }, func() { x.ForVerb(verb + 1) },

View file

@ -9,7 +9,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test" cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa" 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" "github.com/nspcc-dev/neofs-sdk-go/session"
) )
@ -71,8 +71,11 @@ func Object() *session.Object {
panic(err) panic(err)
} }
addr := oidtest.Address()
tok.ForVerb(session.VerbObjectPut) tok.ForVerb(session.VerbObjectPut)
tok.ApplyTo(*addresstest.Address()) tok.BindContainer(addr.Container())
tok.LimitByObject(addr.Object())
tok.SetID(uuid.New()) tok.SetID(uuid.New())
tok.SetAuthKey((*neofsecdsa.PublicKey)(&priv.PublicKey)) tok.SetAuthKey((*neofsecdsa.PublicKey)(&priv.PublicKey))
tok.SetExp(11) tok.SetExp(11)