From eecc97f90a295d83336b4ebf249724a895e33189 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 4 Mar 2024 15:43:19 +0400 Subject: [PATCH] bearer: Set issuer explicitly on signing Dedicated field for the bearer token issuer was recently added to the protocol https://github.com/nspcc-dev/neofs-api/issues/266. Now SDK provides getter and setter for it. Previously, `Token` type accepted `neofscrypto.Signer` parameter in `Sign` method to calculate and set signature of the bearer token. Obviously, the method did not set nonexistent issuer field. The only way to access the issuer was `ResolveIssuer` method resolving user ID from the public key. Now `Sign` method accepts parameter of `user.Signer` type to additionally set issuer field. This is a breaking change overall, but still needed for stable system authorization and library usage. `ResolveIssuer` method is marked as deprecated in favor of new `Issuer` one. Signed-off-by: Leonard Lyubich --- bearer/bearer.go | 49 +++++++++++++++++++++++++++++++--- bearer/bearer_test.go | 58 +++++++++++++++++++++++++++++++++++++++-- bearer/example_test.go | 3 +-- go.mod | 2 +- go.sum | 4 +-- object/slicer/slicer.go | 2 +- 6 files changed, 106 insertions(+), 12 deletions(-) diff --git a/bearer/bearer.go b/bearer/bearer.go index e6b18bcf..e03f8306 100644 --- a/bearer/bearer.go +++ b/bearer/bearer.go @@ -23,6 +23,9 @@ type Token struct { targetUserSet bool targetUser user.ID + issuerSet bool + issuer user.ID + eaclTableSet bool eaclTable eacl.Table @@ -58,6 +61,14 @@ func (b *Token) readFromV2(m acl.BearerToken, checkFieldPresence bool) error { } } + issuer := body.GetIssuer() + if b.issuerSet = issuer != nil; b.issuerSet { + err = b.issuer.ReadFromV2(*issuer) + if err != nil { + return fmt.Errorf("invalid issuer: %w", err) + } + } + lifetime := body.GetLifetime() if b.lifetimeSet = lifetime != nil; b.lifetimeSet { b.iat = lifetime.GetIat() @@ -85,7 +96,7 @@ func (b *Token) ReadFromV2(m acl.BearerToken) error { } func (b Token) fillBody() *acl.BearerTokenBody { - if !b.eaclTableSet && !b.targetUserSet && !b.lifetimeSet { + if !b.eaclTableSet && !b.targetUserSet && !b.lifetimeSet && !b.issuerSet { return nil } @@ -102,6 +113,13 @@ func (b Token) fillBody() *acl.BearerTokenBody { body.SetOwnerID(&targetUser) } + if b.issuerSet { + var issuer refs.OwnerID + b.issuer.WriteToV2(&issuer) + + body.SetIssuer(&issuer) + } + if b.lifetimeSet { var lifetime acl.TokenLifetime lifetime.SetIat(b.iat) @@ -244,8 +262,8 @@ 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 signer. -// Returns signature calculation errors. +// Sign calculates and writes signature of the [Token] data along with issuer ID +// using signer. Returns signature calculation errors. // // Sign MUST be called if [Token] is going to be transmitted over // NeoFS API V2 protocol. @@ -254,7 +272,9 @@ func (b Token) AssertUser(id user.ID) bool { // expected to be calculated as a final stage of Token formation. // // See also [Token.VerifySignature], [Token.Issuer], [Token.SignedData]. -func (b *Token) Sign(signer neofscrypto.Signer) error { +func (b *Token) Sign(signer user.Signer) error { + b.SetIssuer(signer.UserID()) + var sig neofscrypto.Signature err := sig.Calculate(signer, b.signedData()) @@ -364,10 +384,31 @@ func (b Token) SigningKeyBytes() []byte { return nil } +// SetIssuer sets NeoFS user ID of the [Token] issuer. +// +// See also [Token.Issuer], [Token.SignByIssuer]. +func (b *Token) SetIssuer(usr user.ID) { + b.issuerSet = true + b.issuer = usr +} + +// Issuer returns NeoFS user ID of the Token issuer. Zero value means unset +// issuer. +// +// See also [Token.SetIssuer]. +func (b Token) Issuer() user.ID { + if b.issuerSet { + return b.issuer + } + return user.ID{} +} + // 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 [Token.SigningKeyBytes]. +// +// Deprecated: use [Token.Issuer] instead. func (b Token) ResolveIssuer() user.ID { var usr user.ID binKey := b.SigningKeyBytes() diff --git a/bearer/bearer_test.go b/bearer/bearer_test.go index 23866fe7..808a5d29 100644 --- a/bearer/bearer_test.go +++ b/bearer/bearer_test.go @@ -257,7 +257,7 @@ func TestToken_Sign(t *testing.T) { require.False(t, val.VerifySignature()) - signer := test.RandomSigner(t) + signer := test.RandomSignerRFC6979(t) val = bearertest.Token(t) @@ -291,8 +291,9 @@ func TestToken_SignedData(t *testing.T) { signer := test.RandomSignerRFC6979(t) val = bearertest.Token(t) + val.SetIssuer(signer.UserID()) - test.SignedDataComponent(t, signer, &val) + test.SignedDataComponentUser(t, signer, &val) } func TestToken_ReadFromV2(t *testing.T) { @@ -391,3 +392,56 @@ func TestResolveIssuer(t *testing.T) { require.Equal(t, usr, val.ResolveIssuer()) } + +func TestToken_Issuer(t *testing.T) { + var token bearer.Token + var msg acl.BearerToken + filled := bearertest.Token(t) + + token.WriteToV2(&msg) + require.Zero(t, msg.GetBody()) + + val2 := filled + require.NoError(t, val2.Unmarshal(token.Marshal())) + + val2.WriteToV2(&msg) + require.Zero(t, msg.GetBody()) + + val2 = filled + + jd, err := token.MarshalJSON() + require.NoError(t, err) + + require.NoError(t, val2.UnmarshalJSON(jd)) + + val2.WriteToV2(&msg) + require.Zero(t, msg.GetBody()) + + // set value + usr := usertest.ID(t) + + var usrV2 refs.OwnerID + usr.WriteToV2(&usrV2) + + token.SetIssuer(usr) + + token.WriteToV2(&msg) + require.Equal(t, usrV2, *msg.GetBody().GetIssuer()) + + val2 = filled + + require.NoError(t, val2.Unmarshal(token.Marshal())) + + val2.WriteToV2(&msg) + require.Equal(t, usrV2, *msg.GetBody().GetIssuer()) + + val2 = filled + + jd, err = token.MarshalJSON() + require.NoError(t, err) + + require.NoError(t, val2.UnmarshalJSON(jd)) + + val2.WriteToV2(&msg) + require.Equal(t, usrV2, *msg.GetBody().GetIssuer()) +} diff --git a/bearer/example_test.go b/bearer/example_test.go index add9f637..a56353b1 100644 --- a/bearer/example_test.go +++ b/bearer/example_test.go @@ -6,7 +6,6 @@ import ( "github.com/nspcc-dev/neofs-sdk-go/bearer" "github.com/nspcc-dev/neofs-sdk-go/client" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" - neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto" "github.com/nspcc-dev/neofs-sdk-go/eacl" oid "github.com/nspcc-dev/neofs-sdk-go/object/id" "github.com/nspcc-dev/neofs-sdk-go/user" @@ -31,7 +30,7 @@ func Example() { // Bearer token must be signed by owner of the container. // import neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto" - var signer neofscrypto.Signer + var signer user.Signer // signer initialization, bearerToken initialization, other steps ... _ = bearerToken.Sign(signer) diff --git a/go.mod b/go.mod index 93fe75dc..a446f934 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/mr-tron/base58 v1.2.0 github.com/nspcc-dev/hrw/v2 v2.0.1 github.com/nspcc-dev/neo-go v0.105.1 - github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240228163253-cb87bbd5e4eb + github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240305074711-35bc78d84dc4 github.com/nspcc-dev/tzhash v1.7.2 github.com/stretchr/testify v1.8.4 github.com/testcontainers/testcontainers-go v0.24.1 diff --git a/go.sum b/go.sum index 4bafa082..eab04072 100644 --- a/go.sum +++ b/go.sum @@ -75,8 +75,8 @@ github.com/nspcc-dev/hrw/v2 v2.0.1 h1:CxYUkBeJvNfMEn2lHhrV6FjY8pZPceSxXUtMVq0BUO github.com/nspcc-dev/hrw/v2 v2.0.1/go.mod h1:iZAs5hT2q47EGq6AZ0FjaUI6ggntOi7vrY4utfzk5VA= github.com/nspcc-dev/neo-go v0.105.1 h1:r0b2yIwLBi+ARBKU94gHL9oTFEB/XMJ0YlS2HN9Qw34= github.com/nspcc-dev/neo-go v0.105.1/go.mod h1:GNh0cRALV/cuj+/xg2ZHDsrFbqcInqG7jjhqsLEnlNc= -github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240228163253-cb87bbd5e4eb h1:vvMxf818Ea2Ql+j9QX7zOlEXDrVlbAzR0DhGvrULilQ= -github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240228163253-cb87bbd5e4eb/go.mod h1:7Tm1NKEoUVVIUlkVwFrPh7GG5+Lmta2m7EGr4oVpBd8= +github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240305074711-35bc78d84dc4 h1:arN0Ypn+jawZpu1BND7TGRn44InAVIqKygndsx0y2no= +github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240305074711-35bc78d84dc4/go.mod h1:7Tm1NKEoUVVIUlkVwFrPh7GG5+Lmta2m7EGr4oVpBd8= github.com/nspcc-dev/rfc6979 v0.2.1 h1:8wWxkamHWFmO790GsewSoKUSJjVnL1fmdRpokU/RgRM= github.com/nspcc-dev/rfc6979 v0.2.1/go.mod h1:Tk7h5kyUWkhjyO3zUgFFhy1v2vQv3BvQEntakdtqrWc= github.com/nspcc-dev/tzhash v1.7.2 h1:iRXoa9TJqH/DQO7FFcqpq9BdruF9E7/xnFGlIghl5J4= diff --git a/object/slicer/slicer.go b/object/slicer/slicer.go index 0b74de7a..b99da7b1 100644 --- a/object/slicer/slicer.go +++ b/object/slicer/slicer.go @@ -263,7 +263,7 @@ func initPayloadStream(ctx context.Context, ow ObjectWriter, header object.Objec } else if opts.bearerToken != nil { prm.WithBearerToken(*opts.bearerToken) // token issuer is a container owner. - issuer := opts.bearerToken.ResolveIssuer() + issuer := opts.bearerToken.Issuer() owner = issuer header.SetOwnerID(&owner) }