package main import ( "bytes" "crypto/tls" "fmt" "io/ioutil" "os" "testing" "github.com/docker/notary" "github.com/docker/notary/signer" "github.com/docker/notary/signer/keydbstore" "github.com/docker/notary/tuf/data" "github.com/docker/notary/utils" "github.com/jinzhu/gorm" _ "github.com/mattn/go-sqlite3" "github.com/spf13/viper" "github.com/stretchr/testify/require" ) const ( Cert = "../../fixtures/notary-signer.crt" Key = "../../fixtures/notary-signer.key" Root = "../../fixtures/root-ca.crt" ) // initializes a viper object with test configuration func configure(jsonConfig string) *viper.Viper { config := viper.New() config.SetConfigType("json") config.ReadConfig(bytes.NewBuffer([]byte(jsonConfig))) return config } // If the TLS configuration is invalid, an error is returned. This doesn't test // all the cases of the TLS configuration being invalid, since it's just // calling configuration.ParseTLSConfig - this test just makes sure the // error is propagated. func TestGetAddrAndTLSConfigInvalidTLS(t *testing.T) { invalids := []string{ `{"server": {"http_addr": ":1234", "grpc_addr": ":2345"}}`, `{"server": { "http_addr": ":1234", "grpc_addr": ":2345", "tls_cert_file": "nope", "tls_key_file": "nope" }}`, } for _, configJSON := range invalids { _, _, _, err := getAddrAndTLSConfig(configure(configJSON)) require.Error(t, err) require.Contains(t, err.Error(), "unable to set up TLS") } } // If a GRPC address is not provided, an error is returned. func TestGetAddrAndTLSConfigNoGRPCAddr(t *testing.T) { _, _, _, err := getAddrAndTLSConfig(configure(fmt.Sprintf(`{ "server": { "http_addr": ":1234", "tls_cert_file": "%s", "tls_key_file": "%s" } }`, Cert, Key))) require.Error(t, err) require.Contains(t, err.Error(), "grpc listen address required for server") } // If an HTTP address is not provided, an error is returned. func TestGetAddrAndTLSConfigNoHTTPAddr(t *testing.T) { _, _, _, err := getAddrAndTLSConfig(configure(fmt.Sprintf(`{ "server": { "grpc_addr": ":1234", "tls_cert_file": "%s", "tls_key_file": "%s" } }`, Cert, Key))) require.Error(t, err) require.Contains(t, err.Error(), "http listen address required for server") } // Success parsing a valid TLS config, HTTP address, and GRPC address. func TestGetAddrAndTLSConfigSuccess(t *testing.T) { httpAddr, grpcAddr, tlsConf, err := getAddrAndTLSConfig(configure(fmt.Sprintf(`{ "server": { "http_addr": ":2345", "grpc_addr": ":1234", "tls_cert_file": "%s", "tls_key_file": "%s" } }`, Cert, Key))) require.NoError(t, err) require.Equal(t, ":2345", httpAddr) require.Equal(t, ":1234", grpcAddr) require.NotNil(t, tlsConf) } // If a default alias is not provided to a DB backend, an error is returned. func TestSetupCryptoServicesDBStoreNoDefaultAlias(t *testing.T) { tmpFile, err := ioutil.TempFile("/tmp", "sqlite3") require.NoError(t, err) tmpFile.Close() defer os.Remove(tmpFile.Name()) _, err = setUpCryptoservices( configure(fmt.Sprintf( `{"storage": {"backend": "%s", "db_url": "%s"}}`, notary.SqliteBackend, tmpFile.Name())), []string{notary.SqliteBackend}) require.Error(t, err) require.Contains(t, err.Error(), "must provide a default alias for the key DB") } // If a default alias *is* provided to a valid DB backend, a valid // CryptoService is returned. (This depends on ParseStorage, which is tested // separately, so this doesn't test all the possible cases of storage // success/failure). func TestSetupCryptoServicesDBStoreSuccess(t *testing.T) { tmpFile, err := ioutil.TempFile("/tmp", "sqlite3") require.NoError(t, err) tmpFile.Close() defer os.Remove(tmpFile.Name()) // Ensure that the private_key table exists db, err := gorm.Open("sqlite3", tmpFile.Name()) require.NoError(t, err) var ( gormKey = keydbstore.GormPrivateKey{} count int ) db.CreateTable(&gormKey) db.Model(&gormKey).Count(&count) require.Equal(t, 0, count) cryptoServices, err := setUpCryptoservices( configure(fmt.Sprintf( `{"storage": {"backend": "%s", "db_url": "%s"}, "default_alias": "timestamp"}`, notary.SqliteBackend, tmpFile.Name())), []string{notary.SqliteBackend}) require.NoError(t, err) require.Len(t, cryptoServices, 2) edService, ok := cryptoServices[data.ED25519Key] require.True(t, ok) ecService, ok := cryptoServices[data.ECDSAKey] require.True(t, ok) require.Equal(t, edService, ecService) // since the keystores are not exposed by CryptoService, try creating // a key and seeing if it is in the sqlite DB. os.Setenv("NOTARY_SIGNER_TIMESTAMP", "password") defer os.Unsetenv("NOTARY_SIGNER_TIMESTAMP") _, err = ecService.Create("timestamp", "", data.ECDSAKey) require.NoError(t, err) db.Model(&gormKey).Count(&count) require.Equal(t, 1, count) } // If a memory backend is specified, then a default alias is not needed, and // a valid CryptoService is returned. func TestSetupCryptoServicesMemoryStore(t *testing.T) { config := configure(fmt.Sprintf(`{"storage": {"backend": "%s"}}`, notary.MemoryBackend)) cryptoServices, err := setUpCryptoservices(config, []string{notary.SqliteBackend, notary.MemoryBackend}) require.NoError(t, err) require.Len(t, cryptoServices, 2) edService, ok := cryptoServices[data.ED25519Key] require.True(t, ok) ecService, ok := cryptoServices[data.ECDSAKey] require.True(t, ok) require.Equal(t, edService, ecService) // since the keystores are not exposed by CryptoService, try creating // and getting the key pubKey, err := ecService.Create("", "", data.ECDSAKey) require.NoError(t, err) privKey, _, err := ecService.GetPrivateKey(pubKey.ID()) require.NoError(t, err) require.NotNil(t, privKey) } func TestSetupHTTPServer(t *testing.T) { httpServer := setupHTTPServer(":4443", nil, make(signer.CryptoServiceIndex)) require.Equal(t, ":4443", httpServer.Addr) require.Nil(t, httpServer.TLSConfig) } func TestSetupGRPCServerInvalidAddress(t *testing.T) { _, _, err := setupGRPCServer("nope", nil, make(signer.CryptoServiceIndex)) require.Error(t, err) require.Contains(t, err.Error(), "grpc server failed to listen on nope") } func TestSetupGRPCServerSuccess(t *testing.T) { tlsConf := tls.Config{InsecureSkipVerify: true} grpcServer, lis, err := setupGRPCServer(":7899", &tlsConf, make(signer.CryptoServiceIndex)) defer lis.Close() require.NoError(t, err) require.Equal(t, "[::]:7899", lis.Addr().String()) require.Equal(t, "tcp", lis.Addr().Network()) require.NotNil(t, grpcServer) }