forked from TrueCloudLab/frostfs-sdk-go
[#261] session: Share common code between Container
and Object
`Container` and `Object` types are transmitted in single `session.Token` message. They differ only by session context. Share common parts of the message in `commonData` struct. Embed struct into `Container` and `Object`. Make `ReadFromV2` methods to check protocol compliance. Make `Unmarshal`/`UmarshalJSON` to check field format in case of presence only. Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
This commit is contained in:
parent
d3b998d672
commit
0bb40b3245
5 changed files with 1361 additions and 866 deletions
334
session/common.go
Normal file
334
session/common.go
Normal file
|
@ -0,0 +1,334 @@
|
||||||
|
package session
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
||||||
|
"github.com/nspcc-dev/neofs-api-go/v2/session"
|
||||||
|
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/user"
|
||||||
|
)
|
||||||
|
|
||||||
|
type commonData struct {
|
||||||
|
idSet bool
|
||||||
|
id uuid.UUID
|
||||||
|
|
||||||
|
issuerSet bool
|
||||||
|
issuer user.ID
|
||||||
|
|
||||||
|
lifetimeSet bool
|
||||||
|
iat, nbf, exp uint64
|
||||||
|
|
||||||
|
authKey []byte
|
||||||
|
|
||||||
|
sigSet bool
|
||||||
|
sig refs.Signature
|
||||||
|
}
|
||||||
|
|
||||||
|
type contextReader func(session.TokenContext, bool) error
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
// Calls contextReader if session context is set. Passes checkFieldPresence into contextReader.
|
||||||
|
func (x *commonData) readFromV2(m session.Token, checkFieldPresence bool, r contextReader) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
body := m.GetBody()
|
||||||
|
if checkFieldPresence && body == nil {
|
||||||
|
return errors.New("missing token body")
|
||||||
|
}
|
||||||
|
|
||||||
|
binID := body.GetID()
|
||||||
|
if x.idSet = len(binID) > 0; x.idSet {
|
||||||
|
err = x.id.UnmarshalBinary(binID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid session ID: %w", err)
|
||||||
|
} else if ver := x.id.Version(); ver != 4 {
|
||||||
|
return fmt.Errorf("invalid session UUID version %d", ver)
|
||||||
|
}
|
||||||
|
} else if checkFieldPresence {
|
||||||
|
return errors.New("missing session ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
issuer := body.GetOwnerID()
|
||||||
|
if x.issuerSet = issuer != nil; x.issuerSet {
|
||||||
|
err = x.issuer.ReadFromV2(*issuer)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid session issuer: %w", err)
|
||||||
|
}
|
||||||
|
} else if checkFieldPresence {
|
||||||
|
return errors.New("missing session issuer")
|
||||||
|
}
|
||||||
|
|
||||||
|
lifetime := body.GetLifetime()
|
||||||
|
if x.lifetimeSet = lifetime != nil; x.lifetimeSet {
|
||||||
|
x.iat = lifetime.GetIat()
|
||||||
|
x.nbf = lifetime.GetNbf()
|
||||||
|
x.exp = lifetime.GetExp()
|
||||||
|
} else if checkFieldPresence {
|
||||||
|
return errors.New("missing token lifetime")
|
||||||
|
}
|
||||||
|
|
||||||
|
x.authKey = body.GetSessionKey()
|
||||||
|
if checkFieldPresence && len(x.authKey) == 0 {
|
||||||
|
return errors.New("missing session public key")
|
||||||
|
}
|
||||||
|
|
||||||
|
c := body.GetContext()
|
||||||
|
if c != nil {
|
||||||
|
err = r(c, checkFieldPresence)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid context: %w", err)
|
||||||
|
}
|
||||||
|
} else if checkFieldPresence {
|
||||||
|
return errors.New("missing session context")
|
||||||
|
}
|
||||||
|
|
||||||
|
sig := m.GetSignature()
|
||||||
|
if x.sigSet = sig != nil; sig != nil {
|
||||||
|
x.sig = *sig
|
||||||
|
} else if checkFieldPresence {
|
||||||
|
return errors.New("missing body signature")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type contextWriter func() session.TokenContext
|
||||||
|
|
||||||
|
func (x commonData) fillBody(w contextWriter) *session.TokenBody {
|
||||||
|
var body session.TokenBody
|
||||||
|
|
||||||
|
if x.idSet {
|
||||||
|
binID, err := x.id.MarshalBinary()
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("unexpected error from UUID.MarshalBinary: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
body.SetID(binID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if x.issuerSet {
|
||||||
|
var issuer refs.OwnerID
|
||||||
|
x.issuer.WriteToV2(&issuer)
|
||||||
|
|
||||||
|
body.SetOwnerID(&issuer)
|
||||||
|
}
|
||||||
|
|
||||||
|
if x.lifetimeSet {
|
||||||
|
var lifetime session.TokenLifetime
|
||||||
|
lifetime.SetIat(x.iat)
|
||||||
|
lifetime.SetNbf(x.nbf)
|
||||||
|
lifetime.SetExp(x.exp)
|
||||||
|
|
||||||
|
body.SetLifetime(&lifetime)
|
||||||
|
}
|
||||||
|
|
||||||
|
body.SetSessionKey(x.authKey)
|
||||||
|
|
||||||
|
body.SetContext(w())
|
||||||
|
|
||||||
|
return &body
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x commonData) writeToV2(m *session.Token, w contextWriter) {
|
||||||
|
body := x.fillBody(w)
|
||||||
|
|
||||||
|
m.SetBody(body)
|
||||||
|
|
||||||
|
var sig *refs.Signature
|
||||||
|
|
||||||
|
if x.sigSet {
|
||||||
|
sig = &x.sig
|
||||||
|
}
|
||||||
|
|
||||||
|
m.SetSignature(sig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x commonData) signedData(w contextWriter) []byte {
|
||||||
|
return x.fillBody(w).StableMarshal(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *commonData) sign(key ecdsa.PrivateKey, w contextWriter) error {
|
||||||
|
user.IDFromKey(&x.issuer, key.PublicKey)
|
||||||
|
x.issuerSet = true
|
||||||
|
|
||||||
|
var sig neofscrypto.Signature
|
||||||
|
|
||||||
|
err := sig.Calculate(neofsecdsa.Signer(key), x.signedData(w))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sig.WriteToV2(&x.sig)
|
||||||
|
x.sigSet = true
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x commonData) verifySignature(w contextWriter) bool {
|
||||||
|
if !x.sigSet {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var sig neofscrypto.Signature
|
||||||
|
sig.ReadFromV2(x.sig)
|
||||||
|
|
||||||
|
// TODO: (#233) check owner<->key relation
|
||||||
|
return sig.Verify(x.signedData(w))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x commonData) marshal(w contextWriter) []byte {
|
||||||
|
var m session.Token
|
||||||
|
x.writeToV2(&m, w)
|
||||||
|
|
||||||
|
return m.StableMarshal(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *commonData) unmarshal(data []byte, r contextReader) error {
|
||||||
|
var m session.Token
|
||||||
|
|
||||||
|
err := m.Unmarshal(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return x.readFromV2(m, false, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x commonData) marshalJSON(w contextWriter) ([]byte, error) {
|
||||||
|
var m session.Token
|
||||||
|
x.writeToV2(&m, w)
|
||||||
|
|
||||||
|
return m.MarshalJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *commonData) unmarshalJSON(data []byte, r contextReader) error {
|
||||||
|
var m session.Token
|
||||||
|
|
||||||
|
err := m.UnmarshalJSON(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return x.readFromV2(m, false, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetExp sets "exp" (expiration time) claim which identifies the expiration time
|
||||||
|
// (in NeoFS epochs) on or after which the session MUST NOT be accepted for
|
||||||
|
// processing. The processing of the "exp" claim requires that the current
|
||||||
|
// epoch MUST be before the expiration epoch listed in the "exp" claim.
|
||||||
|
//
|
||||||
|
// Naming is inspired by https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.4.
|
||||||
|
//
|
||||||
|
// See also ExpiredAt.
|
||||||
|
func (x *commonData) SetExp(exp uint64) {
|
||||||
|
x.exp = exp
|
||||||
|
x.lifetimeSet = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNbf sets "nbf" (not before) claim which identifies the time (in NeoFS
|
||||||
|
// epochs) before which the session MUST NOT be accepted for processing.
|
||||||
|
// The processing of the "nbf" claim requires that the current date/time MUST be
|
||||||
|
// after or equal to the not-before date/time listed in the "nbf" claim.
|
||||||
|
//
|
||||||
|
// Naming is inspired by https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.5.
|
||||||
|
//
|
||||||
|
// See also InvalidAt.
|
||||||
|
func (x *commonData) SetNbf(nbf uint64) {
|
||||||
|
x.nbf = nbf
|
||||||
|
x.lifetimeSet = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetIat sets "iat" (issued at) claim which identifies the time (in NeoFS
|
||||||
|
// epochs) at which the session was issued. This claim can be used to
|
||||||
|
// determine the age of the session.
|
||||||
|
//
|
||||||
|
// Naming is inspired by https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.6.
|
||||||
|
//
|
||||||
|
// See also InvalidAt.
|
||||||
|
func (x *commonData) SetIat(iat uint64) {
|
||||||
|
x.iat = iat
|
||||||
|
x.lifetimeSet = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x commonData) expiredAt(epoch uint64) bool {
|
||||||
|
return !x.lifetimeSet || x.exp <= epoch
|
||||||
|
}
|
||||||
|
|
||||||
|
// InvalidAt asserts "exp", "nbf" and "iat" claims.
|
||||||
|
//
|
||||||
|
// Zero session is invalid in any epoch.
|
||||||
|
//
|
||||||
|
// See also SetExp, SetNbf, SetIat.
|
||||||
|
func (x commonData) InvalidAt(epoch uint64) bool {
|
||||||
|
return x.expiredAt(epoch) || x.nbf > epoch || x.iat > epoch
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetID sets a unique identifier for the session. The identifier value MUST be
|
||||||
|
// assigned in a manner that ensures that there is a negligible probability
|
||||||
|
// that the same value will be accidentally assigned to a different session.
|
||||||
|
//
|
||||||
|
// ID format MUST be UUID version 4 (random). uuid.New can be used to generate
|
||||||
|
// a new ID. See https://datatracker.ietf.org/doc/html/rfc4122 and
|
||||||
|
// github.com/google/uuid package docs for details.
|
||||||
|
//
|
||||||
|
// See also ID.
|
||||||
|
func (x *commonData) SetID(id uuid.UUID) {
|
||||||
|
x.id = id
|
||||||
|
x.idSet = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID returns a unique identifier for the session.
|
||||||
|
//
|
||||||
|
// Zero session has empty UUID (all zeros, see uuid.Nil) which is legitimate
|
||||||
|
// but most likely not suitable.
|
||||||
|
//
|
||||||
|
// See also SetID.
|
||||||
|
func (x commonData) ID() uuid.UUID {
|
||||||
|
if x.idSet {
|
||||||
|
return x.id
|
||||||
|
}
|
||||||
|
|
||||||
|
return uuid.Nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAuthKey public key corresponding to the private key bound to the session.
|
||||||
|
//
|
||||||
|
// See also AssertAuthKey.
|
||||||
|
func (x *commonData) SetAuthKey(key neofscrypto.PublicKey) {
|
||||||
|
x.authKey = make([]byte, key.MaxEncodedSize())
|
||||||
|
x.authKey = x.authKey[:key.Encode(x.authKey)]
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertAuthKey asserts public key bound to the session.
|
||||||
|
//
|
||||||
|
// Zero session fails the check.
|
||||||
|
//
|
||||||
|
// See also SetAuthKey.
|
||||||
|
func (x commonData) AssertAuthKey(key neofscrypto.PublicKey) bool {
|
||||||
|
bKey := make([]byte, key.MaxEncodedSize())
|
||||||
|
bKey = bKey[:key.Encode(bKey)]
|
||||||
|
|
||||||
|
return bytes.Equal(bKey, x.authKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Issuer returns user ID of the session issuer.
|
||||||
|
//
|
||||||
|
// Makes sense only for signed session instances. For unsigned instances,
|
||||||
|
// Issuer returns zero user.ID.
|
||||||
|
//
|
||||||
|
// See also Sign.
|
||||||
|
func (x commonData) Issuer() user.ID {
|
||||||
|
if x.issuerSet {
|
||||||
|
return x.issuer
|
||||||
|
}
|
||||||
|
|
||||||
|
return user.ID{}
|
||||||
|
}
|
|
@ -1,17 +1,13 @@
|
||||||
package session
|
package session
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"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"
|
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/user"
|
"github.com/nspcc-dev/neofs-sdk-go/user"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -26,67 +22,67 @@ import (
|
||||||
//
|
//
|
||||||
// Instances can be created using built-in var declaration.
|
// Instances can be created using built-in var declaration.
|
||||||
type Container struct {
|
type Container struct {
|
||||||
|
commonData
|
||||||
|
|
||||||
|
verb ContainerVerb
|
||||||
|
|
||||||
cnrSet bool
|
cnrSet bool
|
||||||
|
cnr cid.ID
|
||||||
lt session.TokenLifetime
|
|
||||||
|
|
||||||
c session.ContainerSessionContext
|
|
||||||
|
|
||||||
body session.TokenBody
|
|
||||||
|
|
||||||
sig neofscrypto.Signature
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadFromV2 reads Container from the session.Token message.
|
// readContext is a contextReader needed for commonData methods.
|
||||||
|
func (x *Container) readContext(c session.TokenContext, checkFieldPresence bool) error {
|
||||||
|
cCnr, ok := c.(*session.ContainerSessionContext)
|
||||||
|
if !ok || cCnr == nil {
|
||||||
|
return fmt.Errorf("invalid context %T", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
x.cnrSet = !cCnr.Wildcard()
|
||||||
|
cnr := cCnr.ContainerID()
|
||||||
|
|
||||||
|
if x.cnrSet {
|
||||||
|
if cnr != nil {
|
||||||
|
err := x.cnr.ReadFromV2(*cnr)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid container ID: %w", err)
|
||||||
|
}
|
||||||
|
} else if checkFieldPresence {
|
||||||
|
return errors.New("missing container or wildcard flag")
|
||||||
|
}
|
||||||
|
} else if cnr != nil {
|
||||||
|
return errors.New("container conflicts with wildcard flag")
|
||||||
|
}
|
||||||
|
|
||||||
|
x.verb = ContainerVerb(cCnr.Verb())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Container) readFromV2(m session.Token, checkFieldPresence bool) error {
|
||||||
|
return x.commonData.readFromV2(m, checkFieldPresence, x.readContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadFromV2 reads Container from the session.Token message. Checks if the
|
||||||
|
// message conforms to NeoFS API V2 protocol.
|
||||||
//
|
//
|
||||||
// See also WriteToV2.
|
// See also WriteToV2.
|
||||||
func (x *Container) ReadFromV2(m session.Token) error {
|
func (x *Container) ReadFromV2(m session.Token) error {
|
||||||
b := m.GetBody()
|
return x.readFromV2(m, true)
|
||||||
if b == nil {
|
|
||||||
return errors.New("missing body")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bID := b.GetID()
|
func (x Container) writeContext() session.TokenContext {
|
||||||
var id uuid.UUID
|
var c session.ContainerSessionContext
|
||||||
|
c.SetWildcard(!x.cnrSet)
|
||||||
|
c.SetVerb(session.ContainerSessionVerb(x.verb))
|
||||||
|
|
||||||
err := id.UnmarshalBinary(bID)
|
if x.cnrSet {
|
||||||
if err != nil {
|
var cnr refs.ContainerID
|
||||||
return fmt.Errorf("invalid binary ID: %w", err)
|
x.cnr.WriteToV2(&cnr)
|
||||||
} else if ver := id.Version(); ver != 4 {
|
|
||||||
return fmt.Errorf("invalid UUID version %s", ver)
|
c.SetContainerID(&cnr)
|
||||||
}
|
}
|
||||||
|
|
||||||
c, ok := b.GetContext().(*session.ContainerSessionContext)
|
return &c
|
||||||
if !ok || c == nil {
|
|
||||||
return fmt.Errorf("invalid context %T", b.GetContext())
|
|
||||||
}
|
|
||||||
|
|
||||||
cnr := c.ContainerID()
|
|
||||||
x.cnrSet = !c.Wildcard()
|
|
||||||
|
|
||||||
if x.cnrSet && cnr == nil {
|
|
||||||
return errors.New("container is not specified with unset wildcard")
|
|
||||||
}
|
|
||||||
|
|
||||||
x.body = *b
|
|
||||||
|
|
||||||
x.c = *c
|
|
||||||
|
|
||||||
lt := b.GetLifetime()
|
|
||||||
if lt != nil {
|
|
||||||
x.lt = *lt
|
|
||||||
} else {
|
|
||||||
x.lt = session.TokenLifetime{}
|
|
||||||
}
|
|
||||||
|
|
||||||
sig := m.GetSignature()
|
|
||||||
if sig != nil {
|
|
||||||
x.sig.ReadFromV2(*sig)
|
|
||||||
} else {
|
|
||||||
x.sig = neofscrypto.Signature{}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteToV2 writes Container to the session.Token message.
|
// WriteToV2 writes Container to the session.Token message.
|
||||||
|
@ -94,11 +90,7 @@ func (x *Container) ReadFromV2(m session.Token) error {
|
||||||
//
|
//
|
||||||
// See also ReadFromV2.
|
// See also ReadFromV2.
|
||||||
func (x Container) WriteToV2(m *session.Token) {
|
func (x Container) WriteToV2(m *session.Token) {
|
||||||
var sig refs.Signature
|
x.writeToV2(m, x.writeContext)
|
||||||
x.sig.WriteToV2(&sig)
|
|
||||||
|
|
||||||
m.SetBody(&x.body)
|
|
||||||
m.SetSignature(&sig)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marshal encodes Container into a binary format of the NeoFS API protocol
|
// Marshal encodes Container into a binary format of the NeoFS API protocol
|
||||||
|
@ -106,10 +98,7 @@ func (x Container) WriteToV2(m *session.Token) {
|
||||||
//
|
//
|
||||||
// See also Unmarshal.
|
// See also Unmarshal.
|
||||||
func (x Container) Marshal() []byte {
|
func (x Container) Marshal() []byte {
|
||||||
var m session.Token
|
return x.marshal(x.writeContext)
|
||||||
x.WriteToV2(&m)
|
|
||||||
|
|
||||||
return m.StableMarshal(nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unmarshal decodes NeoFS API protocol binary format into the Container
|
// Unmarshal decodes NeoFS API protocol binary format into the Container
|
||||||
|
@ -118,14 +107,7 @@ func (x Container) Marshal() []byte {
|
||||||
//
|
//
|
||||||
// See also Marshal.
|
// See also Marshal.
|
||||||
func (x *Container) Unmarshal(data []byte) error {
|
func (x *Container) Unmarshal(data []byte) error {
|
||||||
var m session.Token
|
return x.unmarshal(data, x.readContext)
|
||||||
|
|
||||||
err := m.Unmarshal(data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return x.ReadFromV2(m)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalJSON encodes Container into a JSON format of the NeoFS API protocol
|
// MarshalJSON encodes Container into a JSON format of the NeoFS API protocol
|
||||||
|
@ -133,10 +115,7 @@ func (x *Container) Unmarshal(data []byte) error {
|
||||||
//
|
//
|
||||||
// See also UnmarshalJSON.
|
// See also UnmarshalJSON.
|
||||||
func (x Container) MarshalJSON() ([]byte, error) {
|
func (x Container) MarshalJSON() ([]byte, error) {
|
||||||
var m session.Token
|
return x.marshalJSON(x.writeContext)
|
||||||
x.WriteToV2(&m)
|
|
||||||
|
|
||||||
return m.MarshalJSON()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSON decodes NeoFS API protocol JSON format into the Container
|
// UnmarshalJSON decodes NeoFS API protocol JSON format into the Container
|
||||||
|
@ -144,14 +123,7 @@ func (x Container) MarshalJSON() ([]byte, error) {
|
||||||
//
|
//
|
||||||
// See also MarshalJSON.
|
// See also MarshalJSON.
|
||||||
func (x *Container) UnmarshalJSON(data []byte) error {
|
func (x *Container) UnmarshalJSON(data []byte) error {
|
||||||
var m session.Token
|
return x.unmarshalJSON(data, x.readContext)
|
||||||
|
|
||||||
err := m.UnmarshalJSON(data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return x.ReadFromV2(m)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sign calculates and writes signature of the Container data.
|
// Sign calculates and writes signature of the Container data.
|
||||||
|
@ -164,19 +136,7 @@ func (x *Container) UnmarshalJSON(data []byte) error {
|
||||||
//
|
//
|
||||||
// See also VerifySignature.
|
// See also VerifySignature.
|
||||||
func (x *Container) Sign(key ecdsa.PrivateKey) error {
|
func (x *Container) Sign(key ecdsa.PrivateKey) error {
|
||||||
var idUser user.ID
|
return x.sign(key, x.writeContext)
|
||||||
user.IDFromKey(&idUser, key.PublicKey)
|
|
||||||
|
|
||||||
var idUserV2 refs.OwnerID
|
|
||||||
idUser.WriteToV2(&idUserV2)
|
|
||||||
|
|
||||||
x.c.SetWildcard(!x.cnrSet)
|
|
||||||
|
|
||||||
x.body.SetOwnerID(&idUserV2)
|
|
||||||
x.body.SetLifetime(&x.lt)
|
|
||||||
x.body.SetContext(&x.c)
|
|
||||||
|
|
||||||
return x.sig.Calculate(neofsecdsa.Signer(key), x.body.StableMarshal(nil))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifySignature checks if Container signature is presented and valid.
|
// VerifySignature checks if Container signature is presented and valid.
|
||||||
|
@ -185,18 +145,14 @@ func (x *Container) Sign(key ecdsa.PrivateKey) error {
|
||||||
//
|
//
|
||||||
// See also Sign.
|
// See also Sign.
|
||||||
func (x Container) VerifySignature() bool {
|
func (x Container) VerifySignature() bool {
|
||||||
// TODO: (#233) check owner<->key relation
|
return x.verifySignature(x.writeContext)
|
||||||
return x.sig.Verify(x.body.StableMarshal(nil))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApplyOnlyTo limits session scope to a given author container.
|
// ApplyOnlyTo limits session scope to a given author container.
|
||||||
//
|
//
|
||||||
// See also AppliedTo.
|
// See also AppliedTo.
|
||||||
func (x *Container) ApplyOnlyTo(cnr cid.ID) {
|
func (x *Container) ApplyOnlyTo(cnr cid.ID) {
|
||||||
var cnrv2 refs.ContainerID
|
x.cnr = cnr
|
||||||
cnr.WriteToV2(&cnrv2)
|
|
||||||
|
|
||||||
x.c.SetContainerID(&cnrv2)
|
|
||||||
x.cnrSet = true
|
x.cnrSet = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,18 +162,7 @@ func (x *Container) ApplyOnlyTo(cnr cid.ID) {
|
||||||
//
|
//
|
||||||
// See also ApplyOnlyTo.
|
// See also ApplyOnlyTo.
|
||||||
func (x Container) AppliedTo(cnr cid.ID) bool {
|
func (x Container) AppliedTo(cnr cid.ID) bool {
|
||||||
if !x.cnrSet {
|
return !x.cnrSet || x.cnr.Equals(cnr)
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
var cnr2 cid.ID
|
|
||||||
|
|
||||||
if err := cnr2.ReadFromV2(*x.c.ContainerID()); err != nil {
|
|
||||||
// NPE and error must never happen
|
|
||||||
panic(fmt.Sprintf("unexpected error from cid.ReadFromV2: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
return cnr2.Equals(cnr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContainerVerb enumerates container operations.
|
// ContainerVerb enumerates container operations.
|
||||||
|
@ -236,7 +181,7 @@ const (
|
||||||
//
|
//
|
||||||
// See also AssertVerb.
|
// See also AssertVerb.
|
||||||
func (x *Container) ForVerb(verb ContainerVerb) {
|
func (x *Container) ForVerb(verb ContainerVerb) {
|
||||||
x.c.SetVerb(session.ContainerSessionVerb(verb))
|
x.verb = verb
|
||||||
}
|
}
|
||||||
|
|
||||||
// AssertVerb checks if Container relates to the given container operation.
|
// AssertVerb checks if Container relates to the given container operation.
|
||||||
|
@ -245,134 +190,7 @@ func (x *Container) ForVerb(verb ContainerVerb) {
|
||||||
//
|
//
|
||||||
// See also ForVerb.
|
// See also ForVerb.
|
||||||
func (x Container) AssertVerb(verb ContainerVerb) bool {
|
func (x Container) AssertVerb(verb ContainerVerb) bool {
|
||||||
return verb == ContainerVerb(x.c.Verb())
|
return x.verb == verb
|
||||||
}
|
|
||||||
|
|
||||||
// SetExp sets "exp" (expiration time) claim which identifies the expiration time
|
|
||||||
// (in NeoFS epochs) on or after which the Container MUST NOT be accepted for
|
|
||||||
// processing. The processing of the "exp" claim requires that the current
|
|
||||||
// epoch MUST be before the expiration epoch listed in the "exp" claim.
|
|
||||||
//
|
|
||||||
// Naming is inspired by https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.4.
|
|
||||||
//
|
|
||||||
// See also ExpiredAt.
|
|
||||||
func (x *Container) SetExp(exp uint64) {
|
|
||||||
x.lt.SetExp(exp)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExpiredAt asserts "exp" claim.
|
|
||||||
//
|
|
||||||
// Zero Container is expired in any epoch.
|
|
||||||
//
|
|
||||||
// See also SetExp.
|
|
||||||
func (x Container) ExpiredAt(epoch uint64) bool {
|
|
||||||
return x.lt.GetExp() <= epoch
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetNbf sets "nbf" (not before) claim which identifies the time (in NeoFS
|
|
||||||
// epochs) before which the Container MUST NOT be accepted for processing.
|
|
||||||
// The processing of the "nbf" claim requires that the current date/time MUST be
|
|
||||||
// after or equal to the not-before date/time listed in the "nbf" claim.
|
|
||||||
//
|
|
||||||
// Naming is inspired by https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.5.
|
|
||||||
//
|
|
||||||
// See also InvalidAt.
|
|
||||||
func (x *Container) SetNbf(nbf uint64) {
|
|
||||||
x.lt.SetNbf(nbf)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetIat sets "iat" (issued at) claim which identifies the time (in NeoFS
|
|
||||||
// epochs) at which the Container was issued. This claim can be used to
|
|
||||||
// determine the age of the Container.
|
|
||||||
//
|
|
||||||
// Naming is inspired by https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.6.
|
|
||||||
//
|
|
||||||
// See also InvalidAt.
|
|
||||||
func (x *Container) SetIat(iat uint64) {
|
|
||||||
x.lt.SetIat(iat)
|
|
||||||
}
|
|
||||||
|
|
||||||
// InvalidAt asserts "exp", "nbf" and "iat" claims.
|
|
||||||
//
|
|
||||||
// Zero Container is invalid in any epoch.
|
|
||||||
//
|
|
||||||
// See also SetExp, SetNbf, SetIat.
|
|
||||||
func (x Container) InvalidAt(epoch uint64) bool {
|
|
||||||
return x.lt.GetNbf() > epoch || x.lt.GetIat() > epoch || x.ExpiredAt(epoch)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetID sets a unique identifier for the session. The identifier value MUST be
|
|
||||||
// assigned in a manner that ensures that there is a negligible probability
|
|
||||||
// that the same value will be accidentally assigned to a different session.
|
|
||||||
//
|
|
||||||
// ID format MUST be UUID version 4 (random). uuid.New can be used to generate
|
|
||||||
// a new ID. See https://datatracker.ietf.org/doc/html/rfc4122 and
|
|
||||||
// github.com/google/uuid package docs for details.
|
|
||||||
//
|
|
||||||
// See also ID.
|
|
||||||
func (x *Container) SetID(id uuid.UUID) {
|
|
||||||
x.body.SetID(id[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
// ID returns a unique identifier for the session.
|
|
||||||
//
|
|
||||||
// Zero Container has empty UUID (all zeros, see uuid.Nil) which is legitimate
|
|
||||||
// but most likely not suitable.
|
|
||||||
//
|
|
||||||
// See also SetID.
|
|
||||||
func (x Container) ID() uuid.UUID {
|
|
||||||
data := x.body.GetID()
|
|
||||||
if data == nil {
|
|
||||||
return uuid.Nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var id uuid.UUID
|
|
||||||
|
|
||||||
err := id.UnmarshalBinary(x.body.GetID())
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("unexpected error from UUID.UnmarshalBinary: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetAuthKey public key corresponding to the private key bound to the session.
|
|
||||||
//
|
|
||||||
// See also AssertAuthKey.
|
|
||||||
func (x *Container) SetAuthKey(key neofscrypto.PublicKey) {
|
|
||||||
bKey := make([]byte, key.MaxEncodedSize())
|
|
||||||
bKey = bKey[:key.Encode(bKey)]
|
|
||||||
|
|
||||||
x.body.SetSessionKey(bKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AssertAuthKey asserts public key bound to the session.
|
|
||||||
//
|
|
||||||
// Zero Container fails the check.
|
|
||||||
//
|
|
||||||
// See also SetAuthKey.
|
|
||||||
func (x Container) AssertAuthKey(key neofscrypto.PublicKey) bool {
|
|
||||||
bKey := make([]byte, key.MaxEncodedSize())
|
|
||||||
bKey = bKey[:key.Encode(bKey)]
|
|
||||||
|
|
||||||
return bytes.Equal(bKey, x.body.GetSessionKey())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Issuer returns user ID of the session issuer.
|
|
||||||
//
|
|
||||||
// Makes sense only for signed Container instances. For unsigned instances,
|
|
||||||
// Issuer returns zero user.ID.
|
|
||||||
//
|
|
||||||
// See also Sign.
|
|
||||||
func (x Container) Issuer() user.ID {
|
|
||||||
var issuer user.ID
|
|
||||||
|
|
||||||
issuerV2 := x.body.GetOwnerID()
|
|
||||||
if issuerV2 != nil {
|
|
||||||
_ = issuer.ReadFromV2(*issuerV2)
|
|
||||||
}
|
|
||||||
|
|
||||||
return issuer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IssuedBy checks if Container session is issued by the given user.
|
// IssuedBy checks if Container session is issued by the given user.
|
||||||
|
|
|
@ -1,152 +1,382 @@
|
||||||
package session_test
|
package session_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util/slice"
|
||||||
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
"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"
|
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
|
||||||
|
neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa"
|
||||||
"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"
|
||||||
|
usertest "github.com/nspcc-dev/neofs-sdk-go/user/test"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestContainer_ReadFromV2(t *testing.T) {
|
func TestContainerProtocolV2(t *testing.T) {
|
||||||
var x session.Container
|
var validV2 v2session.Token
|
||||||
var m v2session.Token
|
|
||||||
var b v2session.TokenBody
|
var body v2session.TokenBody
|
||||||
var c v2session.ContainerSessionContext
|
validV2.SetBody(&body)
|
||||||
|
|
||||||
|
// ID
|
||||||
id := uuid.New()
|
id := uuid.New()
|
||||||
|
binID, err := id.MarshalBinary()
|
||||||
|
require.NoError(t, err)
|
||||||
|
restoreID := func() {
|
||||||
|
body.SetID(binID)
|
||||||
|
}
|
||||||
|
restoreID()
|
||||||
|
|
||||||
t.Run("protocol violation", func(t *testing.T) {
|
// Owner
|
||||||
require.Error(t, x.ReadFromV2(m))
|
usr := *usertest.ID()
|
||||||
|
var usrV2 refs.OwnerID
|
||||||
|
usr.WriteToV2(&usrV2)
|
||||||
|
restoreUser := func() {
|
||||||
|
body.SetOwnerID(&usrV2)
|
||||||
|
}
|
||||||
|
restoreUser()
|
||||||
|
|
||||||
m.SetBody(&b)
|
// Lifetime
|
||||||
|
var lifetime v2session.TokenLifetime
|
||||||
|
lifetime.SetIat(1)
|
||||||
|
lifetime.SetNbf(2)
|
||||||
|
lifetime.SetExp(3)
|
||||||
|
restoreLifetime := func() {
|
||||||
|
body.SetLifetime(&lifetime)
|
||||||
|
}
|
||||||
|
restoreLifetime()
|
||||||
|
|
||||||
require.Error(t, x.ReadFromV2(m))
|
// Session key
|
||||||
|
signer := randSigner()
|
||||||
|
authKey := neofsecdsa.PublicKey(signer.PublicKey)
|
||||||
|
binAuthKey := make([]byte, authKey.MaxEncodedSize())
|
||||||
|
binAuthKey = binAuthKey[:authKey.Encode(binAuthKey)]
|
||||||
|
restoreAuthKey := func() {
|
||||||
|
body.SetSessionKey(binAuthKey)
|
||||||
|
}
|
||||||
|
restoreAuthKey()
|
||||||
|
|
||||||
b.SetID(id[:])
|
// Context
|
||||||
|
cnr := cidtest.ID()
|
||||||
|
var cnrV2 refs.ContainerID
|
||||||
|
cnr.WriteToV2(&cnrV2)
|
||||||
|
var cCnr v2session.ContainerSessionContext
|
||||||
|
restoreCtx := func() {
|
||||||
|
cCnr.SetContainerID(&cnrV2)
|
||||||
|
cCnr.SetWildcard(false)
|
||||||
|
body.SetContext(&cCnr)
|
||||||
|
}
|
||||||
|
restoreCtx()
|
||||||
|
|
||||||
require.Error(t, x.ReadFromV2(m))
|
// Signature
|
||||||
|
var sig refs.Signature
|
||||||
|
restoreSig := func() {
|
||||||
|
validV2.SetSignature(&sig)
|
||||||
|
}
|
||||||
|
restoreSig()
|
||||||
|
|
||||||
b.SetContext(&c)
|
// TODO(@cthulhu-rider): #260 use functionality for message corruption
|
||||||
|
|
||||||
require.Error(t, x.ReadFromV2(m))
|
for _, testcase := range []struct {
|
||||||
|
name string
|
||||||
|
corrupt []func()
|
||||||
|
restore func()
|
||||||
|
assert func(session.Container)
|
||||||
|
breakSign func(*v2session.Token)
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Signature",
|
||||||
|
corrupt: []func(){
|
||||||
|
func() {
|
||||||
|
validV2.SetSignature(nil)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
restore: restoreSig,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ID",
|
||||||
|
corrupt: []func(){
|
||||||
|
func() {
|
||||||
|
body.SetID([]byte{1, 2, 3})
|
||||||
|
},
|
||||||
|
func() {
|
||||||
|
id, err := uuid.NewDCEPerson()
|
||||||
|
require.NoError(t, err)
|
||||||
|
bindID, err := id.MarshalBinary()
|
||||||
|
require.NoError(t, err)
|
||||||
|
body.SetID(bindID)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
restore: restoreID,
|
||||||
|
assert: func(val session.Container) {
|
||||||
|
require.Equal(t, id, val.ID())
|
||||||
|
},
|
||||||
|
breakSign: func(m *v2session.Token) {
|
||||||
|
id := m.GetBody().GetID()
|
||||||
|
id[len(id)-1]++
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "User",
|
||||||
|
corrupt: []func(){
|
||||||
|
func() {
|
||||||
|
var brokenUsrV2 refs.OwnerID
|
||||||
|
brokenUsrV2.SetValue(append(usrV2.GetValue(), 1))
|
||||||
|
body.SetOwnerID(&brokenUsrV2)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
restore: restoreUser,
|
||||||
|
assert: func(val session.Container) {
|
||||||
|
require.Equal(t, usr, val.Issuer())
|
||||||
|
},
|
||||||
|
breakSign: func(m *v2session.Token) {
|
||||||
|
id := m.GetBody().GetOwnerID().GetValue()
|
||||||
|
copy(id, usertest.ID().WalletBytes())
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Lifetime",
|
||||||
|
corrupt: []func(){
|
||||||
|
func() {
|
||||||
|
body.SetLifetime(nil)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
restore: restoreLifetime,
|
||||||
|
assert: func(val session.Container) {
|
||||||
|
require.True(t, val.InvalidAt(1))
|
||||||
|
require.False(t, val.InvalidAt(2))
|
||||||
|
require.True(t, val.InvalidAt(3))
|
||||||
|
},
|
||||||
|
breakSign: func(m *v2session.Token) {
|
||||||
|
lt := m.GetBody().GetLifetime()
|
||||||
|
lt.SetIat(lt.GetIat() + 1)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Auth key",
|
||||||
|
corrupt: []func(){
|
||||||
|
func() {
|
||||||
|
body.SetSessionKey(nil)
|
||||||
|
},
|
||||||
|
func() {
|
||||||
|
body.SetSessionKey([]byte{})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
restore: restoreAuthKey,
|
||||||
|
assert: func(val session.Container) {
|
||||||
|
require.True(t, val.AssertAuthKey(&authKey))
|
||||||
|
},
|
||||||
|
breakSign: func(m *v2session.Token) {
|
||||||
|
body := m.GetBody()
|
||||||
|
key := body.GetSessionKey()
|
||||||
|
cp := slice.Copy(key)
|
||||||
|
cp[len(cp)-1]++
|
||||||
|
body.SetSessionKey(cp)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Context",
|
||||||
|
corrupt: []func(){
|
||||||
|
func() {
|
||||||
|
body.SetContext(nil)
|
||||||
|
},
|
||||||
|
func() {
|
||||||
|
cCnr.SetWildcard(true)
|
||||||
|
},
|
||||||
|
func() {
|
||||||
|
cCnr.SetContainerID(nil)
|
||||||
|
},
|
||||||
|
func() {
|
||||||
|
var brokenCnr refs.ContainerID
|
||||||
|
brokenCnr.SetValue(append(cnrV2.GetValue(), 1))
|
||||||
|
cCnr.SetContainerID(&brokenCnr)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
restore: restoreCtx,
|
||||||
|
assert: func(val session.Container) {
|
||||||
|
require.True(t, val.AppliedTo(cnr))
|
||||||
|
require.False(t, val.AppliedTo(cidtest.ID()))
|
||||||
|
},
|
||||||
|
breakSign: func(m *v2session.Token) {
|
||||||
|
cnr := m.GetBody().GetContext().(*v2session.ContainerSessionContext).ContainerID().GetValue()
|
||||||
|
cnr[len(cnr)-1]++
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
var val session.Container
|
||||||
|
|
||||||
c.SetWildcard(true)
|
for i, corrupt := range testcase.corrupt {
|
||||||
|
corrupt()
|
||||||
|
require.Error(t, val.ReadFromV2(validV2), testcase.name, fmt.Sprintf("corrupt #%d", i))
|
||||||
|
|
||||||
require.NoError(t, x.ReadFromV2(m))
|
testcase.restore()
|
||||||
})
|
require.NoError(t, val.ReadFromV2(validV2), testcase.name)
|
||||||
|
|
||||||
m.SetBody(&b)
|
if testcase.assert != nil {
|
||||||
b.SetContext(&c)
|
testcase.assert(val)
|
||||||
b.SetID(id[:])
|
|
||||||
c.SetWildcard(true)
|
|
||||||
|
|
||||||
t.Run("container", func(t *testing.T) {
|
|
||||||
cnr1 := cidtest.ID()
|
|
||||||
cnr2 := cidtest.ID()
|
|
||||||
|
|
||||||
require.NoError(t, x.ReadFromV2(m))
|
|
||||||
require.True(t, x.AppliedTo(cnr1))
|
|
||||||
require.True(t, x.AppliedTo(cnr2))
|
|
||||||
|
|
||||||
var cnrv2 refs.ContainerID
|
|
||||||
cnr1.WriteToV2(&cnrv2)
|
|
||||||
|
|
||||||
c.SetContainerID(&cnrv2)
|
|
||||||
c.SetWildcard(false)
|
|
||||||
|
|
||||||
require.NoError(t, x.ReadFromV2(m))
|
|
||||||
require.True(t, x.AppliedTo(cnr1))
|
|
||||||
require.False(t, x.AppliedTo(cnr2))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("verb", func(t *testing.T) {
|
|
||||||
require.NoError(t, x.ReadFromV2(m))
|
|
||||||
require.True(t, x.AssertVerb(0))
|
|
||||||
|
|
||||||
verb := v2session.ContainerSessionVerb(rand.Uint32())
|
|
||||||
|
|
||||||
c.SetVerb(verb)
|
|
||||||
|
|
||||||
require.NoError(t, x.ReadFromV2(m))
|
|
||||||
require.True(t, x.AssertVerb(session.ContainerVerb(verb)))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("id", func(t *testing.T) {
|
|
||||||
id := uuid.New()
|
|
||||||
bID := id[:]
|
|
||||||
|
|
||||||
b.SetID(bID)
|
|
||||||
|
|
||||||
require.NoError(t, x.ReadFromV2(m))
|
|
||||||
require.Equal(t, id, x.ID())
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("lifetime", func(t *testing.T) {
|
|
||||||
const nbf, iat, exp = 11, 22, 33
|
|
||||||
|
|
||||||
var lt v2session.TokenLifetime
|
|
||||||
lt.SetNbf(nbf)
|
|
||||||
lt.SetIat(iat)
|
|
||||||
lt.SetExp(exp)
|
|
||||||
|
|
||||||
b.SetLifetime(<)
|
|
||||||
|
|
||||||
require.NoError(t, x.ReadFromV2(m))
|
|
||||||
require.False(t, x.ExpiredAt(exp-1))
|
|
||||||
require.True(t, x.ExpiredAt(exp))
|
|
||||||
require.True(t, x.ExpiredAt(exp+1))
|
|
||||||
require.True(t, x.InvalidAt(nbf-1))
|
|
||||||
require.True(t, x.InvalidAt(iat-1))
|
|
||||||
require.False(t, x.InvalidAt(iat))
|
|
||||||
require.False(t, x.InvalidAt(exp-1))
|
|
||||||
require.True(t, x.InvalidAt(exp))
|
|
||||||
require.True(t, x.InvalidAt(exp+1))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("session key", func(t *testing.T) {
|
|
||||||
key := randPublicKey()
|
|
||||||
|
|
||||||
bKey := make([]byte, key.MaxEncodedSize())
|
|
||||||
bKey = bKey[:key.Encode(bKey)]
|
|
||||||
|
|
||||||
b.SetSessionKey(bKey)
|
|
||||||
|
|
||||||
require.NoError(t, x.ReadFromV2(m))
|
|
||||||
require.True(t, x.AssertAuthKey(key))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEncodingContainer(t *testing.T) {
|
if testcase.breakSign != nil {
|
||||||
tok := *sessiontest.ContainerSigned()
|
require.NoError(t, val.Sign(signer), testcase.name)
|
||||||
|
require.True(t, val.VerifySignature(), testcase.name)
|
||||||
|
|
||||||
t.Run("binary", func(t *testing.T) {
|
var signedV2 v2session.Token
|
||||||
data := tok.Marshal()
|
val.WriteToV2(&signedV2)
|
||||||
|
|
||||||
var tok2 session.Container
|
var restored session.Container
|
||||||
require.NoError(t, tok2.Unmarshal(data))
|
require.NoError(t, restored.ReadFromV2(signedV2), testcase.name)
|
||||||
|
require.True(t, restored.VerifySignature(), testcase.name)
|
||||||
|
|
||||||
require.Equal(t, tok, tok2)
|
testcase.breakSign(&signedV2)
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("json", func(t *testing.T) {
|
require.NoError(t, restored.ReadFromV2(signedV2), testcase.name)
|
||||||
data, err := tok.MarshalJSON()
|
require.False(t, restored.VerifySignature(), testcase.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContainer_WriteToV2(t *testing.T) {
|
||||||
|
var val session.Container
|
||||||
|
|
||||||
|
assert := func(baseAssert func(v2session.Token)) {
|
||||||
|
var m v2session.Token
|
||||||
|
val.WriteToV2(&m)
|
||||||
|
baseAssert(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID
|
||||||
|
id := uuid.New()
|
||||||
|
|
||||||
|
binID, err := id.MarshalBinary()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var tok2 session.Container
|
val.SetID(id)
|
||||||
require.NoError(t, tok2.UnmarshalJSON(data))
|
assert(func(m v2session.Token) {
|
||||||
|
require.Equal(t, binID, m.GetBody().GetID())
|
||||||
|
})
|
||||||
|
|
||||||
require.Equal(t, tok, tok2)
|
// Owner/Signature
|
||||||
|
signer := randSigner()
|
||||||
|
|
||||||
|
require.NoError(t, val.Sign(signer))
|
||||||
|
|
||||||
|
var usr user.ID
|
||||||
|
user.IDFromKey(&usr, signer.PublicKey)
|
||||||
|
|
||||||
|
var usrV2 refs.OwnerID
|
||||||
|
usr.WriteToV2(&usrV2)
|
||||||
|
|
||||||
|
assert(func(m v2session.Token) {
|
||||||
|
require.Equal(t, &usrV2, m.GetBody().GetOwnerID())
|
||||||
|
|
||||||
|
sig := m.GetSignature()
|
||||||
|
require.NotZero(t, sig.GetKey())
|
||||||
|
require.NotZero(t, sig.GetSign())
|
||||||
|
})
|
||||||
|
|
||||||
|
// Lifetime
|
||||||
|
const iat, nbf, exp = 1, 2, 3
|
||||||
|
val.SetIat(iat)
|
||||||
|
val.SetNbf(nbf)
|
||||||
|
val.SetExp(exp)
|
||||||
|
|
||||||
|
assert(func(m v2session.Token) {
|
||||||
|
lt := m.GetBody().GetLifetime()
|
||||||
|
require.EqualValues(t, iat, lt.GetIat())
|
||||||
|
require.EqualValues(t, nbf, lt.GetNbf())
|
||||||
|
require.EqualValues(t, exp, lt.GetExp())
|
||||||
|
})
|
||||||
|
|
||||||
|
// Context
|
||||||
|
assert(func(m v2session.Token) {
|
||||||
|
cCnr, ok := m.GetBody().GetContext().(*v2session.ContainerSessionContext)
|
||||||
|
require.True(t, ok)
|
||||||
|
require.True(t, cCnr.Wildcard())
|
||||||
|
require.Zero(t, cCnr.ContainerID())
|
||||||
|
})
|
||||||
|
|
||||||
|
cnr := cidtest.ID()
|
||||||
|
|
||||||
|
var cnrV2 refs.ContainerID
|
||||||
|
cnr.WriteToV2(&cnrV2)
|
||||||
|
|
||||||
|
val.ApplyOnlyTo(cnr)
|
||||||
|
|
||||||
|
assert(func(m v2session.Token) {
|
||||||
|
cCnr, ok := m.GetBody().GetContext().(*v2session.ContainerSessionContext)
|
||||||
|
require.True(t, ok)
|
||||||
|
require.False(t, cCnr.Wildcard())
|
||||||
|
require.Equal(t, &cnrV2, cCnr.ContainerID())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainerAppliedTo(t *testing.T) {
|
func TestContainer_ApplyOnlyTo(t *testing.T) {
|
||||||
|
var val session.Container
|
||||||
|
var m v2session.Token
|
||||||
|
filled := sessiontest.Container()
|
||||||
|
|
||||||
|
assertDefaults := func() {
|
||||||
|
cCnr, ok := m.GetBody().GetContext().(*v2session.ContainerSessionContext)
|
||||||
|
require.True(t, ok)
|
||||||
|
require.True(t, cCnr.Wildcard())
|
||||||
|
require.Zero(t, cCnr.ContainerID())
|
||||||
|
}
|
||||||
|
|
||||||
|
assertBinary := func(baseAssert func()) {
|
||||||
|
val2 := filled
|
||||||
|
|
||||||
|
require.NoError(t, val2.Unmarshal(val.Marshal()))
|
||||||
|
baseAssert()
|
||||||
|
}
|
||||||
|
|
||||||
|
assertJSON := func(baseAssert func()) {
|
||||||
|
val2 := filled
|
||||||
|
|
||||||
|
jd, err := val.MarshalJSON()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.NoError(t, val2.UnmarshalJSON(jd))
|
||||||
|
baseAssert()
|
||||||
|
}
|
||||||
|
|
||||||
|
val.WriteToV2(&m)
|
||||||
|
|
||||||
|
assertDefaults()
|
||||||
|
assertBinary(assertDefaults)
|
||||||
|
assertJSON(assertDefaults)
|
||||||
|
|
||||||
|
// set value
|
||||||
|
|
||||||
|
cnr := cidtest.ID()
|
||||||
|
|
||||||
|
var cnrV2 refs.ContainerID
|
||||||
|
cnr.WriteToV2(&cnrV2)
|
||||||
|
|
||||||
|
val.ApplyOnlyTo(cnr)
|
||||||
|
|
||||||
|
val.WriteToV2(&m)
|
||||||
|
|
||||||
|
assertCnr := func() {
|
||||||
|
cCnr, ok := m.GetBody().GetContext().(*v2session.ContainerSessionContext)
|
||||||
|
require.True(t, ok)
|
||||||
|
require.False(t, cCnr.Wildcard())
|
||||||
|
require.Equal(t, &cnrV2, cCnr.ContainerID())
|
||||||
|
}
|
||||||
|
|
||||||
|
assertCnr()
|
||||||
|
assertBinary(assertCnr)
|
||||||
|
assertJSON(assertCnr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContainer_AppliedTo(t *testing.T) {
|
||||||
var x session.Container
|
var x session.Container
|
||||||
|
|
||||||
cnr1 := cidtest.ID()
|
cnr1 := cidtest.ID()
|
||||||
|
@ -161,21 +391,7 @@ func TestContainerAppliedTo(t *testing.T) {
|
||||||
require.False(t, x.AppliedTo(cnr2))
|
require.False(t, x.AppliedTo(cnr2))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainerExp(t *testing.T) {
|
func TestContainer_InvalidAt(t *testing.T) {
|
||||||
var x session.Container
|
|
||||||
|
|
||||||
exp := rand.Uint64()
|
|
||||||
|
|
||||||
require.True(t, x.ExpiredAt(exp))
|
|
||||||
|
|
||||||
x.SetExp(exp)
|
|
||||||
|
|
||||||
require.False(t, x.ExpiredAt(exp-1))
|
|
||||||
require.True(t, x.ExpiredAt(exp))
|
|
||||||
require.True(t, x.ExpiredAt(exp+1))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContainerLifetime(t *testing.T) {
|
|
||||||
var x session.Container
|
var x session.Container
|
||||||
|
|
||||||
nbf := rand.Uint64()
|
nbf := rand.Uint64()
|
||||||
|
@ -196,7 +412,7 @@ func TestContainerLifetime(t *testing.T) {
|
||||||
require.True(t, x.InvalidAt(exp))
|
require.True(t, x.InvalidAt(exp))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainerID(t *testing.T) {
|
func TestContainer_ID(t *testing.T) {
|
||||||
var x session.Container
|
var x session.Container
|
||||||
|
|
||||||
require.Zero(t, x.ID())
|
require.Zero(t, x.ID())
|
||||||
|
@ -208,7 +424,7 @@ func TestContainerID(t *testing.T) {
|
||||||
require.Equal(t, id, x.ID())
|
require.Equal(t, id, x.ID())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainerAuthKey(t *testing.T) {
|
func TestContainer_AssertAuthKey(t *testing.T) {
|
||||||
var x session.Container
|
var x session.Container
|
||||||
|
|
||||||
key := randPublicKey()
|
key := randPublicKey()
|
||||||
|
@ -220,7 +436,64 @@ func TestContainerAuthKey(t *testing.T) {
|
||||||
require.True(t, x.AssertAuthKey(key))
|
require.True(t, x.AssertAuthKey(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainerVerb(t *testing.T) {
|
func TestContainer_ForVerb(t *testing.T) {
|
||||||
|
var val session.Container
|
||||||
|
var m v2session.Token
|
||||||
|
filled := sessiontest.Container()
|
||||||
|
|
||||||
|
assertDefaults := func() {
|
||||||
|
cCnr, ok := m.GetBody().GetContext().(*v2session.ContainerSessionContext)
|
||||||
|
require.True(t, ok)
|
||||||
|
require.Zero(t, cCnr.Verb())
|
||||||
|
}
|
||||||
|
|
||||||
|
assertBinary := func(baseAssert func()) {
|
||||||
|
val2 := filled
|
||||||
|
|
||||||
|
require.NoError(t, val2.Unmarshal(val.Marshal()))
|
||||||
|
baseAssert()
|
||||||
|
}
|
||||||
|
|
||||||
|
assertJSON := func(baseAssert func()) {
|
||||||
|
val2 := filled
|
||||||
|
|
||||||
|
jd, err := val.MarshalJSON()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.NoError(t, val2.UnmarshalJSON(jd))
|
||||||
|
baseAssert()
|
||||||
|
}
|
||||||
|
|
||||||
|
val.WriteToV2(&m)
|
||||||
|
|
||||||
|
assertDefaults()
|
||||||
|
assertBinary(assertDefaults)
|
||||||
|
assertJSON(assertDefaults)
|
||||||
|
|
||||||
|
// set value
|
||||||
|
|
||||||
|
assertVerb := func(verb v2session.ContainerSessionVerb) {
|
||||||
|
cCnr, ok := m.GetBody().GetContext().(*v2session.ContainerSessionContext)
|
||||||
|
require.True(t, ok)
|
||||||
|
require.Equal(t, verb, cCnr.Verb())
|
||||||
|
}
|
||||||
|
|
||||||
|
for from, to := range map[session.ContainerVerb]v2session.ContainerSessionVerb{
|
||||||
|
session.VerbContainerPut: v2session.ContainerVerbPut,
|
||||||
|
session.VerbContainerDelete: v2session.ContainerVerbDelete,
|
||||||
|
session.VerbContainerSetEACL: v2session.ContainerVerbSetEACL,
|
||||||
|
} {
|
||||||
|
val.ForVerb(from)
|
||||||
|
|
||||||
|
val.WriteToV2(&m)
|
||||||
|
|
||||||
|
assertVerb(to)
|
||||||
|
assertBinary(func() { assertVerb(to) })
|
||||||
|
assertJSON(func() { assertVerb(to) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContainer_AssertVerb(t *testing.T) {
|
||||||
var x session.Container
|
var x session.Container
|
||||||
|
|
||||||
const v1, v2 = session.VerbContainerPut, session.VerbContainerDelete
|
const v1, v2 = session.VerbContainerPut, session.VerbContainerDelete
|
||||||
|
@ -233,60 +506,6 @@ func TestContainerVerb(t *testing.T) {
|
||||||
require.False(t, x.AssertVerb(v2))
|
require.False(t, x.AssertVerb(v2))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainerSignature(t *testing.T) {
|
|
||||||
var x session.Container
|
|
||||||
|
|
||||||
const nbf = 11
|
|
||||||
const iat = 22
|
|
||||||
const exp = 33
|
|
||||||
id := uuid.New()
|
|
||||||
key := randPublicKey()
|
|
||||||
cnr := cidtest.ID()
|
|
||||||
verb := session.VerbContainerPut
|
|
||||||
|
|
||||||
signer := randSigner()
|
|
||||||
|
|
||||||
fs := []func(){
|
|
||||||
func() { x.SetNbf(nbf) },
|
|
||||||
func() { x.SetNbf(nbf + 1) },
|
|
||||||
|
|
||||||
func() { x.SetIat(iat) },
|
|
||||||
func() { x.SetIat(iat + 1) },
|
|
||||||
|
|
||||||
func() { x.SetExp(exp) },
|
|
||||||
func() { x.SetExp(exp + 1) },
|
|
||||||
|
|
||||||
func() { x.SetID(id) },
|
|
||||||
func() {
|
|
||||||
idcp := id
|
|
||||||
idcp[0]++
|
|
||||||
x.SetID(idcp)
|
|
||||||
},
|
|
||||||
|
|
||||||
func() { x.SetAuthKey(key) },
|
|
||||||
func() { x.SetAuthKey(randPublicKey()) },
|
|
||||||
|
|
||||||
func() { x.ApplyOnlyTo(cnr) },
|
|
||||||
func() { x.ApplyOnlyTo(cidtest.ID()) },
|
|
||||||
|
|
||||||
func() { x.ForVerb(verb) },
|
|
||||||
func() { x.ForVerb(verb + 1) },
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < len(fs); i += 2 {
|
|
||||||
fs[i]()
|
|
||||||
|
|
||||||
require.NoError(t, x.Sign(signer))
|
|
||||||
require.True(t, x.VerifySignature())
|
|
||||||
|
|
||||||
fs[i+1]()
|
|
||||||
require.False(t, x.VerifySignature())
|
|
||||||
|
|
||||||
fs[i]()
|
|
||||||
require.True(t, x.VerifySignature())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIssuedBy(t *testing.T) {
|
func TestIssuedBy(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
token session.Container
|
token session.Container
|
||||||
|
|
|
@ -1,19 +1,14 @@
|
||||||
package session
|
package session
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"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"
|
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"
|
|
||||||
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/user"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Object represents token of the NeoFS Object session. A session is opened
|
// Object represents token of the NeoFS Object session. A session is opened
|
||||||
|
@ -27,69 +22,90 @@ import (
|
||||||
//
|
//
|
||||||
// Instances can be created using built-in var declaration.
|
// Instances can be created using built-in var declaration.
|
||||||
type Object struct {
|
type Object struct {
|
||||||
lt session.TokenLifetime
|
commonData
|
||||||
|
|
||||||
obj refs.Address
|
verb ObjectVerb
|
||||||
|
|
||||||
c session.ObjectSessionContext
|
cnrSet bool
|
||||||
|
cnr cid.ID
|
||||||
|
|
||||||
body session.TokenBody
|
objSet bool
|
||||||
|
obj oid.ID
|
||||||
sig neofscrypto.Signature
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadFromV2 reads Object from the session.Token message.
|
func (x *Object) readContext(c session.TokenContext, checkFieldPresence bool) error {
|
||||||
|
cObj, ok := c.(*session.ObjectSessionContext)
|
||||||
|
if !ok || cObj == nil {
|
||||||
|
return fmt.Errorf("invalid context %T", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
addr := cObj.GetAddress()
|
||||||
|
if checkFieldPresence && addr == nil {
|
||||||
|
return errors.New("missing object address")
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
cnr := addr.GetContainerID()
|
||||||
|
if x.cnrSet = cnr != nil; x.cnrSet {
|
||||||
|
err := x.cnr.ReadFromV2(*cnr)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid container ID: %w", err)
|
||||||
|
}
|
||||||
|
} else if checkFieldPresence {
|
||||||
|
return errors.New("missing container in object address")
|
||||||
|
}
|
||||||
|
|
||||||
|
obj := addr.GetObjectID()
|
||||||
|
if x.objSet = obj != nil; x.objSet {
|
||||||
|
err = x.obj.ReadFromV2(*obj)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid object ID: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
x.verb = ObjectVerb(cObj.GetVerb())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Object) readFromV2(m session.Token, checkFieldPresence bool) error {
|
||||||
|
return x.commonData.readFromV2(m, checkFieldPresence, x.readContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadFromV2 reads Object from the session.Token message. Checks if the
|
||||||
|
// message conforms to NeoFS API V2 protocol.
|
||||||
//
|
//
|
||||||
// See also WriteToV2.
|
// See also WriteToV2.
|
||||||
func (x *Object) ReadFromV2(m session.Token) error {
|
func (x *Object) ReadFromV2(m session.Token) error {
|
||||||
b := m.GetBody()
|
return x.readFromV2(m, true)
|
||||||
if b == nil {
|
|
||||||
return errors.New("missing body")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bID := b.GetID()
|
func (x Object) writeContext() session.TokenContext {
|
||||||
var id uuid.UUID
|
var c session.ObjectSessionContext
|
||||||
|
c.SetVerb(session.ObjectSessionVerb(x.verb))
|
||||||
|
|
||||||
err := id.UnmarshalBinary(bID)
|
if x.cnrSet || x.objSet {
|
||||||
if err != nil {
|
var addr refs.Address
|
||||||
return fmt.Errorf("invalid binary ID: %w", err)
|
|
||||||
} else if ver := id.Version(); ver != 4 {
|
if x.cnrSet {
|
||||||
return fmt.Errorf("invalid UUID version %s", ver)
|
var cnr refs.ContainerID
|
||||||
|
x.cnr.WriteToV2(&cnr)
|
||||||
|
|
||||||
|
addr.SetContainerID(&cnr)
|
||||||
}
|
}
|
||||||
|
|
||||||
c, ok := b.GetContext().(*session.ObjectSessionContext)
|
if x.objSet {
|
||||||
if !ok || c == nil {
|
var obj refs.ObjectID
|
||||||
return fmt.Errorf("invalid context %T", b.GetContext())
|
x.obj.WriteToV2(&obj)
|
||||||
|
|
||||||
|
addr.SetObjectID(&obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
x.body = *b
|
c.SetAddress(&addr)
|
||||||
|
|
||||||
x.c = *c
|
|
||||||
|
|
||||||
obj := c.GetAddress()
|
|
||||||
|
|
||||||
cnr := obj.GetContainerID()
|
|
||||||
if cnr == nil {
|
|
||||||
return errors.New("missing bound container")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
x.obj = *obj
|
return &c
|
||||||
|
|
||||||
lt := b.GetLifetime()
|
|
||||||
if lt != nil {
|
|
||||||
x.lt = *lt
|
|
||||||
} else {
|
|
||||||
x.lt = session.TokenLifetime{}
|
|
||||||
}
|
|
||||||
|
|
||||||
sig := m.GetSignature()
|
|
||||||
if sig != nil {
|
|
||||||
x.sig.ReadFromV2(*sig)
|
|
||||||
} else {
|
|
||||||
x.sig = neofscrypto.Signature{}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteToV2 writes Object to the session.Token message.
|
// WriteToV2 writes Object to the session.Token message.
|
||||||
|
@ -97,11 +113,7 @@ func (x *Object) ReadFromV2(m session.Token) error {
|
||||||
//
|
//
|
||||||
// See also ReadFromV2.
|
// See also ReadFromV2.
|
||||||
func (x Object) WriteToV2(m *session.Token) {
|
func (x Object) WriteToV2(m *session.Token) {
|
||||||
var sig refs.Signature
|
x.writeToV2(m, x.writeContext)
|
||||||
x.sig.WriteToV2(&sig)
|
|
||||||
|
|
||||||
m.SetBody(&x.body)
|
|
||||||
m.SetSignature(&sig)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marshal encodes Object into a binary format of the NeoFS API protocol
|
// Marshal encodes Object into a binary format of the NeoFS API protocol
|
||||||
|
@ -112,7 +124,7 @@ func (x Object) Marshal() []byte {
|
||||||
var m session.Token
|
var m session.Token
|
||||||
x.WriteToV2(&m)
|
x.WriteToV2(&m)
|
||||||
|
|
||||||
return m.StableMarshal(nil)
|
return x.marshal(x.writeContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unmarshal decodes NeoFS API protocol binary format into the Object
|
// Unmarshal decodes NeoFS API protocol binary format into the Object
|
||||||
|
@ -121,14 +133,7 @@ func (x Object) Marshal() []byte {
|
||||||
//
|
//
|
||||||
// See also Marshal.
|
// See also Marshal.
|
||||||
func (x *Object) Unmarshal(data []byte) error {
|
func (x *Object) Unmarshal(data []byte) error {
|
||||||
var m session.Token
|
return x.unmarshal(data, x.readContext)
|
||||||
|
|
||||||
err := m.Unmarshal(data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return x.ReadFromV2(m)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalJSON encodes Object into a JSON format of the NeoFS API protocol
|
// MarshalJSON encodes Object into a JSON format of the NeoFS API protocol
|
||||||
|
@ -136,10 +141,7 @@ func (x *Object) Unmarshal(data []byte) error {
|
||||||
//
|
//
|
||||||
// See also UnmarshalJSON.
|
// See also UnmarshalJSON.
|
||||||
func (x Object) MarshalJSON() ([]byte, error) {
|
func (x Object) MarshalJSON() ([]byte, error) {
|
||||||
var m session.Token
|
return x.marshalJSON(x.writeContext)
|
||||||
x.WriteToV2(&m)
|
|
||||||
|
|
||||||
return m.MarshalJSON()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSON decodes NeoFS API protocol JSON format into the Object
|
// UnmarshalJSON decodes NeoFS API protocol JSON format into the Object
|
||||||
|
@ -147,14 +149,7 @@ func (x Object) MarshalJSON() ([]byte, error) {
|
||||||
//
|
//
|
||||||
// See also MarshalJSON.
|
// See also MarshalJSON.
|
||||||
func (x *Object) UnmarshalJSON(data []byte) error {
|
func (x *Object) UnmarshalJSON(data []byte) error {
|
||||||
var m session.Token
|
return x.unmarshalJSON(data, x.readContext)
|
||||||
|
|
||||||
err := m.UnmarshalJSON(data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return x.ReadFromV2(m)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sign calculates and writes signature of the Object data.
|
// Sign calculates and writes signature of the Object data.
|
||||||
|
@ -167,19 +162,7 @@ func (x *Object) UnmarshalJSON(data []byte) error {
|
||||||
//
|
//
|
||||||
// See also VerifySignature.
|
// See also VerifySignature.
|
||||||
func (x *Object) Sign(key ecdsa.PrivateKey) error {
|
func (x *Object) Sign(key ecdsa.PrivateKey) error {
|
||||||
var idUser user.ID
|
return x.sign(key, x.writeContext)
|
||||||
user.IDFromKey(&idUser, key.PublicKey)
|
|
||||||
|
|
||||||
var idUserV2 refs.OwnerID
|
|
||||||
idUser.WriteToV2(&idUserV2)
|
|
||||||
|
|
||||||
x.c.SetAddress(&x.obj)
|
|
||||||
|
|
||||||
x.body.SetOwnerID(&idUserV2)
|
|
||||||
x.body.SetLifetime(&x.lt)
|
|
||||||
x.body.SetContext(&x.c)
|
|
||||||
|
|
||||||
return x.sig.Calculate(neofsecdsa.Signer(key), x.body.StableMarshal(nil))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifySignature checks if Object signature is presented and valid.
|
// VerifySignature checks if Object signature is presented and valid.
|
||||||
|
@ -189,7 +172,7 @@ func (x *Object) Sign(key ecdsa.PrivateKey) error {
|
||||||
// See also Sign.
|
// See also Sign.
|
||||||
func (x Object) VerifySignature() bool {
|
func (x Object) VerifySignature() bool {
|
||||||
// TODO: (#233) check owner<->key relation
|
// TODO: (#233) check owner<->key relation
|
||||||
return x.sig.Verify(x.body.StableMarshal(nil))
|
return x.verifySignature(x.writeContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BindContainer binds the Object session to a given container. Each session
|
// BindContainer binds the Object session to a given container. Each session
|
||||||
|
@ -197,10 +180,8 @@ func (x Object) VerifySignature() bool {
|
||||||
//
|
//
|
||||||
// See also AssertContainer.
|
// See also AssertContainer.
|
||||||
func (x *Object) BindContainer(cnr cid.ID) {
|
func (x *Object) BindContainer(cnr cid.ID) {
|
||||||
var cnrV2 refs.ContainerID
|
x.cnr = cnr
|
||||||
cnr.WriteToV2(&cnrV2)
|
x.cnrSet = true
|
||||||
|
|
||||||
x.obj.SetContainerID(&cnrV2)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AssertContainer checks if Object session bound to a given container.
|
// AssertContainer checks if Object session bound to a given container.
|
||||||
|
@ -210,16 +191,7 @@ func (x *Object) BindContainer(cnr cid.ID) {
|
||||||
//
|
//
|
||||||
// See also BindContainer.
|
// See also BindContainer.
|
||||||
func (x Object) AssertContainer(cnr cid.ID) bool {
|
func (x Object) AssertContainer(cnr cid.ID) bool {
|
||||||
cnrV2 := x.obj.GetContainerID()
|
return x.cnr.Equals(cnr)
|
||||||
if cnrV2 == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
// LimitByObject limits session scope to a given object from the container
|
||||||
|
@ -227,10 +199,8 @@ func (x Object) AssertContainer(cnr cid.ID) bool {
|
||||||
//
|
//
|
||||||
// See also AssertObject.
|
// See also AssertObject.
|
||||||
func (x *Object) LimitByObject(obj oid.ID) {
|
func (x *Object) LimitByObject(obj oid.ID) {
|
||||||
var objV2 refs.ObjectID
|
x.obj = obj
|
||||||
obj.WriteToV2(&objV2)
|
x.objSet = true
|
||||||
|
|
||||||
x.obj.SetObjectID(&objV2)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AssertObject checks if Object session is applied to a given object.
|
// AssertObject checks if Object session is applied to a given object.
|
||||||
|
@ -239,16 +209,7 @@ func (x *Object) LimitByObject(obj oid.ID) {
|
||||||
//
|
//
|
||||||
// See also LimitByObject.
|
// See also LimitByObject.
|
||||||
func (x Object) AssertObject(obj oid.ID) bool {
|
func (x Object) AssertObject(obj oid.ID) bool {
|
||||||
objV2 := x.obj.GetObjectID()
|
return !x.objSet || x.obj.Equals(obj)
|
||||||
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.
|
||||||
|
@ -271,7 +232,7 @@ const (
|
||||||
//
|
//
|
||||||
// See also AssertVerb.
|
// See also AssertVerb.
|
||||||
func (x *Object) ForVerb(verb ObjectVerb) {
|
func (x *Object) ForVerb(verb ObjectVerb) {
|
||||||
x.c.SetVerb(session.ObjectSessionVerb(verb))
|
x.verb = verb
|
||||||
}
|
}
|
||||||
|
|
||||||
// AssertVerb checks if Object relates to one of the given object operations.
|
// AssertVerb checks if Object relates to one of the given object operations.
|
||||||
|
@ -280,10 +241,8 @@ func (x *Object) ForVerb(verb ObjectVerb) {
|
||||||
//
|
//
|
||||||
// See also ForVerb.
|
// See also ForVerb.
|
||||||
func (x Object) AssertVerb(verbs ...ObjectVerb) bool {
|
func (x Object) AssertVerb(verbs ...ObjectVerb) bool {
|
||||||
verb := ObjectVerb(x.c.GetVerb())
|
|
||||||
|
|
||||||
for i := range verbs {
|
for i := range verbs {
|
||||||
if verbs[i] == verb {
|
if verbs[i] == x.verb {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -291,129 +250,11 @@ func (x Object) AssertVerb(verbs ...ObjectVerb) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetExp sets "exp" (expiration time) claim which identifies the expiration time
|
|
||||||
// (in NeoFS epochs) on or after which the Object MUST NOT be accepted for
|
|
||||||
// processing. The processing of the "exp" claim requires that the current
|
|
||||||
// epoch MUST be before the expiration epoch listed in the "exp" claim.
|
|
||||||
//
|
|
||||||
// Naming is inspired by https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.4.
|
|
||||||
//
|
|
||||||
// See also ExpiredAt.
|
|
||||||
func (x *Object) SetExp(exp uint64) {
|
|
||||||
x.lt.SetExp(exp)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExpiredAt asserts "exp" claim.
|
// ExpiredAt asserts "exp" claim.
|
||||||
//
|
//
|
||||||
// Zero Object is expired in any epoch.
|
// Zero Object is expired in any epoch.
|
||||||
//
|
//
|
||||||
// See also SetExp.
|
// See also SetExp.
|
||||||
func (x Object) ExpiredAt(epoch uint64) bool {
|
func (x Object) ExpiredAt(epoch uint64) bool {
|
||||||
return x.lt.GetExp() <= epoch
|
return x.expiredAt(epoch)
|
||||||
}
|
|
||||||
|
|
||||||
// SetNbf sets "nbf" (not before) claim which identifies the time (in NeoFS
|
|
||||||
// epochs) before which the Object MUST NOT be accepted for processing.
|
|
||||||
// The processing of the "nbf" claim requires that the current date/time MUST be
|
|
||||||
// after or equal to the not-before date/time listed in the "nbf" claim.
|
|
||||||
//
|
|
||||||
// Naming is inspired by https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.5.
|
|
||||||
//
|
|
||||||
// See also InvalidAt.
|
|
||||||
func (x *Object) SetNbf(nbf uint64) {
|
|
||||||
x.lt.SetNbf(nbf)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetIat sets "iat" (issued at) claim which identifies the time (in NeoFS
|
|
||||||
// epochs) at which the Object was issued. This claim can be used to
|
|
||||||
// determine the age of the Object.
|
|
||||||
//
|
|
||||||
// Naming is inspired by https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.6.
|
|
||||||
//
|
|
||||||
// See also InvalidAt.
|
|
||||||
func (x *Object) SetIat(iat uint64) {
|
|
||||||
x.lt.SetIat(iat)
|
|
||||||
}
|
|
||||||
|
|
||||||
// InvalidAt asserts "exp", "nbf" and "iat" claims.
|
|
||||||
//
|
|
||||||
// Zero Object is invalid in any epoch.
|
|
||||||
//
|
|
||||||
// See also SetExp, SetNbf, SetIat.
|
|
||||||
func (x Object) InvalidAt(epoch uint64) bool {
|
|
||||||
return x.lt.GetNbf() > epoch || x.lt.GetIat() > epoch || x.ExpiredAt(epoch)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetID sets a unique identifier for the session. The identifier value MUST be
|
|
||||||
// assigned in a manner that ensures that there is a negligible probability
|
|
||||||
// that the same value will be accidentally assigned to a different session.
|
|
||||||
//
|
|
||||||
// ID format MUST be UUID version 4 (random). uuid.New can be used to generate
|
|
||||||
// a new ID. See https://datatracker.ietf.org/doc/html/rfc4122 and
|
|
||||||
// github.com/google/uuid package docs for details.
|
|
||||||
//
|
|
||||||
// See also ID.
|
|
||||||
func (x *Object) SetID(id uuid.UUID) {
|
|
||||||
x.body.SetID(id[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
// ID returns a unique identifier for the session.
|
|
||||||
//
|
|
||||||
// Zero Object has empty UUID (all zeros, see uuid.Nil) which is legitimate
|
|
||||||
// but most likely not suitable.
|
|
||||||
//
|
|
||||||
// See also SetID.
|
|
||||||
func (x Object) ID() uuid.UUID {
|
|
||||||
data := x.body.GetID()
|
|
||||||
if data == nil {
|
|
||||||
return uuid.Nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var id uuid.UUID
|
|
||||||
|
|
||||||
err := id.UnmarshalBinary(x.body.GetID())
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("unexpected error from UUID.UnmarshalBinary: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetAuthKey public key corresponding to the private key bound to the session.
|
|
||||||
//
|
|
||||||
// See also AssertAuthKey.
|
|
||||||
func (x *Object) SetAuthKey(key neofscrypto.PublicKey) {
|
|
||||||
bKey := make([]byte, key.MaxEncodedSize())
|
|
||||||
bKey = bKey[:key.Encode(bKey)]
|
|
||||||
|
|
||||||
x.body.SetSessionKey(bKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AssertAuthKey asserts public key bound to the session.
|
|
||||||
//
|
|
||||||
// Zero Object fails the check.
|
|
||||||
//
|
|
||||||
// See also SetAuthKey.
|
|
||||||
func (x Object) AssertAuthKey(key neofscrypto.PublicKey) bool {
|
|
||||||
bKey := make([]byte, key.MaxEncodedSize())
|
|
||||||
bKey = bKey[:key.Encode(bKey)]
|
|
||||||
|
|
||||||
return bytes.Equal(bKey, x.body.GetSessionKey())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Issuer returns user ID of the session issuer.
|
|
||||||
//
|
|
||||||
// Makes sense only for signed Object instances. For unsigned instances,
|
|
||||||
// Issuer returns zero user.ID.
|
|
||||||
//
|
|
||||||
// See also Sign.
|
|
||||||
func (x Object) Issuer() user.ID {
|
|
||||||
var issuer user.ID
|
|
||||||
|
|
||||||
issuerV2 := x.body.GetOwnerID()
|
|
||||||
if issuerV2 != nil {
|
|
||||||
_ = issuer.ReadFromV2(*issuerV2)
|
|
||||||
}
|
|
||||||
|
|
||||||
return issuer
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,15 +9,18 @@ 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/neo-go/pkg/util/slice"
|
||||||
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
"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"
|
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"
|
||||||
|
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
|
||||||
oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/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"
|
||||||
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"
|
||||||
|
usertest "github.com/nspcc-dev/neofs-sdk-go/user/test"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -35,143 +38,378 @@ func randPublicKey() neofscrypto.PublicKey {
|
||||||
return (*neofsecdsa.PublicKey)(&k)
|
return (*neofsecdsa.PublicKey)(&k)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestObject_ReadFromV2(t *testing.T) {
|
func TestObjectProtocolV2(t *testing.T) {
|
||||||
var x session.Object
|
var validV2 v2session.Token
|
||||||
var m v2session.Token
|
|
||||||
var b v2session.TokenBody
|
var body v2session.TokenBody
|
||||||
var c v2session.ObjectSessionContext
|
validV2.SetBody(&body)
|
||||||
|
|
||||||
|
// ID
|
||||||
id := uuid.New()
|
id := uuid.New()
|
||||||
|
binID, err := id.MarshalBinary()
|
||||||
|
require.NoError(t, err)
|
||||||
|
restoreID := func() {
|
||||||
|
body.SetID(binID)
|
||||||
|
}
|
||||||
|
restoreID()
|
||||||
|
|
||||||
|
// Owner
|
||||||
|
usr := *usertest.ID()
|
||||||
|
var usrV2 refs.OwnerID
|
||||||
|
usr.WriteToV2(&usrV2)
|
||||||
|
restoreUser := func() {
|
||||||
|
body.SetOwnerID(&usrV2)
|
||||||
|
}
|
||||||
|
restoreUser()
|
||||||
|
|
||||||
|
// Lifetime
|
||||||
|
var lifetime v2session.TokenLifetime
|
||||||
|
lifetime.SetIat(1)
|
||||||
|
lifetime.SetNbf(2)
|
||||||
|
lifetime.SetExp(3)
|
||||||
|
restoreLifetime := func() {
|
||||||
|
body.SetLifetime(&lifetime)
|
||||||
|
}
|
||||||
|
restoreLifetime()
|
||||||
|
|
||||||
|
// Session key
|
||||||
|
signer := randSigner()
|
||||||
|
authKey := neofsecdsa.PublicKey(signer.PublicKey)
|
||||||
|
binAuthKey := make([]byte, authKey.MaxEncodedSize())
|
||||||
|
binAuthKey = binAuthKey[:authKey.Encode(binAuthKey)]
|
||||||
|
restoreAuthKey := func() {
|
||||||
|
body.SetSessionKey(binAuthKey)
|
||||||
|
}
|
||||||
|
restoreAuthKey()
|
||||||
|
|
||||||
|
// Context
|
||||||
|
cnr := cidtest.ID()
|
||||||
|
obj := oidtest.ID()
|
||||||
|
var addr oid.Address
|
||||||
|
addr.SetContainer(cnr)
|
||||||
|
addr.SetObject(obj)
|
||||||
|
var addrV2 refs.Address
|
||||||
|
addr.WriteToV2(&addrV2)
|
||||||
|
var cObj v2session.ObjectSessionContext
|
||||||
|
restoreCtx := func() {
|
||||||
|
cObj.SetAddress(&addrV2)
|
||||||
|
body.SetContext(&cObj)
|
||||||
|
}
|
||||||
|
restoreCtx()
|
||||||
|
|
||||||
|
// Signature
|
||||||
|
var sig refs.Signature
|
||||||
|
restoreSig := func() {
|
||||||
|
validV2.SetSignature(&sig)
|
||||||
|
}
|
||||||
|
restoreSig()
|
||||||
|
|
||||||
|
// TODO(@cthulhu-rider): #260 use functionality for message corruption
|
||||||
|
|
||||||
|
for _, testcase := range []struct {
|
||||||
|
name string
|
||||||
|
corrupt []func()
|
||||||
|
restore func()
|
||||||
|
assert func(session.Object)
|
||||||
|
breakSign func(*v2session.Token)
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Signature",
|
||||||
|
corrupt: []func(){
|
||||||
|
func() {
|
||||||
|
validV2.SetSignature(nil)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
restore: restoreSig,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ID",
|
||||||
|
corrupt: []func(){
|
||||||
|
func() {
|
||||||
|
body.SetID([]byte{1, 2, 3})
|
||||||
|
},
|
||||||
|
func() {
|
||||||
|
id, err := uuid.NewDCEPerson()
|
||||||
|
require.NoError(t, err)
|
||||||
|
bindID, err := id.MarshalBinary()
|
||||||
|
require.NoError(t, err)
|
||||||
|
body.SetID(bindID)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
restore: restoreID,
|
||||||
|
assert: func(val session.Object) {
|
||||||
|
require.Equal(t, id, val.ID())
|
||||||
|
},
|
||||||
|
breakSign: func(m *v2session.Token) {
|
||||||
|
id := m.GetBody().GetID()
|
||||||
|
id[len(id)-1]++
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "User",
|
||||||
|
corrupt: []func(){
|
||||||
|
func() {
|
||||||
|
var brokenUsrV2 refs.OwnerID
|
||||||
|
brokenUsrV2.SetValue(append(usrV2.GetValue(), 1))
|
||||||
|
body.SetOwnerID(&brokenUsrV2)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
restore: restoreUser,
|
||||||
|
assert: func(val session.Object) {
|
||||||
|
require.Equal(t, usr, val.Issuer())
|
||||||
|
},
|
||||||
|
breakSign: func(m *v2session.Token) {
|
||||||
|
id := m.GetBody().GetOwnerID().GetValue()
|
||||||
|
copy(id, usertest.ID().WalletBytes())
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Lifetime",
|
||||||
|
corrupt: []func(){
|
||||||
|
func() {
|
||||||
|
body.SetLifetime(nil)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
restore: restoreLifetime,
|
||||||
|
assert: func(val session.Object) {
|
||||||
|
require.True(t, val.InvalidAt(1))
|
||||||
|
require.False(t, val.InvalidAt(2))
|
||||||
|
require.True(t, val.InvalidAt(3))
|
||||||
|
},
|
||||||
|
breakSign: func(m *v2session.Token) {
|
||||||
|
lt := m.GetBody().GetLifetime()
|
||||||
|
lt.SetIat(lt.GetIat() + 1)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Auth key",
|
||||||
|
corrupt: []func(){
|
||||||
|
func() {
|
||||||
|
body.SetSessionKey(nil)
|
||||||
|
},
|
||||||
|
func() {
|
||||||
|
body.SetSessionKey([]byte{})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
restore: restoreAuthKey,
|
||||||
|
assert: func(val session.Object) {
|
||||||
|
require.True(t, val.AssertAuthKey(&authKey))
|
||||||
|
},
|
||||||
|
breakSign: func(m *v2session.Token) {
|
||||||
|
body := m.GetBody()
|
||||||
|
key := body.GetSessionKey()
|
||||||
|
cp := slice.Copy(key)
|
||||||
|
cp[len(cp)-1]++
|
||||||
|
body.SetSessionKey(cp)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Context",
|
||||||
|
corrupt: []func(){
|
||||||
|
func() {
|
||||||
|
body.SetContext(nil)
|
||||||
|
},
|
||||||
|
func() {
|
||||||
|
cObj.SetAddress(new(refs.Address))
|
||||||
|
},
|
||||||
|
func() {
|
||||||
|
var brokenCnr refs.ContainerID
|
||||||
|
brokenCnr.SetValue(append(addrV2.GetContainerID().GetValue(), 1))
|
||||||
|
var addr refs.Address
|
||||||
|
addr.SetContainerID(&brokenCnr)
|
||||||
|
cObj.SetAddress(&addr)
|
||||||
|
},
|
||||||
|
func() {
|
||||||
|
var brokenObj refs.ObjectID
|
||||||
|
brokenObj.SetValue(append(addrV2.GetContainerID().GetValue(), 1))
|
||||||
|
var addr refs.Address
|
||||||
|
addr.SetObjectID(&brokenObj)
|
||||||
|
cObj.SetAddress(&addr)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
restore: restoreCtx,
|
||||||
|
assert: func(val session.Object) {
|
||||||
|
require.True(t, val.AssertContainer(cnr))
|
||||||
|
require.False(t, val.AssertContainer(cidtest.ID()))
|
||||||
|
require.True(t, val.AssertObject(obj))
|
||||||
|
require.False(t, val.AssertObject(oidtest.ID()))
|
||||||
|
},
|
||||||
|
breakSign: func(m *v2session.Token) {
|
||||||
|
cnr := m.GetBody().GetContext().(*v2session.ObjectSessionContext).GetAddress().GetContainerID().GetValue()
|
||||||
|
cnr[len(cnr)-1]++
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
var val session.Object
|
||||||
|
|
||||||
|
for i, corrupt := range testcase.corrupt {
|
||||||
|
corrupt()
|
||||||
|
require.Error(t, val.ReadFromV2(validV2), testcase.name, fmt.Sprintf("corrupt #%d", i))
|
||||||
|
|
||||||
|
testcase.restore()
|
||||||
|
require.NoError(t, val.ReadFromV2(validV2), testcase.name, fmt.Sprintf("corrupt #%d", i))
|
||||||
|
|
||||||
|
if testcase.assert != nil {
|
||||||
|
testcase.assert(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
if testcase.breakSign != nil {
|
||||||
|
require.NoError(t, val.Sign(signer), testcase.name)
|
||||||
|
require.True(t, val.VerifySignature(), testcase.name)
|
||||||
|
|
||||||
|
var signedV2 v2session.Token
|
||||||
|
val.WriteToV2(&signedV2)
|
||||||
|
|
||||||
|
var restored session.Object
|
||||||
|
require.NoError(t, restored.ReadFromV2(signedV2), testcase.name)
|
||||||
|
require.True(t, restored.VerifySignature(), testcase.name)
|
||||||
|
|
||||||
|
testcase.breakSign(&signedV2)
|
||||||
|
|
||||||
|
require.NoError(t, restored.ReadFromV2(signedV2), testcase.name)
|
||||||
|
require.False(t, restored.VerifySignature(), testcase.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestObject_WriteToV2(t *testing.T) {
|
||||||
|
var val session.Object
|
||||||
|
|
||||||
|
assert := func(baseAssert func(v2session.Token)) {
|
||||||
|
var m v2session.Token
|
||||||
|
val.WriteToV2(&m)
|
||||||
|
baseAssert(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID
|
||||||
|
id := uuid.New()
|
||||||
|
|
||||||
|
binID, err := id.MarshalBinary()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
val.SetID(id)
|
||||||
|
assert(func(m v2session.Token) {
|
||||||
|
require.Equal(t, binID, m.GetBody().GetID())
|
||||||
|
})
|
||||||
|
|
||||||
|
// Owner/Signature
|
||||||
|
signer := randSigner()
|
||||||
|
|
||||||
|
require.NoError(t, val.Sign(signer))
|
||||||
|
|
||||||
|
var usr user.ID
|
||||||
|
user.IDFromKey(&usr, signer.PublicKey)
|
||||||
|
|
||||||
|
var usrV2 refs.OwnerID
|
||||||
|
usr.WriteToV2(&usrV2)
|
||||||
|
|
||||||
|
assert(func(m v2session.Token) {
|
||||||
|
require.Equal(t, &usrV2, m.GetBody().GetOwnerID())
|
||||||
|
|
||||||
|
sig := m.GetSignature()
|
||||||
|
require.NotZero(t, sig.GetKey())
|
||||||
|
require.NotZero(t, sig.GetSign())
|
||||||
|
})
|
||||||
|
|
||||||
|
// Lifetime
|
||||||
|
const iat, nbf, exp = 1, 2, 3
|
||||||
|
val.SetIat(iat)
|
||||||
|
val.SetNbf(nbf)
|
||||||
|
val.SetExp(exp)
|
||||||
|
|
||||||
|
assert(func(m v2session.Token) {
|
||||||
|
lt := m.GetBody().GetLifetime()
|
||||||
|
require.EqualValues(t, iat, lt.GetIat())
|
||||||
|
require.EqualValues(t, nbf, lt.GetNbf())
|
||||||
|
require.EqualValues(t, exp, lt.GetExp())
|
||||||
|
})
|
||||||
|
|
||||||
|
// Context
|
||||||
|
assert(func(m v2session.Token) {
|
||||||
|
cCnr, ok := m.GetBody().GetContext().(*v2session.ObjectSessionContext)
|
||||||
|
require.True(t, ok)
|
||||||
|
require.Zero(t, cCnr.GetAddress())
|
||||||
|
})
|
||||||
|
|
||||||
cnr := cidtest.ID()
|
cnr := cidtest.ID()
|
||||||
|
|
||||||
var cnrV2 refs.ContainerID
|
var cnrV2 refs.ContainerID
|
||||||
cnr.WriteToV2(&cnrV2)
|
cnr.WriteToV2(&cnrV2)
|
||||||
|
|
||||||
var addrV2 refs.Address
|
obj := oidtest.ID()
|
||||||
addrV2.SetContainerID(&cnrV2)
|
|
||||||
|
|
||||||
t.Run("protocol violation", func(t *testing.T) {
|
var objV2 refs.ObjectID
|
||||||
require.Error(t, x.ReadFromV2(m))
|
|
||||||
|
|
||||||
m.SetBody(&b)
|
|
||||||
|
|
||||||
require.Error(t, x.ReadFromV2(m))
|
|
||||||
|
|
||||||
b.SetID(id[:])
|
|
||||||
|
|
||||||
require.Error(t, x.ReadFromV2(m))
|
|
||||||
|
|
||||||
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) {
|
|
||||||
require.NoError(t, x.ReadFromV2(m))
|
|
||||||
require.True(t, x.AssertContainer(cnr))
|
|
||||||
|
|
||||||
obj := oidtest.Address()
|
|
||||||
|
|
||||||
var objV2 refs.Address
|
|
||||||
obj.WriteToV2(&objV2)
|
obj.WriteToV2(&objV2)
|
||||||
|
|
||||||
c.SetAddress(&objV2)
|
val.BindContainer(cnr)
|
||||||
|
val.LimitByObject(obj)
|
||||||
|
|
||||||
require.NoError(t, x.ReadFromV2(m))
|
assert(func(m v2session.Token) {
|
||||||
require.True(t, x.AssertContainer(obj.Container()))
|
cCnr, ok := m.GetBody().GetContext().(*v2session.ObjectSessionContext)
|
||||||
require.True(t, x.AssertObject(obj.Object()))
|
require.True(t, ok)
|
||||||
})
|
require.Equal(t, &cnrV2, cCnr.GetAddress().GetContainerID())
|
||||||
|
require.Equal(t, &objV2, cCnr.GetAddress().GetObjectID())
|
||||||
t.Run("verb", func(t *testing.T) {
|
|
||||||
require.NoError(t, x.ReadFromV2(m))
|
|
||||||
require.True(t, x.AssertVerb(0))
|
|
||||||
|
|
||||||
verb := v2session.ObjectSessionVerb(rand.Uint32())
|
|
||||||
|
|
||||||
c.SetVerb(verb)
|
|
||||||
|
|
||||||
require.NoError(t, x.ReadFromV2(m))
|
|
||||||
require.True(t, x.AssertVerb(session.ObjectVerb(verb)))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("id", func(t *testing.T) {
|
|
||||||
id := uuid.New()
|
|
||||||
bID := id[:]
|
|
||||||
|
|
||||||
b.SetID(bID)
|
|
||||||
|
|
||||||
require.NoError(t, x.ReadFromV2(m))
|
|
||||||
require.Equal(t, id, x.ID())
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("lifetime", func(t *testing.T) {
|
|
||||||
const nbf, iat, exp = 11, 22, 33
|
|
||||||
|
|
||||||
var lt v2session.TokenLifetime
|
|
||||||
lt.SetNbf(nbf)
|
|
||||||
lt.SetIat(iat)
|
|
||||||
lt.SetExp(exp)
|
|
||||||
|
|
||||||
b.SetLifetime(<)
|
|
||||||
|
|
||||||
require.NoError(t, x.ReadFromV2(m))
|
|
||||||
require.False(t, x.ExpiredAt(exp-1))
|
|
||||||
require.True(t, x.ExpiredAt(exp))
|
|
||||||
require.True(t, x.ExpiredAt(exp+1))
|
|
||||||
require.True(t, x.InvalidAt(nbf-1))
|
|
||||||
require.True(t, x.InvalidAt(iat-1))
|
|
||||||
require.False(t, x.InvalidAt(iat))
|
|
||||||
require.False(t, x.InvalidAt(exp-1))
|
|
||||||
require.True(t, x.InvalidAt(exp))
|
|
||||||
require.True(t, x.InvalidAt(exp+1))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("session key", func(t *testing.T) {
|
|
||||||
key := randPublicKey()
|
|
||||||
|
|
||||||
bKey := make([]byte, key.MaxEncodedSize())
|
|
||||||
bKey = bKey[:key.Encode(bKey)]
|
|
||||||
|
|
||||||
b.SetSessionKey(bKey)
|
|
||||||
|
|
||||||
require.NoError(t, x.ReadFromV2(m))
|
|
||||||
require.True(t, x.AssertAuthKey(key))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEncodingObject(t *testing.T) {
|
|
||||||
tok := *sessiontest.ObjectSigned()
|
|
||||||
|
|
||||||
t.Run("binary", func(t *testing.T) {
|
|
||||||
data := tok.Marshal()
|
|
||||||
|
|
||||||
var tok2 session.Object
|
|
||||||
require.NoError(t, tok2.Unmarshal(data))
|
|
||||||
|
|
||||||
require.Equal(t, tok, tok2)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("json", func(t *testing.T) {
|
|
||||||
data, err := tok.MarshalJSON()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
var tok2 session.Object
|
|
||||||
require.NoError(t, tok2.UnmarshalJSON(data))
|
|
||||||
|
|
||||||
require.Equal(t, tok, tok2)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestObject_BindContainer(t *testing.T) {
|
func TestObject_BindContainer(t *testing.T) {
|
||||||
|
var val session.Object
|
||||||
|
var m v2session.Token
|
||||||
|
filled := sessiontest.Object()
|
||||||
|
|
||||||
|
assertDefaults := func() {
|
||||||
|
cCnr, ok := m.GetBody().GetContext().(*v2session.ObjectSessionContext)
|
||||||
|
require.True(t, ok)
|
||||||
|
require.Zero(t, cCnr.GetAddress())
|
||||||
|
}
|
||||||
|
|
||||||
|
assertBinary := func(baseAssert func()) {
|
||||||
|
val2 := filled
|
||||||
|
|
||||||
|
require.NoError(t, val2.Unmarshal(val.Marshal()))
|
||||||
|
baseAssert()
|
||||||
|
}
|
||||||
|
|
||||||
|
assertJSON := func(baseAssert func()) {
|
||||||
|
val2 := filled
|
||||||
|
|
||||||
|
jd, err := val.MarshalJSON()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.NoError(t, val2.UnmarshalJSON(jd))
|
||||||
|
baseAssert()
|
||||||
|
}
|
||||||
|
|
||||||
|
val.WriteToV2(&m)
|
||||||
|
|
||||||
|
assertDefaults()
|
||||||
|
assertBinary(assertDefaults)
|
||||||
|
assertJSON(assertDefaults)
|
||||||
|
|
||||||
|
// set value
|
||||||
|
|
||||||
|
cnr := cidtest.ID()
|
||||||
|
|
||||||
|
var cnrV2 refs.ContainerID
|
||||||
|
cnr.WriteToV2(&cnrV2)
|
||||||
|
|
||||||
|
val.BindContainer(cnr)
|
||||||
|
|
||||||
|
val.WriteToV2(&m)
|
||||||
|
|
||||||
|
assertCnr := func() {
|
||||||
|
cObj, ok := m.GetBody().GetContext().(*v2session.ObjectSessionContext)
|
||||||
|
require.True(t, ok)
|
||||||
|
require.Equal(t, &cnrV2, cObj.GetAddress().GetContainerID())
|
||||||
|
}
|
||||||
|
|
||||||
|
assertCnr()
|
||||||
|
assertBinary(assertCnr)
|
||||||
|
assertJSON(assertCnr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestObject_AssertContainer(t *testing.T) {
|
||||||
var x session.Object
|
var x session.Object
|
||||||
|
|
||||||
cnr := cidtest.ID()
|
cnr := cidtest.ID()
|
||||||
|
@ -184,6 +422,62 @@ func TestObject_BindContainer(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestObject_LimitByObject(t *testing.T) {
|
func TestObject_LimitByObject(t *testing.T) {
|
||||||
|
var val session.Object
|
||||||
|
var m v2session.Token
|
||||||
|
filled := sessiontest.Object()
|
||||||
|
|
||||||
|
assertDefaults := func() {
|
||||||
|
cCnr, ok := m.GetBody().GetContext().(*v2session.ObjectSessionContext)
|
||||||
|
require.True(t, ok)
|
||||||
|
require.Zero(t, cCnr.GetAddress())
|
||||||
|
}
|
||||||
|
|
||||||
|
assertBinary := func(baseAssert func()) {
|
||||||
|
val2 := filled
|
||||||
|
|
||||||
|
require.NoError(t, val2.Unmarshal(val.Marshal()))
|
||||||
|
baseAssert()
|
||||||
|
}
|
||||||
|
|
||||||
|
assertJSON := func(baseAssert func()) {
|
||||||
|
val2 := filled
|
||||||
|
|
||||||
|
jd, err := val.MarshalJSON()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.NoError(t, val2.UnmarshalJSON(jd))
|
||||||
|
baseAssert()
|
||||||
|
}
|
||||||
|
|
||||||
|
val.WriteToV2(&m)
|
||||||
|
|
||||||
|
assertDefaults()
|
||||||
|
assertBinary(assertDefaults)
|
||||||
|
assertJSON(assertDefaults)
|
||||||
|
|
||||||
|
// set value
|
||||||
|
|
||||||
|
obj := oidtest.ID()
|
||||||
|
|
||||||
|
var objV2 refs.ObjectID
|
||||||
|
obj.WriteToV2(&objV2)
|
||||||
|
|
||||||
|
val.LimitByObject(obj)
|
||||||
|
|
||||||
|
val.WriteToV2(&m)
|
||||||
|
|
||||||
|
assertObj := func() {
|
||||||
|
cObj, ok := m.GetBody().GetContext().(*v2session.ObjectSessionContext)
|
||||||
|
require.True(t, ok)
|
||||||
|
require.Equal(t, &objV2, cObj.GetAddress().GetObjectID())
|
||||||
|
}
|
||||||
|
|
||||||
|
assertObj()
|
||||||
|
assertBinary(assertObj)
|
||||||
|
assertJSON(assertObj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestObject_AssertObject(t *testing.T) {
|
||||||
var x session.Object
|
var x session.Object
|
||||||
|
|
||||||
obj := oidtest.ID()
|
obj := oidtest.ID()
|
||||||
|
@ -198,21 +492,7 @@ func TestObject_LimitByObject(t *testing.T) {
|
||||||
require.False(t, x.AssertObject(obj2))
|
require.False(t, x.AssertObject(obj2))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestObjectExp(t *testing.T) {
|
func TestObject_InvalidAt(t *testing.T) {
|
||||||
var x session.Object
|
|
||||||
|
|
||||||
exp := rand.Uint64()
|
|
||||||
|
|
||||||
require.True(t, x.ExpiredAt(exp))
|
|
||||||
|
|
||||||
x.SetExp(exp)
|
|
||||||
|
|
||||||
require.False(t, x.ExpiredAt(exp-1))
|
|
||||||
require.True(t, x.ExpiredAt(exp))
|
|
||||||
require.True(t, x.ExpiredAt(exp+1))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestObjectLifetime(t *testing.T) {
|
|
||||||
var x session.Object
|
var x session.Object
|
||||||
|
|
||||||
nbf := rand.Uint64()
|
nbf := rand.Uint64()
|
||||||
|
@ -233,7 +513,7 @@ func TestObjectLifetime(t *testing.T) {
|
||||||
require.True(t, x.InvalidAt(exp))
|
require.True(t, x.InvalidAt(exp))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestObjectID(t *testing.T) {
|
func TestObject_ID(t *testing.T) {
|
||||||
var x session.Object
|
var x session.Object
|
||||||
|
|
||||||
require.Zero(t, x.ID())
|
require.Zero(t, x.ID())
|
||||||
|
@ -245,7 +525,7 @@ func TestObjectID(t *testing.T) {
|
||||||
require.Equal(t, id, x.ID())
|
require.Equal(t, id, x.ID())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestObjectAuthKey(t *testing.T) {
|
func TestObject_AssertAuthKey(t *testing.T) {
|
||||||
var x session.Object
|
var x session.Object
|
||||||
|
|
||||||
key := randPublicKey()
|
key := randPublicKey()
|
||||||
|
@ -257,7 +537,68 @@ func TestObjectAuthKey(t *testing.T) {
|
||||||
require.True(t, x.AssertAuthKey(key))
|
require.True(t, x.AssertAuthKey(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestObjectVerb(t *testing.T) {
|
func TestObject_ForVerb(t *testing.T) {
|
||||||
|
var val session.Object
|
||||||
|
var m v2session.Token
|
||||||
|
filled := sessiontest.Object()
|
||||||
|
|
||||||
|
assertDefaults := func() {
|
||||||
|
cCnr, ok := m.GetBody().GetContext().(*v2session.ObjectSessionContext)
|
||||||
|
require.True(t, ok)
|
||||||
|
require.Zero(t, cCnr.GetVerb())
|
||||||
|
}
|
||||||
|
|
||||||
|
assertBinary := func(baseAssert func()) {
|
||||||
|
val2 := filled
|
||||||
|
|
||||||
|
require.NoError(t, val2.Unmarshal(val.Marshal()))
|
||||||
|
baseAssert()
|
||||||
|
}
|
||||||
|
|
||||||
|
assertJSON := func(baseAssert func()) {
|
||||||
|
val2 := filled
|
||||||
|
|
||||||
|
jd, err := val.MarshalJSON()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.NoError(t, val2.UnmarshalJSON(jd))
|
||||||
|
baseAssert()
|
||||||
|
}
|
||||||
|
|
||||||
|
val.WriteToV2(&m)
|
||||||
|
|
||||||
|
assertDefaults()
|
||||||
|
assertBinary(assertDefaults)
|
||||||
|
assertJSON(assertDefaults)
|
||||||
|
|
||||||
|
// set value
|
||||||
|
|
||||||
|
assertVerb := func(verb v2session.ObjectSessionVerb) {
|
||||||
|
cCnr, ok := m.GetBody().GetContext().(*v2session.ObjectSessionContext)
|
||||||
|
require.True(t, ok)
|
||||||
|
require.Equal(t, verb, cCnr.GetVerb())
|
||||||
|
}
|
||||||
|
|
||||||
|
for from, to := range map[session.ObjectVerb]v2session.ObjectSessionVerb{
|
||||||
|
session.VerbObjectPut: v2session.ObjectVerbPut,
|
||||||
|
session.VerbObjectGet: v2session.ObjectVerbGet,
|
||||||
|
session.VerbObjectHead: v2session.ObjectVerbHead,
|
||||||
|
session.VerbObjectSearch: v2session.ObjectVerbSearch,
|
||||||
|
session.VerbObjectRangeHash: v2session.ObjectVerbRangeHash,
|
||||||
|
session.VerbObjectRange: v2session.ObjectVerbRange,
|
||||||
|
session.VerbObjectDelete: v2session.ObjectVerbDelete,
|
||||||
|
} {
|
||||||
|
val.ForVerb(from)
|
||||||
|
|
||||||
|
val.WriteToV2(&m)
|
||||||
|
|
||||||
|
assertVerb(to)
|
||||||
|
assertBinary(func() { assertVerb(to) })
|
||||||
|
assertJSON(func() { assertVerb(to) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestObject_AssertVerb(t *testing.T) {
|
||||||
var x session.Object
|
var x session.Object
|
||||||
|
|
||||||
const v1, v2 = session.VerbObjectGet, session.VerbObjectPut
|
const v1, v2 = session.VerbObjectGet, session.VerbObjectPut
|
||||||
|
@ -271,64 +612,6 @@ func TestObjectVerb(t *testing.T) {
|
||||||
require.True(t, x.AssertVerb(v2, v1))
|
require.True(t, x.AssertVerb(v2, v1))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestObjectSignature(t *testing.T) {
|
|
||||||
var x session.Object
|
|
||||||
|
|
||||||
const nbf = 11
|
|
||||||
const iat = 22
|
|
||||||
const exp = 33
|
|
||||||
id := uuid.New()
|
|
||||||
key := randPublicKey()
|
|
||||||
cnr := cidtest.ID()
|
|
||||||
obj := oidtest.ID()
|
|
||||||
verb := session.VerbObjectDelete
|
|
||||||
|
|
||||||
signer := randSigner()
|
|
||||||
|
|
||||||
fs := []func(){
|
|
||||||
func() { x.SetNbf(nbf) },
|
|
||||||
func() { x.SetNbf(nbf + 1) },
|
|
||||||
|
|
||||||
func() { x.SetIat(iat) },
|
|
||||||
func() { x.SetIat(iat + 1) },
|
|
||||||
|
|
||||||
func() { x.SetExp(exp) },
|
|
||||||
func() { x.SetExp(exp + 1) },
|
|
||||||
|
|
||||||
func() { x.SetID(id) },
|
|
||||||
func() {
|
|
||||||
idcp := id
|
|
||||||
idcp[0]++
|
|
||||||
x.SetID(idcp)
|
|
||||||
},
|
|
||||||
|
|
||||||
func() { x.SetAuthKey(key) },
|
|
||||||
func() { x.SetAuthKey(randPublicKey()) },
|
|
||||||
|
|
||||||
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) },
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < len(fs); i += 2 {
|
|
||||||
fs[i]()
|
|
||||||
|
|
||||||
require.NoError(t, x.Sign(signer))
|
|
||||||
require.True(t, x.VerifySignature())
|
|
||||||
|
|
||||||
fs[i+1]()
|
|
||||||
require.False(t, x.VerifySignature())
|
|
||||||
|
|
||||||
fs[i]()
|
|
||||||
require.True(t, x.VerifySignature())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestObject_Issuer(t *testing.T) {
|
func TestObject_Issuer(t *testing.T) {
|
||||||
var token session.Object
|
var token session.Object
|
||||||
signer := randSigner()
|
signer := randSigner()
|
||||||
|
|
Loading…
Reference in a new issue