session: implement gRPC session creator

This commit is contained in:
Leonard Lyubich 2020-05-08 12:34:16 +03:00
parent 6d71ea239b
commit b079a7604f
6 changed files with 219 additions and 5 deletions

View file

@ -37,9 +37,14 @@ type (
OwnerID chain.WalletAddress OwnerID chain.WalletAddress
) )
// OwnerIDSource is an interface of the container of an OwnerID value with read access.
type OwnerIDSource interface {
GetOwnerID() OwnerID
}
// OwnerIDContainer is an interface of the container of an OwnerID value. // OwnerIDContainer is an interface of the container of an OwnerID value.
type OwnerIDContainer interface { type OwnerIDContainer interface {
GetOwnerID() OwnerID OwnerIDSource
SetOwnerID(OwnerID) SetOwnerID(OwnerID)
} }

View file

@ -124,6 +124,18 @@ type ExpirationEpochContainer interface {
SetExpirationEpoch(uint64) SetExpirationEpoch(uint64)
} }
// LifetimeSource is an interface of the container of creation-expiration epoch pair with read access.
type LifetimeSource interface {
CreationEpochSource
ExpirationEpochSource
}
// LifetimeSource is an interface of the container of creation-expiration epoch pair.
type LifetimeContainer interface {
CreationEpochContainer
ExpirationEpochContainer
}
// SessionKeySource is an interface of the container of session key bytes with read access. // SessionKeySource is an interface of the container of session key bytes with read access.
type SessionKeySource interface { type SessionKeySource interface {
GetSessionKey() []byte GetSessionKey() []byte
@ -157,16 +169,14 @@ type SessionTokenSource interface {
// - ID of the token's owner; // - ID of the token's owner;
// - verb of the session; // - verb of the session;
// - address of the session object; // - address of the session object;
// - creation epoch number of the token; // - token lifetime;
// - expiration epoch number of the token;
// - public session key bytes. // - public session key bytes.
type SessionTokenInfo interface { type SessionTokenInfo interface {
TokenIDContainer TokenIDContainer
OwnerIDContainer OwnerIDContainer
VerbContainer VerbContainer
AddressContainer AddressContainer
CreationEpochContainer LifetimeContainer
ExpirationEpochContainer
SessionKeyContainer SessionKeyContainer
} }

67
session/create.go Normal file
View file

@ -0,0 +1,67 @@
package session
import (
"context"
"crypto/ecdsa"
"github.com/nspcc-dev/neofs-api-go/internal"
"github.com/nspcc-dev/neofs-api-go/service"
crypto "github.com/nspcc-dev/neofs-crypto"
"google.golang.org/grpc"
)
type gRPCCreator struct {
conn *grpc.ClientConn
key *ecdsa.PrivateKey
clientFunc func(*grpc.ClientConn) SessionClient
}
const ErrNilCreateParamsSource = internal.Error("create params source is nil")
const ErrNilGPRCClientConn = internal.Error("gRPC client connection is nil")
// NewGRPCCreator unites virtual gRPC client with private ket and returns Creator interface.
//
// If passed ClientConn is nil, ErrNilGPRCClientConn returns.
// If passed private key is nil, crypto.ErrEmptyPrivateKey returns.
func NewGRPCCreator(conn *grpc.ClientConn, key *ecdsa.PrivateKey) (Creator, error) {
if conn == nil {
return nil, ErrNilGPRCClientConn
} else if key == nil {
return nil, crypto.ErrEmptyPrivateKey
}
return &gRPCCreator{
conn: conn,
key: key,
clientFunc: NewSessionClient,
}, nil
}
// Create constructs message, signs it with private key and sends it to a gRPC client.
//
// If passed CreateParamsSource is nil, ErrNilCreateParamsSource returns.
// If message could not be signed, an error returns.
func (s gRPCCreator) Create(ctx context.Context, p CreateParamsSource) (CreateResult, error) {
if p == nil {
return nil, ErrNilCreateParamsSource
}
// create and fill a message
req := new(CreateRequest)
req.SetOwnerID(p.GetOwnerID())
req.SetCreationEpoch(p.CreationEpoch())
req.SetExpirationEpoch(p.ExpirationEpoch())
// sign with private key
if err := service.SignDataWithSessionToken(s.key, req); err != nil {
return nil, err
}
// make gRPC call
return s.clientFunc(s.conn).Create(ctx, req)
}

103
session/create_test.go Normal file
View file

@ -0,0 +1,103 @@
package session
import (
"context"
"crypto/ecdsa"
"testing"
"github.com/nspcc-dev/neofs-api-go/service"
crypto "github.com/nspcc-dev/neofs-crypto"
"github.com/nspcc-dev/neofs-crypto/test"
"github.com/pkg/errors"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
)
type testSessionClient struct {
fn func(*CreateRequest)
resp *CreateResponse
err error
}
func (s testSessionClient) Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) (*CreateResponse, error) {
if s.fn != nil {
s.fn(in)
}
return s.resp, s.err
}
func TestNewGRPCCreator(t *testing.T) {
var (
err error
conn = new(grpc.ClientConn)
sk = new(ecdsa.PrivateKey)
)
// nil client connection
_, err = NewGRPCCreator(nil, sk)
require.EqualError(t, err, ErrNilGPRCClientConn.Error())
// nil private key
_, err = NewGRPCCreator(conn, nil)
require.EqualError(t, err, crypto.ErrEmptyPrivateKey.Error())
// valid params
res, err := NewGRPCCreator(conn, sk)
require.NoError(t, err)
v := res.(*gRPCCreator)
require.Equal(t, conn, v.conn)
require.Equal(t, sk, v.key)
require.NotNil(t, v.clientFunc)
}
func TestGRPCCreator_Create(t *testing.T) {
ctx := context.TODO()
s := new(gRPCCreator)
// nil CreateParamsSource
_, err := s.Create(ctx, nil)
require.EqualError(t, err, ErrNilCreateParamsSource.Error())
var (
ownerID = OwnerID{1, 2, 3}
created = uint64(2)
expired = uint64(4)
)
p := NewParams()
p.SetOwnerID(ownerID)
p.SetCreationEpoch(created)
p.SetExpirationEpoch(expired)
// nil private key
_, err = s.Create(ctx, p)
require.Error(t, err)
// create test private key
s.key = test.DecodeKey(0)
// create test client
c := &testSessionClient{
fn: func(req *CreateRequest) {
require.Equal(t, ownerID, req.GetOwnerID())
require.Equal(t, created, req.CreationEpoch())
require.Equal(t, expired, req.ExpirationEpoch())
require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(req))
},
resp: &CreateResponse{
ID: TokenID{1, 2, 3},
SessionKey: []byte{1, 2, 3},
},
err: errors.New("test error"),
}
s.clientFunc = func(*grpc.ClientConn) SessionClient {
return c
}
res, err := s.Create(ctx, p)
require.EqualError(t, err, c.err.Error())
require.Equal(t, c.resp, res)
}

View file

@ -14,6 +14,10 @@ const signedRequestDataSize = 0 +
var requestEndianness = binary.BigEndian var requestEndianness = binary.BigEndian
func NewParams() CreateParamsContainer {
return new(CreateRequest)
}
// GetOwnerID is an OwnerID field getter. // GetOwnerID is an OwnerID field getter.
func (m CreateRequest) GetOwnerID() OwnerID { func (m CreateRequest) GetOwnerID() OwnerID {
return m.OwnerID return m.OwnerID

View file

@ -5,6 +5,8 @@ import (
"crypto/ecdsa" "crypto/ecdsa"
"github.com/nspcc-dev/neofs-api-go/internal" "github.com/nspcc-dev/neofs-api-go/internal"
"github.com/nspcc-dev/neofs-api-go/refs"
"github.com/nspcc-dev/neofs-api-go/service"
) )
// PrivateToken is an interface of session private part. // PrivateToken is an interface of session private part.
@ -55,5 +57,28 @@ type KeyStore interface {
Get(context.Context, OwnerID) ([]*ecdsa.PublicKey, error) Get(context.Context, OwnerID) ([]*ecdsa.PublicKey, error)
} }
// CreateParamsSource is an interface of the container of session parameters with read access.
type CreateParamsSource interface {
refs.OwnerIDSource
service.LifetimeSource
}
// CreateParamsContainer is an interface of the container of session parameters.
type CreateParamsContainer interface {
refs.OwnerIDContainer
service.LifetimeContainer
}
// CreateResult is an interface of the container of an opened session info with read access.
type CreateResult interface {
service.TokenIDSource
service.SessionKeySource
}
// Creator is an interface of the tool for a session opening.
type Creator interface {
Create(context.Context, CreateParamsSource) (CreateResult, error)
}
// ErrPrivateTokenNotFound is raised when addressed private token was not found in storage. // ErrPrivateTokenNotFound is raised when addressed private token was not found in storage.
const ErrPrivateTokenNotFound = internal.Error("private token not found") const ErrPrivateTokenNotFound = internal.Error("private token not found")