forked from TrueCloudLab/frostfs-sdk-go
[#170] bearer: Add docs, refactor
Signed-off-by: Alex Vanin <alexey@nspcc.ru>
This commit is contained in:
parent
96892d7bc4
commit
27cd721422
14 changed files with 463 additions and 368 deletions
316
bearer/bearer.go
Normal file
316
bearer/bearer.go
Normal file
|
@ -0,0 +1,316 @@
|
|||
package bearer
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"errors"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neofs-api-go/v2/acl"
|
||||
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/signature"
|
||||
sigutil "github.com/nspcc-dev/neofs-sdk-go/util/signature"
|
||||
)
|
||||
|
||||
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 is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/acl.BearerToken
|
||||
// message. See ReadFromV2 / WriteToV2 methods.
|
||||
//
|
||||
// Instances can be created using built-in var declaration.
|
||||
//
|
||||
// Note that direct typecast is not safe and may result in loss of compatibility:
|
||||
// _ = Token(acl.BearerToken{}) // not recommended
|
||||
type Token acl.BearerToken
|
||||
|
||||
// ReadFromV2 reads Token from the acl.BearerToken message.
|
||||
//
|
||||
// See also WriteToV2.
|
||||
func (b *Token) ReadFromV2(m acl.BearerToken) {
|
||||
*b = Token(m)
|
||||
}
|
||||
|
||||
// WriteToV2 writes Token to the acl.BearerToken message.
|
||||
// The message must not be nil.
|
||||
//
|
||||
// See also ReadFromV2.
|
||||
func (b Token) WriteToV2(m *acl.BearerToken) {
|
||||
*m = (acl.BearerToken)(b)
|
||||
}
|
||||
|
||||
// IsEmpty returns true if bearer token has no fields set.
|
||||
func (b Token) IsEmpty() bool {
|
||||
v2token := (acl.BearerToken)(b)
|
||||
return v2token.GetBody() == nil && v2token.GetSignature() == nil
|
||||
}
|
||||
|
||||
// SetExpiration sets "exp" (expiration time) claim which identifies the
|
||||
// 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
|
||||
// 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 Expiration.
|
||||
func (b *Token) SetExpiration(exp uint64) {
|
||||
v2token := (*acl.BearerToken)(b)
|
||||
|
||||
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.
|
||||
//
|
||||
// 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
|
||||
// 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.
|
||||
//
|
||||
// Naming is inspired by https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.5.
|
||||
//
|
||||
// See also NotBefore.
|
||||
func (b *Token) SetNotBefore(nbf uint64) {
|
||||
v2token := (*acl.BearerToken)(b)
|
||||
|
||||
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.
|
||||
//
|
||||
// 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
|
||||
// the age of the Token.
|
||||
//
|
||||
// Naming is inspired by https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.6.
|
||||
//
|
||||
// See also IssuedAt.
|
||||
func (b *Token) SetIssuedAt(iat uint64) {
|
||||
v2token := (*acl.BearerToken)(b)
|
||||
|
||||
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.
|
||||
//
|
||||
// Empty Token has zero "iat".
|
||||
//
|
||||
// See also SetIssuedAt.
|
||||
func (b Token) IssuedAt() uint64 {
|
||||
v2token := (acl.BearerToken)(b)
|
||||
return v2token.GetBody().GetLifetime().GetIat()
|
||||
}
|
||||
|
||||
// SetEACLTable sets extended ACL table that should be used during object
|
||||
// service request processing with bearer token.
|
||||
//
|
||||
// See also EACLTable.
|
||||
func (b *Token) SetEACLTable(table eacl.Table) {
|
||||
v2 := (*acl.BearerToken)(b)
|
||||
|
||||
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
|
||||
// service request processing with bearer token.
|
||||
//
|
||||
// See also SetEACLTable.
|
||||
func (b Token) EACLTable() eacl.Table {
|
||||
v2 := (acl.BearerToken)(b)
|
||||
return *eacl.NewTableFromV2(v2.GetBody().GetEACL())
|
||||
}
|
||||
|
||||
// SetOwnerID sets owner.ID value of the user who can attach bearer token to
|
||||
// its requests.
|
||||
//
|
||||
// See also OwnerID.
|
||||
func (b *Token) SetOwnerID(id owner.ID) {
|
||||
v2 := (*acl.BearerToken)(b)
|
||||
|
||||
body := v2.GetBody()
|
||||
if body == nil {
|
||||
body = new(acl.BearerTokenBody)
|
||||
}
|
||||
|
||||
body.SetOwnerID(id.ToV2())
|
||||
v2.SetBody(body)
|
||||
}
|
||||
|
||||
// OwnerID returns owner.ID value of the user who can attach bearer token to
|
||||
// its requests.
|
||||
//
|
||||
// See also SetOwnerID.
|
||||
func (b Token) OwnerID() owner.ID {
|
||||
v2 := (acl.BearerToken)(b)
|
||||
return *owner.NewIDFromV2(v2.GetBody().GetOwnerID())
|
||||
}
|
||||
|
||||
// Sign signs bearer token. This method should be invoked with the private
|
||||
// key of container owner to allow overriding extended ACL table of the container
|
||||
// included in this token.
|
||||
//
|
||||
// See also Signature.
|
||||
func (b *Token) Sign(key ecdsa.PrivateKey) error {
|
||||
err := sanityCheck(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v2 := (*acl.BearerToken)(b)
|
||||
signWrapper := v2signature.StableMarshalerWrapper{SM: v2.GetBody()}
|
||||
|
||||
sig, err := sigutil.SignData(&key, signWrapper)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v2.SetSignature(sig.ToV2())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifySignature returns nil if bearer token contains correct signature.
|
||||
func (b Token) VerifySignature() error {
|
||||
if b.IsEmpty() {
|
||||
return nil
|
||||
}
|
||||
|
||||
v2 := (acl.BearerToken)(b)
|
||||
|
||||
return sigutil.VerifyData(
|
||||
v2signature.StableMarshalerWrapper{SM: v2.GetBody()},
|
||||
signature.NewFromV2(v2.GetSignature()))
|
||||
}
|
||||
|
||||
// Issuer returns owner.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.
|
||||
//
|
||||
// See also Sign.
|
||||
func (b Token) Issuer() (id owner.ID) {
|
||||
v2 := (acl.BearerToken)(b)
|
||||
|
||||
pub, _ := keys.NewPublicKeyFromBytes(v2.GetSignature().GetKey(), elliptic.P256())
|
||||
if pub == nil {
|
||||
return id
|
||||
}
|
||||
|
||||
return *owner.NewIDFromPublicKey((*ecdsa.PublicKey)(pub))
|
||||
}
|
||||
|
||||
// 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 {
|
||||
v2 := (acl.BearerToken)(b)
|
||||
|
||||
data, err := v2.StableMarshal(nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
57
bearer/bearer_test.go
Normal file
57
bearer/bearer_test.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
package bearer_test
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/bearer"
|
||||
tokentest "github.com/nspcc-dev/neofs-sdk-go/bearer/test"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/eacl"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/owner"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestBearerToken_Issuer(t *testing.T) {
|
||||
var bearerToken bearer.Token
|
||||
|
||||
t.Run("non signed token", func(t *testing.T) {
|
||||
id := bearerToken.Issuer()
|
||||
require.Equal(t, owner.ID{}, id)
|
||||
})
|
||||
|
||||
t.Run("signed token", func(t *testing.T) {
|
||||
p, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
|
||||
ownerID := owner.NewIDFromPublicKey((*ecdsa.PublicKey)(p.PublicKey()))
|
||||
|
||||
bearerToken.SetEACLTable(*eacl.NewTable())
|
||||
require.NoError(t, bearerToken.Sign(p.PrivateKey))
|
||||
issuer := bearerToken.Issuer()
|
||||
require.True(t, ownerID.Equal(&issuer))
|
||||
})
|
||||
}
|
||||
|
||||
func TestFilterEncoding(t *testing.T) {
|
||||
f := tokentest.Token()
|
||||
|
||||
t.Run("binary", func(t *testing.T) {
|
||||
data := f.Marshal()
|
||||
|
||||
var f2 bearer.Token
|
||||
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)
|
||||
|
||||
var d2 bearer.Token
|
||||
require.NoError(t, d2.UnmarshalJSON(data))
|
||||
|
||||
require.Equal(t, f, d2)
|
||||
})
|
||||
}
|
28
bearer/doc.go
Normal file
28
bearer/doc.go
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
Package bearer provides bearer token definition.
|
||||
|
||||
Bearer token is attached to the object service requests, and it overwrites
|
||||
extended ACL of the container. Mainly it is used to provide access of private
|
||||
data for specific user. Therefore, it must be signed by owner of the container.
|
||||
|
||||
Define bearer token by setting correct lifetime, extended ACL and owner ID of
|
||||
the user that will attach token to its requests.
|
||||
var bearerToken bearer.Token
|
||||
bearerToken.SetExpiration(500)
|
||||
bearerToken.SetIssuedAt(10)
|
||||
bearerToken.SetNotBefore(10)
|
||||
bearerToken.SetEACL(eaclTable)
|
||||
bearerToken.SetOwner(ownerID)
|
||||
|
||||
Bearer token must be signed by owner of the container.
|
||||
err := bearerToken.Sign(privateKey)
|
||||
|
||||
Provide signed token in JSON or binary format to the request sender. Request
|
||||
sender can attach this bearer token to the object service requests:
|
||||
import sdkClient "github.com/nspcc-dev/neofs-sdk-go/client"
|
||||
|
||||
var headParams sdkClient.PrmObjectHead
|
||||
headParams.WithBearerToken(bearerToken)
|
||||
response, err := client.ObjectHead(ctx, headParams)
|
||||
*/
|
||||
package bearer
|
6
bearer/test/doc.go
Normal file
6
bearer/test/doc.go
Normal file
|
@ -0,0 +1,6 @@
|
|||
/*
|
||||
Package bearertest provides functions for testing bearer package.
|
||||
|
||||
Note that this package intended only for tests.
|
||||
*/
|
||||
package bearertest
|
20
bearer/test/generate.go
Normal file
20
bearer/test/generate.go
Normal file
|
@ -0,0 +1,20 @@
|
|||
package bearertest
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neofs-sdk-go/bearer"
|
||||
eacltest "github.com/nspcc-dev/neofs-sdk-go/eacl/test"
|
||||
ownertest "github.com/nspcc-dev/neofs-sdk-go/owner/test"
|
||||
)
|
||||
|
||||
// Token returns random bearer.Token.
|
||||
//
|
||||
// Resulting token is unsigned.
|
||||
func Token() (t bearer.Token) {
|
||||
t.SetExpiration(3)
|
||||
t.SetNotBefore(2)
|
||||
t.SetIssuedAt(1)
|
||||
t.SetOwnerID(*ownertest.ID())
|
||||
t.SetEACLTable(*eacltest.Table())
|
||||
|
||||
return t
|
||||
}
|
|
@ -4,15 +4,16 @@ import (
|
|||
"context"
|
||||
"crypto/ecdsa"
|
||||
|
||||
"github.com/nspcc-dev/neofs-api-go/v2/acl"
|
||||
v2object "github.com/nspcc-dev/neofs-api-go/v2/object"
|
||||
v2refs "github.com/nspcc-dev/neofs-api-go/v2/refs"
|
||||
rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc"
|
||||
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
|
||||
v2session "github.com/nspcc-dev/neofs-api-go/v2/session"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/bearer"
|
||||
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
||||
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/session"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/token"
|
||||
)
|
||||
|
||||
// PrmObjectDelete groups parameters of ObjectDelete operation.
|
||||
|
@ -42,8 +43,10 @@ func (x *PrmObjectDelete) WithinSession(t session.Token) {
|
|||
// If set, underlying eACL rules will be used in access control.
|
||||
//
|
||||
// Must be signed.
|
||||
func (x *PrmObjectDelete) WithBearerToken(t token.BearerToken) {
|
||||
x.meta.SetBearerToken(t.ToV2())
|
||||
func (x *PrmObjectDelete) WithBearerToken(t bearer.Token) {
|
||||
var v2token acl.BearerToken
|
||||
t.WriteToV2(&v2token)
|
||||
x.meta.SetBearerToken(&v2token)
|
||||
}
|
||||
|
||||
// FromContainer specifies NeoFS container of the object.
|
||||
|
|
|
@ -7,17 +7,18 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/nspcc-dev/neofs-api-go/v2/acl"
|
||||
v2object "github.com/nspcc-dev/neofs-api-go/v2/object"
|
||||
v2refs "github.com/nspcc-dev/neofs-api-go/v2/refs"
|
||||
rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc"
|
||||
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
|
||||
v2session "github.com/nspcc-dev/neofs-api-go/v2/session"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/bearer"
|
||||
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
|
||||
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/object"
|
||||
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/session"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/token"
|
||||
)
|
||||
|
||||
// shared parameters of GET/HEAD/RANGE.
|
||||
|
@ -32,7 +33,7 @@ type prmObjectRead struct {
|
|||
session session.Token
|
||||
|
||||
bearerSet bool
|
||||
bearer token.BearerToken
|
||||
bearer bearer.Token
|
||||
|
||||
cnrSet bool
|
||||
cnr cid.ID
|
||||
|
@ -47,7 +48,9 @@ func (x prmObjectRead) writeToMetaHeader(h *v2session.RequestMetaHeader) {
|
|||
}
|
||||
|
||||
if x.bearerSet {
|
||||
h.SetBearerToken(x.bearer.ToV2())
|
||||
var v2token acl.BearerToken
|
||||
x.bearer.WriteToV2(&v2token)
|
||||
h.SetBearerToken(&v2token)
|
||||
}
|
||||
|
||||
if x.sessionSet {
|
||||
|
@ -83,7 +86,7 @@ func (x *prmObjectRead) WithinSession(t session.Token) {
|
|||
// If set, underlying eACL rules will be used in access control.
|
||||
//
|
||||
// Must be signed.
|
||||
func (x *prmObjectRead) WithBearerToken(t token.BearerToken) {
|
||||
func (x *prmObjectRead) WithBearerToken(t bearer.Token) {
|
||||
x.bearer = t
|
||||
x.bearerSet = true
|
||||
}
|
||||
|
|
|
@ -3,15 +3,16 @@ package client
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/nspcc-dev/neofs-api-go/v2/acl"
|
||||
v2object "github.com/nspcc-dev/neofs-api-go/v2/object"
|
||||
v2refs "github.com/nspcc-dev/neofs-api-go/v2/refs"
|
||||
rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc"
|
||||
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
|
||||
v2session "github.com/nspcc-dev/neofs-api-go/v2/session"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/bearer"
|
||||
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
||||
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/session"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/token"
|
||||
)
|
||||
|
||||
// PrmObjectHash groups parameters of ObjectHash operation.
|
||||
|
@ -45,8 +46,10 @@ func (x *PrmObjectHash) WithinSession(t session.Token) {
|
|||
// If set, underlying eACL rules will be used in access control.
|
||||
//
|
||||
// Must be signed.
|
||||
func (x *PrmObjectHash) WithBearerToken(t token.BearerToken) {
|
||||
x.meta.SetBearerToken(t.ToV2())
|
||||
func (x *PrmObjectHash) WithBearerToken(t bearer.Token) {
|
||||
var v2token acl.BearerToken
|
||||
t.WriteToV2(&v2token)
|
||||
x.meta.SetBearerToken(&v2token)
|
||||
}
|
||||
|
||||
// FromContainer specifies NeoFS container of the object.
|
||||
|
|
|
@ -5,14 +5,15 @@ import (
|
|||
"crypto/ecdsa"
|
||||
"fmt"
|
||||
|
||||
"github.com/nspcc-dev/neofs-api-go/v2/acl"
|
||||
v2object "github.com/nspcc-dev/neofs-api-go/v2/object"
|
||||
rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc"
|
||||
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
|
||||
v2session "github.com/nspcc-dev/neofs-api-go/v2/session"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/bearer"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/object"
|
||||
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/session"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/token"
|
||||
)
|
||||
|
||||
// PrmObjectPutInit groups parameters of ObjectPutInit operation.
|
||||
|
@ -69,8 +70,10 @@ func (x *ObjectWriter) UseKey(key ecdsa.PrivateKey) {
|
|||
|
||||
// WithBearerToken attaches bearer token to be used for the operation.
|
||||
// Should be called once before any writing steps.
|
||||
func (x *ObjectWriter) WithBearerToken(t token.BearerToken) {
|
||||
x.metaHdr.SetBearerToken(t.ToV2())
|
||||
func (x *ObjectWriter) WithBearerToken(t bearer.Token) {
|
||||
var v2token acl.BearerToken
|
||||
t.WriteToV2(&v2token)
|
||||
x.metaHdr.SetBearerToken(&v2token)
|
||||
}
|
||||
|
||||
// WithinSession specifies session within which object should be stored.
|
||||
|
|
|
@ -7,17 +7,18 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/nspcc-dev/neofs-api-go/v2/acl"
|
||||
v2object "github.com/nspcc-dev/neofs-api-go/v2/object"
|
||||
v2refs "github.com/nspcc-dev/neofs-api-go/v2/refs"
|
||||
rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc"
|
||||
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
|
||||
v2session "github.com/nspcc-dev/neofs-api-go/v2/session"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/bearer"
|
||||
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
|
||||
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/object"
|
||||
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/session"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/token"
|
||||
)
|
||||
|
||||
// PrmObjectSearch groups parameters of ObjectSearch operation.
|
||||
|
@ -30,7 +31,7 @@ type PrmObjectSearch struct {
|
|||
session session.Token
|
||||
|
||||
bearerSet bool
|
||||
bearer token.BearerToken
|
||||
bearer bearer.Token
|
||||
|
||||
cnrSet bool
|
||||
cnr cid.ID
|
||||
|
@ -59,7 +60,7 @@ func (x *PrmObjectSearch) WithinSession(t session.Token) {
|
|||
// If set, underlying eACL rules will be used in access control.
|
||||
//
|
||||
// Must be signed.
|
||||
func (x *PrmObjectSearch) WithBearerToken(t token.BearerToken) {
|
||||
func (x *PrmObjectSearch) WithBearerToken(t bearer.Token) {
|
||||
x.bearer = t
|
||||
x.bearerSet = true
|
||||
}
|
||||
|
@ -257,7 +258,9 @@ func (c *Client) ObjectSearchInit(ctx context.Context, prm PrmObjectSearch) (*Ob
|
|||
}
|
||||
|
||||
if prm.bearerSet {
|
||||
meta.SetBearerToken(prm.bearer.ToV2())
|
||||
var v2token acl.BearerToken
|
||||
prm.bearer.WriteToV2(&v2token)
|
||||
meta.SetBearerToken(&v2token)
|
||||
}
|
||||
|
||||
if prm.sessionSet {
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
sessionv2 "github.com/nspcc-dev/neofs-api-go/v2/session"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/accounting"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/bearer"
|
||||
sdkClient "github.com/nspcc-dev/neofs-sdk-go/client"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/container"
|
||||
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
||||
|
@ -27,7 +28,6 @@ import (
|
|||
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/owner"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/session"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/token"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
|
@ -620,7 +620,7 @@ func (x *prmContext) useVerb(verb sessionv2.ObjectSessionVerb) {
|
|||
|
||||
type prmCommon struct {
|
||||
key *ecdsa.PrivateKey
|
||||
btoken *token.BearerToken
|
||||
btoken *bearer.Token
|
||||
stoken *session.Token
|
||||
}
|
||||
|
||||
|
@ -631,8 +631,8 @@ func (x *prmCommon) UseKey(key *ecdsa.PrivateKey) {
|
|||
}
|
||||
|
||||
// UseBearer attaches bearer token to be used for the operation.
|
||||
func (x *prmCommon) UseBearer(token *token.BearerToken) {
|
||||
x.btoken = token
|
||||
func (x *prmCommon) UseBearer(token bearer.Token) {
|
||||
x.btoken = &token
|
||||
}
|
||||
|
||||
// UseSession specifies session within which operation should be performed.
|
||||
|
|
226
token/bearer.go
226
token/bearer.go
|
@ -1,226 +0,0 @@
|
|||
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"
|
||||
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/signature"
|
||||
sigutil "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 ContainerEACL 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) Empty() bool {
|
||||
return b == nil || b.token.GetBody() == nil && b.token.GetSignature() == nil
|
||||
}
|
||||
|
||||
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) Expiration() uint64 {
|
||||
return b.token.GetBody().GetLifetime().GetExp()
|
||||
}
|
||||
|
||||
func (b BearerToken) NotBeforeTime() uint64 {
|
||||
return b.token.GetBody().GetLifetime().GetNbf()
|
||||
}
|
||||
|
||||
func (b BearerToken) IssuedAt() uint64 {
|
||||
return b.token.GetBody().GetLifetime().GetIat()
|
||||
}
|
||||
|
||||
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) EACLTable() *eacl.Table {
|
||||
return eacl.NewTableFromV2(b.token.GetBody().GetEACL())
|
||||
}
|
||||
|
||||
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) OwnerID() *owner.ID {
|
||||
return owner.NewIDFromV2(b.token.GetBody().GetOwnerID())
|
||||
}
|
||||
|
||||
func (b *BearerToken) SignToken(key *ecdsa.PrivateKey) error {
|
||||
err := sanityCheck(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
signWrapper := v2signature.StableMarshalerWrapper{SM: b.token.GetBody()}
|
||||
|
||||
sig, err := sigutil.SignData(key, signWrapper)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b.token.SetSignature(sig.ToV2())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b BearerToken) Signature() *signature.Signature {
|
||||
return signature.NewFromV2(b.token.GetSignature())
|
||||
}
|
||||
|
||||
func (b BearerToken) VerifySignature() error {
|
||||
if b.Empty() {
|
||||
return nil
|
||||
}
|
||||
|
||||
sigV2 := b.token.GetSignature()
|
||||
return sigutil.VerifyData(
|
||||
v2signature.StableMarshalerWrapper{SM: b.token.GetBody()},
|
||||
signature.NewFromV2(sigV2))
|
||||
}
|
||||
|
||||
// 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())
|
||||
if pub == nil {
|
||||
return nil
|
||||
}
|
||||
return owner.NewIDFromPublicKey((*ecdsa.PublicKey)(pub))
|
||||
}
|
||||
|
||||
// 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 ContainerEACL 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
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
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)
|
||||
|
||||
ownerID := owner.NewIDFromPublicKey((*ecdsa.PublicKey)(p.PublicKey()))
|
||||
|
||||
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.BearerToken()
|
||||
|
||||
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())
|
||||
})
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
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"
|
||||
)
|
||||
|
||||
// BearerToken returns random token.BearerToken.
|
||||
//
|
||||
// Resulting token is unsigned.
|
||||
func BearerToken() *token.BearerToken {
|
||||
x := token.NewBearerToken()
|
||||
|
||||
x.SetLifetime(3, 2, 1)
|
||||
x.SetOwner(ownertest.ID())
|
||||
x.SetEACLTable(eacltest.Table())
|
||||
|
||||
return x
|
||||
}
|
||||
|
||||
// SignedBearerToken returns signed random token.BearerToken.
|
||||
//
|
||||
// Panics if token could not be signed (actually unexpected).
|
||||
func SignedBearerToken() *token.BearerToken {
|
||||
tok := BearerToken()
|
||||
|
||||
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