[#61] token: move package from neofs-api-go
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
parent
49a24e4b3e
commit
6c55c0fd4b
3 changed files with 312 additions and 0 deletions
188
token/bearer.go
Normal file
188
token/bearer.go
Normal file
|
@ -0,0 +1,188 @@
|
|||
package token
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"errors"
|
||||
|
||||
"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"
|
||||
v2signature "github.com/nspcc-dev/neofs-api-go/v2/signature"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/eacl"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/owner"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/util/signature"
|
||||
)
|
||||
|
||||
var (
|
||||
errNilBearerToken = errors.New("bearer token is not set")
|
||||
errNilBearerTokenBody = errors.New("bearer token body is not set")
|
||||
errNilBearerTokenEACL = errors.New("bearer token EACL table is not set")
|
||||
)
|
||||
|
||||
type BearerToken struct {
|
||||
token acl.BearerToken
|
||||
}
|
||||
|
||||
// ToV2 converts BearerToken to v2 BearerToken message.
|
||||
//
|
||||
// Nil BearerToken converts to nil.
|
||||
func (b *BearerToken) ToV2() *acl.BearerToken {
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &b.token
|
||||
}
|
||||
|
||||
func (b *BearerToken) SetLifetime(exp, nbf, iat uint64) {
|
||||
body := b.token.GetBody()
|
||||
if body == nil {
|
||||
body = new(acl.BearerTokenBody)
|
||||
}
|
||||
|
||||
lt := new(acl.TokenLifetime)
|
||||
lt.SetExp(exp)
|
||||
lt.SetNbf(nbf)
|
||||
lt.SetIat(iat)
|
||||
|
||||
body.SetLifetime(lt)
|
||||
b.token.SetBody(body)
|
||||
}
|
||||
|
||||
func (b *BearerToken) SetEACLTable(table *eacl.Table) {
|
||||
body := b.token.GetBody()
|
||||
if body == nil {
|
||||
body = new(acl.BearerTokenBody)
|
||||
}
|
||||
|
||||
body.SetEACL(table.ToV2())
|
||||
b.token.SetBody(body)
|
||||
}
|
||||
|
||||
func (b *BearerToken) SetOwner(id *owner.ID) {
|
||||
body := b.token.GetBody()
|
||||
if body == nil {
|
||||
body = new(acl.BearerTokenBody)
|
||||
}
|
||||
|
||||
body.SetOwnerID(id.ToV2())
|
||||
b.token.SetBody(body)
|
||||
}
|
||||
|
||||
func (b *BearerToken) SignToken(key *ecdsa.PrivateKey) error {
|
||||
err := sanityCheck(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
signWrapper := v2signature.StableMarshalerWrapper{SM: b.token.GetBody()}
|
||||
|
||||
return signature.SignDataWithHandler(key, signWrapper, func(key []byte, sig []byte) {
|
||||
bearerSignature := new(refs.Signature)
|
||||
bearerSignature.SetKey(key)
|
||||
bearerSignature.SetSign(sig)
|
||||
b.token.SetSignature(bearerSignature)
|
||||
})
|
||||
}
|
||||
|
||||
// Issuer returns owner.ID associated with the key that signed bearer token.
|
||||
// To pass node validation it should be owner of requested container. Returns
|
||||
// nil if token is not signed.
|
||||
func (b *BearerToken) Issuer() *owner.ID {
|
||||
pub, _ := keys.NewPublicKeyFromBytes(b.token.GetSignature().GetKey(), elliptic.P256())
|
||||
wallet, err := owner.NEO3WalletFromPublicKey((*ecdsa.PublicKey)(pub))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return owner.NewIDFromNeo3Wallet(wallet)
|
||||
}
|
||||
|
||||
// NewBearerToken creates and initializes blank BearerToken.
|
||||
//
|
||||
// Defaults:
|
||||
// - signature: nil;
|
||||
// - eacl: nil;
|
||||
// - ownerID: nil;
|
||||
// - exp: 0;
|
||||
// - nbf: 0;
|
||||
// - iat: 0.
|
||||
func NewBearerToken() *BearerToken {
|
||||
b := new(BearerToken)
|
||||
b.token = acl.BearerToken{}
|
||||
b.token.SetBody(new(acl.BearerTokenBody))
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// ToV2 converts BearerToken to v2 BearerToken message.
|
||||
func NewBearerTokenFromV2(v2 *acl.BearerToken) *BearerToken {
|
||||
if v2 == nil {
|
||||
v2 = new(acl.BearerToken)
|
||||
}
|
||||
|
||||
return &BearerToken{
|
||||
token: *v2,
|
||||
}
|
||||
}
|
||||
|
||||
// sanityCheck if bearer token is ready to be issued.
|
||||
func sanityCheck(b *BearerToken) error {
|
||||
switch {
|
||||
case b == nil:
|
||||
return errNilBearerToken
|
||||
case b.token.GetBody() == nil:
|
||||
return errNilBearerTokenBody
|
||||
case b.token.GetBody().GetEACL() == nil:
|
||||
return errNilBearerTokenEACL
|
||||
}
|
||||
|
||||
// consider checking EACL sanity there, lifetime correctness, etc.
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshal marshals BearerToken into a protobuf binary form.
|
||||
//
|
||||
// Buffer is allocated when the argument is empty.
|
||||
// Otherwise, the first buffer is used.
|
||||
func (b *BearerToken) Marshal(bs ...[]byte) ([]byte, error) {
|
||||
var buf []byte
|
||||
if len(bs) > 0 {
|
||||
buf = bs[0]
|
||||
}
|
||||
|
||||
return b.ToV2().
|
||||
StableMarshal(buf)
|
||||
}
|
||||
|
||||
// Unmarshal unmarshals protobuf binary representation of BearerToken.
|
||||
func (b *BearerToken) Unmarshal(data []byte) error {
|
||||
fV2 := new(acl.BearerToken)
|
||||
if err := fV2.Unmarshal(data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*b = *NewBearerTokenFromV2(fV2)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON encodes BearerToken to protobuf JSON format.
|
||||
func (b *BearerToken) MarshalJSON() ([]byte, error) {
|
||||
return b.ToV2().
|
||||
MarshalJSON()
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes BearerToken from protobuf JSON format.
|
||||
func (b *BearerToken) UnmarshalJSON(data []byte) error {
|
||||
fV2 := new(acl.BearerToken)
|
||||
if err := fV2.UnmarshalJSON(data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*b = *NewBearerTokenFromV2(fV2)
|
||||
|
||||
return nil
|
||||
}
|
84
token/bearer_test.go
Normal file
84
token/bearer_test.go
Normal file
|
@ -0,0 +1,84 @@
|
|||
package token_test
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/eacl"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/owner"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/token"
|
||||
tokentest "github.com/nspcc-dev/neofs-sdk-go/token/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestBearerToken_Issuer(t *testing.T) {
|
||||
bearerToken := token.NewBearerToken()
|
||||
|
||||
t.Run("non signed token", func(t *testing.T) {
|
||||
require.Nil(t, bearerToken.Issuer())
|
||||
})
|
||||
|
||||
t.Run("signed token", func(t *testing.T) {
|
||||
p, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
|
||||
wallet, err := owner.NEO3WalletFromPublicKey((*ecdsa.PublicKey)(p.PublicKey()))
|
||||
require.NoError(t, err)
|
||||
|
||||
ownerID := owner.NewIDFromNeo3Wallet(wallet)
|
||||
|
||||
bearerToken.SetEACLTable(eacl.NewTable())
|
||||
require.NoError(t, bearerToken.SignToken(&p.PrivateKey))
|
||||
require.True(t, ownerID.Equal(bearerToken.Issuer()))
|
||||
})
|
||||
}
|
||||
|
||||
func TestFilterEncoding(t *testing.T) {
|
||||
f := tokentest.Generate()
|
||||
|
||||
t.Run("binary", func(t *testing.T) {
|
||||
data, err := f.Marshal()
|
||||
require.NoError(t, err)
|
||||
|
||||
f2 := token.NewBearerToken()
|
||||
require.NoError(t, f2.Unmarshal(data))
|
||||
|
||||
require.Equal(t, f, f2)
|
||||
})
|
||||
|
||||
t.Run("json", func(t *testing.T) {
|
||||
data, err := f.MarshalJSON()
|
||||
require.NoError(t, err)
|
||||
|
||||
d2 := token.NewBearerToken()
|
||||
require.NoError(t, d2.UnmarshalJSON(data))
|
||||
|
||||
require.Equal(t, f, d2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestBearerToken_ToV2(t *testing.T) {
|
||||
t.Run("nil", func(t *testing.T) {
|
||||
var x *token.BearerToken
|
||||
|
||||
require.Nil(t, x.ToV2())
|
||||
})
|
||||
}
|
||||
|
||||
func TestNewBearerToken(t *testing.T) {
|
||||
t.Run("default values", func(t *testing.T) {
|
||||
tkn := token.NewBearerToken()
|
||||
|
||||
// convert to v2 message
|
||||
tknV2 := tkn.ToV2()
|
||||
|
||||
require.NotNil(t, tknV2.GetBody())
|
||||
require.Zero(t, tknV2.GetBody().GetLifetime().GetExp())
|
||||
require.Zero(t, tknV2.GetBody().GetLifetime().GetNbf())
|
||||
require.Zero(t, tknV2.GetBody().GetLifetime().GetIat())
|
||||
require.Nil(t, tknV2.GetBody().GetEACL())
|
||||
require.Nil(t, tknV2.GetBody().GetOwnerID())
|
||||
require.Nil(t, tknV2.GetSignature())
|
||||
})
|
||||
}
|
40
token/test/generate.go
Normal file
40
token/test/generate.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
package tokentest
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
eacltest "github.com/nspcc-dev/neofs-sdk-go/eacl/test"
|
||||
ownertest "github.com/nspcc-dev/neofs-sdk-go/owner/test"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/token"
|
||||
)
|
||||
|
||||
// Generate returns random token.BearerToken.
|
||||
//
|
||||
// Resulting token is unsigned.
|
||||
func Generate() *token.BearerToken {
|
||||
x := token.NewBearerToken()
|
||||
|
||||
x.SetLifetime(3, 2, 1)
|
||||
x.SetOwner(ownertest.GenerateID())
|
||||
x.SetEACLTable(eacltest.Table())
|
||||
|
||||
return x
|
||||
}
|
||||
|
||||
// GenerateSigned returns signed random token.BearerToken.
|
||||
//
|
||||
// Panics if token could not be signed (actually unexpected).
|
||||
func GenerateSigned() *token.BearerToken {
|
||||
tok := Generate()
|
||||
|
||||
p, err := keys.NewPrivateKey()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = tok.SignToken(&p.PrivateKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return tok
|
||||
}
|
Loading…
Reference in a new issue