forked from TrueCloudLab/frostfs-sdk-go
[#53] session: move package from neofs-api-go
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
parent
1feafcbcbf
commit
ee47683848
6 changed files with 825 additions and 0 deletions
139
session/container.go
Normal file
139
session/container.go
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
package session
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/nspcc-dev/neofs-api-go/v2/session"
|
||||||
|
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ContainerContext represents NeoFS API v2-compatible
|
||||||
|
// context of the container session.
|
||||||
|
//
|
||||||
|
// It is a wrapper over session.ContainerSessionContext
|
||||||
|
// which allows to abstract from details of the message
|
||||||
|
// structure.
|
||||||
|
type ContainerContext session.ContainerSessionContext
|
||||||
|
|
||||||
|
// NewContainerContext creates and returns blank ContainerSessionContext.
|
||||||
|
//
|
||||||
|
// Defaults:
|
||||||
|
// - not bound to any operation;
|
||||||
|
// - applied to all containers.
|
||||||
|
func NewContainerContext() *ContainerContext {
|
||||||
|
v2 := new(session.ContainerSessionContext)
|
||||||
|
v2.SetWildcard(true)
|
||||||
|
|
||||||
|
return ContainerContextFromV2(v2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerContextFromV2 wraps session.ContainerSessionContext
|
||||||
|
// into ContainerContext.
|
||||||
|
func ContainerContextFromV2(v *session.ContainerSessionContext) *ContainerContext {
|
||||||
|
return (*ContainerContext)(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToV2 converts ContainerContext to session.ContainerSessionContext
|
||||||
|
// message structure.
|
||||||
|
func (x *ContainerContext) ToV2() *session.ContainerSessionContext {
|
||||||
|
return (*session.ContainerSessionContext)(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyTo specifies which container the ContainerContext applies to.
|
||||||
|
//
|
||||||
|
// If id is nil, ContainerContext is applied to all containers.
|
||||||
|
func (x *ContainerContext) ApplyTo(id *cid.ID) {
|
||||||
|
v2 := (*session.ContainerSessionContext)(x)
|
||||||
|
|
||||||
|
v2.SetWildcard(id == nil)
|
||||||
|
v2.SetContainerID(id.ToV2())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ActOnAllContainers is a helper function that conveniently
|
||||||
|
// applies ContainerContext to all containers.
|
||||||
|
func ApplyToAllContainers(c *ContainerContext) {
|
||||||
|
c.ApplyTo(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Container returns identifier of the container
|
||||||
|
// to which the ContainerContext applies.
|
||||||
|
//
|
||||||
|
// Returns nil if ContainerContext is applied to
|
||||||
|
// all containers.
|
||||||
|
func (x *ContainerContext) Container() *cid.ID {
|
||||||
|
v2 := (*session.ContainerSessionContext)(x)
|
||||||
|
|
||||||
|
if v2.Wildcard() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return cid.NewFromV2(v2.ContainerID())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ContainerContext) forVerb(v session.ContainerSessionVerb) {
|
||||||
|
(*session.ContainerSessionContext)(x).
|
||||||
|
SetVerb(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ContainerContext) isForVerb(v session.ContainerSessionVerb) bool {
|
||||||
|
return (*session.ContainerSessionContext)(x).
|
||||||
|
Verb() == v
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForPut binds the ContainerContext to
|
||||||
|
// PUT operation.
|
||||||
|
func (x *ContainerContext) ForPut() {
|
||||||
|
x.forVerb(session.ContainerVerbPut)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsForPut checks if ContainerContext is bound to
|
||||||
|
// PUT operation.
|
||||||
|
func (x *ContainerContext) IsForPut() bool {
|
||||||
|
return x.isForVerb(session.ContainerVerbPut)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForDelete binds the ContainerContext to
|
||||||
|
// DELETE operation.
|
||||||
|
func (x *ContainerContext) ForDelete() {
|
||||||
|
x.forVerb(session.ContainerVerbDelete)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsForDelete checks if ContainerContext is bound to
|
||||||
|
// DELETE operation.
|
||||||
|
func (x *ContainerContext) IsForDelete() bool {
|
||||||
|
return x.isForVerb(session.ContainerVerbDelete)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForSetEACL binds the ContainerContext to
|
||||||
|
// SETEACL operation.
|
||||||
|
func (x *ContainerContext) ForSetEACL() {
|
||||||
|
x.forVerb(session.ContainerVerbSetEACL)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsForSetEACL checks if ContainerContext is bound to
|
||||||
|
// SETEACL operation.
|
||||||
|
func (x *ContainerContext) IsForSetEACL() bool {
|
||||||
|
return x.isForVerb(session.ContainerVerbSetEACL)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal marshals ContainerContext into a protobuf binary form.
|
||||||
|
func (x *ContainerContext) Marshal(bs ...[]byte) ([]byte, error) {
|
||||||
|
var buf []byte
|
||||||
|
if len(bs) > 0 {
|
||||||
|
buf = bs[0]
|
||||||
|
}
|
||||||
|
return x.ToV2().StableMarshal(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal unmarshals protobuf binary representation of ContainerContext.
|
||||||
|
func (x *ContainerContext) Unmarshal(data []byte) error {
|
||||||
|
return x.ToV2().Unmarshal(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON encodes ContainerContext to protobuf JSON format.
|
||||||
|
func (x *ContainerContext) MarshalJSON() ([]byte, error) {
|
||||||
|
return x.ToV2().MarshalJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON decodes ContainerContext from protobuf JSON format.
|
||||||
|
func (x *ContainerContext) UnmarshalJSON(data []byte) error {
|
||||||
|
return x.ToV2().UnmarshalJSON(data)
|
||||||
|
}
|
112
session/container_test.go
Normal file
112
session/container_test.go
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
package session_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
v2session "github.com/nspcc-dev/neofs-api-go/v2/session"
|
||||||
|
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
|
||||||
|
"github.com/nspcc-dev/neofs-sdk-go/session"
|
||||||
|
sessiontest "github.com/nspcc-dev/neofs-sdk-go/session/test"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestContainerContextVerbs(t *testing.T) {
|
||||||
|
c := session.NewContainerContext()
|
||||||
|
|
||||||
|
assert := func(setter func(), getter func() bool, verb v2session.ContainerSessionVerb) {
|
||||||
|
setter()
|
||||||
|
|
||||||
|
require.True(t, getter())
|
||||||
|
|
||||||
|
require.Equal(t, verb, c.ToV2().Verb())
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("PUT", func(t *testing.T) {
|
||||||
|
assert(c.ForPut, c.IsForPut, v2session.ContainerVerbPut)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("DELETE", func(t *testing.T) {
|
||||||
|
assert(c.ForDelete, c.IsForDelete, v2session.ContainerVerbDelete)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("SETEACL", func(t *testing.T) {
|
||||||
|
assert(c.ForSetEACL, c.IsForSetEACL, v2session.ContainerVerbSetEACL)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContainerContext_ApplyTo(t *testing.T) {
|
||||||
|
c := session.NewContainerContext()
|
||||||
|
id := cidtest.GenerateID()
|
||||||
|
|
||||||
|
t.Run("method", func(t *testing.T) {
|
||||||
|
c.ApplyTo(id)
|
||||||
|
|
||||||
|
require.Equal(t, id, c.Container())
|
||||||
|
|
||||||
|
c.ApplyTo(nil)
|
||||||
|
|
||||||
|
require.Nil(t, c.Container())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("helper functions", func(t *testing.T) {
|
||||||
|
c.ApplyTo(id)
|
||||||
|
|
||||||
|
session.ApplyToAllContainers(c)
|
||||||
|
|
||||||
|
require.Nil(t, c.Container())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFilter_ToV2(t *testing.T) {
|
||||||
|
t.Run("nil", func(t *testing.T) {
|
||||||
|
var x *session.ContainerContext
|
||||||
|
|
||||||
|
require.Nil(t, x.ToV2())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("default values", func(t *testing.T) {
|
||||||
|
c := session.NewContainerContext()
|
||||||
|
|
||||||
|
// check initial values
|
||||||
|
require.Nil(t, c.Container())
|
||||||
|
|
||||||
|
for _, op := range []func() bool{
|
||||||
|
c.IsForPut,
|
||||||
|
c.IsForDelete,
|
||||||
|
c.IsForSetEACL,
|
||||||
|
} {
|
||||||
|
require.False(t, op())
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert to v2 message
|
||||||
|
cV2 := c.ToV2()
|
||||||
|
|
||||||
|
require.Equal(t, v2session.ContainerVerbUnknown, cV2.Verb())
|
||||||
|
require.True(t, cV2.Wildcard())
|
||||||
|
require.Nil(t, cV2.ContainerID())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContainerContextEncoding(t *testing.T) {
|
||||||
|
c := sessiontest.ContainerContext()
|
||||||
|
|
||||||
|
t.Run("binary", func(t *testing.T) {
|
||||||
|
data, err := c.Marshal()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
c2 := session.NewContainerContext()
|
||||||
|
require.NoError(t, c2.Unmarshal(data))
|
||||||
|
|
||||||
|
require.Equal(t, c, c2)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("json", func(t *testing.T) {
|
||||||
|
data, err := c.MarshalJSON()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
c2 := session.NewContainerContext()
|
||||||
|
require.NoError(t, c2.UnmarshalJSON(data))
|
||||||
|
|
||||||
|
require.Equal(t, c, c2)
|
||||||
|
})
|
||||||
|
}
|
282
session/session.go
Normal file
282
session/session.go
Normal file
|
@ -0,0 +1,282 @@
|
||||||
|
package session
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
||||||
|
"github.com/nspcc-dev/neofs-api-go/v2/session"
|
||||||
|
v2signature "github.com/nspcc-dev/neofs-api-go/v2/signature"
|
||||||
|
"github.com/nspcc-dev/neofs-sdk-go/owner"
|
||||||
|
"github.com/nspcc-dev/neofs-sdk-go/signature"
|
||||||
|
sigutil "github.com/nspcc-dev/neofs-sdk-go/util/signature"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Token represents NeoFS API v2-compatible
|
||||||
|
// session token.
|
||||||
|
type Token session.SessionToken
|
||||||
|
|
||||||
|
// NewTokenFromV2 wraps session.SessionToken message structure
|
||||||
|
// into Token.
|
||||||
|
//
|
||||||
|
// Nil session.SessionToken converts to nil.
|
||||||
|
func NewTokenFromV2(tV2 *session.SessionToken) *Token {
|
||||||
|
return (*Token)(tV2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewToken creates and returns blank Token.
|
||||||
|
//
|
||||||
|
// Defaults:
|
||||||
|
// - body: nil;
|
||||||
|
// - id: nil;
|
||||||
|
// - ownerId: nil;
|
||||||
|
// - sessionKey: nil;
|
||||||
|
// - exp: 0;
|
||||||
|
// - iat: 0;
|
||||||
|
// - nbf: 0;
|
||||||
|
func NewToken() *Token {
|
||||||
|
return NewTokenFromV2(new(session.SessionToken))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToV2 converts Token to session.SessionToken message structure.
|
||||||
|
//
|
||||||
|
// Nil Token converts to nil.
|
||||||
|
func (t *Token) ToV2() *session.SessionToken {
|
||||||
|
return (*session.SessionToken)(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Token) setBodyField(setter func(*session.SessionTokenBody)) {
|
||||||
|
token := (*session.SessionToken)(t)
|
||||||
|
body := token.GetBody()
|
||||||
|
|
||||||
|
if body == nil {
|
||||||
|
body = new(session.SessionTokenBody)
|
||||||
|
token.SetBody(body)
|
||||||
|
}
|
||||||
|
|
||||||
|
setter(body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID returns Token identifier.
|
||||||
|
func (t *Token) ID() []byte {
|
||||||
|
return (*session.SessionToken)(t).
|
||||||
|
GetBody().
|
||||||
|
GetID()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetID sets Token identifier.
|
||||||
|
func (t *Token) SetID(v []byte) {
|
||||||
|
t.setBodyField(func(body *session.SessionTokenBody) {
|
||||||
|
body.SetID(v)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// OwnerID returns Token's owner identifier.
|
||||||
|
func (t *Token) OwnerID() *owner.ID {
|
||||||
|
return owner.NewIDFromV2(
|
||||||
|
(*session.SessionToken)(t).
|
||||||
|
GetBody().
|
||||||
|
GetOwnerID(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOwnerID sets Token's owner identifier.
|
||||||
|
func (t *Token) SetOwnerID(v *owner.ID) {
|
||||||
|
t.setBodyField(func(body *session.SessionTokenBody) {
|
||||||
|
body.SetOwnerID(v.ToV2())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionKey returns public key of the session
|
||||||
|
// in a binary format.
|
||||||
|
func (t *Token) SessionKey() []byte {
|
||||||
|
return (*session.SessionToken)(t).
|
||||||
|
GetBody().
|
||||||
|
GetSessionKey()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSessionKey sets public key of the session
|
||||||
|
// in a binary format.
|
||||||
|
func (t *Token) SetSessionKey(v []byte) {
|
||||||
|
t.setBodyField(func(body *session.SessionTokenBody) {
|
||||||
|
body.SetSessionKey(v)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Token) setLifetimeField(f func(*session.TokenLifetime)) {
|
||||||
|
t.setBodyField(func(body *session.SessionTokenBody) {
|
||||||
|
lt := body.GetLifetime()
|
||||||
|
if lt == nil {
|
||||||
|
lt = new(session.TokenLifetime)
|
||||||
|
body.SetLifetime(lt)
|
||||||
|
}
|
||||||
|
|
||||||
|
f(lt)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exp returns epoch number of the token expiration.
|
||||||
|
func (t *Token) Exp() uint64 {
|
||||||
|
return (*session.SessionToken)(t).
|
||||||
|
GetBody().
|
||||||
|
GetLifetime().
|
||||||
|
GetExp()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetExp sets epoch number of the token expiration.
|
||||||
|
func (t *Token) SetExp(exp uint64) {
|
||||||
|
t.setLifetimeField(func(lt *session.TokenLifetime) {
|
||||||
|
lt.SetExp(exp)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nbf returns starting epoch number of the token.
|
||||||
|
func (t *Token) Nbf() uint64 {
|
||||||
|
return (*session.SessionToken)(t).
|
||||||
|
GetBody().
|
||||||
|
GetLifetime().
|
||||||
|
GetNbf()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNbf sets starting epoch number of the token.
|
||||||
|
func (t *Token) SetNbf(nbf uint64) {
|
||||||
|
t.setLifetimeField(func(lt *session.TokenLifetime) {
|
||||||
|
lt.SetNbf(nbf)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iat returns starting epoch number of the token.
|
||||||
|
func (t *Token) Iat() uint64 {
|
||||||
|
return (*session.SessionToken)(t).
|
||||||
|
GetBody().
|
||||||
|
GetLifetime().
|
||||||
|
GetIat()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetIat sets the number of the epoch in which the token was issued.
|
||||||
|
func (t *Token) SetIat(iat uint64) {
|
||||||
|
t.setLifetimeField(func(lt *session.TokenLifetime) {
|
||||||
|
lt.SetIat(iat)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign calculates and writes signature of the Token data.
|
||||||
|
//
|
||||||
|
// Returns signature calculation errors.
|
||||||
|
func (t *Token) Sign(key *ecdsa.PrivateKey) error {
|
||||||
|
tV2 := (*session.SessionToken)(t)
|
||||||
|
|
||||||
|
signedData := v2signature.StableMarshalerWrapper{
|
||||||
|
SM: tV2.GetBody(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return sigutil.SignDataWithHandler(key, signedData, func(key, sig []byte) {
|
||||||
|
tSig := tV2.GetSignature()
|
||||||
|
if tSig == nil {
|
||||||
|
tSig = new(refs.Signature)
|
||||||
|
}
|
||||||
|
|
||||||
|
tSig.SetKey(key)
|
||||||
|
tSig.SetSign(sig)
|
||||||
|
|
||||||
|
tV2.SetSignature(tSig)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifySignature checks if token signature is
|
||||||
|
// presented and valid.
|
||||||
|
func (t *Token) VerifySignature() bool {
|
||||||
|
tV2 := (*session.SessionToken)(t)
|
||||||
|
|
||||||
|
signedData := v2signature.StableMarshalerWrapper{
|
||||||
|
SM: tV2.GetBody(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return sigutil.VerifyDataWithSource(signedData, func() (key, sig []byte) {
|
||||||
|
tSig := tV2.GetSignature()
|
||||||
|
return tSig.GetKey(), tSig.GetSign()
|
||||||
|
}) == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signature returns Token signature.
|
||||||
|
func (t *Token) Signature() *signature.Signature {
|
||||||
|
return signature.NewFromV2(
|
||||||
|
(*session.SessionToken)(t).
|
||||||
|
GetSignature(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetContext sets context of the Token.
|
||||||
|
//
|
||||||
|
// Supported contexts:
|
||||||
|
// - *ContainerContext.
|
||||||
|
//
|
||||||
|
// Resets context if it is not supported.
|
||||||
|
func (t *Token) SetContext(v interface{}) {
|
||||||
|
var cV2 session.SessionTokenContext
|
||||||
|
|
||||||
|
switch c := v.(type) {
|
||||||
|
case *ContainerContext:
|
||||||
|
cV2 = c.ToV2()
|
||||||
|
}
|
||||||
|
|
||||||
|
t.setBodyField(func(body *session.SessionTokenBody) {
|
||||||
|
body.SetContext(cV2)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Context returns context of the Token.
|
||||||
|
//
|
||||||
|
// Supports same contexts as SetContext.
|
||||||
|
//
|
||||||
|
// Returns nil if context is not supported.
|
||||||
|
func (t *Token) Context() interface{} {
|
||||||
|
switch v := (*session.SessionToken)(t).
|
||||||
|
GetBody().
|
||||||
|
GetContext(); c := v.(type) {
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
case *session.ContainerSessionContext:
|
||||||
|
return ContainerContextFromV2(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetContainerContext is a helper function that casts
|
||||||
|
// Token context to ContainerContext.
|
||||||
|
//
|
||||||
|
// Returns nil if context is not a ContainerContext.
|
||||||
|
func GetContainerContext(t *Token) *ContainerContext {
|
||||||
|
c, _ := t.Context().(*ContainerContext)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal marshals Token into a protobuf binary form.
|
||||||
|
//
|
||||||
|
// Buffer is allocated when the argument is empty.
|
||||||
|
// Otherwise, the first buffer is used.
|
||||||
|
func (t *Token) Marshal(bs ...[]byte) ([]byte, error) {
|
||||||
|
var buf []byte
|
||||||
|
if len(bs) > 0 {
|
||||||
|
buf = bs[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
return (*session.SessionToken)(t).
|
||||||
|
StableMarshal(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal unmarshals protobuf binary representation of Token.
|
||||||
|
func (t *Token) Unmarshal(data []byte) error {
|
||||||
|
return (*session.SessionToken)(t).
|
||||||
|
Unmarshal(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON encodes Token to protobuf JSON format.
|
||||||
|
func (t *Token) MarshalJSON() ([]byte, error) {
|
||||||
|
return (*session.SessionToken)(t).
|
||||||
|
MarshalJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON decodes Token from protobuf JSON format.
|
||||||
|
func (t *Token) UnmarshalJSON(data []byte) error {
|
||||||
|
return (*session.SessionToken)(t).
|
||||||
|
UnmarshalJSON(data)
|
||||||
|
}
|
202
session/session_test.go
Normal file
202
session/session_test.go
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
package session_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
sessionv2 "github.com/nspcc-dev/neofs-api-go/v2/session"
|
||||||
|
ownertest "github.com/nspcc-dev/neofs-sdk-go/owner/test"
|
||||||
|
"github.com/nspcc-dev/neofs-sdk-go/session"
|
||||||
|
sessiontest "github.com/nspcc-dev/neofs-sdk-go/session/test"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSessionToken_SetID(t *testing.T) {
|
||||||
|
token := session.NewToken()
|
||||||
|
|
||||||
|
id := []byte{1, 2, 3}
|
||||||
|
token.SetID(id)
|
||||||
|
|
||||||
|
require.Equal(t, id, token.ID())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSessionToken_SetOwnerID(t *testing.T) {
|
||||||
|
token := session.NewToken()
|
||||||
|
|
||||||
|
ownerID := ownertest.GenerateID()
|
||||||
|
|
||||||
|
token.SetOwnerID(ownerID)
|
||||||
|
|
||||||
|
require.Equal(t, ownerID, token.OwnerID())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSessionToken_SetSessionKey(t *testing.T) {
|
||||||
|
token := session.NewToken()
|
||||||
|
|
||||||
|
key := []byte{1, 2, 3}
|
||||||
|
token.SetSessionKey(key)
|
||||||
|
|
||||||
|
require.Equal(t, key, token.SessionKey())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSessionTokenEncoding(t *testing.T) {
|
||||||
|
tok := sessiontest.Generate()
|
||||||
|
|
||||||
|
t.Run("binary", func(t *testing.T) {
|
||||||
|
data, err := tok.Marshal()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
tok2 := session.NewToken()
|
||||||
|
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)
|
||||||
|
|
||||||
|
tok2 := session.NewToken()
|
||||||
|
require.NoError(t, tok2.UnmarshalJSON(data))
|
||||||
|
|
||||||
|
require.Equal(t, tok, tok2)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToken_VerifySignature(t *testing.T) {
|
||||||
|
t.Run("nil", func(t *testing.T) {
|
||||||
|
var tok *session.Token
|
||||||
|
|
||||||
|
require.False(t, tok.VerifySignature())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("unsigned", func(t *testing.T) {
|
||||||
|
tok := sessiontest.Generate()
|
||||||
|
|
||||||
|
require.False(t, tok.VerifySignature())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("signed", func(t *testing.T) {
|
||||||
|
tok := sessiontest.GenerateSigned()
|
||||||
|
|
||||||
|
require.True(t, tok.VerifySignature())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var unsupportedContexts = []interface{}{
|
||||||
|
123,
|
||||||
|
true,
|
||||||
|
session.NewToken(),
|
||||||
|
}
|
||||||
|
|
||||||
|
var nonContainerContexts = unsupportedContexts
|
||||||
|
|
||||||
|
func TestToken_Context(t *testing.T) {
|
||||||
|
tok := session.NewToken()
|
||||||
|
|
||||||
|
for _, item := range []struct {
|
||||||
|
ctx interface{}
|
||||||
|
v2assert func(interface{})
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
ctx: sessiontest.ContainerContext(),
|
||||||
|
v2assert: func(c interface{}) {
|
||||||
|
require.Equal(t, c.(*session.ContainerContext).ToV2(), tok.ToV2().GetBody().GetContext())
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
tok.SetContext(item.ctx)
|
||||||
|
|
||||||
|
require.Equal(t, item.ctx, tok.Context())
|
||||||
|
|
||||||
|
item.v2assert(item.ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range unsupportedContexts {
|
||||||
|
tok.SetContext(c)
|
||||||
|
|
||||||
|
require.Nil(t, tok.Context())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetContainerContext(t *testing.T) {
|
||||||
|
tok := session.NewToken()
|
||||||
|
|
||||||
|
c := sessiontest.ContainerContext()
|
||||||
|
|
||||||
|
tok.SetContext(c)
|
||||||
|
|
||||||
|
require.Equal(t, c, session.GetContainerContext(tok))
|
||||||
|
|
||||||
|
for _, c := range nonContainerContexts {
|
||||||
|
tok.SetContext(c)
|
||||||
|
|
||||||
|
require.Nil(t, session.GetContainerContext(tok))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToken_Exp(t *testing.T) {
|
||||||
|
tok := session.NewToken()
|
||||||
|
|
||||||
|
const exp = 11
|
||||||
|
|
||||||
|
tok.SetExp(exp)
|
||||||
|
|
||||||
|
require.EqualValues(t, exp, tok.Exp())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToken_Nbf(t *testing.T) {
|
||||||
|
tok := session.NewToken()
|
||||||
|
|
||||||
|
const nbf = 22
|
||||||
|
|
||||||
|
tok.SetNbf(nbf)
|
||||||
|
|
||||||
|
require.EqualValues(t, nbf, tok.Nbf())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToken_Iat(t *testing.T) {
|
||||||
|
tok := session.NewToken()
|
||||||
|
|
||||||
|
const iat = 33
|
||||||
|
|
||||||
|
tok.SetIat(iat)
|
||||||
|
|
||||||
|
require.EqualValues(t, iat, tok.Iat())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewTokenFromV2(t *testing.T) {
|
||||||
|
t.Run("from nil", func(t *testing.T) {
|
||||||
|
var x *sessionv2.SessionToken
|
||||||
|
|
||||||
|
require.Nil(t, session.NewTokenFromV2(x))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToken_ToV2(t *testing.T) {
|
||||||
|
t.Run("nil", func(t *testing.T) {
|
||||||
|
var x *session.Token
|
||||||
|
|
||||||
|
require.Nil(t, x.ToV2())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewToken(t *testing.T) {
|
||||||
|
t.Run("default values", func(t *testing.T) {
|
||||||
|
token := session.NewToken()
|
||||||
|
|
||||||
|
// check initial values
|
||||||
|
require.Nil(t, token.Signature())
|
||||||
|
require.Nil(t, token.OwnerID())
|
||||||
|
require.Nil(t, token.SessionKey())
|
||||||
|
require.Nil(t, token.ID())
|
||||||
|
require.Zero(t, token.Exp())
|
||||||
|
require.Zero(t, token.Iat())
|
||||||
|
require.Zero(t, token.Nbf())
|
||||||
|
|
||||||
|
// convert to v2 message
|
||||||
|
tokenV2 := token.ToV2()
|
||||||
|
|
||||||
|
require.Nil(t, tokenV2.GetSignature())
|
||||||
|
require.Nil(t, tokenV2.GetBody())
|
||||||
|
})
|
||||||
|
}
|
26
session/test/container.go
Normal file
26
session/test/container.go
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package sessiontest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
|
||||||
|
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
|
||||||
|
"github.com/nspcc-dev/neofs-sdk-go/session"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ContainerContext returns session.ContainerContext
|
||||||
|
// which applies to random operation on a random container.
|
||||||
|
func ContainerContext() *session.ContainerContext {
|
||||||
|
c := session.NewContainerContext()
|
||||||
|
|
||||||
|
setters := []func(){
|
||||||
|
c.ForPut,
|
||||||
|
c.ForDelete,
|
||||||
|
c.ForSetEACL,
|
||||||
|
}
|
||||||
|
|
||||||
|
setters[rand.Uint32()%uint32(len(setters))]()
|
||||||
|
|
||||||
|
c.ApplyTo(cidtest.GenerateID())
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
64
session/test/token.go
Normal file
64
session/test/token.go
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
package sessiontest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
"github.com/nspcc-dev/neofs-sdk-go/owner"
|
||||||
|
"github.com/nspcc-dev/neofs-sdk-go/session"
|
||||||
|
)
|
||||||
|
|
||||||
|
var p *keys.PrivateKey
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
p, err = keys.NewPrivateKey()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate returns random session.Token.
|
||||||
|
//
|
||||||
|
// Resulting token is unsigned.
|
||||||
|
func Generate() *session.Token {
|
||||||
|
tok := session.NewToken()
|
||||||
|
|
||||||
|
uid, err := uuid.New().MarshalBinary()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
w := new(owner.NEO3Wallet)
|
||||||
|
rand.Read(w.Bytes())
|
||||||
|
|
||||||
|
ownerID := owner.NewID()
|
||||||
|
ownerID.SetNeo3Wallet(w)
|
||||||
|
|
||||||
|
keyBin := p.PublicKey().Bytes()
|
||||||
|
|
||||||
|
tok.SetID(uid)
|
||||||
|
tok.SetOwnerID(ownerID)
|
||||||
|
tok.SetSessionKey(keyBin)
|
||||||
|
tok.SetExp(11)
|
||||||
|
tok.SetNbf(22)
|
||||||
|
tok.SetIat(33)
|
||||||
|
|
||||||
|
return tok
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateSigned returns signed random session.Token.
|
||||||
|
//
|
||||||
|
// Panics if token could not be signed (actually unexpected).
|
||||||
|
func GenerateSigned() *session.Token {
|
||||||
|
tok := Generate()
|
||||||
|
|
||||||
|
err := tok.Sign(&p.PrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tok
|
||||||
|
}
|
Loading…
Reference in a new issue