[#170] bearer: Add docs, refactor

Signed-off-by: Alex Vanin <alexey@nspcc.ru>
This commit is contained in:
Alex Vanin 2022-03-24 11:05:41 +03:00 committed by LeL
parent 96892d7bc4
commit 27cd721422
14 changed files with 463 additions and 368 deletions

316
bearer/bearer.go Normal file
View 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
View 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
View 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
View 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
View 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
}

View file

@ -4,15 +4,16 @@ import (
"context" "context"
"crypto/ecdsa" "crypto/ecdsa"
"github.com/nspcc-dev/neofs-api-go/v2/acl"
v2object "github.com/nspcc-dev/neofs-api-go/v2/object" v2object "github.com/nspcc-dev/neofs-api-go/v2/object"
v2refs "github.com/nspcc-dev/neofs-api-go/v2/refs" v2refs "github.com/nspcc-dev/neofs-api-go/v2/refs"
rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc" rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc"
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client" "github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
v2session "github.com/nspcc-dev/neofs-api-go/v2/session" 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" cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id" oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
"github.com/nspcc-dev/neofs-sdk-go/session" "github.com/nspcc-dev/neofs-sdk-go/session"
"github.com/nspcc-dev/neofs-sdk-go/token"
) )
// PrmObjectDelete groups parameters of ObjectDelete operation. // 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. // If set, underlying eACL rules will be used in access control.
// //
// Must be signed. // Must be signed.
func (x *PrmObjectDelete) WithBearerToken(t token.BearerToken) { func (x *PrmObjectDelete) WithBearerToken(t bearer.Token) {
x.meta.SetBearerToken(t.ToV2()) var v2token acl.BearerToken
t.WriteToV2(&v2token)
x.meta.SetBearerToken(&v2token)
} }
// FromContainer specifies NeoFS container of the object. // FromContainer specifies NeoFS container of the object.

View file

@ -7,17 +7,18 @@ import (
"fmt" "fmt"
"io" "io"
"github.com/nspcc-dev/neofs-api-go/v2/acl"
v2object "github.com/nspcc-dev/neofs-api-go/v2/object" v2object "github.com/nspcc-dev/neofs-api-go/v2/object"
v2refs "github.com/nspcc-dev/neofs-api-go/v2/refs" v2refs "github.com/nspcc-dev/neofs-api-go/v2/refs"
rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc" rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc"
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client" "github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
v2session "github.com/nspcc-dev/neofs-api-go/v2/session" 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" apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id" cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
"github.com/nspcc-dev/neofs-sdk-go/object" "github.com/nspcc-dev/neofs-sdk-go/object"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id" oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
"github.com/nspcc-dev/neofs-sdk-go/session" "github.com/nspcc-dev/neofs-sdk-go/session"
"github.com/nspcc-dev/neofs-sdk-go/token"
) )
// shared parameters of GET/HEAD/RANGE. // shared parameters of GET/HEAD/RANGE.
@ -32,7 +33,7 @@ type prmObjectRead struct {
session session.Token session session.Token
bearerSet bool bearerSet bool
bearer token.BearerToken bearer bearer.Token
cnrSet bool cnrSet bool
cnr cid.ID cnr cid.ID
@ -47,7 +48,9 @@ func (x prmObjectRead) writeToMetaHeader(h *v2session.RequestMetaHeader) {
} }
if x.bearerSet { if x.bearerSet {
h.SetBearerToken(x.bearer.ToV2()) var v2token acl.BearerToken
x.bearer.WriteToV2(&v2token)
h.SetBearerToken(&v2token)
} }
if x.sessionSet { 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. // If set, underlying eACL rules will be used in access control.
// //
// Must be signed. // Must be signed.
func (x *prmObjectRead) WithBearerToken(t token.BearerToken) { func (x *prmObjectRead) WithBearerToken(t bearer.Token) {
x.bearer = t x.bearer = t
x.bearerSet = true x.bearerSet = true
} }

View file

@ -3,15 +3,16 @@ package client
import ( import (
"context" "context"
"github.com/nspcc-dev/neofs-api-go/v2/acl"
v2object "github.com/nspcc-dev/neofs-api-go/v2/object" v2object "github.com/nspcc-dev/neofs-api-go/v2/object"
v2refs "github.com/nspcc-dev/neofs-api-go/v2/refs" v2refs "github.com/nspcc-dev/neofs-api-go/v2/refs"
rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc" rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc"
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client" "github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
v2session "github.com/nspcc-dev/neofs-api-go/v2/session" 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" cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id" oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
"github.com/nspcc-dev/neofs-sdk-go/session" "github.com/nspcc-dev/neofs-sdk-go/session"
"github.com/nspcc-dev/neofs-sdk-go/token"
) )
// PrmObjectHash groups parameters of ObjectHash operation. // 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. // If set, underlying eACL rules will be used in access control.
// //
// Must be signed. // Must be signed.
func (x *PrmObjectHash) WithBearerToken(t token.BearerToken) { func (x *PrmObjectHash) WithBearerToken(t bearer.Token) {
x.meta.SetBearerToken(t.ToV2()) var v2token acl.BearerToken
t.WriteToV2(&v2token)
x.meta.SetBearerToken(&v2token)
} }
// FromContainer specifies NeoFS container of the object. // FromContainer specifies NeoFS container of the object.

View file

@ -5,14 +5,15 @@ import (
"crypto/ecdsa" "crypto/ecdsa"
"fmt" "fmt"
"github.com/nspcc-dev/neofs-api-go/v2/acl"
v2object "github.com/nspcc-dev/neofs-api-go/v2/object" v2object "github.com/nspcc-dev/neofs-api-go/v2/object"
rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc" rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc"
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client" "github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
v2session "github.com/nspcc-dev/neofs-api-go/v2/session" 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" "github.com/nspcc-dev/neofs-sdk-go/object"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id" oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
"github.com/nspcc-dev/neofs-sdk-go/session" "github.com/nspcc-dev/neofs-sdk-go/session"
"github.com/nspcc-dev/neofs-sdk-go/token"
) )
// PrmObjectPutInit groups parameters of ObjectPutInit operation. // 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. // WithBearerToken attaches bearer token to be used for the operation.
// Should be called once before any writing steps. // Should be called once before any writing steps.
func (x *ObjectWriter) WithBearerToken(t token.BearerToken) { func (x *ObjectWriter) WithBearerToken(t bearer.Token) {
x.metaHdr.SetBearerToken(t.ToV2()) var v2token acl.BearerToken
t.WriteToV2(&v2token)
x.metaHdr.SetBearerToken(&v2token)
} }
// WithinSession specifies session within which object should be stored. // WithinSession specifies session within which object should be stored.

View file

@ -7,17 +7,18 @@ import (
"fmt" "fmt"
"io" "io"
"github.com/nspcc-dev/neofs-api-go/v2/acl"
v2object "github.com/nspcc-dev/neofs-api-go/v2/object" v2object "github.com/nspcc-dev/neofs-api-go/v2/object"
v2refs "github.com/nspcc-dev/neofs-api-go/v2/refs" v2refs "github.com/nspcc-dev/neofs-api-go/v2/refs"
rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc" rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc"
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client" "github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
v2session "github.com/nspcc-dev/neofs-api-go/v2/session" 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" apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id" cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
"github.com/nspcc-dev/neofs-sdk-go/object" "github.com/nspcc-dev/neofs-sdk-go/object"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id" oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
"github.com/nspcc-dev/neofs-sdk-go/session" "github.com/nspcc-dev/neofs-sdk-go/session"
"github.com/nspcc-dev/neofs-sdk-go/token"
) )
// PrmObjectSearch groups parameters of ObjectSearch operation. // PrmObjectSearch groups parameters of ObjectSearch operation.
@ -30,7 +31,7 @@ type PrmObjectSearch struct {
session session.Token session session.Token
bearerSet bool bearerSet bool
bearer token.BearerToken bearer bearer.Token
cnrSet bool cnrSet bool
cnr cid.ID 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. // If set, underlying eACL rules will be used in access control.
// //
// Must be signed. // Must be signed.
func (x *PrmObjectSearch) WithBearerToken(t token.BearerToken) { func (x *PrmObjectSearch) WithBearerToken(t bearer.Token) {
x.bearer = t x.bearer = t
x.bearerSet = true x.bearerSet = true
} }
@ -257,7 +258,9 @@ func (c *Client) ObjectSearchInit(ctx context.Context, prm PrmObjectSearch) (*Ob
} }
if prm.bearerSet { if prm.bearerSet {
meta.SetBearerToken(prm.bearer.ToV2()) var v2token acl.BearerToken
prm.bearer.WriteToV2(&v2token)
meta.SetBearerToken(&v2token)
} }
if prm.sessionSet { if prm.sessionSet {

View file

@ -17,6 +17,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
sessionv2 "github.com/nspcc-dev/neofs-api-go/v2/session" 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/accounting"
"github.com/nspcc-dev/neofs-sdk-go/bearer"
sdkClient "github.com/nspcc-dev/neofs-sdk-go/client" sdkClient "github.com/nspcc-dev/neofs-sdk-go/client"
"github.com/nspcc-dev/neofs-sdk-go/container" "github.com/nspcc-dev/neofs-sdk-go/container"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id" 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" 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/owner"
"github.com/nspcc-dev/neofs-sdk-go/session" "github.com/nspcc-dev/neofs-sdk-go/session"
"github.com/nspcc-dev/neofs-sdk-go/token"
"go.uber.org/zap" "go.uber.org/zap"
) )
@ -620,7 +620,7 @@ func (x *prmContext) useVerb(verb sessionv2.ObjectSessionVerb) {
type prmCommon struct { type prmCommon struct {
key *ecdsa.PrivateKey key *ecdsa.PrivateKey
btoken *token.BearerToken btoken *bearer.Token
stoken *session.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. // UseBearer attaches bearer token to be used for the operation.
func (x *prmCommon) UseBearer(token *token.BearerToken) { func (x *prmCommon) UseBearer(token bearer.Token) {
x.btoken = token x.btoken = &token
} }
// UseSession specifies session within which operation should be performed. // UseSession specifies session within which operation should be performed.

View file

@ -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
}

View file

@ -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())
})
}

View file

@ -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
}