[#259] bearer: Align with the protocol

Sync lifetime names with `session` package. Add `AssertContainer` and
`AssertUser` methods which cover specific cases describe in protocol.
Increase test coverage.

Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
This commit is contained in:
Leonard Lyubich 2022-06-01 13:39:33 +03:00 committed by LeL
parent 82d762f536
commit c65be6d469
3 changed files with 648 additions and 282 deletions

View file

@ -2,40 +2,121 @@ package bearer
import ( import (
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic"
"errors" "errors"
"fmt" "fmt"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neofs-api-go/v2/acl" "github.com/nspcc-dev/neofs-api-go/v2/acl"
"github.com/nspcc-dev/neofs-api-go/v2/refs" "github.com/nspcc-dev/neofs-api-go/v2/refs"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto" neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa" neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa"
"github.com/nspcc-dev/neofs-sdk-go/eacl" "github.com/nspcc-dev/neofs-sdk-go/eacl"
"github.com/nspcc-dev/neofs-sdk-go/user" "github.com/nspcc-dev/neofs-sdk-go/user"
) )
var (
errNilBearerTokenBody = errors.New("bearer token body is not set")
errNilBearerTokenEACL = errors.New("bearer token ContainerEACL table is not set")
)
// Token represents bearer token for object service operations. // Token represents bearer token for object service operations.
// //
// Token is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/acl.BearerToken // Token is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/acl.BearerToken
// message. See ReadFromV2 / WriteToV2 methods. // message. See ReadFromV2 / WriteToV2 methods.
// //
// Instances can be created using built-in var declaration. // Instances can be created using built-in var declaration.
// type Token struct {
// Note that direct typecast is not safe and may result in loss of compatibility: targetUserSet bool
// _ = Token(acl.BearerToken{}) // not recommended targetUser user.ID
type Token acl.BearerToken
eaclTableSet bool
eaclTable eacl.Table
lifetimeSet bool
iat, nbf, exp uint64
sigSet bool
sig refs.Signature
}
// reads Token from the acl.BearerToken message. If checkFieldPresence is set,
// returns an error on absence of any protocol-required field.
func (b *Token) readFromV2(m acl.BearerToken, checkFieldPresence bool) error {
var err error
body := m.GetBody()
if checkFieldPresence && body == nil {
return errors.New("missing token body")
}
eaclTable := body.GetEACL()
if b.eaclTableSet = eaclTable != nil; b.eaclTableSet {
b.eaclTable = *eacl.NewTableFromV2(eaclTable)
} else if checkFieldPresence {
return errors.New("missing eACL table")
}
targetUser := body.GetOwnerID()
if b.targetUserSet = targetUser != nil; b.targetUserSet {
err = b.targetUser.ReadFromV2(*targetUser)
if err != nil {
return fmt.Errorf("invalid target user: %w", err)
}
}
lifetime := body.GetLifetime()
if b.lifetimeSet = lifetime != nil; b.lifetimeSet {
b.iat = lifetime.GetIat()
b.nbf = lifetime.GetNbf()
b.exp = lifetime.GetExp()
} else if checkFieldPresence {
return errors.New("missing token lifetime")
}
sig := m.GetSignature()
if b.sigSet = sig != nil; sig != nil {
b.sig = *sig
} else if checkFieldPresence {
return errors.New("missing body signature")
}
return nil
}
// ReadFromV2 reads Token from the acl.BearerToken message. // ReadFromV2 reads Token from the acl.BearerToken message.
// //
// See also WriteToV2. // See also WriteToV2.
func (b *Token) ReadFromV2(m acl.BearerToken) { func (b *Token) ReadFromV2(m acl.BearerToken) error {
*b = Token(m) return b.readFromV2(m, true)
}
func (b Token) fillBody() *acl.BearerTokenBody {
if !b.eaclTableSet && !b.targetUserSet && !b.lifetimeSet {
return nil
}
var body acl.BearerTokenBody
if b.eaclTableSet {
body.SetEACL(b.eaclTable.ToV2())
}
if b.targetUserSet {
var targetUser refs.OwnerID
b.targetUser.WriteToV2(&targetUser)
body.SetOwnerID(&targetUser)
}
if b.lifetimeSet {
var lifetime acl.TokenLifetime
lifetime.SetIat(b.iat)
lifetime.SetNbf(b.nbf)
lifetime.SetExp(b.exp)
body.SetLifetime(&lifetime)
}
return &body
}
func (b Token) signedData() []byte {
return b.fillBody().StableMarshal(nil)
} }
// WriteToV2 writes Token to the acl.BearerToken message. // WriteToV2 writes Token to the acl.BearerToken message.
@ -43,294 +124,247 @@ func (b *Token) ReadFromV2(m acl.BearerToken) {
// //
// See also ReadFromV2. // See also ReadFromV2.
func (b Token) WriteToV2(m *acl.BearerToken) { func (b Token) WriteToV2(m *acl.BearerToken) {
*m = (acl.BearerToken)(b) m.SetBody(b.fillBody())
var sig *refs.Signature
if b.sigSet {
sig = &b.sig
}
m.SetSignature(sig)
} }
// SetExpiration sets "exp" (expiration time) claim which identifies the // SetExp sets "exp" (expiration time) claim which identifies the
// expiration time (in NeoFS epochs) on or after which the Token MUST NOT be // expiration time (in NeoFS epochs) on or after which the Token MUST NOT be
// accepted for processing. The processing of the "exp" claim requires that the // 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. // 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. // Naming is inspired by https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.4.
// //
// See also Expiration. // See also InvalidAt.
func (b *Token) SetExpiration(exp uint64) { func (b *Token) SetExp(exp uint64) {
v2token := (*acl.BearerToken)(b) b.exp = exp
b.lifetimeSet = true
body := v2token.GetBody()
if body == nil {
body = new(acl.BearerTokenBody)
}
lt := new(acl.TokenLifetime)
lt.SetExp(exp)
lt.SetNbf(body.GetLifetime().GetNbf())
lt.SetIat(body.GetLifetime().GetIat())
body.SetLifetime(lt)
v2token.SetBody(body)
} }
// Expiration returns "exp" claim. // SetNbf sets "nbf" (not before) claim which identifies the time (in
//
// Empty Token has zero "exp".
//
// See also SetExpiration.
func (b Token) Expiration() uint64 {
v2token := (acl.BearerToken)(b)
return v2token.GetBody().GetLifetime().GetExp()
}
// SetNotBefore sets "nbf" (not before) claim which identifies the time (in
// NeoFS epochs) before which the Token MUST NOT be accepted for processing. The // NeoFS epochs) before which the Token MUST NOT be accepted for processing. The
// processing of the "nbf" claim requires that the current epoch MUST be // processing of the "nbf" claim requires that the current epoch MUST be
// after or equal to the not-before epoch listed in the "nbf" claim. // after or equal to the not-before epoch listed in the "nbf" claim.
// //
// Naming is inspired by https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.5. // Naming is inspired by https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.5.
// //
// See also NotBefore. // See also InvalidAt.
func (b *Token) SetNotBefore(nbf uint64) { func (b *Token) SetNbf(nbf uint64) {
v2token := (*acl.BearerToken)(b) b.nbf = nbf
b.lifetimeSet = true
body := v2token.GetBody()
if body == nil {
body = new(acl.BearerTokenBody)
}
lt := new(acl.TokenLifetime)
lt.SetExp(body.GetLifetime().GetExp())
lt.SetNbf(nbf)
lt.SetIat(body.GetLifetime().GetIat())
body.SetLifetime(lt)
v2token.SetBody(body)
} }
// NotBefore returns "nbf" claim. // SetIat sets "iat" (issued at) claim which identifies the time (in NeoFS
//
// Empty Token has zero "nbf".
//
// See also SetNotBefore.
func (b Token) NotBefore() uint64 {
v2token := (acl.BearerToken)(b)
return v2token.GetBody().GetLifetime().GetNbf()
}
// SetIssuedAt sets "iat" (issued at) claim which identifies the time (in NeoFS
// epochs) at which the Token was issued. This claim can be used to determine // epochs) at which the Token was issued. This claim can be used to determine
// the age of the Token. // the age of the Token.
// //
// Naming is inspired by https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.6. // Naming is inspired by https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.6.
// //
// See also IssuedAt. // See also InvalidAt.
func (b *Token) SetIssuedAt(iat uint64) { func (b *Token) SetIat(iat uint64) {
v2token := (*acl.BearerToken)(b) b.iat = iat
b.lifetimeSet = true
body := v2token.GetBody()
if body == nil {
body = new(acl.BearerTokenBody)
}
lt := new(acl.TokenLifetime)
lt.SetExp(body.GetLifetime().GetExp())
lt.SetNbf(body.GetLifetime().GetNbf())
lt.SetIat(iat)
body.SetLifetime(lt)
v2token.SetBody(body)
} }
// IssuedAt returns "iat" claim. // InvalidAt asserts "exp", "nbf" and "iat" claims for the given epoch.
// //
// Empty Token has zero "iat". // Zero Container is invalid in any epoch.
// //
// See also SetIssuedAt. // See also SetExp, SetNbf, SetIat.
func (b Token) IssuedAt() uint64 { func (b Token) InvalidAt(epoch uint64) bool {
v2token := (acl.BearerToken)(b) return !b.lifetimeSet || b.nbf > epoch || b.iat > epoch || b.exp <= epoch
return v2token.GetBody().GetLifetime().GetIat()
} }
// SetEACLTable sets extended ACL table that should be used during object // SetEACLTable sets eacl.Table that replaces the one from the issuer's
// service request processing with bearer token. // container. If table has specified container, bearer token can be used only
// for operations within this specific container. Otherwise, Token can be used
// within any issuer's container.
// //
// See also EACLTable. // SetEACLTable MUST be called if Token is going to be transmitted over
// NeoFS API V2 protocol.
//
// See also EACLTable, AssertContainer.
func (b *Token) SetEACLTable(table eacl.Table) { func (b *Token) SetEACLTable(table eacl.Table) {
v2 := (*acl.BearerToken)(b) b.eaclTable = table
b.eaclTableSet = true
body := v2.GetBody()
if body == nil {
body = new(acl.BearerTokenBody)
}
body.SetEACL(table.ToV2())
v2.SetBody(body)
} }
// EACLTable returns extended ACL table that should be used during object // EACLTable returns extended ACL table set by SetEACLTable.
// service request processing with bearer token. //
// Zero Token has zero eacl.Table.
func (b Token) EACLTable() eacl.Table {
if b.eaclTableSet {
return b.eaclTable
}
return eacl.Table{}
}
// AssertContainer checks if the token is valid within the given container.
//
// Note: cnr is assumed to refer to the issuer's container, otherwise the check
// is meaningless.
//
// Zero Token is valid in any container.
// //
// See also SetEACLTable. // See also SetEACLTable.
func (b Token) EACLTable() eacl.Table { func (b Token) AssertContainer(cnr cid.ID) bool {
v2 := (acl.BearerToken)(b) if !b.eaclTableSet {
return *eacl.NewTableFromV2(v2.GetBody().GetEACL()) return true
}
// SetOwnerID sets user.ID value of the user who can attach bearer token to
// its requests.
//
// See also OwnerID.
func (b *Token) SetOwnerID(id user.ID) {
v2 := (*acl.BearerToken)(b)
body := v2.GetBody()
if body == nil {
body = new(acl.BearerTokenBody)
} }
var idV2 refs.OwnerID cnrTable, set := b.eaclTable.CID()
id.WriteToV2(&idV2) return !set || cnrTable.Equals(cnr)
body.SetOwnerID(&idV2)
v2.SetBody(body)
} }
// OwnerID returns user.ID value of the user who can attach bearer token to // ForUser specifies ID of the user who can use the Token for the operations
// its requests. // within issuer's container(s).
// //
// See also SetOwnerID. // Optional: by default, any user has access to Token usage.
func (b Token) OwnerID() user.ID { //
v2 := (acl.BearerToken)(b) // See also AssertUser.
func (b *Token) ForUser(id user.ID) {
var id user.ID b.targetUser = id
b.targetUserSet = true
idV2 := v2.GetBody().GetOwnerID()
if idV2 != nil {
_ = id.ReadFromV2(*idV2)
}
return id
} }
// Sign signs bearer token. This method should be invoked with the private // AssertUser checks if the Token is issued to the given user.
// key of container owner to allow overriding extended ACL table of the container
// included in this token.
// //
// See also Signature. // Zero Token is available to any user.
//
// See also ForUser.
func (b Token) AssertUser(id user.ID) bool {
return !b.targetUserSet || b.targetUser.Equals(id)
}
// Sign calculates and writes signature of the Token data using issuer's secret.
// Returns signature calculation errors.
//
// Sign MUST be called if Token is going to be transmitted over
// NeoFS API V2 protocol.
//
// Note that any Token mutation is likely to break the signature, so it is
// expected to be calculated as a final stage of Token formation.
//
// See also VerifySignature, Issuer.
func (b *Token) Sign(key ecdsa.PrivateKey) error { func (b *Token) Sign(key ecdsa.PrivateKey) error {
err := sanityCheck(b) var sig neofscrypto.Signature
err := sig.Calculate(neofsecdsa.Signer(key), b.signedData())
if err != nil { if err != nil {
return err return err
} }
m := (*acl.BearerToken)(b) sig.WriteToV2(&b.sig)
b.sigSet = true
var sig neofscrypto.Signature
err = sig.Calculate(neofsecdsa.Signer(key), m.GetBody().StableMarshal(nil))
if err != nil {
return fmt.Errorf("calculate signature: %w", err)
}
var sigV2 refs.Signature
sig.WriteToV2(&sigV2)
m.SetSignature(&sigV2)
return nil return nil
} }
// VerifySignature returns nil if bearer token contains correct signature. // VerifySignature checks if Token signature is presented and valid.
func (b Token) VerifySignature() error {
if b.isEmpty() {
return nil
}
m := (acl.BearerToken)(b)
sigV2 := m.GetSignature()
if sigV2 == nil {
return errors.New("missing signature")
}
var sig neofscrypto.Signature
sig.ReadFromV2(*sigV2)
if !sig.Verify(m.GetBody().StableMarshal(nil)) {
return errors.New("wrong signature")
}
return nil
}
// Issuer returns user.ID associated with the key that signed bearer token.
// To pass node validation it should be owner of requested container.
// //
// If token is not signed, Issuer returns empty owner ID and false `ok` flag. // Zero Token fails the check.
// //
// See also Sign. // See also Sign.
func (b Token) Issuer() (id user.ID, ok bool) { func (b Token) VerifySignature() bool {
v2 := (acl.BearerToken)(b) if !b.sigSet {
return false
}
pub, _ := keys.NewPublicKeyFromBytes(v2.GetSignature().GetKey(), elliptic.P256()) var sig neofscrypto.Signature
sig.ReadFromV2(b.sig)
ok = pub != nil // TODO: (#233) check owner<->key relation
if ok { return sig.Verify(b.signedData())
user.IDFromKey(&id, (ecdsa.PublicKey)(*pub)) }
// Marshal encodes Token into a binary format of the NeoFS API protocol
// (Protocol Buffers V3 with direct field order).
//
// See also Unmarshal.
func (b Token) Marshal() []byte {
var m acl.BearerToken
b.WriteToV2(&m)
return m.StableMarshal(nil)
}
// Unmarshal decodes NeoFS API protocol binary data into the Token
// (Protocol Buffers V3 with direct field order). Returns an error describing
// a format violation.
//
// See also Marshal.
func (b *Token) Unmarshal(data []byte) error {
var m acl.BearerToken
err := m.Unmarshal(data)
if err != nil {
return err
}
return b.readFromV2(m, false)
}
// MarshalJSON encodes Token into a JSON format of the NeoFS API protocol
// (Protocol Buffers V3 JSON).
//
// See also UnmarshalJSON.
func (b Token) MarshalJSON() ([]byte, error) {
var m acl.BearerToken
b.WriteToV2(&m)
return m.MarshalJSON()
}
// UnmarshalJSON decodes NeoFS API protocol JSON data into the Token
// (Protocol Buffers V3 JSON). Returns an error describing a format violation.
//
// See also MarshalJSON.
func (b *Token) UnmarshalJSON(data []byte) error {
var m acl.BearerToken
err := m.UnmarshalJSON(data)
if err != nil {
return err
}
return b.readFromV2(m, false)
}
// SigningKeyBytes returns issuer's public key in a binary format of
// NeoFS API protocol.
//
// Unsigned Token has empty key.
//
// See also ResolveIssuer.
func (b Token) SigningKeyBytes() []byte {
if b.sigSet {
return b.sig.GetKey()
}
return nil
}
// ResolveIssuer resolves issuer's user.ID from the key used for Token signing.
// Returns zero user.ID if Token is unsigned or key has incorrect format.
//
// See also SigningKeyBytes.
func ResolveIssuer(b Token) (usr user.ID) {
binKey := b.SigningKeyBytes()
if len(binKey) != 0 {
var key neofsecdsa.PublicKey
if key.Decode(binKey) == nil {
user.IDFromKey(&usr, ecdsa.PublicKey(key))
}
} }
return return
} }
// sanityCheck if bearer token is ready to be issued.
func sanityCheck(b *Token) error {
v2 := (*acl.BearerToken)(b)
switch {
case v2.GetBody() == nil:
return errNilBearerTokenBody
case v2.GetBody().GetEACL() == nil:
return errNilBearerTokenEACL
}
// consider checking ContainerEACL sanity there, lifetime correctness, etc.
return nil
}
// Marshal marshals Token into a canonical NeoFS binary format (proto3
// with direct field order).
//
// See also Unmarshal.
func (b Token) Marshal() []byte {
return (*acl.BearerToken)(&b).StableMarshal(nil)
}
// Unmarshal unmarshals Token from canonical NeoFS binary format (proto3
// with direct field order).
//
// See also Marshal.
func (b *Token) Unmarshal(data []byte) error {
v2 := (*acl.BearerToken)(b)
return v2.Unmarshal(data)
}
// MarshalJSON encodes Token to protobuf JSON format.
//
// See also UnmarshalJSON.
func (b Token) MarshalJSON() ([]byte, error) {
v2 := (acl.BearerToken)(b)
return v2.MarshalJSON()
}
// UnmarshalJSON decodes Token from protobuf JSON format.
//
// See also MarshalJSON.
func (b *Token) UnmarshalJSON(data []byte) error {
v2 := (*acl.BearerToken)(b)
return v2.UnmarshalJSON(data)
}
func (b Token) isEmpty() bool {
v2token := (acl.BearerToken)(b)
return v2token.GetBody() == nil && v2token.GetSignature() == nil
}

View file

@ -1,58 +1,390 @@
package bearer_test package bearer_test
import ( import (
"bytes"
"math/rand"
"testing" "testing"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neofs-api-go/v2/acl"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
"github.com/nspcc-dev/neofs-sdk-go/bearer" "github.com/nspcc-dev/neofs-sdk-go/bearer"
tokentest "github.com/nspcc-dev/neofs-sdk-go/bearer/test" bearertest "github.com/nspcc-dev/neofs-sdk-go/bearer/test"
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa"
"github.com/nspcc-dev/neofs-sdk-go/eacl" "github.com/nspcc-dev/neofs-sdk-go/eacl"
eacltest "github.com/nspcc-dev/neofs-sdk-go/eacl/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 TestBearerToken_Issuer(t *testing.T) { // compares binary representations of two eacl.Table instances.
var bearerToken bearer.Token func isEqualEACLTables(t1, t2 eacl.Table) bool {
d1, err := t1.Marshal()
if err != nil {
panic(err)
}
t.Run("non signed token", func(t *testing.T) { d2, err := t2.Marshal()
_, ok := bearerToken.Issuer() if err != nil {
require.False(t, ok) panic(err)
}
return bytes.Equal(d1, d2)
}
func TestToken_SetEACLTable(t *testing.T) {
var val bearer.Token
var m acl.BearerToken
filled := bearertest.Token()
val.WriteToV2(&m)
require.Zero(t, m.GetBody())
val2 := filled
require.NoError(t, val2.Unmarshal(val.Marshal()))
require.Zero(t, val2.EACLTable())
val2 = filled
jd, err := val.MarshalJSON()
require.NoError(t, err)
require.NoError(t, val2.UnmarshalJSON(jd))
require.Zero(t, val2.EACLTable())
// set value
eaclTable := *eacltest.Table()
val.SetEACLTable(eaclTable)
require.True(t, isEqualEACLTables(eaclTable, val.EACLTable()))
val.WriteToV2(&m)
eaclTableV2 := eaclTable.ToV2()
require.Equal(t, eaclTableV2, m.GetBody().GetEACL())
val2 = filled
require.NoError(t, val2.Unmarshal(val.Marshal()))
require.True(t, isEqualEACLTables(eaclTable, val.EACLTable()))
val2 = filled
jd, err = val.MarshalJSON()
require.NoError(t, err)
require.NoError(t, val2.UnmarshalJSON(jd))
require.True(t, isEqualEACLTables(eaclTable, val.EACLTable()))
}
func TestToken_ForUser(t *testing.T) {
var val bearer.Token
var m acl.BearerToken
filled := bearertest.Token()
val.WriteToV2(&m)
require.Zero(t, m.GetBody())
val2 := filled
require.NoError(t, val2.Unmarshal(val.Marshal()))
val2.WriteToV2(&m)
require.Zero(t, m.GetBody())
val2 = filled
jd, err := val.MarshalJSON()
require.NoError(t, err)
require.NoError(t, val2.UnmarshalJSON(jd))
val2.WriteToV2(&m)
require.Zero(t, m.GetBody())
// set value
usr := *usertest.ID()
var usrV2 refs.OwnerID
usr.WriteToV2(&usrV2)
val.ForUser(usr)
val.WriteToV2(&m)
require.Equal(t, usrV2, *m.GetBody().GetOwnerID())
val2 = filled
require.NoError(t, val2.Unmarshal(val.Marshal()))
val2.WriteToV2(&m)
require.Equal(t, usrV2, *m.GetBody().GetOwnerID())
val2 = filled
jd, err = val.MarshalJSON()
require.NoError(t, err)
require.NoError(t, val2.UnmarshalJSON(jd))
val2.WriteToV2(&m)
require.Equal(t, usrV2, *m.GetBody().GetOwnerID())
}
func testLifetimeClaim(t *testing.T, setter func(*bearer.Token, uint64), getter func(*acl.BearerToken) uint64) {
var val bearer.Token
var m acl.BearerToken
filled := bearertest.Token()
val.WriteToV2(&m)
require.Zero(t, m.GetBody())
val2 := filled
require.NoError(t, val2.Unmarshal(val.Marshal()))
val2.WriteToV2(&m)
require.Zero(t, m.GetBody())
val2 = filled
jd, err := val.MarshalJSON()
require.NoError(t, err)
require.NoError(t, val2.UnmarshalJSON(jd))
val2.WriteToV2(&m)
require.Zero(t, m.GetBody())
// set value
exp := rand.Uint64()
setter(&val, exp)
val.WriteToV2(&m)
require.Equal(t, exp, getter(&m))
val2 = filled
require.NoError(t, val2.Unmarshal(val.Marshal()))
val2.WriteToV2(&m)
require.Equal(t, exp, getter(&m))
val2 = filled
jd, err = val.MarshalJSON()
require.NoError(t, err)
require.NoError(t, val2.UnmarshalJSON(jd))
val2.WriteToV2(&m)
require.Equal(t, exp, getter(&m))
}
func TestToken_SetLifetime(t *testing.T) {
t.Run("iat", func(t *testing.T) {
testLifetimeClaim(t, (*bearer.Token).SetIat, func(token *acl.BearerToken) uint64 {
return token.GetBody().GetLifetime().GetIat()
})
}) })
t.Run("signed token", func(t *testing.T) { t.Run("nbf", func(t *testing.T) {
p, err := keys.NewPrivateKey() testLifetimeClaim(t, (*bearer.Token).SetNbf, func(token *acl.BearerToken) uint64 {
require.NoError(t, err) return token.GetBody().GetLifetime().GetNbf()
})
})
var id user.ID t.Run("exp", func(t *testing.T) {
user.IDFromKey(&id, p.PrivateKey.PublicKey) testLifetimeClaim(t, (*bearer.Token).SetExp, func(token *acl.BearerToken) uint64 {
return token.GetBody().GetLifetime().GetExp()
bearerToken.SetEACLTable(*eacl.NewTable()) })
require.NoError(t, bearerToken.Sign(p.PrivateKey))
issuer, ok := bearerToken.Issuer()
require.True(t, ok)
require.True(t, id.Equals(issuer))
}) })
} }
func TestFilterEncoding(t *testing.T) { func TestToken_InvalidAt(t *testing.T) {
f := tokentest.Token() var val bearer.Token
t.Run("binary", func(t *testing.T) { require.True(t, val.InvalidAt(0))
data := f.Marshal() require.True(t, val.InvalidAt(1))
var f2 bearer.Token val.SetIat(1)
require.NoError(t, f2.Unmarshal(data)) val.SetNbf(2)
val.SetExp(4)
require.Equal(t, f, f2) require.True(t, val.InvalidAt(0))
}) require.True(t, val.InvalidAt(1))
require.False(t, val.InvalidAt(2))
t.Run("json", func(t *testing.T) { require.False(t, val.InvalidAt(3))
data, err := f.MarshalJSON() require.True(t, val.InvalidAt(4))
require.NoError(t, err) require.True(t, val.InvalidAt(5))
}
var d2 bearer.Token
require.NoError(t, d2.UnmarshalJSON(data)) func TestToken_AssertContainer(t *testing.T) {
var val bearer.Token
require.Equal(t, f, d2) cnr := cidtest.ID()
})
require.True(t, val.AssertContainer(cnr))
eaclTable := *eacltest.Table()
eaclTable.SetCID(cidtest.ID())
val.SetEACLTable(eaclTable)
require.False(t, val.AssertContainer(cnr))
eaclTable.SetCID(cnr)
val.SetEACLTable(eaclTable)
require.True(t, val.AssertContainer(cnr))
}
func TestToken_AssertUser(t *testing.T) {
var val bearer.Token
usr := *usertest.ID()
require.True(t, val.AssertUser(usr))
val.ForUser(*usertest.ID())
require.False(t, val.AssertUser(usr))
val.ForUser(usr)
require.True(t, val.AssertUser(usr))
}
func TestToken_Sign(t *testing.T) {
var val bearer.Token
require.False(t, val.VerifySignature())
k, err := keys.NewPrivateKey()
require.NoError(t, err)
key := k.PrivateKey
val = bearertest.Token()
require.NoError(t, val.Sign(key))
require.True(t, val.VerifySignature())
var m acl.BearerToken
val.WriteToV2(&m)
require.NotZero(t, m.GetSignature().GetKey())
require.NotZero(t, m.GetSignature().GetSign())
val2 := bearertest.Token()
require.NoError(t, val2.Unmarshal(val.Marshal()))
require.True(t, val2.VerifySignature())
jd, err := val.MarshalJSON()
require.NoError(t, err)
val2 = bearertest.Token()
require.NoError(t, val2.UnmarshalJSON(jd))
require.True(t, val2.VerifySignature())
}
func TestToken_ReadFromV2(t *testing.T) {
var val bearer.Token
var m acl.BearerToken
require.Error(t, val.ReadFromV2(m))
var body acl.BearerTokenBody
m.SetBody(&body)
require.Error(t, val.ReadFromV2(m))
eaclTable := eacltest.Table().ToV2()
body.SetEACL(eaclTable)
require.Error(t, val.ReadFromV2(m))
var lifetime acl.TokenLifetime
body.SetLifetime(&lifetime)
require.Error(t, val.ReadFromV2(m))
const iat, nbf, exp = 1, 2, 3
lifetime.SetIat(iat)
lifetime.SetNbf(nbf)
lifetime.SetExp(exp)
body.SetLifetime(&lifetime)
require.Error(t, val.ReadFromV2(m))
var sig refs.Signature
m.SetSignature(&sig)
require.NoError(t, val.ReadFromV2(m))
var m2 acl.BearerToken
val.WriteToV2(&m2)
require.Equal(t, m, m2)
usr, usr2 := *usertest.ID(), *usertest.ID()
require.True(t, val.AssertUser(usr))
require.True(t, val.AssertUser(usr2))
var usrV2 refs.OwnerID
usr.WriteToV2(&usrV2)
body.SetOwnerID(&usrV2)
require.NoError(t, val.ReadFromV2(m))
val.WriteToV2(&m2)
require.Equal(t, m, m2)
require.True(t, val.AssertUser(usr))
require.False(t, val.AssertUser(usr2))
k, err := keys.NewPrivateKey()
require.NoError(t, err)
signer := neofsecdsa.Signer(k.PrivateKey)
var s neofscrypto.Signature
require.NoError(t, s.Calculate(signer, body.StableMarshal(nil)))
s.WriteToV2(&sig)
require.NoError(t, val.ReadFromV2(m))
require.True(t, val.VerifySignature())
require.Equal(t, sig.GetKey(), val.SigningKeyBytes())
}
func TestResolveIssuer(t *testing.T) {
k, err := keys.NewPrivateKey()
require.NoError(t, err)
var val bearer.Token
require.Zero(t, bearer.ResolveIssuer(val))
var m acl.BearerToken
var sig refs.Signature
sig.SetKey([]byte("invalid key"))
m.SetSignature(&sig)
require.NoError(t, val.Unmarshal(m.StableMarshal(nil)))
require.Zero(t, bearer.ResolveIssuer(val))
require.NoError(t, val.Sign(k.PrivateKey))
var usr user.ID
user.IDFromKey(&usr, k.PrivateKey.PublicKey)
require.Equal(t, usr, bearer.ResolveIssuer(val))
} }

View file

@ -10,10 +10,10 @@ import (
// //
// Resulting token is unsigned. // Resulting token is unsigned.
func Token() (t bearer.Token) { func Token() (t bearer.Token) {
t.SetExpiration(3) t.SetExp(3)
t.SetNotBefore(2) t.SetNbf(2)
t.SetIssuedAt(1) t.SetIat(1)
t.SetOwnerID(*usertest.ID()) t.ForUser(*usertest.ID())
t.SetEACLTable(*eacltest.Table()) t.SetEACLTable(*eacltest.Table())
return t return t