docs/signer/client/signer_trust_test.go

190 lines
5.6 KiB
Go

package client
import (
"crypto/rand"
"errors"
"strings"
"testing"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"github.com/docker/notary/cryptoservice"
"github.com/docker/notary/passphrase"
pb "github.com/docker/notary/proto"
"github.com/docker/notary/signer"
"github.com/docker/notary/signer/api"
"github.com/docker/notary/trustmanager"
"github.com/docker/notary/tuf/data"
"github.com/docker/notary/tuf/signed"
"github.com/stretchr/testify/assert"
"golang.org/x/net/context"
)
type rpcHealthCheck func(
context.Context, *pb.Void, ...grpc.CallOption) (*pb.HealthStatus, error)
type StubKeyManagementClient struct {
pb.KeyManagementClient
healthCheck rpcHealthCheck
}
func (c StubKeyManagementClient) CheckHealth(x context.Context,
v *pb.Void, o ...grpc.CallOption) (*pb.HealthStatus, error) {
return c.healthCheck(x, v, o...)
}
type StubGRPCConnection struct {
fakeConnStatus grpc.ConnectivityState
}
func (c StubGRPCConnection) State() grpc.ConnectivityState {
return c.fakeConnStatus
}
func stubHealthFunction(t *testing.T, status map[string]string, err error) rpcHealthCheck {
return func(ctx context.Context, v *pb.Void, o ...grpc.CallOption) (*pb.HealthStatus, error) {
_, withDeadline := ctx.Deadline()
assert.True(t, withDeadline)
return &pb.HealthStatus{Status: status}, err
}
}
func makeSigner(kmFunc rpcHealthCheck, conn StubGRPCConnection) NotarySigner {
return NotarySigner{
StubKeyManagementClient{
pb.NewKeyManagementClient(nil),
kmFunc,
},
pb.NewSignerClient(nil),
conn,
}
}
// CheckHealth does not succeed if the KM server is unhealthy
func TestHealthCheckKMUnhealthy(t *testing.T) {
signer := makeSigner(
stubHealthFunction(t, map[string]string{"health": "not good"}, nil),
StubGRPCConnection{})
assert.Error(t, signer.CheckHealth(1*time.Second))
}
// CheckHealth does not succeed if the health check to the KM server errors
func TestHealthCheckKMError(t *testing.T) {
signer := makeSigner(
stubHealthFunction(t, nil, errors.New("Something's wrong")),
StubGRPCConnection{})
assert.Error(t, signer.CheckHealth(1*time.Second))
}
// CheckHealth does not succeed if the health check to the KM server times out
func TestHealthCheckKMTimeout(t *testing.T) {
signer := makeSigner(
stubHealthFunction(t, nil, grpc.Errorf(codes.DeadlineExceeded, "")),
StubGRPCConnection{})
err := signer.CheckHealth(1 * time.Second)
assert.Error(t, err)
assert.True(t, strings.Contains(err.Error(), "Timed out"))
}
// CheckHealth succeeds if KM is healthy and reachable.
func TestHealthCheckKMHealthy(t *testing.T) {
signer := makeSigner(
stubHealthFunction(t, make(map[string]string), nil),
StubGRPCConnection{})
assert.NoError(t, signer.CheckHealth(1*time.Second))
}
// CheckHealth fails immediately if not connected to the server.
func TestHealthCheckConnectionDied(t *testing.T) {
signer := makeSigner(
stubHealthFunction(t, make(map[string]string), nil),
StubGRPCConnection{grpc.Connecting})
assert.Error(t, signer.CheckHealth(1*time.Second))
}
var ret = passphrase.ConstantRetriever("pass")
func TestGetPrivateKeyIfNoKey(t *testing.T) {
signer := setUpSigner(t, trustmanager.NewKeyMemoryStore(ret))
privKey, _, err := signer.GetPrivateKey("bogus key ID")
assert.NoError(t, err)
assert.Nil(t, privKey)
}
func TestGetPrivateKeyAndSignWithExistingKey(t *testing.T) {
key, err := trustmanager.GenerateECDSAKey(rand.Reader)
assert.NoError(t, err, "could not generate key")
store := trustmanager.NewKeyMemoryStore(ret)
err = store.AddKey(key, trustmanager.KeyInfo{Role: data.CanonicalTimestampRole, Gun: "gun"})
assert.NoError(t, err, "could not add key to store")
signer := setUpSigner(t, store)
privKey, _, err := signer.GetPrivateKey(key.ID())
assert.NoError(t, err)
assert.NotNil(t, privKey)
msg := []byte("message!")
sig, err := privKey.Sign(rand.Reader, msg, nil)
assert.NoError(t, err)
err = signed.Verifiers[data.ECDSASignature].Verify(
data.PublicKeyFromPrivate(key), sig, msg)
assert.NoError(t, err)
}
type StubClientFromServers struct {
api.KeyManagementServer
api.SignerServer
}
func (c *StubClientFromServers) CreateKey(ctx context.Context,
algorithm *pb.Algorithm, _ ...grpc.CallOption) (*pb.PublicKey, error) {
return c.KeyManagementServer.CreateKey(ctx, algorithm)
}
func (c *StubClientFromServers) DeleteKey(ctx context.Context, keyID *pb.KeyID,
_ ...grpc.CallOption) (*pb.Void, error) {
return c.KeyManagementServer.DeleteKey(ctx, keyID)
}
func (c *StubClientFromServers) GetKeyInfo(ctx context.Context, keyID *pb.KeyID,
_ ...grpc.CallOption) (*pb.PublicKey, error) {
return c.KeyManagementServer.GetKeyInfo(ctx, keyID)
}
func (c *StubClientFromServers) Sign(ctx context.Context,
sr *pb.SignatureRequest, _ ...grpc.CallOption) (*pb.Signature, error) {
return c.SignerServer.Sign(ctx, sr)
}
func (c *StubClientFromServers) CheckHealth(ctx context.Context, v *pb.Void,
_ ...grpc.CallOption) (*pb.HealthStatus, error) {
return c.KeyManagementServer.CheckHealth(ctx, v)
}
func setUpSigner(t *testing.T, store trustmanager.KeyStore) NotarySigner {
cryptoService := cryptoservice.NewCryptoService("", store)
cryptoServices := signer.CryptoServiceIndex{
data.ED25519Key: cryptoService,
data.RSAKey: cryptoService,
data.ECDSAKey: cryptoService,
}
fakeHealth := func() map[string]string { return map[string]string{} }
client := StubClientFromServers{
KeyManagementServer: api.KeyManagementServer{CryptoServices: cryptoServices,
HealthChecker: fakeHealth},
SignerServer: api.SignerServer{CryptoServices: cryptoServices,
HealthChecker: fakeHealth},
}
return NotarySigner{kmClient: &client, sClient: &client}
}