f9248c21d4
* Satellite verification on storage node * fix formatting * fix formatting * rename SignatureAuth to SignedMessage * fixes after review * fix linter errors * improve errors handling * remove SignedMessageProvider * fix liter errors * params changed to authorization, signed message in audit, minor fixes * fix formatting
340 lines
8.8 KiB
Go
340 lines
8.8 KiB
Go
// Copyright (C) 2018 Storj Labs, Inc.
|
|
// See LICENSE for copying information.
|
|
|
|
package pdbclient
|
|
|
|
import (
|
|
"context"
|
|
"crypto/ecdsa"
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"encoding/base64"
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/golang/mock/gomock"
|
|
"github.com/golang/protobuf/proto"
|
|
"github.com/golang/protobuf/ptypes"
|
|
"github.com/gtank/cryptopasta"
|
|
"github.com/stretchr/testify/assert"
|
|
"google.golang.org/grpc/credentials"
|
|
"google.golang.org/grpc/metadata"
|
|
"google.golang.org/grpc/peer"
|
|
|
|
"storj.io/storj/pkg/auth"
|
|
p "storj.io/storj/pkg/paths"
|
|
"storj.io/storj/pkg/pb"
|
|
"storj.io/storj/pkg/provider"
|
|
"storj.io/storj/pkg/storage/meta"
|
|
)
|
|
|
|
const (
|
|
unauthenticated = "failed API creds"
|
|
noPathGiven = "file path not given"
|
|
)
|
|
|
|
var (
|
|
ErrUnauthenticated = errors.New(unauthenticated)
|
|
ErrNoFileGiven = errors.New(noPathGiven)
|
|
)
|
|
|
|
func TestNewPointerDBClient(t *testing.T) {
|
|
// mocked grpcClient so we don't have
|
|
// to call the network to test the code
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
gc := NewMockPointerDBClient(ctrl)
|
|
pdb := PointerDB{grpcClient: gc}
|
|
|
|
assert.NotNil(t, pdb)
|
|
assert.NotNil(t, pdb.grpcClient)
|
|
}
|
|
|
|
func makePointer(path p.Path) pb.PutRequest {
|
|
// rps is an example slice of RemotePieces to add to this
|
|
// REMOTE pointer type.
|
|
var rps []*pb.RemotePiece
|
|
rps = append(rps, &pb.RemotePiece{
|
|
PieceNum: 1,
|
|
NodeId: "testId",
|
|
})
|
|
pr := pb.PutRequest{
|
|
Path: path.String(),
|
|
Pointer: &pb.Pointer{
|
|
Type: pb.Pointer_REMOTE,
|
|
Remote: &pb.RemoteSegment{
|
|
Redundancy: &pb.RedundancyScheme{
|
|
Type: pb.RedundancyScheme_RS,
|
|
MinReq: 1,
|
|
Total: 3,
|
|
RepairThreshold: 2,
|
|
SuccessThreshold: 3,
|
|
},
|
|
PieceId: "testId",
|
|
RemotePieces: rps,
|
|
},
|
|
Size: int64(1),
|
|
},
|
|
}
|
|
return pr
|
|
}
|
|
|
|
func TestPut(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
for i, tt := range []struct {
|
|
APIKey []byte
|
|
path p.Path
|
|
err error
|
|
errString string
|
|
}{
|
|
{[]byte("abc123"), p.New("file1/file2"), nil, ""},
|
|
{[]byte("wrong key"), p.New("file1/file2"), ErrUnauthenticated, unauthenticated},
|
|
{[]byte("abc123"), p.New(""), ErrNoFileGiven, noPathGiven},
|
|
{[]byte("wrong key"), p.New(""), ErrUnauthenticated, unauthenticated},
|
|
{[]byte(""), p.New(""), ErrUnauthenticated, unauthenticated},
|
|
} {
|
|
ctx := context.Background()
|
|
ctx = auth.WithAPIKey(ctx, tt.APIKey)
|
|
|
|
putRequest := makePointer(tt.path)
|
|
|
|
errTag := fmt.Sprintf("Test case #%d", i)
|
|
gc := NewMockPointerDBClient(ctrl)
|
|
pdb := PointerDB{grpcClient: gc}
|
|
|
|
// here we don't care what type of context we pass
|
|
gc.EXPECT().Put(gomock.Any(), &putRequest).Return(nil, tt.err)
|
|
|
|
err := pdb.Put(ctx, tt.path, putRequest.Pointer)
|
|
|
|
if err != nil {
|
|
assert.EqualError(t, err, tt.errString, errTag)
|
|
} else {
|
|
assert.NoError(t, err, errTag)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGet(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
for i, tt := range []struct {
|
|
APIKey []byte
|
|
path p.Path
|
|
err error
|
|
errString string
|
|
}{
|
|
{[]byte("wrong key"), p.New("file1/file2"), ErrUnauthenticated, unauthenticated},
|
|
{[]byte("abc123"), p.New(""), ErrNoFileGiven, noPathGiven},
|
|
{[]byte("wrong key"), p.New(""), ErrUnauthenticated, unauthenticated},
|
|
{[]byte(""), p.New(""), ErrUnauthenticated, unauthenticated},
|
|
{[]byte("abc123"), p.New("file1/file2"), nil, ""},
|
|
} {
|
|
ctx := context.Background()
|
|
ctx = auth.WithAPIKey(ctx, tt.APIKey)
|
|
|
|
getPointer := makePointer(tt.path)
|
|
getRequest := pb.GetRequest{Path: tt.path.String()}
|
|
|
|
data, err := proto.Marshal(getPointer.Pointer)
|
|
if err != nil {
|
|
log.Fatal("marshaling error: ", err)
|
|
}
|
|
|
|
byteData := data
|
|
ptr := &pb.Pointer{}
|
|
err = proto.Unmarshal(byteData, ptr)
|
|
assert.NoError(t, err)
|
|
|
|
getResponse := pb.GetResponse{Pointer: ptr}
|
|
|
|
errTag := fmt.Sprintf("Test case #%d", i)
|
|
|
|
gc := NewMockPointerDBClient(ctrl)
|
|
pdb := PointerDB{grpcClient: gc}
|
|
|
|
gc.EXPECT().Get(gomock.Any(), &getRequest).Return(&getResponse, tt.err)
|
|
|
|
pointer, err := pdb.Get(ctx, tt.path)
|
|
|
|
if err != nil {
|
|
assert.True(t, strings.Contains(err.Error(), tt.errString), errTag)
|
|
assert.Nil(t, pointer)
|
|
} else {
|
|
assert.NotNil(t, pointer)
|
|
assert.NoError(t, err, errTag)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestList(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
for i, tt := range []struct {
|
|
prefix string
|
|
startAfter string
|
|
endBefore string
|
|
recursive bool
|
|
limit int
|
|
metaFlags uint32
|
|
APIKey string
|
|
items []*pb.ListResponse_Item
|
|
more bool
|
|
err error
|
|
errString string
|
|
}{
|
|
{"", "", "", false, 0, meta.None, "",
|
|
[]*pb.ListResponse_Item{}, false, nil, ""},
|
|
{"", "", "", false, 0, meta.None, "",
|
|
[]*pb.ListResponse_Item{{}}, false, nil, ""},
|
|
{"", "", "", false, -1, meta.None, "",
|
|
[]*pb.ListResponse_Item{}, false, ErrUnauthenticated, unauthenticated},
|
|
{"prefix", "after", "before", false, 1, meta.None, "some key",
|
|
[]*pb.ListResponse_Item{
|
|
{Path: "a/b/c"},
|
|
},
|
|
true, nil, ""},
|
|
{"prefix", "after", "before", false, 1, meta.All, "some key",
|
|
[]*pb.ListResponse_Item{
|
|
{Path: "a/b/c", Pointer: &pb.Pointer{
|
|
Size: 1234,
|
|
CreationDate: ptypes.TimestampNow(),
|
|
ExpirationDate: ptypes.TimestampNow(),
|
|
}},
|
|
},
|
|
true, nil, ""},
|
|
{"some/prefix", "start/after", "end/before", true, 123, meta.Size, "some key",
|
|
[]*pb.ListResponse_Item{
|
|
{Path: "a/b/c", Pointer: &pb.Pointer{Size: 1234}},
|
|
{Path: "x/y", Pointer: &pb.Pointer{Size: 789}},
|
|
},
|
|
true, nil, ""},
|
|
} {
|
|
ctx := context.Background()
|
|
ctx = auth.WithAPIKey(ctx, []byte(tt.APIKey))
|
|
|
|
errTag := fmt.Sprintf("Test case #%d", i)
|
|
|
|
listRequest := pb.ListRequest{
|
|
Prefix: tt.prefix,
|
|
StartAfter: tt.startAfter,
|
|
EndBefore: tt.endBefore,
|
|
Recursive: tt.recursive,
|
|
Limit: int32(tt.limit),
|
|
MetaFlags: tt.metaFlags,
|
|
}
|
|
|
|
listResponse := pb.ListResponse{Items: tt.items, More: tt.more}
|
|
|
|
gc := NewMockPointerDBClient(ctrl)
|
|
pdb := PointerDB{grpcClient: gc}
|
|
|
|
gc.EXPECT().List(gomock.Any(), &listRequest).Return(&listResponse, tt.err)
|
|
|
|
items, more, err := pdb.List(ctx, p.New(tt.prefix), p.New(tt.startAfter),
|
|
p.New(tt.endBefore), tt.recursive, tt.limit, tt.metaFlags)
|
|
|
|
if err != nil {
|
|
assert.EqualError(t, err, tt.errString, errTag)
|
|
assert.False(t, more)
|
|
assert.Nil(t, items)
|
|
} else {
|
|
assert.NoError(t, err, errTag)
|
|
assert.Equal(t, tt.more, more)
|
|
assert.NotNil(t, items)
|
|
assert.Equal(t, len(tt.items), len(items))
|
|
|
|
for i := 0; i < len(items); i++ {
|
|
assert.Equal(t, tt.items[i].GetPath(), items[i].Path.String())
|
|
assert.Equal(t, tt.items[i].GetPointer().GetSize(),
|
|
items[i].Pointer.GetSize())
|
|
assert.Equal(t, tt.items[i].GetPointer().GetCreationDate(),
|
|
items[i].Pointer.GetCreationDate())
|
|
assert.Equal(t, tt.items[i].GetPointer().GetExpirationDate(),
|
|
items[i].Pointer.GetExpirationDate())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDelete(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
for i, tt := range []struct {
|
|
APIKey []byte
|
|
path p.Path
|
|
err error
|
|
errString string
|
|
}{
|
|
{[]byte("wrong key"), p.New("file1/file2"), ErrUnauthenticated, unauthenticated},
|
|
{[]byte("abc123"), p.New(""), ErrNoFileGiven, noPathGiven},
|
|
{[]byte("wrong key"), p.New(""), ErrUnauthenticated, unauthenticated},
|
|
{[]byte(""), p.New(""), ErrUnauthenticated, unauthenticated},
|
|
{[]byte("abc123"), p.New("file1/file2"), nil, ""},
|
|
} {
|
|
ctx := context.Background()
|
|
ctx = auth.WithAPIKey(ctx, tt.APIKey)
|
|
|
|
deleteRequest := pb.DeleteRequest{Path: tt.path.String()}
|
|
|
|
errTag := fmt.Sprintf("Test case #%d", i)
|
|
gc := NewMockPointerDBClient(ctrl)
|
|
pdb := PointerDB{grpcClient: gc}
|
|
|
|
gc.EXPECT().Delete(gomock.Any(), &deleteRequest).Return(nil, tt.err)
|
|
|
|
err := pdb.Delete(ctx, tt.path)
|
|
|
|
if err != nil {
|
|
assert.EqualError(t, err, tt.errString, errTag)
|
|
} else {
|
|
assert.NoError(t, err, errTag)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSignedMessage(t *testing.T) {
|
|
ctx := context.Background()
|
|
ca, err := provider.NewCA(ctx, 12, 4)
|
|
assert.NoError(t, err)
|
|
identity, err := ca.NewIdentity()
|
|
assert.NoError(t, err)
|
|
|
|
peerCertificates := make([]*x509.Certificate, 2)
|
|
peerCertificates[0] = identity.Leaf
|
|
peerCertificates[1] = identity.CA
|
|
|
|
info := credentials.TLSInfo{State: tls.ConnectionState{PeerCertificates: peerCertificates}}
|
|
|
|
signature := base64.StdEncoding.EncodeToString([]byte("some value"))
|
|
|
|
header := metadata.Pairs("signature", signature)
|
|
peer := &peer.Peer{AuthInfo: info}
|
|
pointerdb := &PointerDB{
|
|
signatureHeader: &header,
|
|
peer: peer,
|
|
}
|
|
|
|
auth, err := pointerdb.SignedMessage()
|
|
assert.NoError(t, err)
|
|
|
|
pk, ok := identity.Leaf.PublicKey.(*ecdsa.PublicKey)
|
|
assert.Equal(t, true, ok)
|
|
|
|
expectedKey, err := cryptopasta.EncodePublicKey(pk)
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, expectedKey, auth.GetPublicKey())
|
|
assert.Equal(t, identity.ID.Bytes(), auth.GetData())
|
|
assert.Equal(t, "some value", string(auth.GetSignature()))
|
|
}
|