From 7ffaee17f1d2145b7767b771c217720f9a890501 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Wed, 28 Nov 2018 08:16:33 +0000 Subject: [PATCH 01/57] Add initial service and config to interact with Hashicorp Vault --- .../config/HashicorpKeyVaultConfig.java | 33 +++++++++++++ .../quorum/tessera/config/KeyVaultType.java | 2 +- key-vault/hashicorp-key-vault/pom.xml | 27 +++++++++++ .../hashicorp/HashicorpKeyVaultService.java | 48 +++++++++++++++++++ key-vault/pom.xml | 1 + pom.xml | 6 +++ 6 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 config/src/main/java/com/quorum/tessera/config/HashicorpKeyVaultConfig.java create mode 100644 key-vault/hashicorp-key-vault/pom.xml create mode 100644 key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java diff --git a/config/src/main/java/com/quorum/tessera/config/HashicorpKeyVaultConfig.java b/config/src/main/java/com/quorum/tessera/config/HashicorpKeyVaultConfig.java new file mode 100644 index 0000000000..65faeccafe --- /dev/null +++ b/config/src/main/java/com/quorum/tessera/config/HashicorpKeyVaultConfig.java @@ -0,0 +1,33 @@ +package com.quorum.tessera.config; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import javax.xml.bind.annotation.XmlAttribute; + +public class HashicorpKeyVaultConfig extends ConfigItem implements KeyVaultConfig { + + @Valid + @NotNull + @XmlAttribute + private String url; + + public HashicorpKeyVaultConfig(String url) { + this.url = url; + } + + public HashicorpKeyVaultConfig() { + } + + public String getUrl() { + return this.url; + } + + public void setUrl(String url) { + this.url = url; + } + + @Override + public KeyVaultType getKeyVaultType() { + return KeyVaultType.HASHICORP; + } +} diff --git a/config/src/main/java/com/quorum/tessera/config/KeyVaultType.java b/config/src/main/java/com/quorum/tessera/config/KeyVaultType.java index 2fae03681f..fa0dfd6e07 100644 --- a/config/src/main/java/com/quorum/tessera/config/KeyVaultType.java +++ b/config/src/main/java/com/quorum/tessera/config/KeyVaultType.java @@ -1,5 +1,5 @@ package com.quorum.tessera.config; public enum KeyVaultType { - AZURE + AZURE, HASHICORP } diff --git a/key-vault/hashicorp-key-vault/pom.xml b/key-vault/hashicorp-key-vault/pom.xml new file mode 100644 index 0000000000..a08d58ac95 --- /dev/null +++ b/key-vault/hashicorp-key-vault/pom.xml @@ -0,0 +1,27 @@ + + + + key-vault + com.quorum.tessera + 0.8-SNAPSHOT + + 4.0.0 + + hashicorp-key-vault + + + + org.springframework.vault + spring-vault-core + + + + com.quorum.tessera + key-vault-api + + + + + \ No newline at end of file diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java new file mode 100644 index 0000000000..8c6f69ff32 --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java @@ -0,0 +1,48 @@ +package com.quorum.tessera.key.vault.hashicorp; + +import com.quorum.tessera.config.HashicorpKeyVaultConfig; +import com.quorum.tessera.key.vault.KeyVaultService; +import com.quorum.tessera.key.vault.VaultSecretNotFoundException; +import org.springframework.vault.core.VaultTemplate; +import org.springframework.vault.support.VaultResponse; + +import java.util.Map; + +public class HashicorpKeyVaultService implements KeyVaultService { + + private String vaultUrl; + private VaultTemplate vaultTemplate; + + public HashicorpKeyVaultService(HashicorpKeyVaultConfig keyVaultConfig, VaultTemplate vaultTemplate) { + this.vaultUrl = keyVaultConfig.getUrl(); + this.vaultTemplate = vaultTemplate; + } + + public String getHashicorpSecret(String secretPath, String secretName) { + VaultResponse response = vaultTemplate.read(secretPath); + + if(response == null) { + throw new VaultSecretNotFoundException("Hashicorp Vault secret not found at path " + secretPath + " in vault " + vaultUrl); + } + + if(response.getData() == null) { + throw new VaultSecretNotFoundException("Value for secret key " + secretName + " not found at path " + secretPath + " in vault " + vaultUrl); + } + + return response.getData().get(secretName).toString(); + } + + public void setHashicorpSecret(String secretPath, Map keyValuePairs) { + vaultTemplate.write(secretPath, keyValuePairs); + } + + @Override + public String getSecret(String secretName) { + return null; + } + + @Override + public Object setSecret(String secretName, String secret) { + return null; + } +} diff --git a/key-vault/pom.xml b/key-vault/pom.xml index acbd0927b9..b05d4b032a 100644 --- a/key-vault/pom.xml +++ b/key-vault/pom.xml @@ -12,5 +12,6 @@ azure-key-vault key-vault-api + hashicorp-key-vault diff --git a/pom.xml b/pom.xml index ba51605de4..5d0e0be5da 100644 --- a/pom.xml +++ b/pom.xml @@ -501,6 +501,12 @@ ${spring.version} + + org.springframework.vault + spring-vault-core + 2.0.3.RELEASE + + org.bouncycastle bcpkix-jdk15on From 770eb1468e241e85d0a295209518a4f62aa3775b Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Wed, 28 Nov 2018 08:27:08 +0000 Subject: [PATCH 02/57] Add Hashicorp config to KeyConfiguration object --- .../tessera/config/builder/KeyDataBuilder.java | 2 +- .../config/migration/TomlConfigFactoryTest.java | 2 +- .../tessera/config/migration/test/FixtureUtil.java | 4 ++-- .../quorum/tessera/config/JaxbConfigFactory.java | 2 +- .../com/quorum/tessera/config/KeyConfiguration.java | 13 ++++++++++++- .../config/adapters/KeyConfigurationAdapter.java | 2 +- .../com/quorum/tessera/config/ValidationTest.java | 12 ++++++------ .../adapters/KeyConfigurationAdapterTest.java | 10 +++++----- .../constraints/KeyConfigurationValidatorTest.java | 8 ++++---- 9 files changed, 33 insertions(+), 22 deletions(-) diff --git a/config-migration/src/main/java/com/quorum/tessera/config/builder/KeyDataBuilder.java b/config-migration/src/main/java/com/quorum/tessera/config/builder/KeyDataBuilder.java index d995f1ecb9..6b798e0f99 100644 --- a/config-migration/src/main/java/com/quorum/tessera/config/builder/KeyDataBuilder.java +++ b/config-migration/src/main/java/com/quorum/tessera/config/builder/KeyDataBuilder.java @@ -70,7 +70,7 @@ public KeyConfiguration build() { privateKeyPasswordFilePath = null; } - return new KeyConfiguration(privateKeyPasswordFilePath, null, keyData, null); + return new KeyConfiguration(privateKeyPasswordFilePath, null, keyData, null, null); } } diff --git a/config-migration/src/test/java/com/quorum/tessera/config/migration/TomlConfigFactoryTest.java b/config-migration/src/test/java/com/quorum/tessera/config/migration/TomlConfigFactoryTest.java index d5f61f2158..b223035db7 100644 --- a/config-migration/src/test/java/com/quorum/tessera/config/migration/TomlConfigFactoryTest.java +++ b/config-migration/src/test/java/com/quorum/tessera/config/migration/TomlConfigFactoryTest.java @@ -158,7 +158,7 @@ public void ifPublicAndPrivateKeyListAreEmptyThenKeyConfigurationIsAllNulls() th KeyConfiguration result = tomlConfigFactory.createKeyDataBuilder(configData).build(); assertThat(result).isNotNull(); - KeyConfiguration expected = new KeyConfiguration(null, null, Collections.emptyList(), null); + KeyConfiguration expected = new KeyConfiguration(null, null, Collections.emptyList(), null, null); assertThat(result).isEqualTo(expected); } diff --git a/config-migration/src/test/java/com/quorum/tessera/config/migration/test/FixtureUtil.java b/config-migration/src/test/java/com/quorum/tessera/config/migration/test/FixtureUtil.java index 1e97c85138..5ce315d9ea 100644 --- a/config-migration/src/test/java/com/quorum/tessera/config/migration/test/FixtureUtil.java +++ b/config-migration/src/test/java/com/quorum/tessera/config/migration/test/FixtureUtil.java @@ -61,7 +61,7 @@ public static ConfigBuilder builderWithValidValues() { .sslClientTlsCertificatePath("sslClientTlsCertificatePath") .sslServerTlsCertificatePath("sslServerTlsCertificatePath") .keyData(new KeyConfiguration(null, Collections.emptyList(), - Collections.singletonList(new FilesystemKeyPair(Paths.get("public"), Paths.get("private"))), null)); + Collections.singletonList(new FilesystemKeyPair(Paths.get("public"), Paths.get("private"))), null, null)); } public static ConfigBuilder builderWithNullValues() { @@ -91,7 +91,7 @@ public static ConfigBuilder builderWithNullValues() { .sslClientTlsCertificatePath("sslClientTlsCertificatePath") .sslServerTlsCertificatePath("sslServerTlsCertificatePath") .keyData(new KeyConfiguration(null, Collections.emptyList(), - Collections.singletonList(new FilesystemKeyPair(Paths.get("public"), Paths.get("private"))), null)); + Collections.singletonList(new FilesystemKeyPair(Paths.get("public"), Paths.get("private"))), null, null)); } public static JsonObject createUnlockedPrivateKey() { diff --git a/config/src/main/java/com/quorum/tessera/config/JaxbConfigFactory.java b/config/src/main/java/com/quorum/tessera/config/JaxbConfigFactory.java index ebf265133a..862a193cf2 100644 --- a/config/src/main/java/com/quorum/tessera/config/JaxbConfigFactory.java +++ b/config/src/main/java/com/quorum/tessera/config/JaxbConfigFactory.java @@ -69,7 +69,7 @@ public Config create(final InputStream configData, final List new config.getJdbcConfig(), config.getServerConfigs(), config.getPeers(), - new KeyConfiguration(Paths.get("passwords.txt"), null, config.getKeys().getKeyData(), config.getKeys().getAzureKeyVaultConfig()), + new KeyConfiguration(Paths.get("passwords.txt"), null, config.getKeys().getKeyData(), config.getKeys().getAzureKeyVaultConfig(), config.getKeys().getHashicorpKeyVaultConfig()), config.getAlwaysSendTo(), config.getUnixSocketFile(), config.isUseWhiteList(), diff --git a/config/src/main/java/com/quorum/tessera/config/KeyConfiguration.java b/config/src/main/java/com/quorum/tessera/config/KeyConfiguration.java index 59d9a08e33..7fe9fe372b 100644 --- a/config/src/main/java/com/quorum/tessera/config/KeyConfiguration.java +++ b/config/src/main/java/com/quorum/tessera/config/KeyConfiguration.java @@ -35,11 +35,15 @@ public class KeyConfiguration extends ConfigItem { @XmlElement private AzureKeyVaultConfig azureKeyVaultConfig; - public KeyConfiguration(final Path passwordFile, final List passwords, final List keyData, final AzureKeyVaultConfig azureKeyVaultConfig) { + @Valid + @XmlElement HashicorpKeyVaultConfig hashicorpKeyVaultConfig; + + public KeyConfiguration(final Path passwordFile, final List passwords, final List keyData, final AzureKeyVaultConfig azureKeyVaultConfig, final HashicorpKeyVaultConfig hashicorpKeyVaultConfig) { this.passwordFile = passwordFile; this.passwords = passwords; this.keyData = keyData; this.azureKeyVaultConfig = azureKeyVaultConfig; + this.hashicorpKeyVaultConfig = hashicorpKeyVaultConfig; } public KeyConfiguration() { @@ -61,6 +65,10 @@ public AzureKeyVaultConfig getAzureKeyVaultConfig() { return this.azureKeyVaultConfig; } + public HashicorpKeyVaultConfig getHashicorpKeyVaultConfig() { + return hashicorpKeyVaultConfig; + } + public void setPasswordFile(Path passwordFile) { this.passwordFile = passwordFile; } @@ -77,5 +85,8 @@ public void setAzureKeyVaultConfig(AzureKeyVaultConfig azureKeyVaultConfig) { this.azureKeyVaultConfig = azureKeyVaultConfig; } + public void setHashicorpKeyVaultConfig(HashicorpKeyVaultConfig hashicorpKeyVaultConfig) { + this.hashicorpKeyVaultConfig = hashicorpKeyVaultConfig; + } } diff --git a/config/src/main/java/com/quorum/tessera/config/adapters/KeyConfigurationAdapter.java b/config/src/main/java/com/quorum/tessera/config/adapters/KeyConfigurationAdapter.java index 36c9a71178..dfff62c5cd 100644 --- a/config/src/main/java/com/quorum/tessera/config/adapters/KeyConfigurationAdapter.java +++ b/config/src/main/java/com/quorum/tessera/config/adapters/KeyConfigurationAdapter.java @@ -47,7 +47,7 @@ public KeyConfiguration unmarshal(final KeyConfiguration input) { }).collect(Collectors.toList()); } - return new KeyConfiguration(input.getPasswordFile(), input.getPasswords(), keyDataWithPasswords, input.getAzureKeyVaultConfig()); + return new KeyConfiguration(input.getPasswordFile(), input.getPasswords(), keyDataWithPasswords, input.getAzureKeyVaultConfig(), input.getHashicorpKeyVaultConfig()); } @Override diff --git a/config/src/test/java/com/quorum/tessera/config/ValidationTest.java b/config/src/test/java/com/quorum/tessera/config/ValidationTest.java index 936d88238b..d06ccab27d 100644 --- a/config/src/test/java/com/quorum/tessera/config/ValidationTest.java +++ b/config/src/test/java/com/quorum/tessera/config/ValidationTest.java @@ -107,7 +107,7 @@ public void inlineKeyPairNoPasswordProvided() { InlineKeypair spy = Mockito.spy(new InlineKeypair("validkey", keyConfig)); doReturn("validkey").when(spy).getPrivateKey(); - KeyConfiguration keyConfiguration = new KeyConfiguration(null, null, singletonList(spy), null); + KeyConfiguration keyConfiguration = new KeyConfiguration(null, null, singletonList(spy), null, null); Set> violations = validator.validate(keyConfiguration); @@ -208,7 +208,7 @@ public void keypairPathsValidation() { final ConfigKeyPair keyPair = new FilesystemKeyPair(publicKeyPath, privateKeyPath); - final KeyConfiguration keyConfiguration = new KeyConfiguration(null, null, singletonList(keyPair), null); + final KeyConfiguration keyConfiguration = new KeyConfiguration(null, null, singletonList(keyPair), null, null); final Set> violations = validator.validate(keyConfiguration); assertThat(violations).hasSize(2); @@ -232,7 +232,7 @@ public void keypairInlineValidation() { final ConfigKeyPair keyPair = new DirectKeyPair("notvalidbase64", "c=="); - KeyConfiguration keyConfiguration = new KeyConfiguration(null, null, singletonList(keyPair), null); + KeyConfiguration keyConfiguration = new KeyConfiguration(null, null, singletonList(keyPair), null, null); Set> violations = validator.validate(keyConfiguration); assertThat(violations).hasSize(1); @@ -268,7 +268,7 @@ public void azureKeyPairIdsDisallowedCharactersCreateViolation() { @Test public void azureKeyPairProvidedWithoutKeyVaultConfigCreatesViolation() { AzureVaultKeyPair keyPair = new AzureVaultKeyPair("publicVauldId", "privateVaultId"); - KeyConfiguration keyConfiguration = new KeyConfiguration(null, null, singletonList(keyPair), null); + KeyConfiguration keyConfiguration = new KeyConfiguration(null, null, singletonList(keyPair), null, null); Config config = new Config(null, null, null, keyConfiguration, null, null, false, false); Set> violations = validator.validateProperty(config, "keys"); @@ -282,7 +282,7 @@ public void azureKeyPairProvidedWithoutKeyVaultConfigCreatesViolation() { public void nonKeyVaultPairProvidedWithoutKeyVaultConfigDoesNotCreateViolation() { DirectKeyPair keyPair = new DirectKeyPair("pub", "priv"); - KeyConfiguration keyConfiguration = new KeyConfiguration(null, null, singletonList(keyPair), null); + KeyConfiguration keyConfiguration = new KeyConfiguration(null, null, singletonList(keyPair), null, null); Config config = new Config(null, null, null, keyConfiguration, null, null, false, false); Set> violations = validator.validateProperty(config, "keys"); @@ -317,7 +317,7 @@ public void azureVaultConfigWithNoUrlCreatesNullViolation() { public void azureVaultKeyPairProvidedButKeyVaultConfigHasNullUrlCreatesNullViolation() { AzureVaultKeyPair keyPair = new AzureVaultKeyPair("pubId", "privId"); AzureKeyVaultConfig keyVaultConfig = new AzureKeyVaultConfig(null); - KeyConfiguration keyConfiguration = new KeyConfiguration(null, null, singletonList(keyPair), keyVaultConfig); + KeyConfiguration keyConfiguration = new KeyConfiguration(null, null, singletonList(keyPair), keyVaultConfig, null); Set> violations = validator.validate(keyConfiguration); assertThat(violations).hasSize(1); diff --git a/config/src/test/java/com/quorum/tessera/config/adapters/KeyConfigurationAdapterTest.java b/config/src/test/java/com/quorum/tessera/config/adapters/KeyConfigurationAdapterTest.java index 53b7a4ae55..c978f65e70 100644 --- a/config/src/test/java/com/quorum/tessera/config/adapters/KeyConfigurationAdapterTest.java +++ b/config/src/test/java/com/quorum/tessera/config/adapters/KeyConfigurationAdapterTest.java @@ -20,7 +20,7 @@ public class KeyConfigurationAdapterTest { @Test public void marshallingDoesNothing() { - final KeyConfiguration keyConfiguration = new KeyConfiguration(null, null, emptyList(), null); + final KeyConfiguration keyConfiguration = new KeyConfiguration(null, null, emptyList(), null, null); final KeyConfiguration marshalled = this.keyConfigurationAdapter.marshal(keyConfiguration); @@ -33,7 +33,7 @@ public void emptyPasswordsReturnsSameKeys() { //null paths since we won't actually be reading them final ConfigKeyPair keypair = new FilesystemKeyPair(null, null); - final KeyConfiguration keyConfiguration = new KeyConfiguration(null, emptyList(), singletonList(keypair), null); + final KeyConfiguration keyConfiguration = new KeyConfiguration(null, emptyList(), singletonList(keypair), null, null); final KeyConfiguration configuration = this.keyConfigurationAdapter.unmarshal(keyConfiguration); @@ -50,7 +50,7 @@ public void noPasswordsReturnsSameKeys() { //null paths since we won't actually be reading them final ConfigKeyPair keypair = new FilesystemKeyPair(null, null); - final KeyConfiguration keyConfiguration = new KeyConfiguration(null, null, singletonList(keypair), null); + final KeyConfiguration keyConfiguration = new KeyConfiguration(null, null, singletonList(keypair), null, null); final KeyConfiguration configuration = this.keyConfigurationAdapter.unmarshal(keyConfiguration); @@ -68,7 +68,7 @@ public void passwordsAssignedToKeys() { //null paths since we won't actually be reading them final ConfigKeyPair keypair = new FilesystemKeyPair(null, null); final KeyConfiguration keyConfiguration - = new KeyConfiguration(null, singletonList("passwordsAssignedToKeys"), singletonList(keypair), null); + = new KeyConfiguration(null, singletonList("passwordsAssignedToKeys"), singletonList(keypair), null, null); final KeyConfiguration configuration = this.keyConfigurationAdapter.unmarshal(keyConfiguration); @@ -83,7 +83,7 @@ public void unreadablePasswordFileGivesNoPasswords() throws IOException { final Path passes = Files.createTempDirectory("testdirectory").resolve("nonexistantfile.txt"); final ConfigKeyPair keypair = new FilesystemKeyPair(null, null); - final KeyConfiguration keyConfiguration = new KeyConfiguration(passes, null, singletonList(keypair), null); + final KeyConfiguration keyConfiguration = new KeyConfiguration(passes, null, singletonList(keypair), null, null); final KeyConfiguration configuration = this.keyConfigurationAdapter.unmarshal(keyConfiguration); diff --git a/config/src/test/java/com/quorum/tessera/config/constraints/KeyConfigurationValidatorTest.java b/config/src/test/java/com/quorum/tessera/config/constraints/KeyConfigurationValidatorTest.java index 893e19de16..bc0919da80 100644 --- a/config/src/test/java/com/quorum/tessera/config/constraints/KeyConfigurationValidatorTest.java +++ b/config/src/test/java/com/quorum/tessera/config/constraints/KeyConfigurationValidatorTest.java @@ -23,7 +23,7 @@ public void init() { @Test public void bothNotSetIsValid() { - final KeyConfiguration configuration = new KeyConfiguration(null, null, null, null); + final KeyConfiguration configuration = new KeyConfiguration(null, null, null, null, null); assertThat(validator.isValid(configuration, mock(ConstraintValidatorContext.class))).isTrue(); @@ -32,7 +32,7 @@ public void bothNotSetIsValid() { @Test public void fileSetIsValid() { - final KeyConfiguration configuration = new KeyConfiguration(Paths.get("anything"), null, null, null); + final KeyConfiguration configuration = new KeyConfiguration(Paths.get("anything"), null, null, null, null); assertThat(validator.isValid(configuration, mock(ConstraintValidatorContext.class))).isTrue(); @@ -41,7 +41,7 @@ public void fileSetIsValid() { @Test public void inlineSetIsValid() { - final KeyConfiguration configuration = new KeyConfiguration(null, emptyList(), null, null); + final KeyConfiguration configuration = new KeyConfiguration(null, emptyList(), null, null, null); assertThat(validator.isValid(configuration, mock(ConstraintValidatorContext.class))).isTrue(); @@ -50,7 +50,7 @@ public void inlineSetIsValid() { @Test public void bothSetIsInvalid() { - final KeyConfiguration configuration = new KeyConfiguration(Paths.get("anything"), emptyList(), null, null); + final KeyConfiguration configuration = new KeyConfiguration(Paths.get("anything"), emptyList(), null, null, null); assertThat(validator.isValid(configuration, mock(ConstraintValidatorContext.class))).isFalse(); From 44089b29cb5fa0fd62331204f1309b628600f705 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Wed, 28 Nov 2018 08:37:34 +0000 Subject: [PATCH 03/57] Add factory to create hashicorp service using the token auth method --- .../azure/AzureKeyVaultServiceFactory.java | 2 +- .../hashicorp/HashicorpKeyVaultService.java | 2 + .../HashicorpKeyVaultServiceFactory.java | 58 +++++++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java diff --git a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactory.java b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactory.java index c5ceb14d1d..a080ba37e5 100644 --- a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactory.java +++ b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactory.java @@ -28,7 +28,7 @@ public KeyVaultService create(Config config, EnvironmentVariableProvider envProv AzureKeyVaultConfig keyVaultConfig = Optional.ofNullable(config.getKeys()) .map(KeyConfiguration::getAzureKeyVaultConfig) - .orElseThrow(() -> new ConfigException(new RuntimeException("Trying to create Azure key vault but no Azure configuration provided in the configfile"))); + .orElseThrow(() -> new ConfigException(new RuntimeException("Trying to create Azure key vault connection but no Azure configuration provided in the configfile"))); return new AzureKeyVaultService( keyVaultConfig, diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java index 8c6f69ff32..3596c82f6e 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java @@ -10,6 +10,8 @@ public class HashicorpKeyVaultService implements KeyVaultService { + //TODO Make use of methods from KeyVaultService + private String vaultUrl; private VaultTemplate vaultTemplate; diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java new file mode 100644 index 0000000000..8f969980d7 --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java @@ -0,0 +1,58 @@ +package com.quorum.tessera.key.vault.hashicorp; + +import com.quorum.tessera.config.*; +import com.quorum.tessera.config.util.EnvironmentVariableProvider; +import com.quorum.tessera.key.vault.KeyVaultService; +import com.quorum.tessera.key.vault.KeyVaultServiceFactory; +import org.springframework.vault.authentication.ClientAuthentication; +import org.springframework.vault.authentication.TokenAuthentication; +import org.springframework.vault.client.VaultEndpoint; +import org.springframework.vault.core.VaultTemplate; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Objects; +import java.util.Optional; + +public class HashicorpKeyVaultServiceFactory implements KeyVaultServiceFactory { + + private final String authTokenEnvVar = "HASHICORP_TOKEN"; + + @Override + public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider) { + Objects.requireNonNull(config); + Objects.requireNonNull(envProvider); + + String authToken = envProvider.getEnv(authTokenEnvVar); + + if(authToken == null) { + throw new RuntimeException(authTokenEnvVar + " must be set"); + } + + HashicorpKeyVaultConfig keyVaultConfig = Optional.ofNullable(config.getKeys()) + .map(KeyConfiguration::getHashicorpKeyVaultConfig) + .orElseThrow(() -> new ConfigException(new RuntimeException("Trying to create Hashicorp Vault connection but no Vault configuration provided in the configfile"))); + + VaultEndpoint vaultEndpoint; + try { + vaultEndpoint = VaultEndpoint.from(new URI(keyVaultConfig.getUrl())); + } catch (URISyntaxException e) { + throw new ConfigException(new Throwable("Provided Hashicorp Vault url is incorrectly formatted", e)); + } + + ClientAuthentication clientAuthentication = new TokenAuthentication(authToken); + + return new HashicorpKeyVaultService( + keyVaultConfig, + new VaultTemplate( + vaultEndpoint, + clientAuthentication + ) + ); + } + + @Override + public KeyVaultType getType() { + return KeyVaultType.HASHICORP; + } +} From 8e558f12ddad0acdf1f0f0a311896964a72a2471 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Wed, 28 Nov 2018 09:02:58 +0000 Subject: [PATCH 04/57] Read Hashicorp key data from config and marshall to new key pair type --- .../com/quorum/tessera/config/KeyData.java | 28 ++++++++- .../config/adapters/KeyDataAdapter.java | 31 +++++++--- .../keypairs/HashicorpVaultKeyPair.java | 60 +++++++++++++++++++ .../config/keypairs/UnsupportedKeyPair.java | 26 +++++++- .../quorum/tessera/config/ValidationTest.java | 6 +- .../config/adapters/KeyDataAdapterTest.java | 32 +++++----- .../UnsupportedKeyPairValidatorTest.java | 26 ++++---- .../keypairs/UnsupportedKeyPairTest.java | 2 +- .../key/generation/FileKeyGenerator.java | 6 ++ 9 files changed, 173 insertions(+), 44 deletions(-) create mode 100644 config/src/main/java/com/quorum/tessera/config/keypairs/HashicorpVaultKeyPair.java diff --git a/config/src/main/java/com/quorum/tessera/config/KeyData.java b/config/src/main/java/com/quorum/tessera/config/KeyData.java index f0d5516827..dfac91f1b5 100644 --- a/config/src/main/java/com/quorum/tessera/config/KeyData.java +++ b/config/src/main/java/com/quorum/tessera/config/KeyData.java @@ -43,13 +43,25 @@ public class KeyData extends ConfigItem { @Pattern(regexp = "^[0-9a-zA-Z\\-]*$") private String azureVaultPrivateKeyId; + @XmlElement + private String hashicorpVaultPublicKeyId; + + @XmlElement + private String hashicorpVaultPrivateKeyId; + + @XmlElement + private String hashicorpVaultSecretPath; + public KeyData(final KeyDataConfig keyDataConfig, final String privateKey, final String publicKey, final Path privKeyPath, final Path pubKeyPath, final String azureVaultPrivateKeyId, - final String azureVaultPublicKeyId) { + final String azureVaultPublicKeyId, + final String hashicorpVaultPrivateKeyId, + final String hashicorpVaultPublicKeyId, + final String hashicorpVaultSecretPath) { this.privateKey = privateKey; this.publicKey = publicKey; this.config = keyDataConfig; @@ -57,6 +69,9 @@ public KeyData(final KeyDataConfig keyDataConfig, this.publicKeyPath = pubKeyPath; this.azureVaultPublicKeyId = azureVaultPublicKeyId; this.azureVaultPrivateKeyId = azureVaultPrivateKeyId; + this.hashicorpVaultPublicKeyId = hashicorpVaultPublicKeyId; + this.hashicorpVaultPrivateKeyId = hashicorpVaultPrivateKeyId; + this.hashicorpVaultSecretPath = hashicorpVaultSecretPath; } public KeyData() { @@ -91,4 +106,15 @@ public String getAzureVaultPrivateKeyId() { return azureVaultPrivateKeyId; } + public String getHashicorpVaultPublicKeyId() { + return hashicorpVaultPublicKeyId; + } + + public String getHashicorpVaultPrivateKeyId() { + return hashicorpVaultPrivateKeyId; + } + + public String getHashicorpVaultSecretPath() { + return hashicorpVaultSecretPath; + } } diff --git a/config/src/main/java/com/quorum/tessera/config/adapters/KeyDataAdapter.java b/config/src/main/java/com/quorum/tessera/config/adapters/KeyDataAdapter.java index 5f2f82f7fd..3304616f75 100644 --- a/config/src/main/java/com/quorum/tessera/config/adapters/KeyDataAdapter.java +++ b/config/src/main/java/com/quorum/tessera/config/adapters/KeyDataAdapter.java @@ -23,17 +23,22 @@ public ConfigKeyPair unmarshal(final KeyData keyData) { return new InlineKeypair(keyData.getPublicKey(), keyData.getConfig()); } - //case 3, the key vault ids are provided + //case 3, the Azure Key Vault data is provided if(keyData.getAzureVaultPublicKeyId() != null && keyData.getAzureVaultPrivateKeyId() != null) { return new AzureVaultKeyPair(keyData.getAzureVaultPublicKeyId(), keyData.getAzureVaultPrivateKeyId()); } - //case 4, the keys are provided inside a file + //case 4, the Hashicorp Vault data is provided + if(keyData.getHashicorpVaultPublicKeyId() != null && keyData.getHashicorpVaultPrivateKeyId() != null && keyData.getHashicorpVaultSecretPath() != null) { + return new HashicorpVaultKeyPair(keyData.getHashicorpVaultPublicKeyId(), keyData.getHashicorpVaultPrivateKeyId(), keyData.getHashicorpVaultSecretPath()); + } + + //case 5, the keys are provided inside a file if(keyData.getPublicKeyPath() != null && keyData.getPrivateKeyPath() != null) { return new FilesystemKeyPair(keyData.getPublicKeyPath(), keyData.getPrivateKeyPath()); } - //case 5, the key config specified is invalid + //case 6, the key config specified is invalid return new UnsupportedKeyPair( keyData.getConfig(), keyData.getPrivateKey(), @@ -41,7 +46,10 @@ public ConfigKeyPair unmarshal(final KeyData keyData) { keyData.getPrivateKeyPath(), keyData.getPublicKeyPath(), keyData.getAzureVaultPublicKeyId(), - keyData.getAzureVaultPrivateKeyId() + keyData.getAzureVaultPrivateKeyId(), + keyData.getHashicorpVaultPublicKeyId(), + keyData.getHashicorpVaultPrivateKeyId(), + keyData.getHashicorpVaultSecretPath() ); } @@ -50,27 +58,32 @@ public KeyData marshal(final ConfigKeyPair keyData) { if(keyData instanceof DirectKeyPair) { DirectKeyPair kp = (DirectKeyPair) keyData; - return new KeyData(null, kp.getPrivateKey(), kp.getPublicKey(), null, null, null, null); + return new KeyData(null, kp.getPrivateKey(), kp.getPublicKey(), null, null, null, null, null, null, null); } if(keyData instanceof InlineKeypair) { InlineKeypair kp = (InlineKeypair) keyData; - return new KeyData(kp.getPrivateKeyConfig(), null, kp.getPublicKey(), null, null, null, null); + return new KeyData(kp.getPrivateKeyConfig(), null, kp.getPublicKey(), null, null, null, null, null, null, null); } if(keyData instanceof AzureVaultKeyPair) { AzureVaultKeyPair kp = (AzureVaultKeyPair) keyData; - return new KeyData(null, null, null, null, null, kp.getPrivateKeyId(), kp.getPublicKeyId()); + return new KeyData(null, null, null, null, null, kp.getPrivateKeyId(), kp.getPublicKeyId(), null, null, null); + } + + if(keyData instanceof HashicorpVaultKeyPair) { + HashicorpVaultKeyPair kp = (HashicorpVaultKeyPair) keyData; + return new KeyData(null, null, null, null, null, null, null, kp.getPrivateKeyId(), kp.getPublicKeyId(), kp.getSecretPath()); } if(keyData instanceof FilesystemKeyPair) { FilesystemKeyPair kp = (FilesystemKeyPair) keyData; - return new KeyData(null, null, null, kp.getPrivateKeyPath(), kp.getPublicKeyPath(), null, null); + return new KeyData(null, null, null, kp.getPrivateKeyPath(), kp.getPublicKeyPath(), null, null, null, null, null); } if(keyData instanceof UnsupportedKeyPair) { UnsupportedKeyPair kp = (UnsupportedKeyPair) keyData; - return new KeyData(kp.getConfig(), kp.getPrivateKey(), kp.getPublicKey(), kp.getPrivateKeyPath(), kp.getPublicKeyPath(), kp.getAzureVaultPrivateKeyId(), kp.getAzureVaultPublicKeyId()); + return new KeyData(kp.getConfig(), kp.getPrivateKey(), kp.getPublicKey(), kp.getPrivateKeyPath(), kp.getPublicKeyPath(), kp.getAzureVaultPrivateKeyId(), kp.getAzureVaultPublicKeyId(), kp.getHashicorpVaultPrivateKeyId(), kp.getHashicorpVaultPublicKeyId(), kp.getHashicorpVaultSecretPath()); } throw new UnsupportedOperationException("The keypair type " + keyData.getClass() + " is not allowed"); diff --git a/config/src/main/java/com/quorum/tessera/config/keypairs/HashicorpVaultKeyPair.java b/config/src/main/java/com/quorum/tessera/config/keypairs/HashicorpVaultKeyPair.java new file mode 100644 index 0000000000..a352b72acd --- /dev/null +++ b/config/src/main/java/com/quorum/tessera/config/keypairs/HashicorpVaultKeyPair.java @@ -0,0 +1,60 @@ +package com.quorum.tessera.config.keypairs; + +import javax.validation.constraints.NotNull; +import javax.xml.bind.annotation.XmlElement; + +public class HashicorpVaultKeyPair implements ConfigKeyPair { + + @NotNull + @XmlElement + private String publicKeyId; + + @NotNull + @XmlElement + private String privateKeyId; + + @NotNull + @XmlElement + private String secretPath; + + public HashicorpVaultKeyPair(String publicKeyId, String privateKeyId, String secretPath) { + this.publicKeyId = publicKeyId; + this.privateKeyId = privateKeyId; + this.secretPath = secretPath; + } + + public String getPublicKeyId() { + return publicKeyId; + } + + public String getPrivateKeyId() { + return privateKeyId; + } + + public String getSecretPath() { + return secretPath; + } + + @Override + public String getPublicKey() { + //keys are not fetched from vault yet so return null + return null; + } + + @Override + public String getPrivateKey() { + //keys are not fetched from vault yet so return null + return null; + } + + @Override + public void withPassword(String password) { + //password not used with vault stored keys + } + + @Override + public String getPassword() { + //no password to return + return ""; + } +} diff --git a/config/src/main/java/com/quorum/tessera/config/keypairs/UnsupportedKeyPair.java b/config/src/main/java/com/quorum/tessera/config/keypairs/UnsupportedKeyPair.java index dd84b4a168..40094955f1 100644 --- a/config/src/main/java/com/quorum/tessera/config/keypairs/UnsupportedKeyPair.java +++ b/config/src/main/java/com/quorum/tessera/config/keypairs/UnsupportedKeyPair.java @@ -34,7 +34,16 @@ public class UnsupportedKeyPair implements ConfigKeyPair { @XmlElement private final String azureVaultPrivateKeyId; - public UnsupportedKeyPair(KeyDataConfig config, String privateKey, String publicKey, Path privateKeyPath, Path publicKeyPath, String azureVaultPublicKeyId, String azureVaultPrivateKeyId) { + @XmlElement + private String hashicorpVaultPublicKeyId; + + @XmlElement + private String hashicorpVaultPrivateKeyId; + + @XmlElement + private String hashicorpVaultSecretPath; + + public UnsupportedKeyPair(KeyDataConfig config, String privateKey, String publicKey, Path privateKeyPath, Path publicKeyPath, String azureVaultPublicKeyId, String azureVaultPrivateKeyId, String hashicorpVaultPublicKeyId, String hashicorpVaultPrivateKeyId, String hashicorpVaultSecretPath) { this.config = config; this.privateKey = privateKey; this.publicKey = publicKey; @@ -42,6 +51,9 @@ public UnsupportedKeyPair(KeyDataConfig config, String privateKey, String public this.publicKeyPath = publicKeyPath; this.azureVaultPublicKeyId = azureVaultPublicKeyId; this.azureVaultPrivateKeyId = azureVaultPrivateKeyId; + this.hashicorpVaultPublicKeyId = hashicorpVaultPublicKeyId; + this.hashicorpVaultPrivateKeyId = hashicorpVaultPrivateKeyId; + this.hashicorpVaultSecretPath = hashicorpVaultSecretPath; } @Override @@ -74,6 +86,18 @@ public String getAzureVaultPrivateKeyId() { return azureVaultPrivateKeyId; } + public String getHashicorpVaultPublicKeyId() { + return hashicorpVaultPublicKeyId; + } + + public String getHashicorpVaultPrivateKeyId() { + return hashicorpVaultPrivateKeyId; + } + + public String getHashicorpVaultSecretPath() { + return hashicorpVaultSecretPath; + } + @Override public void withPassword(String password) { //do nothing as password not used with this keypair type diff --git a/config/src/test/java/com/quorum/tessera/config/ValidationTest.java b/config/src/test/java/com/quorum/tessera/config/ValidationTest.java index d06ccab27d..e2d984b463 100644 --- a/config/src/test/java/com/quorum/tessera/config/ValidationTest.java +++ b/config/src/test/java/com/quorum/tessera/config/ValidationTest.java @@ -60,7 +60,7 @@ public void validateArgonOptionsAllNullAlgoHasDefaultValue() { public void keyDataConfigMissingPassword() { PrivateKeyData privateKeyData = new PrivateKeyData(null, "snonce", "asalt", "sbox", mock(ArgonOptions.class), null); KeyDataConfig keyDataConfig = new KeyDataConfig(privateKeyData, PrivateKeyType.LOCKED); - KeyData keyData = new KeyData(keyDataConfig, "privateKey", "publicKey", null, null, null, null); + KeyData keyData = new KeyData(keyDataConfig, "privateKey", "publicKey", null, null, null, null, null, null, null); Set> violations = validator.validate(keyData); assertThat(violations).hasSize(1); @@ -74,7 +74,7 @@ public void keyDataConfigMissingPassword() { public void keyDataConfigNaclFailure() { PrivateKeyData privateKeyData = new PrivateKeyData(null, "snonce", "asalt", "sbox", mock(ArgonOptions.class), "SECRET"); KeyDataConfig keyDataConfig = new KeyDataConfig(privateKeyData, PrivateKeyType.LOCKED); - KeyData keyData = new KeyData(keyDataConfig, "NACL_FAILURE", "publicKey", null, null, null, null); + KeyData keyData = new KeyData(keyDataConfig, "NACL_FAILURE", "publicKey", null, null, null, null, null, null, null); Set> violations = validator.validate(keyData); assertThat(violations).hasSize(1); @@ -88,7 +88,7 @@ public void keyDataConfigNaclFailure() { public void keyDataConfigInvalidBase64() { PrivateKeyData privateKeyData = new PrivateKeyData(null, "snonce", "asalt", "sbox", mock(ArgonOptions.class), "SECRET"); KeyDataConfig keyDataConfig = new KeyDataConfig(privateKeyData, PrivateKeyType.LOCKED); - KeyData keyData = new KeyData(keyDataConfig, "INAVLID_BASE", "publicKey", null, null, null, null); + KeyData keyData = new KeyData(keyDataConfig, "INAVLID_BASE", "publicKey", null, null, null, null, null, null, null); Set> violations = validator.validate(keyData); assertThat(violations).hasSize(1); diff --git a/config/src/test/java/com/quorum/tessera/config/adapters/KeyDataAdapterTest.java b/config/src/test/java/com/quorum/tessera/config/adapters/KeyDataAdapterTest.java index 4407c3d492..052fc0882b 100644 --- a/config/src/test/java/com/quorum/tessera/config/adapters/KeyDataAdapterTest.java +++ b/config/src/test/java/com/quorum/tessera/config/adapters/KeyDataAdapterTest.java @@ -21,7 +21,7 @@ public class KeyDataAdapterTest { @Test public void marshallDirectKeys() { final ConfigKeyPair keys = new DirectKeyPair("PUB", "PRIV"); - final KeyData expected = new KeyData(null, "PRIV", "PUB", null, null, null, null); + final KeyData expected = new KeyData(null, "PRIV", "PUB", null, null, null, null, null, null, null); final KeyData marshalledKey = adapter.marshal(keys); @@ -32,7 +32,7 @@ public void marshallDirectKeys() { public void marshallInlineKeys() { final PrivateKeyData pkd = new PrivateKeyData("val", null, null, null, null, null); final ConfigKeyPair keys = new InlineKeypair("PUB", new KeyDataConfig(pkd, UNLOCKED)); - final KeyData expected = new KeyData(new KeyDataConfig(pkd, UNLOCKED), null, "PUB", null, null, null, null); + final KeyData expected = new KeyData(new KeyDataConfig(pkd, UNLOCKED), null, "PUB", null, null, null, null, null, null, null); final KeyData marshalledKey = adapter.marshal(keys); @@ -44,7 +44,7 @@ public void marshallFilesystemKeys() { final Path path = mock(Path.class); final FilesystemKeyPair keyPair = new FilesystemKeyPair(path, path); - final KeyData expected = new KeyData(null, null, null, path, path, null, null); + final KeyData expected = new KeyData(null, null, null, path, path, null, null, null, null, null); final KeyData result = adapter.marshal(keyPair); assertThat(result).isEqualTo(expected); @@ -54,7 +54,7 @@ public void marshallFilesystemKeys() { public void marshallAzureKeys() { final AzureVaultKeyPair keyPair = new AzureVaultKeyPair("pubId", "privId"); - final KeyData expected = new KeyData(null, null, null, null, null, "privId", "pubId"); + final KeyData expected = new KeyData(null, null, null, null, null, "privId", "pubId", null, null, null); final KeyData result = adapter.marshal(keyPair); assertThat(result).isEqualTo(expected); @@ -64,9 +64,9 @@ public void marshallAzureKeys() { public void marshallUnsupportedKeys() { final KeyDataConfig keyDataConfig = mock(KeyDataConfig.class); final Path path = mock(Path.class); - final UnsupportedKeyPair keyPair = new UnsupportedKeyPair(keyDataConfig, "priv", null, path, null, null, null); + final UnsupportedKeyPair keyPair = new UnsupportedKeyPair(keyDataConfig, "priv", null, path, null, null, null, null, null, null); - final KeyData expected = new KeyData(keyDataConfig, "priv", null, path, null, null, null); + final KeyData expected = new KeyData(keyDataConfig, "priv", null, path, null, null, null, null, null, null); final KeyData result = adapter.marshal(keyPair); assertThat(result).isEqualTo(expected); @@ -118,7 +118,7 @@ public void marshallLockedKeyNullifiesPrivateKey() { @Test public void unmarshallingDirectKeysGivesCorrectKeypair() { - final KeyData input = new KeyData(null, "private", "public", null, null, null, null); + final KeyData input = new KeyData(null, "private", "public", null, null, null, null, null, null, null); final ConfigKeyPair result = this.adapter.unmarshal(input); assertThat(result).isInstanceOf(DirectKeyPair.class); @@ -127,7 +127,7 @@ public void unmarshallingDirectKeysGivesCorrectKeypair() { @Test public void unmarshallingInlineKeysGivesCorrectKeypair() { - final KeyData input = new KeyData(new KeyDataConfig(null, null), null, "public", null, null, null, null); + final KeyData input = new KeyData(new KeyDataConfig(null, null), null, "public", null, null, null, null, null, null, null); final ConfigKeyPair result = this.adapter.unmarshal(input); assertThat(result).isInstanceOf(InlineKeypair.class); @@ -136,7 +136,7 @@ public void unmarshallingInlineKeysGivesCorrectKeypair() { @Test public void unmarshallingFilesystemKeysGivesCorrectKeypair() { - final KeyData input = new KeyData(null, null, null, Paths.get("private"), Paths.get("public"), null, null); + final KeyData input = new KeyData(null, null, null, Paths.get("private"), Paths.get("public"), null, null, null, null, null); final ConfigKeyPair result = this.adapter.unmarshal(input); assertThat(result).isInstanceOf(FilesystemKeyPair.class); @@ -144,7 +144,7 @@ public void unmarshallingFilesystemKeysGivesCorrectKeypair() { @Test public void unmarshallingAzureKeysGivesCorrectKeyPair() { - final KeyData input = new KeyData(null, null, null, null, null, "privId", "pubId"); + final KeyData input = new KeyData(null, null, null, null, null, "privId", "pubId", null, null, null); final ConfigKeyPair result = this.adapter.unmarshal(input); assertThat(result).isInstanceOf(AzureVaultKeyPair.class); @@ -152,7 +152,7 @@ public void unmarshallingAzureKeysGivesCorrectKeyPair() { @Test public void unmarshallingPrivateOnlyGivesUnsupportedKeyPair() { - final KeyData input = new KeyData(null, "private", null, null, null, null, null); + final KeyData input = new KeyData(null, "private", null, null, null, null, null, null, null, null); final ConfigKeyPair result = this.adapter.unmarshal(input); assertThat(result).isInstanceOf(UnsupportedKeyPair.class); @@ -161,7 +161,7 @@ public void unmarshallingPrivateOnlyGivesUnsupportedKeyPair() { @Test public void unmarshallingPrivateConfigOnlyGivesUnsupportedKeyPair() { final KeyDataConfig keyDataConfig = mock(KeyDataConfig.class); - final KeyData input = new KeyData(keyDataConfig, null, null, null, null, null, null); + final KeyData input = new KeyData(keyDataConfig, null, null, null, null, null, null, null, null, null); final ConfigKeyPair result = this.adapter.unmarshal(input); assertThat(result).isInstanceOf(UnsupportedKeyPair.class); @@ -169,7 +169,7 @@ public void unmarshallingPrivateConfigOnlyGivesUnsupportedKeyPair() { @Test public void unmarshallingAzurePublicOnlyGivesUnsupportedKeyPair() { - final KeyData input = new KeyData(null, null, null, null, null, null, "pubId"); + final KeyData input = new KeyData(null, null, null, null, null, null, "pubId", null, null, null); final ConfigKeyPair result = this.adapter.unmarshal(input); assertThat(result).isInstanceOf(UnsupportedKeyPair.class); @@ -177,7 +177,7 @@ public void unmarshallingAzurePublicOnlyGivesUnsupportedKeyPair() { @Test public void unmarshallingAzurePrivateOnlyGivesUnsupportedKeyPair() { - final KeyData input = new KeyData(null, null, null, null, null, "priv", null); + final KeyData input = new KeyData(null, null, null, null, null, "priv", null, null, null, null); final ConfigKeyPair result = this.adapter.unmarshal(input); assertThat(result).isInstanceOf(UnsupportedKeyPair.class); @@ -186,7 +186,7 @@ public void unmarshallingAzurePrivateOnlyGivesUnsupportedKeyPair() { @Test public void unmarshallingPublicPathOnlyGivesUnsupportedKeyPair() { final Path path = mock(Path.class); - final KeyData input = new KeyData(null, null, null, null, path, null, null); + final KeyData input = new KeyData(null, null, null, null, path, null, null, null, null, null); final ConfigKeyPair result = this.adapter.unmarshal(input); assertThat(result).isInstanceOf(UnsupportedKeyPair.class); @@ -195,7 +195,7 @@ public void unmarshallingPublicPathOnlyGivesUnsupportedKeyPair() { @Test public void unmarshallingPrivatePathOnlyGivesUnsupportedKeyPair() { final Path path = mock(Path.class); - final KeyData input = new KeyData(null, null, null, path, null, null, null); + final KeyData input = new KeyData(null, null, null, path, null, null, null, null, null, null); final ConfigKeyPair result = this.adapter.unmarshal(input); assertThat(result).isInstanceOf(UnsupportedKeyPair.class); diff --git a/config/src/test/java/com/quorum/tessera/config/constraints/UnsupportedKeyPairValidatorTest.java b/config/src/test/java/com/quorum/tessera/config/constraints/UnsupportedKeyPairValidatorTest.java index bba8e02bc6..9a06f2b37d 100644 --- a/config/src/test/java/com/quorum/tessera/config/constraints/UnsupportedKeyPairValidatorTest.java +++ b/config/src/test/java/com/quorum/tessera/config/constraints/UnsupportedKeyPairValidatorTest.java @@ -31,7 +31,7 @@ public void setUp() { @Test public void directViolationIfPublicKeyButNoPrivateKey() { - UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, "public", null, null, null, null); + UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, "public", null, null, null, null, null, null, null); validator.isValid(keyPair, context); @@ -40,7 +40,7 @@ public void directViolationIfPublicKeyButNoPrivateKey() { @Test public void directViolationIfNoPublicKeyButPrivateKey() { - UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, "private", null, null, null, null, null); + UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, "private", null, null, null, null, null, null, null, null); validator.isValid(keyPair, context); @@ -52,7 +52,7 @@ public void directViolationIsDefaultIfNoDirectPublicEvenIfMultipleIncompleteKeyP KeyDataConfig keyDataConfig = mock(KeyDataConfig.class); Path path = mock(Path.class); - UnsupportedKeyPair keyPair = new UnsupportedKeyPair(keyDataConfig, "private", null, path, null, null, "privVault"); + UnsupportedKeyPair keyPair = new UnsupportedKeyPair(keyDataConfig, "private", null, path, null, null, "privVault", null, null, null); validator.isValid(keyPair, context); @@ -64,7 +64,7 @@ public void directViolationIsDefaultIfNoDirectPrivateEvenIfMultipleIncompleteKey KeyDataConfig keyDataConfig = mock(KeyDataConfig.class); Path path = mock(Path.class); - UnsupportedKeyPair keyPair = new UnsupportedKeyPair(keyDataConfig, null, "public", null, path, "pubVault", null); + UnsupportedKeyPair keyPair = new UnsupportedKeyPair(keyDataConfig, null, "public", null, path, "pubVault", null, null, null, null); validator.isValid(keyPair, context); @@ -74,7 +74,7 @@ public void directViolationIsDefaultIfNoDirectPrivateEvenIfMultipleIncompleteKey @Test public void inlineViolationIfPrivateKeyConfigButNoPublicKey() { KeyDataConfig keyDataConfig = mock(KeyDataConfig.class); - UnsupportedKeyPair keyPair = new UnsupportedKeyPair(keyDataConfig, null, null, null, null, null, null); + UnsupportedKeyPair keyPair = new UnsupportedKeyPair(keyDataConfig, null, null, null, null, null, null, null, null, null); validator.isValid(keyPair, context); @@ -86,7 +86,7 @@ public void inlineViolationIfNoPublicEvenIfVaultAndFilesystemAreIncomplete() { KeyDataConfig keyDataConfig = mock(KeyDataConfig.class); Path path = mock(Path.class); - UnsupportedKeyPair keyPair = new UnsupportedKeyPair(keyDataConfig, null, null, null, path, "pubId", null); + UnsupportedKeyPair keyPair = new UnsupportedKeyPair(keyDataConfig, null, null, null, path, "pubId", null, null, null, null); validator.isValid(keyPair, context); @@ -95,7 +95,7 @@ public void inlineViolationIfNoPublicEvenIfVaultAndFilesystemAreIncomplete() { @Test public void azureViolationIfPublicIdButNoPrivateId() { - UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, null, null, null, "pubId", null); + UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, null, null, null, "pubId", null, null, null, null); validator.isValid(keyPair, context); @@ -104,7 +104,7 @@ public void azureViolationIfPublicIdButNoPrivateId() { @Test public void azureViolationIfNoPublicIdButPrivateId() { - UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, null, null, null, null, "privId"); + UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, null, null, null, null, "privId", null, null, null); validator.isValid(keyPair, context); @@ -115,7 +115,7 @@ public void azureViolationIfNoPublicIdButPrivateId() { public void azureViolationIfNoPublicIdEvenIfFilesystemIncomplete() { Path path = mock(Path.class); - UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, null, null, path, null, "privId"); + UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, null, null, path, null, "privId", null, null, null); validator.isValid(keyPair, context); @@ -126,7 +126,7 @@ public void azureViolationIfNoPublicIdEvenIfFilesystemIncomplete() { public void azureViolationIfNoPrivateIdEvenIfFilesystemIncomplete() { Path path = mock(Path.class); - UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, null, null, path, "pubId", null); + UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, null, null, path, "pubId", null, null, null, null); validator.isValid(keyPair, context); @@ -137,7 +137,7 @@ public void azureViolationIfNoPrivateIdEvenIfFilesystemIncomplete() { public void filesystemViolationIfPublicPathButNoPrivatePath() { Path path = mock(Path.class); - UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, null, null, path, null, null); + UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, null, null, path, null, null, null, null, null); validator.isValid(keyPair, context); @@ -148,7 +148,7 @@ public void filesystemViolationIfPublicPathButNoPrivatePath() { public void filesystemViolationIfNoPublicPathButPrivatePath() { Path path = mock(Path.class); - UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, null, path, null, null, null); + UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, null, path, null, null, null, null, null, null); validator.isValid(keyPair, context); @@ -157,7 +157,7 @@ public void filesystemViolationIfNoPublicPathButPrivatePath() { @Test public void defaultViolationIfNoRecognisedKeyPairDataProvided() { - UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, null, null, null, null, null); + UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, null, null, null, null, null, null, null, null); validator.isValid(keyPair, context); diff --git a/config/src/test/java/com/quorum/tessera/config/keypairs/UnsupportedKeyPairTest.java b/config/src/test/java/com/quorum/tessera/config/keypairs/UnsupportedKeyPairTest.java index 74348c3eb7..66b22c8ef5 100644 --- a/config/src/test/java/com/quorum/tessera/config/keypairs/UnsupportedKeyPairTest.java +++ b/config/src/test/java/com/quorum/tessera/config/keypairs/UnsupportedKeyPairTest.java @@ -11,7 +11,7 @@ public class UnsupportedKeyPairTest { @Before public void setUp() { - this.keyPair = new UnsupportedKeyPair(null, null, null, null, null, null, null); + this.keyPair = new UnsupportedKeyPair(null, null, null, null, null, null, null, null, null, null); } @Test diff --git a/key-generation/src/main/java/com/quorum/tessera/key/generation/FileKeyGenerator.java b/key-generation/src/main/java/com/quorum/tessera/key/generation/FileKeyGenerator.java index 2d9ba722d4..78d2df9ff8 100644 --- a/key-generation/src/main/java/com/quorum/tessera/key/generation/FileKeyGenerator.java +++ b/key-generation/src/main/java/com/quorum/tessera/key/generation/FileKeyGenerator.java @@ -72,6 +72,9 @@ public FilesystemKeyPair generate(final String filename, final ArgonOptions encr null, null, null, + null, + null, + null, null ); @@ -91,6 +94,9 @@ public FilesystemKeyPair generate(final String filename, final ArgonOptions encr null, null, null, + null, + null, + null, null ); From f82ba267d672c0f58dad42d42ded78f4ea5108ed Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Wed, 28 Nov 2018 10:02:35 +0000 Subject: [PATCH 05/57] Add tests for Hashicorp Service and ServiceFactory --- .../azure/AzureKeyVaultServiceFactory.java | 2 +- .../AzureKeyVaultServiceFactoryTest.java | 4 +- .../HashicorpCredentialNotSetException.java | 9 ++ .../hashicorp/HashicorpKeyVaultService.java | 6 +- .../HashicorpKeyVaultServiceFactory.java | 6 +- ...ashicorpCredentialNotSetExceptionTest.java | 17 +++ .../HashicorpKeyVaultServiceFactoryTest.java | 135 ++++++++++++++++++ .../HashicorpKeyVaultServiceTest.java | 106 ++++++++++++++ 8 files changed, 278 insertions(+), 7 deletions(-) create mode 100644 key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpCredentialNotSetException.java create mode 100644 key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpCredentialNotSetExceptionTest.java create mode 100644 key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java create mode 100644 key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java diff --git a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactory.java b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactory.java index a080ba37e5..dafb04e16f 100644 --- a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactory.java +++ b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactory.java @@ -28,7 +28,7 @@ public KeyVaultService create(Config config, EnvironmentVariableProvider envProv AzureKeyVaultConfig keyVaultConfig = Optional.ofNullable(config.getKeys()) .map(KeyConfiguration::getAzureKeyVaultConfig) - .orElseThrow(() -> new ConfigException(new RuntimeException("Trying to create Azure key vault connection but no Azure configuration provided in the configfile"))); + .orElseThrow(() -> new ConfigException(new RuntimeException("Trying to create Azure key vault connection but no Azure configuration provided"))); return new AzureKeyVaultService( keyVaultConfig, diff --git a/key-vault/azure-key-vault/src/test/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactoryTest.java b/key-vault/azure-key-vault/src/test/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactoryTest.java index 9e1da39841..24bca04ab2 100644 --- a/key-vault/azure-key-vault/src/test/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactoryTest.java +++ b/key-vault/azure-key-vault/src/test/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactoryTest.java @@ -78,7 +78,7 @@ public void nullKeyConfigurationThrowsException() { Throwable ex = catchThrowable(() -> azureKeyVaultServiceFactory.create(config, envProvider)); assertThat(ex).isExactlyInstanceOf(ConfigException.class); - assertThat(ex.getMessage()).contains("Trying to create Azure key vault but no Azure configuration provided in the configfile"); + assertThat(ex.getMessage()).contains("Trying to create Azure key vault but no Azure configuration provided"); } @Test @@ -91,7 +91,7 @@ public void nullKeyVaultConfigurationThrowsException() { Throwable ex = catchThrowable(() -> azureKeyVaultServiceFactory.create(config, envProvider)); assertThat(ex).isExactlyInstanceOf(ConfigException.class); - assertThat(ex.getMessage()).contains("Trying to create Azure key vault but no Azure configuration provided in the configfile"); + assertThat(ex.getMessage()).contains("Trying to create Azure key vault but no Azure configuration provided"); } @Test diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpCredentialNotSetException.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpCredentialNotSetException.java new file mode 100644 index 0000000000..dd82377669 --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpCredentialNotSetException.java @@ -0,0 +1,9 @@ +package com.quorum.tessera.key.vault.hashicorp; + +public class HashicorpCredentialNotSetException extends IllegalStateException { + + public HashicorpCredentialNotSetException(String message) { + super(message); + } + +} diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java index 3596c82f6e..867744b283 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java @@ -28,7 +28,11 @@ public String getHashicorpSecret(String secretPath, String secretName) { } if(response.getData() == null) { - throw new VaultSecretNotFoundException("Value for secret key " + secretName + " not found at path " + secretPath + " in vault " + vaultUrl); + throw new VaultSecretNotFoundException("No data for Hashicorp Vault secret at path " + secretPath + " in vault " + vaultUrl); + } + + if(response.getData().get(secretName) == null) { + throw new VaultSecretNotFoundException("Value for secret id " + secretName + " not found at path " + secretPath + " in vault " + vaultUrl); } return response.getData().get(secretName).toString(); diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java index 8f969980d7..91f4da2272 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java @@ -26,17 +26,17 @@ public KeyVaultService create(Config config, EnvironmentVariableProvider envProv String authToken = envProvider.getEnv(authTokenEnvVar); if(authToken == null) { - throw new RuntimeException(authTokenEnvVar + " must be set"); + throw new HashicorpCredentialNotSetException(authTokenEnvVar + " must be set"); } HashicorpKeyVaultConfig keyVaultConfig = Optional.ofNullable(config.getKeys()) .map(KeyConfiguration::getHashicorpKeyVaultConfig) - .orElseThrow(() -> new ConfigException(new RuntimeException("Trying to create Hashicorp Vault connection but no Vault configuration provided in the configfile"))); + .orElseThrow(() -> new ConfigException(new RuntimeException("Trying to create Hashicorp Vault connection but no Vault configuration provided"))); VaultEndpoint vaultEndpoint; try { vaultEndpoint = VaultEndpoint.from(new URI(keyVaultConfig.getUrl())); - } catch (URISyntaxException e) { + } catch (URISyntaxException | IllegalArgumentException e) { throw new ConfigException(new Throwable("Provided Hashicorp Vault url is incorrectly formatted", e)); } diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpCredentialNotSetExceptionTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpCredentialNotSetExceptionTest.java new file mode 100644 index 0000000000..2ff2239b51 --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpCredentialNotSetExceptionTest.java @@ -0,0 +1,17 @@ +package com.quorum.tessera.key.vault.hashicorp; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class HashicorpCredentialNotSetExceptionTest { + + @Test + public void createWithMessage() { + final String msg = "msg"; + HashicorpCredentialNotSetException exception = new HashicorpCredentialNotSetException(msg); + + assertThat(exception).hasMessage(msg); + } + +} diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java new file mode 100644 index 0000000000..4bd9da0f60 --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java @@ -0,0 +1,135 @@ +package com.quorum.tessera.key.vault.hashicorp; + +import com.quorum.tessera.config.*; +import com.quorum.tessera.config.util.EnvironmentVariableProvider; +import com.quorum.tessera.key.vault.KeyVaultService; +import org.junit.Before; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class HashicorpKeyVaultServiceFactoryTest { + + private HashicorpKeyVaultServiceFactory factory; + + private Config config; + + private EnvironmentVariableProvider envProvider; + + @Before + public void setUp() { + config = mock(Config.class); + envProvider = mock(EnvironmentVariableProvider.class); + factory = new HashicorpKeyVaultServiceFactory(); + } + + @Test(expected = NullPointerException.class) + public void nullConfigThrowsException() { + factory.create(null, envProvider); + } + + @Test(expected = NullPointerException.class) + public void nullEnvVarProviderThrowsException() { + factory.create(config, null); + } + + @Test + public void createThrowsExceptionIfTokenEnvVarNotSet() { + Config config = mock(Config.class); + EnvironmentVariableProvider envProvider = mock(EnvironmentVariableProvider.class); + + when(envProvider.getEnv(anyString())).thenReturn(null); + + Throwable ex = catchThrowable(() -> factory.create(config, envProvider)); + + assertThat(ex).isInstanceOf(HashicorpCredentialNotSetException.class); + assertThat(ex.getMessage()).isEqualTo("HASHICORP_TOKEN must be set"); + } + + @Test + public void nullKeyConfigurationThrowsException() { + when(envProvider.getEnv(anyString())).thenReturn("envVar"); + when(config.getKeys()).thenReturn(null); + + Throwable ex = catchThrowable(() -> factory.create(config, envProvider)); + + assertThat(ex).isInstanceOf(ConfigException.class); + assertThat(ex.getMessage()).contains("Trying to create Hashicorp Vault connection but no Vault configuration provided"); + } + + @Test + public void nullHashicorpVaultConfigThrowsException() { + when(envProvider.getEnv(anyString())).thenReturn("envVar"); + KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); + when(config.getKeys()).thenReturn(keyConfiguration); + when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(null); + + Throwable ex = catchThrowable(() -> factory.create(config, envProvider)); + + assertThat(ex).isInstanceOf(ConfigException.class); + assertThat(ex.getMessage()).contains("Trying to create Hashicorp Vault connection but no Vault configuration provided"); + } + + @Test + public void incorrectSyntaxUrlInConfigThrowsException() { + when(envProvider.getEnv(anyString())).thenReturn("envVar"); + + KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); + when(config.getKeys()).thenReturn(keyConfiguration); + + HashicorpKeyVaultConfig vaultConfig = new HashicorpKeyVaultConfig(); + String url = "!@£$%^"; + vaultConfig.setUrl(url); + when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(vaultConfig); + + Throwable ex = catchThrowable(() -> factory.create(config, envProvider)); + + assertThat(ex).isInstanceOf(ConfigException.class); + assertThat(ex.getMessage()).contains("Provided Hashicorp Vault url is incorrectly formatted"); + } + + @Test + public void incorrectlyFormattedUrlInConfigThrowsException() { + when(envProvider.getEnv(anyString())).thenReturn("envVar"); + + KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); + when(config.getKeys()).thenReturn(keyConfiguration); + + HashicorpKeyVaultConfig vaultConfig = new HashicorpKeyVaultConfig(); + String url = "notaurl"; + vaultConfig.setUrl(url); + when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(vaultConfig); + + Throwable ex = catchThrowable(() -> factory.create(config, envProvider)); + + assertThat(ex).isInstanceOf(ConfigException.class); + assertThat(ex.getMessage()).contains("Provided Hashicorp Vault url is incorrectly formatted"); + } + + @Test + public void createReturnsNewHashicorpKeyVaultService() { + when(envProvider.getEnv(anyString())).thenReturn("envVar"); + + KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); + when(config.getKeys()).thenReturn(keyConfiguration); + + HashicorpKeyVaultConfig vaultConfig = new HashicorpKeyVaultConfig(); + String url = "http://someurl"; + vaultConfig.setUrl(url); + when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(vaultConfig); + + KeyVaultService result = factory.create(config, envProvider); + + assertThat(result).isInstanceOf(HashicorpKeyVaultService.class); + } + + @Test + public void getType() { + assertThat(factory.getType()).isEqualTo(KeyVaultType.HASHICORP); + } + +} diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java new file mode 100644 index 0000000000..83bc7c25f3 --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java @@ -0,0 +1,106 @@ +package com.quorum.tessera.key.vault.hashicorp; + +import com.quorum.tessera.config.HashicorpKeyVaultConfig; +import com.quorum.tessera.key.vault.VaultSecretNotFoundException; +import org.junit.Before; +import org.junit.Test; +import org.mockito.verification.VerificationMode; +import org.springframework.vault.core.VaultTemplate; +import org.springframework.vault.support.VaultResponse; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; + +public class HashicorpKeyVaultServiceTest { + + private HashicorpKeyVaultService keyVaultService; + private VaultTemplate vaultTemplate; + + private final String url = "someurl"; + private final String secretPath = "secret/path"; + private final String secretName = "secretname"; + + + @Before + public void setUp() { + HashicorpKeyVaultConfig keyVaultConfig = mock(HashicorpKeyVaultConfig.class); + when(keyVaultConfig.getUrl()).thenReturn(url); + vaultTemplate = mock(VaultTemplate.class); + + keyVaultService = new HashicorpKeyVaultService(keyVaultConfig, vaultTemplate); + } + + @Test + public void getSecretThrowsExceptionIfSecretNotFoundAtPath() { + when(vaultTemplate.read(anyString())).thenReturn(null); + + final Throwable ex = catchThrowable(() -> keyVaultService.getHashicorpSecret(secretPath, secretName)); + + assertThat(ex).isInstanceOf(VaultSecretNotFoundException.class); + assertThat(ex).hasMessage("Hashicorp Vault secret not found at path " + secretPath + " in vault " + url); + + } + + @Test + public void getSecretThrowsExceptionIfNoDataForSpecifiedSecret() { + VaultResponse vaultResponse = mock(VaultResponse.class); + + when(vaultTemplate.read(anyString())).thenReturn(vaultResponse); + when(vaultResponse.getData()).thenReturn(null); + + final Throwable ex = catchThrowable(() -> keyVaultService.getHashicorpSecret(secretPath, secretName)); + + assertThat(ex).isInstanceOf(VaultSecretNotFoundException.class); + assertThat(ex).hasMessage("No data for Hashicorp Vault secret at path " + secretPath + " in vault " + url); + } + + @Test + public void getSecretThrowsExceptionIfNoValueForSpecifiedSecretName() { + VaultResponse vaultResponse = mock(VaultResponse.class); + Map secretData = Collections.singletonMap("othername", "value"); + + when(vaultTemplate.read(anyString())).thenReturn(vaultResponse); + when(vaultResponse.getData()).thenReturn(secretData); + + final Throwable ex = catchThrowable(() -> keyVaultService.getHashicorpSecret(secretPath, secretName)); + + assertThat(ex).isInstanceOf(VaultSecretNotFoundException.class); + assertThat(ex).hasMessage("Value for secret id " + secretName + " not found at path " + secretPath + " in vault " + url); + } + + @Test + public void getSecretRetrievesValueForSpecifiedKeyAtSpecifiedPath() { + VaultResponse vaultResponse = mock(VaultResponse.class); + String value = "value"; + Map secretData = Collections.singletonMap(secretName, value); + + when(vaultTemplate.read(anyString())).thenReturn(vaultResponse); + when(vaultResponse.getData()).thenReturn(secretData); + + String result = keyVaultService.getHashicorpSecret(secretPath, secretName); + + assertThat(result).isEqualTo(value); + } + + @Test + public void setSecretCallsVaultTemplate() { + Map secretData = Collections.singletonMap(secretName, "value"); + keyVaultService.setHashicorpSecret(secretPath, secretData); + + verify(vaultTemplate, times(1)).write(secretPath, secretData); + } + + @Test + //TODO Not ideal - see class todos + public void getSecretAndSetSecretReturnNull() { + assertThat(keyVaultService.getSecret(secretName)).isNull(); + assertThat(keyVaultService.setSecret(secretPath, secretName)).isNull(); + } + +} From 7375b4db890f79c3a2ffb299f69bbe783329942a Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Wed, 28 Nov 2018 10:45:57 +0000 Subject: [PATCH 06/57] Add tests for Hashicorp vault config additions --- .../tessera/config/KeyConfiguration.java | 3 +- .../config/HashicorpKeyVaultConfigTest.java | 34 +++++++++++++++ .../config/adapters/KeyDataAdapterTest.java | 43 +++++++++++++++++++ .../keypairs/HashicorpVaultKeyPairTest.java | 33 ++++++++++++++ 4 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 config/src/test/java/com/quorum/tessera/config/HashicorpKeyVaultConfigTest.java create mode 100644 config/src/test/java/com/quorum/tessera/config/keypairs/HashicorpVaultKeyPairTest.java diff --git a/config/src/main/java/com/quorum/tessera/config/KeyConfiguration.java b/config/src/main/java/com/quorum/tessera/config/KeyConfiguration.java index 7fe9fe372b..2875ff0808 100644 --- a/config/src/main/java/com/quorum/tessera/config/KeyConfiguration.java +++ b/config/src/main/java/com/quorum/tessera/config/KeyConfiguration.java @@ -36,7 +36,8 @@ public class KeyConfiguration extends ConfigItem { private AzureKeyVaultConfig azureKeyVaultConfig; @Valid - @XmlElement HashicorpKeyVaultConfig hashicorpKeyVaultConfig; + @XmlElement + private HashicorpKeyVaultConfig hashicorpKeyVaultConfig; public KeyConfiguration(final Path passwordFile, final List passwords, final List keyData, final AzureKeyVaultConfig azureKeyVaultConfig, final HashicorpKeyVaultConfig hashicorpKeyVaultConfig) { this.passwordFile = passwordFile; diff --git a/config/src/test/java/com/quorum/tessera/config/HashicorpKeyVaultConfigTest.java b/config/src/test/java/com/quorum/tessera/config/HashicorpKeyVaultConfigTest.java new file mode 100644 index 0000000000..7246bc923a --- /dev/null +++ b/config/src/test/java/com/quorum/tessera/config/HashicorpKeyVaultConfigTest.java @@ -0,0 +1,34 @@ +package com.quorum.tessera.config; + +import org.junit.Before; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class HashicorpKeyVaultConfigTest { + + private HashicorpKeyVaultConfig vaultConfig; + + @Before + public void setUp() { + vaultConfig = new HashicorpKeyVaultConfig("url"); + } + + @Test + public void getters() { + assertThat(vaultConfig.getUrl()).isEqualTo("url"); + } + + @Test + public void setters() { + assertThat(vaultConfig.getUrl()).isEqualTo("url"); + vaultConfig.setUrl("newUrl"); + assertThat(vaultConfig.getUrl()).isEqualTo("newUrl"); + } + + @Test + public void getType() { + assertThat(vaultConfig.getKeyVaultType()).isEqualTo(KeyVaultType.HASHICORP); + } + +} diff --git a/config/src/test/java/com/quorum/tessera/config/adapters/KeyDataAdapterTest.java b/config/src/test/java/com/quorum/tessera/config/adapters/KeyDataAdapterTest.java index 052fc0882b..e12e774436 100644 --- a/config/src/test/java/com/quorum/tessera/config/adapters/KeyDataAdapterTest.java +++ b/config/src/test/java/com/quorum/tessera/config/adapters/KeyDataAdapterTest.java @@ -60,6 +60,17 @@ public void marshallAzureKeys() { assertThat(result).isEqualTo(expected); } + @Test + public void marshallHashicorpKeys() { + final HashicorpVaultKeyPair keyPair = new HashicorpVaultKeyPair("pubId", "privId", "secretPath"); + + final KeyData expected = new KeyData(null, null, null, null, null, null, null, "privId", "pubId", "secretPath"); + + final KeyData result = adapter.marshal(keyPair); + + assertThat(result).isEqualTo(expected); + } + @Test public void marshallUnsupportedKeys() { final KeyDataConfig keyDataConfig = mock(KeyDataConfig.class); @@ -150,6 +161,14 @@ public void unmarshallingAzureKeysGivesCorrectKeyPair() { assertThat(result).isInstanceOf(AzureVaultKeyPair.class); } + @Test + public void unmarshallingHashicorpKeysGivesCorrectKeyPair() { + final KeyData input = new KeyData(null, null, null, null, null, null, null, "privId", "pubId", "secretPath"); + + final ConfigKeyPair result = this.adapter.unmarshal(input); + assertThat(result).isInstanceOf(HashicorpVaultKeyPair.class); + } + @Test public void unmarshallingPrivateOnlyGivesUnsupportedKeyPair() { final KeyData input = new KeyData(null, "private", null, null, null, null, null, null, null, null); @@ -183,6 +202,30 @@ public void unmarshallingAzurePrivateOnlyGivesUnsupportedKeyPair() { assertThat(result).isInstanceOf(UnsupportedKeyPair.class); } + @Test + public void unmarshallingHashicorpPublicOnlyGivesUnsupprtedKeyPair() { + final KeyData input = new KeyData(null, null, null, null, null, null, null, null, "pubId", null); + + final ConfigKeyPair result = this.adapter.unmarshal(input); + assertThat(result).isInstanceOf(UnsupportedKeyPair.class); + } + + @Test + public void unmarshallingHashicorpPrivateOnlyGivesUnsupprtedKeyPair() { + final KeyData input = new KeyData(null, null, null, null, null, null, null, "privId", null, null); + + final ConfigKeyPair result = this.adapter.unmarshal(input); + assertThat(result).isInstanceOf(UnsupportedKeyPair.class); + } + + @Test + public void unmarshallingHashicorpSecretPathOnlyGivesUnsupprtedKeyPair() { + final KeyData input = new KeyData(null, null, null, null, null, null, null, null, null, "secretPath"); + + final ConfigKeyPair result = this.adapter.unmarshal(input); + assertThat(result).isInstanceOf(UnsupportedKeyPair.class); + } + @Test public void unmarshallingPublicPathOnlyGivesUnsupportedKeyPair() { final Path path = mock(Path.class); diff --git a/config/src/test/java/com/quorum/tessera/config/keypairs/HashicorpVaultKeyPairTest.java b/config/src/test/java/com/quorum/tessera/config/keypairs/HashicorpVaultKeyPairTest.java new file mode 100644 index 0000000000..d45ad1e944 --- /dev/null +++ b/config/src/test/java/com/quorum/tessera/config/keypairs/HashicorpVaultKeyPairTest.java @@ -0,0 +1,33 @@ +package com.quorum.tessera.config.keypairs; + +import org.junit.Before; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class HashicorpVaultKeyPairTest { + + private HashicorpVaultKeyPair keyPair; + + @Before + public void setUp() { + keyPair = new HashicorpVaultKeyPair("pubId", "privId", "secretPath"); + } + + @Test + public void getters() { + assertThat(keyPair.getPublicKeyId()).isEqualTo("pubId"); + assertThat(keyPair.getPrivateKeyId()).isEqualTo("privId"); + assertThat(keyPair.getSecretPath()).isEqualTo("secretPath"); + assertThat(keyPair.getPublicKey()).isEqualTo(null); + assertThat(keyPair.getPrivateKey()).isEqualTo(null); + assertThat(keyPair.getPassword()).isEqualTo(""); + } + + @Test + public void withPasswordDoesNothing() { + assertThat(keyPair.getPassword()).isEqualTo(""); + keyPair.withPassword("newpwd"); + assertThat(keyPair.getPassword()).isEqualTo(""); + } +} From 5bf1b9a1574eaa55a367c8489814e1c821f83c79 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Wed, 28 Nov 2018 11:31:25 +0000 Subject: [PATCH 07/57] Update validation to cover incomplete Hashicorp keydata --- .../UnsupportedKeyPairValidator.java | 13 +++++ .../resources/ValidationMessages.properties | 1 + .../UnsupportedKeyPairValidatorTest.java | 56 +++++++++++++++++++ 3 files changed, 70 insertions(+) diff --git a/config/src/main/java/com/quorum/tessera/config/constraints/UnsupportedKeyPairValidator.java b/config/src/main/java/com/quorum/tessera/config/constraints/UnsupportedKeyPairValidator.java index 8f981b784c..b84f9f1ad6 100644 --- a/config/src/main/java/com/quorum/tessera/config/constraints/UnsupportedKeyPairValidator.java +++ b/config/src/main/java/com/quorum/tessera/config/constraints/UnsupportedKeyPairValidator.java @@ -32,6 +32,11 @@ else if(isIncompleteAzureVaultKeyPair(keyPair)) { context.buildConstraintViolationWithTemplate("{UnsupportedKeyPair.bothAzureKeysRequired.message}") .addConstraintViolation(); } + else if(isIncompleteHashicorpVaultKeyPair(keyPair)) { + context.disableDefaultConstraintViolation(); + context.buildConstraintViolationWithTemplate("{UnsupportedKeyPair.allHashicorpKeyDataRequired.message}") + .addConstraintViolation(); + } else if(isIncompleteFilesystemKeyPair(keyPair)) { context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate("{UnsupportedKeyPair.bothFilesystemKeysRequired.message}") @@ -53,6 +58,14 @@ private boolean isIncompleteAzureVaultKeyPair(UnsupportedKeyPair keyPair) { return isOnlyOneInputNull(keyPair.getAzureVaultPublicKeyId(), keyPair.getAzureVaultPrivateKeyId()); } + private boolean isIncompleteHashicorpVaultKeyPair(UnsupportedKeyPair keyPair) { + if(isOnlyOneInputNull(keyPair.getHashicorpVaultPublicKeyId(), keyPair.getHashicorpVaultPrivateKeyId())) { + return true; + } + + return isOnlyOneInputNull(keyPair.getHashicorpVaultPublicKeyId(), keyPair.getHashicorpVaultSecretPath()); + } + private boolean isIncompleteFilesystemKeyPair(UnsupportedKeyPair keyPair) { return isOnlyOneInputNull(keyPair.getPublicKeyPath(), keyPair.getPrivateKeyPath()); } diff --git a/config/src/main/resources/ValidationMessages.properties b/config/src/main/resources/ValidationMessages.properties index 0eb6a73f37..5ed6272462 100644 --- a/config/src/main/resources/ValidationMessages.properties +++ b/config/src/main/resources/ValidationMessages.properties @@ -16,6 +16,7 @@ UnsupportedKeyPair.message=Invalid key-pair. Ensure that the public and private UnsupportedKeyPair.bothDirectKeysRequired.message=Invalid direct key-pair. Ensure that both the public and private keys for the key-pair have been provided. UnsupportedKeyPair.bothInlineKeysRequired.message=Invalid inline key-pair. Ensure that both the public key and private key config for the key-pair have been provided. UnsupportedKeyPair.bothAzureKeysRequired.message=Invalid Azure Key Vault key-pair. Ensure that both the public key ID and private key ID for the key-pair have been provided. +UnsupportedKeyPair.allHashicorpKeyDataRequired.message=Invalid Hashicorp Key Vault key-pair. Ensure that the public key ID, private key ID and secret path for the key-pair have been provided. UnsupportedKeyPair.bothFilesystemKeysRequired.message=Invalid filesystem key-pair. Ensure that both the public key path and private key path for the key-pair have been provided. ValidKeyDataConfig.message=A locked key was provided without a password.\n Please ensure the same number of passwords are provided as there are keys and remember to include empty passwords for unlocked keys InlineKeyData.message=A locked key was provided without a password.\n Please ensure the same number of passwords are provided as there are keys and remember to include empty passwords for unlocked keys diff --git a/config/src/test/java/com/quorum/tessera/config/constraints/UnsupportedKeyPairValidatorTest.java b/config/src/test/java/com/quorum/tessera/config/constraints/UnsupportedKeyPairValidatorTest.java index 9a06f2b37d..89656b04b5 100644 --- a/config/src/test/java/com/quorum/tessera/config/constraints/UnsupportedKeyPairValidatorTest.java +++ b/config/src/test/java/com/quorum/tessera/config/constraints/UnsupportedKeyPairValidatorTest.java @@ -122,6 +122,62 @@ public void azureViolationIfNoPublicIdEvenIfFilesystemIncomplete() { verify(context).buildConstraintViolationWithTemplate("{UnsupportedKeyPair.bothAzureKeysRequired.message}"); } + @Test + public void hashicorpViolationIfPublicIdButNoPrivateIdOrSecretPath() { + UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, null, null, null, null, null, "pubId", null, null); + + validator.isValid(keyPair, context); + + verify(context).buildConstraintViolationWithTemplate("{UnsupportedKeyPair.allHashicorpKeyDataRequired.message}"); + } + + @Test + public void hashicorpViolationIfPrivateIdButNoPublicIdOrSecretPath() { + UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, null, null, null, null, null, null, "privId", null); + + validator.isValid(keyPair, context); + + verify(context).buildConstraintViolationWithTemplate("{UnsupportedKeyPair.allHashicorpKeyDataRequired.message}"); + } + + @Test + public void hashicorpViolationIfSecretPathButNoPublicIdOrPrivateId() { + UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, null, null, null, null, null, null, null, "secretPath"); + + validator.isValid(keyPair, context); + + verify(context).buildConstraintViolationWithTemplate("{UnsupportedKeyPair.allHashicorpKeyDataRequired.message}"); + } + + @Test + public void hashicorpViolationIfPublicIdAndPrivateIdButNoSecretPath() { + UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, null, null, null, null, null, "pubId", "privId", null); + + validator.isValid(keyPair, context); + + verify(context).buildConstraintViolationWithTemplate("{UnsupportedKeyPair.allHashicorpKeyDataRequired.message}"); + } + + @Test + public void hashicorpViolationIfPublicIdAndSecretPathButNoPrivateId() { + UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, null, null, null, null, null, "pubId", null, "secretPath"); + + validator.isValid(keyPair, context); + + verify(context).buildConstraintViolationWithTemplate("{UnsupportedKeyPair.allHashicorpKeyDataRequired.message}"); + } + + @Test + public void hashicorpViolationIfPrivateIdAndSecretPathButNoPublicId() { + UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, null, null, null, null, null, null, "privId", "secretPath"); + + validator.isValid(keyPair, context); + + verify(context).buildConstraintViolationWithTemplate("{UnsupportedKeyPair.allHashicorpKeyDataRequired.message}"); + } + + + @Test public void azureViolationIfNoPrivateIdEvenIfFilesystemIncomplete() { Path path = mock(Path.class); From d54a432a6778a5a41d79d7ee62f8a2e8489a6c8c Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Wed, 28 Nov 2018 11:59:43 +0000 Subject: [PATCH 08/57] Add validation to require Hashicorp config if providing Hashicorp keys --- .../KeyVaultConfigurationValidator.java | 17 ++++ .../resources/ValidationMessages.properties | 4 +- .../quorum/tessera/config/ValidationTest.java | 97 +++++++++++++++++-- 3 files changed, 108 insertions(+), 10 deletions(-) diff --git a/config/src/main/java/com/quorum/tessera/config/constraints/KeyVaultConfigurationValidator.java b/config/src/main/java/com/quorum/tessera/config/constraints/KeyVaultConfigurationValidator.java index 1edd7d9cf8..424a7eb277 100644 --- a/config/src/main/java/com/quorum/tessera/config/constraints/KeyVaultConfigurationValidator.java +++ b/config/src/main/java/com/quorum/tessera/config/constraints/KeyVaultConfigurationValidator.java @@ -2,6 +2,7 @@ import com.quorum.tessera.config.KeyConfiguration; import com.quorum.tessera.config.keypairs.AzureVaultKeyPair; +import com.quorum.tessera.config.keypairs.HashicorpVaultKeyPair; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; @@ -28,6 +29,22 @@ public boolean isValid(KeyConfiguration keyConfiguration, ConstraintValidatorCon .anyMatch(keyPair -> keyPair instanceof AzureVaultKeyPair); if(isUsingAzureVaultKeys && keyConfiguration.getAzureKeyVaultConfig() == null) { + cvc.disableDefaultConstraintViolation(); + cvc.buildConstraintViolationWithTemplate("{ValidKeyVaultConfiguration.azure.message}") + .addConstraintViolation(); + + return false; + } + + boolean isUsingHashicorpVaultKeys = keyConfiguration.getKeyData() + .stream() + .anyMatch(keyPair -> keyPair instanceof HashicorpVaultKeyPair); + + if(isUsingHashicorpVaultKeys && keyConfiguration.getHashicorpKeyVaultConfig() == null) { + cvc.disableDefaultConstraintViolation(); + cvc.buildConstraintViolationWithTemplate("{ValidKeyVaultConfiguration.hashicorp.message}") + .addConstraintViolation(); + return false; } diff --git a/config/src/main/resources/ValidationMessages.properties b/config/src/main/resources/ValidationMessages.properties index 5ed6272462..4aebb52539 100644 --- a/config/src/main/resources/ValidationMessages.properties +++ b/config/src/main/resources/ValidationMessages.properties @@ -21,4 +21,6 @@ UnsupportedKeyPair.bothFilesystemKeysRequired.message=Invalid filesystem key-pai ValidKeyDataConfig.message=A locked key was provided without a password.\n Please ensure the same number of passwords are provided as there are keys and remember to include empty passwords for unlocked keys InlineKeyData.message=A locked key was provided without a password.\n Please ensure the same number of passwords are provided as there are keys and remember to include empty passwords for unlocked keys ValidKeyConfiguration.message=A password file and inline passwords were provided. Please choose one or the other -ValidKeyVaultConfiguration.message=No azureKeyVaultConfig was specified but azureVaultPublicKeyId and azureVaultPrivateKeyId were provided +ValidKeyVaultConfiguration.message=No key vault configuration was specified but vault key data was provided +ValidKeyVaultConfiguration.azure.message=No azureKeyVaultConfig was specified but azureVaultPublicKeyId and azureVaultPrivateKeyId were provided +ValidKeyVaultConfiguration.hashicorp.message=No hashicorpKeyVaultConfig was specified but hashicorpVaultPublicKeyId, hashicorpVaultPrivateKeyId and hashicorpVaultSecretPath were provided diff --git a/config/src/test/java/com/quorum/tessera/config/ValidationTest.java b/config/src/test/java/com/quorum/tessera/config/ValidationTest.java index e2d984b463..fbae046506 100644 --- a/config/src/test/java/com/quorum/tessera/config/ValidationTest.java +++ b/config/src/test/java/com/quorum/tessera/config/ValidationTest.java @@ -1,10 +1,6 @@ package com.quorum.tessera.config; -import com.quorum.tessera.config.keypairs.AzureVaultKeyPair; -import com.quorum.tessera.config.keypairs.ConfigKeyPair; -import com.quorum.tessera.config.keypairs.DirectKeyPair; -import com.quorum.tessera.config.keypairs.FilesystemKeyPair; -import com.quorum.tessera.config.keypairs.InlineKeypair; +import com.quorum.tessera.config.keypairs.*; import org.junit.Test; import org.mockito.Mockito; @@ -275,11 +271,70 @@ public void azureKeyPairProvidedWithoutKeyVaultConfigCreatesViolation() { assertThat(violations).hasSize(1); ConstraintViolation violation = violations.iterator().next(); - assertThat(violation.getMessageTemplate()).isEqualTo("{ValidKeyVaultConfiguration.message}"); + assertThat(violation.getMessageTemplate()).isEqualTo("{ValidKeyVaultConfiguration.azure.message}"); } @Test - public void nonKeyVaultPairProvidedWithoutKeyVaultConfigDoesNotCreateViolation() { + public void azureKeyPairProvidedWithHashicorpKeyVaultConfigCreatesViolation() { + AzureVaultKeyPair keyPair = new AzureVaultKeyPair("publicVauldId", "privateVaultId"); + + KeyConfiguration keyConfiguration = new KeyConfiguration(); + keyConfiguration.setKeyData(singletonList(keyPair)); + + HashicorpKeyVaultConfig hashicorpConfig = new HashicorpKeyVaultConfig(); + keyConfiguration.setHashicorpKeyVaultConfig(hashicorpConfig); + + Config config = new Config(); + config.setKeys(keyConfiguration); + + Set> violations = validator.validateProperty(config, "keys"); + assertThat(violations).hasSize(1); + + ConstraintViolation violation = violations.iterator().next(); + assertThat(violation.getMessageTemplate()).isEqualTo("{ValidKeyVaultConfiguration.azure.message}"); + } + + @Test + public void hashicorpKeyPairProvidedWithoutKeyVaultConfigCreatesViolation() { + HashicorpVaultKeyPair keyPair = new HashicorpVaultKeyPair("pubId", "privdId", "secretPath"); + + KeyConfiguration keyConfiguration = new KeyConfiguration(); + keyConfiguration.setKeyData(singletonList(keyPair)); + keyConfiguration.setHashicorpKeyVaultConfig(null); + + Config config = new Config(); + config.setKeys(keyConfiguration); + + Set> violations = validator.validateProperty(config, "keys"); + assertThat(violations).hasSize(1); + + ConstraintViolation violation = violations.iterator().next(); + assertThat(violation.getMessageTemplate()).isEqualTo("{ValidKeyVaultConfiguration.hashicorp.message}"); + } + + @Test + public void hashicorpKeyPairProvidedWithAzureKeyVaultConfigCreatesViolation() { + HashicorpVaultKeyPair keyPair = new HashicorpVaultKeyPair("pubId", "privdId", "secretPath"); + + KeyConfiguration keyConfiguration = new KeyConfiguration(); + keyConfiguration.setKeyData(singletonList(keyPair)); + keyConfiguration.setHashicorpKeyVaultConfig(null); + + AzureKeyVaultConfig azureConfig = new AzureKeyVaultConfig(); + keyConfiguration.setAzureKeyVaultConfig(azureConfig); + + Config config = new Config(); + config.setKeys(keyConfiguration); + + Set> violations = validator.validateProperty(config, "keys"); + assertThat(violations).hasSize(1); + + ConstraintViolation violation = violations.iterator().next(); + assertThat(violation.getMessageTemplate()).isEqualTo("{ValidKeyVaultConfiguration.hashicorp.message}"); + } + + @Test + public void nonKeyVaultPairProvidedWithoutAzureAndHashicorpKeyVaultConfigDoesNotCreateViolation() { DirectKeyPair keyPair = new DirectKeyPair("pub", "priv"); KeyConfiguration keyConfiguration = new KeyConfiguration(null, null, singletonList(keyPair), null, null); @@ -289,7 +344,6 @@ public void nonKeyVaultPairProvidedWithoutKeyVaultConfigDoesNotCreateViolation() assertThat(violations).hasSize(0); } - @Test public void keyConfigurationIsNullCreatesNotNullViolation() { Config config = new Config(null, null, null, null, null, null, false, false); @@ -314,7 +368,7 @@ public void azureVaultConfigWithNoUrlCreatesNullViolation() { } @Test - public void azureVaultKeyPairProvidedButKeyVaultConfigHasNullUrlCreatesNullViolation() { + public void azureVaultKeyPairProvidedButKeyVaultConfigHasNullUrlCreatesNotNullViolation() { AzureVaultKeyPair keyPair = new AzureVaultKeyPair("pubId", "privId"); AzureKeyVaultConfig keyVaultConfig = new AzureKeyVaultConfig(null); KeyConfiguration keyConfiguration = new KeyConfiguration(null, null, singletonList(keyPair), keyVaultConfig, null); @@ -326,4 +380,29 @@ public void azureVaultKeyPairProvidedButKeyVaultConfigHasNullUrlCreatesNullViola assertThat(violation.getMessageTemplate()).isEqualTo("{javax.validation.constraints.NotNull.message}"); assertThat(violation.getPropertyPath().toString()).isEqualTo("azureKeyVaultConfig.url"); } + + @Test + public void hashicorpVaultConfigWithNoUrlCreatesNotNullViolation() { + HashicorpKeyVaultConfig keyVaultConfig = new HashicorpKeyVaultConfig(null); + + Set> violations = validator.validate(keyVaultConfig); + assertThat(violations).hasSize(1); + + ConstraintViolation violation = violations.iterator().next(); + assertThat(violation.getMessageTemplate()).isEqualTo("{javax.validation.constraints.NotNull.message}"); + } + + @Test + public void hashicorpVaultKeyPairProvidedButKeyVaultConfigHasNullUrlCreatesNotNullViolation() { + HashicorpVaultKeyPair keyPair = new HashicorpVaultKeyPair("pubId", "privId", "secretPath"); + HashicorpKeyVaultConfig keyVaultConfig = new HashicorpKeyVaultConfig(null); + KeyConfiguration keyConfiguration = new KeyConfiguration(null, null, singletonList(keyPair), null, keyVaultConfig); + + Set> violations = validator.validate(keyConfiguration); + assertThat(violations).hasSize(1); + + ConstraintViolation violation = violations.iterator().next(); + assertThat(violation.getMessageTemplate()).isEqualTo("{javax.validation.constraints.NotNull.message}"); + assertThat(violation.getPropertyPath().toString()).isEqualTo("hashicorpKeyVaultConfig.url"); + } } From 35b09a1543a943c41ca7f1522ae9abf7dd3996ff Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Wed, 28 Nov 2018 14:10:38 +0000 Subject: [PATCH 09/57] Add logic to KeyPairConverter for Hashicorp key pair types --- .../keypairconverter/KeyPairConverter.java | 15 ++++++++++- .../KeyPairConverterTest.java | 18 ++++++++++--- .../MockHashicorpKeyVaultServiceFactory.java | 26 +++++++++++++++++++ ...m.tessera.key.vault.KeyVaultServiceFactory | 3 ++- .../key/vault/azure/AzureKeyVaultService.java | 11 ++++++++ .../hashicorp/HashicorpKeyVaultService.java | 26 ++++++++++--------- ...m.tessera.key.vault.KeyVaultServiceFactory | 1 + .../HashicorpKeyVaultServiceTest.java | 10 +++---- .../tessera/key/vault/KeyVaultService.java | 6 +++++ pom.xml | 6 +++++ tessera-core/pom.xml | 6 +++++ 11 files changed, 105 insertions(+), 23 deletions(-) create mode 100644 key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/MockHashicorpKeyVaultServiceFactory.java create mode 100644 key-vault/hashicorp-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.KeyVaultServiceFactory diff --git a/key-pair-converter/src/main/java/com/quorum/tessera/keypairconverter/KeyPairConverter.java b/key-pair-converter/src/main/java/com/quorum/tessera/keypairconverter/KeyPairConverter.java index a519daf5f4..2f7a84d0fa 100644 --- a/key-pair-converter/src/main/java/com/quorum/tessera/keypairconverter/KeyPairConverter.java +++ b/key-pair-converter/src/main/java/com/quorum/tessera/keypairconverter/KeyPairConverter.java @@ -5,6 +5,7 @@ import com.quorum.tessera.config.KeyVaultType; import com.quorum.tessera.config.keypairs.AzureVaultKeyPair; import com.quorum.tessera.config.keypairs.ConfigKeyPair; +import com.quorum.tessera.config.keypairs.HashicorpVaultKeyPair; import com.quorum.tessera.config.util.EnvironmentVariableProvider; import com.quorum.tessera.encryption.KeyPair; import com.quorum.tessera.encryption.PrivateKey; @@ -40,6 +41,7 @@ private KeyPair convert(ConfigKeyPair configKeyPair) { String base64PrivateKey; if(configKeyPair instanceof AzureVaultKeyPair) { + KeyVaultServiceFactory keyVaultServiceFactory = KeyVaultServiceFactory.getInstance(KeyVaultType.AZURE); KeyVaultService keyVaultService = keyVaultServiceFactory.create(config, envProvider); AzureVaultKeyPair akp = (AzureVaultKeyPair) configKeyPair; @@ -47,7 +49,18 @@ private KeyPair convert(ConfigKeyPair configKeyPair) { base64PublicKey = keyVaultService.getSecret(akp.getPublicKeyId()); base64PrivateKey = keyVaultService.getSecret(akp.getPrivateKeyId()); - } else { + } + else if(configKeyPair instanceof HashicorpVaultKeyPair) { + + KeyVaultServiceFactory keyVaultServiceFactory = KeyVaultServiceFactory.getInstance(KeyVaultType.HASHICORP); + KeyVaultService keyVaultService = keyVaultServiceFactory.create(config, envProvider); + HashicorpVaultKeyPair hkp = (HashicorpVaultKeyPair) configKeyPair; + + base64PublicKey = keyVaultService.getSecretFromPath(hkp.getSecretPath(), hkp.getPublicKeyId()); + base64PrivateKey = keyVaultService.getSecretFromPath(hkp.getSecretPath(), hkp.getPrivateKeyId()); + + } + else { base64PublicKey = configKeyPair.getPublicKey(); base64PrivateKey = configKeyPair.getPrivateKey(); diff --git a/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/KeyPairConverterTest.java b/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/KeyPairConverterTest.java index fe5f434fb2..a5f7f2aae6 100644 --- a/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/KeyPairConverterTest.java +++ b/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/KeyPairConverterTest.java @@ -1,10 +1,7 @@ package com.quorum.tessera.keypairconverter; import com.quorum.tessera.config.Config; -import com.quorum.tessera.config.keypairs.AzureVaultKeyPair; -import com.quorum.tessera.config.keypairs.DirectKeyPair; -import com.quorum.tessera.config.keypairs.FilesystemKeyPair; -import com.quorum.tessera.config.keypairs.InlineKeypair; +import com.quorum.tessera.config.keypairs.*; import com.quorum.tessera.config.util.EnvironmentVariableProvider; import com.quorum.tessera.encryption.KeyPair; import com.quorum.tessera.encryption.PrivateKey; @@ -95,6 +92,19 @@ public void convertSingleAzureVaultKeyPair() { assertThat(resultKeyPair).isEqualToComparingFieldByField(expected); } + @Test + public void convertSingleHashicorpVaultKeyPair() { + final HashicorpVaultKeyPair keyPair = new HashicorpVaultKeyPair("pub", "priv", "secretPath"); + + Collection result = converter.convert(Collections.singletonList(keyPair)); + + assertThat(result).hasSize(1); + + KeyPair resultKeyPair = result.iterator().next(); + KeyPair expected = new KeyPair(PublicKey.from(decodeBase64("publicSecret")), PrivateKey.from(decodeBase64("privSecret"))); + + assertThat(resultKeyPair).isEqualToComparingFieldByField(expected); + } @Test public void convertMultipleKeyPairs() { diff --git a/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/MockHashicorpKeyVaultServiceFactory.java b/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/MockHashicorpKeyVaultServiceFactory.java new file mode 100644 index 0000000000..12beb70fdf --- /dev/null +++ b/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/MockHashicorpKeyVaultServiceFactory.java @@ -0,0 +1,26 @@ +package com.quorum.tessera.keypairconverter; + +import com.quorum.tessera.config.Config; +import com.quorum.tessera.config.KeyVaultType; +import com.quorum.tessera.config.util.EnvironmentVariableProvider; +import com.quorum.tessera.key.vault.KeyVaultService; +import com.quorum.tessera.key.vault.KeyVaultServiceFactory; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class MockHashicorpKeyVaultServiceFactory implements KeyVaultServiceFactory { + @Override + public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider) { + KeyVaultService mock = mock(KeyVaultService.class); + when(mock.getSecretFromPath("secretPath", "pub")).thenReturn("publicSecret"); + when(mock.getSecretFromPath("secretPath", "priv")).thenReturn("privSecret"); + + return mock; + } + + @Override + public KeyVaultType getType() { + return KeyVaultType.HASHICORP; + } +} diff --git a/key-pair-converter/src/test/resources/META-INF/services/com.quorum.tessera.key.vault.KeyVaultServiceFactory b/key-pair-converter/src/test/resources/META-INF/services/com.quorum.tessera.key.vault.KeyVaultServiceFactory index 469af955cc..f614cc09d2 100644 --- a/key-pair-converter/src/test/resources/META-INF/services/com.quorum.tessera.key.vault.KeyVaultServiceFactory +++ b/key-pair-converter/src/test/resources/META-INF/services/com.quorum.tessera.key.vault.KeyVaultServiceFactory @@ -1 +1,2 @@ -com.quorum.tessera.keypairconverter.MockAzureKeyVaultServiceFactory \ No newline at end of file +com.quorum.tessera.keypairconverter.MockAzureKeyVaultServiceFactory +com.quorum.tessera.keypairconverter.MockHashicorpKeyVaultServiceFactory \ No newline at end of file diff --git a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultService.java b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultService.java index f0322589d0..35ffd6c0a8 100644 --- a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultService.java +++ b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultService.java @@ -6,6 +6,7 @@ import com.quorum.tessera.key.vault.KeyVaultService; import com.quorum.tessera.key.vault.VaultSecretNotFoundException; +import java.util.Map; import java.util.Objects; public class AzureKeyVaultService implements KeyVaultService { @@ -30,10 +31,20 @@ public String getSecret(String secretName) { return secretBundle.value(); } + @Override + public String getSecretFromPath(String secretPath, String secretName) { + return null; + } + @Override public SecretBundle setSecret(String secretName, String secret) { SetSecretRequest setSecretRequest = new SetSecretRequest.Builder(vaultUrl, secretName, secret).build(); return this.azureKeyVaultClientDelegate.setSecret(setSecretRequest); } + + @Override + public Object setSecretAtPath(String secretPath, Map secretData) { + return null; + } } diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java index 867744b283..3309569bca 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java @@ -20,7 +20,18 @@ public HashicorpKeyVaultService(HashicorpKeyVaultConfig keyVaultConfig, VaultTem this.vaultTemplate = vaultTemplate; } - public String getHashicorpSecret(String secretPath, String secretName) { + @Override + public String getSecret(String secretName) { + return null; + } + + @Override + public Object setSecret(String secretName, String secret) { + return null; + } + + @Override + public String getSecretFromPath(String secretPath, String secretName) { VaultResponse response = vaultTemplate.read(secretPath); if(response == null) { @@ -38,17 +49,8 @@ public String getHashicorpSecret(String secretPath, String secretName) { return response.getData().get(secretName).toString(); } - public void setHashicorpSecret(String secretPath, Map keyValuePairs) { - vaultTemplate.write(secretPath, keyValuePairs); - } - - @Override - public String getSecret(String secretName) { - return null; - } - @Override - public Object setSecret(String secretName, String secret) { - return null; + public Object setSecretAtPath(String secretPath, Map secretData) { + return vaultTemplate.write(secretPath, secretData); } } diff --git a/key-vault/hashicorp-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.KeyVaultServiceFactory b/key-vault/hashicorp-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.KeyVaultServiceFactory new file mode 100644 index 0000000000..dfa2a119c7 --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.KeyVaultServiceFactory @@ -0,0 +1 @@ +com.quorum.tessera.key.vault.hashicorp.HashicorpKeyVaultServiceFactory \ No newline at end of file diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java index 83bc7c25f3..4f35271b33 100644 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java @@ -40,7 +40,7 @@ public void setUp() { public void getSecretThrowsExceptionIfSecretNotFoundAtPath() { when(vaultTemplate.read(anyString())).thenReturn(null); - final Throwable ex = catchThrowable(() -> keyVaultService.getHashicorpSecret(secretPath, secretName)); + final Throwable ex = catchThrowable(() -> keyVaultService.getSecretFromPath(secretPath, secretName)); assertThat(ex).isInstanceOf(VaultSecretNotFoundException.class); assertThat(ex).hasMessage("Hashicorp Vault secret not found at path " + secretPath + " in vault " + url); @@ -54,7 +54,7 @@ public void getSecretThrowsExceptionIfNoDataForSpecifiedSecret() { when(vaultTemplate.read(anyString())).thenReturn(vaultResponse); when(vaultResponse.getData()).thenReturn(null); - final Throwable ex = catchThrowable(() -> keyVaultService.getHashicorpSecret(secretPath, secretName)); + final Throwable ex = catchThrowable(() -> keyVaultService.getSecretFromPath(secretPath, secretName)); assertThat(ex).isInstanceOf(VaultSecretNotFoundException.class); assertThat(ex).hasMessage("No data for Hashicorp Vault secret at path " + secretPath + " in vault " + url); @@ -68,7 +68,7 @@ public void getSecretThrowsExceptionIfNoValueForSpecifiedSecretName() { when(vaultTemplate.read(anyString())).thenReturn(vaultResponse); when(vaultResponse.getData()).thenReturn(secretData); - final Throwable ex = catchThrowable(() -> keyVaultService.getHashicorpSecret(secretPath, secretName)); + final Throwable ex = catchThrowable(() -> keyVaultService.getSecretFromPath(secretPath, secretName)); assertThat(ex).isInstanceOf(VaultSecretNotFoundException.class); assertThat(ex).hasMessage("Value for secret id " + secretName + " not found at path " + secretPath + " in vault " + url); @@ -83,7 +83,7 @@ public void getSecretRetrievesValueForSpecifiedKeyAtSpecifiedPath() { when(vaultTemplate.read(anyString())).thenReturn(vaultResponse); when(vaultResponse.getData()).thenReturn(secretData); - String result = keyVaultService.getHashicorpSecret(secretPath, secretName); + String result = keyVaultService.getSecretFromPath(secretPath, secretName); assertThat(result).isEqualTo(value); } @@ -91,7 +91,7 @@ public void getSecretRetrievesValueForSpecifiedKeyAtSpecifiedPath() { @Test public void setSecretCallsVaultTemplate() { Map secretData = Collections.singletonMap(secretName, "value"); - keyVaultService.setHashicorpSecret(secretPath, secretData); + keyVaultService.setSecretAtPath(secretPath, secretData); verify(vaultTemplate, times(1)).write(secretPath, secretData); } diff --git a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultService.java b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultService.java index 9db8b313f4..fde0ddac9e 100644 --- a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultService.java +++ b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultService.java @@ -1,8 +1,14 @@ package com.quorum.tessera.key.vault; +import java.util.Map; + public interface KeyVaultService { String getSecret(String secretName); + String getSecretFromPath(String secretPath, String secretName); + Object setSecret(String secretName, String secret); + Object setSecretAtPath(String secretPath, Map secretData); + } diff --git a/pom.xml b/pom.xml index 5d0e0be5da..5363bed219 100644 --- a/pom.xml +++ b/pom.xml @@ -422,6 +422,12 @@ 0.8-SNAPSHOT + + com.quorum.tessera + hashicorp-key-vault + 0.8-SNAPSHOT + + com.quorum.tessera key-generation diff --git a/tessera-core/pom.xml b/tessera-core/pom.xml index 15b953dd2d..eba2ad5375 100644 --- a/tessera-core/pom.xml +++ b/tessera-core/pom.xml @@ -50,6 +50,12 @@ runtime + + com.quorum.tessera + hashicorp-key-vault + runtime + + javax.transaction javax.transaction-api From e37f6d945f4308831d5fbd485844d42c43400221 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Wed, 28 Nov 2018 16:15:36 +0000 Subject: [PATCH 10/57] Add vault config validation tests and update other tests --- .../tessera/config/cli/OverrideUtilTest.java | 1 + .../KeyVaultConfigurationValidatorTest.java | 195 +++++++++++++++++- .../AzureKeyVaultServiceFactoryTest.java | 4 +- .../vault/azure/AzureKeyVaultServiceTest.java | 22 ++ .../HashicorpKeyVaultServiceTest.java | 2 - 5 files changed, 210 insertions(+), 14 deletions(-) diff --git a/config-cli/src/test/java/com/quorum/tessera/config/cli/OverrideUtilTest.java b/config-cli/src/test/java/com/quorum/tessera/config/cli/OverrideUtilTest.java index 2ca3756b03..3ff1c7cd90 100644 --- a/config-cli/src/test/java/com/quorum/tessera/config/cli/OverrideUtilTest.java +++ b/config-cli/src/test/java/com/quorum/tessera/config/cli/OverrideUtilTest.java @@ -41,6 +41,7 @@ public void buildOptions() { "keys.keyData.config.data.aopts.parallelism", "keys.keyData.privateKeyPath", "keys.azureKeyVaultConfig.url", + "keys.hashicorpKeyVaultConfig.url", "alwaysSendTo", "unixSocketFile", "useWhiteList", diff --git a/config/src/test/java/com/quorum/tessera/config/constraints/KeyVaultConfigurationValidatorTest.java b/config/src/test/java/com/quorum/tessera/config/constraints/KeyVaultConfigurationValidatorTest.java index 4b8622df80..32a4987616 100644 --- a/config/src/test/java/com/quorum/tessera/config/constraints/KeyVaultConfigurationValidatorTest.java +++ b/config/src/test/java/com/quorum/tessera/config/constraints/KeyVaultConfigurationValidatorTest.java @@ -1,6 +1,7 @@ package com.quorum.tessera.config.constraints; import com.quorum.tessera.config.AzureKeyVaultConfig; +import com.quorum.tessera.config.HashicorpKeyVaultConfig; import com.quorum.tessera.config.KeyConfiguration; import com.quorum.tessera.config.keypairs.*; import org.junit.Before; @@ -14,7 +15,9 @@ import java.util.List; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class KeyVaultConfigurationValidatorTest { @@ -28,6 +31,9 @@ public class KeyVaultConfigurationValidatorTest { public void setUp() { context = mock(ConstraintValidatorContext.class); + ConstraintValidatorContext.ConstraintViolationBuilder builder = mock(ConstraintValidatorContext.ConstraintViolationBuilder.class); + when(context.buildConstraintViolationWithTemplate(any(String.class))).thenReturn(builder); + validator = new KeyVaultConfigurationValidator(); ValidKeyVaultConfiguration validKeyVaultConfiguration = mock(ValidKeyVaultConfiguration.class); validator.initialize(validKeyVaultConfiguration); @@ -39,7 +45,7 @@ public void nullKeyConfigurationIsAllowedAndWillBePickedUpByNotNullAnnotation() } @Test - public void keyVaultConfigWithVaultKeyPairIsValid() { + public void azureConfigWithAzureKeyPairIsValid() { KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); AzureVaultKeyPair keyPair = mock(AzureVaultKeyPair.class); AzureKeyVaultConfig keyVaultConfig = mock(AzureKeyVaultConfig.class); @@ -51,7 +57,7 @@ public void keyVaultConfigWithVaultKeyPairIsValid() { } @Test - public void keyVaultConfigWithMultipleVaultKeyPairTypesIsValid() { + public void azureConfigWithMultipleAzureKeyPairsIsValid() { List keyPairs = new ArrayList<>(); keyPairs.add(mock(AzureVaultKeyPair.class)); keyPairs.add(mock(AzureVaultKeyPair.class)); @@ -66,7 +72,7 @@ public void keyVaultConfigWithMultipleVaultKeyPairTypesIsValid() { } @Test - public void keyVaultConfigWithMultipleKeyPairTypesIncludingVaultIsValid() { + public void azureConfigWithMultipleKeyPairTypesIncludingAzureIsValid() { List keyPairs = new ArrayList<>(); keyPairs.add(mock(AzureVaultKeyPair.class)); keyPairs.add(mock(DirectKeyPair.class)); @@ -81,7 +87,7 @@ public void keyVaultConfigWithMultipleKeyPairTypesIncludingVaultIsValid() { } @Test - public void keyVaultConfigWithNonVaultKeyPairIsValid() { + public void azureConfigWithNonAzureKeyPairIsValid() { KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); DirectKeyPair keyPair = mock(DirectKeyPair.class); AzureKeyVaultConfig keyVaultConfig = mock(AzureKeyVaultConfig.class); @@ -93,7 +99,7 @@ public void keyVaultConfigWithNonVaultKeyPairIsValid() { } @Test - public void keyVaultConfigWithMultipleNonVaultKeyPairsIsValid() { + public void azureConfigWithMultipleNonAzureKeyPairsIsValid() { List keyPairs = new ArrayList<>(); keyPairs.add(mock(DirectKeyPair.class)); keyPairs.add(mock(InlineKeypair.class)); @@ -108,7 +114,7 @@ public void keyVaultConfigWithMultipleNonVaultKeyPairsIsValid() { } @Test - public void noKeyVaultConfigWithVaultKeyPairIsInvalid() { + public void noAzureConfigWithAzureKeyPairIsInvalid() { KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); AzureVaultKeyPair keyPair = mock(AzureVaultKeyPair.class); @@ -116,10 +122,11 @@ public void noKeyVaultConfigWithVaultKeyPairIsInvalid() { when(keyConfiguration.getAzureKeyVaultConfig()).thenReturn(null); assertThat(validator.isValid(keyConfiguration, context)).isFalse(); + verify(context).buildConstraintViolationWithTemplate("{ValidKeyVaultConfiguration.azure.message}"); } @Test - public void noKeyVaultConfigWithMultipleVaultKeyPairsIsInvalid() { + public void noAzureConfigWithMultipleAzureKeyPairsIsInvalid() { KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); List keyPairs = new ArrayList<>(); @@ -130,10 +137,11 @@ public void noKeyVaultConfigWithMultipleVaultKeyPairsIsInvalid() { when(keyConfiguration.getAzureKeyVaultConfig()).thenReturn(null); assertThat(validator.isValid(keyConfiguration, context)).isFalse(); + verify(context).buildConstraintViolationWithTemplate("{ValidKeyVaultConfiguration.azure.message}"); } @Test - public void noKeyVaultConfigWithMultipleKeyPairsIncludingVaultIsInvalid() { + public void noAzureConfigWithMultipleKeyPairsIncludingAzureIsInvalid() { KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); List keyPairs = new ArrayList<>(); @@ -144,10 +152,11 @@ public void noKeyVaultConfigWithMultipleKeyPairsIncludingVaultIsInvalid() { when(keyConfiguration.getAzureKeyVaultConfig()).thenReturn(null); assertThat(validator.isValid(keyConfiguration, context)).isFalse(); + verify(context).buildConstraintViolationWithTemplate("{ValidKeyVaultConfiguration.azure.message}"); } @Test - public void noKeyVaultConfigWithNonVaultKeyPairIsValid() { + public void noAzureConfigWithNonAzureKeyPairIsValid() { KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); DirectKeyPair keyPair = mock(DirectKeyPair.class); @@ -158,7 +167,7 @@ public void noKeyVaultConfigWithNonVaultKeyPairIsValid() { } @Test - public void noKeyVaultConfigWithMultipleNonVaultKeyPairsIsValid() { + public void noAzureConfigWithMultipleNonAzureKeyPairsIsValid() { KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); List keyPairs = new ArrayList<>(); @@ -171,4 +180,170 @@ public void noKeyVaultConfigWithMultipleNonVaultKeyPairsIsValid() { assertThat(validator.isValid(keyConfiguration, context)).isTrue(); } + @Test + public void hashicorpConfigWithHashicorpKeyPairIsValid() { + KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); + HashicorpVaultKeyPair keyPair = mock(HashicorpVaultKeyPair.class); + HashicorpKeyVaultConfig keyVaultConfig = mock(HashicorpKeyVaultConfig.class); + + when(keyConfiguration.getKeyData()).thenReturn(Collections.singletonList(keyPair)); + when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(keyVaultConfig); + + assertThat(validator.isValid(keyConfiguration, context)).isTrue(); + } + + @Test + public void hashicorpConfigWithMultipleHashicorpKeyPairsIsValid() { + List keyPairs = new ArrayList<>(); + keyPairs.add(mock(HashicorpVaultKeyPair.class)); + keyPairs.add(mock(HashicorpVaultKeyPair.class)); + + KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); + HashicorpKeyVaultConfig keyVaultConfig = mock(HashicorpKeyVaultConfig.class); + + when(keyConfiguration.getKeyData()).thenReturn(keyPairs); + when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(keyVaultConfig); + + assertThat(validator.isValid(keyConfiguration, context)).isTrue(); + } + + @Test + public void hashicorpConfigWithMultipleKeyPairTypesIncludingHashicorpIsValid() { + List keyPairs = new ArrayList<>(); + keyPairs.add(mock(HashicorpVaultKeyPair.class)); + keyPairs.add(mock(DirectKeyPair.class)); + + KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); + HashicorpKeyVaultConfig keyVaultConfig = mock(HashicorpKeyVaultConfig.class); + + when(keyConfiguration.getKeyData()).thenReturn(keyPairs); + when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(keyVaultConfig); + + assertThat(validator.isValid(keyConfiguration, context)).isTrue(); + } + + @Test + public void hashicorpConfigWithNonHashicorpKeyPairIsValid() { + KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); + DirectKeyPair keyPair = mock(DirectKeyPair.class); + HashicorpKeyVaultConfig keyVaultConfig = mock(HashicorpKeyVaultConfig.class); + + when(keyConfiguration.getKeyData()).thenReturn(Collections.singletonList(keyPair)); + when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(keyVaultConfig); + + assertThat(validator.isValid(keyConfiguration, context)).isTrue(); + } + + @Test + public void hashicorpConfigWithMultipleNonHashicorpKeyPairsIsValid() { + List keyPairs = new ArrayList<>(); + keyPairs.add(mock(DirectKeyPair.class)); + keyPairs.add(mock(InlineKeypair.class)); + + KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); + HashicorpKeyVaultConfig keyVaultConfig = mock(HashicorpKeyVaultConfig.class); + + when(keyConfiguration.getKeyData()).thenReturn(keyPairs); + when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(keyVaultConfig); + + assertThat(validator.isValid(keyConfiguration, context)).isTrue(); + } + + @Test + public void noHashicorpConfigWithHashicorpKeyPairIsInvalid() { + KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); + HashicorpVaultKeyPair keyPair = mock(HashicorpVaultKeyPair.class); + + when(keyConfiguration.getKeyData()).thenReturn(Collections.singletonList(keyPair)); + when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(null); + + assertThat(validator.isValid(keyConfiguration, context)).isFalse(); + verify(context).buildConstraintViolationWithTemplate("{ValidKeyVaultConfiguration.hashicorp.message}"); + } + + @Test + public void noHashicorpConfigWithMultipleHashicorpKeyPairsIsInvalid() { + KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); + List keyPairs = new ArrayList<>(); + + keyPairs.add(mock(HashicorpVaultKeyPair.class)); + keyPairs.add(mock(HashicorpVaultKeyPair.class)); + + when(keyConfiguration.getKeyData()).thenReturn(keyPairs); + when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(null); + + assertThat(validator.isValid(keyConfiguration, context)).isFalse(); + verify(context).buildConstraintViolationWithTemplate("{ValidKeyVaultConfiguration.hashicorp.message}"); + } + + @Test + public void noHashicorpConfigWithMultipleKeyPairsIncludingHashicorpIsInvalid() { + KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); + List keyPairs = new ArrayList<>(); + + keyPairs.add(mock(HashicorpVaultKeyPair.class)); + keyPairs.add(mock(DirectKeyPair.class)); + + when(keyConfiguration.getKeyData()).thenReturn(keyPairs); + when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(null); + + assertThat(validator.isValid(keyConfiguration, context)).isFalse(); + verify(context).buildConstraintViolationWithTemplate("{ValidKeyVaultConfiguration.hashicorp.message}"); + } + + @Test + public void noHashicorpConfigWithNonHashicorpKeyPairIsValid() { + KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); + DirectKeyPair keyPair = mock(DirectKeyPair.class); + + when(keyConfiguration.getKeyData()).thenReturn(Collections.singletonList(keyPair)); + when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(null); + + assertThat(validator.isValid(keyConfiguration, context)).isTrue(); + } + + @Test + public void noHashicorpConfigWithMultipleNonHashicorpKeyPairsIsValid() { + KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); + List keyPairs = new ArrayList<>(); + + keyPairs.add(mock(DirectKeyPair.class)); + keyPairs.add(mock(FilesystemKeyPair.class)); + + when(keyConfiguration.getKeyData()).thenReturn(keyPairs); + when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(null); + + assertThat(validator.isValid(keyConfiguration, context)).isTrue(); + } + + @Test + public void azureConfigWithHashicorpKeyPairIsInvalid() { + KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); + List keyPairs = new ArrayList<>(); + + keyPairs.add(mock(HashicorpVaultKeyPair.class)); + + when(keyConfiguration.getKeyData()).thenReturn(keyPairs); + AzureKeyVaultConfig azureConfig = mock(AzureKeyVaultConfig.class); + when(keyConfiguration.getAzureKeyVaultConfig()).thenReturn(azureConfig); + + assertThat(validator.isValid(keyConfiguration, context)).isFalse(); + verify(context).buildConstraintViolationWithTemplate("{ValidKeyVaultConfiguration.hashicorp.message}"); + } + + @Test + public void hashicorpConfigWithAzureKeyPairIsInvalid() { + KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); + List keyPairs = new ArrayList<>(); + + keyPairs.add(mock(AzureVaultKeyPair.class)); + + when(keyConfiguration.getKeyData()).thenReturn(keyPairs); + HashicorpKeyVaultConfig hashicorpConfig = mock(HashicorpKeyVaultConfig.class); + when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(hashicorpConfig); + + assertThat(validator.isValid(keyConfiguration, context)).isFalse(); + verify(context).buildConstraintViolationWithTemplate("{ValidKeyVaultConfiguration.azure.message}"); + } + } diff --git a/key-vault/azure-key-vault/src/test/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactoryTest.java b/key-vault/azure-key-vault/src/test/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactoryTest.java index 24bca04ab2..6f9f4da2cb 100644 --- a/key-vault/azure-key-vault/src/test/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactoryTest.java +++ b/key-vault/azure-key-vault/src/test/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactoryTest.java @@ -78,7 +78,7 @@ public void nullKeyConfigurationThrowsException() { Throwable ex = catchThrowable(() -> azureKeyVaultServiceFactory.create(config, envProvider)); assertThat(ex).isExactlyInstanceOf(ConfigException.class); - assertThat(ex.getMessage()).contains("Trying to create Azure key vault but no Azure configuration provided"); + assertThat(ex.getMessage()).contains("Trying to create Azure key vault connection but no Azure configuration provided"); } @Test @@ -91,7 +91,7 @@ public void nullKeyVaultConfigurationThrowsException() { Throwable ex = catchThrowable(() -> azureKeyVaultServiceFactory.create(config, envProvider)); assertThat(ex).isExactlyInstanceOf(ConfigException.class); - assertThat(ex.getMessage()).contains("Trying to create Azure key vault but no Azure configuration provided"); + assertThat(ex.getMessage()).contains("Trying to create Azure key vault connection but no Azure configuration provided"); } @Test diff --git a/key-vault/azure-key-vault/src/test/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceTest.java b/key-vault/azure-key-vault/src/test/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceTest.java index 0d32628839..8024003ae9 100644 --- a/key-vault/azure-key-vault/src/test/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceTest.java +++ b/key-vault/azure-key-vault/src/test/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceTest.java @@ -8,6 +8,8 @@ import org.junit.Test; import org.mockito.ArgumentCaptor; +import java.util.Collections; + import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; import static org.mockito.Mockito.*; @@ -81,4 +83,24 @@ public void setSecretRequestIsUsedToRetrieveSecretFromVault() { assertThat(argument.getValue()).isEqualToComparingFieldByField(expected); } + + @Test + public void getSecretFromPathDoesNotInteractWithAzureClient() { + AzureKeyVaultConfig keyVaultConfig = mock(AzureKeyVaultConfig.class); + + AzureKeyVaultService azureKeyVaultService = new AzureKeyVaultService(keyVaultConfig, azureKeyVaultClientDelegate); + + assertThat(azureKeyVaultService.getSecretFromPath("secretPath", "secretName")).isNull(); + verifyZeroInteractions(azureKeyVaultClientDelegate); + } + + @Test + public void setSecretAtPathReturnsNull() { + AzureKeyVaultConfig keyVaultConfig = mock(AzureKeyVaultConfig.class); + + AzureKeyVaultService azureKeyVaultService = new AzureKeyVaultService(keyVaultConfig, azureKeyVaultClientDelegate); + + assertThat(azureKeyVaultService.setSecretAtPath("secretPath", Collections.singletonMap("secretName", "secretValue"))).isNull(); + verifyZeroInteractions(azureKeyVaultClientDelegate); + } } diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java index 4f35271b33..44226f50e5 100644 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java @@ -4,12 +4,10 @@ import com.quorum.tessera.key.vault.VaultSecretNotFoundException; import org.junit.Before; import org.junit.Test; -import org.mockito.verification.VerificationMode; import org.springframework.vault.core.VaultTemplate; import org.springframework.vault.support.VaultResponse; import java.util.Collections; -import java.util.HashMap; import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; From 7bae62b6120a58a2c1f93f3a2c3d5d44d3d21ec7 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Thu, 29 Nov 2018 09:53:13 +0000 Subject: [PATCH 11/57] Add ability to save generated keys in a Hashicorp Vault --- .../cli/parsers/KeyGenerationParser.java | 35 ++++++++---- .../cli/parsers/KeyGenerationParserTest.java | 56 +++++++++++++++++- .../DefaultKeyGeneratorFactory.java | 26 ++++++--- .../HashicorpVaultKeyGenerator.java | 46 +++++++++++++++ .../HashicorpVaultKeyGeneratorTest.java | 57 +++++++++++++++++++ 5 files changed, 200 insertions(+), 20 deletions(-) create mode 100644 key-generation/src/main/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGenerator.java create mode 100644 key-generation/src/test/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGeneratorTest.java diff --git a/config-cli/src/main/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParser.java b/config-cli/src/main/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParser.java index a69726f9b7..27920b61a4 100644 --- a/config-cli/src/main/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParser.java +++ b/config-cli/src/main/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParser.java @@ -1,9 +1,6 @@ package com.quorum.tessera.config.cli.parsers; -import com.quorum.tessera.config.ArgonOptions; -import com.quorum.tessera.config.AzureKeyVaultConfig; -import com.quorum.tessera.config.KeyVaultConfig; -import com.quorum.tessera.config.KeyVaultType; +import com.quorum.tessera.config.*; import com.quorum.tessera.config.cli.CliException; import com.quorum.tessera.config.keypairs.ConfigKeyPair; import com.quorum.tessera.config.util.JaxbUtil; @@ -88,23 +85,39 @@ private Optional keyVaultConfig(CommandLine commandLine) { return Optional.empty(); } - String t = commandLine.getOptionValue("keygenvaulttype"); + final String t = commandLine.getOptionValue("keygenvaulttype"); + KeyVaultType keyVaultType; try { - KeyVaultType.valueOf(t); + keyVaultType = KeyVaultType.valueOf(t); } catch(IllegalArgumentException | NullPointerException e) { throw new CliException("Key vault type either not provided or not recognised. Ensure provided value is UPPERCASE and has no leading or trailing whitespace characters"); } String keyVaultUrl = commandLine.getOptionValue("keygenvaulturl"); - //Only Azure supported atm so no need to check keyvaulttype - KeyVaultConfig keyVaultConfig = new AzureKeyVaultConfig(keyVaultUrl); + KeyVaultConfig keyVaultConfig; - Set> violations = validator.validate((AzureKeyVaultConfig)keyVaultConfig); + if(keyVaultType.equals(KeyVaultType.AZURE)) { + keyVaultConfig = new AzureKeyVaultConfig(keyVaultUrl); - if(!violations.isEmpty()) { - throw new ConstraintViolationException(violations); + Set> violations = validator.validate((AzureKeyVaultConfig)keyVaultConfig); + + if(!violations.isEmpty()) { + throw new ConstraintViolationException(violations); + } + } else { + if(!commandLine.hasOption("filename")) { + throw new CliException("At least one -filename must be provided when saving generated keys in a Hashicorp Vault"); + } + + keyVaultConfig = new HashicorpKeyVaultConfig(keyVaultUrl); + + Set> violations = validator.validate((HashicorpKeyVaultConfig)keyVaultConfig); + + if(!violations.isEmpty()) { + throw new ConstraintViolationException(violations); + } } return Optional.of(keyVaultConfig); diff --git a/config-cli/src/test/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParserTest.java b/config-cli/src/test/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParserTest.java index 00831e9dce..df9466abc8 100644 --- a/config-cli/src/test/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParserTest.java +++ b/config-cli/src/test/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParserTest.java @@ -135,7 +135,7 @@ public void ifAllVaultOptionsProvidedAndValidThenOkay() throws Exception { } @Test - public void ifOnlyValidVaultTypeOptionProvidedThenValidationException() { + public void ifAzureVaultTypeOptionProvidedButNoVaultUrlThenValidationException() { when(commandLine.hasOption("keygenvaulttype")).thenReturn(true); when(commandLine.hasOption("keygenvaulturl")).thenReturn(false); when(commandLine.getOptionValue("keygenvaulttype")).thenReturn("AZURE"); @@ -157,6 +157,31 @@ public void ifOnlyValidVaultTypeOptionProvidedThenValidationException() { assertThat(violation.getMessage()).isEqualTo("may not be null"); } + @Test + public void ifHashicorpVaultTypeOptionAndFilenameProvidedButNoVaultUrlThenValidationException() { + when(commandLine.hasOption("keygenvaulttype")).thenReturn(true); + when(commandLine.hasOption("keygenvaulturl")).thenReturn(false); + when(commandLine.getOptionValue("keygenvaulttype")).thenReturn("HASHICORP"); + when(commandLine.hasOption("filename")).thenReturn(true); + when(commandLine.getOptionValue("filename")).thenReturn("secret/path"); + + Throwable ex = catchThrowable(() -> this.parser.parse(commandLine)); + + verify(commandLine, times(1)).getOptionValue("keygenvaulttype"); + verify(commandLine, times(1)).getOptionValue("keygenvaulturl"); + + assertThat(ex).isInstanceOf(ConstraintViolationException.class); + + Set> violations = ((ConstraintViolationException) ex).getConstraintViolations(); + + assertThat(violations.size()).isEqualTo(1); + + ConstraintViolation violation = violations.iterator().next(); + + assertThat(violation.getPropertyPath().toString()).isEqualTo("url"); + assertThat(violation.getMessage()).isEqualTo("may not be null"); + } + @Test public void ifOnlyVaultUrlOptionProvidedThenException() { when(commandLine.hasOption("keygenvaulttype")).thenReturn(false); @@ -181,4 +206,33 @@ public void ifAllVaultOptionsProvidedButTypeUnknownThenException() { assertThat(ex.getMessage()).isEqualTo("Key vault type either not provided or not recognised. Ensure provided value is UPPERCASE and has no leading or trailing whitespace characters"); } + @Test + public void noFilenameProvidedWhenUsingHashicorpVaultThrowsException() { + when(commandLine.hasOption("keygenvaulttype")).thenReturn(true); + when(commandLine.hasOption("keygenvaulturl")).thenReturn(true); + when(commandLine.getOptionValue("keygenvaulttype")).thenReturn("HASHICORP"); + when(commandLine.hasOption("filename")).thenReturn(false); + + Throwable ex = catchThrowable(() -> this.parser.parse(commandLine)); + + assertThat(ex).isInstanceOf(CliException.class); + assertThat(ex.getMessage()).isEqualTo("At least one -filename must be provided when saving generated keys in a Hashicorp Vault"); + } + + @Test + public void ifAllVaultOptionsAndFilenameProvidedForHashicorpThenOkay() throws Exception { + when(commandLine.hasOption("keygenvaulttype")).thenReturn(true); + when(commandLine.hasOption("keygenvaulturl")).thenReturn(true); + when(commandLine.hasOption("filename")).thenReturn(true); + when(commandLine.getOptionValue("keygenvaulturl")).thenReturn("someurl"); + when(commandLine.getOptionValue("keygenvaulttype")).thenReturn("HASHICORP"); + when(commandLine.getOptionValue("filename")).thenReturn("secret/path"); + + this.parser.parse(commandLine); + + verify(commandLine, times(1)).getOptionValue("keygenvaulttype"); + verify(commandLine, times(1)).getOptionValue("keygenvaulttype"); + verify(commandLine, times(1)).getOptionValue("keygenvaulturl"); + } + } diff --git a/key-generation/src/main/java/com/quorum/tessera/key/generation/DefaultKeyGeneratorFactory.java b/key-generation/src/main/java/com/quorum/tessera/key/generation/DefaultKeyGeneratorFactory.java index bb00a1721a..9b738e0197 100644 --- a/key-generation/src/main/java/com/quorum/tessera/key/generation/DefaultKeyGeneratorFactory.java +++ b/key-generation/src/main/java/com/quorum/tessera/key/generation/DefaultKeyGeneratorFactory.java @@ -1,9 +1,6 @@ package com.quorum.tessera.key.generation; -import com.quorum.tessera.config.AzureKeyVaultConfig; -import com.quorum.tessera.config.Config; -import com.quorum.tessera.config.KeyConfiguration; -import com.quorum.tessera.config.KeyVaultConfig; +import com.quorum.tessera.config.*; import com.quorum.tessera.config.keys.KeyEncryptorFactory; import com.quorum.tessera.config.util.EnvironmentVariableProvider; import com.quorum.tessera.config.util.PasswordReaderFactory; @@ -21,12 +18,25 @@ public KeyGenerator create(KeyVaultConfig keyVaultConfig) { final Config config = new Config(); final KeyConfiguration keyConfiguration = new KeyConfiguration(); - keyConfiguration.setAzureKeyVaultConfig((AzureKeyVaultConfig)keyVaultConfig); - config.setKeys(keyConfiguration); - final KeyVaultService keyVaultService = keyVaultServiceFactory.create(config, new EnvironmentVariableProvider()); + if(keyVaultConfig.getKeyVaultType().equals(KeyVaultType.AZURE)) { + keyConfiguration.setAzureKeyVaultConfig((AzureKeyVaultConfig) keyVaultConfig); - return new AzureVaultKeyGenerator(NaclFacadeFactory.newFactory().create(), keyVaultService); + config.setKeys(keyConfiguration); + + final KeyVaultService keyVaultService = keyVaultServiceFactory.create(config, new EnvironmentVariableProvider()); + + return new AzureVaultKeyGenerator(NaclFacadeFactory.newFactory().create(), keyVaultService); + + } else { + keyConfiguration.setHashicorpKeyVaultConfig((HashicorpKeyVaultConfig) keyVaultConfig); + + config.setKeys(keyConfiguration); + + final KeyVaultService keyVaultService = keyVaultServiceFactory.create(config, new EnvironmentVariableProvider()); + + return new HashicorpVaultKeyGenerator(NaclFacadeFactory.newFactory().create(), keyVaultService); + } } return new FileKeyGenerator( diff --git a/key-generation/src/main/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGenerator.java b/key-generation/src/main/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGenerator.java new file mode 100644 index 0000000000..f7c078cb02 --- /dev/null +++ b/key-generation/src/main/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGenerator.java @@ -0,0 +1,46 @@ +package com.quorum.tessera.key.generation; + +import com.quorum.tessera.config.ArgonOptions; +import com.quorum.tessera.config.keypairs.HashicorpVaultKeyPair; +import com.quorum.tessera.encryption.KeyPair; +import com.quorum.tessera.key.vault.KeyVaultService; +import com.quorum.tessera.nacl.NaclFacade; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +public class HashicorpVaultKeyGenerator implements KeyGenerator { + private static final Logger LOGGER = LoggerFactory.getLogger(HashicorpVaultKeyGenerator.class); + + private final NaclFacade nacl; + private final KeyVaultService keyVaultService; + + public HashicorpVaultKeyGenerator(final NaclFacade nacl, KeyVaultService keyVaultService) { + this.nacl = nacl; + this.keyVaultService = keyVaultService; + } + + @Override + public HashicorpVaultKeyPair generate(String filename, ArgonOptions encryptionOptions) { + Objects.requireNonNull(filename); + + final KeyPair keys = this.nacl.generateNewKeys(); + + String pubId = "publicKey"; + String privId = "privateKey"; + Map keyPairData = new HashMap<>(); + keyPairData.put(pubId, keys.getPublicKey().encodeToBase64()); + keyPairData.put(privId, keys.getPrivateKey().encodeToBase64()); + + keyVaultService.setSecretAtPath(filename, keyPairData); + LOGGER.debug("Key {} saved to vault with path {} and id {}", keyPairData.get(pubId), filename, pubId); + LOGGER.info("Key saved to vault with path {} and id {}", filename, pubId); + LOGGER.debug("Key {} saved to vault with path {} and id {}", keyPairData.get(privId), filename, privId); + LOGGER.info("Key saved to vault with path {} and id {}", filename, privId); + + return new HashicorpVaultKeyPair(pubId, privId, filename); + } +} diff --git a/key-generation/src/test/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGeneratorTest.java b/key-generation/src/test/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGeneratorTest.java new file mode 100644 index 0000000000..8b71663205 --- /dev/null +++ b/key-generation/src/test/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGeneratorTest.java @@ -0,0 +1,57 @@ +package com.quorum.tessera.key.generation; + +import com.quorum.tessera.encryption.KeyPair; +import com.quorum.tessera.encryption.PrivateKey; +import com.quorum.tessera.encryption.PublicKey; +import com.quorum.tessera.key.vault.KeyVaultService; +import com.quorum.tessera.nacl.NaclFacade; +import org.junit.Before; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.mockito.Mockito.*; + +public class HashicorpVaultKeyGeneratorTest { + + private final String pubStr = "public"; + private final String privStr = "private"; + private final PublicKey pub = PublicKey.from(pubStr.getBytes()); + private final PrivateKey priv = PrivateKey.from(privStr.getBytes()); + + private NaclFacade naclFacade; + private KeyVaultService keyVaultService; + private HashicorpVaultKeyGenerator hashicorpVaultKeyGenerator; + + @Before + public void setUp() { + this.naclFacade = mock(NaclFacade.class); + this.keyVaultService = mock(KeyVaultService.class); + + final KeyPair keyPair = new KeyPair(pub, priv); + when(naclFacade.generateNewKeys()).thenReturn(keyPair); + + this.hashicorpVaultKeyGenerator = new HashicorpVaultKeyGenerator(naclFacade, keyVaultService); + + } + + @Test(expected = NullPointerException.class) + public void nullFilenameThrowsException() { + hashicorpVaultKeyGenerator.generate(null, null); + } + + @Test + public void generatedKeyPairIsSavedToSpecifiedPathInVaultWithIds() { + String filename = "secret/path"; + + hashicorpVaultKeyGenerator.generate(filename, null); + + Map expectedData = new HashMap<>(); + expectedData.put("publicKey", pub.encodeToBase64()); + expectedData.put("privateKey", priv.encodeToBase64()); + + verify(keyVaultService).setSecretAtPath(filename, expectedData); + } + +} From d6778554e46d2b443b6233ec3f06b1f020d2feda Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Thu, 29 Nov 2018 10:10:14 +0000 Subject: [PATCH 12/57] Provide more descriptive message if error encountered writing secret --- .../vault/hashicorp/HashicorpKeyVaultService.java | 7 ++++++- .../hashicorp/HashicorpKeyVaultServiceTest.java | 12 ++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java index 3309569bca..2f4a6fa116 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java @@ -3,6 +3,7 @@ import com.quorum.tessera.config.HashicorpKeyVaultConfig; import com.quorum.tessera.key.vault.KeyVaultService; import com.quorum.tessera.key.vault.VaultSecretNotFoundException; +import org.springframework.vault.VaultException; import org.springframework.vault.core.VaultTemplate; import org.springframework.vault.support.VaultResponse; @@ -51,6 +52,10 @@ public String getSecretFromPath(String secretPath, String secretName) { @Override public Object setSecretAtPath(String secretPath, Map secretData) { - return vaultTemplate.write(secretPath, secretData); + try { + return vaultTemplate.write(secretPath, secretData); + } catch(VaultException e) { + throw new VaultSecretNotFoundException("Unable to write secret to path '" + secretPath + "' - " + e.getMessage() ); + } } } diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java index 44226f50e5..752a4e0a44 100644 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java @@ -4,6 +4,7 @@ import com.quorum.tessera.key.vault.VaultSecretNotFoundException; import org.junit.Before; import org.junit.Test; +import org.springframework.vault.VaultException; import org.springframework.vault.core.VaultTemplate; import org.springframework.vault.support.VaultResponse; @@ -94,6 +95,17 @@ public void setSecretCallsVaultTemplate() { verify(vaultTemplate, times(1)).write(secretPath, secretData); } + @Test + public void setSecretThrowsExceptionIfUnsuccessful() { + when(vaultTemplate.write(anyString(), anyMap())).thenThrow(new VaultException("new exception")); + + Throwable ex = catchThrowable(() -> keyVaultService.setSecretAtPath("secretpath", Collections.emptyMap())); + + assertThat(ex).isInstanceOf(VaultSecretNotFoundException.class); + assertThat(ex.getMessage()).isEqualTo("Unable to write secret to path 'secretpath' - new exception"); + + } + @Test //TODO Not ideal - see class todos public void getSecretAndSetSecretReturnNull() { From 832f062c03a95e89b63c2a03f30a4cc2737f6b62 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Thu, 29 Nov 2018 12:54:18 +0000 Subject: [PATCH 13/57] Add test for changes made to key generator factory --- .../generation/KeyGeneratorFactoryTest.java | 17 ++++++++++-- .../MockHashicorpKeyVaultServiceFactory.java | 27 +++++++++++++++++++ ...m.tessera.key.vault.KeyVaultServiceFactory | 3 ++- 3 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 key-generation/src/test/java/com/quorum/tessera/key/generation/MockHashicorpKeyVaultServiceFactory.java diff --git a/key-generation/src/test/java/com/quorum/tessera/key/generation/KeyGeneratorFactoryTest.java b/key-generation/src/test/java/com/quorum/tessera/key/generation/KeyGeneratorFactoryTest.java index 085f52ad06..54bc7fae77 100644 --- a/key-generation/src/test/java/com/quorum/tessera/key/generation/KeyGeneratorFactoryTest.java +++ b/key-generation/src/test/java/com/quorum/tessera/key/generation/KeyGeneratorFactoryTest.java @@ -1,6 +1,7 @@ package com.quorum.tessera.key.generation; import com.quorum.tessera.config.AzureKeyVaultConfig; +import com.quorum.tessera.config.HashicorpKeyVaultConfig; import com.quorum.tessera.config.util.EnvironmentVariableProvider; import org.junit.Test; @@ -22,8 +23,8 @@ public void fileKeyGeneratorWhenKeyVaultConfigNotProvided() { } @Test - public void azureVaultKeyGeneratorWhenKeyVaultConfigProvided() { - final AzureKeyVaultConfig keyVaultConfig = new AzureKeyVaultConfig("url"); + public void azureVaultKeyGeneratorWhenAzureConfigProvided() { + final AzureKeyVaultConfig keyVaultConfig = new AzureKeyVaultConfig(); final KeyGenerator keyGenerator = KeyGeneratorFactory.newFactory().create(keyVaultConfig); @@ -31,4 +32,16 @@ public void azureVaultKeyGeneratorWhenKeyVaultConfigProvided() { assertThat(keyGenerator).isExactlyInstanceOf(AzureVaultKeyGenerator.class); } + @Test + public void hashicorpVaultKeyGeneratorWhenHashicorpConfigProvided() { + final HashicorpKeyVaultConfig keyVaultConfig = new HashicorpKeyVaultConfig(); + + final KeyGenerator keyGenerator = KeyGeneratorFactory.newFactory().create(keyVaultConfig); + + assertThat(keyGenerator).isNotNull(); + assertThat(keyGenerator).isExactlyInstanceOf(HashicorpVaultKeyGenerator.class); + } + + + } diff --git a/key-generation/src/test/java/com/quorum/tessera/key/generation/MockHashicorpKeyVaultServiceFactory.java b/key-generation/src/test/java/com/quorum/tessera/key/generation/MockHashicorpKeyVaultServiceFactory.java new file mode 100644 index 0000000000..2bfc184f4e --- /dev/null +++ b/key-generation/src/test/java/com/quorum/tessera/key/generation/MockHashicorpKeyVaultServiceFactory.java @@ -0,0 +1,27 @@ +package com.quorum.tessera.key.generation; + +import com.quorum.tessera.config.Config; +import com.quorum.tessera.config.KeyVaultType; +import com.quorum.tessera.config.util.EnvironmentVariableProvider; +import com.quorum.tessera.key.vault.KeyVaultService; +import com.quorum.tessera.key.vault.KeyVaultServiceFactory; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class MockHashicorpKeyVaultServiceFactory implements KeyVaultServiceFactory { + @Override + public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider) { + KeyVaultService mock = mock(KeyVaultService.class); + when(mock.getSecret("pub")).thenReturn("publicSecret"); + when(mock.getSecret("priv")).thenReturn("privSecret"); + + return mock; + } + + @Override + public KeyVaultType getType() { + return KeyVaultType.HASHICORP; + } +} + diff --git a/key-generation/src/test/resources/META-INF/services/com.quorum.tessera.key.vault.KeyVaultServiceFactory b/key-generation/src/test/resources/META-INF/services/com.quorum.tessera.key.vault.KeyVaultServiceFactory index bf914724f3..ec7218aba2 100644 --- a/key-generation/src/test/resources/META-INF/services/com.quorum.tessera.key.vault.KeyVaultServiceFactory +++ b/key-generation/src/test/resources/META-INF/services/com.quorum.tessera.key.vault.KeyVaultServiceFactory @@ -1 +1,2 @@ -com.quorum.tessera.key.generation.MockAzureKeyVaultServiceFactory \ No newline at end of file +com.quorum.tessera.key.generation.MockAzureKeyVaultServiceFactory +com.quorum.tessera.key.generation.MockHashicorpKeyVaultServiceFactory \ No newline at end of file From 382139a59cd56adce9c3de796f63d877f0cb6cfe Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Fri, 30 Nov 2018 16:53:02 +0000 Subject: [PATCH 14/57] Change Vault SDK used and add support for TLS comms w/ Vault Switch to use vault-java-driver instead of spring-vault-core Add config item for TLS certificate path --- .../cli/parsers/KeyGenerationParser.java | 3 +- .../config/HashicorpKeyVaultConfig.java | 23 ++++++- .../util/EnvironmentVariableProvider.java | 1 + .../config/HashicorpKeyVaultConfigTest.java | 3 +- .../quorum/tessera/config/ValidationTest.java | 8 +-- .../HashicorpVaultKeyGenerator.java | 2 +- .../HashicorpVaultKeyGeneratorTest.java | 2 +- .../key/vault/azure/AzureKeyVaultService.java | 2 +- key-vault/hashicorp-key-vault/pom.xml | 6 ++ .../hashicorp/HashicorpKeyVaultService.java | 55 +++++++--------- .../HashicorpKeyVaultServiceFactory.java | 65 +++++++++++++------ .../HashicorpKeyVaultSpringService.java | 61 +++++++++++++++++ ...HashicorpKeyVaultSpringServiceFactory.java | 58 +++++++++++++++++ .../hashicorp/HashicorpVaultException.java | 14 ++++ .../key/vault/hashicorp/VaultCallback.java | 21 ++++++ ...corpKeyVaultSpringServiceFactoryTest.java} | 8 +-- ...> HashicorpKeyVaultSpringServiceTest.java} | 8 +-- .../tessera/key/vault/KeyVaultException.java | 13 ++++ .../tessera/key/vault/KeyVaultService.java | 2 +- 19 files changed, 284 insertions(+), 71 deletions(-) create mode 100644 key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringService.java create mode 100644 key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringServiceFactory.java create mode 100644 key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpVaultException.java create mode 100644 key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/VaultCallback.java rename key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/{HashicorpKeyVaultServiceFactoryTest.java => HashicorpKeyVaultSpringServiceFactoryTest.java} (94%) rename key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/{HashicorpKeyVaultServiceTest.java => HashicorpKeyVaultSpringServiceTest.java} (94%) create mode 100644 key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultException.java diff --git a/config-cli/src/main/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParser.java b/config-cli/src/main/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParser.java index 27920b61a4..ca5d63db0e 100644 --- a/config-cli/src/main/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParser.java +++ b/config-cli/src/main/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParser.java @@ -111,7 +111,8 @@ private Optional keyVaultConfig(CommandLine commandLine) { throw new CliException("At least one -filename must be provided when saving generated keys in a Hashicorp Vault"); } - keyVaultConfig = new HashicorpKeyVaultConfig(keyVaultUrl); + //TODO + keyVaultConfig = new HashicorpKeyVaultConfig(keyVaultUrl, null); Set> violations = validator.validate((HashicorpKeyVaultConfig)keyVaultConfig); diff --git a/config/src/main/java/com/quorum/tessera/config/HashicorpKeyVaultConfig.java b/config/src/main/java/com/quorum/tessera/config/HashicorpKeyVaultConfig.java index 65faeccafe..16c0afa404 100644 --- a/config/src/main/java/com/quorum/tessera/config/HashicorpKeyVaultConfig.java +++ b/config/src/main/java/com/quorum/tessera/config/HashicorpKeyVaultConfig.java @@ -1,8 +1,14 @@ package com.quorum.tessera.config; +import com.quorum.tessera.config.adapters.PathAdapter; +import com.quorum.tessera.config.constraints.ValidPath; + import javax.validation.Valid; import javax.validation.constraints.NotNull; import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import java.nio.file.Path; public class HashicorpKeyVaultConfig extends ConfigItem implements KeyVaultConfig { @@ -11,8 +17,15 @@ public class HashicorpKeyVaultConfig extends ConfigItem implements KeyVaultConfi @XmlAttribute private String url; - public HashicorpKeyVaultConfig(String url) { + @Valid + @ValidPath(checkExists = true, message = "File does not exist") + @XmlElement(type = String.class) + @XmlJavaTypeAdapter(PathAdapter.class) + private Path tlsCertificatePath; + + public HashicorpKeyVaultConfig(String url, Path tlsCertificatePath) { this.url = url; + this.tlsCertificatePath = tlsCertificatePath; } public HashicorpKeyVaultConfig() { @@ -26,6 +39,14 @@ public void setUrl(String url) { this.url = url; } + public Path getTlsCertificatePath() { + return tlsCertificatePath; + } + + public void setTlsCertificatePath(Path tlsCertificatePath) { + this.tlsCertificatePath = tlsCertificatePath; + } + @Override public KeyVaultType getKeyVaultType() { return KeyVaultType.HASHICORP; diff --git a/config/src/main/java/com/quorum/tessera/config/util/EnvironmentVariableProvider.java b/config/src/main/java/com/quorum/tessera/config/util/EnvironmentVariableProvider.java index bd90561327..019cce2ad2 100644 --- a/config/src/main/java/com/quorum/tessera/config/util/EnvironmentVariableProvider.java +++ b/config/src/main/java/com/quorum/tessera/config/util/EnvironmentVariableProvider.java @@ -7,4 +7,5 @@ public String getEnv(String name) { return System.getenv(name); } + } diff --git a/config/src/test/java/com/quorum/tessera/config/HashicorpKeyVaultConfigTest.java b/config/src/test/java/com/quorum/tessera/config/HashicorpKeyVaultConfigTest.java index 7246bc923a..e1436ac7e0 100644 --- a/config/src/test/java/com/quorum/tessera/config/HashicorpKeyVaultConfigTest.java +++ b/config/src/test/java/com/quorum/tessera/config/HashicorpKeyVaultConfigTest.java @@ -11,7 +11,8 @@ public class HashicorpKeyVaultConfigTest { @Before public void setUp() { - vaultConfig = new HashicorpKeyVaultConfig("url"); + vaultConfig = new HashicorpKeyVaultConfig(); + vaultConfig.setUrl("url"); } @Test diff --git a/config/src/test/java/com/quorum/tessera/config/ValidationTest.java b/config/src/test/java/com/quorum/tessera/config/ValidationTest.java index fbae046506..b0e0e6f310 100644 --- a/config/src/test/java/com/quorum/tessera/config/ValidationTest.java +++ b/config/src/test/java/com/quorum/tessera/config/ValidationTest.java @@ -13,9 +13,7 @@ import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; public class ValidationTest { @@ -383,7 +381,7 @@ public void azureVaultKeyPairProvidedButKeyVaultConfigHasNullUrlCreatesNotNullVi @Test public void hashicorpVaultConfigWithNoUrlCreatesNotNullViolation() { - HashicorpKeyVaultConfig keyVaultConfig = new HashicorpKeyVaultConfig(null); + HashicorpKeyVaultConfig keyVaultConfig = new HashicorpKeyVaultConfig(); Set> violations = validator.validate(keyVaultConfig); assertThat(violations).hasSize(1); @@ -395,7 +393,7 @@ public void hashicorpVaultConfigWithNoUrlCreatesNotNullViolation() { @Test public void hashicorpVaultKeyPairProvidedButKeyVaultConfigHasNullUrlCreatesNotNullViolation() { HashicorpVaultKeyPair keyPair = new HashicorpVaultKeyPair("pubId", "privId", "secretPath"); - HashicorpKeyVaultConfig keyVaultConfig = new HashicorpKeyVaultConfig(null); + HashicorpKeyVaultConfig keyVaultConfig = new HashicorpKeyVaultConfig(); KeyConfiguration keyConfiguration = new KeyConfiguration(null, null, singletonList(keyPair), null, keyVaultConfig); Set> violations = validator.validate(keyConfiguration); diff --git a/key-generation/src/main/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGenerator.java b/key-generation/src/main/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGenerator.java index f7c078cb02..f46f0ef834 100644 --- a/key-generation/src/main/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGenerator.java +++ b/key-generation/src/main/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGenerator.java @@ -31,7 +31,7 @@ public HashicorpVaultKeyPair generate(String filename, ArgonOptions encryptionOp String pubId = "publicKey"; String privId = "privateKey"; - Map keyPairData = new HashMap<>(); + Map keyPairData = new HashMap<>(); keyPairData.put(pubId, keys.getPublicKey().encodeToBase64()); keyPairData.put(privId, keys.getPrivateKey().encodeToBase64()); diff --git a/key-generation/src/test/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGeneratorTest.java b/key-generation/src/test/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGeneratorTest.java index 8b71663205..c5cfc8d377 100644 --- a/key-generation/src/test/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGeneratorTest.java +++ b/key-generation/src/test/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGeneratorTest.java @@ -47,7 +47,7 @@ public void generatedKeyPairIsSavedToSpecifiedPathInVaultWithIds() { hashicorpVaultKeyGenerator.generate(filename, null); - Map expectedData = new HashMap<>(); + Map expectedData = new HashMap<>(); expectedData.put("publicKey", pub.encodeToBase64()); expectedData.put("privateKey", priv.encodeToBase64()); diff --git a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultService.java b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultService.java index 35ffd6c0a8..90c0f722a2 100644 --- a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultService.java +++ b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultService.java @@ -44,7 +44,7 @@ public SecretBundle setSecret(String secretName, String secret) { } @Override - public Object setSecretAtPath(String secretPath, Map secretData) { + public Object setSecretAtPath(String secretPath, Map secretData) { return null; } } diff --git a/key-vault/hashicorp-key-vault/pom.xml b/key-vault/hashicorp-key-vault/pom.xml index a08d58ac95..5cec1d9a2b 100644 --- a/key-vault/hashicorp-key-vault/pom.xml +++ b/key-vault/hashicorp-key-vault/pom.xml @@ -17,6 +17,12 @@ spring-vault-core + + com.bettercloud + vault-java-driver + 3.1.0 + + com.quorum.tessera key-vault-api diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java index 2f4a6fa116..3debd5799c 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java @@ -1,24 +1,23 @@ package com.quorum.tessera.key.vault.hashicorp; +import com.bettercloud.vault.Vault; +import com.bettercloud.vault.VaultException; +import com.bettercloud.vault.response.LogicalResponse; import com.quorum.tessera.config.HashicorpKeyVaultConfig; import com.quorum.tessera.key.vault.KeyVaultService; -import com.quorum.tessera.key.vault.VaultSecretNotFoundException; -import org.springframework.vault.VaultException; -import org.springframework.vault.core.VaultTemplate; -import org.springframework.vault.support.VaultResponse; import java.util.Map; +import java.util.Optional; public class HashicorpKeyVaultService implements KeyVaultService { - //TODO Make use of methods from KeyVaultService + private final HashicorpKeyVaultConfig keyVaultConfig; - private String vaultUrl; - private VaultTemplate vaultTemplate; + private final Vault vault; - public HashicorpKeyVaultService(HashicorpKeyVaultConfig keyVaultConfig, VaultTemplate vaultTemplate) { - this.vaultUrl = keyVaultConfig.getUrl(); - this.vaultTemplate = vaultTemplate; + public HashicorpKeyVaultService(HashicorpKeyVaultConfig keyVaultConfig, Vault vault) { + this.keyVaultConfig = keyVaultConfig; + this.vault = vault; } @Override @@ -26,36 +25,32 @@ public String getSecret(String secretName) { return null; } - @Override - public Object setSecret(String secretName, String secret) { - return null; - } - @Override public String getSecretFromPath(String secretPath, String secretName) { - VaultResponse response = vaultTemplate.read(secretPath); - - if(response == null) { - throw new VaultSecretNotFoundException("Hashicorp Vault secret not found at path " + secretPath + " in vault " + vaultUrl); - } - - if(response.getData() == null) { - throw new VaultSecretNotFoundException("No data for Hashicorp Vault secret at path " + secretPath + " in vault " + vaultUrl); + LogicalResponse response; + try { + response = vault.logical().read(secretPath); + } catch(VaultException e) { + throw new HashicorpVaultException("Error getting secret " + secretName + " from path " + secretPath + " - " + e.getMessage()); } - if(response.getData().get(secretName) == null) { - throw new VaultSecretNotFoundException("Value for secret id " + secretName + " not found at path " + secretPath + " in vault " + vaultUrl); - } + return Optional.of(response) + .map(LogicalResponse::getData) + .map(data -> data.get(secretName)) + .orElseThrow(() -> new HashicorpVaultException("No secret " + secretName + " found at path " + secretPath)); + } - return response.getData().get(secretName).toString(); + @Override + public Object setSecret(String secretName, String secret) { + return null; } @Override - public Object setSecretAtPath(String secretPath, Map secretData) { + public Object setSecretAtPath(String secretPath, Map secretData) { try { - return vaultTemplate.write(secretPath, secretData); + return vault.logical().write(secretPath, secretData); } catch(VaultException e) { - throw new VaultSecretNotFoundException("Unable to write secret to path '" + secretPath + "' - " + e.getMessage() ); + throw new HashicorpVaultException("Error writing secret to path " + secretPath + " - " + e.getMessage()); } } } diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java index 91f4da2272..b719f31217 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java @@ -1,21 +1,21 @@ package com.quorum.tessera.key.vault.hashicorp; +import com.bettercloud.vault.SslConfig; +import com.bettercloud.vault.Vault; +import com.bettercloud.vault.VaultConfig; +import com.bettercloud.vault.response.AuthResponse; import com.quorum.tessera.config.*; import com.quorum.tessera.config.util.EnvironmentVariableProvider; import com.quorum.tessera.key.vault.KeyVaultService; import com.quorum.tessera.key.vault.KeyVaultServiceFactory; -import org.springframework.vault.authentication.ClientAuthentication; -import org.springframework.vault.authentication.TokenAuthentication; -import org.springframework.vault.client.VaultEndpoint; -import org.springframework.vault.core.VaultTemplate; -import java.net.URI; -import java.net.URISyntaxException; import java.util.Objects; import java.util.Optional; public class HashicorpKeyVaultServiceFactory implements KeyVaultServiceFactory { + private final String roleIdEnvVar = "HASHICORP_ROLE_ID"; + private final String secretIdEnvVar = "HASHICORP_SECRET_ID"; private final String authTokenEnvVar = "HASHICORP_TOKEN"; @Override @@ -23,36 +23,59 @@ public KeyVaultService create(Config config, EnvironmentVariableProvider envProv Objects.requireNonNull(config); Objects.requireNonNull(envProvider); + String roleId = envProvider.getEnv(roleIdEnvVar); + String secretId = envProvider.getEnv(secretIdEnvVar); String authToken = envProvider.getEnv(authTokenEnvVar); - if(authToken == null) { - throw new HashicorpCredentialNotSetException(authTokenEnvVar + " must be set"); + if(roleId == null && secretId == null && authToken == null) { + throw new HashicorpCredentialNotSetException("Environment variables must be set to authenticate with Hashicorp Vault. Set the " + roleIdEnvVar + " and " + secretIdEnvVar + " environment variables if using the AppRole authentication method. Set the " + authTokenEnvVar + " if using another authentication method."); + } + else if(isOnlyOneInputNull(roleId, secretId)) { + throw new HashicorpCredentialNotSetException("Only one of the " + roleIdEnvVar + " and " + secretIdEnvVar + " environment variables to authenticate with Hashicorp Vault using the AppRole method has been set"); } HashicorpKeyVaultConfig keyVaultConfig = Optional.ofNullable(config.getKeys()) .map(KeyConfiguration::getHashicorpKeyVaultConfig) .orElseThrow(() -> new ConfigException(new RuntimeException("Trying to create Hashicorp Vault connection but no Vault configuration provided"))); - VaultEndpoint vaultEndpoint; - try { - vaultEndpoint = VaultEndpoint.from(new URI(keyVaultConfig.getUrl())); - } catch (URISyntaxException | IllegalArgumentException e) { - throw new ConfigException(new Throwable("Provided Hashicorp Vault url is incorrectly formatted", e)); + VaultConfig vaultConfig = new VaultConfig() + .address(keyVaultConfig.getUrl()); + + if(keyVaultConfig.getTlsCertificatePath() != null) { + SslConfig vaultSslConfig = new SslConfig(); + VaultCallback.execute( + () -> vaultSslConfig + .pemFile(keyVaultConfig.getTlsCertificatePath().toFile()) + .build() + ); + + vaultConfig.sslConfig(vaultSslConfig); + } + + VaultCallback.execute(() -> vaultConfig.build()); + + final Vault vault = new Vault(vaultConfig); + + String token; + + if(roleId != null && secretId != null) { + AuthResponse loginResponse = VaultCallback.execute(() -> vault.auth().loginByAppRole("approle", roleId, secretId)); + token = loginResponse.getAuthClientToken(); + } else { + token = authToken; } - ClientAuthentication clientAuthentication = new TokenAuthentication(authToken); + vaultConfig.token(token); - return new HashicorpKeyVaultService( - keyVaultConfig, - new VaultTemplate( - vaultEndpoint, - clientAuthentication - ) - ); + return new HashicorpKeyVaultService(keyVaultConfig, vault); } @Override public KeyVaultType getType() { return KeyVaultType.HASHICORP; } + + private boolean isOnlyOneInputNull(Object obj1, Object obj2) { + return Objects.isNull(obj1) ^ Objects.isNull(obj2); + } } diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringService.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringService.java new file mode 100644 index 0000000000..7253bc6100 --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringService.java @@ -0,0 +1,61 @@ +package com.quorum.tessera.key.vault.hashicorp; + +import com.quorum.tessera.config.HashicorpKeyVaultConfig; +import com.quorum.tessera.key.vault.KeyVaultService; +import com.quorum.tessera.key.vault.VaultSecretNotFoundException; +import org.springframework.vault.VaultException; +import org.springframework.vault.core.VaultTemplate; +import org.springframework.vault.support.VaultResponse; + +import java.util.Map; + +public class HashicorpKeyVaultSpringService implements KeyVaultService { + + //TODO Make use of methods from KeyVaultService + + private String vaultUrl; + private VaultTemplate vaultTemplate; + + public HashicorpKeyVaultSpringService(HashicorpKeyVaultConfig keyVaultConfig, VaultTemplate vaultTemplate) { + this.vaultUrl = keyVaultConfig.getUrl(); + this.vaultTemplate = vaultTemplate; + } + + @Override + public String getSecret(String secretName) { + return null; + } + + @Override + public Object setSecret(String secretName, String secret) { + return null; + } + + @Override + public String getSecretFromPath(String secretPath, String secretName) { + VaultResponse response = vaultTemplate.read(secretPath); + + if(response == null) { + throw new VaultSecretNotFoundException("Hashicorp Vault secret not found at path " + secretPath + " in vault " + vaultUrl); + } + + if(response.getData() == null) { + throw new VaultSecretNotFoundException("No data for Hashicorp Vault secret at path " + secretPath + " in vault " + vaultUrl); + } + + if(response.getData().get(secretName) == null) { + throw new VaultSecretNotFoundException("Value for secret id " + secretName + " not found at path " + secretPath + " in vault " + vaultUrl); + } + + return response.getData().get(secretName).toString(); + } + + @Override + public Object setSecretAtPath(String secretPath, Map secretData) { + try { + return vaultTemplate.write(secretPath, secretData); + } catch(VaultException e) { + throw new VaultSecretNotFoundException("Unable to write secret to path '" + secretPath + "' - " + e.getMessage() ); + } + } +} diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringServiceFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringServiceFactory.java new file mode 100644 index 0000000000..2a8aaff5cc --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringServiceFactory.java @@ -0,0 +1,58 @@ +package com.quorum.tessera.key.vault.hashicorp; + +import com.quorum.tessera.config.*; +import com.quorum.tessera.config.util.EnvironmentVariableProvider; +import com.quorum.tessera.key.vault.KeyVaultService; +import com.quorum.tessera.key.vault.KeyVaultServiceFactory; +import org.springframework.vault.authentication.ClientAuthentication; +import org.springframework.vault.authentication.TokenAuthentication; +import org.springframework.vault.client.VaultEndpoint; +import org.springframework.vault.core.VaultTemplate; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Objects; +import java.util.Optional; + +public class HashicorpKeyVaultSpringServiceFactory implements KeyVaultServiceFactory { + + private final String authTokenEnvVar = "HASHICORP_TOKEN"; + + @Override + public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider) { + Objects.requireNonNull(config); + Objects.requireNonNull(envProvider); + + String authToken = envProvider.getEnv(authTokenEnvVar); + + if(authToken == null) { + throw new HashicorpCredentialNotSetException(authTokenEnvVar + " must be set"); + } + + HashicorpKeyVaultConfig keyVaultConfig = Optional.ofNullable(config.getKeys()) + .map(KeyConfiguration::getHashicorpKeyVaultConfig) + .orElseThrow(() -> new ConfigException(new RuntimeException("Trying to create Hashicorp Vault connection but no Vault configuration provided"))); + + VaultEndpoint vaultEndpoint; + try { + vaultEndpoint = VaultEndpoint.from(new URI(keyVaultConfig.getUrl())); + } catch (URISyntaxException | IllegalArgumentException e) { + throw new ConfigException(new Throwable("Provided Hashicorp Vault url is incorrectly formatted", e)); + } + + ClientAuthentication clientAuthentication = new TokenAuthentication(authToken); + + return new HashicorpKeyVaultSpringService( + keyVaultConfig, + new VaultTemplate( + vaultEndpoint, + clientAuthentication + ) + ); + } + + @Override + public KeyVaultType getType() { + return KeyVaultType.HASHICORP; + } +} diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpVaultException.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpVaultException.java new file mode 100644 index 0000000000..b7ad310fe5 --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpVaultException.java @@ -0,0 +1,14 @@ +package com.quorum.tessera.key.vault.hashicorp; + +import com.quorum.tessera.key.vault.KeyVaultException; + +public class HashicorpVaultException extends KeyVaultException { + + public HashicorpVaultException(Throwable cause) { + super(cause); + } + + public HashicorpVaultException(String message) { + super(message); + } +} diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/VaultCallback.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/VaultCallback.java new file mode 100644 index 0000000000..a9fecfdb3d --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/VaultCallback.java @@ -0,0 +1,21 @@ +package com.quorum.tessera.key.vault.hashicorp; + +import com.bettercloud.vault.VaultException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@FunctionalInterface +public interface VaultCallback { + Logger LOGGER = LoggerFactory.getLogger(VaultCallback.class); + + T doExecute() throws VaultException; + + static T execute(VaultCallback callback) { + try { + return callback.doExecute(); + } catch (final VaultException ex) { + LOGGER.debug(null, ex); + throw new HashicorpVaultException(ex); + } + } +} diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringServiceFactoryTest.java similarity index 94% rename from key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java rename to key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringServiceFactoryTest.java index 4bd9da0f60..0e39097f7c 100644 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringServiceFactoryTest.java @@ -12,9 +12,9 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -public class HashicorpKeyVaultServiceFactoryTest { +public class HashicorpKeyVaultSpringServiceFactoryTest { - private HashicorpKeyVaultServiceFactory factory; + private HashicorpKeyVaultSpringServiceFactory factory; private Config config; @@ -24,7 +24,7 @@ public class HashicorpKeyVaultServiceFactoryTest { public void setUp() { config = mock(Config.class); envProvider = mock(EnvironmentVariableProvider.class); - factory = new HashicorpKeyVaultServiceFactory(); + factory = new HashicorpKeyVaultSpringServiceFactory(); } @Test(expected = NullPointerException.class) @@ -124,7 +124,7 @@ public void createReturnsNewHashicorpKeyVaultService() { KeyVaultService result = factory.create(config, envProvider); - assertThat(result).isInstanceOf(HashicorpKeyVaultService.class); + assertThat(result).isInstanceOf(HashicorpKeyVaultSpringService.class); } @Test diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringServiceTest.java similarity index 94% rename from key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java rename to key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringServiceTest.java index 752a4e0a44..743c8adaa1 100644 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringServiceTest.java @@ -16,9 +16,9 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.*; -public class HashicorpKeyVaultServiceTest { +public class HashicorpKeyVaultSpringServiceTest { - private HashicorpKeyVaultService keyVaultService; + private HashicorpKeyVaultSpringService keyVaultService; private VaultTemplate vaultTemplate; private final String url = "someurl"; @@ -32,7 +32,7 @@ public void setUp() { when(keyVaultConfig.getUrl()).thenReturn(url); vaultTemplate = mock(VaultTemplate.class); - keyVaultService = new HashicorpKeyVaultService(keyVaultConfig, vaultTemplate); + keyVaultService = new HashicorpKeyVaultSpringService(keyVaultConfig, vaultTemplate); } @Test @@ -89,7 +89,7 @@ public void getSecretRetrievesValueForSpecifiedKeyAtSpecifiedPath() { @Test public void setSecretCallsVaultTemplate() { - Map secretData = Collections.singletonMap(secretName, "value"); + Map secretData = Collections.singletonMap(secretName, "value"); keyVaultService.setSecretAtPath(secretPath, secretData); verify(vaultTemplate, times(1)).write(secretPath, secretData); diff --git a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultException.java b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultException.java new file mode 100644 index 0000000000..aa9f4b2a61 --- /dev/null +++ b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultException.java @@ -0,0 +1,13 @@ +package com.quorum.tessera.key.vault; + +public class KeyVaultException extends RuntimeException { + + public KeyVaultException(Throwable cause) { + super(cause); + } + + public KeyVaultException(String message) { + super(message); + } + +} diff --git a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultService.java b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultService.java index fde0ddac9e..fa6db1c5f1 100644 --- a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultService.java +++ b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultService.java @@ -9,6 +9,6 @@ public interface KeyVaultService { Object setSecret(String secretName, String secret); - Object setSecretAtPath(String secretPath, Map secretData); + Object setSecretAtPath(String secretPath, Map secretData); } From 699ed560b2c57520fe82c2d8f816cf2a77d52961 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Fri, 30 Nov 2018 17:57:14 +0000 Subject: [PATCH 15/57] Support TLS for keygen by adding certificate CLI option --- .../quorum/tessera/config/cli/DefaultCliAdapter.java | 9 +++++++++ .../config/cli/parsers/KeyGenerationParser.java | 10 ++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/config-cli/src/main/java/com/quorum/tessera/config/cli/DefaultCliAdapter.java b/config-cli/src/main/java/com/quorum/tessera/config/cli/DefaultCliAdapter.java index ae1370d2b5..599c09002d 100644 --- a/config-cli/src/main/java/com/quorum/tessera/config/cli/DefaultCliAdapter.java +++ b/config-cli/src/main/java/com/quorum/tessera/config/cli/DefaultCliAdapter.java @@ -180,6 +180,15 @@ private Options buildBaseOptions() { .build() ); + options.addOption( + Option.builder("keygenvaultcert") + .desc("TLS certificate for key vault - not required for Azure vaults") + .hasArg() + .optionalArg(false) + .argName("PATH") + .build() + ); + options.addOption( Option.builder("pidfile") .desc("Path to pid file") diff --git a/config-cli/src/main/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParser.java b/config-cli/src/main/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParser.java index ca5d63db0e..c97f1a7311 100644 --- a/config-cli/src/main/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParser.java +++ b/config-cli/src/main/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParser.java @@ -15,6 +15,7 @@ import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; @@ -111,8 +112,13 @@ private Optional keyVaultConfig(CommandLine commandLine) { throw new CliException("At least one -filename must be provided when saving generated keys in a Hashicorp Vault"); } - //TODO - keyVaultConfig = new HashicorpKeyVaultConfig(keyVaultUrl, null); + Path tlsCertificatePath = null; + + if(commandLine.hasOption("keygenvaultcert")) { + tlsCertificatePath = Paths.get(commandLine.getOptionValue("keygenvaultcert")); + } + + keyVaultConfig = new HashicorpKeyVaultConfig(keyVaultUrl, tlsCertificatePath); Set> violations = validator.validate((HashicorpKeyVaultConfig)keyVaultConfig); From d20d5265fe82a946af285f697a5b744310aa14bb Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Fri, 30 Nov 2018 19:00:03 +0000 Subject: [PATCH 16/57] Remove spring-vault-core implementation --- key-vault/hashicorp-key-vault/pom.xml | 4 -- .../HashicorpKeyVaultSpringService.java | 61 ------------------- ...HashicorpKeyVaultSpringServiceFactory.java | 58 ------------------ 3 files changed, 123 deletions(-) delete mode 100644 key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringService.java delete mode 100644 key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringServiceFactory.java diff --git a/key-vault/hashicorp-key-vault/pom.xml b/key-vault/hashicorp-key-vault/pom.xml index 5cec1d9a2b..08706b0e82 100644 --- a/key-vault/hashicorp-key-vault/pom.xml +++ b/key-vault/hashicorp-key-vault/pom.xml @@ -12,10 +12,6 @@ hashicorp-key-vault - - org.springframework.vault - spring-vault-core - com.bettercloud diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringService.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringService.java deleted file mode 100644 index 7253bc6100..0000000000 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringService.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.quorum.tessera.key.vault.hashicorp; - -import com.quorum.tessera.config.HashicorpKeyVaultConfig; -import com.quorum.tessera.key.vault.KeyVaultService; -import com.quorum.tessera.key.vault.VaultSecretNotFoundException; -import org.springframework.vault.VaultException; -import org.springframework.vault.core.VaultTemplate; -import org.springframework.vault.support.VaultResponse; - -import java.util.Map; - -public class HashicorpKeyVaultSpringService implements KeyVaultService { - - //TODO Make use of methods from KeyVaultService - - private String vaultUrl; - private VaultTemplate vaultTemplate; - - public HashicorpKeyVaultSpringService(HashicorpKeyVaultConfig keyVaultConfig, VaultTemplate vaultTemplate) { - this.vaultUrl = keyVaultConfig.getUrl(); - this.vaultTemplate = vaultTemplate; - } - - @Override - public String getSecret(String secretName) { - return null; - } - - @Override - public Object setSecret(String secretName, String secret) { - return null; - } - - @Override - public String getSecretFromPath(String secretPath, String secretName) { - VaultResponse response = vaultTemplate.read(secretPath); - - if(response == null) { - throw new VaultSecretNotFoundException("Hashicorp Vault secret not found at path " + secretPath + " in vault " + vaultUrl); - } - - if(response.getData() == null) { - throw new VaultSecretNotFoundException("No data for Hashicorp Vault secret at path " + secretPath + " in vault " + vaultUrl); - } - - if(response.getData().get(secretName) == null) { - throw new VaultSecretNotFoundException("Value for secret id " + secretName + " not found at path " + secretPath + " in vault " + vaultUrl); - } - - return response.getData().get(secretName).toString(); - } - - @Override - public Object setSecretAtPath(String secretPath, Map secretData) { - try { - return vaultTemplate.write(secretPath, secretData); - } catch(VaultException e) { - throw new VaultSecretNotFoundException("Unable to write secret to path '" + secretPath + "' - " + e.getMessage() ); - } - } -} diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringServiceFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringServiceFactory.java deleted file mode 100644 index 2a8aaff5cc..0000000000 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringServiceFactory.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.quorum.tessera.key.vault.hashicorp; - -import com.quorum.tessera.config.*; -import com.quorum.tessera.config.util.EnvironmentVariableProvider; -import com.quorum.tessera.key.vault.KeyVaultService; -import com.quorum.tessera.key.vault.KeyVaultServiceFactory; -import org.springframework.vault.authentication.ClientAuthentication; -import org.springframework.vault.authentication.TokenAuthentication; -import org.springframework.vault.client.VaultEndpoint; -import org.springframework.vault.core.VaultTemplate; - -import java.net.URI; -import java.net.URISyntaxException; -import java.util.Objects; -import java.util.Optional; - -public class HashicorpKeyVaultSpringServiceFactory implements KeyVaultServiceFactory { - - private final String authTokenEnvVar = "HASHICORP_TOKEN"; - - @Override - public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider) { - Objects.requireNonNull(config); - Objects.requireNonNull(envProvider); - - String authToken = envProvider.getEnv(authTokenEnvVar); - - if(authToken == null) { - throw new HashicorpCredentialNotSetException(authTokenEnvVar + " must be set"); - } - - HashicorpKeyVaultConfig keyVaultConfig = Optional.ofNullable(config.getKeys()) - .map(KeyConfiguration::getHashicorpKeyVaultConfig) - .orElseThrow(() -> new ConfigException(new RuntimeException("Trying to create Hashicorp Vault connection but no Vault configuration provided"))); - - VaultEndpoint vaultEndpoint; - try { - vaultEndpoint = VaultEndpoint.from(new URI(keyVaultConfig.getUrl())); - } catch (URISyntaxException | IllegalArgumentException e) { - throw new ConfigException(new Throwable("Provided Hashicorp Vault url is incorrectly formatted", e)); - } - - ClientAuthentication clientAuthentication = new TokenAuthentication(authToken); - - return new HashicorpKeyVaultSpringService( - keyVaultConfig, - new VaultTemplate( - vaultEndpoint, - clientAuthentication - ) - ); - } - - @Override - public KeyVaultType getType() { - return KeyVaultType.HASHICORP; - } -} From 5f459b5f6e319947a88113227935842d3e2d87c5 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Fri, 30 Nov 2018 19:01:26 +0000 Subject: [PATCH 17/57] Remove spring-vault-core-implementation --- key-vault/hashicorp-key-vault/pom.xml | 1 - pom.xml | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/key-vault/hashicorp-key-vault/pom.xml b/key-vault/hashicorp-key-vault/pom.xml index 08706b0e82..bb33541253 100644 --- a/key-vault/hashicorp-key-vault/pom.xml +++ b/key-vault/hashicorp-key-vault/pom.xml @@ -16,7 +16,6 @@ com.bettercloud vault-java-driver - 3.1.0 diff --git a/pom.xml b/pom.xml index 5363bed219..5fec5b39fe 100644 --- a/pom.xml +++ b/pom.xml @@ -508,9 +508,9 @@ - org.springframework.vault - spring-vault-core - 2.0.3.RELEASE + com.bettercloud + vault-java-driver + 3.1.0 From 169f9e0016208633e7596889c011b3710671eca3 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Fri, 30 Nov 2018 19:03:49 +0000 Subject: [PATCH 18/57] Introduce custom objects for parameters of the KeyVaultService methods --- .../key/vault/azure/AzureGetSecretData.java | 22 +++++++++++ .../key/vault/azure/AzureKeyVaultService.java | 33 +++++++++++++++- .../key/vault/azure/AzureSetSecretData.java | 28 +++++++++++++ .../hashicorp/HashicorpGetSecretData.java | 28 +++++++++++++ .../hashicorp/HashicorpKeyVaultService.java | 39 +++++++++++++++++++ .../hashicorp/HashicorpSetSecretData.java | 31 +++++++++++++++ .../tessera/key/vault/GetSecretData.java | 7 ++++ .../tessera/key/vault/KeyVaultService.java | 4 ++ .../tessera/key/vault/SetSecretData.java | 7 ++++ 9 files changed, 197 insertions(+), 2 deletions(-) create mode 100644 key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureGetSecretData.java create mode 100644 key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureSetSecretData.java create mode 100644 key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretData.java create mode 100644 key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretData.java create mode 100644 key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/GetSecretData.java create mode 100644 key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/SetSecretData.java diff --git a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureGetSecretData.java b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureGetSecretData.java new file mode 100644 index 0000000000..5893fcc974 --- /dev/null +++ b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureGetSecretData.java @@ -0,0 +1,22 @@ +package com.quorum.tessera.key.vault.azure; + +import com.quorum.tessera.config.KeyVaultType; +import com.quorum.tessera.key.vault.GetSecretData; + +public class AzureGetSecretData implements GetSecretData { + + private String secretName; + + public AzureGetSecretData(String secretName) { + this.secretName = secretName; + } + + public String getSecretName() { + return secretName; + } + + @Override + public KeyVaultType getType() { + return KeyVaultType.AZURE; + } +} diff --git a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultService.java b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultService.java index 90c0f722a2..bb3ee74834 100644 --- a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultService.java +++ b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultService.java @@ -3,8 +3,7 @@ import com.microsoft.azure.keyvault.models.SecretBundle; import com.microsoft.azure.keyvault.requests.SetSecretRequest; import com.quorum.tessera.config.AzureKeyVaultConfig; -import com.quorum.tessera.key.vault.KeyVaultService; -import com.quorum.tessera.key.vault.VaultSecretNotFoundException; +import com.quorum.tessera.key.vault.*; import java.util.Map; import java.util.Objects; @@ -47,4 +46,34 @@ public SecretBundle setSecret(String secretName, String secret) { public Object setSecretAtPath(String secretPath, Map secretData) { return null; } + + @Override + public String getSecret(GetSecretData getSecretData) { + if(!(getSecretData instanceof AzureGetSecretData)) { + throw new KeyVaultException("Incorrect data type passed to HashicorpKeyVaultService. Type was " + getSecretData.getType()); + } + + AzureGetSecretData azureGetSecretData = (AzureGetSecretData) getSecretData; + + SecretBundle secretBundle = azureKeyVaultClientDelegate.getSecret(vaultUrl, azureGetSecretData.getSecretName()); + + if(secretBundle == null) { + throw new VaultSecretNotFoundException("Azure Key Vault secret " + azureGetSecretData.getSecretName() + " was not found in vault " + vaultUrl); + } + + return secretBundle.value(); + } + + @Override + public Object setSecret(SetSecretData setSecretData) { + if(!(setSecretData instanceof AzureSetSecretData)) { + throw new KeyVaultException("Incorrect data type passed to HashicorpKeyVaultService. Type was " + setSecretData.getType()); + } + + AzureSetSecretData azureSetSecretData = (AzureSetSecretData) setSecretData; + + SetSecretRequest setSecretRequest = new SetSecretRequest.Builder(vaultUrl, azureSetSecretData.getSecretName(), azureSetSecretData.getSecret()).build(); + + return this.azureKeyVaultClientDelegate.setSecret(setSecretRequest); + } } diff --git a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureSetSecretData.java b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureSetSecretData.java new file mode 100644 index 0000000000..daa263c6b7 --- /dev/null +++ b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureSetSecretData.java @@ -0,0 +1,28 @@ +package com.quorum.tessera.key.vault.azure; + +import com.quorum.tessera.config.KeyVaultType; +import com.quorum.tessera.key.vault.SetSecretData; + +public class AzureSetSecretData implements SetSecretData { + private String secretName; + + private String secret; + + public AzureSetSecretData(String secretName, String secret) { + this.secretName = secretName; + this.secret = secret; + } + + public String getSecretName() { + return secretName; + } + + public String getSecret() { + return secret; + } + + @Override + public KeyVaultType getType() { + return KeyVaultType.AZURE; + } +} diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretData.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretData.java new file mode 100644 index 0000000000..a918559486 --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretData.java @@ -0,0 +1,28 @@ +package com.quorum.tessera.key.vault.hashicorp; + +import com.quorum.tessera.config.KeyVaultType; +import com.quorum.tessera.key.vault.GetSecretData; + +public class HashicorpGetSecretData implements GetSecretData { + private String secretPath; + + private String secretName; + + public HashicorpGetSecretData(String secretPath, String secretName) { + this.secretPath = secretPath; + this.secretName = secretName; + } + + public String getSecretPath() { + return secretPath; + } + + public String getSecretName() { + return secretName; + } + + @Override + public KeyVaultType getType() { + return KeyVaultType.HASHICORP; + } +} diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java index 3debd5799c..1587925484 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java @@ -4,7 +4,10 @@ import com.bettercloud.vault.VaultException; import com.bettercloud.vault.response.LogicalResponse; import com.quorum.tessera.config.HashicorpKeyVaultConfig; +import com.quorum.tessera.key.vault.GetSecretData; +import com.quorum.tessera.key.vault.KeyVaultException; import com.quorum.tessera.key.vault.KeyVaultService; +import com.quorum.tessera.key.vault.SetSecretData; import java.util.Map; import java.util.Optional; @@ -53,4 +56,40 @@ public Object setSecretAtPath(String secretPath, Map secretData) throw new HashicorpVaultException("Error writing secret to path " + secretPath + " - " + e.getMessage()); } } + + @Override + public String getSecret(GetSecretData getSecretData) { + if(!(getSecretData instanceof HashicorpGetSecretData)) { + throw new KeyVaultException("Incorrect data type passed to HashicorpKeyVaultService. Type was " + getSecretData.getType()); + } + + HashicorpGetSecretData hashicorpGetSecretData = (HashicorpGetSecretData) getSecretData; + + LogicalResponse response; + try { + response = vault.logical().read(hashicorpGetSecretData.getSecretPath()); + } catch(VaultException e) { + throw new HashicorpVaultException("Error getting secret " + hashicorpGetSecretData.getSecretName() + " from path " + hashicorpGetSecretData.getSecretPath() + " - " + e.getMessage()); + } + + return Optional.of(response) + .map(LogicalResponse::getData) + .map(data -> data.get(hashicorpGetSecretData.getSecretName())) + .orElseThrow(() -> new HashicorpVaultException("No secret " + hashicorpGetSecretData.getSecretName() + " found at path " + hashicorpGetSecretData.getSecretPath())); + } + + @Override + public Object setSecret(SetSecretData setSecretData) { + if(!(setSecretData instanceof HashicorpSetSecretData)) { + throw new KeyVaultException("Incorrect data type passed to HashicorpKeyVaultService. Type was " + setSecretData.getType()); + } + + HashicorpSetSecretData hashicorpSetSecretData = (HashicorpSetSecretData) setSecretData; + + try { + return vault.logical().write(hashicorpSetSecretData.getSecretPath(), hashicorpSetSecretData.getNameValuePairs()); + } catch(VaultException e) { + throw new HashicorpVaultException("Error writing secret to path " + hashicorpSetSecretData.getSecretPath() + " - " + e.getMessage()); + } + } } diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretData.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretData.java new file mode 100644 index 0000000000..7189b4f2f8 --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretData.java @@ -0,0 +1,31 @@ +package com.quorum.tessera.key.vault.hashicorp; + +import com.quorum.tessera.config.KeyVaultType; +import com.quorum.tessera.key.vault.SetSecretData; + +import java.util.Map; + +public class HashicorpSetSecretData implements SetSecretData { + + private String secretPath; + + private Map nameValuePairs; + + public HashicorpSetSecretData(String secretPath, Map nameValuePairs) { + this.secretPath = secretPath; + this.nameValuePairs = nameValuePairs; + } + + public String getSecretPath() { + return secretPath; + } + + public Map getNameValuePairs() { + return nameValuePairs; + } + + @Override + public KeyVaultType getType() { + return KeyVaultType.HASHICORP; + } +} diff --git a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/GetSecretData.java b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/GetSecretData.java new file mode 100644 index 0000000000..6c5887bf82 --- /dev/null +++ b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/GetSecretData.java @@ -0,0 +1,7 @@ +package com.quorum.tessera.key.vault; + +import com.quorum.tessera.config.KeyVaultType; + +public interface GetSecretData { + KeyVaultType getType(); +} diff --git a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultService.java b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultService.java index fa6db1c5f1..4605e9f28b 100644 --- a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultService.java +++ b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultService.java @@ -11,4 +11,8 @@ public interface KeyVaultService { Object setSecretAtPath(String secretPath, Map secretData); + String getSecret(GetSecretData getSecretData); + + Object setSecret(SetSecretData setSecretData); + } diff --git a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/SetSecretData.java b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/SetSecretData.java new file mode 100644 index 0000000000..74c62bd1db --- /dev/null +++ b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/SetSecretData.java @@ -0,0 +1,7 @@ +package com.quorum.tessera.key.vault; + +import com.quorum.tessera.config.KeyVaultType; + +public interface SetSecretData { + KeyVaultType getType(); +} From 346513ef7c41b18a3146dd73db0e55204a082a73 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Mon, 3 Dec 2018 13:28:12 +0000 Subject: [PATCH 19/57] Add tests for Hashicorp data, exception and service classes --- .../hashicorp/HashicorpGetSecretDataTest.java | 29 +++ .../HashicorpKeyVaultServiceTest.java | 185 ++++++++++++++++++ .../hashicorp/HashicorpSetSecretDataTest.java | 31 +++ .../HashicorpVaultExceptionTest.java | 23 +++ .../key/vault/KeyVaultExceptionTest.java | 25 +++ 5 files changed, 293 insertions(+) create mode 100644 key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretDataTest.java create mode 100644 key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java create mode 100644 key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretDataTest.java create mode 100644 key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpVaultExceptionTest.java create mode 100644 key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/KeyVaultExceptionTest.java diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretDataTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretDataTest.java new file mode 100644 index 0000000000..cc40527994 --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretDataTest.java @@ -0,0 +1,29 @@ +package com.quorum.tessera.key.vault.hashicorp; + +import com.quorum.tessera.config.KeyVaultType; +import org.junit.Before; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class HashicorpGetSecretDataTest { + + private HashicorpGetSecretData getSecretData; + + @Before + public void setUp() { + this.getSecretData = new HashicorpGetSecretData("secret/path", "secretName"); + } + + @Test + public void getters() { + assertThat(getSecretData.getSecretPath()).isEqualTo("secret/path"); + assertThat(getSecretData.getSecretName()).isEqualTo("secretName"); + } + + @Test + public void getType() { + assertThat(getSecretData.getType()).isEqualTo(KeyVaultType.HASHICORP); + } + +} diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java new file mode 100644 index 0000000000..984eb37623 --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java @@ -0,0 +1,185 @@ +package com.quorum.tessera.key.vault.hashicorp; + +import com.bettercloud.vault.Vault; +import com.bettercloud.vault.VaultException; +import com.bettercloud.vault.api.Logical; +import com.bettercloud.vault.response.LogicalResponse; +import com.quorum.tessera.config.HashicorpKeyVaultConfig; +import com.quorum.tessera.key.vault.GetSecretData; +import com.quorum.tessera.key.vault.KeyVaultException; +import com.quorum.tessera.key.vault.SetSecretData; +import org.junit.Before; +import org.junit.Test; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class HashicorpKeyVaultServiceTest { + + private HashicorpKeyVaultService keyVaultService; + + private HashicorpKeyVaultConfig keyVaultConfig; + + private Vault vault; + + @Before + public void setUp() { + this.keyVaultConfig = mock(HashicorpKeyVaultConfig.class); + this.vault = mock(Vault.class); + this.keyVaultService = new HashicorpKeyVaultService(keyVaultConfig, vault); + } + + @Test + public void getSecretThrowsExceptionIfProvidedDataIsNotCorrectType() { + GetSecretData getSecretData = mock(GetSecretData.class); + when(getSecretData.getType()).thenReturn(null); + + Throwable ex = catchThrowable(() -> keyVaultService.getSecret(getSecretData)); + + assertThat(ex).isExactlyInstanceOf(KeyVaultException.class); + assertThat(ex).hasMessage("Incorrect data type passed to HashicorpKeyVaultService. Type was null"); + } + + @Test + public void getSecretThrowsExceptionIfErrorRetrievingSecretFromVault() throws Exception { + Logical logical = mock(Logical.class); + when(vault.logical()).thenReturn(logical); + when(logical.read(anyString())).thenThrow(new VaultException("vault exception msg")); + + GetSecretData getSecretData = new HashicorpGetSecretData("secret/path", "secretName"); + + Throwable ex = catchThrowable(() -> keyVaultService.getSecret(getSecretData)); + + assertThat(ex).isExactlyInstanceOf(HashicorpVaultException.class); + assertThat(ex).hasMessage("Error getting secret secretName from path secret/path - vault exception msg"); + } + + @Test + public void getSecretThrowsExceptionIfResponseHasNoData() throws Exception { + Logical logical = mock(Logical.class); + when(vault.logical()).thenReturn(logical); + + LogicalResponse response = mock(LogicalResponse.class); + when(logical.read(anyString())).thenReturn(response); + + when(response.getData()).thenReturn(null); + + GetSecretData getSecretData = new HashicorpGetSecretData("secret/path", "secretName"); + + Throwable ex = catchThrowable(() -> keyVaultService.getSecret(getSecretData)); + + assertThat(ex).isExactlyInstanceOf(HashicorpVaultException.class); + assertThat(ex).hasMessage("No secret secretName found at path secret/path"); + } + + @Test + public void getSecretThrowsExceptionIfValueNotFoundForGivenSecretName() throws Exception { + Logical logical = mock(Logical.class); + when(vault.logical()).thenReturn(logical); + + LogicalResponse response = mock(LogicalResponse.class); + when(logical.read(anyString())).thenReturn(response); + + Map data = Collections.singletonMap("diffName", "value"); + when(response.getData()).thenReturn(data); + + GetSecretData getSecretData = new HashicorpGetSecretData("secret/path", "secretName"); + + Throwable ex = catchThrowable(() -> keyVaultService.getSecret(getSecretData)); + + assertThat(ex).isExactlyInstanceOf(HashicorpVaultException.class); + assertThat(ex).hasMessage("No secret secretName found at path secret/path"); + } + + @Test + public void getSecretReturnsValueForGivenSecretNameAtGivenPath() throws Exception { + Logical logical = mock(Logical.class); + when(vault.logical()).thenReturn(logical); + + LogicalResponse response = mock(LogicalResponse.class); + when(logical.read(anyString())).thenReturn(response); + + String expected = "value"; + + Map data = Collections.singletonMap("secretName", expected); + when(response.getData()).thenReturn(data); + + GetSecretData getSecretData = new HashicorpGetSecretData("secret/path", "secretName"); + + String result = keyVaultService.getSecret(getSecretData); + + assertThat(result).isEqualTo(expected); + } + + @Test + public void getSecretReturnsCorrectValueIfMultipleFoundAtGivenPath() throws Exception { + Logical logical = mock(Logical.class); + when(vault.logical()).thenReturn(logical); + + LogicalResponse response = mock(LogicalResponse.class); + when(logical.read(anyString())).thenReturn(response); + + String expected = "value"; + + Map data = new HashMap<>(); + data.put("someOtherSecret", "someOtherValue"); + data.put("secretName", expected); + when(response.getData()).thenReturn(data); + + GetSecretData getSecretData = new HashicorpGetSecretData("secret/path", "secretName"); + + String result = keyVaultService.getSecret(getSecretData); + + assertThat(result).isEqualTo(expected); + } + + @Test + public void setSecretThrowsExceptionIfProvidedDataIsNotCorrectType() { + SetSecretData setSecretData = mock(SetSecretData.class); + when(setSecretData.getType()).thenReturn(null); + + Throwable ex = catchThrowable(() -> keyVaultService.setSecret(setSecretData)); + + assertThat(ex).isExactlyInstanceOf(KeyVaultException.class); + assertThat(ex).hasMessage("Incorrect data type passed to HashicorpKeyVaultService. Type was null"); + } + + @Test + public void setSecretThrowsExceptionIfErrorWritingToVault() throws Exception { + Logical logical = mock(Logical.class); + when(vault.logical()).thenReturn(logical); + when(logical.write(anyString(), anyMap())).thenThrow(new VaultException("vault exception msg")); + + SetSecretData setSecretData = new HashicorpSetSecretData("secret/path", Collections.emptyMap()); + + Throwable ex = catchThrowable(() -> keyVaultService.setSecret(setSecretData)); + + assertThat(ex).isExactlyInstanceOf(HashicorpVaultException.class); + assertThat(ex).hasMessage("Error writing secret to path secret/path - vault exception msg"); + } + + @Test + public void setSecretReturnsLogicalResponse() throws Exception { + Logical logical = mock(Logical.class); + when(vault.logical()).thenReturn(logical); + + LogicalResponse response = mock(LogicalResponse.class); + when(logical.write(anyString(), anyMap())).thenReturn(response); + + SetSecretData setSecretData = new HashicorpSetSecretData("secret/path", Collections.emptyMap()); + + Object result = keyVaultService.setSecret(setSecretData); + + assertThat(result).isInstanceOf(LogicalResponse.class); + assertThat(result).isEqualTo(response); + } + +} diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretDataTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretDataTest.java new file mode 100644 index 0000000000..ae314439e6 --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretDataTest.java @@ -0,0 +1,31 @@ +package com.quorum.tessera.key.vault.hashicorp; + +import com.quorum.tessera.config.KeyVaultType; +import org.junit.Before; +import org.junit.Test; + +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; + +public class HashicorpSetSecretDataTest { + + private HashicorpSetSecretData setSecretData; + + @Before + public void setUp() { + this.setSecretData = new HashicorpSetSecretData("secret/path", Collections.singletonMap("name", "value")); + } + + @Test + public void getters() { + assertThat(setSecretData.getSecretPath()).isEqualTo("secret/path"); + assertThat(setSecretData.getNameValuePairs()).isEqualTo(Collections.singletonMap("name", "value")); + } + + @Test + public void getType() { + assertThat(setSecretData.getType()).isEqualTo(KeyVaultType.HASHICORP); + } + +} diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpVaultExceptionTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpVaultExceptionTest.java new file mode 100644 index 0000000000..1ff410fd42 --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpVaultExceptionTest.java @@ -0,0 +1,23 @@ +package com.quorum.tessera.key.vault.hashicorp; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class HashicorpVaultExceptionTest { + @Test + public void createWithMessage() { + final String msg = "msg"; + HashicorpVaultException exception = new HashicorpVaultException(msg); + + assertThat(exception).hasMessage(msg); + } + + @Test + public void createWithCause() { + Throwable cause = new Exception("cause"); + HashicorpVaultException exception = new HashicorpVaultException(cause); + + assertThat(exception).hasCause(cause); + } +} diff --git a/key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/KeyVaultExceptionTest.java b/key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/KeyVaultExceptionTest.java new file mode 100644 index 0000000000..642ebff3fb --- /dev/null +++ b/key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/KeyVaultExceptionTest.java @@ -0,0 +1,25 @@ +package com.quorum.tessera.key.vault; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class KeyVaultExceptionTest { + + @Test + public void createWithMessage() { + final String msg = "msg"; + KeyVaultException exception = new KeyVaultException(msg); + + assertThat(exception).hasMessage(msg); + } + + @Test + public void createWithCause() { + Throwable cause = new Exception("cause"); + KeyVaultException exception = new KeyVaultException(cause); + + assertThat(exception).hasCause(cause); + } + +} From 55c76bc2b8bf1f8d51504965c79f80b0cde8a6d7 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Mon, 3 Dec 2018 15:23:03 +0000 Subject: [PATCH 20/57] Start writing tests for Hashicorp vault-java-driver service --- .../HashicorpKeyVaultClientFactory.java | 60 ++++ .../HashicorpKeyVaultServiceFactory.java | 80 +++--- .../key/vault/hashicorp/SslConfigFactory.java | 10 + .../vault/hashicorp/VaultConfigFactory.java | 10 + .../HashicorpKeyVaultClientFactoryTest.java | 72 +++++ .../HashicorpKeyVaultServiceFactoryTest.java | 192 +++++++++++++ ...icorpKeyVaultSpringServiceFactoryTest.java | 270 +++++++++--------- .../HashicorpKeyVaultSpringServiceTest.java | 116 -------- .../key/vault/KeyVaultClientFactory.java | 5 + .../key/vault/KeyVaultServiceFactory.java | 2 + .../MockAzureKeyVaultServiceFactory.java | 5 + 11 files changed, 539 insertions(+), 283 deletions(-) create mode 100644 key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactory.java create mode 100644 key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/SslConfigFactory.java create mode 100644 key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/VaultConfigFactory.java create mode 100644 key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactoryTest.java create mode 100644 key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java delete mode 100644 key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringServiceTest.java create mode 100644 key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultClientFactory.java diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactory.java new file mode 100644 index 0000000000..768fde57c8 --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactory.java @@ -0,0 +1,60 @@ +package com.quorum.tessera.key.vault.hashicorp; + +import com.bettercloud.vault.SslConfig; +import com.bettercloud.vault.Vault; +import com.bettercloud.vault.VaultConfig; +import com.bettercloud.vault.response.AuthResponse; +import com.quorum.tessera.config.HashicorpKeyVaultConfig; +import com.quorum.tessera.key.vault.KeyVaultClientFactory; + +public class HashicorpKeyVaultClientFactory implements KeyVaultClientFactory { + + private VaultConfig vaultConfig; + + private SslConfig sslConfig; + + private Vault vault; + + public HashicorpKeyVaultClientFactory init(HashicorpKeyVaultConfig keyVaultConfig, VaultConfigFactory vaultConfigFactory, SslConfigFactory sslConfigFactory) { + + this.vaultConfig = vaultConfigFactory.create() + .address(keyVaultConfig.getUrl()); + + if(keyVaultConfig.getTlsCertificatePath() != null) { + this.sslConfig = sslConfigFactory.create(); + + VaultCallback.execute( + () -> sslConfig.pemFile(keyVaultConfig.getTlsCertificatePath().toFile()) + .build() + ); + + vaultConfig.sslConfig(sslConfig); + } + + VaultCallback.execute(vaultConfig::build); + + this.vault = new Vault(vaultConfig); + + return this; + } + + public HashicorpKeyVaultClientFactory login(String roleId, String secretId, String authToken) { + String token; + + if(roleId != null && secretId != null) { + AuthResponse loginResponse = VaultCallback.execute(() -> vault.auth().loginByAppRole("approle", roleId, secretId)); + token = loginResponse.getAuthClientToken(); + } else { + token = authToken; + } + + this.vaultConfig.token(token); + + return this; + } + + public Vault create() { + return this.vault; + } + +} diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java index b719f31217..7d7eef00ec 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java @@ -1,11 +1,9 @@ package com.quorum.tessera.key.vault.hashicorp; -import com.bettercloud.vault.SslConfig; import com.bettercloud.vault.Vault; -import com.bettercloud.vault.VaultConfig; -import com.bettercloud.vault.response.AuthResponse; import com.quorum.tessera.config.*; import com.quorum.tessera.config.util.EnvironmentVariableProvider; +import com.quorum.tessera.key.vault.KeyVaultClientFactory; import com.quorum.tessera.key.vault.KeyVaultService; import com.quorum.tessera.key.vault.KeyVaultServiceFactory; @@ -20,15 +18,24 @@ public class HashicorpKeyVaultServiceFactory implements KeyVaultServiceFactory { @Override public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider) { + return null; + } + + @Override + public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, KeyVaultClientFactory keyVaultClientFactory) { Objects.requireNonNull(config); Objects.requireNonNull(envProvider); + if(!(keyVaultClientFactory instanceof HashicorpKeyVaultClientFactory)) { + throw new HashicorpVaultException("Incorrect KeyVaultClientFactoryType passed to HashicorpKeyVaultServiceFactory"); + } + String roleId = envProvider.getEnv(roleIdEnvVar); String secretId = envProvider.getEnv(secretIdEnvVar); String authToken = envProvider.getEnv(authTokenEnvVar); if(roleId == null && secretId == null && authToken == null) { - throw new HashicorpCredentialNotSetException("Environment variables must be set to authenticate with Hashicorp Vault. Set the " + roleIdEnvVar + " and " + secretIdEnvVar + " environment variables if using the AppRole authentication method. Set the " + authTokenEnvVar + " if using another authentication method."); + throw new HashicorpCredentialNotSetException("Environment variables must be set to authenticate with Hashicorp Vault. Set the " + roleIdEnvVar + " and " + secretIdEnvVar + " environment variables if using the AppRole authentication method. Set the " + authTokenEnvVar + " environment variable if using another authentication method."); } else if(isOnlyOneInputNull(roleId, secretId)) { throw new HashicorpCredentialNotSetException("Only one of the " + roleIdEnvVar + " and " + secretIdEnvVar + " environment variables to authenticate with Hashicorp Vault using the AppRole method has been set"); @@ -38,34 +45,43 @@ else if(isOnlyOneInputNull(roleId, secretId)) { .map(KeyConfiguration::getHashicorpKeyVaultConfig) .orElseThrow(() -> new ConfigException(new RuntimeException("Trying to create Hashicorp Vault connection but no Vault configuration provided"))); - VaultConfig vaultConfig = new VaultConfig() - .address(keyVaultConfig.getUrl()); - - if(keyVaultConfig.getTlsCertificatePath() != null) { - SslConfig vaultSslConfig = new SslConfig(); - VaultCallback.execute( - () -> vaultSslConfig - .pemFile(keyVaultConfig.getTlsCertificatePath().toFile()) - .build() - ); - - vaultConfig.sslConfig(vaultSslConfig); - } - - VaultCallback.execute(() -> vaultConfig.build()); - - final Vault vault = new Vault(vaultConfig); - - String token; - - if(roleId != null && secretId != null) { - AuthResponse loginResponse = VaultCallback.execute(() -> vault.auth().loginByAppRole("approle", roleId, secretId)); - token = loginResponse.getAuthClientToken(); - } else { - token = authToken; - } - - vaultConfig.token(token); + Vault vault = ((HashicorpKeyVaultClientFactory) keyVaultClientFactory) + .init(keyVaultConfig, new VaultConfigFactory(), new SslConfigFactory()) + .login(roleId, secretId, authToken) + .create(); + +// HashicorpKeyVaultConfig keyVaultConfig = Optional.ofNullable(config.getKeys()) +// .map(KeyConfiguration::getHashicorpKeyVaultConfig) +// .orElseThrow(() -> new ConfigException(new RuntimeException("Trying to create Hashicorp Vault connection but no Vault configuration provided"))); +// +// VaultConfig vaultConfig = new VaultConfig() +// .address(keyVaultConfig.getUrl()); +// +// if(keyVaultConfig.getTlsCertificatePath() != null) { +// SslConfig vaultSslConfig = new SslConfig(); +// VaultCallback.execute( +// () -> vaultSslConfig +// .pemFile(keyVaultConfig.getTlsCertificatePath().toFile()) +// .build() +// ); +// +// vaultConfig.sslConfig(vaultSslConfig); +// } +// +// VaultCallback.execute(vaultConfig::build); +// +// final Vault vault = new Vault(vaultConfig); +// +// String token; +// +// if(roleId != null && secretId != null) { +// AuthResponse loginResponse = VaultCallback.execute(() -> vault.auth().loginByAppRole("approle", roleId, secretId)); +// token = loginResponse.getAuthClientToken(); +// } else { +// token = authToken; +// } +// +// vaultConfig.token(token); return new HashicorpKeyVaultService(keyVaultConfig, vault); } diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/SslConfigFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/SslConfigFactory.java new file mode 100644 index 0000000000..5f397a62c1 --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/SslConfigFactory.java @@ -0,0 +1,10 @@ +package com.quorum.tessera.key.vault.hashicorp; + +import com.bettercloud.vault.SslConfig; + +public class SslConfigFactory { + + public SslConfig create() { + return new SslConfig(); + } +} diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/VaultConfigFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/VaultConfigFactory.java new file mode 100644 index 0000000000..9651351ce9 --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/VaultConfigFactory.java @@ -0,0 +1,10 @@ +package com.quorum.tessera.key.vault.hashicorp; + +import com.bettercloud.vault.VaultConfig; + +public class VaultConfigFactory { + + VaultConfig create() { + return new VaultConfig(); + } +} diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactoryTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactoryTest.java new file mode 100644 index 0000000000..c8e9dcf6c4 --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactoryTest.java @@ -0,0 +1,72 @@ +package com.quorum.tessera.key.vault.hashicorp; + +import com.bettercloud.vault.VaultConfig; +import com.quorum.tessera.config.HashicorpKeyVaultConfig; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.nio.file.Path; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; + +public class HashicorpKeyVaultClientFactoryTest { + + private HashicorpKeyVaultClientFactory clientFactory; + + private HashicorpKeyVaultConfig keyVaultConfig; + + private VaultConfigFactory vaultConfigFactory; + + private SslConfigFactory sslConfigFactory; + + @Before + public void setUp() { + this.clientFactory = new HashicorpKeyVaultClientFactory(); + + this.keyVaultConfig = mock(HashicorpKeyVaultConfig.class); + when(keyVaultConfig.getUrl()).thenReturn("url"); + + this.vaultConfigFactory = mock(VaultConfigFactory.class); + this.sslConfigFactory = mock(SslConfigFactory.class); + } + + @Test + public void initCreatesVaultClientWithUrlProvidedInConfig() throws Exception { + VaultConfig vaultConfig = mock(VaultConfig.class); + when(vaultConfigFactory.create()).thenReturn(vaultConfig); + + when(vaultConfig.address(anyString())).thenReturn(vaultConfig); + + clientFactory.init(keyVaultConfig, vaultConfigFactory, null); + + verify(vaultConfigFactory).create(); + verify(vaultConfig).address("url"); + + verify(vaultConfig).build(); + } + + @Test + public void initAddsTlsConfigToVaultClientIfProvided() throws Exception { + Path certPath = mock(Path.class); + when(keyVaultConfig.getTlsCertificatePath()).thenReturn(certPath); + + File certFile = mock(File.class); + when(certPath.toFile()).thenReturn(certFile); + + VaultConfig vaultConfig = spy(VaultConfig.class); + when(vaultConfigFactory.create()).thenReturn(vaultConfig); + + + clientFactory.init(keyVaultConfig, vaultConfigFactory, null); + + verify(vaultConfigFactory).create(); + verify(vaultConfig).address("url"); + + verify(vaultConfig).sslConfig(any()); + + verify(vaultConfig).build(); + } + +} diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java new file mode 100644 index 0000000000..0526c81928 --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java @@ -0,0 +1,192 @@ +package com.quorum.tessera.key.vault.hashicorp; + +import com.quorum.tessera.config.Config; +import com.quorum.tessera.config.ConfigException; +import com.quorum.tessera.config.KeyConfiguration; +import com.quorum.tessera.config.KeyVaultType; +import com.quorum.tessera.config.util.EnvironmentVariableProvider; +import org.junit.Before; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class HashicorpKeyVaultServiceFactoryTest { + + private HashicorpKeyVaultServiceFactory keyVaultServiceFactory; + + private Config config; + + private EnvironmentVariableProvider envProvider; + + private String noCredentialsExceptionMsg = "Environment variables must be set to authenticate with Hashicorp Vault. Set the HASHICORP_ROLE_ID and HASHICORP_SECRET_ID environment variables if using the AppRole authentication method. Set the HASHICORP_TOKEN environment variable if using another authentication method."; + + private String approleCredentialsExceptionMsg = "Only one of the HASHICORP_ROLE_ID and HASHICORP_SECRET_ID environment variables to authenticate with Hashicorp Vault using the AppRole method has been set"; + + @Before + public void setUp() { + this.keyVaultServiceFactory = new HashicorpKeyVaultServiceFactory(); + this.config = mock(Config.class); + this.envProvider = mock(EnvironmentVariableProvider.class); + } + + @Test(expected = NullPointerException.class) + public void nullConfigThrowsException() { + keyVaultServiceFactory.create(null, envProvider); + } + + @Test(expected = NullPointerException.class) + public void nullEnvVarProviderThrowsException() { + keyVaultServiceFactory.create(config, null); + } + + @Test + public void getType() { + assertThat(keyVaultServiceFactory.getType()).isEqualTo(KeyVaultType.HASHICORP); + } + + @Test + public void exceptionThrownIfNoEnvVarsSet() { + when(envProvider.getEnv("HASHICORP_ROLE_ID")).thenReturn(null); + when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn(null); + when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn(null); + + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider)); + + assertThat(ex).isInstanceOf(HashicorpCredentialNotSetException.class); + assertThat(ex).hasMessage(noCredentialsExceptionMsg); + } + + @Test + public void exceptionThrownIfOnlyRoleIdEnvVarSet() { + when(envProvider.getEnv("HASHICORP_ROLE_ID")).thenReturn("role-id"); + when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn(null); + when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn(null); + + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider)); + + assertThat(ex).isInstanceOf(HashicorpCredentialNotSetException.class); + assertThat(ex).hasMessage(approleCredentialsExceptionMsg); + } + + @Test + public void exceptionThrownIfOnlySecretIdEnvVarSet() { + when(envProvider.getEnv("HASHICORP_ROLE_ID")).thenReturn(null); + when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn("secret-id"); + when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn(null); + + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider)); + + assertThat(ex).isInstanceOf(HashicorpCredentialNotSetException.class); + assertThat(ex).hasMessage(approleCredentialsExceptionMsg); + } + + @Test + public void exceptionThrownIfOnlyRoleIdAndTokenEnvVarsSet() { + when(envProvider.getEnv("HASHICORP_ROLE_ID")).thenReturn("role-id"); + when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn(null); + when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn("token"); + + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider)); + + assertThat(ex).isInstanceOf(HashicorpCredentialNotSetException.class); + assertThat(ex).hasMessage(approleCredentialsExceptionMsg); + } + + @Test + public void exceptionThrownIfOnlySecretIdAndTokenEnvVarsSet() { + when(envProvider.getEnv("HASHICORP_ROLE_ID")).thenReturn(null); + when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn("secret-id"); + when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn("token"); + + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider)); + + assertThat(ex).isInstanceOf(HashicorpCredentialNotSetException.class); + assertThat(ex).hasMessage(approleCredentialsExceptionMsg); + } + + @Test + public void roleIdAndSecretIdEnvVarsAreSetIsAllowed() { + when(envProvider.getEnv("HASHICORP_ROLE_ID")).thenReturn("role-id"); + when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn("secret-id"); + when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn(null); + + //Exception unrelated to env vars will be thrown + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider)); + + assertThat(ex).isNotInstanceOf(HashicorpCredentialNotSetException.class); + } + + @Test + public void onlyTokenEnvVarIsSetIsAllowed() { + when(envProvider.getEnv("HASHICORP_ROLE_ID")).thenReturn(null); + when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn(null); + when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn("token"); + + //Exception unrelated to env vars will be thrown + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider)); + + assertThat(ex).isNotInstanceOf(HashicorpCredentialNotSetException.class); + } + + @Test + public void allEnvVarsSetIsAllowed() { + when(envProvider.getEnv("HASHICORP_ROLE_ID")).thenReturn("role-id"); + when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn("secret-id"); + when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn("token"); + + //Exception unrelated to env vars will be thrown + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider)); + + assertThat(ex).isNotInstanceOf(HashicorpCredentialNotSetException.class); + } + + @Test + public void exceptionThrownIfProvidedConfigHasNoKeyConfiguration() { + when(envProvider.getEnv("HASHICORP_ROLE_ID")).thenReturn("role-id"); + when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn("secret-id"); + when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn(null); + + when(config.getKeys()).thenReturn(null); + + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider)); + + assertThat(ex).isInstanceOf(ConfigException.class); + assertThat(ex).hasMessageContaining("Trying to create Hashicorp Vault connection but no Vault configuration provided"); + } + + @Test + public void exceptionThrownIfProvidedConfigHasNoHashicorpKeyVaultConfig() { + when(envProvider.getEnv("HASHICORP_ROLE_ID")).thenReturn("role-id"); + when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn("secret-id"); + when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn(null); + + KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); + when(config.getKeys()).thenReturn(keyConfiguration); + + when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(null); + + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider)); + + assertThat(ex).isInstanceOf(ConfigException.class); + assertThat(ex).hasMessageContaining("Trying to create Hashicorp Vault connection but no Vault configuration provided"); + } + + @Test + public void tlsConfigAddedIfProvided() { + + } + +} + +//nullConfigThrowsException +//nullEnvVarProviderThrowsException +//createThrowsExceptionIfTokenEnvVarNotSet +//nullKeyConfigurationThrowsException +//nullHashicorpVaultConfigThrowsException +//incorrectSyntaxUrlInConfigThrowsException +//incorrectlyFormattedUrlInConfigThrowsException +//createReturnsNewHashicorpKeyVaultService +//getType diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringServiceFactoryTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringServiceFactoryTest.java index 0e39097f7c..37cd3121f2 100644 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringServiceFactoryTest.java +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringServiceFactoryTest.java @@ -1,135 +1,135 @@ -package com.quorum.tessera.key.vault.hashicorp; - -import com.quorum.tessera.config.*; -import com.quorum.tessera.config.util.EnvironmentVariableProvider; -import com.quorum.tessera.key.vault.KeyVaultService; -import org.junit.Before; -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.catchThrowable; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class HashicorpKeyVaultSpringServiceFactoryTest { - - private HashicorpKeyVaultSpringServiceFactory factory; - - private Config config; - - private EnvironmentVariableProvider envProvider; - - @Before - public void setUp() { - config = mock(Config.class); - envProvider = mock(EnvironmentVariableProvider.class); - factory = new HashicorpKeyVaultSpringServiceFactory(); - } - - @Test(expected = NullPointerException.class) - public void nullConfigThrowsException() { - factory.create(null, envProvider); - } - - @Test(expected = NullPointerException.class) - public void nullEnvVarProviderThrowsException() { - factory.create(config, null); - } - - @Test - public void createThrowsExceptionIfTokenEnvVarNotSet() { - Config config = mock(Config.class); - EnvironmentVariableProvider envProvider = mock(EnvironmentVariableProvider.class); - - when(envProvider.getEnv(anyString())).thenReturn(null); - - Throwable ex = catchThrowable(() -> factory.create(config, envProvider)); - - assertThat(ex).isInstanceOf(HashicorpCredentialNotSetException.class); - assertThat(ex.getMessage()).isEqualTo("HASHICORP_TOKEN must be set"); - } - - @Test - public void nullKeyConfigurationThrowsException() { - when(envProvider.getEnv(anyString())).thenReturn("envVar"); - when(config.getKeys()).thenReturn(null); - - Throwable ex = catchThrowable(() -> factory.create(config, envProvider)); - - assertThat(ex).isInstanceOf(ConfigException.class); - assertThat(ex.getMessage()).contains("Trying to create Hashicorp Vault connection but no Vault configuration provided"); - } - - @Test - public void nullHashicorpVaultConfigThrowsException() { - when(envProvider.getEnv(anyString())).thenReturn("envVar"); - KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); - when(config.getKeys()).thenReturn(keyConfiguration); - when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(null); - - Throwable ex = catchThrowable(() -> factory.create(config, envProvider)); - - assertThat(ex).isInstanceOf(ConfigException.class); - assertThat(ex.getMessage()).contains("Trying to create Hashicorp Vault connection but no Vault configuration provided"); - } - - @Test - public void incorrectSyntaxUrlInConfigThrowsException() { - when(envProvider.getEnv(anyString())).thenReturn("envVar"); - - KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); - when(config.getKeys()).thenReturn(keyConfiguration); - - HashicorpKeyVaultConfig vaultConfig = new HashicorpKeyVaultConfig(); - String url = "!@£$%^"; - vaultConfig.setUrl(url); - when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(vaultConfig); - - Throwable ex = catchThrowable(() -> factory.create(config, envProvider)); - - assertThat(ex).isInstanceOf(ConfigException.class); - assertThat(ex.getMessage()).contains("Provided Hashicorp Vault url is incorrectly formatted"); - } - - @Test - public void incorrectlyFormattedUrlInConfigThrowsException() { - when(envProvider.getEnv(anyString())).thenReturn("envVar"); - - KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); - when(config.getKeys()).thenReturn(keyConfiguration); - - HashicorpKeyVaultConfig vaultConfig = new HashicorpKeyVaultConfig(); - String url = "notaurl"; - vaultConfig.setUrl(url); - when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(vaultConfig); - - Throwable ex = catchThrowable(() -> factory.create(config, envProvider)); - - assertThat(ex).isInstanceOf(ConfigException.class); - assertThat(ex.getMessage()).contains("Provided Hashicorp Vault url is incorrectly formatted"); - } - - @Test - public void createReturnsNewHashicorpKeyVaultService() { - when(envProvider.getEnv(anyString())).thenReturn("envVar"); - - KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); - when(config.getKeys()).thenReturn(keyConfiguration); - - HashicorpKeyVaultConfig vaultConfig = new HashicorpKeyVaultConfig(); - String url = "http://someurl"; - vaultConfig.setUrl(url); - when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(vaultConfig); - - KeyVaultService result = factory.create(config, envProvider); - - assertThat(result).isInstanceOf(HashicorpKeyVaultSpringService.class); - } - - @Test - public void getType() { - assertThat(factory.getType()).isEqualTo(KeyVaultType.HASHICORP); - } - -} +//package com.quorum.tessera.key.vault.hashicorp; +// +//import com.quorum.tessera.config.*; +//import com.quorum.tessera.config.util.EnvironmentVariableProvider; +//import com.quorum.tessera.key.vault.KeyVaultService; +//import org.junit.Before; +//import org.junit.Test; +// +//import static org.assertj.core.api.Assertions.assertThat; +//import static org.assertj.core.api.Assertions.catchThrowable; +//import static org.mockito.ArgumentMatchers.anyString; +//import static org.mockito.Mockito.mock; +//import static org.mockito.Mockito.when; +// +//public class HashicorpKeyVaultSpringServiceFactoryTest { +// +// private HashicorpKeyVaultSpringServiceFactory factory; +// +// private Config config; +// +// private EnvironmentVariableProvider envProvider; +// +// @Before +// public void setUp() { +// config = mock(Config.class); +// envProvider = mock(EnvironmentVariableProvider.class); +// factory = new HashicorpKeyVaultSpringServiceFactory(); +// } +// +// @Test(expected = NullPointerException.class) +// public void nullConfigThrowsException() { +// factory.create(null, envProvider); +// } +// +// @Test(expected = NullPointerException.class) +// public void nullEnvVarProviderThrowsException() { +// factory.create(config, null); +// } +// +// @Test +// public void createThrowsExceptionIfTokenEnvVarNotSet() { +// Config config = mock(Config.class); +// EnvironmentVariableProvider envProvider = mock(EnvironmentVariableProvider.class); +// +// when(envProvider.getEnv(anyString())).thenReturn(null); +// +// Throwable ex = catchThrowable(() -> factory.create(config, envProvider)); +// +// assertThat(ex).isInstanceOf(HashicorpCredentialNotSetException.class); +// assertThat(ex.getMessage()).isEqualTo("HASHICORP_TOKEN must be set"); +// } +// +// @Test +// public void nullKeyConfigurationThrowsException() { +// when(envProvider.getEnv(anyString())).thenReturn("envVar"); +// when(config.getKeys()).thenReturn(null); +// +// Throwable ex = catchThrowable(() -> factory.create(config, envProvider)); +// +// assertThat(ex).isInstanceOf(ConfigException.class); +// assertThat(ex.getMessage()).contains("Trying to create Hashicorp Vault connection but no Vault configuration provided"); +// } +// +// @Test +// public void nullHashicorpVaultConfigThrowsException() { +// when(envProvider.getEnv(anyString())).thenReturn("envVar"); +// KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); +// when(config.getKeys()).thenReturn(keyConfiguration); +// when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(null); +// +// Throwable ex = catchThrowable(() -> factory.create(config, envProvider)); +// +// assertThat(ex).isInstanceOf(ConfigException.class); +// assertThat(ex.getMessage()).contains("Trying to create Hashicorp Vault connection but no Vault configuration provided"); +// } +// +// @Test +// public void incorrectSyntaxUrlInConfigThrowsException() { +// when(envProvider.getEnv(anyString())).thenReturn("envVar"); +// +// KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); +// when(config.getKeys()).thenReturn(keyConfiguration); +// +// HashicorpKeyVaultConfig vaultConfig = new HashicorpKeyVaultConfig(); +// String url = "!@£$%^"; +// vaultConfig.setUrl(url); +// when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(vaultConfig); +// +// Throwable ex = catchThrowable(() -> factory.create(config, envProvider)); +// +// assertThat(ex).isInstanceOf(ConfigException.class); +// assertThat(ex.getMessage()).contains("Provided Hashicorp Vault url is incorrectly formatted"); +// } +// +// @Test +// public void incorrectlyFormattedUrlInConfigThrowsException() { +// when(envProvider.getEnv(anyString())).thenReturn("envVar"); +// +// KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); +// when(config.getKeys()).thenReturn(keyConfiguration); +// +// HashicorpKeyVaultConfig vaultConfig = new HashicorpKeyVaultConfig(); +// String url = "notaurl"; +// vaultConfig.setUrl(url); +// when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(vaultConfig); +// +// Throwable ex = catchThrowable(() -> factory.create(config, envProvider)); +// +// assertThat(ex).isInstanceOf(ConfigException.class); +// assertThat(ex.getMessage()).contains("Provided Hashicorp Vault url is incorrectly formatted"); +// } +// +// @Test +// public void createReturnsNewHashicorpKeyVaultService() { +// when(envProvider.getEnv(anyString())).thenReturn("envVar"); +// +// KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); +// when(config.getKeys()).thenReturn(keyConfiguration); +// +// HashicorpKeyVaultConfig vaultConfig = new HashicorpKeyVaultConfig(); +// String url = "http://someurl"; +// vaultConfig.setUrl(url); +// when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(vaultConfig); +// +// KeyVaultService result = factory.create(config, envProvider); +// +// assertThat(result).isInstanceOf(HashicorpKeyVaultSpringService.class); +// } +// +// @Test +// public void getType() { +// assertThat(factory.getType()).isEqualTo(KeyVaultType.HASHICORP); +// } +// +//} diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringServiceTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringServiceTest.java deleted file mode 100644 index 743c8adaa1..0000000000 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringServiceTest.java +++ /dev/null @@ -1,116 +0,0 @@ -package com.quorum.tessera.key.vault.hashicorp; - -import com.quorum.tessera.config.HashicorpKeyVaultConfig; -import com.quorum.tessera.key.vault.VaultSecretNotFoundException; -import org.junit.Before; -import org.junit.Test; -import org.springframework.vault.VaultException; -import org.springframework.vault.core.VaultTemplate; -import org.springframework.vault.support.VaultResponse; - -import java.util.Collections; -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.catchThrowable; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.*; - -public class HashicorpKeyVaultSpringServiceTest { - - private HashicorpKeyVaultSpringService keyVaultService; - private VaultTemplate vaultTemplate; - - private final String url = "someurl"; - private final String secretPath = "secret/path"; - private final String secretName = "secretname"; - - - @Before - public void setUp() { - HashicorpKeyVaultConfig keyVaultConfig = mock(HashicorpKeyVaultConfig.class); - when(keyVaultConfig.getUrl()).thenReturn(url); - vaultTemplate = mock(VaultTemplate.class); - - keyVaultService = new HashicorpKeyVaultSpringService(keyVaultConfig, vaultTemplate); - } - - @Test - public void getSecretThrowsExceptionIfSecretNotFoundAtPath() { - when(vaultTemplate.read(anyString())).thenReturn(null); - - final Throwable ex = catchThrowable(() -> keyVaultService.getSecretFromPath(secretPath, secretName)); - - assertThat(ex).isInstanceOf(VaultSecretNotFoundException.class); - assertThat(ex).hasMessage("Hashicorp Vault secret not found at path " + secretPath + " in vault " + url); - - } - - @Test - public void getSecretThrowsExceptionIfNoDataForSpecifiedSecret() { - VaultResponse vaultResponse = mock(VaultResponse.class); - - when(vaultTemplate.read(anyString())).thenReturn(vaultResponse); - when(vaultResponse.getData()).thenReturn(null); - - final Throwable ex = catchThrowable(() -> keyVaultService.getSecretFromPath(secretPath, secretName)); - - assertThat(ex).isInstanceOf(VaultSecretNotFoundException.class); - assertThat(ex).hasMessage("No data for Hashicorp Vault secret at path " + secretPath + " in vault " + url); - } - - @Test - public void getSecretThrowsExceptionIfNoValueForSpecifiedSecretName() { - VaultResponse vaultResponse = mock(VaultResponse.class); - Map secretData = Collections.singletonMap("othername", "value"); - - when(vaultTemplate.read(anyString())).thenReturn(vaultResponse); - when(vaultResponse.getData()).thenReturn(secretData); - - final Throwable ex = catchThrowable(() -> keyVaultService.getSecretFromPath(secretPath, secretName)); - - assertThat(ex).isInstanceOf(VaultSecretNotFoundException.class); - assertThat(ex).hasMessage("Value for secret id " + secretName + " not found at path " + secretPath + " in vault " + url); - } - - @Test - public void getSecretRetrievesValueForSpecifiedKeyAtSpecifiedPath() { - VaultResponse vaultResponse = mock(VaultResponse.class); - String value = "value"; - Map secretData = Collections.singletonMap(secretName, value); - - when(vaultTemplate.read(anyString())).thenReturn(vaultResponse); - when(vaultResponse.getData()).thenReturn(secretData); - - String result = keyVaultService.getSecretFromPath(secretPath, secretName); - - assertThat(result).isEqualTo(value); - } - - @Test - public void setSecretCallsVaultTemplate() { - Map secretData = Collections.singletonMap(secretName, "value"); - keyVaultService.setSecretAtPath(secretPath, secretData); - - verify(vaultTemplate, times(1)).write(secretPath, secretData); - } - - @Test - public void setSecretThrowsExceptionIfUnsuccessful() { - when(vaultTemplate.write(anyString(), anyMap())).thenThrow(new VaultException("new exception")); - - Throwable ex = catchThrowable(() -> keyVaultService.setSecretAtPath("secretpath", Collections.emptyMap())); - - assertThat(ex).isInstanceOf(VaultSecretNotFoundException.class); - assertThat(ex.getMessage()).isEqualTo("Unable to write secret to path 'secretpath' - new exception"); - - } - - @Test - //TODO Not ideal - see class todos - public void getSecretAndSetSecretReturnNull() { - assertThat(keyVaultService.getSecret(secretName)).isNull(); - assertThat(keyVaultService.setSecret(secretPath, secretName)).isNull(); - } - -} diff --git a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultClientFactory.java b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultClientFactory.java new file mode 100644 index 0000000000..4724e4ac1a --- /dev/null +++ b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultClientFactory.java @@ -0,0 +1,5 @@ +package com.quorum.tessera.key.vault; + +public interface KeyVaultClientFactory { + +} diff --git a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultServiceFactory.java b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultServiceFactory.java index 8ad4d27fb7..de6564d6c3 100644 --- a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultServiceFactory.java +++ b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultServiceFactory.java @@ -12,6 +12,8 @@ public interface KeyVaultServiceFactory { KeyVaultService create(Config config, EnvironmentVariableProvider envProvider); + KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, KeyVaultClientFactory keyVaultClientFactory); + KeyVaultType getType(); static KeyVaultServiceFactory getInstance(KeyVaultType keyVaultType) { diff --git a/key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/MockAzureKeyVaultServiceFactory.java b/key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/MockAzureKeyVaultServiceFactory.java index a871e26059..c8a692e5ea 100644 --- a/key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/MockAzureKeyVaultServiceFactory.java +++ b/key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/MockAzureKeyVaultServiceFactory.java @@ -10,6 +10,11 @@ public KeyVaultService create(Config config, EnvironmentVariableProvider envProv throw new UnsupportedOperationException("This mock object's method is not expected to be called"); } + @Override + public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, KeyVaultClientFactory keyVaultClientFactory) { + return null; + } + @Override public KeyVaultType getType() { return KeyVaultType.AZURE; From a67b4d913fcf2bf20634682367a8062d7d9db9cf Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Tue, 4 Dec 2018 09:54:58 +0000 Subject: [PATCH 21/57] Move Vault client creation into new class to facilitate testing --- .../HashicorpKeyVaultClientFactory.java | 55 +++---- .../HashicorpKeyVaultServiceFactory.java | 58 +++---- .../HashicorpKeyVaultClientFactoryTest.java | 85 +++++++--- .../HashicorpKeyVaultServiceFactoryTest.java | 151 ++++++++++++++---- .../key/vault/KeyVaultServiceFactory.java | 2 +- .../MockAzureKeyVaultServiceFactory.java | 10 +- 6 files changed, 242 insertions(+), 119 deletions(-) diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactory.java index 768fde57c8..1861621665 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactory.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactory.java @@ -3,25 +3,35 @@ import com.bettercloud.vault.SslConfig; import com.bettercloud.vault.Vault; import com.bettercloud.vault.VaultConfig; -import com.bettercloud.vault.response.AuthResponse; import com.quorum.tessera.config.HashicorpKeyVaultConfig; import com.quorum.tessera.key.vault.KeyVaultClientFactory; public class HashicorpKeyVaultClientFactory implements KeyVaultClientFactory { - private VaultConfig vaultConfig; + public Vault createUnauthenticatedClient(HashicorpKeyVaultConfig keyVaultConfig, VaultConfigFactory vaultConfigFactory, SslConfigFactory sslConfigFactory) { + VaultConfig vaultConfig = createBaseVaultConfig(keyVaultConfig, vaultConfigFactory, sslConfigFactory); - private SslConfig sslConfig; + VaultCallback.execute(vaultConfig::build); + + return new Vault(vaultConfig); + } + + public Vault createAuthenticatedClient(HashicorpKeyVaultConfig keyVaultConfig, VaultConfigFactory vaultConfigFactory, SslConfigFactory sslConfigFactory, String authToken) { + VaultConfig vaultConfig = createBaseVaultConfig(keyVaultConfig, vaultConfigFactory, sslConfigFactory); + + vaultConfig.token(authToken); - private Vault vault; + VaultCallback.execute(vaultConfig::build); - public HashicorpKeyVaultClientFactory init(HashicorpKeyVaultConfig keyVaultConfig, VaultConfigFactory vaultConfigFactory, SslConfigFactory sslConfigFactory) { + return new Vault(vaultConfig); + } - this.vaultConfig = vaultConfigFactory.create() - .address(keyVaultConfig.getUrl()); + private VaultConfig createBaseVaultConfig(HashicorpKeyVaultConfig keyVaultConfig, VaultConfigFactory vaultConfigFactory, SslConfigFactory sslConfigFactory) { + VaultConfig vaultConfig = vaultConfigFactory.create() + .address(keyVaultConfig.getUrl()); - if(keyVaultConfig.getTlsCertificatePath() != null) { - this.sslConfig = sslConfigFactory.create(); + if (keyVaultConfig.getTlsCertificatePath() != null) { + SslConfig sslConfig = sslConfigFactory.create(); VaultCallback.execute( () -> sslConfig.pemFile(keyVaultConfig.getTlsCertificatePath().toFile()) @@ -30,31 +40,6 @@ public HashicorpKeyVaultClientFactory init(HashicorpKeyVaultConfig keyVaultConfi vaultConfig.sslConfig(sslConfig); } - - VaultCallback.execute(vaultConfig::build); - - this.vault = new Vault(vaultConfig); - - return this; - } - - public HashicorpKeyVaultClientFactory login(String roleId, String secretId, String authToken) { - String token; - - if(roleId != null && secretId != null) { - AuthResponse loginResponse = VaultCallback.execute(() -> vault.auth().loginByAppRole("approle", roleId, secretId)); - token = loginResponse.getAuthClientToken(); - } else { - token = authToken; - } - - this.vaultConfig.token(token); - - return this; + return vaultConfig; } - - public Vault create() { - return this.vault; - } - } diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java index 7d7eef00ec..77ec6c7d74 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java @@ -1,6 +1,7 @@ package com.quorum.tessera.key.vault.hashicorp; import com.bettercloud.vault.Vault; +import com.bettercloud.vault.response.AuthResponse; import com.quorum.tessera.config.*; import com.quorum.tessera.config.util.EnvironmentVariableProvider; import com.quorum.tessera.key.vault.KeyVaultClientFactory; @@ -16,20 +17,16 @@ public class HashicorpKeyVaultServiceFactory implements KeyVaultServiceFactory { private final String secretIdEnvVar = "HASHICORP_SECRET_ID"; private final String authTokenEnvVar = "HASHICORP_TOKEN"; - @Override - public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider) { - return null; - } +// @Override +// public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider) { +// return null; +// } @Override public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, KeyVaultClientFactory keyVaultClientFactory) { Objects.requireNonNull(config); Objects.requireNonNull(envProvider); - if(!(keyVaultClientFactory instanceof HashicorpKeyVaultClientFactory)) { - throw new HashicorpVaultException("Incorrect KeyVaultClientFactoryType passed to HashicorpKeyVaultServiceFactory"); - } - String roleId = envProvider.getEnv(roleIdEnvVar); String secretId = envProvider.getEnv(secretIdEnvVar); String authToken = envProvider.getEnv(authTokenEnvVar); @@ -45,15 +42,11 @@ else if(isOnlyOneInputNull(roleId, secretId)) { .map(KeyConfiguration::getHashicorpKeyVaultConfig) .orElseThrow(() -> new ConfigException(new RuntimeException("Trying to create Hashicorp Vault connection but no Vault configuration provided"))); - Vault vault = ((HashicorpKeyVaultClientFactory) keyVaultClientFactory) - .init(keyVaultConfig, new VaultConfigFactory(), new SslConfigFactory()) - .login(roleId, secretId, authToken) - .create(); +// Vault vault = ((HashicorpKeyVaultClientFactory) keyVaultClientFactory) +// .init(keyVaultConfig, new VaultConfigFactory(), new SslConfigFactory()) +// .login(roleId, secretId, authToken) +// .create(); -// HashicorpKeyVaultConfig keyVaultConfig = Optional.ofNullable(config.getKeys()) -// .map(KeyConfiguration::getHashicorpKeyVaultConfig) -// .orElseThrow(() -> new ConfigException(new RuntimeException("Trying to create Hashicorp Vault connection but no Vault configuration provided"))); -// // VaultConfig vaultConfig = new VaultConfig() // .address(keyVaultConfig.getUrl()); // @@ -71,19 +64,28 @@ else if(isOnlyOneInputNull(roleId, secretId)) { // VaultCallback.execute(vaultConfig::build); // // final Vault vault = new Vault(vaultConfig); -// -// String token; -// -// if(roleId != null && secretId != null) { -// AuthResponse loginResponse = VaultCallback.execute(() -> vault.auth().loginByAppRole("approle", roleId, secretId)); -// token = loginResponse.getAuthClientToken(); -// } else { -// token = authToken; -// } -// -// vaultConfig.token(token); - return new HashicorpKeyVaultService(keyVaultConfig, vault); + if(!(keyVaultClientFactory instanceof HashicorpKeyVaultClientFactory)) { + throw new HashicorpVaultException("Incorrect KeyVaultClientFactoryType passed to HashicorpKeyVaultServiceFactory"); + } + + HashicorpKeyVaultClientFactory hashicorpClientFactory = (HashicorpKeyVaultClientFactory) keyVaultClientFactory; + + final Vault unauthenticatedVault = hashicorpClientFactory.createUnauthenticatedClient(keyVaultConfig, new VaultConfigFactory(), new SslConfigFactory()); + + String token; + + if(roleId != null && secretId != null) { + //TODO allow other paths + AuthResponse loginResponse = VaultCallback.execute(() -> unauthenticatedVault.auth().loginByAppRole("approle", roleId, secretId)); + token = loginResponse.getAuthClientToken(); + } else { + token = authToken; + } + + final Vault authenticatedVault = hashicorpClientFactory.createAuthenticatedClient(keyVaultConfig, new VaultConfigFactory(), new SslConfigFactory(), token); + + return new HashicorpKeyVaultService(keyVaultConfig, authenticatedVault); } @Override diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactoryTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactoryTest.java index c8e9dcf6c4..a693ef3a1c 100644 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactoryTest.java +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactoryTest.java @@ -1,5 +1,6 @@ package com.quorum.tessera.key.vault.hashicorp; +import com.bettercloud.vault.SslConfig; import com.bettercloud.vault.VaultConfig; import com.quorum.tessera.config.HashicorpKeyVaultConfig; import org.junit.Before; @@ -13,7 +14,7 @@ public class HashicorpKeyVaultClientFactoryTest { - private HashicorpKeyVaultClientFactory clientFactory; + private HashicorpKeyVaultClientFactory keyVaultClientFactory; private HashicorpKeyVaultConfig keyVaultConfig; @@ -23,50 +24,92 @@ public class HashicorpKeyVaultClientFactoryTest { @Before public void setUp() { - this.clientFactory = new HashicorpKeyVaultClientFactory(); - + this.keyVaultClientFactory = new HashicorpKeyVaultClientFactory(); this.keyVaultConfig = mock(HashicorpKeyVaultConfig.class); when(keyVaultConfig.getUrl()).thenReturn("url"); - - this.vaultConfigFactory = mock(VaultConfigFactory.class); + this.vaultConfigFactory = mock(VaultConfigFactory.class, RETURNS_DEEP_STUBS); this.sslConfigFactory = mock(SslConfigFactory.class); } @Test - public void initCreatesVaultClientWithUrlProvidedInConfig() throws Exception { + public void tlsConfigAddedToUnauthenticatedVaultClientIfProvided() throws Exception { + Path certPath = mock(Path.class); + when(keyVaultConfig.getTlsCertificatePath()).thenReturn(certPath); + File certFile = mock(File.class); + when(certPath.toFile()).thenReturn(certFile); + VaultConfig vaultConfig = mock(VaultConfig.class); - when(vaultConfigFactory.create()).thenReturn(vaultConfig); + when(vaultConfigFactory.create().address(anyString())).thenReturn(vaultConfig); + when(vaultConfig.build()).thenReturn(vaultConfig); - when(vaultConfig.address(anyString())).thenReturn(vaultConfig); + SslConfig sslConfig = mock(SslConfig.class, RETURNS_DEEP_STUBS); + when(sslConfigFactory.create()).thenReturn(sslConfig); - clientFactory.init(keyVaultConfig, vaultConfigFactory, null); + when(sslConfig.pemFile(any(File.class)).build()).thenReturn(sslConfig); + + keyVaultClientFactory.createUnauthenticatedClient(keyVaultConfig, vaultConfigFactory, sslConfigFactory); + + verify(keyVaultConfig, times(2)).getTlsCertificatePath(); + verify(vaultConfig).sslConfig(sslConfig); + } + + @Test + public void tlsConfigNotAddedToUnauthenticatedVaultClientIfNotProvided() throws Exception { + when(keyVaultConfig.getTlsCertificatePath()).thenReturn(null); + + VaultConfig vaultConfig = mock(VaultConfig.class); + when(vaultConfigFactory.create().address(anyString())).thenReturn(vaultConfig); + when(vaultConfig.build()).thenReturn(vaultConfig); - verify(vaultConfigFactory).create(); - verify(vaultConfig).address("url"); + keyVaultClientFactory.createUnauthenticatedClient(keyVaultConfig, vaultConfigFactory, sslConfigFactory); - verify(vaultConfig).build(); + verify(vaultConfig, never()).sslConfig(any(SslConfig.class)); } @Test - public void initAddsTlsConfigToVaultClientIfProvided() throws Exception { + public void tlsConfigAddedToAuthenticatedVaultClientIfProvided() throws Exception { Path certPath = mock(Path.class); when(keyVaultConfig.getTlsCertificatePath()).thenReturn(certPath); - File certFile = mock(File.class); when(certPath.toFile()).thenReturn(certFile); - VaultConfig vaultConfig = spy(VaultConfig.class); - when(vaultConfigFactory.create()).thenReturn(vaultConfig); + VaultConfig vaultConfig = mock(VaultConfig.class); + when(vaultConfigFactory.create().address(anyString())).thenReturn(vaultConfig); + when(vaultConfig.build()).thenReturn(vaultConfig); + + SslConfig sslConfig = mock(SslConfig.class, RETURNS_DEEP_STUBS); + when(sslConfigFactory.create()).thenReturn(sslConfig); + + when(sslConfig.pemFile(any(File.class)).build()).thenReturn(sslConfig); + + keyVaultClientFactory.createAuthenticatedClient(keyVaultConfig, vaultConfigFactory, sslConfigFactory, "sometoken"); + + verify(keyVaultConfig, times(2)).getTlsCertificatePath(); + verify(vaultConfig).sslConfig(sslConfig); + } + + @Test + public void tlsConfigNotAddedToAuthenticatedVaultClientIfNotProvided() throws Exception { + when(keyVaultConfig.getTlsCertificatePath()).thenReturn(null); + VaultConfig vaultConfig = mock(VaultConfig.class); + when(vaultConfigFactory.create().address(anyString())).thenReturn(vaultConfig); + when(vaultConfig.build()).thenReturn(vaultConfig); - clientFactory.init(keyVaultConfig, vaultConfigFactory, null); + keyVaultClientFactory.createAuthenticatedClient(keyVaultConfig, vaultConfigFactory, sslConfigFactory, "sometoken"); - verify(vaultConfigFactory).create(); - verify(vaultConfig).address("url"); + verify(vaultConfig, never()).sslConfig(any(SslConfig.class)); + } + + @Test + public void tokenGetsAddedToAuthenticateVaultClientConfig() throws Exception { + VaultConfig vaultConfig = mock(VaultConfig.class); + when(vaultConfigFactory.create().address(anyString())).thenReturn(vaultConfig); + when(vaultConfig.build()).thenReturn(vaultConfig); - verify(vaultConfig).sslConfig(any()); + keyVaultClientFactory.createAuthenticatedClient(keyVaultConfig, vaultConfigFactory, sslConfigFactory, "sometoken"); - verify(vaultConfig).build(); + verify(vaultConfig).token("sometoken"); } } diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java index 0526c81928..a499bf8a80 100644 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java @@ -1,17 +1,19 @@ package com.quorum.tessera.key.vault.hashicorp; -import com.quorum.tessera.config.Config; -import com.quorum.tessera.config.ConfigException; -import com.quorum.tessera.config.KeyConfiguration; -import com.quorum.tessera.config.KeyVaultType; +import com.bettercloud.vault.Vault; +import com.bettercloud.vault.api.Auth; +import com.bettercloud.vault.response.AuthResponse; +import com.quorum.tessera.config.*; import com.quorum.tessera.config.util.EnvironmentVariableProvider; +import com.quorum.tessera.key.vault.KeyVaultClientFactory; import org.junit.Before; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; public class HashicorpKeyVaultServiceFactoryTest { @@ -21,6 +23,8 @@ public class HashicorpKeyVaultServiceFactoryTest { private EnvironmentVariableProvider envProvider; + private HashicorpKeyVaultClientFactory keyVaultClientFactory; + private String noCredentialsExceptionMsg = "Environment variables must be set to authenticate with Hashicorp Vault. Set the HASHICORP_ROLE_ID and HASHICORP_SECRET_ID environment variables if using the AppRole authentication method. Set the HASHICORP_TOKEN environment variable if using another authentication method."; private String approleCredentialsExceptionMsg = "Only one of the HASHICORP_ROLE_ID and HASHICORP_SECRET_ID environment variables to authenticate with Hashicorp Vault using the AppRole method has been set"; @@ -30,16 +34,17 @@ public void setUp() { this.keyVaultServiceFactory = new HashicorpKeyVaultServiceFactory(); this.config = mock(Config.class); this.envProvider = mock(EnvironmentVariableProvider.class); + this.keyVaultClientFactory = mock(HashicorpKeyVaultClientFactory.class); } @Test(expected = NullPointerException.class) public void nullConfigThrowsException() { - keyVaultServiceFactory.create(null, envProvider); + keyVaultServiceFactory.create(null, envProvider, keyVaultClientFactory); } @Test(expected = NullPointerException.class) public void nullEnvVarProviderThrowsException() { - keyVaultServiceFactory.create(config, null); + keyVaultServiceFactory.create(config, null, keyVaultClientFactory); } @Test @@ -53,7 +58,7 @@ public void exceptionThrownIfNoEnvVarsSet() { when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn(null); when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn(null); - Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider)); + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory)); assertThat(ex).isInstanceOf(HashicorpCredentialNotSetException.class); assertThat(ex).hasMessage(noCredentialsExceptionMsg); @@ -65,7 +70,7 @@ public void exceptionThrownIfOnlyRoleIdEnvVarSet() { when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn(null); when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn(null); - Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider)); + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory)); assertThat(ex).isInstanceOf(HashicorpCredentialNotSetException.class); assertThat(ex).hasMessage(approleCredentialsExceptionMsg); @@ -77,7 +82,7 @@ public void exceptionThrownIfOnlySecretIdEnvVarSet() { when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn("secret-id"); when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn(null); - Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider)); + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory)); assertThat(ex).isInstanceOf(HashicorpCredentialNotSetException.class); assertThat(ex).hasMessage(approleCredentialsExceptionMsg); @@ -89,7 +94,7 @@ public void exceptionThrownIfOnlyRoleIdAndTokenEnvVarsSet() { when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn(null); when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn("token"); - Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider)); + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory)); assertThat(ex).isInstanceOf(HashicorpCredentialNotSetException.class); assertThat(ex).hasMessage(approleCredentialsExceptionMsg); @@ -101,7 +106,7 @@ public void exceptionThrownIfOnlySecretIdAndTokenEnvVarsSet() { when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn("secret-id"); when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn("token"); - Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider)); + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory)); assertThat(ex).isInstanceOf(HashicorpCredentialNotSetException.class); assertThat(ex).hasMessage(approleCredentialsExceptionMsg); @@ -114,7 +119,7 @@ public void roleIdAndSecretIdEnvVarsAreSetIsAllowed() { when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn(null); //Exception unrelated to env vars will be thrown - Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider)); + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory)); assertThat(ex).isNotInstanceOf(HashicorpCredentialNotSetException.class); } @@ -126,7 +131,7 @@ public void onlyTokenEnvVarIsSetIsAllowed() { when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn("token"); //Exception unrelated to env vars will be thrown - Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider)); + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory)); assertThat(ex).isNotInstanceOf(HashicorpCredentialNotSetException.class); } @@ -138,7 +143,7 @@ public void allEnvVarsSetIsAllowed() { when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn("token"); //Exception unrelated to env vars will be thrown - Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider)); + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory)); assertThat(ex).isNotInstanceOf(HashicorpCredentialNotSetException.class); } @@ -151,7 +156,7 @@ public void exceptionThrownIfProvidedConfigHasNoKeyConfiguration() { when(config.getKeys()).thenReturn(null); - Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider)); + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory)); assertThat(ex).isInstanceOf(ConfigException.class); assertThat(ex).hasMessageContaining("Trying to create Hashicorp Vault connection but no Vault configuration provided"); @@ -168,25 +173,113 @@ public void exceptionThrownIfProvidedConfigHasNoHashicorpKeyVaultConfig() { when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(null); - Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider)); + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory)); assertThat(ex).isInstanceOf(ConfigException.class); assertThat(ex).hasMessageContaining("Trying to create Hashicorp Vault connection but no Vault configuration provided"); } @Test - public void tlsConfigAddedIfProvided() { + public void exceptionThrownIfKeyVaultClientFactoryNotHashicorpImplementation() { + when(envProvider.getEnv("HASHICORP_ROLE_ID")).thenReturn("role-id"); + when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn("secret-id"); + when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn(null); + + KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); + when(config.getKeys()).thenReturn(keyConfiguration); + when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(mock(HashicorpKeyVaultConfig.class)); + KeyVaultClientFactory wrongImpl = mock(KeyVaultClientFactory.class); + + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider, wrongImpl)); + + assertThat(ex).isInstanceOf(HashicorpVaultException.class); + assertThat(ex).hasMessageContaining("Incorrect KeyVaultClientFactoryType passed to HashicorpKeyVaultServiceFactory"); } -} + @Test + public void ifRoleIdAndSecretIdEnvVarsSetThenAppRoleIsUsedToAuthenticate() throws Exception { + when(envProvider.getEnv("HASHICORP_ROLE_ID")).thenReturn("role-id"); + when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn("secret-id"); + when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn(null); + + KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); + when(config.getKeys()).thenReturn(keyConfiguration); + + HashicorpKeyVaultConfig keyVaultConfig = mock(HashicorpKeyVaultConfig.class); + when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(keyVaultConfig); + + Vault unauthenticatedVault = mock(Vault.class); + when( + keyVaultClientFactory + .createUnauthenticatedClient(any(HashicorpKeyVaultConfig.class), any(VaultConfigFactory.class), any(SslConfigFactory.class)) + ).thenReturn(unauthenticatedVault); -//nullConfigThrowsException -//nullEnvVarProviderThrowsException -//createThrowsExceptionIfTokenEnvVarNotSet -//nullKeyConfigurationThrowsException -//nullHashicorpVaultConfigThrowsException -//incorrectSyntaxUrlInConfigThrowsException -//incorrectlyFormattedUrlInConfigThrowsException -//createReturnsNewHashicorpKeyVaultService -//getType + Auth auth = mock(Auth.class); + when(unauthenticatedVault.auth()).thenReturn(auth); + AuthResponse loginResponse = mock(AuthResponse.class); + when(auth.loginByAppRole(anyString(), anyString(), anyString())).thenReturn(loginResponse); + String token = "token"; + when(loginResponse.getAuthClientToken()).thenReturn(token); + + keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory); + + verify(auth).loginByAppRole("approle", "role-id", "secret-id"); + verify(keyVaultClientFactory).createAuthenticatedClient(any(HashicorpKeyVaultConfig.class), any(VaultConfigFactory.class), any(SslConfigFactory.class), matches(token)); + } + + @Test + public void ifAllEnvVarsSetThenAppRoleIsUsedToAuthenticate() throws Exception { + when(envProvider.getEnv("HASHICORP_ROLE_ID")).thenReturn("role-id"); + when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn("secret-id"); + when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn("env-token"); + + KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); + when(config.getKeys()).thenReturn(keyConfiguration); + + HashicorpKeyVaultConfig keyVaultConfig = mock(HashicorpKeyVaultConfig.class); + when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(keyVaultConfig); + + Vault unauthenticatedVault = mock(Vault.class); + when( + keyVaultClientFactory + .createUnauthenticatedClient(any(HashicorpKeyVaultConfig.class), any(VaultConfigFactory.class), any(SslConfigFactory.class)) + ).thenReturn(unauthenticatedVault); + + Auth auth = mock(Auth.class); + when(unauthenticatedVault.auth()).thenReturn(auth); + AuthResponse loginResponse = mock(AuthResponse.class); + when(auth.loginByAppRole(anyString(), anyString(), anyString())).thenReturn(loginResponse); + String token = "token"; + when(loginResponse.getAuthClientToken()).thenReturn(token); + + keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory); + + verify(auth).loginByAppRole("approle", "role-id", "secret-id"); + verify(keyVaultClientFactory).createAuthenticatedClient(any(HashicorpKeyVaultConfig.class), any(VaultConfigFactory.class), any(SslConfigFactory.class), matches(token)); + } + + @Test + public void ifOnlyTokenEnvVarSetThenTokenIsUsedToAuthenticate() throws Exception { + when(envProvider.getEnv("HASHICORP_ROLE_ID")).thenReturn(null); + when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn(null); + when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn("env-token"); + + KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); + when(config.getKeys()).thenReturn(keyConfiguration); + + HashicorpKeyVaultConfig keyVaultConfig = mock(HashicorpKeyVaultConfig.class); + when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(keyVaultConfig); + + Vault unauthenticatedVault = mock(Vault.class); + when( + keyVaultClientFactory + .createUnauthenticatedClient(any(HashicorpKeyVaultConfig.class), any(VaultConfigFactory.class), any(SslConfigFactory.class)) + ).thenReturn(unauthenticatedVault); + + keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory); + + verify(keyVaultClientFactory).createAuthenticatedClient(any(HashicorpKeyVaultConfig.class), any(VaultConfigFactory.class), any(SslConfigFactory.class), matches("env-token")); + } + +} diff --git a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultServiceFactory.java b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultServiceFactory.java index de6564d6c3..cc4289b2ad 100644 --- a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultServiceFactory.java +++ b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultServiceFactory.java @@ -10,7 +10,7 @@ public interface KeyVaultServiceFactory { - KeyVaultService create(Config config, EnvironmentVariableProvider envProvider); +// KeyVaultService create(Config config, EnvironmentVariableProvider envProvider); KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, KeyVaultClientFactory keyVaultClientFactory); diff --git a/key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/MockAzureKeyVaultServiceFactory.java b/key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/MockAzureKeyVaultServiceFactory.java index c8a692e5ea..b9492a414f 100644 --- a/key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/MockAzureKeyVaultServiceFactory.java +++ b/key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/MockAzureKeyVaultServiceFactory.java @@ -6,14 +6,14 @@ public class MockAzureKeyVaultServiceFactory implements KeyVaultServiceFactory { @Override - public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider) { + public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, KeyVaultClientFactory keyVaultClientFactory) { throw new UnsupportedOperationException("This mock object's method is not expected to be called"); } - @Override - public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, KeyVaultClientFactory keyVaultClientFactory) { - return null; - } +// @Override +// public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, KeyVaultClientFactory keyVaultClientFactory) { +// return null; +// } @Override public KeyVaultType getType() { From 4e7a4ac0f450075550f432cf50bb71111bdce4c5 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Tue, 4 Dec 2018 10:08:05 +0000 Subject: [PATCH 22/57] Add tests for new Hashicorp config factory classes --- .../vault/hashicorp/SslConfigFactoryTest.java | 19 +++++++++++++++++++ .../hashicorp/VaultConfigFactoryTest.java | 19 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/SslConfigFactoryTest.java create mode 100644 key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/VaultConfigFactoryTest.java diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/SslConfigFactoryTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/SslConfigFactoryTest.java new file mode 100644 index 0000000000..4b688468c2 --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/SslConfigFactoryTest.java @@ -0,0 +1,19 @@ +package com.quorum.tessera.key.vault.hashicorp; + +import com.bettercloud.vault.SslConfig; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class SslConfigFactoryTest { + + @Test + public void create() { + SslConfigFactory sslConfigFactory = new SslConfigFactory(); + SslConfig emptySslConfig = new SslConfig(); + + SslConfig result = sslConfigFactory.create(); + + assertThat(result).isEqualToComparingFieldByField(emptySslConfig); + } +} diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/VaultConfigFactoryTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/VaultConfigFactoryTest.java new file mode 100644 index 0000000000..5437bb7735 --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/VaultConfigFactoryTest.java @@ -0,0 +1,19 @@ +package com.quorum.tessera.key.vault.hashicorp; + +import com.bettercloud.vault.VaultConfig; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class VaultConfigFactoryTest { + + @Test + public void create() { + VaultConfigFactory vaultConfigFactory = new VaultConfigFactory(); + VaultConfig emptyVaultConfig = new VaultConfig(); + + VaultConfig result = vaultConfigFactory.create(); + + assertThat(result).isEqualToComparingFieldByField(emptyVaultConfig); + } +} From 3ee62ea8e7ff953eefa6287d6f9a2c6a0d0ae64b Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Tue, 4 Dec 2018 10:35:43 +0000 Subject: [PATCH 23/57] Begin migration to KeyVaultService methods using SecretData types --- .../key/vault/azure/AzureKeyVaultService.java | 57 ++++++++-------- .../azure/AzureKeyVaultServiceFactory.java | 3 +- .../AzureKeyVaultServiceFactoryTest.java | 16 ++--- .../vault/azure/AzureKeyVaultServiceTest.java | 62 +++++++++++------ .../hashicorp/HashicorpKeyVaultService.java | 67 +++++++++---------- .../tessera/key/vault/KeyVaultService.java | 16 ++--- 6 files changed, 119 insertions(+), 102 deletions(-) diff --git a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultService.java b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultService.java index bb3ee74834..04eb7fa299 100644 --- a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultService.java +++ b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultService.java @@ -5,7 +5,6 @@ import com.quorum.tessera.config.AzureKeyVaultConfig; import com.quorum.tessera.key.vault.*; -import java.util.Map; import java.util.Objects; public class AzureKeyVaultService implements KeyVaultService { @@ -20,37 +19,37 @@ public AzureKeyVaultService(AzureKeyVaultConfig keyVaultConfig, AzureKeyVaultCli this.azureKeyVaultClientDelegate = azureKeyVaultClientDelegate; } - public String getSecret(String secretName) { - SecretBundle secretBundle = azureKeyVaultClientDelegate.getSecret(vaultUrl, secretName); - - if(secretBundle == null) { - throw new VaultSecretNotFoundException("Azure Key Vault secret " + secretName + " was not found in vault " + vaultUrl); - } - - return secretBundle.value(); - } - - @Override - public String getSecretFromPath(String secretPath, String secretName) { - return null; - } - - @Override - public SecretBundle setSecret(String secretName, String secret) { - SetSecretRequest setSecretRequest = new SetSecretRequest.Builder(vaultUrl, secretName, secret).build(); - - return this.azureKeyVaultClientDelegate.setSecret(setSecretRequest); - } - - @Override - public Object setSecretAtPath(String secretPath, Map secretData) { - return null; - } +// public String getSecret(String secretName) { +// SecretBundle secretBundle = azureKeyVaultClientDelegate.getSecret(vaultUrl, secretName); +// +// if(secretBundle == null) { +// throw new VaultSecretNotFoundException("Azure Key Vault secret " + secretName + " was not found in vault " + vaultUrl); +// } +// +// return secretBundle.value(); +// } +// +// @Override +// public String getSecretFromPath(String secretPath, String secretName) { +// return null; +// } +// +// @Override +// public SecretBundle setSecret(String secretName, String secret) { +// SetSecretRequest setSecretRequest = new SetSecretRequest.Builder(vaultUrl, secretName, secret).build(); +// +// return this.azureKeyVaultClientDelegate.setSecret(setSecretRequest); +// } +// +// @Override +// public Object setSecretAtPath(String secretPath, Map secretData) { +// return null; +// } @Override public String getSecret(GetSecretData getSecretData) { if(!(getSecretData instanceof AzureGetSecretData)) { - throw new KeyVaultException("Incorrect data type passed to HashicorpKeyVaultService. Type was " + getSecretData.getType()); + throw new KeyVaultException("Incorrect data type passed to AzureKeyVaultService. Type was " + getSecretData.getType()); } AzureGetSecretData azureGetSecretData = (AzureGetSecretData) getSecretData; @@ -67,7 +66,7 @@ public String getSecret(GetSecretData getSecretData) { @Override public Object setSecret(SetSecretData setSecretData) { if(!(setSecretData instanceof AzureSetSecretData)) { - throw new KeyVaultException("Incorrect data type passed to HashicorpKeyVaultService. Type was " + setSecretData.getType()); + throw new KeyVaultException("Incorrect data type passed to AzureKeyVaultService. Type was " + setSecretData.getType()); } AzureSetSecretData azureSetSecretData = (AzureSetSecretData) setSecretData; diff --git a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactory.java b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactory.java index dafb04e16f..81980b0058 100644 --- a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactory.java +++ b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactory.java @@ -2,6 +2,7 @@ import com.quorum.tessera.config.*; import com.quorum.tessera.config.util.EnvironmentVariableProvider; +import com.quorum.tessera.key.vault.KeyVaultClientFactory; import com.quorum.tessera.key.vault.KeyVaultService; import com.quorum.tessera.key.vault.KeyVaultServiceFactory; @@ -15,7 +16,7 @@ public class AzureKeyVaultServiceFactory implements KeyVaultServiceFactory { private final String clientSecretEnvVar = "AZURE_CLIENT_SECRET"; @Override - public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider) { + public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, KeyVaultClientFactory keyVaultClientFactory) { Objects.requireNonNull(config); Objects.requireNonNull(envProvider); diff --git a/key-vault/azure-key-vault/src/test/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactoryTest.java b/key-vault/azure-key-vault/src/test/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactoryTest.java index 6f9f4da2cb..f021593d75 100644 --- a/key-vault/azure-key-vault/src/test/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactoryTest.java +++ b/key-vault/azure-key-vault/src/test/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactoryTest.java @@ -29,17 +29,17 @@ public void setUp() { @Test(expected = NullPointerException.class) public void nullConfigThrowsException() { - azureKeyVaultServiceFactory.create(null, envProvider); + azureKeyVaultServiceFactory.create(null, envProvider, null); } @Test(expected = NullPointerException.class) public void nullEnvVarProviderThrowsException() { - azureKeyVaultServiceFactory.create(config, null); + azureKeyVaultServiceFactory.create(config, null, null); } @Test public void clientIdEnvironmentVariableNotSetThrowsException() { - Throwable ex = catchThrowable(() -> azureKeyVaultServiceFactory.create(config, envProvider)); + Throwable ex = catchThrowable(() -> azureKeyVaultServiceFactory.create(config, envProvider, null)); when(envProvider.getEnv("AZURE_CLIENT_ID")).thenReturn(null); when(envProvider.getEnv("AZURE_CLIENT_SECRET")).thenReturn("secret"); @@ -50,7 +50,7 @@ public void clientIdEnvironmentVariableNotSetThrowsException() { @Test public void clientSecretEnvironmentVariableNotSetThrowsException() { - Throwable ex = catchThrowable(() -> azureKeyVaultServiceFactory.create(config, envProvider)); + Throwable ex = catchThrowable(() -> azureKeyVaultServiceFactory.create(config, envProvider, null)); when(envProvider.getEnv("AZURE_CLIENT_ID")).thenReturn("id"); when(envProvider.getEnv("AZURE_CLIENT_SECRET")).thenReturn(null); @@ -61,7 +61,7 @@ public void clientSecretEnvironmentVariableNotSetThrowsException() { @Test public void bothClientIdAndClientSecretEnvironmentVariablesNotSetThrowsException() { - Throwable ex = catchThrowable(() -> azureKeyVaultServiceFactory.create(config, envProvider)); + Throwable ex = catchThrowable(() -> azureKeyVaultServiceFactory.create(config, envProvider, null)); when(envProvider.getEnv("AZURE_CLIENT_ID")).thenReturn(null); when(envProvider.getEnv("AZURE_CLIENT_SECRET")).thenReturn(null); @@ -75,7 +75,7 @@ public void nullKeyConfigurationThrowsException() { when(envProvider.getEnv(anyString())).thenReturn("envVar"); when(config.getKeys()).thenReturn(null); - Throwable ex = catchThrowable(() -> azureKeyVaultServiceFactory.create(config, envProvider)); + Throwable ex = catchThrowable(() -> azureKeyVaultServiceFactory.create(config, envProvider, null)); assertThat(ex).isExactlyInstanceOf(ConfigException.class); assertThat(ex.getMessage()).contains("Trying to create Azure key vault connection but no Azure configuration provided"); @@ -88,7 +88,7 @@ public void nullKeyVaultConfigurationThrowsException() { when(keyConfiguration.getAzureKeyVaultConfig()).thenReturn(null); when(config.getKeys()).thenReturn(keyConfiguration); - Throwable ex = catchThrowable(() -> azureKeyVaultServiceFactory.create(config, envProvider)); + Throwable ex = catchThrowable(() -> azureKeyVaultServiceFactory.create(config, envProvider, null)); assertThat(ex).isExactlyInstanceOf(ConfigException.class); assertThat(ex.getMessage()).contains("Trying to create Azure key vault connection but no Azure configuration provided"); @@ -102,7 +102,7 @@ public void envVarsAndKeyVaultConfigProvidedCreatesAzureKeyVaultService() { when(keyConfiguration.getAzureKeyVaultConfig()).thenReturn(keyVaultConfig); when(config.getKeys()).thenReturn(keyConfiguration); - KeyVaultService result = azureKeyVaultServiceFactory.create(config, envProvider); + KeyVaultService result = azureKeyVaultServiceFactory.create(config, envProvider, null); assertThat(result).isInstanceOf(AzureKeyVaultService.class); } diff --git a/key-vault/azure-key-vault/src/test/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceTest.java b/key-vault/azure-key-vault/src/test/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceTest.java index 8024003ae9..40bff7977a 100644 --- a/key-vault/azure-key-vault/src/test/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceTest.java +++ b/key-vault/azure-key-vault/src/test/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceTest.java @@ -3,13 +3,14 @@ import com.microsoft.azure.keyvault.models.SecretBundle; import com.microsoft.azure.keyvault.requests.SetSecretRequest; import com.quorum.tessera.config.AzureKeyVaultConfig; +import com.quorum.tessera.key.vault.GetSecretData; +import com.quorum.tessera.key.vault.KeyVaultException; +import com.quorum.tessera.key.vault.SetSecretData; import com.quorum.tessera.key.vault.VaultSecretNotFoundException; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; -import java.util.Collections; - import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; import static org.mockito.Mockito.*; @@ -23,7 +24,7 @@ public void setUp() { } @Test - public void exceptionThrownIfKeyNotFoundInVault() { + public void getSecretExceptionThrownIfKeyNotFoundInVault() { String secretName = "secret"; String vaultUrl = "vaultUrl"; @@ -33,7 +34,10 @@ public void exceptionThrownIfKeyNotFoundInVault() { AzureKeyVaultService azureKeyVaultService = new AzureKeyVaultService(keyVaultConfig, azureKeyVaultClientDelegate); - Throwable throwable = catchThrowable(() -> azureKeyVaultService.getSecret(secretName)); + AzureGetSecretData getSecretData = mock(AzureGetSecretData.class); + when(getSecretData.getSecretName()).thenReturn(secretName); + + Throwable throwable = catchThrowable(() -> azureKeyVaultService.getSecret(getSecretData)); assertThat(throwable).isInstanceOf(VaultSecretNotFoundException.class); assertThat(throwable).hasMessageContaining("Azure Key Vault secret " + secretName + " was not found in vault " + vaultUrl); @@ -49,7 +53,11 @@ public void getSecretUsingUrlInConfig() { when(azureKeyVaultClientDelegate.getSecret(url, secretId)).thenReturn(new SecretBundle()); AzureKeyVaultService azureKeyVaultService = new AzureKeyVaultService(keyVaultConfig, azureKeyVaultClientDelegate); - azureKeyVaultService.getSecret(secretId); + + AzureGetSecretData getSecretData = mock(AzureGetSecretData.class); + when(getSecretData.getSecretName()).thenReturn(secretId); + + azureKeyVaultService.getSecret(getSecretData); verify(azureKeyVaultClientDelegate).getSecret(url, secretId); } @@ -60,11 +68,27 @@ public void vaultUrlIsNotSetIfKeyVaultConfigNotDefined() { AzureKeyVaultService azureKeyVaultService = new AzureKeyVaultService(null, azureKeyVaultClientDelegate); - azureKeyVaultService.getSecret("secret"); + AzureGetSecretData getSecretData = mock(AzureGetSecretData.class); + when(getSecretData.getSecretName()).thenReturn("secret"); + + + azureKeyVaultService.getSecret(getSecretData); verify(azureKeyVaultClientDelegate).getSecret(null, "secret"); } + @Test + public void getSecretThrowsExceptionIfWrongDataImplProvided() { + AzureKeyVaultService azureKeyVaultService = new AzureKeyVaultService(null, azureKeyVaultClientDelegate); + + GetSecretData wrongImpl = mock(GetSecretData.class); + + Throwable ex = catchThrowable(() -> azureKeyVaultService.getSecret(wrongImpl)); + + assertThat(ex).isInstanceOf(KeyVaultException.class); + assertThat(ex.getMessage()).isEqualTo("Incorrect data type passed to AzureKeyVaultService. Type was null"); + } + @Test public void setSecretRequestIsUsedToRetrieveSecretFromVault() { AzureKeyVaultConfig keyVaultConfig = new AzureKeyVaultConfig("url"); @@ -74,7 +98,11 @@ public void setSecretRequestIsUsedToRetrieveSecretFromVault() { String secretName = "id"; String secret = "secret"; - azureKeyVaultService.setSecret(secretName, secret); + AzureSetSecretData setSecretData = mock(AzureSetSecretData.class); + when(setSecretData.getSecretName()).thenReturn(secretName); + when(setSecretData.getSecret()).thenReturn(secret); + + azureKeyVaultService.setSecret(setSecretData); SetSecretRequest expected = new SetSecretRequest.Builder(keyVaultConfig.getUrl(), secretName, secret).build(); @@ -85,22 +113,14 @@ public void setSecretRequestIsUsedToRetrieveSecretFromVault() { } @Test - public void getSecretFromPathDoesNotInteractWithAzureClient() { - AzureKeyVaultConfig keyVaultConfig = mock(AzureKeyVaultConfig.class); - - AzureKeyVaultService azureKeyVaultService = new AzureKeyVaultService(keyVaultConfig, azureKeyVaultClientDelegate); + public void setSecretThrowsExceptionIfWrongDataImplProvided() { + AzureKeyVaultService azureKeyVaultService = new AzureKeyVaultService(null, azureKeyVaultClientDelegate); - assertThat(azureKeyVaultService.getSecretFromPath("secretPath", "secretName")).isNull(); - verifyZeroInteractions(azureKeyVaultClientDelegate); - } + SetSecretData wrongImpl = mock(SetSecretData.class); - @Test - public void setSecretAtPathReturnsNull() { - AzureKeyVaultConfig keyVaultConfig = mock(AzureKeyVaultConfig.class); - - AzureKeyVaultService azureKeyVaultService = new AzureKeyVaultService(keyVaultConfig, azureKeyVaultClientDelegate); + Throwable ex = catchThrowable(() -> azureKeyVaultService.setSecret(wrongImpl)); - assertThat(azureKeyVaultService.setSecretAtPath("secretPath", Collections.singletonMap("secretName", "secretValue"))).isNull(); - verifyZeroInteractions(azureKeyVaultClientDelegate); + assertThat(ex).isInstanceOf(KeyVaultException.class); + assertThat(ex.getMessage()).isEqualTo("Incorrect data type passed to AzureKeyVaultService. Type was null"); } } diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java index 1587925484..3859ba4104 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java @@ -9,7 +9,6 @@ import com.quorum.tessera.key.vault.KeyVaultService; import com.quorum.tessera.key.vault.SetSecretData; -import java.util.Map; import java.util.Optional; public class HashicorpKeyVaultService implements KeyVaultService { @@ -23,39 +22,39 @@ public HashicorpKeyVaultService(HashicorpKeyVaultConfig keyVaultConfig, Vault va this.vault = vault; } - @Override - public String getSecret(String secretName) { - return null; - } - - @Override - public String getSecretFromPath(String secretPath, String secretName) { - LogicalResponse response; - try { - response = vault.logical().read(secretPath); - } catch(VaultException e) { - throw new HashicorpVaultException("Error getting secret " + secretName + " from path " + secretPath + " - " + e.getMessage()); - } - - return Optional.of(response) - .map(LogicalResponse::getData) - .map(data -> data.get(secretName)) - .orElseThrow(() -> new HashicorpVaultException("No secret " + secretName + " found at path " + secretPath)); - } - - @Override - public Object setSecret(String secretName, String secret) { - return null; - } - - @Override - public Object setSecretAtPath(String secretPath, Map secretData) { - try { - return vault.logical().write(secretPath, secretData); - } catch(VaultException e) { - throw new HashicorpVaultException("Error writing secret to path " + secretPath + " - " + e.getMessage()); - } - } +// @Override +// public String getSecret(String secretName) { +// return null; +// } +// +// @Override +// public String getSecretFromPath(String secretPath, String secretName) { +// LogicalResponse response; +// try { +// response = vault.logical().read(secretPath); +// } catch(VaultException e) { +// throw new HashicorpVaultException("Error getting secret " + secretName + " from path " + secretPath + " - " + e.getMessage()); +// } +// +// return Optional.of(response) +// .map(LogicalResponse::getData) +// .map(data -> data.get(secretName)) +// .orElseThrow(() -> new HashicorpVaultException("No secret " + secretName + " found at path " + secretPath)); +// } +// +// @Override +// public Object setSecret(String secretName, String secret) { +// return null; +// } +// +// @Override +// public Object setSecretAtPath(String secretPath, Map secretData) { +// try { +// return vault.logical().write(secretPath, secretData); +// } catch(VaultException e) { +// throw new HashicorpVaultException("Error writing secret to path " + secretPath + " - " + e.getMessage()); +// } +// } @Override public String getSecret(GetSecretData getSecretData) { diff --git a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultService.java b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultService.java index 4605e9f28b..ebd1178a40 100644 --- a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultService.java +++ b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultService.java @@ -1,15 +1,13 @@ package com.quorum.tessera.key.vault; -import java.util.Map; - public interface KeyVaultService { - String getSecret(String secretName); - - String getSecretFromPath(String secretPath, String secretName); - - Object setSecret(String secretName, String secret); - - Object setSecretAtPath(String secretPath, Map secretData); +// String getSecret(String secretName); +// +// String getSecretFromPath(String secretPath, String secretName); +// +// Object setSecret(String secretName, String secret); +// +// Object setSecretAtPath(String secretPath, Map secretData); String getSecret(GetSecretData getSecretData); From c681778e2c9c62f49762075d8eb755b5a5617038 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Tue, 4 Dec 2018 11:39:30 +0000 Subject: [PATCH 24/57] Add GetSecretData & VaultClient factories for use in KeyPairConverter --- .../keypairconverter/KeyPairConverter.java | 35 ++++++++++++++----- .../key/vault/azure/AzureGetSecretData.java | 8 ++--- .../azure/AzureGetSecretDataFactory.java | 23 ++++++++++++ ...rum.tessera.key.vault.GetSecretDataFactory | 1 + .../HashicorpGetSecretDataFactory.java | 24 +++++++++++++ .../HashicorpKeyVaultClientFactory.java | 6 ++++ ...rum.tessera.key.vault.GetSecretDataFactory | 1 + ...um.tessera.key.vault.KeyVaultClientFactory | 1 + .../key/vault/GetSecretDataFactory.java | 24 +++++++++++++ .../key/vault/KeyVaultClientFactory.java | 17 +++++++++ 10 files changed, 127 insertions(+), 13 deletions(-) create mode 100644 key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureGetSecretDataFactory.java create mode 100644 key-vault/azure-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.GetSecretDataFactory create mode 100644 key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretDataFactory.java create mode 100644 key-vault/hashicorp-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.GetSecretDataFactory create mode 100644 key-vault/hashicorp-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.KeyVaultClientFactory create mode 100644 key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/GetSecretDataFactory.java diff --git a/key-pair-converter/src/main/java/com/quorum/tessera/keypairconverter/KeyPairConverter.java b/key-pair-converter/src/main/java/com/quorum/tessera/keypairconverter/KeyPairConverter.java index 2f7a84d0fa..32b4ada7c2 100644 --- a/key-pair-converter/src/main/java/com/quorum/tessera/keypairconverter/KeyPairConverter.java +++ b/key-pair-converter/src/main/java/com/quorum/tessera/keypairconverter/KeyPairConverter.java @@ -10,11 +10,12 @@ import com.quorum.tessera.encryption.KeyPair; import com.quorum.tessera.encryption.PrivateKey; import com.quorum.tessera.encryption.PublicKey; +import com.quorum.tessera.key.vault.GetSecretDataFactory; +import com.quorum.tessera.key.vault.KeyVaultClientFactory; import com.quorum.tessera.key.vault.KeyVaultService; import com.quorum.tessera.key.vault.KeyVaultServiceFactory; -import java.util.Base64; -import java.util.Collection; +import java.util.*; import java.util.stream.Collectors; @@ -43,21 +44,37 @@ private KeyPair convert(ConfigKeyPair configKeyPair) { if(configKeyPair instanceof AzureVaultKeyPair) { KeyVaultServiceFactory keyVaultServiceFactory = KeyVaultServiceFactory.getInstance(KeyVaultType.AZURE); - KeyVaultService keyVaultService = keyVaultServiceFactory.create(config, envProvider); - AzureVaultKeyPair akp = (AzureVaultKeyPair) configKeyPair; + KeyVaultClientFactory keyVaultClientFactory = KeyVaultClientFactory.getInstance(KeyVaultType.AZURE); + GetSecretDataFactory getSecretDataFactory = GetSecretDataFactory.getInstance(KeyVaultType.AZURE); + + KeyVaultService keyVaultService = keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory); - base64PublicKey = keyVaultService.getSecret(akp.getPublicKeyId()); - base64PrivateKey = keyVaultService.getSecret(akp.getPrivateKeyId()); + AzureVaultKeyPair akp = (AzureVaultKeyPair) configKeyPair; + Map publicKeyData = Collections.singletonMap("secretName", akp.getPublicKeyId()); + Map privateKeyData = Collections.singletonMap("secretName", akp.getPrivateKeyId()); + base64PublicKey = keyVaultService.getSecret(getSecretDataFactory.create(publicKeyData)); + base64PrivateKey = keyVaultService.getSecret(getSecretDataFactory.create(privateKeyData)); } else if(configKeyPair instanceof HashicorpVaultKeyPair) { KeyVaultServiceFactory keyVaultServiceFactory = KeyVaultServiceFactory.getInstance(KeyVaultType.HASHICORP); - KeyVaultService keyVaultService = keyVaultServiceFactory.create(config, envProvider); + KeyVaultClientFactory keyVaultClientFactory = KeyVaultClientFactory.getInstance(KeyVaultType.HASHICORP); + GetSecretDataFactory getSecretDataFactory = GetSecretDataFactory.getInstance(KeyVaultType.HASHICORP); + + KeyVaultService keyVaultService = keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory); + HashicorpVaultKeyPair hkp = (HashicorpVaultKeyPair) configKeyPair; + Map publicKeyData = new HashMap<>(); + publicKeyData.put("secretName", hkp.getPublicKeyId()); + publicKeyData.put("secretPath", hkp.getSecretPath()); + + Map privateKeyData = new HashMap<>(); + privateKeyData.put("secretName", hkp.getPrivateKeyId()); + privateKeyData.put("secretPath", hkp.getSecretPath()); - base64PublicKey = keyVaultService.getSecretFromPath(hkp.getSecretPath(), hkp.getPublicKeyId()); - base64PrivateKey = keyVaultService.getSecretFromPath(hkp.getSecretPath(), hkp.getPrivateKeyId()); + base64PublicKey = keyVaultService.getSecret(getSecretDataFactory.create(publicKeyData)); + base64PrivateKey = keyVaultService.getSecret(getSecretDataFactory.create(privateKeyData)); } else { diff --git a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureGetSecretData.java b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureGetSecretData.java index 5893fcc974..fefc2b1cbf 100644 --- a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureGetSecretData.java +++ b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureGetSecretData.java @@ -11,12 +11,12 @@ public AzureGetSecretData(String secretName) { this.secretName = secretName; } - public String getSecretName() { - return secretName; - } - @Override public KeyVaultType getType() { return KeyVaultType.AZURE; } + + public String getSecretName() { + return secretName; + } } diff --git a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureGetSecretDataFactory.java b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureGetSecretDataFactory.java new file mode 100644 index 0000000000..987a66411f --- /dev/null +++ b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureGetSecretDataFactory.java @@ -0,0 +1,23 @@ +package com.quorum.tessera.key.vault.azure; + +import com.quorum.tessera.config.KeyVaultType; +import com.quorum.tessera.key.vault.GetSecretData; +import com.quorum.tessera.key.vault.GetSecretDataFactory; + +import java.util.Map; + +public class AzureGetSecretDataFactory implements GetSecretDataFactory { + @Override + public GetSecretData create(Map data) { + if(!data.containsKey("secretName")) { + throw new IllegalArgumentException("data must contain value with key 'secretName' and value with key 'secretPath'"); + } + + return new AzureGetSecretData(data.get("secretName")); + } + + @Override + public KeyVaultType getType() { + return KeyVaultType.AZURE; + } +} diff --git a/key-vault/azure-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.GetSecretDataFactory b/key-vault/azure-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.GetSecretDataFactory new file mode 100644 index 0000000000..213a426a6a --- /dev/null +++ b/key-vault/azure-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.GetSecretDataFactory @@ -0,0 +1 @@ +com.quorum.tessera.key.vault.azure.AzureGetSecretDataFactory \ No newline at end of file diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretDataFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretDataFactory.java new file mode 100644 index 0000000000..2156e9398e --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretDataFactory.java @@ -0,0 +1,24 @@ +package com.quorum.tessera.key.vault.hashicorp; + +import com.quorum.tessera.config.KeyVaultType; +import com.quorum.tessera.key.vault.GetSecretData; +import com.quorum.tessera.key.vault.GetSecretDataFactory; + +import java.util.Map; + +public class HashicorpGetSecretDataFactory implements GetSecretDataFactory { + + @Override + public GetSecretData create(Map data) { + if(!data.containsKey("secretName") || !data.containsKey("secretPath")) { + throw new IllegalArgumentException("data must contain value with key 'secretName' and value with key 'secretPath'"); + } + + return new HashicorpGetSecretData(data.get("secretPath"), data.get("secretName")); + } + + @Override + public KeyVaultType getType() { + return KeyVaultType.HASHICORP; + } +} diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactory.java index 1861621665..5b7ac0309a 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactory.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactory.java @@ -4,6 +4,7 @@ import com.bettercloud.vault.Vault; import com.bettercloud.vault.VaultConfig; import com.quorum.tessera.config.HashicorpKeyVaultConfig; +import com.quorum.tessera.config.KeyVaultType; import com.quorum.tessera.key.vault.KeyVaultClientFactory; public class HashicorpKeyVaultClientFactory implements KeyVaultClientFactory { @@ -42,4 +43,9 @@ private VaultConfig createBaseVaultConfig(HashicorpKeyVaultConfig keyVaultConfig } return vaultConfig; } + + @Override + public KeyVaultType getType() { + return KeyVaultType.HASHICORP; + } } diff --git a/key-vault/hashicorp-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.GetSecretDataFactory b/key-vault/hashicorp-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.GetSecretDataFactory new file mode 100644 index 0000000000..15eea9b68a --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.GetSecretDataFactory @@ -0,0 +1 @@ +com.quorum.tessera.key.vault.hashicorp.HashicorpGetSecretDataFactory \ No newline at end of file diff --git a/key-vault/hashicorp-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.KeyVaultClientFactory b/key-vault/hashicorp-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.KeyVaultClientFactory new file mode 100644 index 0000000000..2ad1eb744b --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.KeyVaultClientFactory @@ -0,0 +1 @@ +com.quorum.tessera.key.vault.hashicorp.HashicorpKeyVaultClientFactory \ No newline at end of file diff --git a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/GetSecretDataFactory.java b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/GetSecretDataFactory.java new file mode 100644 index 0000000000..904a746ec0 --- /dev/null +++ b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/GetSecretDataFactory.java @@ -0,0 +1,24 @@ +package com.quorum.tessera.key.vault; + +import com.quorum.tessera.config.KeyVaultType; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.ServiceLoader; + +public interface GetSecretDataFactory { + GetSecretData create(Map data); + + KeyVaultType getType(); + + static GetSecretDataFactory getInstance(KeyVaultType keyVaultType) { + List providers = new ArrayList<>(); + ServiceLoader.load(GetSecretDataFactory.class).forEach(providers::add); + + return providers.stream() + .filter(factory -> factory.getType() == keyVaultType) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException(keyVaultType + " implementation of GetSecretDataFactory was not found on the classpath")); + } +} diff --git a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultClientFactory.java b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultClientFactory.java index 4724e4ac1a..2262a799a1 100644 --- a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultClientFactory.java +++ b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultClientFactory.java @@ -1,5 +1,22 @@ package com.quorum.tessera.key.vault; +import com.quorum.tessera.config.KeyVaultType; + +import java.util.ArrayList; +import java.util.List; +import java.util.ServiceLoader; + public interface KeyVaultClientFactory { + KeyVaultType getType(); + + static KeyVaultClientFactory getInstance(KeyVaultType keyVaultType) { + List providers = new ArrayList<>(); + ServiceLoader.load(KeyVaultClientFactory.class).forEach(providers::add); + + return providers.stream() + .filter(factory -> factory.getType() == keyVaultType) + .findFirst() + .orElse(null); + } } From b6654af32bfb7cc0adddd1e9a54728dac8eac267 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Tue, 4 Dec 2018 13:26:46 +0000 Subject: [PATCH 25/57] Migrate keygeneration code to use SetSecretData types --- .../generation/AzureVaultKeyGenerator.java | 15 +++++++- .../DefaultKeyGeneratorFactory.java | 9 +++-- .../HashicorpVaultKeyGenerator.java | 13 ++++++- .../azure/AzureSetSecretDataFactory.java | 30 ++++++++++++++++ ...rum.tessera.key.vault.SetSecretDataFactory | 1 + .../HashicorpSetSecretDataFactory.java | 35 +++++++++++++++++++ ...rum.tessera.key.vault.SetSecretDataFactory | 1 + .../key/vault/SetSecretDataFactory.java | 24 +++++++++++++ 8 files changed, 124 insertions(+), 4 deletions(-) create mode 100644 key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureSetSecretDataFactory.java create mode 100644 key-vault/azure-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.SetSecretDataFactory create mode 100644 key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretDataFactory.java create mode 100644 key-vault/hashicorp-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.SetSecretDataFactory create mode 100644 key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/SetSecretDataFactory.java diff --git a/key-generation/src/main/java/com/quorum/tessera/key/generation/AzureVaultKeyGenerator.java b/key-generation/src/main/java/com/quorum/tessera/key/generation/AzureVaultKeyGenerator.java index 9c7e76471d..2a8ddfaea9 100644 --- a/key-generation/src/main/java/com/quorum/tessera/key/generation/AzureVaultKeyGenerator.java +++ b/key-generation/src/main/java/com/quorum/tessera/key/generation/AzureVaultKeyGenerator.java @@ -1,10 +1,13 @@ package com.quorum.tessera.key.generation; import com.quorum.tessera.config.ArgonOptions; +import com.quorum.tessera.config.KeyVaultType; import com.quorum.tessera.config.keypairs.AzureVaultKeyPair; import com.quorum.tessera.encryption.Key; import com.quorum.tessera.encryption.KeyPair; import com.quorum.tessera.key.vault.KeyVaultService; +import com.quorum.tessera.key.vault.SetSecretData; +import com.quorum.tessera.key.vault.SetSecretDataFactory; import com.quorum.tessera.nacl.NaclFacade; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -12,6 +15,8 @@ import java.nio.charset.UnsupportedCharsetException; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; public class AzureVaultKeyGenerator implements KeyGenerator { @@ -55,7 +60,15 @@ public AzureVaultKeyPair generate(String filename, ArgonOptions encryptionOption } private void saveKeyInVault(String id, Key key) { - keyVaultService.setSecret(id, key.encodeToBase64()); + SetSecretDataFactory setSecretDataFactory = SetSecretDataFactory.getInstance(KeyVaultType.AZURE); + + Map data = new HashMap<>(); + data.put("secretName", id); + data.put("secret", key.encodeToBase64()); + + SetSecretData setSecretData = setSecretDataFactory.create(data); + + keyVaultService.setSecret(setSecretData); LOGGER.debug("Key {} saved to vault with id {}", key.encodeToBase64(), id); LOGGER.info("Key saved to vault with id {}", id); } diff --git a/key-generation/src/main/java/com/quorum/tessera/key/generation/DefaultKeyGeneratorFactory.java b/key-generation/src/main/java/com/quorum/tessera/key/generation/DefaultKeyGeneratorFactory.java index 9b738e0197..380f3362ad 100644 --- a/key-generation/src/main/java/com/quorum/tessera/key/generation/DefaultKeyGeneratorFactory.java +++ b/key-generation/src/main/java/com/quorum/tessera/key/generation/DefaultKeyGeneratorFactory.java @@ -4,6 +4,7 @@ import com.quorum.tessera.config.keys.KeyEncryptorFactory; import com.quorum.tessera.config.util.EnvironmentVariableProvider; import com.quorum.tessera.config.util.PasswordReaderFactory; +import com.quorum.tessera.key.vault.KeyVaultClientFactory; import com.quorum.tessera.key.vault.KeyVaultService; import com.quorum.tessera.key.vault.KeyVaultServiceFactory; import com.quorum.tessera.nacl.NaclFacadeFactory; @@ -24,7 +25,9 @@ public KeyGenerator create(KeyVaultConfig keyVaultConfig) { config.setKeys(keyConfiguration); - final KeyVaultService keyVaultService = keyVaultServiceFactory.create(config, new EnvironmentVariableProvider()); + KeyVaultClientFactory keyVaultClientFactory = KeyVaultClientFactory.getInstance(KeyVaultType.AZURE); + + final KeyVaultService keyVaultService = keyVaultServiceFactory.create(config, new EnvironmentVariableProvider(), keyVaultClientFactory); return new AzureVaultKeyGenerator(NaclFacadeFactory.newFactory().create(), keyVaultService); @@ -33,7 +36,9 @@ public KeyGenerator create(KeyVaultConfig keyVaultConfig) { config.setKeys(keyConfiguration); - final KeyVaultService keyVaultService = keyVaultServiceFactory.create(config, new EnvironmentVariableProvider()); + KeyVaultClientFactory keyVaultClientFactory = KeyVaultClientFactory.getInstance(KeyVaultType.HASHICORP); + + final KeyVaultService keyVaultService = keyVaultServiceFactory.create(config, new EnvironmentVariableProvider(), keyVaultClientFactory); return new HashicorpVaultKeyGenerator(NaclFacadeFactory.newFactory().create(), keyVaultService); } diff --git a/key-generation/src/main/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGenerator.java b/key-generation/src/main/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGenerator.java index f46f0ef834..ad8fdabc10 100644 --- a/key-generation/src/main/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGenerator.java +++ b/key-generation/src/main/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGenerator.java @@ -1,9 +1,12 @@ package com.quorum.tessera.key.generation; import com.quorum.tessera.config.ArgonOptions; +import com.quorum.tessera.config.KeyVaultType; import com.quorum.tessera.config.keypairs.HashicorpVaultKeyPair; import com.quorum.tessera.encryption.KeyPair; import com.quorum.tessera.key.vault.KeyVaultService; +import com.quorum.tessera.key.vault.SetSecretData; +import com.quorum.tessera.key.vault.SetSecretDataFactory; import com.quorum.tessera.nacl.NaclFacade; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,7 +38,15 @@ public HashicorpVaultKeyPair generate(String filename, ArgonOptions encryptionOp keyPairData.put(pubId, keys.getPublicKey().encodeToBase64()); keyPairData.put(privId, keys.getPrivateKey().encodeToBase64()); - keyVaultService.setSecretAtPath(filename, keyPairData); + SetSecretDataFactory setSecretDataFactory = SetSecretDataFactory.getInstance(KeyVaultType.HASHICORP); + + Map data = new HashMap<>(); + data.put("secretPath", filename); + data.put("nameValuePairs", keyPairData); + + SetSecretData setSecretData = setSecretDataFactory.create(data); + + keyVaultService.setSecret(setSecretData); LOGGER.debug("Key {} saved to vault with path {} and id {}", keyPairData.get(pubId), filename, pubId); LOGGER.info("Key saved to vault with path {} and id {}", filename, pubId); LOGGER.debug("Key {} saved to vault with path {} and id {}", keyPairData.get(privId), filename, privId); diff --git a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureSetSecretDataFactory.java b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureSetSecretDataFactory.java new file mode 100644 index 0000000000..e3f1c45be4 --- /dev/null +++ b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureSetSecretDataFactory.java @@ -0,0 +1,30 @@ +package com.quorum.tessera.key.vault.azure; + +import com.quorum.tessera.config.KeyVaultType; +import com.quorum.tessera.key.vault.SetSecretData; +import com.quorum.tessera.key.vault.SetSecretDataFactory; + +import java.util.Map; + +public class AzureSetSecretDataFactory implements SetSecretDataFactory { + @Override + public SetSecretData create(Map data) { + if(!data.containsKey("secretName") || !data.containsKey("secret")) { + throw new IllegalArgumentException("data must contain value with key 'secretName' and value with key 'secret'"); + } + + if(!(data.get("secretName") instanceof String) || !(data.get("secret") instanceof String)) { + throw new IllegalArgumentException("The values for keys 'secretPath' and 'secret' must be of type String for SetSecretData"); + } + + String secretName = (String) data.get("secretName"); + String secret = (String) data.get("secret"); + + return new AzureSetSecretData(secretName, secret); + } + + @Override + public KeyVaultType getType() { + return KeyVaultType.AZURE; + } +} diff --git a/key-vault/azure-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.SetSecretDataFactory b/key-vault/azure-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.SetSecretDataFactory new file mode 100644 index 0000000000..cbedee051f --- /dev/null +++ b/key-vault/azure-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.SetSecretDataFactory @@ -0,0 +1 @@ +com.quorum.tessera.key.vault.azure.AzureSetSecretDataFactory \ No newline at end of file diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretDataFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretDataFactory.java new file mode 100644 index 0000000000..e84d9243e9 --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretDataFactory.java @@ -0,0 +1,35 @@ +package com.quorum.tessera.key.vault.hashicorp; + +import com.quorum.tessera.config.KeyVaultType; +import com.quorum.tessera.key.vault.SetSecretData; +import com.quorum.tessera.key.vault.SetSecretDataFactory; + +import java.util.Map; + +public class HashicorpSetSecretDataFactory implements SetSecretDataFactory { + + @Override + public SetSecretData create(Map data) { + if(!data.containsKey("secretPath") || !data.containsKey("nameValuePairs")) { + throw new IllegalArgumentException("data must contain value with key 'secretPath' and value with key 'nameValuePairs'"); + } + + if(!(data.get("secretPath") instanceof String)) { + throw new IllegalArgumentException("The value for key 'secretPath' must be of type String for SetSecretData"); + } + + if(!(data.get("nameValuePairs") instanceof Map)) { + throw new IllegalArgumentException("The value for key 'nameValuePairs' must be of type Map for SetSecretData"); + } + + String secretPath = (String) data.get("secretPath"); + Map nameValuePairs = (Map) data.get("nameValuePairs"); + + return new HashicorpSetSecretData(secretPath, nameValuePairs); + } + + @Override + public KeyVaultType getType() { + return KeyVaultType.HASHICORP; + } +} diff --git a/key-vault/hashicorp-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.SetSecretDataFactory b/key-vault/hashicorp-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.SetSecretDataFactory new file mode 100644 index 0000000000..779360d1ac --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.SetSecretDataFactory @@ -0,0 +1 @@ +com.quorum.tessera.key.vault.hashicorp.HashicorpSetSecretDataFactory \ No newline at end of file diff --git a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/SetSecretDataFactory.java b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/SetSecretDataFactory.java new file mode 100644 index 0000000000..0421214a1e --- /dev/null +++ b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/SetSecretDataFactory.java @@ -0,0 +1,24 @@ +package com.quorum.tessera.key.vault; + +import com.quorum.tessera.config.KeyVaultType; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.ServiceLoader; + +public interface SetSecretDataFactory { + SetSecretData create(Map data); + + KeyVaultType getType(); + + static SetSecretDataFactory getInstance(KeyVaultType keyVaultType) { + List providers = new ArrayList<>(); + ServiceLoader.load(SetSecretDataFactory.class).forEach(providers::add); + + return providers.stream() + .filter(factory -> factory.getType() == keyVaultType) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException(keyVaultType + " implementation of SetSecretDataFactory was not found on the classpath")); + } +} From a2733da1dc2ddb7bd9464f8cb03cf682decc374e Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Tue, 4 Dec 2018 15:04:08 +0000 Subject: [PATCH 26/57] Move SecretData objects to config package so can be used across the app --- .../config/vault/data/AzureGetSecretData.java | 21 ++++++ .../config/vault/data/AzureSetSecretData.java | 27 +++++++ .../config/vault/data/GetSecretData.java | 7 ++ .../vault/data/HashicorpGetSecretData.java | 27 +++++++ .../vault/data/HashicorpSetSecretData.java | 30 ++++++++ .../config/vault/data/SetSecretData.java | 7 ++ .../generation/AzureVaultKeyGenerator.java | 19 +++-- .../HashicorpVaultKeyGenerator.java | 16 ++--- .../AzureVaultKeyGeneratorTest.java | 20 +++--- .../HashicorpVaultKeyGeneratorTest.java | 5 +- .../MockAzureKeyVaultServiceFactory.java | 8 +-- .../MockHashicorpKeyVaultServiceFactory.java | 8 +-- .../keypairconverter/KeyPairConverter.java | 41 ++++++----- .../MockAzureKeyVaultServiceFactory.java | 8 +-- .../MockHashicorpKeyVaultServiceFactory.java | 8 +-- .../key/vault/azure/AzureGetSecretData.java | 44 ++++++------ .../azure/AzureGetSecretDataFactory.java | 46 ++++++------ .../key/vault/azure/AzureKeyVaultService.java | 8 ++- .../key/vault/azure/AzureSetSecretData.java | 56 +++++++-------- .../azure/AzureSetSecretDataFactory.java | 60 ++++++++-------- .../vault/azure/AzureKeyVaultServiceTest.java | 6 +- .../hashicorp/HashicorpGetSecretData.java | 56 +++++++-------- .../HashicorpGetSecretDataFactory.java | 48 ++++++------- .../hashicorp/HashicorpKeyVaultService.java | 6 +- .../hashicorp/HashicorpSetSecretData.java | 62 ++++++++-------- .../HashicorpSetSecretDataFactory.java | 70 +++++++++---------- .../hashicorp/HashicorpGetSecretDataTest.java | 1 + .../HashicorpKeyVaultServiceTest.java | 6 +- .../hashicorp/HashicorpSetSecretDataTest.java | 1 + .../tessera/key/vault/GetSecretData.java | 14 ++-- .../key/vault/GetSecretDataFactory.java | 48 ++++++------- .../tessera/key/vault/KeyVaultService.java | 3 + .../tessera/key/vault/SetSecretData.java | 14 ++-- .../key/vault/SetSecretDataFactory.java | 48 ++++++------- 34 files changed, 497 insertions(+), 352 deletions(-) create mode 100644 config/src/main/java/com/quorum/tessera/config/vault/data/AzureGetSecretData.java create mode 100644 config/src/main/java/com/quorum/tessera/config/vault/data/AzureSetSecretData.java create mode 100644 config/src/main/java/com/quorum/tessera/config/vault/data/GetSecretData.java create mode 100644 config/src/main/java/com/quorum/tessera/config/vault/data/HashicorpGetSecretData.java create mode 100644 config/src/main/java/com/quorum/tessera/config/vault/data/HashicorpSetSecretData.java create mode 100644 config/src/main/java/com/quorum/tessera/config/vault/data/SetSecretData.java diff --git a/config/src/main/java/com/quorum/tessera/config/vault/data/AzureGetSecretData.java b/config/src/main/java/com/quorum/tessera/config/vault/data/AzureGetSecretData.java new file mode 100644 index 0000000000..4aec1d2d16 --- /dev/null +++ b/config/src/main/java/com/quorum/tessera/config/vault/data/AzureGetSecretData.java @@ -0,0 +1,21 @@ +package com.quorum.tessera.config.vault.data; + +import com.quorum.tessera.config.KeyVaultType; + +public class AzureGetSecretData implements GetSecretData { + + private String secretName; + + public AzureGetSecretData(String secretName) { + this.secretName = secretName; + } + + @Override + public KeyVaultType getType() { + return KeyVaultType.AZURE; + } + + public String getSecretName() { + return secretName; + } +} diff --git a/config/src/main/java/com/quorum/tessera/config/vault/data/AzureSetSecretData.java b/config/src/main/java/com/quorum/tessera/config/vault/data/AzureSetSecretData.java new file mode 100644 index 0000000000..d40a1f1d08 --- /dev/null +++ b/config/src/main/java/com/quorum/tessera/config/vault/data/AzureSetSecretData.java @@ -0,0 +1,27 @@ +package com.quorum.tessera.config.vault.data; + +import com.quorum.tessera.config.KeyVaultType; + +public class AzureSetSecretData implements SetSecretData { + private String secretName; + + private String secret; + + public AzureSetSecretData(String secretName, String secret) { + this.secretName = secretName; + this.secret = secret; + } + + public String getSecretName() { + return secretName; + } + + public String getSecret() { + return secret; + } + + @Override + public KeyVaultType getType() { + return KeyVaultType.AZURE; + } +} diff --git a/config/src/main/java/com/quorum/tessera/config/vault/data/GetSecretData.java b/config/src/main/java/com/quorum/tessera/config/vault/data/GetSecretData.java new file mode 100644 index 0000000000..2ec5dc1499 --- /dev/null +++ b/config/src/main/java/com/quorum/tessera/config/vault/data/GetSecretData.java @@ -0,0 +1,7 @@ +package com.quorum.tessera.config.vault.data; + +import com.quorum.tessera.config.KeyVaultType; + +public interface GetSecretData { + KeyVaultType getType(); +} diff --git a/config/src/main/java/com/quorum/tessera/config/vault/data/HashicorpGetSecretData.java b/config/src/main/java/com/quorum/tessera/config/vault/data/HashicorpGetSecretData.java new file mode 100644 index 0000000000..ea55cf275a --- /dev/null +++ b/config/src/main/java/com/quorum/tessera/config/vault/data/HashicorpGetSecretData.java @@ -0,0 +1,27 @@ +package com.quorum.tessera.config.vault.data; + +import com.quorum.tessera.config.KeyVaultType; + +public class HashicorpGetSecretData implements GetSecretData { + private String secretPath; + + private String secretName; + + public HashicorpGetSecretData(String secretPath, String secretName) { + this.secretPath = secretPath; + this.secretName = secretName; + } + + public String getSecretPath() { + return secretPath; + } + + public String getSecretName() { + return secretName; + } + + @Override + public KeyVaultType getType() { + return KeyVaultType.HASHICORP; + } +} diff --git a/config/src/main/java/com/quorum/tessera/config/vault/data/HashicorpSetSecretData.java b/config/src/main/java/com/quorum/tessera/config/vault/data/HashicorpSetSecretData.java new file mode 100644 index 0000000000..46dc8bab37 --- /dev/null +++ b/config/src/main/java/com/quorum/tessera/config/vault/data/HashicorpSetSecretData.java @@ -0,0 +1,30 @@ +package com.quorum.tessera.config.vault.data; + +import com.quorum.tessera.config.KeyVaultType; + +import java.util.Map; + +public class HashicorpSetSecretData implements SetSecretData { + + private String secretPath; + + private Map nameValuePairs; + + public HashicorpSetSecretData(String secretPath, Map nameValuePairs) { + this.secretPath = secretPath; + this.nameValuePairs = nameValuePairs; + } + + public String getSecretPath() { + return secretPath; + } + + public Map getNameValuePairs() { + return nameValuePairs; + } + + @Override + public KeyVaultType getType() { + return KeyVaultType.HASHICORP; + } +} diff --git a/config/src/main/java/com/quorum/tessera/config/vault/data/SetSecretData.java b/config/src/main/java/com/quorum/tessera/config/vault/data/SetSecretData.java new file mode 100644 index 0000000000..825cc0b918 --- /dev/null +++ b/config/src/main/java/com/quorum/tessera/config/vault/data/SetSecretData.java @@ -0,0 +1,7 @@ +package com.quorum.tessera.config.vault.data; + +import com.quorum.tessera.config.KeyVaultType; + +public interface SetSecretData { + KeyVaultType getType(); +} diff --git a/key-generation/src/main/java/com/quorum/tessera/key/generation/AzureVaultKeyGenerator.java b/key-generation/src/main/java/com/quorum/tessera/key/generation/AzureVaultKeyGenerator.java index 2a8ddfaea9..3775272dba 100644 --- a/key-generation/src/main/java/com/quorum/tessera/key/generation/AzureVaultKeyGenerator.java +++ b/key-generation/src/main/java/com/quorum/tessera/key/generation/AzureVaultKeyGenerator.java @@ -1,13 +1,12 @@ package com.quorum.tessera.key.generation; import com.quorum.tessera.config.ArgonOptions; -import com.quorum.tessera.config.KeyVaultType; import com.quorum.tessera.config.keypairs.AzureVaultKeyPair; +import com.quorum.tessera.config.vault.data.AzureSetSecretData; +import com.quorum.tessera.config.vault.data.SetSecretData; import com.quorum.tessera.encryption.Key; import com.quorum.tessera.encryption.KeyPair; import com.quorum.tessera.key.vault.KeyVaultService; -import com.quorum.tessera.key.vault.SetSecretData; -import com.quorum.tessera.key.vault.SetSecretDataFactory; import com.quorum.tessera.nacl.NaclFacade; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -15,8 +14,6 @@ import java.nio.charset.UnsupportedCharsetException; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.HashMap; -import java.util.Map; public class AzureVaultKeyGenerator implements KeyGenerator { @@ -60,13 +57,15 @@ public AzureVaultKeyPair generate(String filename, ArgonOptions encryptionOption } private void saveKeyInVault(String id, Key key) { - SetSecretDataFactory setSecretDataFactory = SetSecretDataFactory.getInstance(KeyVaultType.AZURE); +// SetSecretDataFactory setSecretDataFactory = SetSecretDataFactory.getInstance(KeyVaultType.AZURE); - Map data = new HashMap<>(); - data.put("secretName", id); - data.put("secret", key.encodeToBase64()); +// Map data = new HashMap<>(); +// data.put("secretName", id); +// data.put("secret", key.encodeToBase64()); - SetSecretData setSecretData = setSecretDataFactory.create(data); +// SetSecretData setSecretData = setSecretDataFactory.create(data); + + SetSecretData setSecretData = new AzureSetSecretData(id, key.encodeToBase64()); keyVaultService.setSecret(setSecretData); LOGGER.debug("Key {} saved to vault with id {}", key.encodeToBase64(), id); diff --git a/key-generation/src/main/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGenerator.java b/key-generation/src/main/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGenerator.java index ad8fdabc10..da68e800a1 100644 --- a/key-generation/src/main/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGenerator.java +++ b/key-generation/src/main/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGenerator.java @@ -1,12 +1,11 @@ package com.quorum.tessera.key.generation; import com.quorum.tessera.config.ArgonOptions; -import com.quorum.tessera.config.KeyVaultType; import com.quorum.tessera.config.keypairs.HashicorpVaultKeyPair; +import com.quorum.tessera.config.vault.data.HashicorpSetSecretData; +import com.quorum.tessera.config.vault.data.SetSecretData; import com.quorum.tessera.encryption.KeyPair; import com.quorum.tessera.key.vault.KeyVaultService; -import com.quorum.tessera.key.vault.SetSecretData; -import com.quorum.tessera.key.vault.SetSecretDataFactory; import com.quorum.tessera.nacl.NaclFacade; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,13 +37,14 @@ public HashicorpVaultKeyPair generate(String filename, ArgonOptions encryptionOp keyPairData.put(pubId, keys.getPublicKey().encodeToBase64()); keyPairData.put(privId, keys.getPrivateKey().encodeToBase64()); - SetSecretDataFactory setSecretDataFactory = SetSecretDataFactory.getInstance(KeyVaultType.HASHICORP); +// SetSecretDataFactory setSecretDataFactory = SetSecretDataFactory.getInstance(KeyVaultType.HASHICORP); - Map data = new HashMap<>(); - data.put("secretPath", filename); - data.put("nameValuePairs", keyPairData); +// Map data = new HashMap<>(); +// data.put("secretPath", filename); +// data.put("nameValuePairs", keyPairData); - SetSecretData setSecretData = setSecretDataFactory.create(data); +// SetSecretData setSecretData = setSecretDataFactory.create(data); + SetSecretData setSecretData = new HashicorpSetSecretData(filename, keyPairData); keyVaultService.setSecret(setSecretData); LOGGER.debug("Key {} saved to vault with path {} and id {}", keyPairData.get(pubId), filename, pubId); diff --git a/key-generation/src/test/java/com/quorum/tessera/key/generation/AzureVaultKeyGeneratorTest.java b/key-generation/src/test/java/com/quorum/tessera/key/generation/AzureVaultKeyGeneratorTest.java index 9077597787..2c8ddbf57f 100644 --- a/key-generation/src/test/java/com/quorum/tessera/key/generation/AzureVaultKeyGeneratorTest.java +++ b/key-generation/src/test/java/com/quorum/tessera/key/generation/AzureVaultKeyGeneratorTest.java @@ -47,9 +47,9 @@ public void keysSavedInVaultWithProvidedVaultId() { final AzureVaultKeyPair result = azureVaultKeyGenerator.generate(vaultId, null); - verify(keyVaultService, times(2)).setSecret(any(String.class), any(String.class)); - verify(keyVaultService, times(1)).setSecret(vaultId + "Pub", pub.encodeToBase64()); - verify(keyVaultService, times(1)).setSecret(vaultId + "Key", priv.encodeToBase64()); +// verify(keyVaultService, times(2)).setSecret(any(String.class), any(String.class)); +// verify(keyVaultService, times(1)).setSecret(vaultId + "Pub", pub.encodeToBase64()); +// verify(keyVaultService, times(1)).setSecret(vaultId + "Key", priv.encodeToBase64()); verifyNoMoreInteractions(keyVaultService); final AzureVaultKeyPair expected = new AzureVaultKeyPair(pubVaultId, privVaultId); @@ -63,7 +63,7 @@ public void publicKeyIsSavedToVaultAndIdHasPubSuffix() { azureVaultKeyGenerator.generate(vaultId, null); - verify(keyVaultService, times(1)).setSecret(vaultId + "Pub", pub.encodeToBase64()); +// verify(keyVaultService, times(1)).setSecret(vaultId + "Pub", pub.encodeToBase64()); } @Test @@ -72,7 +72,7 @@ public void privateKeyIsSavedToVaultAndIdHasKeySuffix() { azureVaultKeyGenerator.generate(vaultId, null); - verify(keyVaultService, times(1)).setSecret(vaultId + "Key", priv.encodeToBase64()); +// verify(keyVaultService, times(1)).setSecret(vaultId + "Key", priv.encodeToBase64()); } @Test @@ -82,16 +82,16 @@ public void vaultIdIsFinalComponentOfFilePath() { azureVaultKeyGenerator.generate(path, null); - verify(keyVaultService, times(1)).setSecret(vaultId + "Pub", pub.encodeToBase64()); - verify(keyVaultService, times(1)).setSecret(vaultId + "Key", priv.encodeToBase64()); +// verify(keyVaultService, times(1)).setSecret(vaultId + "Pub", pub.encodeToBase64()); +// verify(keyVaultService, times(1)).setSecret(vaultId + "Key", priv.encodeToBase64()); } @Test public void ifNoVaultIdProvidedThenSuffixOnlyIsUsed() { azureVaultKeyGenerator.generate(null, null); - verify(keyVaultService, times(1)).setSecret("Pub", pub.encodeToBase64()); - verify(keyVaultService, times(1)).setSecret("Key", priv.encodeToBase64()); +// verify(keyVaultService, times(1)).setSecret("Pub", pub.encodeToBase64()); +// verify(keyVaultService, times(1)).setSecret("Key", priv.encodeToBase64()); } @Test @@ -100,7 +100,7 @@ public void allowedCharactersUsedInVaultIdDoesNotThrowException() { azureVaultKeyGenerator.generate(allowedId, null); - verify(keyVaultService, times(2)).setSecret(any(String.class), any(String.class)); +// verify(keyVaultService, times(2)).setSecret(any(String.class), any(String.class)); } @Test diff --git a/key-generation/src/test/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGeneratorTest.java b/key-generation/src/test/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGeneratorTest.java index c5cfc8d377..7f86814348 100644 --- a/key-generation/src/test/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGeneratorTest.java +++ b/key-generation/src/test/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGeneratorTest.java @@ -11,7 +11,8 @@ import java.util.HashMap; import java.util.Map; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class HashicorpVaultKeyGeneratorTest { @@ -51,7 +52,7 @@ public void generatedKeyPairIsSavedToSpecifiedPathInVaultWithIds() { expectedData.put("publicKey", pub.encodeToBase64()); expectedData.put("privateKey", priv.encodeToBase64()); - verify(keyVaultService).setSecretAtPath(filename, expectedData); +// verify(keyVaultService).setSecretAtPath(filename, expectedData); } } diff --git a/key-generation/src/test/java/com/quorum/tessera/key/generation/MockAzureKeyVaultServiceFactory.java b/key-generation/src/test/java/com/quorum/tessera/key/generation/MockAzureKeyVaultServiceFactory.java index 93343332c7..a66b946701 100644 --- a/key-generation/src/test/java/com/quorum/tessera/key/generation/MockAzureKeyVaultServiceFactory.java +++ b/key-generation/src/test/java/com/quorum/tessera/key/generation/MockAzureKeyVaultServiceFactory.java @@ -3,18 +3,18 @@ import com.quorum.tessera.config.Config; import com.quorum.tessera.config.KeyVaultType; import com.quorum.tessera.config.util.EnvironmentVariableProvider; +import com.quorum.tessera.key.vault.KeyVaultClientFactory; import com.quorum.tessera.key.vault.KeyVaultService; import com.quorum.tessera.key.vault.KeyVaultServiceFactory; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; public class MockAzureKeyVaultServiceFactory implements KeyVaultServiceFactory { @Override - public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider) { + public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, KeyVaultClientFactory keyVaultClientFactory) { KeyVaultService mock = mock(KeyVaultService.class); - when(mock.getSecret("pub")).thenReturn("publicSecret"); - when(mock.getSecret("priv")).thenReturn("privSecret"); +// when(mock.getSecret("pub")).thenReturn("publicSecret"); +// when(mock.getSecret("priv")).thenReturn("privSecret"); return mock; } diff --git a/key-generation/src/test/java/com/quorum/tessera/key/generation/MockHashicorpKeyVaultServiceFactory.java b/key-generation/src/test/java/com/quorum/tessera/key/generation/MockHashicorpKeyVaultServiceFactory.java index 2bfc184f4e..bdf7df01f5 100644 --- a/key-generation/src/test/java/com/quorum/tessera/key/generation/MockHashicorpKeyVaultServiceFactory.java +++ b/key-generation/src/test/java/com/quorum/tessera/key/generation/MockHashicorpKeyVaultServiceFactory.java @@ -3,18 +3,18 @@ import com.quorum.tessera.config.Config; import com.quorum.tessera.config.KeyVaultType; import com.quorum.tessera.config.util.EnvironmentVariableProvider; +import com.quorum.tessera.key.vault.KeyVaultClientFactory; import com.quorum.tessera.key.vault.KeyVaultService; import com.quorum.tessera.key.vault.KeyVaultServiceFactory; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; public class MockHashicorpKeyVaultServiceFactory implements KeyVaultServiceFactory { @Override - public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider) { + public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, KeyVaultClientFactory keyVaultClientFactory) { KeyVaultService mock = mock(KeyVaultService.class); - when(mock.getSecret("pub")).thenReturn("publicSecret"); - when(mock.getSecret("priv")).thenReturn("privSecret"); +// when(mock.getSecret("pub")).thenReturn("publicSecret"); +// when(mock.getSecret("priv")).thenReturn("privSecret"); return mock; } diff --git a/key-pair-converter/src/main/java/com/quorum/tessera/keypairconverter/KeyPairConverter.java b/key-pair-converter/src/main/java/com/quorum/tessera/keypairconverter/KeyPairConverter.java index 32b4ada7c2..477800ee13 100644 --- a/key-pair-converter/src/main/java/com/quorum/tessera/keypairconverter/KeyPairConverter.java +++ b/key-pair-converter/src/main/java/com/quorum/tessera/keypairconverter/KeyPairConverter.java @@ -7,15 +7,18 @@ import com.quorum.tessera.config.keypairs.ConfigKeyPair; import com.quorum.tessera.config.keypairs.HashicorpVaultKeyPair; import com.quorum.tessera.config.util.EnvironmentVariableProvider; +import com.quorum.tessera.config.vault.data.AzureGetSecretData; +import com.quorum.tessera.config.vault.data.GetSecretData; +import com.quorum.tessera.config.vault.data.HashicorpGetSecretData; import com.quorum.tessera.encryption.KeyPair; import com.quorum.tessera.encryption.PrivateKey; import com.quorum.tessera.encryption.PublicKey; -import com.quorum.tessera.key.vault.GetSecretDataFactory; import com.quorum.tessera.key.vault.KeyVaultClientFactory; import com.quorum.tessera.key.vault.KeyVaultService; import com.quorum.tessera.key.vault.KeyVaultServiceFactory; -import java.util.*; +import java.util.Base64; +import java.util.Collection; import java.util.stream.Collectors; @@ -45,36 +48,42 @@ private KeyPair convert(ConfigKeyPair configKeyPair) { KeyVaultServiceFactory keyVaultServiceFactory = KeyVaultServiceFactory.getInstance(KeyVaultType.AZURE); KeyVaultClientFactory keyVaultClientFactory = KeyVaultClientFactory.getInstance(KeyVaultType.AZURE); - GetSecretDataFactory getSecretDataFactory = GetSecretDataFactory.getInstance(KeyVaultType.AZURE); +// GetSecretDataFactory getSecretDataFactory = GetSecretDataFactory.getInstance(KeyVaultType.AZURE); KeyVaultService keyVaultService = keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory); AzureVaultKeyPair akp = (AzureVaultKeyPair) configKeyPair; - Map publicKeyData = Collections.singletonMap("secretName", akp.getPublicKeyId()); - Map privateKeyData = Collections.singletonMap("secretName", akp.getPrivateKeyId()); +// Map publicKeyData = Collections.singletonMap("secretName", akp.getPublicKeyId()); +// Map privateKeyData = Collections.singletonMap("secretName", akp.getPrivateKeyId()); - base64PublicKey = keyVaultService.getSecret(getSecretDataFactory.create(publicKeyData)); - base64PrivateKey = keyVaultService.getSecret(getSecretDataFactory.create(privateKeyData)); + GetSecretData getPublicKeyData = new AzureGetSecretData(akp.getPublicKeyId()); + GetSecretData getPrivateKeyData = new AzureGetSecretData(akp.getPrivateKeyId()); + + base64PublicKey = keyVaultService.getSecret(getPublicKeyData); + base64PrivateKey = keyVaultService.getSecret(getPrivateKeyData); } else if(configKeyPair instanceof HashicorpVaultKeyPair) { KeyVaultServiceFactory keyVaultServiceFactory = KeyVaultServiceFactory.getInstance(KeyVaultType.HASHICORP); KeyVaultClientFactory keyVaultClientFactory = KeyVaultClientFactory.getInstance(KeyVaultType.HASHICORP); - GetSecretDataFactory getSecretDataFactory = GetSecretDataFactory.getInstance(KeyVaultType.HASHICORP); +// GetSecretDataFactory getSecretDataFactory = GetSecretDataFactory.getInstance(KeyVaultType.HASHICORP); KeyVaultService keyVaultService = keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory); HashicorpVaultKeyPair hkp = (HashicorpVaultKeyPair) configKeyPair; - Map publicKeyData = new HashMap<>(); - publicKeyData.put("secretName", hkp.getPublicKeyId()); - publicKeyData.put("secretPath", hkp.getSecretPath()); +// Map publicKeyData = new HashMap<>(); +// publicKeyData.put("secretName", hkp.getPublicKeyId()); +// publicKeyData.put("secretPath", hkp.getSecretPath()); + +// Map privateKeyData = new HashMap<>(); +// privateKeyData.put("secretName", hkp.getPrivateKeyId()); +// privateKeyData.put("secretPath", hkp.getSecretPath()); - Map privateKeyData = new HashMap<>(); - privateKeyData.put("secretName", hkp.getPrivateKeyId()); - privateKeyData.put("secretPath", hkp.getSecretPath()); + GetSecretData getPublicKeyData = new HashicorpGetSecretData(hkp.getSecretPath(), hkp.getPublicKeyId()); + GetSecretData getPrivateKeyData = new HashicorpGetSecretData(hkp.getSecretPath(), hkp.getPrivateKeyId()); - base64PublicKey = keyVaultService.getSecret(getSecretDataFactory.create(publicKeyData)); - base64PrivateKey = keyVaultService.getSecret(getSecretDataFactory.create(privateKeyData)); + base64PublicKey = keyVaultService.getSecret(getPublicKeyData); + base64PrivateKey = keyVaultService.getSecret(getPrivateKeyData); } else { diff --git a/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/MockAzureKeyVaultServiceFactory.java b/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/MockAzureKeyVaultServiceFactory.java index 707cff3ced..08a3ec6f10 100644 --- a/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/MockAzureKeyVaultServiceFactory.java +++ b/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/MockAzureKeyVaultServiceFactory.java @@ -3,18 +3,18 @@ import com.quorum.tessera.config.Config; import com.quorum.tessera.config.KeyVaultType; import com.quorum.tessera.config.util.EnvironmentVariableProvider; +import com.quorum.tessera.key.vault.KeyVaultClientFactory; import com.quorum.tessera.key.vault.KeyVaultService; import com.quorum.tessera.key.vault.KeyVaultServiceFactory; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; public class MockAzureKeyVaultServiceFactory implements KeyVaultServiceFactory { @Override - public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider) { + public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, KeyVaultClientFactory keyVaultClientFactory) { KeyVaultService mock = mock(KeyVaultService.class); - when(mock.getSecret("pub")).thenReturn("publicSecret"); - when(mock.getSecret("priv")).thenReturn("privSecret"); +// when(mock.getSecret("pub")).thenReturn("publicSecret"); +// when(mock.getSecret("priv")).thenReturn("privSecret"); return mock; } diff --git a/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/MockHashicorpKeyVaultServiceFactory.java b/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/MockHashicorpKeyVaultServiceFactory.java index 12beb70fdf..163391931a 100644 --- a/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/MockHashicorpKeyVaultServiceFactory.java +++ b/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/MockHashicorpKeyVaultServiceFactory.java @@ -3,18 +3,18 @@ import com.quorum.tessera.config.Config; import com.quorum.tessera.config.KeyVaultType; import com.quorum.tessera.config.util.EnvironmentVariableProvider; +import com.quorum.tessera.key.vault.KeyVaultClientFactory; import com.quorum.tessera.key.vault.KeyVaultService; import com.quorum.tessera.key.vault.KeyVaultServiceFactory; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; public class MockHashicorpKeyVaultServiceFactory implements KeyVaultServiceFactory { @Override - public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider) { + public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, KeyVaultClientFactory keyVaultClientFactory) { KeyVaultService mock = mock(KeyVaultService.class); - when(mock.getSecretFromPath("secretPath", "pub")).thenReturn("publicSecret"); - when(mock.getSecretFromPath("secretPath", "priv")).thenReturn("privSecret"); +// when(mock.getSecretFromPath("secretPath", "pub")).thenReturn("publicSecret"); +// when(mock.getSecretFromPath("secretPath", "priv")).thenReturn("privSecret"); return mock; } diff --git a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureGetSecretData.java b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureGetSecretData.java index fefc2b1cbf..b855b5b7a4 100644 --- a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureGetSecretData.java +++ b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureGetSecretData.java @@ -1,22 +1,22 @@ -package com.quorum.tessera.key.vault.azure; - -import com.quorum.tessera.config.KeyVaultType; -import com.quorum.tessera.key.vault.GetSecretData; - -public class AzureGetSecretData implements GetSecretData { - - private String secretName; - - public AzureGetSecretData(String secretName) { - this.secretName = secretName; - } - - @Override - public KeyVaultType getType() { - return KeyVaultType.AZURE; - } - - public String getSecretName() { - return secretName; - } -} +//package com.quorum.tessera.key.vault.azure; +// +//import com.quorum.tessera.config.KeyVaultType; +//import com.quorum.tessera.key.vault.GetSecretData; +// +//public class AzureGetSecretData implements GetSecretData { +// +// private String secretName; +// +// public AzureGetSecretData(String secretName) { +// this.secretName = secretName; +// } +// +// @Override +// public KeyVaultType getType() { +// return KeyVaultType.AZURE; +// } +// +// public String getSecretName() { +// return secretName; +// } +//} diff --git a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureGetSecretDataFactory.java b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureGetSecretDataFactory.java index 987a66411f..ac0e0d558c 100644 --- a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureGetSecretDataFactory.java +++ b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureGetSecretDataFactory.java @@ -1,23 +1,23 @@ -package com.quorum.tessera.key.vault.azure; - -import com.quorum.tessera.config.KeyVaultType; -import com.quorum.tessera.key.vault.GetSecretData; -import com.quorum.tessera.key.vault.GetSecretDataFactory; - -import java.util.Map; - -public class AzureGetSecretDataFactory implements GetSecretDataFactory { - @Override - public GetSecretData create(Map data) { - if(!data.containsKey("secretName")) { - throw new IllegalArgumentException("data must contain value with key 'secretName' and value with key 'secretPath'"); - } - - return new AzureGetSecretData(data.get("secretName")); - } - - @Override - public KeyVaultType getType() { - return KeyVaultType.AZURE; - } -} +//package com.quorum.tessera.key.vault.azure; +// +//import com.quorum.tessera.config.KeyVaultType; +//import com.quorum.tessera.key.vault.GetSecretData; +//import com.quorum.tessera.key.vault.GetSecretDataFactory; +// +//import java.util.Map; +// +//public class AzureGetSecretDataFactory implements GetSecretDataFactory { +// @Override +// public GetSecretData create(Map data) { +// if(!data.containsKey("secretName")) { +// throw new IllegalArgumentException("data must contain value with key 'secretName' and value with key 'secretPath'"); +// } +// +// return new AzureGetSecretData(data.get("secretName")); +// } +// +// @Override +// public KeyVaultType getType() { +// return KeyVaultType.AZURE; +// } +//} diff --git a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultService.java b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultService.java index 04eb7fa299..d9d275f9fc 100644 --- a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultService.java +++ b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultService.java @@ -3,7 +3,13 @@ import com.microsoft.azure.keyvault.models.SecretBundle; import com.microsoft.azure.keyvault.requests.SetSecretRequest; import com.quorum.tessera.config.AzureKeyVaultConfig; -import com.quorum.tessera.key.vault.*; +import com.quorum.tessera.config.vault.data.AzureGetSecretData; +import com.quorum.tessera.config.vault.data.AzureSetSecretData; +import com.quorum.tessera.config.vault.data.GetSecretData; +import com.quorum.tessera.config.vault.data.SetSecretData; +import com.quorum.tessera.key.vault.KeyVaultException; +import com.quorum.tessera.key.vault.KeyVaultService; +import com.quorum.tessera.key.vault.VaultSecretNotFoundException; import java.util.Objects; diff --git a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureSetSecretData.java b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureSetSecretData.java index daa263c6b7..7f24695298 100644 --- a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureSetSecretData.java +++ b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureSetSecretData.java @@ -1,28 +1,28 @@ -package com.quorum.tessera.key.vault.azure; - -import com.quorum.tessera.config.KeyVaultType; -import com.quorum.tessera.key.vault.SetSecretData; - -public class AzureSetSecretData implements SetSecretData { - private String secretName; - - private String secret; - - public AzureSetSecretData(String secretName, String secret) { - this.secretName = secretName; - this.secret = secret; - } - - public String getSecretName() { - return secretName; - } - - public String getSecret() { - return secret; - } - - @Override - public KeyVaultType getType() { - return KeyVaultType.AZURE; - } -} +//package com.quorum.tessera.key.vault.azure; +// +//import com.quorum.tessera.config.KeyVaultType; +//import com.quorum.tessera.key.vault.SetSecretData; +// +//public class AzureSetSecretData implements SetSecretData { +// private String secretName; +// +// private String secret; +// +// public AzureSetSecretData(String secretName, String secret) { +// this.secretName = secretName; +// this.secret = secret; +// } +// +// public String getSecretName() { +// return secretName; +// } +// +// public String getSecret() { +// return secret; +// } +// +// @Override +// public KeyVaultType getType() { +// return KeyVaultType.AZURE; +// } +//} diff --git a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureSetSecretDataFactory.java b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureSetSecretDataFactory.java index e3f1c45be4..f125e5e894 100644 --- a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureSetSecretDataFactory.java +++ b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureSetSecretDataFactory.java @@ -1,30 +1,30 @@ -package com.quorum.tessera.key.vault.azure; - -import com.quorum.tessera.config.KeyVaultType; -import com.quorum.tessera.key.vault.SetSecretData; -import com.quorum.tessera.key.vault.SetSecretDataFactory; - -import java.util.Map; - -public class AzureSetSecretDataFactory implements SetSecretDataFactory { - @Override - public SetSecretData create(Map data) { - if(!data.containsKey("secretName") || !data.containsKey("secret")) { - throw new IllegalArgumentException("data must contain value with key 'secretName' and value with key 'secret'"); - } - - if(!(data.get("secretName") instanceof String) || !(data.get("secret") instanceof String)) { - throw new IllegalArgumentException("The values for keys 'secretPath' and 'secret' must be of type String for SetSecretData"); - } - - String secretName = (String) data.get("secretName"); - String secret = (String) data.get("secret"); - - return new AzureSetSecretData(secretName, secret); - } - - @Override - public KeyVaultType getType() { - return KeyVaultType.AZURE; - } -} +//package com.quorum.tessera.key.vault.azure; +// +//import com.quorum.tessera.config.KeyVaultType; +//import com.quorum.tessera.key.vault.SetSecretData; +//import com.quorum.tessera.key.vault.SetSecretDataFactory; +// +//import java.util.Map; +// +//public class AzureSetSecretDataFactory implements SetSecretDataFactory { +// @Override +// public SetSecretData create(Map data) { +// if(!data.containsKey("secretName") || !data.containsKey("secret")) { +// throw new IllegalArgumentException("data must contain value with key 'secretName' and value with key 'secret'"); +// } +// +// if(!(data.get("secretName") instanceof String) || !(data.get("secret") instanceof String)) { +// throw new IllegalArgumentException("The values for keys 'secretPath' and 'secret' must be of type String for SetSecretData"); +// } +// +// String secretName = (String) data.get("secretName"); +// String secret = (String) data.get("secret"); +// +// return new AzureSetSecretData(secretName, secret); +// } +// +// @Override +// public KeyVaultType getType() { +// return KeyVaultType.AZURE; +// } +//} diff --git a/key-vault/azure-key-vault/src/test/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceTest.java b/key-vault/azure-key-vault/src/test/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceTest.java index 40bff7977a..d38b9959b9 100644 --- a/key-vault/azure-key-vault/src/test/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceTest.java +++ b/key-vault/azure-key-vault/src/test/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceTest.java @@ -3,9 +3,11 @@ import com.microsoft.azure.keyvault.models.SecretBundle; import com.microsoft.azure.keyvault.requests.SetSecretRequest; import com.quorum.tessera.config.AzureKeyVaultConfig; -import com.quorum.tessera.key.vault.GetSecretData; +import com.quorum.tessera.config.vault.data.AzureGetSecretData; +import com.quorum.tessera.config.vault.data.AzureSetSecretData; +import com.quorum.tessera.config.vault.data.GetSecretData; +import com.quorum.tessera.config.vault.data.SetSecretData; import com.quorum.tessera.key.vault.KeyVaultException; -import com.quorum.tessera.key.vault.SetSecretData; import com.quorum.tessera.key.vault.VaultSecretNotFoundException; import org.junit.Before; import org.junit.Test; diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretData.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretData.java index a918559486..50c476c659 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretData.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretData.java @@ -1,28 +1,28 @@ -package com.quorum.tessera.key.vault.hashicorp; - -import com.quorum.tessera.config.KeyVaultType; -import com.quorum.tessera.key.vault.GetSecretData; - -public class HashicorpGetSecretData implements GetSecretData { - private String secretPath; - - private String secretName; - - public HashicorpGetSecretData(String secretPath, String secretName) { - this.secretPath = secretPath; - this.secretName = secretName; - } - - public String getSecretPath() { - return secretPath; - } - - public String getSecretName() { - return secretName; - } - - @Override - public KeyVaultType getType() { - return KeyVaultType.HASHICORP; - } -} +//package com.quorum.tessera.key.vault.hashicorp; +// +//import com.quorum.tessera.config.KeyVaultType; +//import com.quorum.tessera.key.vault.GetSecretData; +// +//public class HashicorpGetSecretData implements GetSecretData { +// private String secretPath; +// +// private String secretName; +// +// public HashicorpGetSecretData(String secretPath, String secretName) { +// this.secretPath = secretPath; +// this.secretName = secretName; +// } +// +// public String getSecretPath() { +// return secretPath; +// } +// +// public String getSecretName() { +// return secretName; +// } +// +// @Override +// public KeyVaultType getType() { +// return KeyVaultType.HASHICORP; +// } +//} diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretDataFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretDataFactory.java index 2156e9398e..44ceaa14f1 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretDataFactory.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretDataFactory.java @@ -1,24 +1,24 @@ -package com.quorum.tessera.key.vault.hashicorp; - -import com.quorum.tessera.config.KeyVaultType; -import com.quorum.tessera.key.vault.GetSecretData; -import com.quorum.tessera.key.vault.GetSecretDataFactory; - -import java.util.Map; - -public class HashicorpGetSecretDataFactory implements GetSecretDataFactory { - - @Override - public GetSecretData create(Map data) { - if(!data.containsKey("secretName") || !data.containsKey("secretPath")) { - throw new IllegalArgumentException("data must contain value with key 'secretName' and value with key 'secretPath'"); - } - - return new HashicorpGetSecretData(data.get("secretPath"), data.get("secretName")); - } - - @Override - public KeyVaultType getType() { - return KeyVaultType.HASHICORP; - } -} +//package com.quorum.tessera.key.vault.hashicorp; +// +//import com.quorum.tessera.config.KeyVaultType; +//import com.quorum.tessera.key.vault.GetSecretData; +//import com.quorum.tessera.key.vault.GetSecretDataFactory; +// +//import java.util.Map; +// +//public class HashicorpGetSecretDataFactory implements GetSecretDataFactory { +// +// @Override +// public GetSecretData create(Map data) { +// if(!data.containsKey("secretName") || !data.containsKey("secretPath")) { +// throw new IllegalArgumentException("data must contain value with key 'secretName' and value with key 'secretPath'"); +// } +// +// return new HashicorpGetSecretData(data.get("secretPath"), data.get("secretName")); +// } +// +// @Override +// public KeyVaultType getType() { +// return KeyVaultType.HASHICORP; +// } +//} diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java index 3859ba4104..6f335683cc 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java @@ -4,10 +4,12 @@ import com.bettercloud.vault.VaultException; import com.bettercloud.vault.response.LogicalResponse; import com.quorum.tessera.config.HashicorpKeyVaultConfig; -import com.quorum.tessera.key.vault.GetSecretData; +import com.quorum.tessera.config.vault.data.GetSecretData; +import com.quorum.tessera.config.vault.data.HashicorpGetSecretData; +import com.quorum.tessera.config.vault.data.HashicorpSetSecretData; +import com.quorum.tessera.config.vault.data.SetSecretData; import com.quorum.tessera.key.vault.KeyVaultException; import com.quorum.tessera.key.vault.KeyVaultService; -import com.quorum.tessera.key.vault.SetSecretData; import java.util.Optional; diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretData.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretData.java index 7189b4f2f8..f8528f52b5 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretData.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretData.java @@ -1,31 +1,31 @@ -package com.quorum.tessera.key.vault.hashicorp; - -import com.quorum.tessera.config.KeyVaultType; -import com.quorum.tessera.key.vault.SetSecretData; - -import java.util.Map; - -public class HashicorpSetSecretData implements SetSecretData { - - private String secretPath; - - private Map nameValuePairs; - - public HashicorpSetSecretData(String secretPath, Map nameValuePairs) { - this.secretPath = secretPath; - this.nameValuePairs = nameValuePairs; - } - - public String getSecretPath() { - return secretPath; - } - - public Map getNameValuePairs() { - return nameValuePairs; - } - - @Override - public KeyVaultType getType() { - return KeyVaultType.HASHICORP; - } -} +//package com.quorum.tessera.key.vault.hashicorp; +// +//import com.quorum.tessera.config.KeyVaultType; +//import com.quorum.tessera.key.vault.SetSecretData; +// +//import java.util.Map; +// +//public class HashicorpSetSecretData implements SetSecretData { +// +// private String secretPath; +// +// private Map nameValuePairs; +// +// public HashicorpSetSecretData(String secretPath, Map nameValuePairs) { +// this.secretPath = secretPath; +// this.nameValuePairs = nameValuePairs; +// } +// +// public String getSecretPath() { +// return secretPath; +// } +// +// public Map getNameValuePairs() { +// return nameValuePairs; +// } +// +// @Override +// public KeyVaultType getType() { +// return KeyVaultType.HASHICORP; +// } +//} diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretDataFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretDataFactory.java index e84d9243e9..821e849be2 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretDataFactory.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretDataFactory.java @@ -1,35 +1,35 @@ -package com.quorum.tessera.key.vault.hashicorp; - -import com.quorum.tessera.config.KeyVaultType; -import com.quorum.tessera.key.vault.SetSecretData; -import com.quorum.tessera.key.vault.SetSecretDataFactory; - -import java.util.Map; - -public class HashicorpSetSecretDataFactory implements SetSecretDataFactory { - - @Override - public SetSecretData create(Map data) { - if(!data.containsKey("secretPath") || !data.containsKey("nameValuePairs")) { - throw new IllegalArgumentException("data must contain value with key 'secretPath' and value with key 'nameValuePairs'"); - } - - if(!(data.get("secretPath") instanceof String)) { - throw new IllegalArgumentException("The value for key 'secretPath' must be of type String for SetSecretData"); - } - - if(!(data.get("nameValuePairs") instanceof Map)) { - throw new IllegalArgumentException("The value for key 'nameValuePairs' must be of type Map for SetSecretData"); - } - - String secretPath = (String) data.get("secretPath"); - Map nameValuePairs = (Map) data.get("nameValuePairs"); - - return new HashicorpSetSecretData(secretPath, nameValuePairs); - } - - @Override - public KeyVaultType getType() { - return KeyVaultType.HASHICORP; - } -} +//package com.quorum.tessera.key.vault.hashicorp; +// +//import com.quorum.tessera.config.KeyVaultType; +//import com.quorum.tessera.key.vault.SetSecretData; +//import com.quorum.tessera.key.vault.SetSecretDataFactory; +// +//import java.util.Map; +// +//public class HashicorpSetSecretDataFactory implements SetSecretDataFactory { +// +// @Override +// public SetSecretData create(Map data) { +// if(!data.containsKey("secretPath") || !data.containsKey("nameValuePairs")) { +// throw new IllegalArgumentException("data must contain value with key 'secretPath' and value with key 'nameValuePairs'"); +// } +// +// if(!(data.get("secretPath") instanceof String)) { +// throw new IllegalArgumentException("The value for key 'secretPath' must be of type String for SetSecretData"); +// } +// +// if(!(data.get("nameValuePairs") instanceof Map)) { +// throw new IllegalArgumentException("The value for key 'nameValuePairs' must be of type Map for SetSecretData"); +// } +// +// String secretPath = (String) data.get("secretPath"); +// Map nameValuePairs = (Map) data.get("nameValuePairs"); +// +// return new HashicorpSetSecretData(secretPath, nameValuePairs); +// } +// +// @Override +// public KeyVaultType getType() { +// return KeyVaultType.HASHICORP; +// } +//} diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretDataTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretDataTest.java index cc40527994..863834dd99 100644 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretDataTest.java +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretDataTest.java @@ -1,6 +1,7 @@ package com.quorum.tessera.key.vault.hashicorp; import com.quorum.tessera.config.KeyVaultType; +import com.quorum.tessera.config.vault.data.HashicorpGetSecretData; import org.junit.Before; import org.junit.Test; diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java index 984eb37623..9e6beeefc6 100644 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java @@ -5,9 +5,11 @@ import com.bettercloud.vault.api.Logical; import com.bettercloud.vault.response.LogicalResponse; import com.quorum.tessera.config.HashicorpKeyVaultConfig; -import com.quorum.tessera.key.vault.GetSecretData; +import com.quorum.tessera.config.vault.data.GetSecretData; +import com.quorum.tessera.config.vault.data.HashicorpGetSecretData; +import com.quorum.tessera.config.vault.data.HashicorpSetSecretData; +import com.quorum.tessera.config.vault.data.SetSecretData; import com.quorum.tessera.key.vault.KeyVaultException; -import com.quorum.tessera.key.vault.SetSecretData; import org.junit.Before; import org.junit.Test; diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretDataTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretDataTest.java index ae314439e6..310e0121eb 100644 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretDataTest.java +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretDataTest.java @@ -1,6 +1,7 @@ package com.quorum.tessera.key.vault.hashicorp; import com.quorum.tessera.config.KeyVaultType; +import com.quorum.tessera.config.vault.data.HashicorpSetSecretData; import org.junit.Before; import org.junit.Test; diff --git a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/GetSecretData.java b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/GetSecretData.java index 6c5887bf82..80941e7de8 100644 --- a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/GetSecretData.java +++ b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/GetSecretData.java @@ -1,7 +1,7 @@ -package com.quorum.tessera.key.vault; - -import com.quorum.tessera.config.KeyVaultType; - -public interface GetSecretData { - KeyVaultType getType(); -} +//package com.quorum.tessera.key.vault; +// +//import com.quorum.tessera.config.KeyVaultType; +// +//public interface GetSecretData { +// KeyVaultType getType(); +//} diff --git a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/GetSecretDataFactory.java b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/GetSecretDataFactory.java index 904a746ec0..36ec6b2535 100644 --- a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/GetSecretDataFactory.java +++ b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/GetSecretDataFactory.java @@ -1,24 +1,24 @@ -package com.quorum.tessera.key.vault; - -import com.quorum.tessera.config.KeyVaultType; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.ServiceLoader; - -public interface GetSecretDataFactory { - GetSecretData create(Map data); - - KeyVaultType getType(); - - static GetSecretDataFactory getInstance(KeyVaultType keyVaultType) { - List providers = new ArrayList<>(); - ServiceLoader.load(GetSecretDataFactory.class).forEach(providers::add); - - return providers.stream() - .filter(factory -> factory.getType() == keyVaultType) - .findFirst() - .orElseThrow(() -> new IllegalArgumentException(keyVaultType + " implementation of GetSecretDataFactory was not found on the classpath")); - } -} +//package com.quorum.tessera.key.vault; +// +//import com.quorum.tessera.config.KeyVaultType; +// +//import java.util.ArrayList; +//import java.util.List; +//import java.util.Map; +//import java.util.ServiceLoader; +// +//public interface GetSecretDataFactory { +// GetSecretData create(Map data); +// +// KeyVaultType getType(); +// +// static GetSecretDataFactory getInstance(KeyVaultType keyVaultType) { +// List providers = new ArrayList<>(); +// ServiceLoader.load(GetSecretDataFactory.class).forEach(providers::add); +// +// return providers.stream() +// .filter(factory -> factory.getType() == keyVaultType) +// .findFirst() +// .orElseThrow(() -> new IllegalArgumentException(keyVaultType + " implementation of GetSecretDataFactory was not found on the classpath")); +// } +//} diff --git a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultService.java b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultService.java index ebd1178a40..cdf84e6433 100644 --- a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultService.java +++ b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultService.java @@ -1,5 +1,8 @@ package com.quorum.tessera.key.vault; +import com.quorum.tessera.config.vault.data.GetSecretData; +import com.quorum.tessera.config.vault.data.SetSecretData; + public interface KeyVaultService { // String getSecret(String secretName); // diff --git a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/SetSecretData.java b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/SetSecretData.java index 74c62bd1db..5f6e8d435b 100644 --- a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/SetSecretData.java +++ b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/SetSecretData.java @@ -1,7 +1,7 @@ -package com.quorum.tessera.key.vault; - -import com.quorum.tessera.config.KeyVaultType; - -public interface SetSecretData { - KeyVaultType getType(); -} +//package com.quorum.tessera.key.vault; +// +//import com.quorum.tessera.config.KeyVaultType; +// +//public interface SetSecretData { +// KeyVaultType getType(); +//} diff --git a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/SetSecretDataFactory.java b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/SetSecretDataFactory.java index 0421214a1e..6e5a809e54 100644 --- a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/SetSecretDataFactory.java +++ b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/SetSecretDataFactory.java @@ -1,24 +1,24 @@ -package com.quorum.tessera.key.vault; - -import com.quorum.tessera.config.KeyVaultType; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.ServiceLoader; - -public interface SetSecretDataFactory { - SetSecretData create(Map data); - - KeyVaultType getType(); - - static SetSecretDataFactory getInstance(KeyVaultType keyVaultType) { - List providers = new ArrayList<>(); - ServiceLoader.load(SetSecretDataFactory.class).forEach(providers::add); - - return providers.stream() - .filter(factory -> factory.getType() == keyVaultType) - .findFirst() - .orElseThrow(() -> new IllegalArgumentException(keyVaultType + " implementation of SetSecretDataFactory was not found on the classpath")); - } -} +//package com.quorum.tessera.key.vault; +// +//import com.quorum.tessera.config.KeyVaultType; +// +//import java.util.ArrayList; +//import java.util.List; +//import java.util.Map; +//import java.util.ServiceLoader; +// +//public interface SetSecretDataFactory { +// SetSecretData create(Map data); +// +// KeyVaultType getType(); +// +// static SetSecretDataFactory getInstance(KeyVaultType keyVaultType) { +// List providers = new ArrayList<>(); +// ServiceLoader.load(SetSecretDataFactory.class).forEach(providers::add); +// +// return providers.stream() +// .filter(factory -> factory.getType() == keyVaultType) +// .findFirst() +// .orElseThrow(() -> new IllegalArgumentException(keyVaultType + " implementation of SetSecretDataFactory was not found on the classpath")); +// } +//} From 535851cb027593c1cbe7bf6744c73947b27a8e46 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Tue, 4 Dec 2018 17:02:24 +0000 Subject: [PATCH 27/57] Clean up no longer needed classes and methods --- .../generation/AzureVaultKeyGenerator.java | 8 -- .../HashicorpVaultKeyGenerator.java | 7 - .../keypairconverter/KeyPairConverter.java | 12 -- .../key/vault/azure/AzureGetSecretData.java | 22 --- .../azure/AzureGetSecretDataFactory.java | 23 --- .../key/vault/azure/AzureKeyVaultService.java | 27 ---- .../key/vault/azure/AzureSetSecretData.java | 28 ---- .../azure/AzureSetSecretDataFactory.java | 30 ---- .../hashicorp/HashicorpGetSecretData.java | 28 ---- .../HashicorpGetSecretDataFactory.java | 24 ---- .../hashicorp/HashicorpKeyVaultService.java | 34 ----- .../hashicorp/HashicorpSetSecretData.java | 31 ---- .../HashicorpSetSecretDataFactory.java | 35 ----- ...icorpKeyVaultSpringServiceFactoryTest.java | 135 ------------------ .../tessera/key/vault/GetSecretData.java | 7 - .../key/vault/GetSecretDataFactory.java | 24 ---- .../tessera/key/vault/KeyVaultService.java | 8 -- .../key/vault/KeyVaultServiceFactory.java | 2 - .../tessera/key/vault/SetSecretData.java | 7 - .../key/vault/SetSecretDataFactory.java | 24 ---- .../MockAzureKeyVaultServiceFactory.java | 5 - 21 files changed, 521 deletions(-) delete mode 100644 key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureGetSecretData.java delete mode 100644 key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureGetSecretDataFactory.java delete mode 100644 key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureSetSecretData.java delete mode 100644 key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureSetSecretDataFactory.java delete mode 100644 key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretData.java delete mode 100644 key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretDataFactory.java delete mode 100644 key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretData.java delete mode 100644 key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretDataFactory.java delete mode 100644 key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringServiceFactoryTest.java delete mode 100644 key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/GetSecretData.java delete mode 100644 key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/GetSecretDataFactory.java delete mode 100644 key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/SetSecretData.java delete mode 100644 key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/SetSecretDataFactory.java diff --git a/key-generation/src/main/java/com/quorum/tessera/key/generation/AzureVaultKeyGenerator.java b/key-generation/src/main/java/com/quorum/tessera/key/generation/AzureVaultKeyGenerator.java index 3775272dba..dc5cfe785d 100644 --- a/key-generation/src/main/java/com/quorum/tessera/key/generation/AzureVaultKeyGenerator.java +++ b/key-generation/src/main/java/com/quorum/tessera/key/generation/AzureVaultKeyGenerator.java @@ -57,14 +57,6 @@ public AzureVaultKeyPair generate(String filename, ArgonOptions encryptionOption } private void saveKeyInVault(String id, Key key) { -// SetSecretDataFactory setSecretDataFactory = SetSecretDataFactory.getInstance(KeyVaultType.AZURE); - -// Map data = new HashMap<>(); -// data.put("secretName", id); -// data.put("secret", key.encodeToBase64()); - -// SetSecretData setSecretData = setSecretDataFactory.create(data); - SetSecretData setSecretData = new AzureSetSecretData(id, key.encodeToBase64()); keyVaultService.setSecret(setSecretData); diff --git a/key-generation/src/main/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGenerator.java b/key-generation/src/main/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGenerator.java index da68e800a1..3311ead318 100644 --- a/key-generation/src/main/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGenerator.java +++ b/key-generation/src/main/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGenerator.java @@ -37,13 +37,6 @@ public HashicorpVaultKeyPair generate(String filename, ArgonOptions encryptionOp keyPairData.put(pubId, keys.getPublicKey().encodeToBase64()); keyPairData.put(privId, keys.getPrivateKey().encodeToBase64()); -// SetSecretDataFactory setSecretDataFactory = SetSecretDataFactory.getInstance(KeyVaultType.HASHICORP); - -// Map data = new HashMap<>(); -// data.put("secretPath", filename); -// data.put("nameValuePairs", keyPairData); - -// SetSecretData setSecretData = setSecretDataFactory.create(data); SetSecretData setSecretData = new HashicorpSetSecretData(filename, keyPairData); keyVaultService.setSecret(setSecretData); diff --git a/key-pair-converter/src/main/java/com/quorum/tessera/keypairconverter/KeyPairConverter.java b/key-pair-converter/src/main/java/com/quorum/tessera/keypairconverter/KeyPairConverter.java index 477800ee13..846b5642db 100644 --- a/key-pair-converter/src/main/java/com/quorum/tessera/keypairconverter/KeyPairConverter.java +++ b/key-pair-converter/src/main/java/com/quorum/tessera/keypairconverter/KeyPairConverter.java @@ -48,13 +48,10 @@ private KeyPair convert(ConfigKeyPair configKeyPair) { KeyVaultServiceFactory keyVaultServiceFactory = KeyVaultServiceFactory.getInstance(KeyVaultType.AZURE); KeyVaultClientFactory keyVaultClientFactory = KeyVaultClientFactory.getInstance(KeyVaultType.AZURE); -// GetSecretDataFactory getSecretDataFactory = GetSecretDataFactory.getInstance(KeyVaultType.AZURE); KeyVaultService keyVaultService = keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory); AzureVaultKeyPair akp = (AzureVaultKeyPair) configKeyPair; -// Map publicKeyData = Collections.singletonMap("secretName", akp.getPublicKeyId()); -// Map privateKeyData = Collections.singletonMap("secretName", akp.getPrivateKeyId()); GetSecretData getPublicKeyData = new AzureGetSecretData(akp.getPublicKeyId()); GetSecretData getPrivateKeyData = new AzureGetSecretData(akp.getPrivateKeyId()); @@ -66,25 +63,16 @@ else if(configKeyPair instanceof HashicorpVaultKeyPair) { KeyVaultServiceFactory keyVaultServiceFactory = KeyVaultServiceFactory.getInstance(KeyVaultType.HASHICORP); KeyVaultClientFactory keyVaultClientFactory = KeyVaultClientFactory.getInstance(KeyVaultType.HASHICORP); -// GetSecretDataFactory getSecretDataFactory = GetSecretDataFactory.getInstance(KeyVaultType.HASHICORP); KeyVaultService keyVaultService = keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory); HashicorpVaultKeyPair hkp = (HashicorpVaultKeyPair) configKeyPair; -// Map publicKeyData = new HashMap<>(); -// publicKeyData.put("secretName", hkp.getPublicKeyId()); -// publicKeyData.put("secretPath", hkp.getSecretPath()); - -// Map privateKeyData = new HashMap<>(); -// privateKeyData.put("secretName", hkp.getPrivateKeyId()); -// privateKeyData.put("secretPath", hkp.getSecretPath()); GetSecretData getPublicKeyData = new HashicorpGetSecretData(hkp.getSecretPath(), hkp.getPublicKeyId()); GetSecretData getPrivateKeyData = new HashicorpGetSecretData(hkp.getSecretPath(), hkp.getPrivateKeyId()); base64PublicKey = keyVaultService.getSecret(getPublicKeyData); base64PrivateKey = keyVaultService.getSecret(getPrivateKeyData); - } else { diff --git a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureGetSecretData.java b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureGetSecretData.java deleted file mode 100644 index b855b5b7a4..0000000000 --- a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureGetSecretData.java +++ /dev/null @@ -1,22 +0,0 @@ -//package com.quorum.tessera.key.vault.azure; -// -//import com.quorum.tessera.config.KeyVaultType; -//import com.quorum.tessera.key.vault.GetSecretData; -// -//public class AzureGetSecretData implements GetSecretData { -// -// private String secretName; -// -// public AzureGetSecretData(String secretName) { -// this.secretName = secretName; -// } -// -// @Override -// public KeyVaultType getType() { -// return KeyVaultType.AZURE; -// } -// -// public String getSecretName() { -// return secretName; -// } -//} diff --git a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureGetSecretDataFactory.java b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureGetSecretDataFactory.java deleted file mode 100644 index ac0e0d558c..0000000000 --- a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureGetSecretDataFactory.java +++ /dev/null @@ -1,23 +0,0 @@ -//package com.quorum.tessera.key.vault.azure; -// -//import com.quorum.tessera.config.KeyVaultType; -//import com.quorum.tessera.key.vault.GetSecretData; -//import com.quorum.tessera.key.vault.GetSecretDataFactory; -// -//import java.util.Map; -// -//public class AzureGetSecretDataFactory implements GetSecretDataFactory { -// @Override -// public GetSecretData create(Map data) { -// if(!data.containsKey("secretName")) { -// throw new IllegalArgumentException("data must contain value with key 'secretName' and value with key 'secretPath'"); -// } -// -// return new AzureGetSecretData(data.get("secretName")); -// } -// -// @Override -// public KeyVaultType getType() { -// return KeyVaultType.AZURE; -// } -//} diff --git a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultService.java b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultService.java index d9d275f9fc..018589f9ed 100644 --- a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultService.java +++ b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultService.java @@ -25,33 +25,6 @@ public AzureKeyVaultService(AzureKeyVaultConfig keyVaultConfig, AzureKeyVaultCli this.azureKeyVaultClientDelegate = azureKeyVaultClientDelegate; } -// public String getSecret(String secretName) { -// SecretBundle secretBundle = azureKeyVaultClientDelegate.getSecret(vaultUrl, secretName); -// -// if(secretBundle == null) { -// throw new VaultSecretNotFoundException("Azure Key Vault secret " + secretName + " was not found in vault " + vaultUrl); -// } -// -// return secretBundle.value(); -// } -// -// @Override -// public String getSecretFromPath(String secretPath, String secretName) { -// return null; -// } -// -// @Override -// public SecretBundle setSecret(String secretName, String secret) { -// SetSecretRequest setSecretRequest = new SetSecretRequest.Builder(vaultUrl, secretName, secret).build(); -// -// return this.azureKeyVaultClientDelegate.setSecret(setSecretRequest); -// } -// -// @Override -// public Object setSecretAtPath(String secretPath, Map secretData) { -// return null; -// } - @Override public String getSecret(GetSecretData getSecretData) { if(!(getSecretData instanceof AzureGetSecretData)) { diff --git a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureSetSecretData.java b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureSetSecretData.java deleted file mode 100644 index 7f24695298..0000000000 --- a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureSetSecretData.java +++ /dev/null @@ -1,28 +0,0 @@ -//package com.quorum.tessera.key.vault.azure; -// -//import com.quorum.tessera.config.KeyVaultType; -//import com.quorum.tessera.key.vault.SetSecretData; -// -//public class AzureSetSecretData implements SetSecretData { -// private String secretName; -// -// private String secret; -// -// public AzureSetSecretData(String secretName, String secret) { -// this.secretName = secretName; -// this.secret = secret; -// } -// -// public String getSecretName() { -// return secretName; -// } -// -// public String getSecret() { -// return secret; -// } -// -// @Override -// public KeyVaultType getType() { -// return KeyVaultType.AZURE; -// } -//} diff --git a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureSetSecretDataFactory.java b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureSetSecretDataFactory.java deleted file mode 100644 index f125e5e894..0000000000 --- a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureSetSecretDataFactory.java +++ /dev/null @@ -1,30 +0,0 @@ -//package com.quorum.tessera.key.vault.azure; -// -//import com.quorum.tessera.config.KeyVaultType; -//import com.quorum.tessera.key.vault.SetSecretData; -//import com.quorum.tessera.key.vault.SetSecretDataFactory; -// -//import java.util.Map; -// -//public class AzureSetSecretDataFactory implements SetSecretDataFactory { -// @Override -// public SetSecretData create(Map data) { -// if(!data.containsKey("secretName") || !data.containsKey("secret")) { -// throw new IllegalArgumentException("data must contain value with key 'secretName' and value with key 'secret'"); -// } -// -// if(!(data.get("secretName") instanceof String) || !(data.get("secret") instanceof String)) { -// throw new IllegalArgumentException("The values for keys 'secretPath' and 'secret' must be of type String for SetSecretData"); -// } -// -// String secretName = (String) data.get("secretName"); -// String secret = (String) data.get("secret"); -// -// return new AzureSetSecretData(secretName, secret); -// } -// -// @Override -// public KeyVaultType getType() { -// return KeyVaultType.AZURE; -// } -//} diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretData.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretData.java deleted file mode 100644 index 50c476c659..0000000000 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretData.java +++ /dev/null @@ -1,28 +0,0 @@ -//package com.quorum.tessera.key.vault.hashicorp; -// -//import com.quorum.tessera.config.KeyVaultType; -//import com.quorum.tessera.key.vault.GetSecretData; -// -//public class HashicorpGetSecretData implements GetSecretData { -// private String secretPath; -// -// private String secretName; -// -// public HashicorpGetSecretData(String secretPath, String secretName) { -// this.secretPath = secretPath; -// this.secretName = secretName; -// } -// -// public String getSecretPath() { -// return secretPath; -// } -// -// public String getSecretName() { -// return secretName; -// } -// -// @Override -// public KeyVaultType getType() { -// return KeyVaultType.HASHICORP; -// } -//} diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretDataFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretDataFactory.java deleted file mode 100644 index 44ceaa14f1..0000000000 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretDataFactory.java +++ /dev/null @@ -1,24 +0,0 @@ -//package com.quorum.tessera.key.vault.hashicorp; -// -//import com.quorum.tessera.config.KeyVaultType; -//import com.quorum.tessera.key.vault.GetSecretData; -//import com.quorum.tessera.key.vault.GetSecretDataFactory; -// -//import java.util.Map; -// -//public class HashicorpGetSecretDataFactory implements GetSecretDataFactory { -// -// @Override -// public GetSecretData create(Map data) { -// if(!data.containsKey("secretName") || !data.containsKey("secretPath")) { -// throw new IllegalArgumentException("data must contain value with key 'secretName' and value with key 'secretPath'"); -// } -// -// return new HashicorpGetSecretData(data.get("secretPath"), data.get("secretName")); -// } -// -// @Override -// public KeyVaultType getType() { -// return KeyVaultType.HASHICORP; -// } -//} diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java index 6f335683cc..61c455d041 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java @@ -24,40 +24,6 @@ public HashicorpKeyVaultService(HashicorpKeyVaultConfig keyVaultConfig, Vault va this.vault = vault; } -// @Override -// public String getSecret(String secretName) { -// return null; -// } -// -// @Override -// public String getSecretFromPath(String secretPath, String secretName) { -// LogicalResponse response; -// try { -// response = vault.logical().read(secretPath); -// } catch(VaultException e) { -// throw new HashicorpVaultException("Error getting secret " + secretName + " from path " + secretPath + " - " + e.getMessage()); -// } -// -// return Optional.of(response) -// .map(LogicalResponse::getData) -// .map(data -> data.get(secretName)) -// .orElseThrow(() -> new HashicorpVaultException("No secret " + secretName + " found at path " + secretPath)); -// } -// -// @Override -// public Object setSecret(String secretName, String secret) { -// return null; -// } -// -// @Override -// public Object setSecretAtPath(String secretPath, Map secretData) { -// try { -// return vault.logical().write(secretPath, secretData); -// } catch(VaultException e) { -// throw new HashicorpVaultException("Error writing secret to path " + secretPath + " - " + e.getMessage()); -// } -// } - @Override public String getSecret(GetSecretData getSecretData) { if(!(getSecretData instanceof HashicorpGetSecretData)) { diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretData.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretData.java deleted file mode 100644 index f8528f52b5..0000000000 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretData.java +++ /dev/null @@ -1,31 +0,0 @@ -//package com.quorum.tessera.key.vault.hashicorp; -// -//import com.quorum.tessera.config.KeyVaultType; -//import com.quorum.tessera.key.vault.SetSecretData; -// -//import java.util.Map; -// -//public class HashicorpSetSecretData implements SetSecretData { -// -// private String secretPath; -// -// private Map nameValuePairs; -// -// public HashicorpSetSecretData(String secretPath, Map nameValuePairs) { -// this.secretPath = secretPath; -// this.nameValuePairs = nameValuePairs; -// } -// -// public String getSecretPath() { -// return secretPath; -// } -// -// public Map getNameValuePairs() { -// return nameValuePairs; -// } -// -// @Override -// public KeyVaultType getType() { -// return KeyVaultType.HASHICORP; -// } -//} diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretDataFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretDataFactory.java deleted file mode 100644 index 821e849be2..0000000000 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretDataFactory.java +++ /dev/null @@ -1,35 +0,0 @@ -//package com.quorum.tessera.key.vault.hashicorp; -// -//import com.quorum.tessera.config.KeyVaultType; -//import com.quorum.tessera.key.vault.SetSecretData; -//import com.quorum.tessera.key.vault.SetSecretDataFactory; -// -//import java.util.Map; -// -//public class HashicorpSetSecretDataFactory implements SetSecretDataFactory { -// -// @Override -// public SetSecretData create(Map data) { -// if(!data.containsKey("secretPath") || !data.containsKey("nameValuePairs")) { -// throw new IllegalArgumentException("data must contain value with key 'secretPath' and value with key 'nameValuePairs'"); -// } -// -// if(!(data.get("secretPath") instanceof String)) { -// throw new IllegalArgumentException("The value for key 'secretPath' must be of type String for SetSecretData"); -// } -// -// if(!(data.get("nameValuePairs") instanceof Map)) { -// throw new IllegalArgumentException("The value for key 'nameValuePairs' must be of type Map for SetSecretData"); -// } -// -// String secretPath = (String) data.get("secretPath"); -// Map nameValuePairs = (Map) data.get("nameValuePairs"); -// -// return new HashicorpSetSecretData(secretPath, nameValuePairs); -// } -// -// @Override -// public KeyVaultType getType() { -// return KeyVaultType.HASHICORP; -// } -//} diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringServiceFactoryTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringServiceFactoryTest.java deleted file mode 100644 index 37cd3121f2..0000000000 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultSpringServiceFactoryTest.java +++ /dev/null @@ -1,135 +0,0 @@ -//package com.quorum.tessera.key.vault.hashicorp; -// -//import com.quorum.tessera.config.*; -//import com.quorum.tessera.config.util.EnvironmentVariableProvider; -//import com.quorum.tessera.key.vault.KeyVaultService; -//import org.junit.Before; -//import org.junit.Test; -// -//import static org.assertj.core.api.Assertions.assertThat; -//import static org.assertj.core.api.Assertions.catchThrowable; -//import static org.mockito.ArgumentMatchers.anyString; -//import static org.mockito.Mockito.mock; -//import static org.mockito.Mockito.when; -// -//public class HashicorpKeyVaultSpringServiceFactoryTest { -// -// private HashicorpKeyVaultSpringServiceFactory factory; -// -// private Config config; -// -// private EnvironmentVariableProvider envProvider; -// -// @Before -// public void setUp() { -// config = mock(Config.class); -// envProvider = mock(EnvironmentVariableProvider.class); -// factory = new HashicorpKeyVaultSpringServiceFactory(); -// } -// -// @Test(expected = NullPointerException.class) -// public void nullConfigThrowsException() { -// factory.create(null, envProvider); -// } -// -// @Test(expected = NullPointerException.class) -// public void nullEnvVarProviderThrowsException() { -// factory.create(config, null); -// } -// -// @Test -// public void createThrowsExceptionIfTokenEnvVarNotSet() { -// Config config = mock(Config.class); -// EnvironmentVariableProvider envProvider = mock(EnvironmentVariableProvider.class); -// -// when(envProvider.getEnv(anyString())).thenReturn(null); -// -// Throwable ex = catchThrowable(() -> factory.create(config, envProvider)); -// -// assertThat(ex).isInstanceOf(HashicorpCredentialNotSetException.class); -// assertThat(ex.getMessage()).isEqualTo("HASHICORP_TOKEN must be set"); -// } -// -// @Test -// public void nullKeyConfigurationThrowsException() { -// when(envProvider.getEnv(anyString())).thenReturn("envVar"); -// when(config.getKeys()).thenReturn(null); -// -// Throwable ex = catchThrowable(() -> factory.create(config, envProvider)); -// -// assertThat(ex).isInstanceOf(ConfigException.class); -// assertThat(ex.getMessage()).contains("Trying to create Hashicorp Vault connection but no Vault configuration provided"); -// } -// -// @Test -// public void nullHashicorpVaultConfigThrowsException() { -// when(envProvider.getEnv(anyString())).thenReturn("envVar"); -// KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); -// when(config.getKeys()).thenReturn(keyConfiguration); -// when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(null); -// -// Throwable ex = catchThrowable(() -> factory.create(config, envProvider)); -// -// assertThat(ex).isInstanceOf(ConfigException.class); -// assertThat(ex.getMessage()).contains("Trying to create Hashicorp Vault connection but no Vault configuration provided"); -// } -// -// @Test -// public void incorrectSyntaxUrlInConfigThrowsException() { -// when(envProvider.getEnv(anyString())).thenReturn("envVar"); -// -// KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); -// when(config.getKeys()).thenReturn(keyConfiguration); -// -// HashicorpKeyVaultConfig vaultConfig = new HashicorpKeyVaultConfig(); -// String url = "!@£$%^"; -// vaultConfig.setUrl(url); -// when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(vaultConfig); -// -// Throwable ex = catchThrowable(() -> factory.create(config, envProvider)); -// -// assertThat(ex).isInstanceOf(ConfigException.class); -// assertThat(ex.getMessage()).contains("Provided Hashicorp Vault url is incorrectly formatted"); -// } -// -// @Test -// public void incorrectlyFormattedUrlInConfigThrowsException() { -// when(envProvider.getEnv(anyString())).thenReturn("envVar"); -// -// KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); -// when(config.getKeys()).thenReturn(keyConfiguration); -// -// HashicorpKeyVaultConfig vaultConfig = new HashicorpKeyVaultConfig(); -// String url = "notaurl"; -// vaultConfig.setUrl(url); -// when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(vaultConfig); -// -// Throwable ex = catchThrowable(() -> factory.create(config, envProvider)); -// -// assertThat(ex).isInstanceOf(ConfigException.class); -// assertThat(ex.getMessage()).contains("Provided Hashicorp Vault url is incorrectly formatted"); -// } -// -// @Test -// public void createReturnsNewHashicorpKeyVaultService() { -// when(envProvider.getEnv(anyString())).thenReturn("envVar"); -// -// KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); -// when(config.getKeys()).thenReturn(keyConfiguration); -// -// HashicorpKeyVaultConfig vaultConfig = new HashicorpKeyVaultConfig(); -// String url = "http://someurl"; -// vaultConfig.setUrl(url); -// when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(vaultConfig); -// -// KeyVaultService result = factory.create(config, envProvider); -// -// assertThat(result).isInstanceOf(HashicorpKeyVaultSpringService.class); -// } -// -// @Test -// public void getType() { -// assertThat(factory.getType()).isEqualTo(KeyVaultType.HASHICORP); -// } -// -//} diff --git a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/GetSecretData.java b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/GetSecretData.java deleted file mode 100644 index 80941e7de8..0000000000 --- a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/GetSecretData.java +++ /dev/null @@ -1,7 +0,0 @@ -//package com.quorum.tessera.key.vault; -// -//import com.quorum.tessera.config.KeyVaultType; -// -//public interface GetSecretData { -// KeyVaultType getType(); -//} diff --git a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/GetSecretDataFactory.java b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/GetSecretDataFactory.java deleted file mode 100644 index 36ec6b2535..0000000000 --- a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/GetSecretDataFactory.java +++ /dev/null @@ -1,24 +0,0 @@ -//package com.quorum.tessera.key.vault; -// -//import com.quorum.tessera.config.KeyVaultType; -// -//import java.util.ArrayList; -//import java.util.List; -//import java.util.Map; -//import java.util.ServiceLoader; -// -//public interface GetSecretDataFactory { -// GetSecretData create(Map data); -// -// KeyVaultType getType(); -// -// static GetSecretDataFactory getInstance(KeyVaultType keyVaultType) { -// List providers = new ArrayList<>(); -// ServiceLoader.load(GetSecretDataFactory.class).forEach(providers::add); -// -// return providers.stream() -// .filter(factory -> factory.getType() == keyVaultType) -// .findFirst() -// .orElseThrow(() -> new IllegalArgumentException(keyVaultType + " implementation of GetSecretDataFactory was not found on the classpath")); -// } -//} diff --git a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultService.java b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultService.java index cdf84e6433..a60015dfbb 100644 --- a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultService.java +++ b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultService.java @@ -4,14 +4,6 @@ import com.quorum.tessera.config.vault.data.SetSecretData; public interface KeyVaultService { -// String getSecret(String secretName); -// -// String getSecretFromPath(String secretPath, String secretName); -// -// Object setSecret(String secretName, String secret); -// -// Object setSecretAtPath(String secretPath, Map secretData); - String getSecret(GetSecretData getSecretData); Object setSecret(SetSecretData setSecretData); diff --git a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultServiceFactory.java b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultServiceFactory.java index cc4289b2ad..20e8e3227a 100644 --- a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultServiceFactory.java +++ b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultServiceFactory.java @@ -10,8 +10,6 @@ public interface KeyVaultServiceFactory { -// KeyVaultService create(Config config, EnvironmentVariableProvider envProvider); - KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, KeyVaultClientFactory keyVaultClientFactory); KeyVaultType getType(); diff --git a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/SetSecretData.java b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/SetSecretData.java deleted file mode 100644 index 5f6e8d435b..0000000000 --- a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/SetSecretData.java +++ /dev/null @@ -1,7 +0,0 @@ -//package com.quorum.tessera.key.vault; -// -//import com.quorum.tessera.config.KeyVaultType; -// -//public interface SetSecretData { -// KeyVaultType getType(); -//} diff --git a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/SetSecretDataFactory.java b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/SetSecretDataFactory.java deleted file mode 100644 index 6e5a809e54..0000000000 --- a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/SetSecretDataFactory.java +++ /dev/null @@ -1,24 +0,0 @@ -//package com.quorum.tessera.key.vault; -// -//import com.quorum.tessera.config.KeyVaultType; -// -//import java.util.ArrayList; -//import java.util.List; -//import java.util.Map; -//import java.util.ServiceLoader; -// -//public interface SetSecretDataFactory { -// SetSecretData create(Map data); -// -// KeyVaultType getType(); -// -// static SetSecretDataFactory getInstance(KeyVaultType keyVaultType) { -// List providers = new ArrayList<>(); -// ServiceLoader.load(SetSecretDataFactory.class).forEach(providers::add); -// -// return providers.stream() -// .filter(factory -> factory.getType() == keyVaultType) -// .findFirst() -// .orElseThrow(() -> new IllegalArgumentException(keyVaultType + " implementation of SetSecretDataFactory was not found on the classpath")); -// } -//} diff --git a/key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/MockAzureKeyVaultServiceFactory.java b/key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/MockAzureKeyVaultServiceFactory.java index b9492a414f..ccda494e9d 100644 --- a/key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/MockAzureKeyVaultServiceFactory.java +++ b/key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/MockAzureKeyVaultServiceFactory.java @@ -10,11 +10,6 @@ public KeyVaultService create(Config config, EnvironmentVariableProvider envProv throw new UnsupportedOperationException("This mock object's method is not expected to be called"); } -// @Override -// public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, KeyVaultClientFactory keyVaultClientFactory) { -// return null; -// } - @Override public KeyVaultType getType() { return KeyVaultType.AZURE; From 7e035d8d27d43dfc750421a1dad06009745c2158 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Tue, 4 Dec 2018 17:58:09 +0000 Subject: [PATCH 28/57] Improve TLS support to mutual authentication --- .../config/HashicorpKeyVaultConfig.java | 28 +++++++++++++++++++ .../HashicorpKeyVaultClientFactory.java | 4 ++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/config/src/main/java/com/quorum/tessera/config/HashicorpKeyVaultConfig.java b/config/src/main/java/com/quorum/tessera/config/HashicorpKeyVaultConfig.java index 16c0afa404..0138863445 100644 --- a/config/src/main/java/com/quorum/tessera/config/HashicorpKeyVaultConfig.java +++ b/config/src/main/java/com/quorum/tessera/config/HashicorpKeyVaultConfig.java @@ -23,6 +23,18 @@ public class HashicorpKeyVaultConfig extends ConfigItem implements KeyVaultConfi @XmlJavaTypeAdapter(PathAdapter.class) private Path tlsCertificatePath; + @Valid + @ValidPath(checkExists = true, message = "File does not exist") + @XmlElement(type = String.class) + @XmlJavaTypeAdapter(PathAdapter.class) + private Path tlsKeyPath; + + @Valid + @ValidPath(checkExists = true, message = "File does not exist") + @XmlElement(type = String.class) + @XmlJavaTypeAdapter(PathAdapter.class) + private Path tlsServerCertificatePath; + public HashicorpKeyVaultConfig(String url, Path tlsCertificatePath) { this.url = url; this.tlsCertificatePath = tlsCertificatePath; @@ -47,6 +59,22 @@ public void setTlsCertificatePath(Path tlsCertificatePath) { this.tlsCertificatePath = tlsCertificatePath; } + public Path getTlsKeyPath() { + return tlsKeyPath; + } + + public void setTlsKeyPath(Path tlsKeyPath) { + this.tlsKeyPath = tlsKeyPath; + } + + public Path getTlsServerCertificatePath() { + return tlsServerCertificatePath; + } + + public void setTlsServerCertificatePath(Path tlsServerCertificatePath) { + this.tlsServerCertificatePath = tlsServerCertificatePath; + } + @Override public KeyVaultType getKeyVaultType() { return KeyVaultType.HASHICORP; diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactory.java index 5b7ac0309a..b70767a03f 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactory.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactory.java @@ -35,7 +35,9 @@ private VaultConfig createBaseVaultConfig(HashicorpKeyVaultConfig keyVaultConfig SslConfig sslConfig = sslConfigFactory.create(); VaultCallback.execute( - () -> sslConfig.pemFile(keyVaultConfig.getTlsCertificatePath().toFile()) + () -> sslConfig.clientPemFile(keyVaultConfig.getTlsCertificatePath().toFile()) + .clientKeyPemFile(keyVaultConfig.getTlsKeyPath().toFile()) + .pemFile(keyVaultConfig.getTlsServerCertificatePath().toFile()) .build() ); From 444b8a9ade8c52f48c26b1795af55dba486c1dd7 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Tue, 4 Dec 2018 18:13:51 +0000 Subject: [PATCH 29/57] Add support for non-default approle auth method paths --- .../tessera/config/HashicorpKeyVaultConfig.java | 15 +++++++++++++++ .../HashicorpKeyVaultServiceFactory.java | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/config/src/main/java/com/quorum/tessera/config/HashicorpKeyVaultConfig.java b/config/src/main/java/com/quorum/tessera/config/HashicorpKeyVaultConfig.java index 0138863445..02f458693b 100644 --- a/config/src/main/java/com/quorum/tessera/config/HashicorpKeyVaultConfig.java +++ b/config/src/main/java/com/quorum/tessera/config/HashicorpKeyVaultConfig.java @@ -17,6 +17,10 @@ public class HashicorpKeyVaultConfig extends ConfigItem implements KeyVaultConfi @XmlAttribute private String url; + @Valid + @XmlElement + private String approlePath; + @Valid @ValidPath(checkExists = true, message = "File does not exist") @XmlElement(type = String.class) @@ -75,6 +79,17 @@ public void setTlsServerCertificatePath(Path tlsServerCertificatePath) { this.tlsServerCertificatePath = tlsServerCertificatePath; } + public String getApprolePath() { + if(approlePath == null) { + return "approle"; + } + return approlePath; + } + + public void setApprolePath(String approlePath) { + this.approlePath = approlePath; + } + @Override public KeyVaultType getKeyVaultType() { return KeyVaultType.HASHICORP; diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java index 77ec6c7d74..ef7558b4ae 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java @@ -77,7 +77,7 @@ else if(isOnlyOneInputNull(roleId, secretId)) { if(roleId != null && secretId != null) { //TODO allow other paths - AuthResponse loginResponse = VaultCallback.execute(() -> unauthenticatedVault.auth().loginByAppRole("approle", roleId, secretId)); + AuthResponse loginResponse = VaultCallback.execute(() -> unauthenticatedVault.auth().loginByAppRole(keyVaultConfig.getApprolePath(), roleId, secretId)); token = loginResponse.getAuthClientToken(); } else { token = authToken; From 2750dac747cf295e70b3157fcf4a3ef11acc2787 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Tue, 4 Dec 2018 19:16:24 +0000 Subject: [PATCH 30/57] Update tests for key generation and key pair converter --- .../AzureVaultKeyGeneratorTest.java | 68 ++++++++++++------- .../HashicorpVaultKeyGeneratorTest.java | 33 +++++++-- .../MockAzureKeyVaultServiceFactory.java | 9 ++- .../MockHashicorpKeyVaultServiceFactory.java | 9 ++- .../MockAzureKeyVaultServiceFactory.java | 9 ++- .../MockHashicorpKeyVaultServiceFactory.java | 9 ++- .../HashicorpKeyVaultServiceFactory.java | 29 -------- 7 files changed, 97 insertions(+), 69 deletions(-) diff --git a/key-generation/src/test/java/com/quorum/tessera/key/generation/AzureVaultKeyGeneratorTest.java b/key-generation/src/test/java/com/quorum/tessera/key/generation/AzureVaultKeyGeneratorTest.java index 2c8ddbf57f..4ea799654e 100644 --- a/key-generation/src/test/java/com/quorum/tessera/key/generation/AzureVaultKeyGeneratorTest.java +++ b/key-generation/src/test/java/com/quorum/tessera/key/generation/AzureVaultKeyGeneratorTest.java @@ -2,6 +2,7 @@ import com.quorum.tessera.config.ArgonOptions; import com.quorum.tessera.config.keypairs.AzureVaultKeyPair; +import com.quorum.tessera.config.vault.data.AzureSetSecretData; import com.quorum.tessera.encryption.KeyPair; import com.quorum.tessera.encryption.PrivateKey; import com.quorum.tessera.encryption.PublicKey; @@ -9,8 +10,10 @@ import com.quorum.tessera.nacl.NaclFacade; import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; import java.nio.charset.UnsupportedCharsetException; +import java.util.List; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; @@ -40,58 +43,73 @@ public void setUp() { } @Test - public void keysSavedInVaultWithProvidedVaultId() { + public void keysSavedInVaultWithProvidedVaultIdAndCorrectSuffix() { final String vaultId = "vaultId"; final String pubVaultId = vaultId + "Pub"; final String privVaultId = vaultId + "Key"; final AzureVaultKeyPair result = azureVaultKeyGenerator.generate(vaultId, null); -// verify(keyVaultService, times(2)).setSecret(any(String.class), any(String.class)); -// verify(keyVaultService, times(1)).setSecret(vaultId + "Pub", pub.encodeToBase64()); -// verify(keyVaultService, times(1)).setSecret(vaultId + "Key", priv.encodeToBase64()); - verifyNoMoreInteractions(keyVaultService); + final ArgumentCaptor captor = ArgumentCaptor.forClass(AzureSetSecretData.class); - final AzureVaultKeyPair expected = new AzureVaultKeyPair(pubVaultId, privVaultId); + verify(keyVaultService, times(2)).setSecret(captor.capture()); - assertThat(result).isEqualToComparingFieldByFieldRecursively(expected); - } - - @Test - public void publicKeyIsSavedToVaultAndIdHasPubSuffix() { - final String vaultId = "vaultId"; + List capturedArgs = captor.getAllValues(); + assertThat(capturedArgs).hasSize(2); - azureVaultKeyGenerator.generate(vaultId, null); + AzureSetSecretData expectedDataPub = new AzureSetSecretData(pubVaultId, pub.encodeToBase64()); + AzureSetSecretData expectedDataPriv = new AzureSetSecretData(privVaultId, priv.encodeToBase64()); -// verify(keyVaultService, times(1)).setSecret(vaultId + "Pub", pub.encodeToBase64()); - } + assertThat(capturedArgs).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder(expectedDataPub, expectedDataPriv); - @Test - public void privateKeyIsSavedToVaultAndIdHasKeySuffix() { - final String vaultId = "vaultId"; + verifyNoMoreInteractions(keyVaultService); - azureVaultKeyGenerator.generate(vaultId, null); + final AzureVaultKeyPair expected = new AzureVaultKeyPair(pubVaultId, privVaultId); -// verify(keyVaultService, times(1)).setSecret(vaultId + "Key", priv.encodeToBase64()); + assertThat(result).isEqualToComparingFieldByFieldRecursively(expected); } @Test public void vaultIdIsFinalComponentOfFilePath() { final String vaultId = "vaultId"; + final String pubVaultId = vaultId + "Pub"; + final String privVaultId = vaultId + "Key"; final String path = "/some/path/" + vaultId; azureVaultKeyGenerator.generate(path, null); -// verify(keyVaultService, times(1)).setSecret(vaultId + "Pub", pub.encodeToBase64()); -// verify(keyVaultService, times(1)).setSecret(vaultId + "Key", priv.encodeToBase64()); + final ArgumentCaptor captor = ArgumentCaptor.forClass(AzureSetSecretData.class); + + verify(keyVaultService, times(2)).setSecret(captor.capture()); + + List capturedArgs = captor.getAllValues(); + assertThat(capturedArgs).hasSize(2); + + AzureSetSecretData expectedDataPub = new AzureSetSecretData(pubVaultId, pub.encodeToBase64()); + AzureSetSecretData expectedDataPriv = new AzureSetSecretData(privVaultId, priv.encodeToBase64()); + + assertThat(capturedArgs).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder(expectedDataPub, expectedDataPriv); + + verifyNoMoreInteractions(keyVaultService); } @Test public void ifNoVaultIdProvidedThenSuffixOnlyIsUsed() { azureVaultKeyGenerator.generate(null, null); -// verify(keyVaultService, times(1)).setSecret("Pub", pub.encodeToBase64()); -// verify(keyVaultService, times(1)).setSecret("Key", priv.encodeToBase64()); + final ArgumentCaptor captor = ArgumentCaptor.forClass(AzureSetSecretData.class); + + verify(keyVaultService, times(2)).setSecret(captor.capture()); + + List capturedArgs = captor.getAllValues(); + assertThat(capturedArgs).hasSize(2); + + AzureSetSecretData expectedDataPub = new AzureSetSecretData("Pub", pub.encodeToBase64()); + AzureSetSecretData expectedDataPriv = new AzureSetSecretData("Key", priv.encodeToBase64()); + + assertThat(capturedArgs).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder(expectedDataPub, expectedDataPriv); + + verifyNoMoreInteractions(keyVaultService); } @Test @@ -100,7 +118,7 @@ public void allowedCharactersUsedInVaultIdDoesNotThrowException() { azureVaultKeyGenerator.generate(allowedId, null); -// verify(keyVaultService, times(2)).setSecret(any(String.class), any(String.class)); + verify(keyVaultService, times(2)).setSecret(any(AzureSetSecretData.class)); } @Test diff --git a/key-generation/src/test/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGeneratorTest.java b/key-generation/src/test/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGeneratorTest.java index 7f86814348..65dd1bb84f 100644 --- a/key-generation/src/test/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGeneratorTest.java +++ b/key-generation/src/test/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGeneratorTest.java @@ -1,5 +1,7 @@ package com.quorum.tessera.key.generation; +import com.quorum.tessera.config.keypairs.HashicorpVaultKeyPair; +import com.quorum.tessera.config.vault.data.HashicorpSetSecretData; import com.quorum.tessera.encryption.KeyPair; import com.quorum.tessera.encryption.PrivateKey; import com.quorum.tessera.encryption.PublicKey; @@ -7,12 +9,13 @@ import com.quorum.tessera.nacl.NaclFacade; import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; import java.util.HashMap; import java.util.Map; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; public class HashicorpVaultKeyGeneratorTest { @@ -46,13 +49,29 @@ public void nullFilenameThrowsException() { public void generatedKeyPairIsSavedToSpecifiedPathInVaultWithIds() { String filename = "secret/path"; - hashicorpVaultKeyGenerator.generate(filename, null); + HashicorpVaultKeyPair result = hashicorpVaultKeyGenerator.generate(filename, null); - Map expectedData = new HashMap<>(); - expectedData.put("publicKey", pub.encodeToBase64()); - expectedData.put("privateKey", priv.encodeToBase64()); -// verify(keyVaultService).setSecretAtPath(filename, expectedData); + final ArgumentCaptor captor = ArgumentCaptor.forClass(HashicorpSetSecretData.class); + + verify(keyVaultService).setSecret(captor.capture()); + + assertThat(captor.getAllValues()).hasSize(1); + HashicorpSetSecretData capturedArg = captor.getValue(); + + Map expectedNameValuePairs = new HashMap<>(); + expectedNameValuePairs.put("publicKey", pub.encodeToBase64()); + expectedNameValuePairs.put("privateKey", priv.encodeToBase64()); + + HashicorpSetSecretData expectedData = new HashicorpSetSecretData(filename, expectedNameValuePairs); + + assertThat(capturedArg).isEqualToComparingFieldByFieldRecursively(expectedData); + + verifyNoMoreInteractions(keyVaultService); + + HashicorpVaultKeyPair expected = new HashicorpVaultKeyPair("publicKey", "privateKey", filename); + + assertThat(result).isEqualToComparingFieldByField(expected); } } diff --git a/key-generation/src/test/java/com/quorum/tessera/key/generation/MockAzureKeyVaultServiceFactory.java b/key-generation/src/test/java/com/quorum/tessera/key/generation/MockAzureKeyVaultServiceFactory.java index a66b946701..ae0eeba0ac 100644 --- a/key-generation/src/test/java/com/quorum/tessera/key/generation/MockAzureKeyVaultServiceFactory.java +++ b/key-generation/src/test/java/com/quorum/tessera/key/generation/MockAzureKeyVaultServiceFactory.java @@ -3,18 +3,23 @@ import com.quorum.tessera.config.Config; import com.quorum.tessera.config.KeyVaultType; import com.quorum.tessera.config.util.EnvironmentVariableProvider; +import com.quorum.tessera.config.vault.data.AzureGetSecretData; import com.quorum.tessera.key.vault.KeyVaultClientFactory; import com.quorum.tessera.key.vault.KeyVaultService; import com.quorum.tessera.key.vault.KeyVaultServiceFactory; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class MockAzureKeyVaultServiceFactory implements KeyVaultServiceFactory { @Override public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, KeyVaultClientFactory keyVaultClientFactory) { KeyVaultService mock = mock(KeyVaultService.class); -// when(mock.getSecret("pub")).thenReturn("publicSecret"); -// when(mock.getSecret("priv")).thenReturn("privSecret"); + + when(mock.getSecret(any(AzureGetSecretData.class))) + .thenReturn("publicSecret") + .thenReturn("privSecret"); return mock; } diff --git a/key-generation/src/test/java/com/quorum/tessera/key/generation/MockHashicorpKeyVaultServiceFactory.java b/key-generation/src/test/java/com/quorum/tessera/key/generation/MockHashicorpKeyVaultServiceFactory.java index bdf7df01f5..fcc5f31da9 100644 --- a/key-generation/src/test/java/com/quorum/tessera/key/generation/MockHashicorpKeyVaultServiceFactory.java +++ b/key-generation/src/test/java/com/quorum/tessera/key/generation/MockHashicorpKeyVaultServiceFactory.java @@ -3,18 +3,23 @@ import com.quorum.tessera.config.Config; import com.quorum.tessera.config.KeyVaultType; import com.quorum.tessera.config.util.EnvironmentVariableProvider; +import com.quorum.tessera.config.vault.data.HashicorpGetSecretData; import com.quorum.tessera.key.vault.KeyVaultClientFactory; import com.quorum.tessera.key.vault.KeyVaultService; import com.quorum.tessera.key.vault.KeyVaultServiceFactory; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class MockHashicorpKeyVaultServiceFactory implements KeyVaultServiceFactory { @Override public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, KeyVaultClientFactory keyVaultClientFactory) { KeyVaultService mock = mock(KeyVaultService.class); -// when(mock.getSecret("pub")).thenReturn("publicSecret"); -// when(mock.getSecret("priv")).thenReturn("privSecret"); + + when(mock.getSecret(any(HashicorpGetSecretData.class))) + .thenReturn("publicSecret") + .thenReturn("privSecret"); return mock; } diff --git a/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/MockAzureKeyVaultServiceFactory.java b/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/MockAzureKeyVaultServiceFactory.java index 08a3ec6f10..6046e83b7a 100644 --- a/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/MockAzureKeyVaultServiceFactory.java +++ b/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/MockAzureKeyVaultServiceFactory.java @@ -3,18 +3,23 @@ import com.quorum.tessera.config.Config; import com.quorum.tessera.config.KeyVaultType; import com.quorum.tessera.config.util.EnvironmentVariableProvider; +import com.quorum.tessera.config.vault.data.AzureGetSecretData; import com.quorum.tessera.key.vault.KeyVaultClientFactory; import com.quorum.tessera.key.vault.KeyVaultService; import com.quorum.tessera.key.vault.KeyVaultServiceFactory; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class MockAzureKeyVaultServiceFactory implements KeyVaultServiceFactory { @Override public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, KeyVaultClientFactory keyVaultClientFactory) { KeyVaultService mock = mock(KeyVaultService.class); -// when(mock.getSecret("pub")).thenReturn("publicSecret"); -// when(mock.getSecret("priv")).thenReturn("privSecret"); + + when(mock.getSecret(any(AzureGetSecretData.class))) + .thenReturn("publicSecret") + .thenReturn("privSecret"); return mock; } diff --git a/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/MockHashicorpKeyVaultServiceFactory.java b/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/MockHashicorpKeyVaultServiceFactory.java index 163391931a..4fdf13884f 100644 --- a/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/MockHashicorpKeyVaultServiceFactory.java +++ b/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/MockHashicorpKeyVaultServiceFactory.java @@ -3,18 +3,23 @@ import com.quorum.tessera.config.Config; import com.quorum.tessera.config.KeyVaultType; import com.quorum.tessera.config.util.EnvironmentVariableProvider; +import com.quorum.tessera.config.vault.data.HashicorpGetSecretData; import com.quorum.tessera.key.vault.KeyVaultClientFactory; import com.quorum.tessera.key.vault.KeyVaultService; import com.quorum.tessera.key.vault.KeyVaultServiceFactory; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class MockHashicorpKeyVaultServiceFactory implements KeyVaultServiceFactory { @Override public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, KeyVaultClientFactory keyVaultClientFactory) { KeyVaultService mock = mock(KeyVaultService.class); -// when(mock.getSecretFromPath("secretPath", "pub")).thenReturn("publicSecret"); -// when(mock.getSecretFromPath("secretPath", "priv")).thenReturn("privSecret"); + + when(mock.getSecret(any(HashicorpGetSecretData.class))) + .thenReturn("publicSecret") + .thenReturn("privSecret"); return mock; } diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java index ef7558b4ae..bb8dc2796a 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java @@ -17,11 +17,6 @@ public class HashicorpKeyVaultServiceFactory implements KeyVaultServiceFactory { private final String secretIdEnvVar = "HASHICORP_SECRET_ID"; private final String authTokenEnvVar = "HASHICORP_TOKEN"; -// @Override -// public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider) { -// return null; -// } - @Override public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, KeyVaultClientFactory keyVaultClientFactory) { Objects.requireNonNull(config); @@ -42,29 +37,6 @@ else if(isOnlyOneInputNull(roleId, secretId)) { .map(KeyConfiguration::getHashicorpKeyVaultConfig) .orElseThrow(() -> new ConfigException(new RuntimeException("Trying to create Hashicorp Vault connection but no Vault configuration provided"))); -// Vault vault = ((HashicorpKeyVaultClientFactory) keyVaultClientFactory) -// .init(keyVaultConfig, new VaultConfigFactory(), new SslConfigFactory()) -// .login(roleId, secretId, authToken) -// .create(); - -// VaultConfig vaultConfig = new VaultConfig() -// .address(keyVaultConfig.getUrl()); -// -// if(keyVaultConfig.getTlsCertificatePath() != null) { -// SslConfig vaultSslConfig = new SslConfig(); -// VaultCallback.execute( -// () -> vaultSslConfig -// .pemFile(keyVaultConfig.getTlsCertificatePath().toFile()) -// .build() -// ); -// -// vaultConfig.sslConfig(vaultSslConfig); -// } -// -// VaultCallback.execute(vaultConfig::build); -// -// final Vault vault = new Vault(vaultConfig); - if(!(keyVaultClientFactory instanceof HashicorpKeyVaultClientFactory)) { throw new HashicorpVaultException("Incorrect KeyVaultClientFactoryType passed to HashicorpKeyVaultServiceFactory"); } @@ -76,7 +48,6 @@ else if(isOnlyOneInputNull(roleId, secretId)) { String token; if(roleId != null && secretId != null) { - //TODO allow other paths AuthResponse loginResponse = VaultCallback.execute(() -> unauthenticatedVault.auth().loginByAppRole(keyVaultConfig.getApprolePath(), roleId, secretId)); token = loginResponse.getAuthClientToken(); } else { From 57c1c3f2019aa81a2f39cb28b4372eae0e53310c Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Tue, 4 Dec 2018 19:31:58 +0000 Subject: [PATCH 31/57] Update hashicorp tests --- .../HashicorpKeyVaultClientFactoryTest.java | 39 +++++++++++++++---- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactoryTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactoryTest.java index a693ef3a1c..1665be0f53 100644 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactoryTest.java +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactoryTest.java @@ -3,12 +3,14 @@ import com.bettercloud.vault.SslConfig; import com.bettercloud.vault.VaultConfig; import com.quorum.tessera.config.HashicorpKeyVaultConfig; +import com.quorum.tessera.config.KeyVaultType; import org.junit.Before; import org.junit.Test; import java.io.File; import java.nio.file.Path; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.*; @@ -33,10 +35,12 @@ public void setUp() { @Test public void tlsConfigAddedToUnauthenticatedVaultClientIfProvided() throws Exception { - Path certPath = mock(Path.class); - when(keyVaultConfig.getTlsCertificatePath()).thenReturn(certPath); + Path tlsPath = mock(Path.class); + when(keyVaultConfig.getTlsCertificatePath()).thenReturn(tlsPath); + when(keyVaultConfig.getTlsKeyPath()).thenReturn(tlsPath); + when(keyVaultConfig.getTlsServerCertificatePath()).thenReturn(tlsPath); File certFile = mock(File.class); - when(certPath.toFile()).thenReturn(certFile); + when(tlsPath.toFile()).thenReturn(certFile); VaultConfig vaultConfig = mock(VaultConfig.class); when(vaultConfigFactory.create().address(anyString())).thenReturn(vaultConfig); @@ -45,7 +49,13 @@ public void tlsConfigAddedToUnauthenticatedVaultClientIfProvided() throws Except SslConfig sslConfig = mock(SslConfig.class, RETURNS_DEEP_STUBS); when(sslConfigFactory.create()).thenReturn(sslConfig); - when(sslConfig.pemFile(any(File.class)).build()).thenReturn(sslConfig); + when( + sslConfig + .clientPemFile(any(File.class)) + .clientKeyPemFile(any(File.class)) + .pemFile(any(File.class)) + .build() + ).thenReturn(sslConfig); keyVaultClientFactory.createUnauthenticatedClient(keyVaultConfig, vaultConfigFactory, sslConfigFactory); @@ -68,10 +78,12 @@ public void tlsConfigNotAddedToUnauthenticatedVaultClientIfNotProvided() throws @Test public void tlsConfigAddedToAuthenticatedVaultClientIfProvided() throws Exception { - Path certPath = mock(Path.class); - when(keyVaultConfig.getTlsCertificatePath()).thenReturn(certPath); + Path tlsPath = mock(Path.class); + when(keyVaultConfig.getTlsCertificatePath()).thenReturn(tlsPath); + when(keyVaultConfig.getTlsKeyPath()).thenReturn(tlsPath); + when(keyVaultConfig.getTlsServerCertificatePath()).thenReturn(tlsPath); File certFile = mock(File.class); - when(certPath.toFile()).thenReturn(certFile); + when(tlsPath.toFile()).thenReturn(certFile); VaultConfig vaultConfig = mock(VaultConfig.class); when(vaultConfigFactory.create().address(anyString())).thenReturn(vaultConfig); @@ -80,7 +92,13 @@ public void tlsConfigAddedToAuthenticatedVaultClientIfProvided() throws Exceptio SslConfig sslConfig = mock(SslConfig.class, RETURNS_DEEP_STUBS); when(sslConfigFactory.create()).thenReturn(sslConfig); - when(sslConfig.pemFile(any(File.class)).build()).thenReturn(sslConfig); + when( + sslConfig + .clientPemFile(any(File.class)) + .clientKeyPemFile(any(File.class)) + .pemFile(any(File.class)) + .build() + ).thenReturn(sslConfig); keyVaultClientFactory.createAuthenticatedClient(keyVaultConfig, vaultConfigFactory, sslConfigFactory, "sometoken"); @@ -112,4 +130,9 @@ public void tokenGetsAddedToAuthenticateVaultClientConfig() throws Exception { verify(vaultConfig).token("sometoken"); } + @Test + public void getType() { + assertThat(keyVaultClientFactory.getType()).isEqualTo(KeyVaultType.HASHICORP); + } + } From a524462fb96d47f1d478735beada1da8be17750b Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Wed, 5 Dec 2018 09:48:54 +0000 Subject: [PATCH 32/57] Add test for VaultCallback class --- .../vault/hashicorp/VaultCallbackTest.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/VaultCallbackTest.java diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/VaultCallbackTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/VaultCallbackTest.java new file mode 100644 index 0000000000..44a1df93ba --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/VaultCallbackTest.java @@ -0,0 +1,22 @@ +package com.quorum.tessera.key.vault.hashicorp; + +import com.bettercloud.vault.VaultException; +import org.junit.Test; + +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; + +public class VaultCallbackTest { + + @Test(expected = HashicorpVaultException.class) + public void executeThrowsSQLException() throws Exception { + + VaultCallback callback = mock(VaultCallback.class); + + doThrow(VaultException.class).when(callback).doExecute(); + + VaultCallback.execute(callback); + + } + +} From 5b07e93554fce42f9a1b55c1090525b89040fa31 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Wed, 5 Dec 2018 10:25:09 +0000 Subject: [PATCH 33/57] Add tests for Get/SetSecretData classes --- .../config/HashicorpKeyVaultConfigTest.java | 15 +++++++-------- .../vault/data/AzureGetSecretDataTest.java | 16 ++++++++++++++++ .../vault/data/AzureSetSecretDataTest.java | 17 +++++++++++++++++ .../vault/data}/HashicorpGetSecretDataTest.java | 3 +-- .../vault/data}/HashicorpSetSecretDataTest.java | 3 +-- 5 files changed, 42 insertions(+), 12 deletions(-) create mode 100644 config/src/test/java/com/quorum/tessera/config/vault/data/AzureGetSecretDataTest.java create mode 100644 config/src/test/java/com/quorum/tessera/config/vault/data/AzureSetSecretDataTest.java rename {key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp => config/src/test/java/com/quorum/tessera/config/vault/data}/HashicorpGetSecretDataTest.java (85%) rename {key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp => config/src/test/java/com/quorum/tessera/config/vault/data}/HashicorpSetSecretDataTest.java (87%) diff --git a/config/src/test/java/com/quorum/tessera/config/HashicorpKeyVaultConfigTest.java b/config/src/test/java/com/quorum/tessera/config/HashicorpKeyVaultConfigTest.java index e1436ac7e0..80cc23fd48 100644 --- a/config/src/test/java/com/quorum/tessera/config/HashicorpKeyVaultConfigTest.java +++ b/config/src/test/java/com/quorum/tessera/config/HashicorpKeyVaultConfigTest.java @@ -16,20 +16,19 @@ public void setUp() { } @Test - public void getters() { - assertThat(vaultConfig.getUrl()).isEqualTo("url"); + public void getType() { + assertThat(vaultConfig.getKeyVaultType()).isEqualTo(KeyVaultType.HASHICORP); } @Test - public void setters() { - assertThat(vaultConfig.getUrl()).isEqualTo("url"); - vaultConfig.setUrl("newUrl"); - assertThat(vaultConfig.getUrl()).isEqualTo("newUrl"); + public void getApprolePathReturnsDefaultIfNotSet() { + assertThat(vaultConfig.getApprolePath()).isEqualTo("approle"); } @Test - public void getType() { - assertThat(vaultConfig.getKeyVaultType()).isEqualTo(KeyVaultType.HASHICORP); + public void getApprolePath() { + vaultConfig.setApprolePath("notdefault"); + assertThat(vaultConfig.getApprolePath()).isEqualTo("notdefault"); } } diff --git a/config/src/test/java/com/quorum/tessera/config/vault/data/AzureGetSecretDataTest.java b/config/src/test/java/com/quorum/tessera/config/vault/data/AzureGetSecretDataTest.java new file mode 100644 index 0000000000..9efeaabbdf --- /dev/null +++ b/config/src/test/java/com/quorum/tessera/config/vault/data/AzureGetSecretDataTest.java @@ -0,0 +1,16 @@ +package com.quorum.tessera.config.vault.data; + +import com.quorum.tessera.config.KeyVaultType; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class AzureGetSecretDataTest { + @Test + public void getters() { + AzureGetSecretData data = new AzureGetSecretData("secretName"); + + assertThat(data.getSecretName()).isEqualTo("secretName"); + assertThat(data.getType()).isEqualTo(KeyVaultType.AZURE); + } +} diff --git a/config/src/test/java/com/quorum/tessera/config/vault/data/AzureSetSecretDataTest.java b/config/src/test/java/com/quorum/tessera/config/vault/data/AzureSetSecretDataTest.java new file mode 100644 index 0000000000..e6aae30695 --- /dev/null +++ b/config/src/test/java/com/quorum/tessera/config/vault/data/AzureSetSecretDataTest.java @@ -0,0 +1,17 @@ +package com.quorum.tessera.config.vault.data; + +import com.quorum.tessera.config.KeyVaultType; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class AzureSetSecretDataTest { + @Test + public void getters() { + AzureSetSecretData data = new AzureSetSecretData("secretName", "secret"); + + assertThat(data.getSecretName()).isEqualTo("secretName"); + assertThat(data.getSecret()).isEqualTo("secret"); + assertThat(data.getType()).isEqualTo(KeyVaultType.AZURE); + } +} diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretDataTest.java b/config/src/test/java/com/quorum/tessera/config/vault/data/HashicorpGetSecretDataTest.java similarity index 85% rename from key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretDataTest.java rename to config/src/test/java/com/quorum/tessera/config/vault/data/HashicorpGetSecretDataTest.java index 863834dd99..9ff81a2609 100644 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpGetSecretDataTest.java +++ b/config/src/test/java/com/quorum/tessera/config/vault/data/HashicorpGetSecretDataTest.java @@ -1,7 +1,6 @@ -package com.quorum.tessera.key.vault.hashicorp; +package com.quorum.tessera.config.vault.data; import com.quorum.tessera.config.KeyVaultType; -import com.quorum.tessera.config.vault.data.HashicorpGetSecretData; import org.junit.Before; import org.junit.Test; diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretDataTest.java b/config/src/test/java/com/quorum/tessera/config/vault/data/HashicorpSetSecretDataTest.java similarity index 87% rename from key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretDataTest.java rename to config/src/test/java/com/quorum/tessera/config/vault/data/HashicorpSetSecretDataTest.java index 310e0121eb..60cbb89c28 100644 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpSetSecretDataTest.java +++ b/config/src/test/java/com/quorum/tessera/config/vault/data/HashicorpSetSecretDataTest.java @@ -1,7 +1,6 @@ -package com.quorum.tessera.key.vault.hashicorp; +package com.quorum.tessera.config.vault.data; import com.quorum.tessera.config.KeyVaultType; -import com.quorum.tessera.config.vault.data.HashicorpSetSecretData; import org.junit.Before; import org.junit.Test; From 80d14154b6046e84ead31f3298fd683fdee7e3f8 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Wed, 5 Dec 2018 11:23:10 +0000 Subject: [PATCH 34/57] Add hashicorp config items to cli for key generation --- .../tessera/config/cli/DefaultCliAdapter.java | 29 ++++++++++++++++++- .../cli/parsers/KeyGenerationParser.java | 21 ++++++++++---- .../tessera/config/cli/OverrideUtilTest.java | 9 +++++- .../HashicorpVaultKeyGeneratorTest.java | 6 ++-- 4 files changed, 54 insertions(+), 11 deletions(-) diff --git a/config-cli/src/main/java/com/quorum/tessera/config/cli/DefaultCliAdapter.java b/config-cli/src/main/java/com/quorum/tessera/config/cli/DefaultCliAdapter.java index 599c09002d..5395a2429a 100644 --- a/config-cli/src/main/java/com/quorum/tessera/config/cli/DefaultCliAdapter.java +++ b/config-cli/src/main/java/com/quorum/tessera/config/cli/DefaultCliAdapter.java @@ -180,15 +180,42 @@ private Options buildBaseOptions() { .build() ); + options.addOption( + Option.builder("keygenvaultapprole") + .desc("AppRole path for Hashicorp Vault authentication (defaults to 'approle')") + .hasArg() + .optionalArg(false) + .argName("PATH") + .build() + ); + options.addOption( Option.builder("keygenvaultcert") - .desc("TLS certificate for key vault - not required for Azure vaults") + .desc("TLS certificate for Hashicorp Vault authentication") .hasArg() .optionalArg(false) .argName("PATH") .build() ); + options.addOption( + Option.builder("keygenvaultcertkey") + .desc("TLS key for Hashicorp Vault authentication") + .hasArg() + .optionalArg(false) + .argName("PATH") + .build() + ); + + options.addOption( + Option.builder("keygenvaultservercert") + .desc("TLS certificate of Vault server for Hashicorp Vault authentication") + .hasArg() + .optionalArg(false) + .argName("PATH") + .build() + ); + options.addOption( Option.builder("pidfile") .desc("Path to pid file") diff --git a/config-cli/src/main/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParser.java b/config-cli/src/main/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParser.java index c97f1a7311..041c9d41a3 100644 --- a/config-cli/src/main/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParser.java +++ b/config-cli/src/main/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParser.java @@ -112,13 +112,24 @@ private Optional keyVaultConfig(CommandLine commandLine) { throw new CliException("At least one -filename must be provided when saving generated keys in a Hashicorp Vault"); } - Path tlsCertificatePath = null; + String approlePath = commandLine.getOptionValue("keygenvaultapprole"); - if(commandLine.hasOption("keygenvaultcert")) { - tlsCertificatePath = Paths.get(commandLine.getOptionValue("keygenvaultcert")); - } + Optional tlsCertificatePath = Optional.ofNullable(commandLine.getOptionValue("keygenvaultcert")) + .map(Paths::get); + + Optional tlsKeyPath = Optional.ofNullable(commandLine.getOptionValue("keygenvaultcertkey")) + .map(Paths::get); + + Optional tlsServerCertificatePath = Optional.ofNullable(commandLine.getOptionValue("keygenvaultservercert")) + .map(Paths::get); - keyVaultConfig = new HashicorpKeyVaultConfig(keyVaultUrl, tlsCertificatePath); + keyVaultConfig = new HashicorpKeyVaultConfig( + keyVaultUrl, + approlePath, + tlsCertificatePath.orElse(null), + tlsKeyPath.orElse(null), + tlsServerCertificatePath.orElse(null) + ); Set> violations = validator.validate((HashicorpKeyVaultConfig)keyVaultConfig); diff --git a/config-cli/src/test/java/com/quorum/tessera/config/cli/OverrideUtilTest.java b/config-cli/src/test/java/com/quorum/tessera/config/cli/OverrideUtilTest.java index 3ff1c7cd90..af4b1b56d0 100644 --- a/config-cli/src/test/java/com/quorum/tessera/config/cli/OverrideUtilTest.java +++ b/config-cli/src/test/java/com/quorum/tessera/config/cli/OverrideUtilTest.java @@ -1,6 +1,9 @@ package com.quorum.tessera.config.cli; -import com.quorum.tessera.config.*; +import com.quorum.tessera.config.Config; +import com.quorum.tessera.config.KeyConfiguration; +import com.quorum.tessera.config.Peer; +import com.quorum.tessera.config.SslAuthenticationMode; import com.quorum.tessera.config.util.JaxbUtil; import org.junit.Ignore; import org.junit.Test; @@ -41,6 +44,10 @@ public void buildOptions() { "keys.keyData.config.data.aopts.parallelism", "keys.keyData.privateKeyPath", "keys.azureKeyVaultConfig.url", + "keys.hashicorpKeyVaultConfig.approlePath", + "keys.hashicorpKeyVaultConfig.tlsCertificatePath", + "keys.hashicorpKeyVaultConfig.tlsServerCertificatePath", + "keys.hashicorpKeyVaultConfig.tlsKeyPath", "keys.hashicorpKeyVaultConfig.url", "alwaysSendTo", "unixSocketFile", diff --git a/key-generation/src/test/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGeneratorTest.java b/key-generation/src/test/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGeneratorTest.java index 65dd1bb84f..f0efbf0f11 100644 --- a/key-generation/src/test/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGeneratorTest.java +++ b/key-generation/src/test/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGeneratorTest.java @@ -51,9 +51,10 @@ public void generatedKeyPairIsSavedToSpecifiedPathInVaultWithIds() { HashicorpVaultKeyPair result = hashicorpVaultKeyGenerator.generate(filename, null); + HashicorpVaultKeyPair expected = new HashicorpVaultKeyPair("publicKey", "privateKey", filename); + assertThat(result).isEqualToComparingFieldByField(expected); final ArgumentCaptor captor = ArgumentCaptor.forClass(HashicorpSetSecretData.class); - verify(keyVaultService).setSecret(captor.capture()); assertThat(captor.getAllValues()).hasSize(1); @@ -69,9 +70,6 @@ public void generatedKeyPairIsSavedToSpecifiedPathInVaultWithIds() { verifyNoMoreInteractions(keyVaultService); - HashicorpVaultKeyPair expected = new HashicorpVaultKeyPair("publicKey", "privateKey", filename); - - assertThat(result).isEqualToComparingFieldByField(expected); } } From 8aa6f6cabbf399394442776c9c46632f77fb2ba1 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Wed, 5 Dec 2018 11:34:25 +0000 Subject: [PATCH 35/57] Add tests to KeyVaultClientFactory and HashicorpKeyVaultConfig --- .../config/HashicorpKeyVaultConfig.java | 5 ++- .../config/HashicorpKeyVaultConfigTest.java | 45 ++++++++++++++++++- .../key/vault/KeyVaultClientFactoryTest.java | 22 +++++++++ .../MockHashicorpKeyVaultClientFactory.java | 10 +++++ ...um.tessera.key.vault.KeyVaultClientFactory | 1 + 5 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/KeyVaultClientFactoryTest.java create mode 100644 key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/MockHashicorpKeyVaultClientFactory.java create mode 100644 key-vault/key-vault-api/src/test/resources/META-INF/services/com.quorum.tessera.key.vault.KeyVaultClientFactory diff --git a/config/src/main/java/com/quorum/tessera/config/HashicorpKeyVaultConfig.java b/config/src/main/java/com/quorum/tessera/config/HashicorpKeyVaultConfig.java index 02f458693b..643c0361e8 100644 --- a/config/src/main/java/com/quorum/tessera/config/HashicorpKeyVaultConfig.java +++ b/config/src/main/java/com/quorum/tessera/config/HashicorpKeyVaultConfig.java @@ -39,9 +39,12 @@ public class HashicorpKeyVaultConfig extends ConfigItem implements KeyVaultConfi @XmlJavaTypeAdapter(PathAdapter.class) private Path tlsServerCertificatePath; - public HashicorpKeyVaultConfig(String url, Path tlsCertificatePath) { + public HashicorpKeyVaultConfig(String url, String approlePath, Path tlsCertificatePath, Path tlsKeyPath, Path tlsServerCertificatePath) { this.url = url; + this.approlePath = approlePath; this.tlsCertificatePath = tlsCertificatePath; + this.tlsKeyPath = tlsKeyPath; + this.tlsServerCertificatePath = tlsServerCertificatePath; } public HashicorpKeyVaultConfig() { diff --git a/config/src/test/java/com/quorum/tessera/config/HashicorpKeyVaultConfigTest.java b/config/src/test/java/com/quorum/tessera/config/HashicorpKeyVaultConfigTest.java index 80cc23fd48..fe8376d8d0 100644 --- a/config/src/test/java/com/quorum/tessera/config/HashicorpKeyVaultConfigTest.java +++ b/config/src/test/java/com/quorum/tessera/config/HashicorpKeyVaultConfigTest.java @@ -3,6 +3,9 @@ import org.junit.Before; import org.junit.Test; +import java.nio.file.Path; +import java.nio.file.Paths; + import static org.assertj.core.api.Assertions.assertThat; public class HashicorpKeyVaultConfigTest { @@ -12,7 +15,47 @@ public class HashicorpKeyVaultConfigTest { @Before public void setUp() { vaultConfig = new HashicorpKeyVaultConfig(); - vaultConfig.setUrl("url"); + } + + @Test + public void multiArgConstructor() { + String url = "url"; + String approle = "approle"; + Path tlsCertPath = Paths.get("tlscertpath"); + Path tlsKeyPath = Paths.get("tlskeypath"); + Path tlsServerCertPath = Paths.get("tlsservercertpath"); + + HashicorpKeyVaultConfig conf = new HashicorpKeyVaultConfig(url, approle, tlsCertPath, tlsKeyPath, tlsServerCertPath); + + assertThat(conf.getUrl()).isEqualTo(url); + assertThat(conf.getApprolePath()).isEqualTo("approle"); + assertThat(conf.getTlsCertificatePath()).isEqualTo(tlsCertPath); + assertThat(conf.getTlsKeyPath()).isEqualTo(tlsKeyPath); + assertThat(conf.getTlsServerCertificatePath()).isEqualTo(tlsServerCertPath); + } + + @Test + public void gettersAndSetters() { + assertThat(vaultConfig.getUrl()).isEqualTo(null); + assertThat(vaultConfig.getTlsCertificatePath()).isEqualTo(null); + assertThat(vaultConfig.getTlsKeyPath()).isEqualTo(null); + assertThat(vaultConfig.getTlsServerCertificatePath()).isEqualTo(null); + + String url = "url"; + Path tlsCertPath = Paths.get("tlscertpath"); + Path tlsKeyPath = Paths.get("tlskeypath"); + Path tlsServerCertPath = Paths.get("tlsservercertpath"); + + vaultConfig.setUrl(url); + vaultConfig.setTlsCertificatePath(tlsCertPath); + vaultConfig.setTlsKeyPath(tlsKeyPath); + vaultConfig.setTlsServerCertificatePath(tlsServerCertPath); + + assertThat(vaultConfig.getUrl()).isEqualTo(url); + assertThat(vaultConfig.getTlsCertificatePath()).isEqualTo(tlsCertPath); + assertThat(vaultConfig.getTlsKeyPath()).isEqualTo(tlsKeyPath); + assertThat(vaultConfig.getTlsServerCertificatePath()).isEqualTo(tlsServerCertPath); + } @Test diff --git a/key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/KeyVaultClientFactoryTest.java b/key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/KeyVaultClientFactoryTest.java new file mode 100644 index 0000000000..9eaca416f2 --- /dev/null +++ b/key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/KeyVaultClientFactoryTest.java @@ -0,0 +1,22 @@ +package com.quorum.tessera.key.vault; + +import com.quorum.tessera.config.KeyVaultType; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class KeyVaultClientFactoryTest { + + @Test + public void getInstance() { + KeyVaultClientFactory keyVaultClientFactory = KeyVaultClientFactory.getInstance(KeyVaultType.HASHICORP); + + assertThat(keyVaultClientFactory).isExactlyInstanceOf(MockHashicorpKeyVaultClientFactory.class); + } + + @Test + public void instanceNotFoundReturnsNull() { + assertThat(KeyVaultClientFactory.getInstance(null)).isNull(); + } + +} diff --git a/key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/MockHashicorpKeyVaultClientFactory.java b/key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/MockHashicorpKeyVaultClientFactory.java new file mode 100644 index 0000000000..7e96867239 --- /dev/null +++ b/key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/MockHashicorpKeyVaultClientFactory.java @@ -0,0 +1,10 @@ +package com.quorum.tessera.key.vault; + +import com.quorum.tessera.config.KeyVaultType; + +public class MockHashicorpKeyVaultClientFactory implements KeyVaultClientFactory { + @Override + public KeyVaultType getType() { + return KeyVaultType.HASHICORP; + } +} diff --git a/key-vault/key-vault-api/src/test/resources/META-INF/services/com.quorum.tessera.key.vault.KeyVaultClientFactory b/key-vault/key-vault-api/src/test/resources/META-INF/services/com.quorum.tessera.key.vault.KeyVaultClientFactory new file mode 100644 index 0000000000..b470a210d7 --- /dev/null +++ b/key-vault/key-vault-api/src/test/resources/META-INF/services/com.quorum.tessera.key.vault.KeyVaultClientFactory @@ -0,0 +1 @@ +com.quorum.tessera.key.vault.MockHashicorpKeyVaultClientFactory \ No newline at end of file From ba86887fc5a564bb53a1d7951a386c4d3c7b9de9 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Wed, 5 Dec 2018 13:06:26 +0000 Subject: [PATCH 36/57] Add additional KeyGenerationParser tests --- .../cli/parsers/KeyGenerationParserTest.java | 49 ++++++++++++++++++- .../HashicorpKeyVaultServiceFactoryTest.java | 2 + 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/config-cli/src/test/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParserTest.java b/config-cli/src/test/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParserTest.java index df9466abc8..594e29c506 100644 --- a/config-cli/src/test/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParserTest.java +++ b/config-cli/src/test/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParserTest.java @@ -13,6 +13,7 @@ import javax.validation.ConstraintViolationException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.UUID; @@ -195,7 +196,7 @@ public void ifOnlyVaultUrlOptionProvidedThenException() { } @Test - public void ifAllVaultOptionsProvidedButTypeUnknownThenException() { + public void ifVaultOptionsProvidedButTypeUnknownThenException() { when(commandLine.hasOption("keygenvaulttype")).thenReturn(true); when(commandLine.hasOption("keygenvaulturl")).thenReturn(true); when(commandLine.getOptionValue("keygenvaulttype")).thenReturn("unknown"); @@ -227,12 +228,58 @@ public void ifAllVaultOptionsAndFilenameProvidedForHashicorpThenOkay() throws Ex when(commandLine.getOptionValue("keygenvaulturl")).thenReturn("someurl"); when(commandLine.getOptionValue("keygenvaulttype")).thenReturn("HASHICORP"); when(commandLine.getOptionValue("filename")).thenReturn("secret/path"); + when(commandLine.getOptionValue("keygenvaultapprole")).thenReturn("approle"); + + Path tempPath = Files.createTempFile(UUID.randomUUID().toString(), ""); + tempPath.toFile().deleteOnExit(); + + when(commandLine.getOptionValue("keygenvaultcert")).thenReturn(tempPath.toString()); + when(commandLine.getOptionValue("keygenvaultcertkey")).thenReturn(tempPath.toString()); + when(commandLine.getOptionValue("keygenvaultservercert")).thenReturn(tempPath.toString()); this.parser.parse(commandLine); verify(commandLine, times(1)).getOptionValue("keygenvaulttype"); + verify(commandLine, times(1)).getOptionValue("keygenvaulturl"); + verify(commandLine, times(1)).getOptionValue("keygenvaultapprole"); + verify(commandLine, times(1)).getOptionValue("keygenvaultcert"); + verify(commandLine, times(1)).getOptionValue("keygenvaultcertkey"); + verify(commandLine, times(1)).getOptionValue("keygenvaultservercert"); + } + + @Test + public void ifHashicorpTlsOptionsProvidedButPathsDontExistThenValidationException() { + when(commandLine.hasOption("keygenvaulttype")).thenReturn(true); + when(commandLine.hasOption("keygenvaulturl")).thenReturn(true); + when(commandLine.getOptionValue("keygenvaulttype")).thenReturn("HASHICORP"); + when(commandLine.getOptionValue("keygenvaulturl")).thenReturn("someurl"); + when(commandLine.hasOption("filename")).thenReturn(true); + when(commandLine.getOptionValue("filename")).thenReturn("secret/path"); + when(commandLine.getOptionValue("keygenvaultapprole")).thenReturn("approle"); + when(commandLine.getOptionValue("keygenvaultcert")).thenReturn("non/existent/path"); + when(commandLine.getOptionValue("keygenvaultcertkey")).thenReturn("non/existent/path"); + when(commandLine.getOptionValue("keygenvaultservercert")).thenReturn("non/existent/path"); + + Throwable ex = catchThrowable(() -> this.parser.parse(commandLine)); + verify(commandLine, times(1)).getOptionValue("keygenvaulttype"); verify(commandLine, times(1)).getOptionValue("keygenvaulturl"); + verify(commandLine, times(1)).getOptionValue("keygenvaultapprole"); + verify(commandLine, times(1)).getOptionValue("keygenvaultcert"); + verify(commandLine, times(1)).getOptionValue("keygenvaultcertkey"); + verify(commandLine, times(1)).getOptionValue("keygenvaultservercert"); + + assertThat(ex).isInstanceOf(ConstraintViolationException.class); + + Set> violations = ((ConstraintViolationException) ex).getConstraintViolations(); + + assertThat(violations.size()).isEqualTo(3); + + Iterator> iterator = violations.iterator(); + + assertThat(iterator.next().getMessage()).isEqualTo("File does not exist"); + assertThat(iterator.next().getMessage()).isEqualTo("File does not exist"); + assertThat(iterator.next().getMessage()).isEqualTo("File does not exist"); } } diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java index a499bf8a80..285a81c4bb 100644 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java @@ -208,6 +208,7 @@ public void ifRoleIdAndSecretIdEnvVarsSetThenAppRoleIsUsedToAuthenticate() throw HashicorpKeyVaultConfig keyVaultConfig = mock(HashicorpKeyVaultConfig.class); when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(keyVaultConfig); + when(keyVaultConfig.getApprolePath()).thenReturn("approle"); Vault unauthenticatedVault = mock(Vault.class); when( @@ -239,6 +240,7 @@ public void ifAllEnvVarsSetThenAppRoleIsUsedToAuthenticate() throws Exception { HashicorpKeyVaultConfig keyVaultConfig = mock(HashicorpKeyVaultConfig.class); when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(keyVaultConfig); + when(keyVaultConfig.getApprolePath()).thenReturn("approle"); Vault unauthenticatedVault = mock(Vault.class); when( From c57ffee8a040afbdbb69df20925184294274aa05 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Wed, 5 Dec 2018 13:53:47 +0000 Subject: [PATCH 37/57] Allow both mutual and 1 way TLS authentication to Vault server --- .../HashicorpKeyVaultClientFactory.java | 19 ++++-- .../HashicorpKeyVaultClientFactoryTest.java | 58 ++++++++++++++++++- 2 files changed, 70 insertions(+), 7 deletions(-) diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactory.java index b70767a03f..2053089c58 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactory.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactory.java @@ -31,18 +31,27 @@ private VaultConfig createBaseVaultConfig(HashicorpKeyVaultConfig keyVaultConfig VaultConfig vaultConfig = vaultConfigFactory.create() .address(keyVaultConfig.getUrl()); - if (keyVaultConfig.getTlsCertificatePath() != null) { + if (keyVaultConfig.getTlsServerCertificatePath() != null) { SslConfig sslConfig = sslConfigFactory.create(); VaultCallback.execute( - () -> sslConfig.clientPemFile(keyVaultConfig.getTlsCertificatePath().toFile()) - .clientKeyPemFile(keyVaultConfig.getTlsKeyPath().toFile()) - .pemFile(keyVaultConfig.getTlsServerCertificatePath().toFile()) - .build() + () -> sslConfig.pemFile(keyVaultConfig.getTlsServerCertificatePath().toFile()) + ); + + if(keyVaultConfig.getTlsCertificatePath() != null && keyVaultConfig.getTlsKeyPath() != null) { + VaultCallback.execute( + () -> sslConfig.clientPemFile(keyVaultConfig.getTlsCertificatePath().toFile()) + .clientKeyPemFile(keyVaultConfig.getTlsKeyPath().toFile()) + ); + } + + VaultCallback.execute( + () -> sslConfig.build() ); vaultConfig.sslConfig(sslConfig); } + return vaultConfig; } diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactoryTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactoryTest.java index 1665be0f53..f9a2a18f48 100644 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactoryTest.java +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactoryTest.java @@ -59,7 +59,6 @@ public void tlsConfigAddedToUnauthenticatedVaultClientIfProvided() throws Except keyVaultClientFactory.createUnauthenticatedClient(keyVaultConfig, vaultConfigFactory, sslConfigFactory); - verify(keyVaultConfig, times(2)).getTlsCertificatePath(); verify(vaultConfig).sslConfig(sslConfig); } @@ -102,7 +101,62 @@ public void tlsConfigAddedToAuthenticatedVaultClientIfProvided() throws Exceptio keyVaultClientFactory.createAuthenticatedClient(keyVaultConfig, vaultConfigFactory, sslConfigFactory, "sometoken"); - verify(keyVaultConfig, times(2)).getTlsCertificatePath(); + verify(vaultConfig).sslConfig(sslConfig); + } + + @Test + public void oneWayTlsSetUpIfOnlyServerCertProvidedInConfig() throws Exception { + Path tlsPath = mock(Path.class); + when(keyVaultConfig.getTlsCertificatePath()).thenReturn(null); + when(keyVaultConfig.getTlsKeyPath()).thenReturn(null); + when(keyVaultConfig.getTlsServerCertificatePath()).thenReturn(tlsPath); + File certFile = mock(File.class); + when(tlsPath.toFile()).thenReturn(certFile); + + VaultConfig vaultConfig = mock(VaultConfig.class); + when(vaultConfigFactory.create().address(anyString())).thenReturn(vaultConfig); + when(vaultConfig.build()).thenReturn(vaultConfig); + + SslConfig sslConfig = mock(SslConfig.class, RETURNS_DEEP_STUBS); + when(sslConfigFactory.create()).thenReturn(sslConfig); + + when(sslConfig.pemFile(any(File.class))).thenReturn(sslConfig); + when(sslConfig.build()).thenReturn(sslConfig); + + keyVaultClientFactory.createAuthenticatedClient(keyVaultConfig, vaultConfigFactory, sslConfigFactory, "sometoken"); + + verify(sslConfig).pemFile(any()); + verify(sslConfig, never()).clientPemFile(any()); + verify(sslConfig, never()).clientPemFile(any()); + verify(vaultConfig).sslConfig(sslConfig); + } + + @Test + public void twoWayTlsSetUpIfAllTlsOptionsProvidedInConfig() throws Exception { + Path tlsPath = mock(Path.class); + when(keyVaultConfig.getTlsCertificatePath()).thenReturn(tlsPath); + when(keyVaultConfig.getTlsKeyPath()).thenReturn(tlsPath); + when(keyVaultConfig.getTlsServerCertificatePath()).thenReturn(tlsPath); + File certFile = mock(File.class); + when(tlsPath.toFile()).thenReturn(certFile); + + VaultConfig vaultConfig = mock(VaultConfig.class); + when(vaultConfigFactory.create().address(anyString())).thenReturn(vaultConfig); + when(vaultConfig.build()).thenReturn(vaultConfig); + + SslConfig sslConfig = mock(SslConfig.class); + when(sslConfigFactory.create()).thenReturn(sslConfig); + + when(sslConfig.pemFile(any(File.class))).thenReturn(sslConfig); + when(sslConfig.clientPemFile(any(File.class))).thenReturn(sslConfig); + when(sslConfig.clientKeyPemFile(any(File.class))).thenReturn(sslConfig); + when(sslConfig.build()).thenReturn(sslConfig); + + keyVaultClientFactory.createAuthenticatedClient(keyVaultConfig, vaultConfigFactory, sslConfigFactory, "sometoken"); + + verify(sslConfig).pemFile(any()); + verify(sslConfig).clientPemFile(any()); + verify(sslConfig).clientPemFile(any()); verify(vaultConfig).sslConfig(sslConfig); } From cd79a4213f51aea412f3f7beece1bf36556344df Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Wed, 5 Dec 2018 15:11:57 +0000 Subject: [PATCH 38/57] Add more descriptive message to Vault authentication exception --- .../hashicorp/HashicorpKeyVaultServiceFactory.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java index bb8dc2796a..be3145dc77 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java @@ -1,6 +1,7 @@ package com.quorum.tessera.key.vault.hashicorp; import com.bettercloud.vault.Vault; +import com.bettercloud.vault.VaultException; import com.bettercloud.vault.response.AuthResponse; import com.quorum.tessera.config.*; import com.quorum.tessera.config.util.EnvironmentVariableProvider; @@ -48,8 +49,13 @@ else if(isOnlyOneInputNull(roleId, secretId)) { String token; if(roleId != null && secretId != null) { - AuthResponse loginResponse = VaultCallback.execute(() -> unauthenticatedVault.auth().loginByAppRole(keyVaultConfig.getApprolePath(), roleId, secretId)); - token = loginResponse.getAuthClientToken(); + try { + AuthResponse loginResponse = unauthenticatedVault.auth().loginByAppRole(keyVaultConfig.getApprolePath(), roleId, secretId); + token = loginResponse.getAuthClientToken(); + } catch (VaultException e) { + throw new HashicorpVaultException("Unable to authenticate using AppRole - " + e.getMessage()); + } + } else { token = authToken; } From 9738595a1bccb7d71f11bbd77b02beaa7280b84b Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Wed, 5 Dec 2018 15:37:53 +0000 Subject: [PATCH 39/57] Update cli help --- .../java/com/quorum/tessera/config/cli/DefaultCliAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config-cli/src/main/java/com/quorum/tessera/config/cli/DefaultCliAdapter.java b/config-cli/src/main/java/com/quorum/tessera/config/cli/DefaultCliAdapter.java index 5395a2429a..0ae85e1113 100644 --- a/config-cli/src/main/java/com/quorum/tessera/config/cli/DefaultCliAdapter.java +++ b/config-cli/src/main/java/com/quorum/tessera/config/cli/DefaultCliAdapter.java @@ -185,7 +185,7 @@ private Options buildBaseOptions() { .desc("AppRole path for Hashicorp Vault authentication (defaults to 'approle')") .hasArg() .optionalArg(false) - .argName("PATH") + .argName("STRING") .build() ); From 899ffb853bb53a4e4ac2aea6a3147f7cbde6b8a8 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Wed, 5 Dec 2018 16:00:14 +0000 Subject: [PATCH 40/57] Add test for AppRole authentication exception --- .../HashicorpKeyVaultServiceFactoryTest.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java index 285a81c4bb..9c8607bef4 100644 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java @@ -1,6 +1,7 @@ package com.quorum.tessera.key.vault.hashicorp; import com.bettercloud.vault.Vault; +import com.bettercloud.vault.VaultException; import com.bettercloud.vault.api.Auth; import com.bettercloud.vault.response.AuthResponse; import com.quorum.tessera.config.*; @@ -261,6 +262,36 @@ public void ifAllEnvVarsSetThenAppRoleIsUsedToAuthenticate() throws Exception { verify(keyVaultClientFactory).createAuthenticatedClient(any(HashicorpKeyVaultConfig.class), any(VaultConfigFactory.class), any(SslConfigFactory.class), matches(token)); } + @Test + public void exceptionThrownIfErrorEncounteredDuringAppRoleAuthentication() throws Exception { + when(envProvider.getEnv("HASHICORP_ROLE_ID")).thenReturn("role-id"); + when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn("secret-id"); + when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn(null); + + KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); + when(config.getKeys()).thenReturn(keyConfiguration); + + HashicorpKeyVaultConfig keyVaultConfig = mock(HashicorpKeyVaultConfig.class); + when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(keyVaultConfig); + when(keyVaultConfig.getApprolePath()).thenReturn("approle"); + + Vault unauthenticatedVault = mock(Vault.class); + when( + keyVaultClientFactory + .createUnauthenticatedClient(any(HashicorpKeyVaultConfig.class), any(VaultConfigFactory.class), any(SslConfigFactory.class)) + ).thenReturn(unauthenticatedVault); + + Auth auth = mock(Auth.class); + when(unauthenticatedVault.auth()).thenReturn(auth); + AuthResponse loginResponse = mock(AuthResponse.class); + when(auth.loginByAppRole(anyString(), anyString(), anyString())).thenThrow(VaultException.class); + + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory)); + + assertThat(ex).isInstanceOf(HashicorpVaultException.class); + assertThat(ex.getMessage()).contains("Unable to authenticate using AppRole"); + } + @Test public void ifOnlyTokenEnvVarSetThenTokenIsUsedToAuthenticate() throws Exception { when(envProvider.getEnv("HASHICORP_ROLE_ID")).thenReturn(null); From 123700450da6f7f411db335c56169bb952023e58 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Wed, 5 Dec 2018 16:55:32 +0000 Subject: [PATCH 41/57] General clean up --- .../tessera/config/util/EnvironmentVariableProvider.java | 1 - .../key/vault/hashicorp/HashicorpKeyVaultService.java | 6 +----- .../vault/hashicorp/HashicorpKeyVaultServiceFactory.java | 9 +++++---- .../tessera/key/vault/hashicorp/SslConfigFactory.java | 1 + .../tessera/key/vault/hashicorp/VaultConfigFactory.java | 1 + .../vault/hashicorp/HashicorpKeyVaultServiceTest.java | 6 +----- 6 files changed, 9 insertions(+), 15 deletions(-) diff --git a/config/src/main/java/com/quorum/tessera/config/util/EnvironmentVariableProvider.java b/config/src/main/java/com/quorum/tessera/config/util/EnvironmentVariableProvider.java index 019cce2ad2..bd90561327 100644 --- a/config/src/main/java/com/quorum/tessera/config/util/EnvironmentVariableProvider.java +++ b/config/src/main/java/com/quorum/tessera/config/util/EnvironmentVariableProvider.java @@ -7,5 +7,4 @@ public String getEnv(String name) { return System.getenv(name); } - } diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java index 61c455d041..e67f355dbd 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java @@ -3,7 +3,6 @@ import com.bettercloud.vault.Vault; import com.bettercloud.vault.VaultException; import com.bettercloud.vault.response.LogicalResponse; -import com.quorum.tessera.config.HashicorpKeyVaultConfig; import com.quorum.tessera.config.vault.data.GetSecretData; import com.quorum.tessera.config.vault.data.HashicorpGetSecretData; import com.quorum.tessera.config.vault.data.HashicorpSetSecretData; @@ -15,12 +14,9 @@ public class HashicorpKeyVaultService implements KeyVaultService { - private final HashicorpKeyVaultConfig keyVaultConfig; - private final Vault vault; - public HashicorpKeyVaultService(HashicorpKeyVaultConfig keyVaultConfig, Vault vault) { - this.keyVaultConfig = keyVaultConfig; + public HashicorpKeyVaultService(Vault vault) { this.vault = vault; } diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java index be3145dc77..d67edcac25 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java @@ -22,10 +22,11 @@ public class HashicorpKeyVaultServiceFactory implements KeyVaultServiceFactory { public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, KeyVaultClientFactory keyVaultClientFactory) { Objects.requireNonNull(config); Objects.requireNonNull(envProvider); + Objects.requireNonNull(keyVaultClientFactory); - String roleId = envProvider.getEnv(roleIdEnvVar); - String secretId = envProvider.getEnv(secretIdEnvVar); - String authToken = envProvider.getEnv(authTokenEnvVar); + final String roleId = envProvider.getEnv(roleIdEnvVar); + final String secretId = envProvider.getEnv(secretIdEnvVar); + final String authToken = envProvider.getEnv(authTokenEnvVar); if(roleId == null && secretId == null && authToken == null) { throw new HashicorpCredentialNotSetException("Environment variables must be set to authenticate with Hashicorp Vault. Set the " + roleIdEnvVar + " and " + secretIdEnvVar + " environment variables if using the AppRole authentication method. Set the " + authTokenEnvVar + " environment variable if using another authentication method."); @@ -62,7 +63,7 @@ else if(isOnlyOneInputNull(roleId, secretId)) { final Vault authenticatedVault = hashicorpClientFactory.createAuthenticatedClient(keyVaultConfig, new VaultConfigFactory(), new SslConfigFactory(), token); - return new HashicorpKeyVaultService(keyVaultConfig, authenticatedVault); + return new HashicorpKeyVaultService(authenticatedVault); } @Override diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/SslConfigFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/SslConfigFactory.java index 5f397a62c1..100f63a5e0 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/SslConfigFactory.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/SslConfigFactory.java @@ -2,6 +2,7 @@ import com.bettercloud.vault.SslConfig; +//Ensures a newly instantiated SslConfig object is used in the HashicorpKeyVaultClientFactory public class SslConfigFactory { public SslConfig create() { diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/VaultConfigFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/VaultConfigFactory.java index 9651351ce9..849841c6b2 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/VaultConfigFactory.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/VaultConfigFactory.java @@ -2,6 +2,7 @@ import com.bettercloud.vault.VaultConfig; +//Ensures a newly instantiated VaultConfig object is used in the HashicorpKeyVaultClientFactory public class VaultConfigFactory { VaultConfig create() { diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java index 9e6beeefc6..19dfdf5f73 100644 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java @@ -4,7 +4,6 @@ import com.bettercloud.vault.VaultException; import com.bettercloud.vault.api.Logical; import com.bettercloud.vault.response.LogicalResponse; -import com.quorum.tessera.config.HashicorpKeyVaultConfig; import com.quorum.tessera.config.vault.data.GetSecretData; import com.quorum.tessera.config.vault.data.HashicorpGetSecretData; import com.quorum.tessera.config.vault.data.HashicorpSetSecretData; @@ -28,15 +27,12 @@ public class HashicorpKeyVaultServiceTest { private HashicorpKeyVaultService keyVaultService; - private HashicorpKeyVaultConfig keyVaultConfig; - private Vault vault; @Before public void setUp() { - this.keyVaultConfig = mock(HashicorpKeyVaultConfig.class); this.vault = mock(Vault.class); - this.keyVaultService = new HashicorpKeyVaultService(keyVaultConfig, vault); + this.keyVaultService = new HashicorpKeyVaultService(vault); } @Test From b36671352014433ed70f5237ad508599fe706d90 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Thu, 6 Dec 2018 10:48:38 +0000 Subject: [PATCH 42/57] Fix whitespace checkstyle issue --- .../tessera/config/cli/parsers/KeyGenerationParserTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config-cli/src/test/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParserTest.java b/config-cli/src/test/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParserTest.java index 6ec43837bb..8f624d1340 100644 --- a/config-cli/src/test/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParserTest.java +++ b/config-cli/src/test/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParserTest.java @@ -159,7 +159,7 @@ public void ifAzureVaultTypeOptionProvidedButNoVaultUrlThenValidationException() } @Test - public void ifHashicorpVaultTypeOptionAndFilenameProvidedButNoVaultUrlThenValidationException() { + public void ifHashicorpVaultTypeOptionAndFilenameProvidedButNoVaultUrlThenValidationException() { when(commandLine.hasOption("keygenvaulttype")).thenReturn(true); when(commandLine.hasOption("keygenvaulturl")).thenReturn(false); when(commandLine.getOptionValue("keygenvaulttype")).thenReturn("HASHICORP"); From 97690f6bd106b2453851533e9d6db7efce418b1a Mon Sep 17 00:00:00 2001 From: namtruong Date: Fri, 7 Dec 2018 11:38:17 +0000 Subject: [PATCH 43/57] minor refactor - reduce unnecessary visibility --- .../vault/azure/AzureCredentialNotSetException.java | 4 ++-- .../vault/azure/AzureKeyVaultClientCredentials.java | 4 ++-- .../vault/azure/AzureKeyVaultClientDelegate.java | 8 ++++---- .../key/vault/azure/AzureKeyVaultClientFactory.java | 6 +++--- .../key/vault/azure/AzureKeyVaultService.java | 2 +- .../vault/azure/AzureKeyVaultServiceFactory.java | 4 ++-- .../HashicorpCredentialNotSetException.java | 4 ++-- .../hashicorp/HashicorpKeyVaultClientFactory.java | 13 ++++++++----- .../vault/hashicorp/HashicorpKeyVaultService.java | 2 +- .../hashicorp/HashicorpKeyVaultServiceFactory.java | 6 +++--- .../vault/hashicorp/HashicorpVaultException.java | 6 +++--- .../key/vault/hashicorp/SslConfigFactory.java | 2 +- .../key/vault/hashicorp/VaultConfigFactory.java | 2 +- .../HashicorpKeyVaultServiceFactoryTest.java | 1 - .../vault/NoKeyVaultServiceFactoryException.java | 4 ++-- 15 files changed, 35 insertions(+), 33 deletions(-) diff --git a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureCredentialNotSetException.java b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureCredentialNotSetException.java index 43d2f61fd6..09afd5880a 100644 --- a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureCredentialNotSetException.java +++ b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureCredentialNotSetException.java @@ -1,8 +1,8 @@ package com.quorum.tessera.key.vault.azure; -public class AzureCredentialNotSetException extends IllegalStateException { +class AzureCredentialNotSetException extends IllegalStateException { - public AzureCredentialNotSetException(String message) { + AzureCredentialNotSetException(String message) { super(message); } diff --git a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultClientCredentials.java b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultClientCredentials.java index 360125fd44..11bbd0f663 100644 --- a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultClientCredentials.java +++ b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultClientCredentials.java @@ -24,13 +24,13 @@ public class AzureKeyVaultClientCredentials extends KeyVaultCredentials { private final ExecutorService executorService; - public AzureKeyVaultClientCredentials(String clientId, String clientSecret, ExecutorService executorService) { + AzureKeyVaultClientCredentials(String clientId, String clientSecret, ExecutorService executorService) { this.clientId = clientId; this.clientSecret = clientSecret; this.executorService = Objects.requireNonNull(executorService); } - protected void setAuthenticationContext(AuthenticationContext authenticationContext) { + void setAuthenticationContext(AuthenticationContext authenticationContext) { this.authenticationContext = authenticationContext; } diff --git a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultClientDelegate.java b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultClientDelegate.java index 63851463d4..c12b92d9ed 100644 --- a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultClientDelegate.java +++ b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultClientDelegate.java @@ -6,18 +6,18 @@ import java.util.Objects; -public class AzureKeyVaultClientDelegate { +class AzureKeyVaultClientDelegate { private final KeyVaultClient keyVaultClient; - public AzureKeyVaultClientDelegate(KeyVaultClient keyVaultClient) { + AzureKeyVaultClientDelegate(KeyVaultClient keyVaultClient) { this.keyVaultClient = Objects.requireNonNull(keyVaultClient); } - public SecretBundle getSecret(String vaultBaseUrl, String secretName) { + SecretBundle getSecret(String vaultBaseUrl, String secretName) { return keyVaultClient.getSecret(vaultBaseUrl, secretName); } - public SecretBundle setSecret(SetSecretRequest setSecretRequest) { + SecretBundle setSecret(SetSecretRequest setSecretRequest) { return keyVaultClient.setSecret(setSecretRequest); } } diff --git a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultClientFactory.java b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultClientFactory.java index 53960a2865..885261d42b 100644 --- a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultClientFactory.java +++ b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultClientFactory.java @@ -3,15 +3,15 @@ import com.microsoft.azure.keyvault.KeyVaultClient; import com.microsoft.rest.credentials.ServiceClientCredentials; -public class AzureKeyVaultClientFactory { +class AzureKeyVaultClientFactory { private final ServiceClientCredentials clientCredentials; - public AzureKeyVaultClientFactory(ServiceClientCredentials clientCredentials) { + AzureKeyVaultClientFactory(ServiceClientCredentials clientCredentials) { this.clientCredentials = clientCredentials; } - public KeyVaultClient getAuthenticatedClient() { + KeyVaultClient getAuthenticatedClient() { return new KeyVaultClient(clientCredentials); } } diff --git a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultService.java b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultService.java index 018589f9ed..0698cda7ad 100644 --- a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultService.java +++ b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultService.java @@ -17,7 +17,7 @@ public class AzureKeyVaultService implements KeyVaultService { private String vaultUrl; private AzureKeyVaultClientDelegate azureKeyVaultClientDelegate; - public AzureKeyVaultService(AzureKeyVaultConfig keyVaultConfig, AzureKeyVaultClientDelegate azureKeyVaultClientDelegate) { + AzureKeyVaultService(AzureKeyVaultConfig keyVaultConfig, AzureKeyVaultClientDelegate azureKeyVaultClientDelegate) { if(Objects.nonNull(keyVaultConfig)) { this.vaultUrl = keyVaultConfig.getUrl(); } diff --git a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactory.java b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactory.java index 81980b0058..5f6332effd 100644 --- a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactory.java +++ b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactory.java @@ -12,8 +12,8 @@ public class AzureKeyVaultServiceFactory implements KeyVaultServiceFactory { - private final String clientIdEnvVar = "AZURE_CLIENT_ID"; - private final String clientSecretEnvVar = "AZURE_CLIENT_SECRET"; + private static final String clientIdEnvVar = "AZURE_CLIENT_ID"; + private static final String clientSecretEnvVar = "AZURE_CLIENT_SECRET"; @Override public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, KeyVaultClientFactory keyVaultClientFactory) { diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpCredentialNotSetException.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpCredentialNotSetException.java index dd82377669..f096b19abb 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpCredentialNotSetException.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpCredentialNotSetException.java @@ -1,8 +1,8 @@ package com.quorum.tessera.key.vault.hashicorp; -public class HashicorpCredentialNotSetException extends IllegalStateException { +class HashicorpCredentialNotSetException extends IllegalStateException { - public HashicorpCredentialNotSetException(String message) { + HashicorpCredentialNotSetException(String message) { super(message); } diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactory.java index 2053089c58..5c3ec15e9d 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactory.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactory.java @@ -9,7 +9,9 @@ public class HashicorpKeyVaultClientFactory implements KeyVaultClientFactory { - public Vault createUnauthenticatedClient(HashicorpKeyVaultConfig keyVaultConfig, VaultConfigFactory vaultConfigFactory, SslConfigFactory sslConfigFactory) { + Vault createUnauthenticatedClient(HashicorpKeyVaultConfig keyVaultConfig, + VaultConfigFactory vaultConfigFactory, + SslConfigFactory sslConfigFactory) { VaultConfig vaultConfig = createBaseVaultConfig(keyVaultConfig, vaultConfigFactory, sslConfigFactory); VaultCallback.execute(vaultConfig::build); @@ -17,7 +19,10 @@ public Vault createUnauthenticatedClient(HashicorpKeyVaultConfig keyVaultConfig, return new Vault(vaultConfig); } - public Vault createAuthenticatedClient(HashicorpKeyVaultConfig keyVaultConfig, VaultConfigFactory vaultConfigFactory, SslConfigFactory sslConfigFactory, String authToken) { + Vault createAuthenticatedClient(HashicorpKeyVaultConfig keyVaultConfig, + VaultConfigFactory vaultConfigFactory, + SslConfigFactory sslConfigFactory, + String authToken) { VaultConfig vaultConfig = createBaseVaultConfig(keyVaultConfig, vaultConfigFactory, sslConfigFactory); vaultConfig.token(authToken); @@ -45,9 +50,7 @@ private VaultConfig createBaseVaultConfig(HashicorpKeyVaultConfig keyVaultConfig ); } - VaultCallback.execute( - () -> sslConfig.build() - ); + VaultCallback.execute(sslConfig::build); vaultConfig.sslConfig(sslConfig); } diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java index e67f355dbd..daded9adc3 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java @@ -16,7 +16,7 @@ public class HashicorpKeyVaultService implements KeyVaultService { private final Vault vault; - public HashicorpKeyVaultService(Vault vault) { + HashicorpKeyVaultService(Vault vault) { this.vault = vault; } diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java index d67edcac25..1227306d2a 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java @@ -14,9 +14,9 @@ public class HashicorpKeyVaultServiceFactory implements KeyVaultServiceFactory { - private final String roleIdEnvVar = "HASHICORP_ROLE_ID"; - private final String secretIdEnvVar = "HASHICORP_SECRET_ID"; - private final String authTokenEnvVar = "HASHICORP_TOKEN"; + private static final String roleIdEnvVar = "HASHICORP_ROLE_ID"; + private static final String secretIdEnvVar = "HASHICORP_SECRET_ID"; + private static final String authTokenEnvVar = "HASHICORP_TOKEN"; @Override public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, KeyVaultClientFactory keyVaultClientFactory) { diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpVaultException.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpVaultException.java index b7ad310fe5..115a3deeca 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpVaultException.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpVaultException.java @@ -2,13 +2,13 @@ import com.quorum.tessera.key.vault.KeyVaultException; -public class HashicorpVaultException extends KeyVaultException { +class HashicorpVaultException extends KeyVaultException { - public HashicorpVaultException(Throwable cause) { + HashicorpVaultException(Throwable cause) { super(cause); } - public HashicorpVaultException(String message) { + HashicorpVaultException(String message) { super(message); } } diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/SslConfigFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/SslConfigFactory.java index 100f63a5e0..7ef24f67db 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/SslConfigFactory.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/SslConfigFactory.java @@ -3,7 +3,7 @@ import com.bettercloud.vault.SslConfig; //Ensures a newly instantiated SslConfig object is used in the HashicorpKeyVaultClientFactory -public class SslConfigFactory { +class SslConfigFactory { public SslConfig create() { return new SslConfig(); diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/VaultConfigFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/VaultConfigFactory.java index 849841c6b2..156c135041 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/VaultConfigFactory.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/VaultConfigFactory.java @@ -3,7 +3,7 @@ import com.bettercloud.vault.VaultConfig; //Ensures a newly instantiated VaultConfig object is used in the HashicorpKeyVaultClientFactory -public class VaultConfigFactory { +class VaultConfigFactory { VaultConfig create() { return new VaultConfig(); diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java index 9c8607bef4..dd82f622f7 100644 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java @@ -283,7 +283,6 @@ public void exceptionThrownIfErrorEncounteredDuringAppRoleAuthentication() throw Auth auth = mock(Auth.class); when(unauthenticatedVault.auth()).thenReturn(auth); - AuthResponse loginResponse = mock(AuthResponse.class); when(auth.loginByAppRole(anyString(), anyString(), anyString())).thenThrow(VaultException.class); Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory)); diff --git a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/NoKeyVaultServiceFactoryException.java b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/NoKeyVaultServiceFactoryException.java index b845a4e9fa..7a10d2eaa9 100644 --- a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/NoKeyVaultServiceFactoryException.java +++ b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/NoKeyVaultServiceFactoryException.java @@ -1,8 +1,8 @@ package com.quorum.tessera.key.vault; -public class NoKeyVaultServiceFactoryException extends RuntimeException { +class NoKeyVaultServiceFactoryException extends RuntimeException { - public NoKeyVaultServiceFactoryException(String message) { + NoKeyVaultServiceFactoryException(String message) { super(message); } From 0e8da5e5decc64faf08af1d24d9b3fee5c9bc1a3 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Wed, 12 Dec 2018 13:38:15 +0000 Subject: [PATCH 44/57] Split Hashicorp secretPath into 2 params and use keystores in config Update tests and usages in config package Use empty constructor w/ setters instead of multi-arg constructor --- .../config/HashicorpKeyVaultConfig.java | 41 ++--- .../com/quorum/tessera/config/KeyData.java | 79 +++++++-- .../config/adapters/KeyDataAdapter.java | 67 +++++--- .../UnsupportedKeyPairValidator.java | 29 ++-- .../keypairs/HashicorpVaultKeyPair.java | 19 ++- .../config/keypairs/UnsupportedKeyPair.java | 79 +++++++-- .../config/HashicorpKeyVaultConfigTest.java | 33 ++-- .../quorum/tessera/config/ValidationTest.java | 27 ++- .../config/adapters/KeyDataAdapterTest.java | 97 ++++++++--- .../UnsupportedKeyPairValidatorTest.java | 154 +++++++++++++++--- .../keypairs/HashicorpVaultKeyPairTest.java | 5 +- .../keypairs/UnsupportedKeyPairTest.java | 2 +- 12 files changed, 458 insertions(+), 174 deletions(-) diff --git a/config/src/main/java/com/quorum/tessera/config/HashicorpKeyVaultConfig.java b/config/src/main/java/com/quorum/tessera/config/HashicorpKeyVaultConfig.java index 643c0361e8..020a859087 100644 --- a/config/src/main/java/com/quorum/tessera/config/HashicorpKeyVaultConfig.java +++ b/config/src/main/java/com/quorum/tessera/config/HashicorpKeyVaultConfig.java @@ -25,26 +25,19 @@ public class HashicorpKeyVaultConfig extends ConfigItem implements KeyVaultConfi @ValidPath(checkExists = true, message = "File does not exist") @XmlElement(type = String.class) @XmlJavaTypeAdapter(PathAdapter.class) - private Path tlsCertificatePath; + private Path tlsKeyStorePath; @Valid @ValidPath(checkExists = true, message = "File does not exist") @XmlElement(type = String.class) @XmlJavaTypeAdapter(PathAdapter.class) - private Path tlsKeyPath; + private Path tlsTrustStorePath; - @Valid - @ValidPath(checkExists = true, message = "File does not exist") - @XmlElement(type = String.class) - @XmlJavaTypeAdapter(PathAdapter.class) - private Path tlsServerCertificatePath; - - public HashicorpKeyVaultConfig(String url, String approlePath, Path tlsCertificatePath, Path tlsKeyPath, Path tlsServerCertificatePath) { + public HashicorpKeyVaultConfig(String url, String approlePath, Path tlsKeyStorePath, Path tlsTrustStorePath) { this.url = url; this.approlePath = approlePath; - this.tlsCertificatePath = tlsCertificatePath; - this.tlsKeyPath = tlsKeyPath; - this.tlsServerCertificatePath = tlsServerCertificatePath; + this.tlsKeyStorePath = tlsKeyStorePath; + this.tlsTrustStorePath = tlsTrustStorePath; } public HashicorpKeyVaultConfig() { @@ -58,28 +51,20 @@ public void setUrl(String url) { this.url = url; } - public Path getTlsCertificatePath() { - return tlsCertificatePath; - } - - public void setTlsCertificatePath(Path tlsCertificatePath) { - this.tlsCertificatePath = tlsCertificatePath; - } - - public Path getTlsKeyPath() { - return tlsKeyPath; + public Path getTlsKeyStorePath() { + return tlsKeyStorePath; } - public void setTlsKeyPath(Path tlsKeyPath) { - this.tlsKeyPath = tlsKeyPath; + public void setTlsKeyStorePath(Path tlsKeyStorePath) { + this.tlsKeyStorePath = tlsKeyStorePath; } - public Path getTlsServerCertificatePath() { - return tlsServerCertificatePath; + public Path getTlsTrustStorePath() { + return tlsTrustStorePath; } - public void setTlsServerCertificatePath(Path tlsServerCertificatePath) { - this.tlsServerCertificatePath = tlsServerCertificatePath; + public void setTlsTrustStorePath(Path tlsTrustStorePath) { + this.tlsTrustStorePath = tlsTrustStorePath; } public String getApprolePath() { diff --git a/config/src/main/java/com/quorum/tessera/config/KeyData.java b/config/src/main/java/com/quorum/tessera/config/KeyData.java index dfac91f1b5..4f96fcae1b 100644 --- a/config/src/main/java/com/quorum/tessera/config/KeyData.java +++ b/config/src/main/java/com/quorum/tessera/config/KeyData.java @@ -50,28 +50,23 @@ public class KeyData extends ConfigItem { private String hashicorpVaultPrivateKeyId; @XmlElement - private String hashicorpVaultSecretPath; - - public KeyData(final KeyDataConfig keyDataConfig, - final String privateKey, - final String publicKey, - final Path privKeyPath, - final Path pubKeyPath, - final String azureVaultPrivateKeyId, - final String azureVaultPublicKeyId, - final String hashicorpVaultPrivateKeyId, - final String hashicorpVaultPublicKeyId, - final String hashicorpVaultSecretPath) { + private String hashicorpVaultSecretEngineName; + + @XmlElement + private String hashicorpVaultSecretName; + + public KeyData(KeyDataConfig config, String privateKey, String publicKey, Path privateKeyPath, Path publicKeyPath, String azureVaultPublicKeyId, String azureVaultPrivateKeyId, String hashicorpVaultPublicKeyId, String hashicorpVaultPrivateKeyId, String hashicorpVaultSecretEngineName, String hashicorpVaultSecretName) { + this.config = config; this.privateKey = privateKey; this.publicKey = publicKey; - this.config = keyDataConfig; - this.privateKeyPath = privKeyPath; - this.publicKeyPath = pubKeyPath; + this.privateKeyPath = privateKeyPath; + this.publicKeyPath = publicKeyPath; this.azureVaultPublicKeyId = azureVaultPublicKeyId; this.azureVaultPrivateKeyId = azureVaultPrivateKeyId; this.hashicorpVaultPublicKeyId = hashicorpVaultPublicKeyId; this.hashicorpVaultPrivateKeyId = hashicorpVaultPrivateKeyId; - this.hashicorpVaultSecretPath = hashicorpVaultSecretPath; + this.hashicorpVaultSecretEngineName = hashicorpVaultSecretEngineName; + this.hashicorpVaultSecretName = hashicorpVaultSecretName; } public KeyData() { @@ -114,7 +109,55 @@ public String getHashicorpVaultPrivateKeyId() { return hashicorpVaultPrivateKeyId; } - public String getHashicorpVaultSecretPath() { - return hashicorpVaultSecretPath; + public String getHashicorpVaultSecretEngineName() { + return hashicorpVaultSecretEngineName; + } + + public String getHashicorpVaultSecretName() { + return hashicorpVaultSecretName; + } + + public void setConfig(KeyDataConfig config) { + this.config = config; + } + + public void setPrivateKey(String privateKey) { + this.privateKey = privateKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public void setPrivateKeyPath(Path privateKeyPath) { + this.privateKeyPath = privateKeyPath; + } + + public void setPublicKeyPath(Path publicKeyPath) { + this.publicKeyPath = publicKeyPath; + } + + public void setAzureVaultPublicKeyId(String azureVaultPublicKeyId) { + this.azureVaultPublicKeyId = azureVaultPublicKeyId; + } + + public void setAzureVaultPrivateKeyId(String azureVaultPrivateKeyId) { + this.azureVaultPrivateKeyId = azureVaultPrivateKeyId; + } + + public void setHashicorpVaultPublicKeyId(String hashicorpVaultPublicKeyId) { + this.hashicorpVaultPublicKeyId = hashicorpVaultPublicKeyId; + } + + public void setHashicorpVaultPrivateKeyId(String hashicorpVaultPrivateKeyId) { + this.hashicorpVaultPrivateKeyId = hashicorpVaultPrivateKeyId; + } + + public void setHashicorpVaultSecretEngineName(String hashicorpVaultSecretEngineName) { + this.hashicorpVaultSecretEngineName = hashicorpVaultSecretEngineName; + } + + public void setHashicorpVaultSecretName(String hashicorpVaultSecretName) { + this.hashicorpVaultSecretName = hashicorpVaultSecretName; } } diff --git a/config/src/main/java/com/quorum/tessera/config/adapters/KeyDataAdapter.java b/config/src/main/java/com/quorum/tessera/config/adapters/KeyDataAdapter.java index 3304616f75..38021b3b6c 100644 --- a/config/src/main/java/com/quorum/tessera/config/adapters/KeyDataAdapter.java +++ b/config/src/main/java/com/quorum/tessera/config/adapters/KeyDataAdapter.java @@ -29,8 +29,9 @@ public ConfigKeyPair unmarshal(final KeyData keyData) { } //case 4, the Hashicorp Vault data is provided - if(keyData.getHashicorpVaultPublicKeyId() != null && keyData.getHashicorpVaultPrivateKeyId() != null && keyData.getHashicorpVaultSecretPath() != null) { - return new HashicorpVaultKeyPair(keyData.getHashicorpVaultPublicKeyId(), keyData.getHashicorpVaultPrivateKeyId(), keyData.getHashicorpVaultSecretPath()); + if(keyData.getHashicorpVaultPublicKeyId() != null && keyData.getHashicorpVaultPrivateKeyId() != null + && keyData.getHashicorpVaultSecretEngineName() != null && keyData.getHashicorpVaultSecretName() != null) { + return new HashicorpVaultKeyPair(keyData.getHashicorpVaultPublicKeyId(), keyData.getHashicorpVaultPrivateKeyId(), keyData.getHashicorpVaultSecretEngineName(), keyData.getHashicorpVaultSecretName()); } //case 5, the keys are provided inside a file @@ -49,43 +50,63 @@ public ConfigKeyPair unmarshal(final KeyData keyData) { keyData.getAzureVaultPrivateKeyId(), keyData.getHashicorpVaultPublicKeyId(), keyData.getHashicorpVaultPrivateKeyId(), - keyData.getHashicorpVaultSecretPath() + keyData.getHashicorpVaultSecretEngineName(), + keyData.getHashicorpVaultSecretName() ); } @Override - public KeyData marshal(final ConfigKeyPair keyData) { + public KeyData marshal(final ConfigKeyPair keyPair) { - if(keyData instanceof DirectKeyPair) { - DirectKeyPair kp = (DirectKeyPair) keyData; - return new KeyData(null, kp.getPrivateKey(), kp.getPublicKey(), null, null, null, null, null, null, null); + KeyData keyData = new KeyData(); + + if(keyPair instanceof DirectKeyPair) { + DirectKeyPair kp = (DirectKeyPair) keyPair; + + keyData.setPublicKey(kp.getPublicKey()); + keyData.setPrivateKey(kp.getPrivateKey()); + return keyData; } - if(keyData instanceof InlineKeypair) { - InlineKeypair kp = (InlineKeypair) keyData; - return new KeyData(kp.getPrivateKeyConfig(), null, kp.getPublicKey(), null, null, null, null, null, null, null); + if(keyPair instanceof InlineKeypair) { + InlineKeypair kp = (InlineKeypair) keyPair; + + keyData.setPublicKey(kp.getPublicKey()); + keyData.setConfig(kp.getPrivateKeyConfig()); + return keyData; } - if(keyData instanceof AzureVaultKeyPair) { - AzureVaultKeyPair kp = (AzureVaultKeyPair) keyData; - return new KeyData(null, null, null, null, null, kp.getPrivateKeyId(), kp.getPublicKeyId(), null, null, null); + if(keyPair instanceof AzureVaultKeyPair) { + AzureVaultKeyPair kp = (AzureVaultKeyPair) keyPair; + + keyData.setAzureVaultPublicKeyId(kp.getPublicKeyId()); + keyData.setAzureVaultPrivateKeyId(kp.getPrivateKeyId()); + return keyData; } - if(keyData instanceof HashicorpVaultKeyPair) { - HashicorpVaultKeyPair kp = (HashicorpVaultKeyPair) keyData; - return new KeyData(null, null, null, null, null, null, null, kp.getPrivateKeyId(), kp.getPublicKeyId(), kp.getSecretPath()); + if(keyPair instanceof HashicorpVaultKeyPair) { + HashicorpVaultKeyPair kp = (HashicorpVaultKeyPair) keyPair; + + keyData.setHashicorpVaultPublicKeyId(kp.getPublicKeyId()); + keyData.setHashicorpVaultPrivateKeyId(kp.getPrivateKeyId()); + keyData.setHashicorpVaultSecretEngineName(kp.getSecretEngineName()); + keyData.setHashicorpVaultSecretName(kp.getSecretName()); + return keyData; } - if(keyData instanceof FilesystemKeyPair) { - FilesystemKeyPair kp = (FilesystemKeyPair) keyData; - return new KeyData(null, null, null, kp.getPrivateKeyPath(), kp.getPublicKeyPath(), null, null, null, null, null); + if(keyPair instanceof FilesystemKeyPair) { + FilesystemKeyPair kp = (FilesystemKeyPair) keyPair; + + keyData.setPublicKeyPath(kp.getPublicKeyPath()); + keyData.setPrivateKeyPath(kp.getPrivateKeyPath()); + return keyData; } - if(keyData instanceof UnsupportedKeyPair) { - UnsupportedKeyPair kp = (UnsupportedKeyPair) keyData; - return new KeyData(kp.getConfig(), kp.getPrivateKey(), kp.getPublicKey(), kp.getPrivateKeyPath(), kp.getPublicKeyPath(), kp.getAzureVaultPrivateKeyId(), kp.getAzureVaultPublicKeyId(), kp.getHashicorpVaultPrivateKeyId(), kp.getHashicorpVaultPublicKeyId(), kp.getHashicorpVaultSecretPath()); + if(keyPair instanceof UnsupportedKeyPair) { + UnsupportedKeyPair kp = (UnsupportedKeyPair) keyPair; + return new KeyData(kp.getConfig(), kp.getPrivateKey(), kp.getPublicKey(), kp.getPrivateKeyPath(), kp.getPublicKeyPath(), kp.getAzureVaultPrivateKeyId(), kp.getAzureVaultPublicKeyId(), kp.getHashicorpVaultPrivateKeyId(), kp.getHashicorpVaultPublicKeyId(), kp.getHashicorpVaultSecretEngineName(), kp.getHashicorpVaultSecretName()); } - throw new UnsupportedOperationException("The keypair type " + keyData.getClass() + " is not allowed"); + throw new UnsupportedOperationException("The keypair type " + keyPair.getClass() + " is not allowed"); } } diff --git a/config/src/main/java/com/quorum/tessera/config/constraints/UnsupportedKeyPairValidator.java b/config/src/main/java/com/quorum/tessera/config/constraints/UnsupportedKeyPairValidator.java index b84f9f1ad6..05a7337282 100644 --- a/config/src/main/java/com/quorum/tessera/config/constraints/UnsupportedKeyPairValidator.java +++ b/config/src/main/java/com/quorum/tessera/config/constraints/UnsupportedKeyPairValidator.java @@ -5,6 +5,7 @@ import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import java.util.Objects; +import java.util.stream.Stream; public class UnsupportedKeyPairValidator implements ConstraintValidator { @@ -47,30 +48,38 @@ else if(isIncompleteFilesystemKeyPair(keyPair)) { } private boolean isIncompleteDirectKeyPair(UnsupportedKeyPair keyPair) { - return isOnlyOneInputNull(keyPair.getPublicKey(), keyPair.getPrivateKey()); + return isIncomplete(keyPair.getPublicKey(), keyPair.getPrivateKey()); } private boolean isIncompleteInlineKeyPair(UnsupportedKeyPair keyPair) { - return isOnlyOneInputNull(keyPair.getPublicKey(), keyPair.getConfig()); + return isIncomplete(keyPair.getPublicKey(), keyPair.getConfig()); } private boolean isIncompleteAzureVaultKeyPair(UnsupportedKeyPair keyPair) { - return isOnlyOneInputNull(keyPair.getAzureVaultPublicKeyId(), keyPair.getAzureVaultPrivateKeyId()); + return isIncomplete(keyPair.getAzureVaultPublicKeyId(), keyPair.getAzureVaultPrivateKeyId()); } private boolean isIncompleteHashicorpVaultKeyPair(UnsupportedKeyPair keyPair) { - if(isOnlyOneInputNull(keyPair.getHashicorpVaultPublicKeyId(), keyPair.getHashicorpVaultPrivateKeyId())) { + return isIncomplete(keyPair.getHashicorpVaultPublicKeyId(), keyPair.getHashicorpVaultPrivateKeyId(), keyPair.getHashicorpVaultSecretEngineName(), keyPair.getHashicorpVaultSecretName()); + } + + private boolean isIncompleteFilesystemKeyPair(UnsupportedKeyPair keyPair) { + return isIncomplete(keyPair.getPublicKeyPath(), keyPair.getPrivateKeyPath()); + } + + private boolean isIncomplete(Object ...args) { + if(areAnyNull(args) && areAnyNonNull(args)) { return true; } - - return isOnlyOneInputNull(keyPair.getHashicorpVaultPublicKeyId(), keyPair.getHashicorpVaultSecretPath()); + return false; } - private boolean isIncompleteFilesystemKeyPair(UnsupportedKeyPair keyPair) { - return isOnlyOneInputNull(keyPair.getPublicKeyPath(), keyPair.getPrivateKeyPath()); + private boolean areAnyNull(Object... args) { + return Stream.of(args).anyMatch(Objects::isNull); } - private boolean isOnlyOneInputNull(Object obj1, Object obj2) { - return Objects.isNull(obj1) ^ Objects.isNull(obj2); + private boolean areAnyNonNull(Object... args) { + return Stream.of(args).anyMatch(Objects::nonNull); } + } diff --git a/config/src/main/java/com/quorum/tessera/config/keypairs/HashicorpVaultKeyPair.java b/config/src/main/java/com/quorum/tessera/config/keypairs/HashicorpVaultKeyPair.java index a352b72acd..a00f16c507 100644 --- a/config/src/main/java/com/quorum/tessera/config/keypairs/HashicorpVaultKeyPair.java +++ b/config/src/main/java/com/quorum/tessera/config/keypairs/HashicorpVaultKeyPair.java @@ -15,12 +15,17 @@ public class HashicorpVaultKeyPair implements ConfigKeyPair { @NotNull @XmlElement - private String secretPath; + private String secretEngineName; - public HashicorpVaultKeyPair(String publicKeyId, String privateKeyId, String secretPath) { + @NotNull + @XmlElement + private String secretName; + + public HashicorpVaultKeyPair(String publicKeyId, String privateKeyId, String secretEngineName, String secretName) { this.publicKeyId = publicKeyId; this.privateKeyId = privateKeyId; - this.secretPath = secretPath; + this.secretEngineName = secretEngineName; + this.secretName = secretName; } public String getPublicKeyId() { @@ -31,8 +36,12 @@ public String getPrivateKeyId() { return privateKeyId; } - public String getSecretPath() { - return secretPath; + public String getSecretEngineName() { + return secretEngineName; + } + + public String getSecretName() { + return secretName; } @Override diff --git a/config/src/main/java/com/quorum/tessera/config/keypairs/UnsupportedKeyPair.java b/config/src/main/java/com/quorum/tessera/config/keypairs/UnsupportedKeyPair.java index 40094955f1..0523d33ee7 100644 --- a/config/src/main/java/com/quorum/tessera/config/keypairs/UnsupportedKeyPair.java +++ b/config/src/main/java/com/quorum/tessera/config/keypairs/UnsupportedKeyPair.java @@ -12,27 +12,27 @@ public class UnsupportedKeyPair implements ConfigKeyPair { @XmlElement - private final KeyDataConfig config; + private KeyDataConfig config; @XmlElement - private final String privateKey; + private String privateKey; @XmlElement - private final String publicKey; + private String publicKey; @XmlElement @XmlJavaTypeAdapter(PathAdapter.class) - private final Path privateKeyPath; + private Path privateKeyPath; @XmlElement @XmlJavaTypeAdapter(PathAdapter.class) - private final Path publicKeyPath; + private Path publicKeyPath; @XmlElement - private final String azureVaultPublicKeyId; + private String azureVaultPublicKeyId; @XmlElement - private final String azureVaultPrivateKeyId; + private String azureVaultPrivateKeyId; @XmlElement private String hashicorpVaultPublicKeyId; @@ -41,9 +41,12 @@ public class UnsupportedKeyPair implements ConfigKeyPair { private String hashicorpVaultPrivateKeyId; @XmlElement - private String hashicorpVaultSecretPath; + private String hashicorpVaultSecretEngineName; - public UnsupportedKeyPair(KeyDataConfig config, String privateKey, String publicKey, Path privateKeyPath, Path publicKeyPath, String azureVaultPublicKeyId, String azureVaultPrivateKeyId, String hashicorpVaultPublicKeyId, String hashicorpVaultPrivateKeyId, String hashicorpVaultSecretPath) { + @XmlElement + private String hashicorpVaultSecretName; + + public UnsupportedKeyPair(KeyDataConfig config, String privateKey, String publicKey, Path privateKeyPath, Path publicKeyPath, String azureVaultPublicKeyId, String azureVaultPrivateKeyId, String hashicorpVaultPublicKeyId, String hashicorpVaultPrivateKeyId, String hashicorpVaultSecretEngineName, String hashicorpVaultSecretName) { this.config = config; this.privateKey = privateKey; this.publicKey = publicKey; @@ -53,7 +56,12 @@ public UnsupportedKeyPair(KeyDataConfig config, String privateKey, String public this.azureVaultPrivateKeyId = azureVaultPrivateKeyId; this.hashicorpVaultPublicKeyId = hashicorpVaultPublicKeyId; this.hashicorpVaultPrivateKeyId = hashicorpVaultPrivateKeyId; - this.hashicorpVaultSecretPath = hashicorpVaultSecretPath; + this.hashicorpVaultSecretEngineName = hashicorpVaultSecretEngineName; + this.hashicorpVaultSecretName = hashicorpVaultSecretName; + } + + public UnsupportedKeyPair() { + } @Override @@ -94,8 +102,12 @@ public String getHashicorpVaultPrivateKeyId() { return hashicorpVaultPrivateKeyId; } - public String getHashicorpVaultSecretPath() { - return hashicorpVaultSecretPath; + public String getHashicorpVaultSecretEngineName() { + return hashicorpVaultSecretEngineName; + } + + public String getHashicorpVaultSecretName() { + return hashicorpVaultSecretName; } @Override @@ -108,4 +120,47 @@ public String getPassword() { return null; } + public void setConfig(KeyDataConfig config) { + this.config = config; + } + + public void setPrivateKey(String privateKey) { + this.privateKey = privateKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public void setPrivateKeyPath(Path privateKeyPath) { + this.privateKeyPath = privateKeyPath; + } + + public void setPublicKeyPath(Path publicKeyPath) { + this.publicKeyPath = publicKeyPath; + } + + public void setAzureVaultPublicKeyId(String azureVaultPublicKeyId) { + this.azureVaultPublicKeyId = azureVaultPublicKeyId; + } + + public void setAzureVaultPrivateKeyId(String azureVaultPrivateKeyId) { + this.azureVaultPrivateKeyId = azureVaultPrivateKeyId; + } + + public void setHashicorpVaultPublicKeyId(String hashicorpVaultPublicKeyId) { + this.hashicorpVaultPublicKeyId = hashicorpVaultPublicKeyId; + } + + public void setHashicorpVaultPrivateKeyId(String hashicorpVaultPrivateKeyId) { + this.hashicorpVaultPrivateKeyId = hashicorpVaultPrivateKeyId; + } + + public void setHashicorpVaultSecretEngineName(String hashicorpVaultSecretEngineName) { + this.hashicorpVaultSecretEngineName = hashicorpVaultSecretEngineName; + } + + public void setHashicorpVaultSecretName(String hashicorpVaultSecretName) { + this.hashicorpVaultSecretName = hashicorpVaultSecretName; + } } diff --git a/config/src/test/java/com/quorum/tessera/config/HashicorpKeyVaultConfigTest.java b/config/src/test/java/com/quorum/tessera/config/HashicorpKeyVaultConfigTest.java index fe8376d8d0..12797d874f 100644 --- a/config/src/test/java/com/quorum/tessera/config/HashicorpKeyVaultConfigTest.java +++ b/config/src/test/java/com/quorum/tessera/config/HashicorpKeyVaultConfigTest.java @@ -21,41 +21,34 @@ public void setUp() { public void multiArgConstructor() { String url = "url"; String approle = "approle"; - Path tlsCertPath = Paths.get("tlscertpath"); - Path tlsKeyPath = Paths.get("tlskeypath"); - Path tlsServerCertPath = Paths.get("tlsservercertpath"); + Path keyStore = Paths.get("keystore"); + Path trustStore = Paths.get("truststore"); - HashicorpKeyVaultConfig conf = new HashicorpKeyVaultConfig(url, approle, tlsCertPath, tlsKeyPath, tlsServerCertPath); + HashicorpKeyVaultConfig conf = new HashicorpKeyVaultConfig(url, approle, keyStore, trustStore); assertThat(conf.getUrl()).isEqualTo(url); assertThat(conf.getApprolePath()).isEqualTo("approle"); - assertThat(conf.getTlsCertificatePath()).isEqualTo(tlsCertPath); - assertThat(conf.getTlsKeyPath()).isEqualTo(tlsKeyPath); - assertThat(conf.getTlsServerCertificatePath()).isEqualTo(tlsServerCertPath); + assertThat(conf.getTlsKeyStorePath()).isEqualTo(keyStore); + assertThat(conf.getTlsTrustStorePath()).isEqualTo(trustStore); } @Test public void gettersAndSetters() { assertThat(vaultConfig.getUrl()).isEqualTo(null); - assertThat(vaultConfig.getTlsCertificatePath()).isEqualTo(null); - assertThat(vaultConfig.getTlsKeyPath()).isEqualTo(null); - assertThat(vaultConfig.getTlsServerCertificatePath()).isEqualTo(null); + assertThat(vaultConfig.getTlsKeyStorePath()).isEqualTo(null); + assertThat(vaultConfig.getTlsTrustStorePath()).isEqualTo(null); String url = "url"; - Path tlsCertPath = Paths.get("tlscertpath"); - Path tlsKeyPath = Paths.get("tlskeypath"); - Path tlsServerCertPath = Paths.get("tlsservercertpath"); + Path keyStore = Paths.get("keystore"); + Path trustStore = Paths.get("truststore"); vaultConfig.setUrl(url); - vaultConfig.setTlsCertificatePath(tlsCertPath); - vaultConfig.setTlsKeyPath(tlsKeyPath); - vaultConfig.setTlsServerCertificatePath(tlsServerCertPath); + vaultConfig.setTlsKeyStorePath(keyStore); + vaultConfig.setTlsTrustStorePath(trustStore); assertThat(vaultConfig.getUrl()).isEqualTo(url); - assertThat(vaultConfig.getTlsCertificatePath()).isEqualTo(tlsCertPath); - assertThat(vaultConfig.getTlsKeyPath()).isEqualTo(tlsKeyPath); - assertThat(vaultConfig.getTlsServerCertificatePath()).isEqualTo(tlsServerCertPath); - + assertThat(vaultConfig.getTlsKeyStorePath()).isEqualTo(keyStore); + assertThat(vaultConfig.getTlsTrustStorePath()).isEqualTo(trustStore); } @Test diff --git a/config/src/test/java/com/quorum/tessera/config/ValidationTest.java b/config/src/test/java/com/quorum/tessera/config/ValidationTest.java index b0e0e6f310..68b8241361 100644 --- a/config/src/test/java/com/quorum/tessera/config/ValidationTest.java +++ b/config/src/test/java/com/quorum/tessera/config/ValidationTest.java @@ -54,7 +54,12 @@ public void validateArgonOptionsAllNullAlgoHasDefaultValue() { public void keyDataConfigMissingPassword() { PrivateKeyData privateKeyData = new PrivateKeyData(null, "snonce", "asalt", "sbox", mock(ArgonOptions.class), null); KeyDataConfig keyDataConfig = new KeyDataConfig(privateKeyData, PrivateKeyType.LOCKED); - KeyData keyData = new KeyData(keyDataConfig, "privateKey", "publicKey", null, null, null, null, null, null, null); + + KeyData keyData = new KeyData(); + keyData.setConfig(keyDataConfig); + keyData.setPublicKey("publicKey"); + keyData.setPrivateKey("privateKey"); + Set> violations = validator.validate(keyData); assertThat(violations).hasSize(1); @@ -68,7 +73,12 @@ public void keyDataConfigMissingPassword() { public void keyDataConfigNaclFailure() { PrivateKeyData privateKeyData = new PrivateKeyData(null, "snonce", "asalt", "sbox", mock(ArgonOptions.class), "SECRET"); KeyDataConfig keyDataConfig = new KeyDataConfig(privateKeyData, PrivateKeyType.LOCKED); - KeyData keyData = new KeyData(keyDataConfig, "NACL_FAILURE", "publicKey", null, null, null, null, null, null, null); + + KeyData keyData = new KeyData(); + keyData.setConfig(keyDataConfig); + keyData.setPrivateKey("NACL_FAILURE"); + keyData.setPublicKey("publicKey"); + Set> violations = validator.validate(keyData); assertThat(violations).hasSize(1); @@ -82,7 +92,12 @@ public void keyDataConfigNaclFailure() { public void keyDataConfigInvalidBase64() { PrivateKeyData privateKeyData = new PrivateKeyData(null, "snonce", "asalt", "sbox", mock(ArgonOptions.class), "SECRET"); KeyDataConfig keyDataConfig = new KeyDataConfig(privateKeyData, PrivateKeyType.LOCKED); - KeyData keyData = new KeyData(keyDataConfig, "INAVLID_BASE", "publicKey", null, null, null, null, null, null, null); + + KeyData keyData = new KeyData(); + keyData.setConfig(keyDataConfig); + keyData.setPrivateKey("INVALID_BASE"); + keyData.setPublicKey("publicKey"); + Set> violations = validator.validate(keyData); assertThat(violations).hasSize(1); @@ -294,7 +309,7 @@ public void azureKeyPairProvidedWithHashicorpKeyVaultConfigCreatesViolation() { @Test public void hashicorpKeyPairProvidedWithoutKeyVaultConfigCreatesViolation() { - HashicorpVaultKeyPair keyPair = new HashicorpVaultKeyPair("pubId", "privdId", "secretPath"); + HashicorpVaultKeyPair keyPair = new HashicorpVaultKeyPair("pubId", "privdId", "secretEngine", "secretName"); KeyConfiguration keyConfiguration = new KeyConfiguration(); keyConfiguration.setKeyData(singletonList(keyPair)); @@ -312,7 +327,7 @@ public void hashicorpKeyPairProvidedWithoutKeyVaultConfigCreatesViolation() { @Test public void hashicorpKeyPairProvidedWithAzureKeyVaultConfigCreatesViolation() { - HashicorpVaultKeyPair keyPair = new HashicorpVaultKeyPair("pubId", "privdId", "secretPath"); + HashicorpVaultKeyPair keyPair = new HashicorpVaultKeyPair("pubId", "privdId", "secretEngine", "secretName"); KeyConfiguration keyConfiguration = new KeyConfiguration(); keyConfiguration.setKeyData(singletonList(keyPair)); @@ -392,7 +407,7 @@ public void hashicorpVaultConfigWithNoUrlCreatesNotNullViolation() { @Test public void hashicorpVaultKeyPairProvidedButKeyVaultConfigHasNullUrlCreatesNotNullViolation() { - HashicorpVaultKeyPair keyPair = new HashicorpVaultKeyPair("pubId", "privId", "secretPath"); + HashicorpVaultKeyPair keyPair = new HashicorpVaultKeyPair("pubId", "privId", "secretEngine", "secretName"); HashicorpKeyVaultConfig keyVaultConfig = new HashicorpKeyVaultConfig(); KeyConfiguration keyConfiguration = new KeyConfiguration(null, null, singletonList(keyPair), null, keyVaultConfig); diff --git a/config/src/test/java/com/quorum/tessera/config/adapters/KeyDataAdapterTest.java b/config/src/test/java/com/quorum/tessera/config/adapters/KeyDataAdapterTest.java index e12e774436..e5a2ae876a 100644 --- a/config/src/test/java/com/quorum/tessera/config/adapters/KeyDataAdapterTest.java +++ b/config/src/test/java/com/quorum/tessera/config/adapters/KeyDataAdapterTest.java @@ -21,7 +21,9 @@ public class KeyDataAdapterTest { @Test public void marshallDirectKeys() { final ConfigKeyPair keys = new DirectKeyPair("PUB", "PRIV"); - final KeyData expected = new KeyData(null, "PRIV", "PUB", null, null, null, null, null, null, null); + final KeyData expected = new KeyData(); + expected.setPublicKey("PUB"); + expected.setPrivateKey("PRIV"); final KeyData marshalledKey = adapter.marshal(keys); @@ -32,7 +34,10 @@ public void marshallDirectKeys() { public void marshallInlineKeys() { final PrivateKeyData pkd = new PrivateKeyData("val", null, null, null, null, null); final ConfigKeyPair keys = new InlineKeypair("PUB", new KeyDataConfig(pkd, UNLOCKED)); - final KeyData expected = new KeyData(new KeyDataConfig(pkd, UNLOCKED), null, "PUB", null, null, null, null, null, null, null); + final KeyData expected = new KeyData(); + + expected.setPublicKey("PUB"); + expected.setConfig(new KeyDataConfig(pkd, UNLOCKED)); final KeyData marshalledKey = adapter.marshal(keys); @@ -44,7 +49,10 @@ public void marshallFilesystemKeys() { final Path path = mock(Path.class); final FilesystemKeyPair keyPair = new FilesystemKeyPair(path, path); - final KeyData expected = new KeyData(null, null, null, path, path, null, null, null, null, null); + final KeyData expected = new KeyData(); + expected.setPublicKeyPath(path); + expected.setPrivateKeyPath(path); + final KeyData result = adapter.marshal(keyPair); assertThat(result).isEqualTo(expected); @@ -54,7 +62,10 @@ public void marshallFilesystemKeys() { public void marshallAzureKeys() { final AzureVaultKeyPair keyPair = new AzureVaultKeyPair("pubId", "privId"); - final KeyData expected = new KeyData(null, null, null, null, null, "privId", "pubId", null, null, null); + final KeyData expected = new KeyData(); + expected.setAzureVaultPublicKeyId("pubId"); + expected.setAzureVaultPrivateKeyId("privId"); + final KeyData result = adapter.marshal(keyPair); assertThat(result).isEqualTo(expected); @@ -62,9 +73,13 @@ public void marshallAzureKeys() { @Test public void marshallHashicorpKeys() { - final HashicorpVaultKeyPair keyPair = new HashicorpVaultKeyPair("pubId", "privId", "secretPath"); + final HashicorpVaultKeyPair keyPair = new HashicorpVaultKeyPair("pubId", "privId", "secretEngineName", "secretName"); - final KeyData expected = new KeyData(null, null, null, null, null, null, null, "privId", "pubId", "secretPath"); + final KeyData expected = new KeyData(); + expected.setHashicorpVaultPublicKeyId("pubId"); + expected.setHashicorpVaultPrivateKeyId("privId"); + expected.setHashicorpVaultSecretEngineName("secretEngineName"); + expected.setHashicorpVaultSecretName("secretName"); final KeyData result = adapter.marshal(keyPair); @@ -75,9 +90,14 @@ public void marshallHashicorpKeys() { public void marshallUnsupportedKeys() { final KeyDataConfig keyDataConfig = mock(KeyDataConfig.class); final Path path = mock(Path.class); - final UnsupportedKeyPair keyPair = new UnsupportedKeyPair(keyDataConfig, "priv", null, path, null, null, null, null, null, null); + final UnsupportedKeyPair keyPair = new UnsupportedKeyPair(keyDataConfig, "priv", null, path, null, null, null, null, null, null, null); + + final KeyData expected = new KeyData(); + //set a random selection of values that are not sufficient to make a complete key pair of any type + expected.setConfig(keyDataConfig); + expected.setPrivateKey("priv"); + expected.setPrivateKeyPath(path); - final KeyData expected = new KeyData(keyDataConfig, "priv", null, path, null, null, null, null, null, null); final KeyData result = adapter.marshal(keyPair); assertThat(result).isEqualTo(expected); @@ -129,7 +149,9 @@ public void marshallLockedKeyNullifiesPrivateKey() { @Test public void unmarshallingDirectKeysGivesCorrectKeypair() { - final KeyData input = new KeyData(null, "private", "public", null, null, null, null, null, null, null); + final KeyData input = new KeyData(); + input.setPublicKey("public"); + input.setPrivateKey("private"); final ConfigKeyPair result = this.adapter.unmarshal(input); assertThat(result).isInstanceOf(DirectKeyPair.class); @@ -138,7 +160,9 @@ public void unmarshallingDirectKeysGivesCorrectKeypair() { @Test public void unmarshallingInlineKeysGivesCorrectKeypair() { - final KeyData input = new KeyData(new KeyDataConfig(null, null), null, "public", null, null, null, null, null, null, null); + final KeyData input = new KeyData(); + input.setPublicKey("public"); + input.setConfig(new KeyDataConfig(null, null)); final ConfigKeyPair result = this.adapter.unmarshal(input); assertThat(result).isInstanceOf(InlineKeypair.class); @@ -147,7 +171,9 @@ public void unmarshallingInlineKeysGivesCorrectKeypair() { @Test public void unmarshallingFilesystemKeysGivesCorrectKeypair() { - final KeyData input = new KeyData(null, null, null, Paths.get("private"), Paths.get("public"), null, null, null, null, null); + final KeyData input = new KeyData(); + input.setPublicKeyPath(Paths.get("public")); + input.setPrivateKeyPath(Paths.get("private")); final ConfigKeyPair result = this.adapter.unmarshal(input); assertThat(result).isInstanceOf(FilesystemKeyPair.class); @@ -155,7 +181,9 @@ public void unmarshallingFilesystemKeysGivesCorrectKeypair() { @Test public void unmarshallingAzureKeysGivesCorrectKeyPair() { - final KeyData input = new KeyData(null, null, null, null, null, "privId", "pubId", null, null, null); + final KeyData input = new KeyData(); + input.setAzureVaultPublicKeyId("pubId"); + input.setAzureVaultPrivateKeyId("privId"); final ConfigKeyPair result = this.adapter.unmarshal(input); assertThat(result).isInstanceOf(AzureVaultKeyPair.class); @@ -163,7 +191,12 @@ public void unmarshallingAzureKeysGivesCorrectKeyPair() { @Test public void unmarshallingHashicorpKeysGivesCorrectKeyPair() { - final KeyData input = new KeyData(null, null, null, null, null, null, null, "privId", "pubId", "secretPath"); + final KeyData input = new KeyData(); + + input.setHashicorpVaultPublicKeyId("pubId"); + input.setHashicorpVaultPrivateKeyId("privId"); + input.setHashicorpVaultSecretEngineName("secretEngine"); + input.setHashicorpVaultSecretName("secretName"); final ConfigKeyPair result = this.adapter.unmarshal(input); assertThat(result).isInstanceOf(HashicorpVaultKeyPair.class); @@ -171,7 +204,8 @@ public void unmarshallingHashicorpKeysGivesCorrectKeyPair() { @Test public void unmarshallingPrivateOnlyGivesUnsupportedKeyPair() { - final KeyData input = new KeyData(null, "private", null, null, null, null, null, null, null, null); + final KeyData input = new KeyData(); + input.setPrivateKey("private"); final ConfigKeyPair result = this.adapter.unmarshal(input); assertThat(result).isInstanceOf(UnsupportedKeyPair.class); @@ -180,7 +214,8 @@ public void unmarshallingPrivateOnlyGivesUnsupportedKeyPair() { @Test public void unmarshallingPrivateConfigOnlyGivesUnsupportedKeyPair() { final KeyDataConfig keyDataConfig = mock(KeyDataConfig.class); - final KeyData input = new KeyData(keyDataConfig, null, null, null, null, null, null, null, null, null); + final KeyData input = new KeyData(); + input.setConfig(keyDataConfig); final ConfigKeyPair result = this.adapter.unmarshal(input); assertThat(result).isInstanceOf(UnsupportedKeyPair.class); @@ -188,7 +223,8 @@ public void unmarshallingPrivateConfigOnlyGivesUnsupportedKeyPair() { @Test public void unmarshallingAzurePublicOnlyGivesUnsupportedKeyPair() { - final KeyData input = new KeyData(null, null, null, null, null, null, "pubId", null, null, null); + final KeyData input = new KeyData(); + input.setAzureVaultPublicKeyId("pubId"); final ConfigKeyPair result = this.adapter.unmarshal(input); assertThat(result).isInstanceOf(UnsupportedKeyPair.class); @@ -196,7 +232,8 @@ public void unmarshallingAzurePublicOnlyGivesUnsupportedKeyPair() { @Test public void unmarshallingAzurePrivateOnlyGivesUnsupportedKeyPair() { - final KeyData input = new KeyData(null, null, null, null, null, "priv", null, null, null, null); + final KeyData input = new KeyData(); + input.setAzureVaultPrivateKeyId("privId"); final ConfigKeyPair result = this.adapter.unmarshal(input); assertThat(result).isInstanceOf(UnsupportedKeyPair.class); @@ -204,7 +241,8 @@ public void unmarshallingAzurePrivateOnlyGivesUnsupportedKeyPair() { @Test public void unmarshallingHashicorpPublicOnlyGivesUnsupprtedKeyPair() { - final KeyData input = new KeyData(null, null, null, null, null, null, null, null, "pubId", null); + final KeyData input = new KeyData(); + input.setHashicorpVaultPublicKeyId("pubId"); final ConfigKeyPair result = this.adapter.unmarshal(input); assertThat(result).isInstanceOf(UnsupportedKeyPair.class); @@ -212,15 +250,26 @@ public void unmarshallingHashicorpPublicOnlyGivesUnsupprtedKeyPair() { @Test public void unmarshallingHashicorpPrivateOnlyGivesUnsupprtedKeyPair() { - final KeyData input = new KeyData(null, null, null, null, null, null, null, "privId", null, null); + final KeyData input = new KeyData(); + input.setHashicorpVaultPrivateKeyId("privId"); + + final ConfigKeyPair result = this.adapter.unmarshal(input); + assertThat(result).isInstanceOf(UnsupportedKeyPair.class); + } + + @Test + public void unmarshallingHashicorpSecretEngineNameOnlyGivesUnsupprtedKeyPair() { + final KeyData input = new KeyData(); + input.setHashicorpVaultSecretEngineName("secretEngine"); final ConfigKeyPair result = this.adapter.unmarshal(input); assertThat(result).isInstanceOf(UnsupportedKeyPair.class); } @Test - public void unmarshallingHashicorpSecretPathOnlyGivesUnsupprtedKeyPair() { - final KeyData input = new KeyData(null, null, null, null, null, null, null, null, null, "secretPath"); + public void unmarshallingHashicorpSecretNameOnlyGivesUnsupprtedKeyPair() { + final KeyData input = new KeyData(); + input.setHashicorpVaultSecretName("secretName"); final ConfigKeyPair result = this.adapter.unmarshal(input); assertThat(result).isInstanceOf(UnsupportedKeyPair.class); @@ -229,7 +278,8 @@ public void unmarshallingHashicorpSecretPathOnlyGivesUnsupprtedKeyPair() { @Test public void unmarshallingPublicPathOnlyGivesUnsupportedKeyPair() { final Path path = mock(Path.class); - final KeyData input = new KeyData(null, null, null, null, path, null, null, null, null, null); + final KeyData input = new KeyData(); + input.setPublicKeyPath(path); final ConfigKeyPair result = this.adapter.unmarshal(input); assertThat(result).isInstanceOf(UnsupportedKeyPair.class); @@ -238,7 +288,8 @@ public void unmarshallingPublicPathOnlyGivesUnsupportedKeyPair() { @Test public void unmarshallingPrivatePathOnlyGivesUnsupportedKeyPair() { final Path path = mock(Path.class); - final KeyData input = new KeyData(null, null, null, path, null, null, null, null, null, null); + final KeyData input = new KeyData(); + input.setPrivateKeyPath(path); final ConfigKeyPair result = this.adapter.unmarshal(input); assertThat(result).isInstanceOf(UnsupportedKeyPair.class); diff --git a/config/src/test/java/com/quorum/tessera/config/constraints/UnsupportedKeyPairValidatorTest.java b/config/src/test/java/com/quorum/tessera/config/constraints/UnsupportedKeyPairValidatorTest.java index 89656b04b5..21c56fbe6f 100644 --- a/config/src/test/java/com/quorum/tessera/config/constraints/UnsupportedKeyPairValidatorTest.java +++ b/config/src/test/java/com/quorum/tessera/config/constraints/UnsupportedKeyPairValidatorTest.java @@ -16,6 +16,9 @@ public class UnsupportedKeyPairValidatorTest { private ValidUnsupportedKeyPair validUnsupportedKeyPair; private ConstraintValidatorContext context; + private UnsupportedKeyPair keyPair; + + @Before public void setUp() { this.validator = new UnsupportedKeyPairValidator(); @@ -27,11 +30,13 @@ public void setUp() { ConstraintValidatorContext.ConstraintViolationBuilder builder = mock(ConstraintValidatorContext.ConstraintViolationBuilder.class); when(context.buildConstraintViolationWithTemplate(any(String.class))).thenReturn(builder); + + this.keyPair = new UnsupportedKeyPair(); } @Test public void directViolationIfPublicKeyButNoPrivateKey() { - UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, "public", null, null, null, null, null, null, null); + keyPair.setPublicKey("public"); validator.isValid(keyPair, context); @@ -40,7 +45,7 @@ public void directViolationIfPublicKeyButNoPrivateKey() { @Test public void directViolationIfNoPublicKeyButPrivateKey() { - UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, "private", null, null, null, null, null, null, null, null); + keyPair.setPrivateKey("private"); validator.isValid(keyPair, context); @@ -52,7 +57,11 @@ public void directViolationIsDefaultIfNoDirectPublicEvenIfMultipleIncompleteKeyP KeyDataConfig keyDataConfig = mock(KeyDataConfig.class); Path path = mock(Path.class); - UnsupportedKeyPair keyPair = new UnsupportedKeyPair(keyDataConfig, "private", null, path, null, null, "privVault", null, null, null); + keyPair.setPrivateKey("private"); + keyPair.setConfig(keyDataConfig); + keyPair.setPrivateKeyPath(path); + keyPair.setAzureVaultPrivateKeyId("privAzure"); + keyPair.setHashicorpVaultPrivateKeyId("privHashicorp"); validator.isValid(keyPair, context); @@ -64,7 +73,11 @@ public void directViolationIsDefaultIfNoDirectPrivateEvenIfMultipleIncompleteKey KeyDataConfig keyDataConfig = mock(KeyDataConfig.class); Path path = mock(Path.class); - UnsupportedKeyPair keyPair = new UnsupportedKeyPair(keyDataConfig, null, "public", null, path, "pubVault", null, null, null, null); + keyPair.setConfig(keyDataConfig); + keyPair.setPublicKey("public"); + keyPair.setPublicKeyPath(path); + keyPair.setAzureVaultPublicKeyId("pubAzure"); + keyPair.setHashicorpVaultPublicKeyId("pubHashicorp"); validator.isValid(keyPair, context); @@ -74,7 +87,8 @@ public void directViolationIsDefaultIfNoDirectPrivateEvenIfMultipleIncompleteKey @Test public void inlineViolationIfPrivateKeyConfigButNoPublicKey() { KeyDataConfig keyDataConfig = mock(KeyDataConfig.class); - UnsupportedKeyPair keyPair = new UnsupportedKeyPair(keyDataConfig, null, null, null, null, null, null, null, null, null); + + keyPair.setConfig(keyDataConfig); validator.isValid(keyPair, context); @@ -86,7 +100,10 @@ public void inlineViolationIfNoPublicEvenIfVaultAndFilesystemAreIncomplete() { KeyDataConfig keyDataConfig = mock(KeyDataConfig.class); Path path = mock(Path.class); - UnsupportedKeyPair keyPair = new UnsupportedKeyPair(keyDataConfig, null, null, null, path, "pubId", null, null, null, null); + keyPair.setConfig(keyDataConfig); + keyPair.setPublicKeyPath(path); + keyPair.setAzureVaultPublicKeyId("pubId"); + keyPair.setHashicorpVaultPublicKeyId("pubId"); validator.isValid(keyPair, context); @@ -95,7 +112,7 @@ public void inlineViolationIfNoPublicEvenIfVaultAndFilesystemAreIncomplete() { @Test public void azureViolationIfPublicIdButNoPrivateId() { - UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, null, null, null, "pubId", null, null, null, null); + keyPair.setAzureVaultPublicKeyId("pubId"); validator.isValid(keyPair, context); @@ -104,7 +121,7 @@ public void azureViolationIfPublicIdButNoPrivateId() { @Test public void azureViolationIfNoPublicIdButPrivateId() { - UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, null, null, null, null, "privId", null, null, null); + keyPair.setAzureVaultPrivateKeyId("privId"); validator.isValid(keyPair, context); @@ -115,7 +132,8 @@ public void azureViolationIfNoPublicIdButPrivateId() { public void azureViolationIfNoPublicIdEvenIfFilesystemIncomplete() { Path path = mock(Path.class); - UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, null, null, path, null, "privId", null, null, null); + keyPair.setPublicKeyPath(path); + keyPair.setAzureVaultPrivateKeyId("privId"); validator.isValid(keyPair, context); @@ -123,8 +141,26 @@ public void azureViolationIfNoPublicIdEvenIfFilesystemIncomplete() { } @Test - public void hashicorpViolationIfPublicIdButNoPrivateIdOrSecretPath() { - UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, null, null, null, null, null, "pubId", null, null); + public void hashicorpViolationIfPublicIdOnly() { + keyPair.setHashicorpVaultPublicKeyId("pubId"); + + validator.isValid(keyPair, context); + + verify(context).buildConstraintViolationWithTemplate("{UnsupportedKeyPair.allHashicorpKeyDataRequired.message}"); + } + + @Test + public void hashicorpViolationIfPrivateIdOnly() { + keyPair.setHashicorpVaultPrivateKeyId("privId"); + + validator.isValid(keyPair, context); + + verify(context).buildConstraintViolationWithTemplate("{UnsupportedKeyPair.allHashicorpKeyDataRequired.message}"); + } + + @Test + public void hashicorpViolationIfSecretEngineNameOnly() { + keyPair.setHashicorpVaultSecretEngineName("secretEngineName"); validator.isValid(keyPair, context); @@ -132,8 +168,8 @@ public void hashicorpViolationIfPublicIdButNoPrivateIdOrSecretPath() { } @Test - public void hashicorpViolationIfPrivateIdButNoPublicIdOrSecretPath() { - UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, null, null, null, null, null, null, "privId", null); + public void hashicorpViolationIfSecretNameOnly() { + keyPair.setHashicorpVaultSecretName("secretName"); validator.isValid(keyPair, context); @@ -141,8 +177,9 @@ public void hashicorpViolationIfPrivateIdButNoPublicIdOrSecretPath() { } @Test - public void hashicorpViolationIfSecretPathButNoPublicIdOrPrivateId() { - UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, null, null, null, null, null, null, null, "secretPath"); + public void hashicorpViolationIfPublicIdAndPrivateIdOnly() { + keyPair.setHashicorpVaultPublicKeyId("pubId"); + keyPair.setHashicorpVaultPrivateKeyId("privId"); validator.isValid(keyPair, context); @@ -150,8 +187,9 @@ public void hashicorpViolationIfSecretPathButNoPublicIdOrPrivateId() { } @Test - public void hashicorpViolationIfPublicIdAndPrivateIdButNoSecretPath() { - UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, null, null, null, null, null, "pubId", "privId", null); + public void hashicorpViolationIfPublicIdAndSecretEngineNameOnly() { + keyPair.setHashicorpVaultPublicKeyId("pubId"); + keyPair.setHashicorpVaultSecretEngineName("secretEngine"); validator.isValid(keyPair, context); @@ -159,8 +197,9 @@ public void hashicorpViolationIfPublicIdAndPrivateIdButNoSecretPath() { } @Test - public void hashicorpViolationIfPublicIdAndSecretPathButNoPrivateId() { - UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, null, null, null, null, null, "pubId", null, "secretPath"); + public void hashicorpViolationIfPublicIdAndSecretNameOnly() { + keyPair.setHashicorpVaultPublicKeyId("pubId"); + keyPair.setHashicorpVaultSecretName("secretName"); validator.isValid(keyPair, context); @@ -168,21 +207,85 @@ public void hashicorpViolationIfPublicIdAndSecretPathButNoPrivateId() { } @Test - public void hashicorpViolationIfPrivateIdAndSecretPathButNoPublicId() { - UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, null, null, null, null, null, null, "privId", "secretPath"); + public void hashicorpViolationIfPrivateIdAndSecretEngineNameOnly() { + keyPair.setHashicorpVaultPrivateKeyId("privId"); + keyPair.setHashicorpVaultSecretEngineName("secretEngine"); validator.isValid(keyPair, context); verify(context).buildConstraintViolationWithTemplate("{UnsupportedKeyPair.allHashicorpKeyDataRequired.message}"); } + @Test + public void hashicorpViolationIfPrivateIdAndSecretNameOnly() { + keyPair.setHashicorpVaultPrivateKeyId("privId"); + keyPair.setHashicorpVaultSecretName("secretName"); + + validator.isValid(keyPair, context); + verify(context).buildConstraintViolationWithTemplate("{UnsupportedKeyPair.allHashicorpKeyDataRequired.message}"); + } + + @Test + public void hashicorpViolationIfSecretEngineNameAndSecretNameOnly() { + keyPair.setHashicorpVaultSecretEngineName("secretEngine"); + keyPair.setHashicorpVaultSecretName("secretName"); + + validator.isValid(keyPair, context); + + verify(context).buildConstraintViolationWithTemplate("{UnsupportedKeyPair.allHashicorpKeyDataRequired.message}"); + } + + @Test + public void hashicorpViolationIfPublicIdAndPrivateIdAndSecretEngineNameOnly() { + keyPair.setHashicorpVaultPublicKeyId("pubId"); + keyPair.setHashicorpVaultPrivateKeyId("privId"); + keyPair.setHashicorpVaultSecretEngineName("secretEngine"); + + validator.isValid(keyPair, context); + + verify(context).buildConstraintViolationWithTemplate("{UnsupportedKeyPair.allHashicorpKeyDataRequired.message}"); + } + + @Test + public void hashicorpViolationIfPublicIdAndPrivateIdAndSecretNameOnly() { + keyPair.setHashicorpVaultPublicKeyId("pubId"); + keyPair.setHashicorpVaultPrivateKeyId("privId"); + keyPair.setHashicorpVaultSecretName("secretName"); + + validator.isValid(keyPair, context); + + verify(context).buildConstraintViolationWithTemplate("{UnsupportedKeyPair.allHashicorpKeyDataRequired.message}"); + } + + @Test + public void hashicorpViolationIfPublicIdAndSecretEngineNameAndSecretNameOnly() { + keyPair.setHashicorpVaultPublicKeyId("pubId"); + keyPair.setHashicorpVaultSecretEngineName("secretEngine"); + keyPair.setHashicorpVaultSecretName("secretName"); + + validator.isValid(keyPair, context); + + verify(context).buildConstraintViolationWithTemplate("{UnsupportedKeyPair.allHashicorpKeyDataRequired.message}"); + } + + @Test + public void hashicorpViolationIfPrivateIdAndSecretEngineNameAndSecretNameOnly() { + keyPair.setHashicorpVaultPrivateKeyId("privId"); + keyPair.setHashicorpVaultSecretEngineName("secretEngine"); + keyPair.setHashicorpVaultSecretName("secretName"); + + validator.isValid(keyPair, context); + + verify(context).buildConstraintViolationWithTemplate("{UnsupportedKeyPair.allHashicorpKeyDataRequired.message}"); + } @Test public void azureViolationIfNoPrivateIdEvenIfFilesystemIncomplete() { Path path = mock(Path.class); - UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, null, null, path, "pubId", null, null, null, null); + keyPair.setAzureVaultPublicKeyId("pubId"); + keyPair.setPublicKeyPath(path); validator.isValid(keyPair, context); @@ -193,7 +296,7 @@ public void azureViolationIfNoPrivateIdEvenIfFilesystemIncomplete() { public void filesystemViolationIfPublicPathButNoPrivatePath() { Path path = mock(Path.class); - UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, null, null, path, null, null, null, null, null); + keyPair.setPublicKeyPath(path); validator.isValid(keyPair, context); @@ -204,7 +307,7 @@ public void filesystemViolationIfPublicPathButNoPrivatePath() { public void filesystemViolationIfNoPublicPathButPrivatePath() { Path path = mock(Path.class); - UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, null, path, null, null, null, null, null, null); + keyPair.setPrivateKeyPath(path); validator.isValid(keyPair, context); @@ -213,8 +316,7 @@ public void filesystemViolationIfNoPublicPathButPrivatePath() { @Test public void defaultViolationIfNoRecognisedKeyPairDataProvided() { - UnsupportedKeyPair keyPair = new UnsupportedKeyPair(null, null, null, null, null, null, null, null, null, null); - + //nothing set validator.isValid(keyPair, context); verifyNoMoreInteractions(context); diff --git a/config/src/test/java/com/quorum/tessera/config/keypairs/HashicorpVaultKeyPairTest.java b/config/src/test/java/com/quorum/tessera/config/keypairs/HashicorpVaultKeyPairTest.java index d45ad1e944..600faa10f5 100644 --- a/config/src/test/java/com/quorum/tessera/config/keypairs/HashicorpVaultKeyPairTest.java +++ b/config/src/test/java/com/quorum/tessera/config/keypairs/HashicorpVaultKeyPairTest.java @@ -11,14 +11,15 @@ public class HashicorpVaultKeyPairTest { @Before public void setUp() { - keyPair = new HashicorpVaultKeyPair("pubId", "privId", "secretPath"); + keyPair = new HashicorpVaultKeyPair("pubId", "privId", "secretEngine", "secretName"); } @Test public void getters() { assertThat(keyPair.getPublicKeyId()).isEqualTo("pubId"); assertThat(keyPair.getPrivateKeyId()).isEqualTo("privId"); - assertThat(keyPair.getSecretPath()).isEqualTo("secretPath"); + assertThat(keyPair.getSecretEngineName()).isEqualTo("secretEngine"); + assertThat(keyPair.getSecretName()).isEqualTo("secretName"); assertThat(keyPair.getPublicKey()).isEqualTo(null); assertThat(keyPair.getPrivateKey()).isEqualTo(null); assertThat(keyPair.getPassword()).isEqualTo(""); diff --git a/config/src/test/java/com/quorum/tessera/config/keypairs/UnsupportedKeyPairTest.java b/config/src/test/java/com/quorum/tessera/config/keypairs/UnsupportedKeyPairTest.java index 66b22c8ef5..7a094b9da4 100644 --- a/config/src/test/java/com/quorum/tessera/config/keypairs/UnsupportedKeyPairTest.java +++ b/config/src/test/java/com/quorum/tessera/config/keypairs/UnsupportedKeyPairTest.java @@ -11,7 +11,7 @@ public class UnsupportedKeyPairTest { @Before public void setUp() { - this.keyPair = new UnsupportedKeyPair(null, null, null, null, null, null, null, null, null, null); + this.keyPair = new UnsupportedKeyPair(null, null, null, null, null, null, null, null, null, null, null); } @Test From 9d66a0d429729dcf4e160d1da5591753098fa262 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Wed, 12 Dec 2018 17:43:49 +0000 Subject: [PATCH 45/57] [WIP] Change client library and update ServiceFactory --- .../util/EnvironmentVariableProvider.java | 4 + .../azure/AzureKeyVaultServiceFactory.java | 3 +- key-vault/hashicorp-key-vault/pom.xml | 11 + .../HashicorpKeyVaultClientFactory.java | 65 ------ .../HashicorpKeyVaultServiceFactory.java | 62 +++--- .../HashicorpKeyVaultServiceFactoryUtil.java | 97 ++++++++ .../HashicorpKeyVaultClientFactoryTest.java | 192 ---------------- .../HashicorpKeyVaultServiceFactoryTest.java | 185 ++++++---------- ...shicorpKeyVaultServiceFactoryUtilTest.java | 208 ++++++++++++++++++ .../key/vault/KeyVaultServiceFactory.java | 2 +- .../MockAzureKeyVaultServiceFactory.java | 2 +- pom.xml | 2 +- 12 files changed, 430 insertions(+), 403 deletions(-) delete mode 100644 key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactory.java create mode 100644 key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryUtil.java delete mode 100644 key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactoryTest.java create mode 100644 key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryUtilTest.java diff --git a/config/src/main/java/com/quorum/tessera/config/util/EnvironmentVariableProvider.java b/config/src/main/java/com/quorum/tessera/config/util/EnvironmentVariableProvider.java index bd90561327..7641be5928 100644 --- a/config/src/main/java/com/quorum/tessera/config/util/EnvironmentVariableProvider.java +++ b/config/src/main/java/com/quorum/tessera/config/util/EnvironmentVariableProvider.java @@ -7,4 +7,8 @@ public String getEnv(String name) { return System.getenv(name); } + public char[] getEnvAsCharArray(String name) { + return System.getenv(name).toCharArray(); + } + } diff --git a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactory.java b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactory.java index 5f6332effd..82722e8e41 100644 --- a/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactory.java +++ b/key-vault/azure-key-vault/src/main/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactory.java @@ -2,7 +2,6 @@ import com.quorum.tessera.config.*; import com.quorum.tessera.config.util.EnvironmentVariableProvider; -import com.quorum.tessera.key.vault.KeyVaultClientFactory; import com.quorum.tessera.key.vault.KeyVaultService; import com.quorum.tessera.key.vault.KeyVaultServiceFactory; @@ -16,7 +15,7 @@ public class AzureKeyVaultServiceFactory implements KeyVaultServiceFactory { private static final String clientSecretEnvVar = "AZURE_CLIENT_SECRET"; @Override - public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, KeyVaultClientFactory keyVaultClientFactory) { + public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider) { Objects.requireNonNull(config); Objects.requireNonNull(envProvider); diff --git a/key-vault/hashicorp-key-vault/pom.xml b/key-vault/hashicorp-key-vault/pom.xml index bb33541253..a6fcdd0b9e 100644 --- a/key-vault/hashicorp-key-vault/pom.xml +++ b/key-vault/hashicorp-key-vault/pom.xml @@ -23,6 +23,17 @@ key-vault-api + + org.springframework.vault + spring-vault-core + 2.1.1.RELEASE + + + + io.netty + netty-all + + \ No newline at end of file diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactory.java deleted file mode 100644 index 5c3ec15e9d..0000000000 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactory.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.quorum.tessera.key.vault.hashicorp; - -import com.bettercloud.vault.SslConfig; -import com.bettercloud.vault.Vault; -import com.bettercloud.vault.VaultConfig; -import com.quorum.tessera.config.HashicorpKeyVaultConfig; -import com.quorum.tessera.config.KeyVaultType; -import com.quorum.tessera.key.vault.KeyVaultClientFactory; - -public class HashicorpKeyVaultClientFactory implements KeyVaultClientFactory { - - Vault createUnauthenticatedClient(HashicorpKeyVaultConfig keyVaultConfig, - VaultConfigFactory vaultConfigFactory, - SslConfigFactory sslConfigFactory) { - VaultConfig vaultConfig = createBaseVaultConfig(keyVaultConfig, vaultConfigFactory, sslConfigFactory); - - VaultCallback.execute(vaultConfig::build); - - return new Vault(vaultConfig); - } - - Vault createAuthenticatedClient(HashicorpKeyVaultConfig keyVaultConfig, - VaultConfigFactory vaultConfigFactory, - SslConfigFactory sslConfigFactory, - String authToken) { - VaultConfig vaultConfig = createBaseVaultConfig(keyVaultConfig, vaultConfigFactory, sslConfigFactory); - - vaultConfig.token(authToken); - - VaultCallback.execute(vaultConfig::build); - - return new Vault(vaultConfig); - } - - private VaultConfig createBaseVaultConfig(HashicorpKeyVaultConfig keyVaultConfig, VaultConfigFactory vaultConfigFactory, SslConfigFactory sslConfigFactory) { - VaultConfig vaultConfig = vaultConfigFactory.create() - .address(keyVaultConfig.getUrl()); - - if (keyVaultConfig.getTlsServerCertificatePath() != null) { - SslConfig sslConfig = sslConfigFactory.create(); - - VaultCallback.execute( - () -> sslConfig.pemFile(keyVaultConfig.getTlsServerCertificatePath().toFile()) - ); - - if(keyVaultConfig.getTlsCertificatePath() != null && keyVaultConfig.getTlsKeyPath() != null) { - VaultCallback.execute( - () -> sslConfig.clientPemFile(keyVaultConfig.getTlsCertificatePath().toFile()) - .clientKeyPemFile(keyVaultConfig.getTlsKeyPath().toFile()) - ); - } - - VaultCallback.execute(sslConfig::build); - - vaultConfig.sslConfig(sslConfig); - } - - return vaultConfig; - } - - @Override - public KeyVaultType getType() { - return KeyVaultType.HASHICORP; - } -} diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java index 1227306d2a..904ac58e57 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java @@ -1,14 +1,21 @@ package com.quorum.tessera.key.vault.hashicorp; -import com.bettercloud.vault.Vault; -import com.bettercloud.vault.VaultException; -import com.bettercloud.vault.response.AuthResponse; import com.quorum.tessera.config.*; import com.quorum.tessera.config.util.EnvironmentVariableProvider; -import com.quorum.tessera.key.vault.KeyVaultClientFactory; import com.quorum.tessera.key.vault.KeyVaultService; import com.quorum.tessera.key.vault.KeyVaultServiceFactory; - +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.vault.authentication.ClientAuthentication; +import org.springframework.vault.authentication.SessionManager; +import org.springframework.vault.authentication.SimpleSessionManager; +import org.springframework.vault.client.VaultEndpoint; +import org.springframework.vault.core.VaultOperations; +import org.springframework.vault.core.VaultTemplate; +import org.springframework.vault.support.ClientOptions; +import org.springframework.vault.support.SslConfiguration; + +import java.net.URI; +import java.net.URISyntaxException; import java.util.Objects; import java.util.Optional; @@ -19,10 +26,20 @@ public class HashicorpKeyVaultServiceFactory implements KeyVaultServiceFactory { private static final String authTokenEnvVar = "HASHICORP_TOKEN"; @Override - public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, KeyVaultClientFactory keyVaultClientFactory) { + public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider) { + Objects.requireNonNull(config); + Objects.requireNonNull(envProvider); + + HashicorpKeyVaultServiceFactoryUtil util = new HashicorpKeyVaultServiceFactoryUtil(roleIdEnvVar, secretIdEnvVar, authTokenEnvVar); + + return this.create(config, envProvider, util); + } + + //This method should not be called directly. It has been left public to enable injection of util during testing + public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, HashicorpKeyVaultServiceFactoryUtil util) { Objects.requireNonNull(config); Objects.requireNonNull(envProvider); - Objects.requireNonNull(keyVaultClientFactory); + Objects.requireNonNull(util); final String roleId = envProvider.getEnv(roleIdEnvVar); final String secretId = envProvider.getEnv(secretIdEnvVar); @@ -39,31 +56,26 @@ else if(isOnlyOneInputNull(roleId, secretId)) { .map(KeyConfiguration::getHashicorpKeyVaultConfig) .orElseThrow(() -> new ConfigException(new RuntimeException("Trying to create Hashicorp Vault connection but no Vault configuration provided"))); - if(!(keyVaultClientFactory instanceof HashicorpKeyVaultClientFactory)) { - throw new HashicorpVaultException("Incorrect KeyVaultClientFactoryType passed to HashicorpKeyVaultServiceFactory"); - } + VaultEndpoint vaultEndpoint; - HashicorpKeyVaultClientFactory hashicorpClientFactory = (HashicorpKeyVaultClientFactory) keyVaultClientFactory; + try { + vaultEndpoint = VaultEndpoint.from(new URI(keyVaultConfig.getUrl())); + } catch (URISyntaxException | IllegalArgumentException e) { + throw new RuntimeException("Provided Hashicorp Vault url is incorrectly formatted", e); + } - final Vault unauthenticatedVault = hashicorpClientFactory.createUnauthenticatedClient(keyVaultConfig, new VaultConfigFactory(), new SslConfigFactory()); + SslConfiguration sslConfiguration = util.configureSsl(keyVaultConfig, envProvider); - String token; + ClientOptions clientOptions = new ClientOptions(); - if(roleId != null && secretId != null) { - try { - AuthResponse loginResponse = unauthenticatedVault.auth().loginByAppRole(keyVaultConfig.getApprolePath(), roleId, secretId); - token = loginResponse.getAuthClientToken(); - } catch (VaultException e) { - throw new HashicorpVaultException("Unable to authenticate using AppRole - " + e.getMessage()); - } + ClientHttpRequestFactory clientHttpRequestFactory = util.createClientHttpRequestFactory(clientOptions, sslConfiguration); - } else { - token = authToken; - } + ClientAuthentication clientAuthentication = util.configureClientAuthentication(keyVaultConfig, envProvider, clientHttpRequestFactory, vaultEndpoint); - final Vault authenticatedVault = hashicorpClientFactory.createAuthenticatedClient(keyVaultConfig, new VaultConfigFactory(), new SslConfigFactory(), token); + SessionManager sessionManager = new SimpleSessionManager(clientAuthentication); + VaultOperations vaultOperations = new VaultTemplate(vaultEndpoint, clientHttpRequestFactory, sessionManager); - return new HashicorpKeyVaultService(authenticatedVault); + return new HashicorpKeyVaultService(vaultOperations); } @Override diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryUtil.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryUtil.java new file mode 100644 index 0000000000..16d9eeb1cf --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryUtil.java @@ -0,0 +1,97 @@ +package com.quorum.tessera.key.vault.hashicorp; + +import com.quorum.tessera.config.HashicorpKeyVaultConfig; +import com.quorum.tessera.config.util.EnvironmentVariableProvider; +import org.springframework.core.io.FileSystemResource; +import org.springframework.core.io.Resource; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.vault.authentication.AppRoleAuthentication; +import org.springframework.vault.authentication.AppRoleAuthenticationOptions; +import org.springframework.vault.authentication.ClientAuthentication; +import org.springframework.vault.authentication.TokenAuthentication; +import org.springframework.vault.client.VaultClients; +import org.springframework.vault.client.VaultEndpoint; +import org.springframework.vault.config.ClientHttpRequestFactoryFactory; +import org.springframework.vault.support.ClientOptions; +import org.springframework.vault.support.SslConfiguration; +import org.springframework.web.client.RestOperations; + +import java.util.Objects; + +class HashicorpKeyVaultServiceFactoryUtil { + + private final String roleIdEnvVar; + private final String secretIdEnvVar; + private final String authTokenEnvVar; + private static final String keyStorePwdEnvVar = "HASHICORP_CLIENT_KEYSTORE_PWD"; + private static final String trustStorePwdEnvVar = "HASHICORP_CLIENT_TRUSTSTORE_PWD"; + + HashicorpKeyVaultServiceFactoryUtil(String roleIdEnvVar, String secretIdEnvVar, String authTokenEnvVar) { + this.roleIdEnvVar = roleIdEnvVar; + this.secretIdEnvVar = secretIdEnvVar; + this.authTokenEnvVar = authTokenEnvVar; + } + + SslConfiguration configureSsl(HashicorpKeyVaultConfig keyVaultConfig, EnvironmentVariableProvider envProvider) { + if(keyVaultConfig.getTlsKeyStorePath() != null && keyVaultConfig.getTlsTrustStorePath() != null) { + + Resource clientKeyStore = new FileSystemResource(keyVaultConfig.getTlsKeyStorePath().toFile()); + Resource clientTrustStore = new FileSystemResource(keyVaultConfig.getTlsTrustStorePath().toFile()); + + SslConfiguration.KeyStoreConfiguration keyStoreConfiguration = SslConfiguration.KeyStoreConfiguration.of( + clientKeyStore, + envProvider.getEnvAsCharArray(keyStorePwdEnvVar) + ); + + SslConfiguration.KeyStoreConfiguration trustStoreConfiguration = SslConfiguration.KeyStoreConfiguration.of( + clientTrustStore, + envProvider.getEnvAsCharArray(trustStorePwdEnvVar) + ); + + return new SslConfiguration(keyStoreConfiguration, trustStoreConfiguration); + + } else if (keyVaultConfig.getTlsTrustStorePath() != null) { + + Resource clientTrustStore = new FileSystemResource(keyVaultConfig.getTlsTrustStorePath().toFile()); + + return SslConfiguration.forTrustStore(clientTrustStore, envProvider.getEnvAsCharArray(trustStorePwdEnvVar)); + + } else { + return SslConfiguration.unconfigured(); + } + } + + ClientHttpRequestFactory createClientHttpRequestFactory(ClientOptions clientOptions, SslConfiguration sslConfiguration) { + return ClientHttpRequestFactoryFactory.create(clientOptions, sslConfiguration); + } + + ClientAuthentication configureClientAuthentication(HashicorpKeyVaultConfig keyVaultConfig, EnvironmentVariableProvider envProvider, ClientHttpRequestFactory clientHttpRequestFactory, VaultEndpoint vaultEndpoint) { + + final String roleId = envProvider.getEnv(roleIdEnvVar); + final String secretId = envProvider.getEnv(secretIdEnvVar); + final String authToken = envProvider.getEnv(authTokenEnvVar); + + if(roleId != null && secretId != null) { + + AppRoleAuthenticationOptions appRoleAuthenticationOptions = AppRoleAuthenticationOptions.builder() + .path(keyVaultConfig.getApprolePath()) + .roleId(AppRoleAuthenticationOptions.RoleId.provided(roleId)) + .secretId(AppRoleAuthenticationOptions.SecretId.provided(secretId)) + .build(); + + RestOperations restOperations = VaultClients.createRestTemplate(vaultEndpoint, clientHttpRequestFactory); + + return new AppRoleAuthentication(appRoleAuthenticationOptions, restOperations); + + } else if (Objects.isNull(roleId) != Objects.isNull(secretId)) { + + throw new RuntimeException("Both " + roleIdEnvVar + " and " + secretIdEnvVar + " environment variables must be set to use the AppRole authentication method"); + + } else if (authToken == null){ + + throw new RuntimeException("Both " + roleIdEnvVar + " and " + secretIdEnvVar + " environment variables must be set to use the AppRole authentication method. Alternatively set " + authTokenEnvVar + " to authenticate using the Token method"); + } + + return new TokenAuthentication(authToken); + } +} diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactoryTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactoryTest.java deleted file mode 100644 index f9a2a18f48..0000000000 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultClientFactoryTest.java +++ /dev/null @@ -1,192 +0,0 @@ -package com.quorum.tessera.key.vault.hashicorp; - -import com.bettercloud.vault.SslConfig; -import com.bettercloud.vault.VaultConfig; -import com.quorum.tessera.config.HashicorpKeyVaultConfig; -import com.quorum.tessera.config.KeyVaultType; -import org.junit.Before; -import org.junit.Test; - -import java.io.File; -import java.nio.file.Path; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.*; - -public class HashicorpKeyVaultClientFactoryTest { - - private HashicorpKeyVaultClientFactory keyVaultClientFactory; - - private HashicorpKeyVaultConfig keyVaultConfig; - - private VaultConfigFactory vaultConfigFactory; - - private SslConfigFactory sslConfigFactory; - - @Before - public void setUp() { - this.keyVaultClientFactory = new HashicorpKeyVaultClientFactory(); - this.keyVaultConfig = mock(HashicorpKeyVaultConfig.class); - when(keyVaultConfig.getUrl()).thenReturn("url"); - this.vaultConfigFactory = mock(VaultConfigFactory.class, RETURNS_DEEP_STUBS); - this.sslConfigFactory = mock(SslConfigFactory.class); - } - - @Test - public void tlsConfigAddedToUnauthenticatedVaultClientIfProvided() throws Exception { - Path tlsPath = mock(Path.class); - when(keyVaultConfig.getTlsCertificatePath()).thenReturn(tlsPath); - when(keyVaultConfig.getTlsKeyPath()).thenReturn(tlsPath); - when(keyVaultConfig.getTlsServerCertificatePath()).thenReturn(tlsPath); - File certFile = mock(File.class); - when(tlsPath.toFile()).thenReturn(certFile); - - VaultConfig vaultConfig = mock(VaultConfig.class); - when(vaultConfigFactory.create().address(anyString())).thenReturn(vaultConfig); - when(vaultConfig.build()).thenReturn(vaultConfig); - - SslConfig sslConfig = mock(SslConfig.class, RETURNS_DEEP_STUBS); - when(sslConfigFactory.create()).thenReturn(sslConfig); - - when( - sslConfig - .clientPemFile(any(File.class)) - .clientKeyPemFile(any(File.class)) - .pemFile(any(File.class)) - .build() - ).thenReturn(sslConfig); - - keyVaultClientFactory.createUnauthenticatedClient(keyVaultConfig, vaultConfigFactory, sslConfigFactory); - - verify(vaultConfig).sslConfig(sslConfig); - } - - @Test - public void tlsConfigNotAddedToUnauthenticatedVaultClientIfNotProvided() throws Exception { - when(keyVaultConfig.getTlsCertificatePath()).thenReturn(null); - - VaultConfig vaultConfig = mock(VaultConfig.class); - when(vaultConfigFactory.create().address(anyString())).thenReturn(vaultConfig); - when(vaultConfig.build()).thenReturn(vaultConfig); - - keyVaultClientFactory.createUnauthenticatedClient(keyVaultConfig, vaultConfigFactory, sslConfigFactory); - - verify(vaultConfig, never()).sslConfig(any(SslConfig.class)); - } - - @Test - public void tlsConfigAddedToAuthenticatedVaultClientIfProvided() throws Exception { - Path tlsPath = mock(Path.class); - when(keyVaultConfig.getTlsCertificatePath()).thenReturn(tlsPath); - when(keyVaultConfig.getTlsKeyPath()).thenReturn(tlsPath); - when(keyVaultConfig.getTlsServerCertificatePath()).thenReturn(tlsPath); - File certFile = mock(File.class); - when(tlsPath.toFile()).thenReturn(certFile); - - VaultConfig vaultConfig = mock(VaultConfig.class); - when(vaultConfigFactory.create().address(anyString())).thenReturn(vaultConfig); - when(vaultConfig.build()).thenReturn(vaultConfig); - - SslConfig sslConfig = mock(SslConfig.class, RETURNS_DEEP_STUBS); - when(sslConfigFactory.create()).thenReturn(sslConfig); - - when( - sslConfig - .clientPemFile(any(File.class)) - .clientKeyPemFile(any(File.class)) - .pemFile(any(File.class)) - .build() - ).thenReturn(sslConfig); - - keyVaultClientFactory.createAuthenticatedClient(keyVaultConfig, vaultConfigFactory, sslConfigFactory, "sometoken"); - - verify(vaultConfig).sslConfig(sslConfig); - } - - @Test - public void oneWayTlsSetUpIfOnlyServerCertProvidedInConfig() throws Exception { - Path tlsPath = mock(Path.class); - when(keyVaultConfig.getTlsCertificatePath()).thenReturn(null); - when(keyVaultConfig.getTlsKeyPath()).thenReturn(null); - when(keyVaultConfig.getTlsServerCertificatePath()).thenReturn(tlsPath); - File certFile = mock(File.class); - when(tlsPath.toFile()).thenReturn(certFile); - - VaultConfig vaultConfig = mock(VaultConfig.class); - when(vaultConfigFactory.create().address(anyString())).thenReturn(vaultConfig); - when(vaultConfig.build()).thenReturn(vaultConfig); - - SslConfig sslConfig = mock(SslConfig.class, RETURNS_DEEP_STUBS); - when(sslConfigFactory.create()).thenReturn(sslConfig); - - when(sslConfig.pemFile(any(File.class))).thenReturn(sslConfig); - when(sslConfig.build()).thenReturn(sslConfig); - - keyVaultClientFactory.createAuthenticatedClient(keyVaultConfig, vaultConfigFactory, sslConfigFactory, "sometoken"); - - verify(sslConfig).pemFile(any()); - verify(sslConfig, never()).clientPemFile(any()); - verify(sslConfig, never()).clientPemFile(any()); - verify(vaultConfig).sslConfig(sslConfig); - } - - @Test - public void twoWayTlsSetUpIfAllTlsOptionsProvidedInConfig() throws Exception { - Path tlsPath = mock(Path.class); - when(keyVaultConfig.getTlsCertificatePath()).thenReturn(tlsPath); - when(keyVaultConfig.getTlsKeyPath()).thenReturn(tlsPath); - when(keyVaultConfig.getTlsServerCertificatePath()).thenReturn(tlsPath); - File certFile = mock(File.class); - when(tlsPath.toFile()).thenReturn(certFile); - - VaultConfig vaultConfig = mock(VaultConfig.class); - when(vaultConfigFactory.create().address(anyString())).thenReturn(vaultConfig); - when(vaultConfig.build()).thenReturn(vaultConfig); - - SslConfig sslConfig = mock(SslConfig.class); - when(sslConfigFactory.create()).thenReturn(sslConfig); - - when(sslConfig.pemFile(any(File.class))).thenReturn(sslConfig); - when(sslConfig.clientPemFile(any(File.class))).thenReturn(sslConfig); - when(sslConfig.clientKeyPemFile(any(File.class))).thenReturn(sslConfig); - when(sslConfig.build()).thenReturn(sslConfig); - - keyVaultClientFactory.createAuthenticatedClient(keyVaultConfig, vaultConfigFactory, sslConfigFactory, "sometoken"); - - verify(sslConfig).pemFile(any()); - verify(sslConfig).clientPemFile(any()); - verify(sslConfig).clientPemFile(any()); - verify(vaultConfig).sslConfig(sslConfig); - } - - @Test - public void tlsConfigNotAddedToAuthenticatedVaultClientIfNotProvided() throws Exception { - when(keyVaultConfig.getTlsCertificatePath()).thenReturn(null); - - VaultConfig vaultConfig = mock(VaultConfig.class); - when(vaultConfigFactory.create().address(anyString())).thenReturn(vaultConfig); - when(vaultConfig.build()).thenReturn(vaultConfig); - - keyVaultClientFactory.createAuthenticatedClient(keyVaultConfig, vaultConfigFactory, sslConfigFactory, "sometoken"); - - verify(vaultConfig, never()).sslConfig(any(SslConfig.class)); - } - - @Test - public void tokenGetsAddedToAuthenticateVaultClientConfig() throws Exception { - VaultConfig vaultConfig = mock(VaultConfig.class); - when(vaultConfigFactory.create().address(anyString())).thenReturn(vaultConfig); - when(vaultConfig.build()).thenReturn(vaultConfig); - - keyVaultClientFactory.createAuthenticatedClient(keyVaultConfig, vaultConfigFactory, sslConfigFactory, "sometoken"); - - verify(vaultConfig).token("sometoken"); - } - - @Test - public void getType() { - assertThat(keyVaultClientFactory.getType()).isEqualTo(KeyVaultType.HASHICORP); - } - -} diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java index dd82f622f7..3d84d1bffb 100644 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java @@ -1,19 +1,19 @@ package com.quorum.tessera.key.vault.hashicorp; -import com.bettercloud.vault.Vault; -import com.bettercloud.vault.VaultException; -import com.bettercloud.vault.api.Auth; -import com.bettercloud.vault.response.AuthResponse; import com.quorum.tessera.config.*; import com.quorum.tessera.config.util.EnvironmentVariableProvider; -import com.quorum.tessera.key.vault.KeyVaultClientFactory; +import com.quorum.tessera.key.vault.KeyVaultService; import org.junit.Before; import org.junit.Test; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.vault.authentication.ClientAuthentication; +import org.springframework.vault.client.VaultEndpoint; +import org.springframework.vault.support.ClientOptions; +import org.springframework.vault.support.SslConfiguration; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.*; public class HashicorpKeyVaultServiceFactoryTest { @@ -24,7 +24,7 @@ public class HashicorpKeyVaultServiceFactoryTest { private EnvironmentVariableProvider envProvider; - private HashicorpKeyVaultClientFactory keyVaultClientFactory; + private HashicorpKeyVaultServiceFactoryUtil keyVaultServiceFactoryUtil; private String noCredentialsExceptionMsg = "Environment variables must be set to authenticate with Hashicorp Vault. Set the HASHICORP_ROLE_ID and HASHICORP_SECRET_ID environment variables if using the AppRole authentication method. Set the HASHICORP_TOKEN environment variable if using another authentication method."; @@ -35,17 +35,17 @@ public void setUp() { this.keyVaultServiceFactory = new HashicorpKeyVaultServiceFactory(); this.config = mock(Config.class); this.envProvider = mock(EnvironmentVariableProvider.class); - this.keyVaultClientFactory = mock(HashicorpKeyVaultClientFactory.class); + this.keyVaultServiceFactoryUtil = mock(HashicorpKeyVaultServiceFactoryUtil.class); } @Test(expected = NullPointerException.class) public void nullConfigThrowsException() { - keyVaultServiceFactory.create(null, envProvider, keyVaultClientFactory); + keyVaultServiceFactory.create(null, envProvider); } @Test(expected = NullPointerException.class) public void nullEnvVarProviderThrowsException() { - keyVaultServiceFactory.create(config, null, keyVaultClientFactory); + keyVaultServiceFactory.create(config, null); } @Test @@ -54,97 +54,97 @@ public void getType() { } @Test - public void exceptionThrownIfNoEnvVarsSet() { + public void exceptionThrownIfNoAuthEnvVarsSet() { when(envProvider.getEnv("HASHICORP_ROLE_ID")).thenReturn(null); when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn(null); when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn(null); - Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory)); + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider)); assertThat(ex).isInstanceOf(HashicorpCredentialNotSetException.class); assertThat(ex).hasMessage(noCredentialsExceptionMsg); } @Test - public void exceptionThrownIfOnlyRoleIdEnvVarSet() { + public void exceptionThrownIfOnlyRoleIdAuthEnvVarSet() { when(envProvider.getEnv("HASHICORP_ROLE_ID")).thenReturn("role-id"); when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn(null); when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn(null); - Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory)); + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider)); assertThat(ex).isInstanceOf(HashicorpCredentialNotSetException.class); assertThat(ex).hasMessage(approleCredentialsExceptionMsg); } @Test - public void exceptionThrownIfOnlySecretIdEnvVarSet() { + public void exceptionThrownIfOnlySecretIdAuthEnvVarSet() { when(envProvider.getEnv("HASHICORP_ROLE_ID")).thenReturn(null); when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn("secret-id"); when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn(null); - Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory)); + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider)); assertThat(ex).isInstanceOf(HashicorpCredentialNotSetException.class); assertThat(ex).hasMessage(approleCredentialsExceptionMsg); } @Test - public void exceptionThrownIfOnlyRoleIdAndTokenEnvVarsSet() { + public void exceptionThrownIfOnlyRoleIdAndTokenAuthEnvVarsSet() { when(envProvider.getEnv("HASHICORP_ROLE_ID")).thenReturn("role-id"); when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn(null); when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn("token"); - Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory)); + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider)); assertThat(ex).isInstanceOf(HashicorpCredentialNotSetException.class); assertThat(ex).hasMessage(approleCredentialsExceptionMsg); } @Test - public void exceptionThrownIfOnlySecretIdAndTokenEnvVarsSet() { + public void exceptionThrownIfOnlySecretIdAndTokenAuthEnvVarsSet() { when(envProvider.getEnv("HASHICORP_ROLE_ID")).thenReturn(null); when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn("secret-id"); when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn("token"); - Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory)); + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider)); assertThat(ex).isInstanceOf(HashicorpCredentialNotSetException.class); assertThat(ex).hasMessage(approleCredentialsExceptionMsg); } @Test - public void roleIdAndSecretIdEnvVarsAreSetIsAllowed() { + public void roleIdAndSecretIdAuthEnvVarsAreSetIsAllowed() { when(envProvider.getEnv("HASHICORP_ROLE_ID")).thenReturn("role-id"); when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn("secret-id"); when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn(null); //Exception unrelated to env vars will be thrown - Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory)); + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider)); assertThat(ex).isNotInstanceOf(HashicorpCredentialNotSetException.class); } @Test - public void onlyTokenEnvVarIsSetIsAllowed() { + public void onlyTokenAuthEnvVarIsSetIsAllowed() { when(envProvider.getEnv("HASHICORP_ROLE_ID")).thenReturn(null); when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn(null); when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn("token"); //Exception unrelated to env vars will be thrown - Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory)); + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider)); assertThat(ex).isNotInstanceOf(HashicorpCredentialNotSetException.class); } @Test - public void allEnvVarsSetIsAllowed() { + public void allAuthEnvVarsSetIsAllowed() { when(envProvider.getEnv("HASHICORP_ROLE_ID")).thenReturn("role-id"); when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn("secret-id"); when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn("token"); //Exception unrelated to env vars will be thrown - Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory)); + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider)); assertThat(ex).isNotInstanceOf(HashicorpCredentialNotSetException.class); } @@ -157,7 +157,7 @@ public void exceptionThrownIfProvidedConfigHasNoKeyConfiguration() { when(config.getKeys()).thenReturn(null); - Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory)); + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider)); assertThat(ex).isInstanceOf(ConfigException.class); assertThat(ex).hasMessageContaining("Trying to create Hashicorp Vault connection but no Vault configuration provided"); @@ -174,144 +174,97 @@ public void exceptionThrownIfProvidedConfigHasNoHashicorpKeyVaultConfig() { when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(null); - Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory)); + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider)); assertThat(ex).isInstanceOf(ConfigException.class); assertThat(ex).hasMessageContaining("Trying to create Hashicorp Vault connection but no Vault configuration provided"); } @Test - public void exceptionThrownIfKeyVaultClientFactoryNotHashicorpImplementation() { + public void exceptionThrownIfKeyVaultConfigUrlSyntaxIncorrect() { when(envProvider.getEnv("HASHICORP_ROLE_ID")).thenReturn("role-id"); when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn("secret-id"); - when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn(null); - - KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); - when(config.getKeys()).thenReturn(keyConfiguration); - when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(mock(HashicorpKeyVaultConfig.class)); - - KeyVaultClientFactory wrongImpl = mock(KeyVaultClientFactory.class); - - Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider, wrongImpl)); - - assertThat(ex).isInstanceOf(HashicorpVaultException.class); - assertThat(ex).hasMessageContaining("Incorrect KeyVaultClientFactoryType passed to HashicorpKeyVaultServiceFactory"); - } - - @Test - public void ifRoleIdAndSecretIdEnvVarsSetThenAppRoleIsUsedToAuthenticate() throws Exception { - when(envProvider.getEnv("HASHICORP_ROLE_ID")).thenReturn("role-id"); - when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn("secret-id"); - when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn(null); + when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn("token"); KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); when(config.getKeys()).thenReturn(keyConfiguration); HashicorpKeyVaultConfig keyVaultConfig = mock(HashicorpKeyVaultConfig.class); when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(keyVaultConfig); - when(keyVaultConfig.getApprolePath()).thenReturn("approle"); - Vault unauthenticatedVault = mock(Vault.class); - when( - keyVaultClientFactory - .createUnauthenticatedClient(any(HashicorpKeyVaultConfig.class), any(VaultConfigFactory.class), any(SslConfigFactory.class)) - ).thenReturn(unauthenticatedVault); + when(keyVaultConfig.getUrl()).thenReturn("noschemeurl"); + when(keyVaultConfig.getApprolePath()).thenReturn("approle"); - Auth auth = mock(Auth.class); - when(unauthenticatedVault.auth()).thenReturn(auth); - AuthResponse loginResponse = mock(AuthResponse.class); - when(auth.loginByAppRole(anyString(), anyString(), anyString())).thenReturn(loginResponse); - String token = "token"; - when(loginResponse.getAuthClientToken()).thenReturn(token); + setUpUtilMocks(keyVaultConfig); - keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory); + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider, keyVaultServiceFactoryUtil)); - verify(auth).loginByAppRole("approle", "role-id", "secret-id"); - verify(keyVaultClientFactory).createAuthenticatedClient(any(HashicorpKeyVaultConfig.class), any(VaultConfigFactory.class), any(SslConfigFactory.class), matches(token)); + assertThat(ex).isExactlyInstanceOf(RuntimeException.class); + assertThat(ex.getMessage()).isEqualTo("Provided Hashicorp Vault url is incorrectly formatted"); } @Test - public void ifAllEnvVarsSetThenAppRoleIsUsedToAuthenticate() throws Exception { + public void exceptionThrownIfKeyVaultConfigUrlIsMalformed() { when(envProvider.getEnv("HASHICORP_ROLE_ID")).thenReturn("role-id"); when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn("secret-id"); - when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn("env-token"); + when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn("token"); KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); when(config.getKeys()).thenReturn(keyConfiguration); HashicorpKeyVaultConfig keyVaultConfig = mock(HashicorpKeyVaultConfig.class); when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(keyVaultConfig); + + when(keyVaultConfig.getUrl()).thenReturn("http://malformedurl:-1"); when(keyVaultConfig.getApprolePath()).thenReturn("approle"); - Vault unauthenticatedVault = mock(Vault.class); - when( - keyVaultClientFactory - .createUnauthenticatedClient(any(HashicorpKeyVaultConfig.class), any(VaultConfigFactory.class), any(SslConfigFactory.class)) - ).thenReturn(unauthenticatedVault); + setUpUtilMocks(keyVaultConfig); - Auth auth = mock(Auth.class); - when(unauthenticatedVault.auth()).thenReturn(auth); - AuthResponse loginResponse = mock(AuthResponse.class); - when(auth.loginByAppRole(anyString(), anyString(), anyString())).thenReturn(loginResponse); - String token = "token"; - when(loginResponse.getAuthClientToken()).thenReturn(token); + Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider, keyVaultServiceFactoryUtil)); - keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory); + assertThat(ex).isExactlyInstanceOf(RuntimeException.class); + assertThat(ex.getMessage()).isEqualTo("Provided Hashicorp Vault url is incorrectly formatted"); + } - verify(auth).loginByAppRole("approle", "role-id", "secret-id"); - verify(keyVaultClientFactory).createAuthenticatedClient(any(HashicorpKeyVaultConfig.class), any(VaultConfigFactory.class), any(SslConfigFactory.class), matches(token)); + private void setUpUtilMocks(HashicorpKeyVaultConfig keyVaultConfig) { + SslConfiguration sslConfiguration = mock(SslConfiguration.class); + when(keyVaultServiceFactoryUtil.configureSsl(keyVaultConfig, envProvider)).thenReturn(sslConfiguration); + + ClientHttpRequestFactory clientHttpRequestFactory = mock(ClientHttpRequestFactory.class); + when(keyVaultServiceFactoryUtil.createClientHttpRequestFactory( + any(ClientOptions.class), + eq(sslConfiguration)) + ).thenReturn(clientHttpRequestFactory); + + ClientAuthentication clientAuthentication = mock(ClientAuthentication.class); + when(keyVaultServiceFactoryUtil.configureClientAuthentication( + eq(keyVaultConfig), + eq(envProvider), + eq(clientHttpRequestFactory), + any(VaultEndpoint.class)) + ).thenReturn(clientAuthentication); } @Test - public void exceptionThrownIfErrorEncounteredDuringAppRoleAuthentication() throws Exception { + public void returnedValueIsCorrectType() { when(envProvider.getEnv("HASHICORP_ROLE_ID")).thenReturn("role-id"); when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn("secret-id"); - when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn(null); + when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn("token"); KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); when(config.getKeys()).thenReturn(keyConfiguration); HashicorpKeyVaultConfig keyVaultConfig = mock(HashicorpKeyVaultConfig.class); when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(keyVaultConfig); - when(keyVaultConfig.getApprolePath()).thenReturn("approle"); - - Vault unauthenticatedVault = mock(Vault.class); - when( - keyVaultClientFactory - .createUnauthenticatedClient(any(HashicorpKeyVaultConfig.class), any(VaultConfigFactory.class), any(SslConfigFactory.class)) - ).thenReturn(unauthenticatedVault); - - Auth auth = mock(Auth.class); - when(unauthenticatedVault.auth()).thenReturn(auth); - when(auth.loginByAppRole(anyString(), anyString(), anyString())).thenThrow(VaultException.class); - - Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory)); - - assertThat(ex).isInstanceOf(HashicorpVaultException.class); - assertThat(ex.getMessage()).contains("Unable to authenticate using AppRole"); - } - - @Test - public void ifOnlyTokenEnvVarSetThenTokenIsUsedToAuthenticate() throws Exception { - when(envProvider.getEnv("HASHICORP_ROLE_ID")).thenReturn(null); - when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn(null); - when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn("env-token"); - KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); - when(config.getKeys()).thenReturn(keyConfiguration); - - HashicorpKeyVaultConfig keyVaultConfig = mock(HashicorpKeyVaultConfig.class); - when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(keyVaultConfig); + when(keyVaultConfig.getUrl()).thenReturn("http://someurl"); + when(keyVaultConfig.getApprolePath()).thenReturn("approle"); - Vault unauthenticatedVault = mock(Vault.class); - when( - keyVaultClientFactory - .createUnauthenticatedClient(any(HashicorpKeyVaultConfig.class), any(VaultConfigFactory.class), any(SslConfigFactory.class)) - ).thenReturn(unauthenticatedVault); + setUpUtilMocks(keyVaultConfig); - keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory); + KeyVaultService result = keyVaultServiceFactory.create(config, envProvider, keyVaultServiceFactoryUtil); - verify(keyVaultClientFactory).createAuthenticatedClient(any(HashicorpKeyVaultConfig.class), any(VaultConfigFactory.class), any(SslConfigFactory.class), matches("env-token")); + assertThat(result).isInstanceOf(HashicorpKeyVaultService.class); } } diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryUtilTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryUtilTest.java new file mode 100644 index 0000000000..d78fbcbd6b --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryUtilTest.java @@ -0,0 +1,208 @@ +package com.quorum.tessera.key.vault.hashicorp; + +import com.quorum.tessera.config.HashicorpKeyVaultConfig; +import com.quorum.tessera.config.util.EnvironmentVariableProvider; +import org.junit.Before; +import org.junit.Test; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.Netty4ClientHttpRequestFactory; +import org.springframework.vault.authentication.AppRoleAuthentication; +import org.springframework.vault.authentication.ClientAuthentication; +import org.springframework.vault.authentication.TokenAuthentication; +import org.springframework.vault.client.VaultEndpoint; +import org.springframework.vault.support.ClientOptions; +import org.springframework.vault.support.SslConfiguration; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Duration; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class HashicorpKeyVaultServiceFactoryUtilTest { + + private HashicorpKeyVaultServiceFactoryUtil util; + + private final String roleIdEnvVar = "HASHICORP_ROLE_ID"; + private final String secretIdEnvVar = "HASHICORP_SECRET_ID"; + private final String authTokenEnvVar = "HASHICORP_TOKEN"; + + @Before + public void setUp() { + this.util = new HashicorpKeyVaultServiceFactoryUtil(roleIdEnvVar, secretIdEnvVar, authTokenEnvVar); + } + + @Test + public void configureSslUsesKeyStoreAndTrustStoreIfBothProvided() throws Exception { + HashicorpKeyVaultConfig keyVaultConfig = mock(HashicorpKeyVaultConfig.class); + EnvironmentVariableProvider envProvider = mock(EnvironmentVariableProvider.class); + + Path path = Files.createTempFile(UUID.randomUUID().toString(), ".tmp"); + path.toFile().deleteOnExit(); + + when(keyVaultConfig.getTlsKeyStorePath()).thenReturn(path); + when(keyVaultConfig.getTlsTrustStorePath()).thenReturn(path); + + SslConfiguration result = util.configureSsl(keyVaultConfig, envProvider); + + assertThat(result.getKeyStoreConfiguration().isPresent()).isTrue(); + assertThat(result.getTrustStoreConfiguration().isPresent()).isTrue(); + } + + @Test + public void configureSslUsesTrustStoreOnlyIfProvided() throws Exception { + HashicorpKeyVaultConfig keyVaultConfig = mock(HashicorpKeyVaultConfig.class); + EnvironmentVariableProvider envProvider = mock(EnvironmentVariableProvider.class); + + Path path = Files.createTempFile(UUID.randomUUID().toString(), ".tmp"); + path.toFile().deleteOnExit(); + + when(keyVaultConfig.getTlsKeyStorePath()).thenReturn(null); + when(keyVaultConfig.getTlsTrustStorePath()).thenReturn(path); + + SslConfiguration result = util.configureSsl(keyVaultConfig, envProvider); + + assertThat(result.getKeyStoreConfiguration().isPresent()).isFalse(); + assertThat(result.getTrustStoreConfiguration().isPresent()).isTrue(); + } + + @Test + public void configureSslUsesNoKeyStoresIfNoneProvided() { + HashicorpKeyVaultConfig keyVaultConfig = mock(HashicorpKeyVaultConfig.class); + EnvironmentVariableProvider envProvider = mock(EnvironmentVariableProvider.class); + + when(keyVaultConfig.getTlsKeyStorePath()).thenReturn(null); + when(keyVaultConfig.getTlsTrustStorePath()).thenReturn(null); + + SslConfiguration result = util.configureSsl(keyVaultConfig, envProvider); + + assertThat(result.getKeyStoreConfiguration().isPresent()).isFalse(); + assertThat(result.getTrustStoreConfiguration().isPresent()).isFalse(); + } + + @Test + public void createClientHttpRequestFactory() { + ClientOptions clientOptions = mock(ClientOptions.class); + SslConfiguration sslConfiguration = mock(SslConfiguration.class); + + SslConfiguration.KeyStoreConfiguration keyStoreConfiguration = mock(SslConfiguration.KeyStoreConfiguration.class); + when(sslConfiguration.getKeyStoreConfiguration()).thenReturn(keyStoreConfiguration); + when(sslConfiguration.getTrustStoreConfiguration()).thenReturn(keyStoreConfiguration); + + when(clientOptions.getConnectionTimeout()).thenReturn(Duration.ZERO); + when(clientOptions.getReadTimeout()).thenReturn(Duration.ZERO); + + ClientHttpRequestFactory result = util.createClientHttpRequestFactory(clientOptions, sslConfiguration); + + assertThat(result).isInstanceOf(Netty4ClientHttpRequestFactory.class); + } + + @Test + public void configureClientAuthenticationIfAllEnvVarsSetThenAppRoleMethod() { + HashicorpKeyVaultConfig keyVaultConfig = mock(HashicorpKeyVaultConfig.class); + EnvironmentVariableProvider envProvider = mock(EnvironmentVariableProvider.class); + ClientHttpRequestFactory clientHttpRequestFactory = mock(ClientHttpRequestFactory.class); + VaultEndpoint vaultEndpoint = mock(VaultEndpoint.class); + + when(envProvider.getEnv(roleIdEnvVar)).thenReturn("role-id"); + when(envProvider.getEnv(secretIdEnvVar)).thenReturn("secret-id"); + when(envProvider.getEnv(authTokenEnvVar)).thenReturn("token"); + + when(keyVaultConfig.getApprolePath()).thenReturn("approle"); + + ClientAuthentication result = util.configureClientAuthentication(keyVaultConfig, envProvider, clientHttpRequestFactory, vaultEndpoint); + + assertThat(result).isInstanceOf(AppRoleAuthentication.class); + } + + @Test + public void configureClientAuthenticationIfOnlyRoleIdAndSecretIdSetThenAppRoleMethod() { + HashicorpKeyVaultConfig keyVaultConfig = mock(HashicorpKeyVaultConfig.class); + EnvironmentVariableProvider envProvider = mock(EnvironmentVariableProvider.class); + ClientHttpRequestFactory clientHttpRequestFactory = mock(ClientHttpRequestFactory.class); + VaultEndpoint vaultEndpoint = mock(VaultEndpoint.class); + + when(envProvider.getEnv(roleIdEnvVar)).thenReturn("role-id"); + when(envProvider.getEnv(secretIdEnvVar)).thenReturn("secret-id"); + when(envProvider.getEnv(authTokenEnvVar)).thenReturn(null); + + when(keyVaultConfig.getApprolePath()).thenReturn("somepath"); + + ClientAuthentication result = util.configureClientAuthentication(keyVaultConfig, envProvider, clientHttpRequestFactory, vaultEndpoint); + + assertThat(result).isInstanceOf(AppRoleAuthentication.class); + } + + + @Test + public void configureClientAuthenticationIfOnlyRoleIdSetThenException() { + HashicorpKeyVaultConfig keyVaultConfig = mock(HashicorpKeyVaultConfig.class); + EnvironmentVariableProvider envProvider = mock(EnvironmentVariableProvider.class); + ClientHttpRequestFactory clientHttpRequestFactory = mock(ClientHttpRequestFactory.class); + VaultEndpoint vaultEndpoint = mock(VaultEndpoint.class); + + when(envProvider.getEnv(roleIdEnvVar)).thenReturn("role-id"); + when(envProvider.getEnv(secretIdEnvVar)).thenReturn(null); + when(envProvider.getEnv(authTokenEnvVar)).thenReturn(null); + + Throwable ex = catchThrowable(() -> util.configureClientAuthentication(keyVaultConfig, envProvider, clientHttpRequestFactory, vaultEndpoint)); + + assertThat(ex).isExactlyInstanceOf(RuntimeException.class); + assertThat(ex.getMessage()).isEqualTo("Both " + roleIdEnvVar + " and " + secretIdEnvVar + " environment variables must be set to use the AppRole authentication method"); + } + + @Test + public void configureClientAuthenticationIfOnlySecretIdSetThenException() { + HashicorpKeyVaultConfig keyVaultConfig = mock(HashicorpKeyVaultConfig.class); + EnvironmentVariableProvider envProvider = mock(EnvironmentVariableProvider.class); + ClientHttpRequestFactory clientHttpRequestFactory = mock(ClientHttpRequestFactory.class); + VaultEndpoint vaultEndpoint = mock(VaultEndpoint.class); + + when(envProvider.getEnv(roleIdEnvVar)).thenReturn(null); + when(envProvider.getEnv(secretIdEnvVar)).thenReturn("secret-id"); + when(envProvider.getEnv(authTokenEnvVar)).thenReturn(null); + + Throwable ex = catchThrowable(() -> util.configureClientAuthentication(keyVaultConfig, envProvider, clientHttpRequestFactory, vaultEndpoint)); + + assertThat(ex).isExactlyInstanceOf(RuntimeException.class); + assertThat(ex.getMessage()).isEqualTo("Both " + roleIdEnvVar + " and " + secretIdEnvVar + " environment variables must be set to use the AppRole authentication method"); + } + + @Test + public void configureClientAuthenticationIfOnlyTokenSetThenTokenMethod() { + HashicorpKeyVaultConfig keyVaultConfig = mock(HashicorpKeyVaultConfig.class); + EnvironmentVariableProvider envProvider = mock(EnvironmentVariableProvider.class); + ClientHttpRequestFactory clientHttpRequestFactory = mock(ClientHttpRequestFactory.class); + VaultEndpoint vaultEndpoint = mock(VaultEndpoint.class); + + when(envProvider.getEnv(roleIdEnvVar)).thenReturn(null); + when(envProvider.getEnv(secretIdEnvVar)).thenReturn(null); + when(envProvider.getEnv(authTokenEnvVar)).thenReturn("token"); + + ClientAuthentication result = util.configureClientAuthentication(keyVaultConfig, envProvider, clientHttpRequestFactory, vaultEndpoint); + + assertThat(result).isInstanceOf(TokenAuthentication.class); + } + + @Test + public void configureClientAuthenticationIfNoEnvVarSetThenException() { + HashicorpKeyVaultConfig keyVaultConfig = mock(HashicorpKeyVaultConfig.class); + EnvironmentVariableProvider envProvider = mock(EnvironmentVariableProvider.class); + ClientHttpRequestFactory clientHttpRequestFactory = mock(ClientHttpRequestFactory.class); + VaultEndpoint vaultEndpoint = mock(VaultEndpoint.class); + + when(envProvider.getEnv(roleIdEnvVar)).thenReturn(null); + when(envProvider.getEnv(secretIdEnvVar)).thenReturn(null); + when(envProvider.getEnv(authTokenEnvVar)).thenReturn(null); + + Throwable ex = catchThrowable(() -> util.configureClientAuthentication(keyVaultConfig, envProvider, clientHttpRequestFactory, vaultEndpoint)); + + assertThat(ex).isExactlyInstanceOf(RuntimeException.class); + assertThat(ex.getMessage()).isEqualTo("Both " + roleIdEnvVar + " and " + secretIdEnvVar + " environment variables must be set to use the AppRole authentication method. Alternatively set " + authTokenEnvVar + " to authenticate using the Token method"); + } + +} diff --git a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultServiceFactory.java b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultServiceFactory.java index 20e8e3227a..8ad4d27fb7 100644 --- a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultServiceFactory.java +++ b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultServiceFactory.java @@ -10,7 +10,7 @@ public interface KeyVaultServiceFactory { - KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, KeyVaultClientFactory keyVaultClientFactory); + KeyVaultService create(Config config, EnvironmentVariableProvider envProvider); KeyVaultType getType(); diff --git a/key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/MockAzureKeyVaultServiceFactory.java b/key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/MockAzureKeyVaultServiceFactory.java index ccda494e9d..a871e26059 100644 --- a/key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/MockAzureKeyVaultServiceFactory.java +++ b/key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/MockAzureKeyVaultServiceFactory.java @@ -6,7 +6,7 @@ public class MockAzureKeyVaultServiceFactory implements KeyVaultServiceFactory { @Override - public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, KeyVaultClientFactory keyVaultClientFactory) { + public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider) { throw new UnsupportedOperationException("This mock object's method is not expected to be called"); } diff --git a/pom.xml b/pom.xml index e1abc91073..03c9a923b2 100644 --- a/pom.xml +++ b/pom.xml @@ -32,7 +32,7 @@ UTF-8 1.8 1.8 - 5.0.6.RELEASE + 5.1.2.RELEASE 1.7.5 2.7.3 1.14.0 From bcc2ca36ade8d16edcb31b837a62dd701c1074d2 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Thu, 13 Dec 2018 10:41:48 +0000 Subject: [PATCH 46/57] [WIP] Use separate SecretData properties for secretengine and secretname --- .../vault/data/HashicorpGetSecretData.java | 19 +++++++++++------- .../vault/data/HashicorpSetSecretData.java | 20 +++++++++++-------- .../data/HashicorpGetSecretDataTest.java | 5 +++-- .../data/HashicorpSetSecretDataTest.java | 5 +++-- 4 files changed, 30 insertions(+), 19 deletions(-) diff --git a/config/src/main/java/com/quorum/tessera/config/vault/data/HashicorpGetSecretData.java b/config/src/main/java/com/quorum/tessera/config/vault/data/HashicorpGetSecretData.java index ea55cf275a..e4aef894e7 100644 --- a/config/src/main/java/com/quorum/tessera/config/vault/data/HashicorpGetSecretData.java +++ b/config/src/main/java/com/quorum/tessera/config/vault/data/HashicorpGetSecretData.java @@ -3,23 +3,28 @@ import com.quorum.tessera.config.KeyVaultType; public class HashicorpGetSecretData implements GetSecretData { - private String secretPath; + private final String secretEngineName; + private final String secretName; + private final String valueId; - private String secretName; - - public HashicorpGetSecretData(String secretPath, String secretName) { - this.secretPath = secretPath; + public HashicorpGetSecretData(String secretEngineName, String secretName, String valueId) { + this.secretEngineName = secretEngineName; this.secretName = secretName; + this.valueId = valueId; } - public String getSecretPath() { - return secretPath; + public String getSecretEngineName() { + return secretEngineName; } public String getSecretName() { return secretName; } + public String getValueId() { + return valueId; + } + @Override public KeyVaultType getType() { return KeyVaultType.HASHICORP; diff --git a/config/src/main/java/com/quorum/tessera/config/vault/data/HashicorpSetSecretData.java b/config/src/main/java/com/quorum/tessera/config/vault/data/HashicorpSetSecretData.java index 46dc8bab37..eff9e0c261 100644 --- a/config/src/main/java/com/quorum/tessera/config/vault/data/HashicorpSetSecretData.java +++ b/config/src/main/java/com/quorum/tessera/config/vault/data/HashicorpSetSecretData.java @@ -5,18 +5,22 @@ import java.util.Map; public class HashicorpSetSecretData implements SetSecretData { + private final String secretEngineName; + private final String secretName; + private final Map nameValuePairs; - private String secretPath; - - private Map nameValuePairs; - - public HashicorpSetSecretData(String secretPath, Map nameValuePairs) { - this.secretPath = secretPath; + public HashicorpSetSecretData(String secretEngineName, String secretName, Map nameValuePairs) { + this.secretEngineName = secretEngineName; + this.secretName = secretName; this.nameValuePairs = nameValuePairs; } - public String getSecretPath() { - return secretPath; + public String getSecretEngineName() { + return secretEngineName; + } + + public String getSecretName() { + return secretName; } public Map getNameValuePairs() { diff --git a/config/src/test/java/com/quorum/tessera/config/vault/data/HashicorpGetSecretDataTest.java b/config/src/test/java/com/quorum/tessera/config/vault/data/HashicorpGetSecretDataTest.java index 9ff81a2609..54402da5a9 100644 --- a/config/src/test/java/com/quorum/tessera/config/vault/data/HashicorpGetSecretDataTest.java +++ b/config/src/test/java/com/quorum/tessera/config/vault/data/HashicorpGetSecretDataTest.java @@ -12,13 +12,14 @@ public class HashicorpGetSecretDataTest { @Before public void setUp() { - this.getSecretData = new HashicorpGetSecretData("secret/path", "secretName"); + this.getSecretData = new HashicorpGetSecretData("secret", "secretName", "keyId"); } @Test public void getters() { - assertThat(getSecretData.getSecretPath()).isEqualTo("secret/path"); + assertThat(getSecretData.getSecretEngineName()).isEqualTo("secret"); assertThat(getSecretData.getSecretName()).isEqualTo("secretName"); + assertThat(getSecretData.getValueId()).isEqualTo("keyId"); } @Test diff --git a/config/src/test/java/com/quorum/tessera/config/vault/data/HashicorpSetSecretDataTest.java b/config/src/test/java/com/quorum/tessera/config/vault/data/HashicorpSetSecretDataTest.java index 60cbb89c28..3f4b2d38e9 100644 --- a/config/src/test/java/com/quorum/tessera/config/vault/data/HashicorpSetSecretDataTest.java +++ b/config/src/test/java/com/quorum/tessera/config/vault/data/HashicorpSetSecretDataTest.java @@ -14,12 +14,13 @@ public class HashicorpSetSecretDataTest { @Before public void setUp() { - this.setSecretData = new HashicorpSetSecretData("secret/path", Collections.singletonMap("name", "value")); + this.setSecretData = new HashicorpSetSecretData("secret", "secretName", Collections.singletonMap("name", "value")); } @Test public void getters() { - assertThat(setSecretData.getSecretPath()).isEqualTo("secret/path"); + assertThat(setSecretData.getSecretEngineName()).isEqualTo("secret"); + assertThat(setSecretData.getSecretName()).isEqualTo("secretName"); assertThat(setSecretData.getNameValuePairs()).isEqualTo(Collections.singletonMap("name", "value")); } From 8e0287d5535ca19fae63f89855ebc470acb54709 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Thu, 13 Dec 2018 10:45:38 +0000 Subject: [PATCH 47/57] [WIP] Update KeyVaultService to use new client and SecretData types --- .../hashicorp/HashicorpKeyVaultService.java | 40 +++-- .../HashicorpKeyVaultServiceDelegate.java | 26 +++ .../HashicorpKeyVaultServiceFactory.java | 2 +- .../HashicorpKeyVaultServiceTest.java | 157 +++++++----------- 4 files changed, 106 insertions(+), 119 deletions(-) create mode 100644 key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceDelegate.java diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java index daded9adc3..fb37b5ef27 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java @@ -1,23 +1,25 @@ package com.quorum.tessera.key.vault.hashicorp; -import com.bettercloud.vault.Vault; -import com.bettercloud.vault.VaultException; -import com.bettercloud.vault.response.LogicalResponse; import com.quorum.tessera.config.vault.data.GetSecretData; import com.quorum.tessera.config.vault.data.HashicorpGetSecretData; import com.quorum.tessera.config.vault.data.HashicorpSetSecretData; import com.quorum.tessera.config.vault.data.SetSecretData; import com.quorum.tessera.key.vault.KeyVaultException; import com.quorum.tessera.key.vault.KeyVaultService; +import org.springframework.vault.core.VaultOperations; +import org.springframework.vault.support.Versioned; -import java.util.Optional; +import java.util.Map; public class HashicorpKeyVaultService implements KeyVaultService { - private final Vault vault; + private final VaultOperations vaultOperations; - HashicorpKeyVaultService(Vault vault) { - this.vault = vault; + private final HashicorpKeyVaultServiceDelegate delegate; + + HashicorpKeyVaultService(VaultOperations vaultOperations, HashicorpKeyVaultServiceDelegate delegate) { + this.vaultOperations = vaultOperations; + this.delegate = delegate; } @Override @@ -28,17 +30,17 @@ public String getSecret(GetSecretData getSecretData) { HashicorpGetSecretData hashicorpGetSecretData = (HashicorpGetSecretData) getSecretData; - LogicalResponse response; - try { - response = vault.logical().read(hashicorpGetSecretData.getSecretPath()); - } catch(VaultException e) { - throw new HashicorpVaultException("Error getting secret " + hashicorpGetSecretData.getSecretName() + " from path " + hashicorpGetSecretData.getSecretPath() + " - " + e.getMessage()); + Versioned> versionedResponse = delegate.get(vaultOperations, hashicorpGetSecretData); + + if(versionedResponse == null || !versionedResponse.hasData()) { + throw new HashicorpVaultException("No data found at " + hashicorpGetSecretData.getSecretEngineName() + "/" + hashicorpGetSecretData.getSecretName()); } - return Optional.of(response) - .map(LogicalResponse::getData) - .map(data -> data.get(hashicorpGetSecretData.getSecretName())) - .orElseThrow(() -> new HashicorpVaultException("No secret " + hashicorpGetSecretData.getSecretName() + " found at path " + hashicorpGetSecretData.getSecretPath())); + if(!versionedResponse.getData().containsKey(hashicorpGetSecretData.getValueId())) { + throw new HashicorpVaultException("No value with id " + hashicorpGetSecretData.getValueId() + " found at " + hashicorpGetSecretData.getSecretEngineName() + "/" + hashicorpGetSecretData.getSecretName()); + } + + return versionedResponse.getData().get(hashicorpGetSecretData.getValueId()).toString(); } @Override @@ -49,10 +51,6 @@ public Object setSecret(SetSecretData setSecretData) { HashicorpSetSecretData hashicorpSetSecretData = (HashicorpSetSecretData) setSecretData; - try { - return vault.logical().write(hashicorpSetSecretData.getSecretPath(), hashicorpSetSecretData.getNameValuePairs()); - } catch(VaultException e) { - throw new HashicorpVaultException("Error writing secret to path " + hashicorpSetSecretData.getSecretPath() + " - " + e.getMessage()); - } + return delegate.set(vaultOperations, hashicorpSetSecretData); } } diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceDelegate.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceDelegate.java new file mode 100644 index 0000000000..1dc3cf0ba1 --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceDelegate.java @@ -0,0 +1,26 @@ +package com.quorum.tessera.key.vault.hashicorp; + +import com.quorum.tessera.config.vault.data.HashicorpGetSecretData; +import com.quorum.tessera.config.vault.data.HashicorpSetSecretData; +import org.springframework.vault.core.VaultOperations; +import org.springframework.vault.core.VaultVersionedKeyValueOperations; +import org.springframework.vault.core.VaultVersionedKeyValueTemplate; +import org.springframework.vault.support.Versioned; + +import java.util.Map; + +class HashicorpKeyVaultServiceDelegate { + + Versioned> get(VaultOperations vaultOperations, HashicorpGetSecretData getSecretData) { + VaultVersionedKeyValueOperations keyValueOperations = new VaultVersionedKeyValueTemplate(vaultOperations, getSecretData.getSecretEngineName()); + + return keyValueOperations.get(getSecretData.getSecretName()); + } + + Versioned.Metadata set(VaultOperations vaultOperations, HashicorpSetSecretData setSecretData) { + VaultVersionedKeyValueOperations keyValueOperations = new VaultVersionedKeyValueTemplate(vaultOperations, setSecretData.getSecretEngineName()); + + return keyValueOperations.put(setSecretData.getSecretName(), setSecretData.getNameValuePairs()); + } + +} diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java index 904ac58e57..4722ef4019 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java @@ -75,7 +75,7 @@ else if(isOnlyOneInputNull(roleId, secretId)) { SessionManager sessionManager = new SimpleSessionManager(clientAuthentication); VaultOperations vaultOperations = new VaultTemplate(vaultEndpoint, clientHttpRequestFactory, sessionManager); - return new HashicorpKeyVaultService(vaultOperations); + return new HashicorpKeyVaultService(vaultOperations, new HashicorpKeyVaultServiceDelegate()); } @Override diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java index 19dfdf5f73..87be0d9668 100644 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java @@ -1,9 +1,5 @@ package com.quorum.tessera.key.vault.hashicorp; -import com.bettercloud.vault.Vault; -import com.bettercloud.vault.VaultException; -import com.bettercloud.vault.api.Logical; -import com.bettercloud.vault.response.LogicalResponse; import com.quorum.tessera.config.vault.data.GetSecretData; import com.quorum.tessera.config.vault.data.HashicorpGetSecretData; import com.quorum.tessera.config.vault.data.HashicorpSetSecretData; @@ -11,15 +7,15 @@ import com.quorum.tessera.key.vault.KeyVaultException; import org.junit.Before; import org.junit.Test; +import org.springframework.vault.core.VaultOperations; +import org.springframework.vault.support.Versioned; import java.util.Collections; -import java.util.HashMap; import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; -import static org.mockito.ArgumentMatchers.anyMap; -import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -27,12 +23,40 @@ public class HashicorpKeyVaultServiceTest { private HashicorpKeyVaultService keyVaultService; - private Vault vault; + private VaultOperations vaultOperations; + + private HashicorpKeyVaultServiceDelegate delegate; @Before public void setUp() { - this.vault = mock(Vault.class); - this.keyVaultService = new HashicorpKeyVaultService(vault); + this.vaultOperations = mock(VaultOperations.class); + this.delegate = mock(HashicorpKeyVaultServiceDelegate.class); + this.keyVaultService = new HashicorpKeyVaultService(vaultOperations, delegate); + } + + @Test + public void getSecret() { + HashicorpGetSecretData getSecretData = mock(HashicorpGetSecretData.class); + + when(getSecretData.getSecretEngineName()).thenReturn("secretEngine"); + when(getSecretData.getSecretName()).thenReturn("secretName"); + when(getSecretData.getValueId()).thenReturn("keyId"); + + Versioned versionedResponse = mock(Versioned.class); + + when(delegate.get(any(VaultOperations.class), any(HashicorpGetSecretData.class))).thenReturn(versionedResponse); + + when(versionedResponse.hasData()).thenReturn(true); + + Map responseData = mock(Map.class); + when(versionedResponse.getData()).thenReturn(responseData); + when(responseData.containsKey("keyId")).thenReturn(true); + String keyValue = "keyvalue"; + when(responseData.get("keyId")).thenReturn(keyValue); + + String result = keyVaultService.getSecret(getSecretData); + + assertThat(result).isEqualTo(keyValue); } @Test @@ -47,97 +71,52 @@ public void getSecretThrowsExceptionIfProvidedDataIsNotCorrectType() { } @Test - public void getSecretThrowsExceptionIfErrorRetrievingSecretFromVault() throws Exception { - Logical logical = mock(Logical.class); - when(vault.logical()).thenReturn(logical); - when(logical.read(anyString())).thenThrow(new VaultException("vault exception msg")); + public void getSecretThrowsExceptionIfNullRetrievedFromVault() { + HashicorpGetSecretData getSecretData = new HashicorpGetSecretData("engine", "secretName", "id"); - GetSecretData getSecretData = new HashicorpGetSecretData("secret/path", "secretName"); + when(delegate.get(vaultOperations, getSecretData)).thenReturn(null); Throwable ex = catchThrowable(() -> keyVaultService.getSecret(getSecretData)); assertThat(ex).isExactlyInstanceOf(HashicorpVaultException.class); - assertThat(ex).hasMessage("Error getting secret secretName from path secret/path - vault exception msg"); + assertThat(ex).hasMessage("No data found at engine/secretName"); } @Test - public void getSecretThrowsExceptionIfResponseHasNoData() throws Exception { - Logical logical = mock(Logical.class); - when(vault.logical()).thenReturn(logical); - - LogicalResponse response = mock(LogicalResponse.class); - when(logical.read(anyString())).thenReturn(response); + public void getSecretThrowsExceptionIfNoDataRetrievedFromVault() { + HashicorpGetSecretData getSecretData = new HashicorpGetSecretData("engine", "secretName", "id"); - when(response.getData()).thenReturn(null); + Versioned versionedResponse = mock(Versioned.class); + when(versionedResponse.hasData()).thenReturn(false); - GetSecretData getSecretData = new HashicorpGetSecretData("secret/path", "secretName"); + when(delegate.get(vaultOperations, getSecretData)).thenReturn(versionedResponse); Throwable ex = catchThrowable(() -> keyVaultService.getSecret(getSecretData)); assertThat(ex).isExactlyInstanceOf(HashicorpVaultException.class); - assertThat(ex).hasMessage("No secret secretName found at path secret/path"); + assertThat(ex).hasMessage("No data found at engine/secretName"); } + @Test - public void getSecretThrowsExceptionIfValueNotFoundForGivenSecretName() throws Exception { - Logical logical = mock(Logical.class); - when(vault.logical()).thenReturn(logical); + public void getSecretThrowsExceptionIfValueNotFoundForGivenId() { + HashicorpGetSecretData getSecretData = new HashicorpGetSecretData("engine", "secretName", "id"); - LogicalResponse response = mock(LogicalResponse.class); - when(logical.read(anyString())).thenReturn(response); + Versioned versionedResponse = mock(Versioned.class); + when(versionedResponse.hasData()).thenReturn(true); - Map data = Collections.singletonMap("diffName", "value"); - when(response.getData()).thenReturn(data); + Map responseData = mock(Map.class); + when(versionedResponse.getData()).thenReturn(responseData); + when(responseData.containsKey("id")).thenReturn(false); - GetSecretData getSecretData = new HashicorpGetSecretData("secret/path", "secretName"); + when(delegate.get(vaultOperations, getSecretData)).thenReturn(versionedResponse); Throwable ex = catchThrowable(() -> keyVaultService.getSecret(getSecretData)); assertThat(ex).isExactlyInstanceOf(HashicorpVaultException.class); - assertThat(ex).hasMessage("No secret secretName found at path secret/path"); + assertThat(ex).hasMessage("No value with id id found at engine/secretName"); } - @Test - public void getSecretReturnsValueForGivenSecretNameAtGivenPath() throws Exception { - Logical logical = mock(Logical.class); - when(vault.logical()).thenReturn(logical); - - LogicalResponse response = mock(LogicalResponse.class); - when(logical.read(anyString())).thenReturn(response); - - String expected = "value"; - - Map data = Collections.singletonMap("secretName", expected); - when(response.getData()).thenReturn(data); - - GetSecretData getSecretData = new HashicorpGetSecretData("secret/path", "secretName"); - - String result = keyVaultService.getSecret(getSecretData); - - assertThat(result).isEqualTo(expected); - } - - @Test - public void getSecretReturnsCorrectValueIfMultipleFoundAtGivenPath() throws Exception { - Logical logical = mock(Logical.class); - when(vault.logical()).thenReturn(logical); - - LogicalResponse response = mock(LogicalResponse.class); - when(logical.read(anyString())).thenReturn(response); - - String expected = "value"; - - Map data = new HashMap<>(); - data.put("someOtherSecret", "someOtherValue"); - data.put("secretName", expected); - when(response.getData()).thenReturn(data); - - GetSecretData getSecretData = new HashicorpGetSecretData("secret/path", "secretName"); - - String result = keyVaultService.getSecret(getSecretData); - - assertThat(result).isEqualTo(expected); - } @Test public void setSecretThrowsExceptionIfProvidedDataIsNotCorrectType() { @@ -150,34 +129,18 @@ public void setSecretThrowsExceptionIfProvidedDataIsNotCorrectType() { assertThat(ex).hasMessage("Incorrect data type passed to HashicorpKeyVaultService. Type was null"); } - @Test - public void setSecretThrowsExceptionIfErrorWritingToVault() throws Exception { - Logical logical = mock(Logical.class); - when(vault.logical()).thenReturn(logical); - when(logical.write(anyString(), anyMap())).thenThrow(new VaultException("vault exception msg")); - - SetSecretData setSecretData = new HashicorpSetSecretData("secret/path", Collections.emptyMap()); - - Throwable ex = catchThrowable(() -> keyVaultService.setSecret(setSecretData)); - - assertThat(ex).isExactlyInstanceOf(HashicorpVaultException.class); - assertThat(ex).hasMessage("Error writing secret to path secret/path - vault exception msg"); - } @Test - public void setSecretReturnsLogicalResponse() throws Exception { - Logical logical = mock(Logical.class); - when(vault.logical()).thenReturn(logical); - - LogicalResponse response = mock(LogicalResponse.class); - when(logical.write(anyString(), anyMap())).thenReturn(response); + public void setSecretReturnsMetadataObject() { + HashicorpSetSecretData setSecretData = new HashicorpSetSecretData("engine", "name", Collections.emptyMap()); - SetSecretData setSecretData = new HashicorpSetSecretData("secret/path", Collections.emptyMap()); + Versioned.Metadata metadata = mock(Versioned.Metadata.class); + when(delegate.set(vaultOperations, setSecretData)).thenReturn(metadata); Object result = keyVaultService.setSecret(setSecretData); - assertThat(result).isInstanceOf(LogicalResponse.class); - assertThat(result).isEqualTo(response); + assertThat(result).isInstanceOf(Versioned.Metadata.class); + assertThat(result).isEqualTo(metadata); } } From c081674b1d2f602399509b1a1e2f3a494083e5f4 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Thu, 13 Dec 2018 12:41:44 +0000 Subject: [PATCH 48/57] [WIP] Create KeyValueOperationsDelegate to aid testing --- .../hashicorp/HashicorpKeyVaultService.java | 19 +++--- .../HashicorpKeyVaultServiceDelegate.java | 26 -------- .../HashicorpKeyVaultServiceFactory.java | 4 +- .../hashicorp/KeyValueOperationsDelegate.java | 26 ++++++++ .../KeyValueOperationsDelegateFactory.java | 21 +++++++ .../HashicorpKeyVaultServiceTest.java | 24 +++---- .../KeyValueOperationsDelegateTest.java | 63 +++++++++++++++++++ pom.xml | 1 + 8 files changed, 137 insertions(+), 47 deletions(-) delete mode 100644 key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceDelegate.java create mode 100644 key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/KeyValueOperationsDelegate.java create mode 100644 key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/KeyValueOperationsDelegateFactory.java create mode 100644 key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/KeyValueOperationsDelegateTest.java diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java index fb37b5ef27..ac84639feb 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultService.java @@ -6,22 +6,19 @@ import com.quorum.tessera.config.vault.data.SetSecretData; import com.quorum.tessera.key.vault.KeyVaultException; import com.quorum.tessera.key.vault.KeyVaultService; -import org.springframework.vault.core.VaultOperations; import org.springframework.vault.support.Versioned; import java.util.Map; public class HashicorpKeyVaultService implements KeyVaultService { - private final VaultOperations vaultOperations; + private final KeyValueOperationsDelegateFactory keyValueOperationsDelegateFactory; - private final HashicorpKeyVaultServiceDelegate delegate; - - HashicorpKeyVaultService(VaultOperations vaultOperations, HashicorpKeyVaultServiceDelegate delegate) { - this.vaultOperations = vaultOperations; - this.delegate = delegate; + HashicorpKeyVaultService(KeyValueOperationsDelegateFactory keyValueOperationsDelegateFactory) { + this.keyValueOperationsDelegateFactory = keyValueOperationsDelegateFactory; } + @Override public String getSecret(GetSecretData getSecretData) { if(!(getSecretData instanceof HashicorpGetSecretData)) { @@ -30,7 +27,9 @@ public String getSecret(GetSecretData getSecretData) { HashicorpGetSecretData hashicorpGetSecretData = (HashicorpGetSecretData) getSecretData; - Versioned> versionedResponse = delegate.get(vaultOperations, hashicorpGetSecretData); + KeyValueOperationsDelegate keyValueOperationsDelegate = keyValueOperationsDelegateFactory.create(hashicorpGetSecretData.getSecretEngineName()); + + Versioned> versionedResponse = keyValueOperationsDelegate.get(hashicorpGetSecretData); if(versionedResponse == null || !versionedResponse.hasData()) { throw new HashicorpVaultException("No data found at " + hashicorpGetSecretData.getSecretEngineName() + "/" + hashicorpGetSecretData.getSecretName()); @@ -51,6 +50,8 @@ public Object setSecret(SetSecretData setSecretData) { HashicorpSetSecretData hashicorpSetSecretData = (HashicorpSetSecretData) setSecretData; - return delegate.set(vaultOperations, hashicorpSetSecretData); + KeyValueOperationsDelegate keyValueOperationsDelegate = keyValueOperationsDelegateFactory.create(hashicorpSetSecretData.getSecretEngineName()); + + return keyValueOperationsDelegate.set(hashicorpSetSecretData); } } diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceDelegate.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceDelegate.java deleted file mode 100644 index 1dc3cf0ba1..0000000000 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceDelegate.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.quorum.tessera.key.vault.hashicorp; - -import com.quorum.tessera.config.vault.data.HashicorpGetSecretData; -import com.quorum.tessera.config.vault.data.HashicorpSetSecretData; -import org.springframework.vault.core.VaultOperations; -import org.springframework.vault.core.VaultVersionedKeyValueOperations; -import org.springframework.vault.core.VaultVersionedKeyValueTemplate; -import org.springframework.vault.support.Versioned; - -import java.util.Map; - -class HashicorpKeyVaultServiceDelegate { - - Versioned> get(VaultOperations vaultOperations, HashicorpGetSecretData getSecretData) { - VaultVersionedKeyValueOperations keyValueOperations = new VaultVersionedKeyValueTemplate(vaultOperations, getSecretData.getSecretEngineName()); - - return keyValueOperations.get(getSecretData.getSecretName()); - } - - Versioned.Metadata set(VaultOperations vaultOperations, HashicorpSetSecretData setSecretData) { - VaultVersionedKeyValueOperations keyValueOperations = new VaultVersionedKeyValueTemplate(vaultOperations, setSecretData.getSecretEngineName()); - - return keyValueOperations.put(setSecretData.getSecretName(), setSecretData.getNameValuePairs()); - } - -} diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java index 4722ef4019..4e56b18e24 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java @@ -75,7 +75,9 @@ else if(isOnlyOneInputNull(roleId, secretId)) { SessionManager sessionManager = new SimpleSessionManager(clientAuthentication); VaultOperations vaultOperations = new VaultTemplate(vaultEndpoint, clientHttpRequestFactory, sessionManager); - return new HashicorpKeyVaultService(vaultOperations, new HashicorpKeyVaultServiceDelegate()); + return new HashicorpKeyVaultService( + new KeyValueOperationsDelegateFactory(vaultOperations) + ); } @Override diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/KeyValueOperationsDelegate.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/KeyValueOperationsDelegate.java new file mode 100644 index 0000000000..67a22cf376 --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/KeyValueOperationsDelegate.java @@ -0,0 +1,26 @@ +package com.quorum.tessera.key.vault.hashicorp; + +import com.quorum.tessera.config.vault.data.HashicorpGetSecretData; +import com.quorum.tessera.config.vault.data.HashicorpSetSecretData; +import org.springframework.vault.core.VaultVersionedKeyValueOperations; +import org.springframework.vault.support.Versioned; + +import java.util.Map; + +class KeyValueOperationsDelegate { + + private final VaultVersionedKeyValueOperations keyValueOperations; + + KeyValueOperationsDelegate(VaultVersionedKeyValueOperations keyValueOperations) { + this.keyValueOperations = keyValueOperations; + } + + Versioned> get(HashicorpGetSecretData getSecretData) { + return keyValueOperations.get(getSecretData.getSecretName()); + } + + Versioned.Metadata set(HashicorpSetSecretData setSecretData) { + return keyValueOperations.put(setSecretData.getSecretName(), setSecretData.getNameValuePairs()); + } + +} diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/KeyValueOperationsDelegateFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/KeyValueOperationsDelegateFactory.java new file mode 100644 index 0000000000..4155cb0f7b --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/KeyValueOperationsDelegateFactory.java @@ -0,0 +1,21 @@ +package com.quorum.tessera.key.vault.hashicorp; + +import org.springframework.vault.core.VaultOperations; +import org.springframework.vault.core.VaultVersionedKeyValueOperations; +import org.springframework.vault.core.VaultVersionedKeyValueTemplate; + +class KeyValueOperationsDelegateFactory { + + private final VaultOperations vaultOperations; + + KeyValueOperationsDelegateFactory(VaultOperations vaultOperations) { + this.vaultOperations = vaultOperations; + } + + KeyValueOperationsDelegate create(String secretEngineName) { + VaultVersionedKeyValueOperations keyValueOperations = new VaultVersionedKeyValueTemplate(vaultOperations, secretEngineName); + + return new KeyValueOperationsDelegate(keyValueOperations); + } + +} diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java index 87be0d9668..3677a643f6 100644 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java @@ -7,7 +7,6 @@ import com.quorum.tessera.key.vault.KeyVaultException; import org.junit.Before; import org.junit.Test; -import org.springframework.vault.core.VaultOperations; import org.springframework.vault.support.Versioned; import java.util.Collections; @@ -16,6 +15,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -23,15 +23,17 @@ public class HashicorpKeyVaultServiceTest { private HashicorpKeyVaultService keyVaultService; - private VaultOperations vaultOperations; + private KeyValueOperationsDelegateFactory delegateFactory; - private HashicorpKeyVaultServiceDelegate delegate; + private KeyValueOperationsDelegate delegate; @Before public void setUp() { - this.vaultOperations = mock(VaultOperations.class); - this.delegate = mock(HashicorpKeyVaultServiceDelegate.class); - this.keyVaultService = new HashicorpKeyVaultService(vaultOperations, delegate); + this.delegateFactory = mock(KeyValueOperationsDelegateFactory.class); + this.delegate = mock(KeyValueOperationsDelegate.class); + when(delegateFactory.create(anyString())).thenReturn(delegate); + + this.keyVaultService = new HashicorpKeyVaultService(delegateFactory); } @Test @@ -44,7 +46,7 @@ public void getSecret() { Versioned versionedResponse = mock(Versioned.class); - when(delegate.get(any(VaultOperations.class), any(HashicorpGetSecretData.class))).thenReturn(versionedResponse); + when(delegate.get(any(HashicorpGetSecretData.class))).thenReturn(versionedResponse); when(versionedResponse.hasData()).thenReturn(true); @@ -74,7 +76,7 @@ public void getSecretThrowsExceptionIfProvidedDataIsNotCorrectType() { public void getSecretThrowsExceptionIfNullRetrievedFromVault() { HashicorpGetSecretData getSecretData = new HashicorpGetSecretData("engine", "secretName", "id"); - when(delegate.get(vaultOperations, getSecretData)).thenReturn(null); + when(delegate.get(getSecretData)).thenReturn(null); Throwable ex = catchThrowable(() -> keyVaultService.getSecret(getSecretData)); @@ -89,7 +91,7 @@ public void getSecretThrowsExceptionIfNoDataRetrievedFromVault() { Versioned versionedResponse = mock(Versioned.class); when(versionedResponse.hasData()).thenReturn(false); - when(delegate.get(vaultOperations, getSecretData)).thenReturn(versionedResponse); + when(delegate.get(getSecretData)).thenReturn(versionedResponse); Throwable ex = catchThrowable(() -> keyVaultService.getSecret(getSecretData)); @@ -109,7 +111,7 @@ public void getSecretThrowsExceptionIfValueNotFoundForGivenId() { when(versionedResponse.getData()).thenReturn(responseData); when(responseData.containsKey("id")).thenReturn(false); - when(delegate.get(vaultOperations, getSecretData)).thenReturn(versionedResponse); + when(delegate.get(getSecretData)).thenReturn(versionedResponse); Throwable ex = catchThrowable(() -> keyVaultService.getSecret(getSecretData)); @@ -135,7 +137,7 @@ public void setSecretReturnsMetadataObject() { HashicorpSetSecretData setSecretData = new HashicorpSetSecretData("engine", "name", Collections.emptyMap()); Versioned.Metadata metadata = mock(Versioned.Metadata.class); - when(delegate.set(vaultOperations, setSecretData)).thenReturn(metadata); + when(delegate.set(setSecretData)).thenReturn(metadata); Object result = keyVaultService.setSecret(setSecretData); diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/KeyValueOperationsDelegateTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/KeyValueOperationsDelegateTest.java new file mode 100644 index 0000000000..0e8092ca3b --- /dev/null +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/KeyValueOperationsDelegateTest.java @@ -0,0 +1,63 @@ +package com.quorum.tessera.key.vault.hashicorp; + +import com.quorum.tessera.config.vault.data.HashicorpGetSecretData; +import com.quorum.tessera.config.vault.data.HashicorpSetSecretData; +import org.junit.Before; +import org.junit.Test; +import org.springframework.vault.core.VaultVersionedKeyValueOperations; +import org.springframework.vault.support.Versioned; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; + +public class KeyValueOperationsDelegateTest { + + private KeyValueOperationsDelegate delegate; + + private VaultVersionedKeyValueOperations keyValueOperations; + + @Before + public void setUp() { + this.keyValueOperations = mock(VaultVersionedKeyValueOperations.class); + this.delegate = new KeyValueOperationsDelegate(keyValueOperations); + } + + @Test + public void get() { + String secretName = "secretName"; + + HashicorpGetSecretData getSecretData = mock(HashicorpGetSecretData.class); + when(getSecretData.getSecretName()).thenReturn(secretName); + + Versioned versionedResponse = mock(Versioned.class); + when(keyValueOperations.get(secretName)).thenReturn(versionedResponse); + + Versioned result = delegate.get(getSecretData); + + verify(keyValueOperations).get(secretName); + + assertThat(result).isEqualTo(versionedResponse); + } + + @Test + public void set() { + String secretName = "secretName"; + + HashicorpSetSecretData setSecretData = mock(HashicorpSetSecretData.class); + when(setSecretData.getSecretName()).thenReturn(secretName); + Map nameValuePairs = mock(Map.class); + when(setSecretData.getNameValuePairs()).thenReturn(nameValuePairs); + + Versioned.Metadata metadata = mock(Versioned.Metadata.class); + when(keyValueOperations.put(secretName, nameValuePairs)).thenReturn(metadata); + + Versioned.Metadata result = delegate.set(setSecretData); + + verify(keyValueOperations).put(secretName, nameValuePairs); + + assertThat(result).isEqualTo(metadata); + } + +} diff --git a/pom.xml b/pom.xml index 03c9a923b2..800fb9e5e4 100644 --- a/pom.xml +++ b/pom.xml @@ -220,6 +220,7 @@ com/quorum/tessera/config/util/ConsolePasswordReader* com/quorum/tessera/config/util/PasswordReaderFactory* com/quorum/tessera/key/vault/azure/AzureKeyVaultClientDelegate* + com/quorum/tessera/key/vault/hashicorp/KeyValueOperationsDelegateFactory* From 3310e7e37135ac98c83143eaa19ea01aca9e5991 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Thu, 13 Dec 2018 12:51:59 +0000 Subject: [PATCH 49/57] [WIP] Remove KeyVaultClientFactory class as no longer in use --- .../MockHashicorpKeyVaultServiceFactory.java | 3 +-- .../keypairconverter/KeyPairConverter.java | 13 +++++------ .../KeyPairConverterTest.java | 2 +- .../MockAzureKeyVaultServiceFactory.java | 3 +-- .../MockHashicorpKeyVaultServiceFactory.java | 3 +-- ...um.tessera.key.vault.KeyVaultClientFactory | 1 - .../key/vault/KeyVaultClientFactory.java | 22 ------------------- .../key/vault/KeyVaultClientFactoryTest.java | 22 ------------------- .../MockHashicorpKeyVaultClientFactory.java | 10 --------- 9 files changed, 9 insertions(+), 70 deletions(-) delete mode 100644 key-vault/hashicorp-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.KeyVaultClientFactory delete mode 100644 key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultClientFactory.java delete mode 100644 key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/KeyVaultClientFactoryTest.java delete mode 100644 key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/MockHashicorpKeyVaultClientFactory.java diff --git a/key-generation/src/test/java/com/quorum/tessera/key/generation/MockHashicorpKeyVaultServiceFactory.java b/key-generation/src/test/java/com/quorum/tessera/key/generation/MockHashicorpKeyVaultServiceFactory.java index fcc5f31da9..2585c24bb9 100644 --- a/key-generation/src/test/java/com/quorum/tessera/key/generation/MockHashicorpKeyVaultServiceFactory.java +++ b/key-generation/src/test/java/com/quorum/tessera/key/generation/MockHashicorpKeyVaultServiceFactory.java @@ -4,7 +4,6 @@ import com.quorum.tessera.config.KeyVaultType; import com.quorum.tessera.config.util.EnvironmentVariableProvider; import com.quorum.tessera.config.vault.data.HashicorpGetSecretData; -import com.quorum.tessera.key.vault.KeyVaultClientFactory; import com.quorum.tessera.key.vault.KeyVaultService; import com.quorum.tessera.key.vault.KeyVaultServiceFactory; @@ -14,7 +13,7 @@ public class MockHashicorpKeyVaultServiceFactory implements KeyVaultServiceFactory { @Override - public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, KeyVaultClientFactory keyVaultClientFactory) { + public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider) { KeyVaultService mock = mock(KeyVaultService.class); when(mock.getSecret(any(HashicorpGetSecretData.class))) diff --git a/key-pair-converter/src/main/java/com/quorum/tessera/keypairconverter/KeyPairConverter.java b/key-pair-converter/src/main/java/com/quorum/tessera/keypairconverter/KeyPairConverter.java index 846b5642db..5eb8fd6fcd 100644 --- a/key-pair-converter/src/main/java/com/quorum/tessera/keypairconverter/KeyPairConverter.java +++ b/key-pair-converter/src/main/java/com/quorum/tessera/keypairconverter/KeyPairConverter.java @@ -13,7 +13,6 @@ import com.quorum.tessera.encryption.KeyPair; import com.quorum.tessera.encryption.PrivateKey; import com.quorum.tessera.encryption.PublicKey; -import com.quorum.tessera.key.vault.KeyVaultClientFactory; import com.quorum.tessera.key.vault.KeyVaultService; import com.quorum.tessera.key.vault.KeyVaultServiceFactory; @@ -33,7 +32,7 @@ public KeyPairConverter(Config config, EnvironmentVariableProvider envProvider) this.envProvider = envProvider; } - public Collection convert(Collection configKeyPairs) { + Collection convert(Collection configKeyPairs) { return configKeyPairs .stream() .map(this::convert) @@ -47,9 +46,8 @@ private KeyPair convert(ConfigKeyPair configKeyPair) { if(configKeyPair instanceof AzureVaultKeyPair) { KeyVaultServiceFactory keyVaultServiceFactory = KeyVaultServiceFactory.getInstance(KeyVaultType.AZURE); - KeyVaultClientFactory keyVaultClientFactory = KeyVaultClientFactory.getInstance(KeyVaultType.AZURE); - KeyVaultService keyVaultService = keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory); + KeyVaultService keyVaultService = keyVaultServiceFactory.create(config, envProvider); AzureVaultKeyPair akp = (AzureVaultKeyPair) configKeyPair; @@ -62,14 +60,13 @@ private KeyPair convert(ConfigKeyPair configKeyPair) { else if(configKeyPair instanceof HashicorpVaultKeyPair) { KeyVaultServiceFactory keyVaultServiceFactory = KeyVaultServiceFactory.getInstance(KeyVaultType.HASHICORP); - KeyVaultClientFactory keyVaultClientFactory = KeyVaultClientFactory.getInstance(KeyVaultType.HASHICORP); - KeyVaultService keyVaultService = keyVaultServiceFactory.create(config, envProvider, keyVaultClientFactory); + KeyVaultService keyVaultService = keyVaultServiceFactory.create(config, envProvider); HashicorpVaultKeyPair hkp = (HashicorpVaultKeyPair) configKeyPair; - GetSecretData getPublicKeyData = new HashicorpGetSecretData(hkp.getSecretPath(), hkp.getPublicKeyId()); - GetSecretData getPrivateKeyData = new HashicorpGetSecretData(hkp.getSecretPath(), hkp.getPrivateKeyId()); + GetSecretData getPublicKeyData = new HashicorpGetSecretData(hkp.getSecretEngineName(), hkp.getSecretName(), hkp.getPublicKeyId()); + GetSecretData getPrivateKeyData = new HashicorpGetSecretData(hkp.getSecretEngineName(), hkp.getSecretName(), hkp.getPrivateKeyId()); base64PublicKey = keyVaultService.getSecret(getPublicKeyData); base64PrivateKey = keyVaultService.getSecret(getPrivateKeyData); diff --git a/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/KeyPairConverterTest.java b/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/KeyPairConverterTest.java index a5f7f2aae6..459c971b8a 100644 --- a/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/KeyPairConverterTest.java +++ b/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/KeyPairConverterTest.java @@ -94,7 +94,7 @@ public void convertSingleAzureVaultKeyPair() { @Test public void convertSingleHashicorpVaultKeyPair() { - final HashicorpVaultKeyPair keyPair = new HashicorpVaultKeyPair("pub", "priv", "secretPath"); + final HashicorpVaultKeyPair keyPair = new HashicorpVaultKeyPair("pub", "priv", "engine", "secretName"); Collection result = converter.convert(Collections.singletonList(keyPair)); diff --git a/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/MockAzureKeyVaultServiceFactory.java b/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/MockAzureKeyVaultServiceFactory.java index 6046e83b7a..bb03726f06 100644 --- a/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/MockAzureKeyVaultServiceFactory.java +++ b/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/MockAzureKeyVaultServiceFactory.java @@ -4,7 +4,6 @@ import com.quorum.tessera.config.KeyVaultType; import com.quorum.tessera.config.util.EnvironmentVariableProvider; import com.quorum.tessera.config.vault.data.AzureGetSecretData; -import com.quorum.tessera.key.vault.KeyVaultClientFactory; import com.quorum.tessera.key.vault.KeyVaultService; import com.quorum.tessera.key.vault.KeyVaultServiceFactory; @@ -14,7 +13,7 @@ public class MockAzureKeyVaultServiceFactory implements KeyVaultServiceFactory { @Override - public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, KeyVaultClientFactory keyVaultClientFactory) { + public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider) { KeyVaultService mock = mock(KeyVaultService.class); when(mock.getSecret(any(AzureGetSecretData.class))) diff --git a/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/MockHashicorpKeyVaultServiceFactory.java b/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/MockHashicorpKeyVaultServiceFactory.java index 4fdf13884f..690df05511 100644 --- a/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/MockHashicorpKeyVaultServiceFactory.java +++ b/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/MockHashicorpKeyVaultServiceFactory.java @@ -4,7 +4,6 @@ import com.quorum.tessera.config.KeyVaultType; import com.quorum.tessera.config.util.EnvironmentVariableProvider; import com.quorum.tessera.config.vault.data.HashicorpGetSecretData; -import com.quorum.tessera.key.vault.KeyVaultClientFactory; import com.quorum.tessera.key.vault.KeyVaultService; import com.quorum.tessera.key.vault.KeyVaultServiceFactory; @@ -14,7 +13,7 @@ public class MockHashicorpKeyVaultServiceFactory implements KeyVaultServiceFactory { @Override - public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, KeyVaultClientFactory keyVaultClientFactory) { + public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider) { KeyVaultService mock = mock(KeyVaultService.class); when(mock.getSecret(any(HashicorpGetSecretData.class))) diff --git a/key-vault/hashicorp-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.KeyVaultClientFactory b/key-vault/hashicorp-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.KeyVaultClientFactory deleted file mode 100644 index 2ad1eb744b..0000000000 --- a/key-vault/hashicorp-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.KeyVaultClientFactory +++ /dev/null @@ -1 +0,0 @@ -com.quorum.tessera.key.vault.hashicorp.HashicorpKeyVaultClientFactory \ No newline at end of file diff --git a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultClientFactory.java b/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultClientFactory.java deleted file mode 100644 index 2262a799a1..0000000000 --- a/key-vault/key-vault-api/src/main/java/com/quorum/tessera/key/vault/KeyVaultClientFactory.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.quorum.tessera.key.vault; - -import com.quorum.tessera.config.KeyVaultType; - -import java.util.ArrayList; -import java.util.List; -import java.util.ServiceLoader; - -public interface KeyVaultClientFactory { - KeyVaultType getType(); - - static KeyVaultClientFactory getInstance(KeyVaultType keyVaultType) { - List providers = new ArrayList<>(); - ServiceLoader.load(KeyVaultClientFactory.class).forEach(providers::add); - - return providers.stream() - .filter(factory -> factory.getType() == keyVaultType) - .findFirst() - .orElse(null); - } - -} diff --git a/key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/KeyVaultClientFactoryTest.java b/key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/KeyVaultClientFactoryTest.java deleted file mode 100644 index 9eaca416f2..0000000000 --- a/key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/KeyVaultClientFactoryTest.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.quorum.tessera.key.vault; - -import com.quorum.tessera.config.KeyVaultType; -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class KeyVaultClientFactoryTest { - - @Test - public void getInstance() { - KeyVaultClientFactory keyVaultClientFactory = KeyVaultClientFactory.getInstance(KeyVaultType.HASHICORP); - - assertThat(keyVaultClientFactory).isExactlyInstanceOf(MockHashicorpKeyVaultClientFactory.class); - } - - @Test - public void instanceNotFoundReturnsNull() { - assertThat(KeyVaultClientFactory.getInstance(null)).isNull(); - } - -} diff --git a/key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/MockHashicorpKeyVaultClientFactory.java b/key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/MockHashicorpKeyVaultClientFactory.java deleted file mode 100644 index 7e96867239..0000000000 --- a/key-vault/key-vault-api/src/test/java/com/quorum/tessera/key/vault/MockHashicorpKeyVaultClientFactory.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.quorum.tessera.key.vault; - -import com.quorum.tessera.config.KeyVaultType; - -public class MockHashicorpKeyVaultClientFactory implements KeyVaultClientFactory { - @Override - public KeyVaultType getType() { - return KeyVaultType.HASHICORP; - } -} From b5889555d0fc12a2c7737baed6e9c6ee45ed6c28 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Thu, 13 Dec 2018 13:45:54 +0000 Subject: [PATCH 50/57] Add keygen CLI options for keystores and secret engine name --- .../tessera/config/cli/DefaultCliAdapter.java | 14 +++---- .../cli/parsers/KeyGenerationParser.java | 26 +++++++------ .../generation/AzureVaultKeyGenerator.java | 2 +- .../DefaultKeyGeneratorFactory.java | 9 +---- .../key/generation/FileKeyGenerator.java | 38 ++++++------------- .../HashicorpVaultKeyGenerator.java | 8 ++-- .../tessera/key/generation/KeyGenerator.java | 2 +- .../key/generation/KeyVaultOptions.java | 13 +++++++ .../AzureVaultKeyGeneratorTest.java | 12 +++--- .../key/generation/FileKeyGeneratorTest.java | 14 +++---- .../HashicorpVaultKeyGeneratorTest.java | 30 ++++++++++++--- .../key/generation/KeyVaultOptionsTest.java | 18 +++++++++ .../MockAzureKeyVaultServiceFactory.java | 3 +- .../keypairconverter/KeyPairConverter.java | 2 +- 14 files changed, 114 insertions(+), 77 deletions(-) create mode 100644 key-generation/src/main/java/com/quorum/tessera/key/generation/KeyVaultOptions.java create mode 100644 key-generation/src/test/java/com/quorum/tessera/key/generation/KeyVaultOptionsTest.java diff --git a/config-cli/src/main/java/com/quorum/tessera/config/cli/DefaultCliAdapter.java b/config-cli/src/main/java/com/quorum/tessera/config/cli/DefaultCliAdapter.java index 0ae85e1113..d5fb07e629 100644 --- a/config-cli/src/main/java/com/quorum/tessera/config/cli/DefaultCliAdapter.java +++ b/config-cli/src/main/java/com/quorum/tessera/config/cli/DefaultCliAdapter.java @@ -190,8 +190,8 @@ private Options buildBaseOptions() { ); options.addOption( - Option.builder("keygenvaultcert") - .desc("TLS certificate for Hashicorp Vault authentication") + Option.builder("keygenvaultkeystore") + .desc("Path to JKS keystore for TLS Hashicorp Vault communication") .hasArg() .optionalArg(false) .argName("PATH") @@ -199,8 +199,8 @@ private Options buildBaseOptions() { ); options.addOption( - Option.builder("keygenvaultcertkey") - .desc("TLS key for Hashicorp Vault authentication") + Option.builder("keygenvaulttruststore") + .desc("Path to JKS truststore for TLS Hashicorp Vault communication") .hasArg() .optionalArg(false) .argName("PATH") @@ -208,11 +208,11 @@ private Options buildBaseOptions() { ); options.addOption( - Option.builder("keygenvaultservercert") - .desc("TLS certificate of Vault server for Hashicorp Vault authentication") + Option.builder("keygenvaultsecretengine") + .desc("Name of already enabled Hashicorp v2 kv secret engine") .hasArg() .optionalArg(false) - .argName("PATH") + .argName("STRING") .build() ); diff --git a/config-cli/src/main/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParser.java b/config-cli/src/main/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParser.java index 041c9d41a3..909273724b 100644 --- a/config-cli/src/main/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParser.java +++ b/config-cli/src/main/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParser.java @@ -6,6 +6,7 @@ import com.quorum.tessera.config.util.JaxbUtil; import com.quorum.tessera.key.generation.KeyGenerator; import com.quorum.tessera.key.generation.KeyGeneratorFactory; +import com.quorum.tessera.key.generation.KeyVaultOptions; import org.apache.commons.cli.CommandLine; import javax.validation.ConstraintViolation; @@ -39,6 +40,7 @@ public class KeyGenerationParser implements Parser> { public List parse(final CommandLine commandLine) throws IOException { final ArgonOptions argonOptions = this.argonOptions(commandLine).orElse(null); + final KeyVaultOptions keyVaultOptions = this.keyVaultOptions(commandLine).orElse(null); final KeyVaultConfig keyVaultConfig = this.keyVaultConfig(commandLine).orElse(null); final KeyGenerator generator = factory.create(keyVaultConfig); @@ -46,7 +48,7 @@ public List parse(final CommandLine commandLine) throws IOExcepti if (commandLine.hasOption("keygen")) { return this.filenames(commandLine) .stream() - .map(name -> generator.generate(name, argonOptions)) + .map(name -> generator.generate(name, argonOptions, keyVaultOptions)) .collect(Collectors.toList()); } @@ -66,6 +68,12 @@ private Optional argonOptions(final CommandLine commandLine) throw return Optional.empty(); } + private Optional keyVaultOptions(final CommandLine commandLine) { + Optional secretEngineName = Optional.ofNullable(commandLine.getOptionValue("keygenvaultsecretengine")); + + return secretEngineName.map(KeyVaultOptions::new); + } + private List filenames(final CommandLine commandLine) { if (commandLine.hasOption("filename")) { @@ -114,21 +122,17 @@ private Optional keyVaultConfig(CommandLine commandLine) { String approlePath = commandLine.getOptionValue("keygenvaultapprole"); - Optional tlsCertificatePath = Optional.ofNullable(commandLine.getOptionValue("keygenvaultcert")) - .map(Paths::get); - - Optional tlsKeyPath = Optional.ofNullable(commandLine.getOptionValue("keygenvaultcertkey")) - .map(Paths::get); + Optional tlsKeyStorePath = Optional.ofNullable(commandLine.getOptionValue("keygenvaultkeystore")) + .map(Paths::get); - Optional tlsServerCertificatePath = Optional.ofNullable(commandLine.getOptionValue("keygenvaultservercert")) - .map(Paths::get); + Optional tlsTrustStorePath = Optional.ofNullable(commandLine.getOptionValue("keygenvaulttruststore")) + .map(Paths::get); keyVaultConfig = new HashicorpKeyVaultConfig( keyVaultUrl, approlePath, - tlsCertificatePath.orElse(null), - tlsKeyPath.orElse(null), - tlsServerCertificatePath.orElse(null) + tlsKeyStorePath.orElse(null), + tlsTrustStorePath.orElse(null) ); Set> violations = validator.validate((HashicorpKeyVaultConfig)keyVaultConfig); diff --git a/key-generation/src/main/java/com/quorum/tessera/key/generation/AzureVaultKeyGenerator.java b/key-generation/src/main/java/com/quorum/tessera/key/generation/AzureVaultKeyGenerator.java index dc5cfe785d..733747fe75 100644 --- a/key-generation/src/main/java/com/quorum/tessera/key/generation/AzureVaultKeyGenerator.java +++ b/key-generation/src/main/java/com/quorum/tessera/key/generation/AzureVaultKeyGenerator.java @@ -28,7 +28,7 @@ public AzureVaultKeyGenerator(final NaclFacade nacl, KeyVaultService keyVaultSer } @Override - public AzureVaultKeyPair generate(String filename, ArgonOptions encryptionOptions) { + public AzureVaultKeyPair generate(String filename, ArgonOptions encryptionOptions, KeyVaultOptions keyVaultOptions) { final KeyPair keys = this.nacl.generateNewKeys(); final StringBuilder publicId = new StringBuilder(); diff --git a/key-generation/src/main/java/com/quorum/tessera/key/generation/DefaultKeyGeneratorFactory.java b/key-generation/src/main/java/com/quorum/tessera/key/generation/DefaultKeyGeneratorFactory.java index 380f3362ad..9b738e0197 100644 --- a/key-generation/src/main/java/com/quorum/tessera/key/generation/DefaultKeyGeneratorFactory.java +++ b/key-generation/src/main/java/com/quorum/tessera/key/generation/DefaultKeyGeneratorFactory.java @@ -4,7 +4,6 @@ import com.quorum.tessera.config.keys.KeyEncryptorFactory; import com.quorum.tessera.config.util.EnvironmentVariableProvider; import com.quorum.tessera.config.util.PasswordReaderFactory; -import com.quorum.tessera.key.vault.KeyVaultClientFactory; import com.quorum.tessera.key.vault.KeyVaultService; import com.quorum.tessera.key.vault.KeyVaultServiceFactory; import com.quorum.tessera.nacl.NaclFacadeFactory; @@ -25,9 +24,7 @@ public KeyGenerator create(KeyVaultConfig keyVaultConfig) { config.setKeys(keyConfiguration); - KeyVaultClientFactory keyVaultClientFactory = KeyVaultClientFactory.getInstance(KeyVaultType.AZURE); - - final KeyVaultService keyVaultService = keyVaultServiceFactory.create(config, new EnvironmentVariableProvider(), keyVaultClientFactory); + final KeyVaultService keyVaultService = keyVaultServiceFactory.create(config, new EnvironmentVariableProvider()); return new AzureVaultKeyGenerator(NaclFacadeFactory.newFactory().create(), keyVaultService); @@ -36,9 +33,7 @@ public KeyGenerator create(KeyVaultConfig keyVaultConfig) { config.setKeys(keyConfiguration); - KeyVaultClientFactory keyVaultClientFactory = KeyVaultClientFactory.getInstance(KeyVaultType.HASHICORP); - - final KeyVaultService keyVaultService = keyVaultServiceFactory.create(config, new EnvironmentVariableProvider(), keyVaultClientFactory); + final KeyVaultService keyVaultService = keyVaultServiceFactory.create(config, new EnvironmentVariableProvider()); return new HashicorpVaultKeyGenerator(NaclFacadeFactory.newFactory().create(), keyVaultService); } diff --git a/key-generation/src/main/java/com/quorum/tessera/key/generation/FileKeyGenerator.java b/key-generation/src/main/java/com/quorum/tessera/key/generation/FileKeyGenerator.java index 78d2df9ff8..8c190c651a 100644 --- a/key-generation/src/main/java/com/quorum/tessera/key/generation/FileKeyGenerator.java +++ b/key-generation/src/main/java/com/quorum/tessera/key/generation/FileKeyGenerator.java @@ -5,8 +5,8 @@ import com.quorum.tessera.config.keys.KeyEncryptor; import com.quorum.tessera.config.util.JaxbUtil; import com.quorum.tessera.config.util.PasswordReader; -import com.quorum.tessera.io.IOCallback; import com.quorum.tessera.encryption.KeyPair; +import com.quorum.tessera.io.IOCallback; import com.quorum.tessera.nacl.NaclFacade; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,7 +39,7 @@ public FileKeyGenerator(final NaclFacade nacl, final KeyEncryptor keyEncryptor, } @Override - public FilesystemKeyPair generate(final String filename, final ArgonOptions encryptionOptions) { + public FilesystemKeyPair generate(final String filename, final ArgonOptions encryptionOptions, final KeyVaultOptions keyVaultOptions) { final String password = this.passwordReader.requestUserPassword(); @@ -47,7 +47,7 @@ public FilesystemKeyPair generate(final String filename, final ArgonOptions encr final String publicKeyBase64 = Base64.getEncoder().encodeToString(generated.getPublicKey().getKeyBytes()); - final KeyData finalKeys; + final KeyData finalKeys = new KeyData(); if (!password.isEmpty()) { @@ -55,7 +55,7 @@ public FilesystemKeyPair generate(final String filename, final ArgonOptions encr generated.getPrivateKey(), password, encryptionOptions ); - finalKeys = new KeyData( + finalKeys.setConfig( new KeyDataConfig( new PrivateKeyData( null, @@ -66,39 +66,25 @@ public FilesystemKeyPair generate(final String filename, final ArgonOptions encr null ), PrivateKeyType.LOCKED - ), - generated.getPrivateKey().toString(), - publicKeyBase64, - null, - null, - null, - null, - null, - null, - null + ) ); + finalKeys.setPrivateKey(generated.getPrivateKey().toString()); + finalKeys.setPublicKey(publicKeyBase64); LOGGER.info("Newly generated private key has been encrypted"); } else { String keyData = Base64.getEncoder().encodeToString(generated.getPrivateKey().getKeyBytes()); - - finalKeys = new KeyData( + + finalKeys.setConfig( new KeyDataConfig( new PrivateKeyData(keyData, null, null, null, null, null), PrivateKeyType.UNLOCKED - ), - generated.getPrivateKey().toString(), - publicKeyBase64, - null, - null, - null, - null, - null, - null, - null + ) ); + finalKeys.setPrivateKey(generated.getPrivateKey().toString()); + finalKeys.setPublicKey(publicKeyBase64); } diff --git a/key-generation/src/main/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGenerator.java b/key-generation/src/main/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGenerator.java index 3311ead318..dc35b18757 100644 --- a/key-generation/src/main/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGenerator.java +++ b/key-generation/src/main/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGenerator.java @@ -26,8 +26,10 @@ public HashicorpVaultKeyGenerator(final NaclFacade nacl, KeyVaultService keyVaul } @Override - public HashicorpVaultKeyPair generate(String filename, ArgonOptions encryptionOptions) { + public HashicorpVaultKeyPair generate(String filename, ArgonOptions encryptionOptions, KeyVaultOptions keyVaultOptions) { Objects.requireNonNull(filename); + Objects.requireNonNull(keyVaultOptions); + Objects.requireNonNull(keyVaultOptions.getSecretEngineName()); final KeyPair keys = this.nacl.generateNewKeys(); @@ -37,7 +39,7 @@ public HashicorpVaultKeyPair generate(String filename, ArgonOptions encryptionOp keyPairData.put(pubId, keys.getPublicKey().encodeToBase64()); keyPairData.put(privId, keys.getPrivateKey().encodeToBase64()); - SetSecretData setSecretData = new HashicorpSetSecretData(filename, keyPairData); + SetSecretData setSecretData = new HashicorpSetSecretData(keyVaultOptions.getSecretEngineName(), filename, keyPairData); keyVaultService.setSecret(setSecretData); LOGGER.debug("Key {} saved to vault with path {} and id {}", keyPairData.get(pubId), filename, pubId); @@ -45,6 +47,6 @@ public HashicorpVaultKeyPair generate(String filename, ArgonOptions encryptionOp LOGGER.debug("Key {} saved to vault with path {} and id {}", keyPairData.get(privId), filename, privId); LOGGER.info("Key saved to vault with path {} and id {}", filename, privId); - return new HashicorpVaultKeyPair(pubId, privId, filename); + return new HashicorpVaultKeyPair(pubId, privId, keyVaultOptions.getSecretEngineName(), filename); } } diff --git a/key-generation/src/main/java/com/quorum/tessera/key/generation/KeyGenerator.java b/key-generation/src/main/java/com/quorum/tessera/key/generation/KeyGenerator.java index 1b95d8b30c..c2ba39e6f7 100644 --- a/key-generation/src/main/java/com/quorum/tessera/key/generation/KeyGenerator.java +++ b/key-generation/src/main/java/com/quorum/tessera/key/generation/KeyGenerator.java @@ -5,6 +5,6 @@ public interface KeyGenerator { - ConfigKeyPair generate(String filename, ArgonOptions encryptionOptions); + ConfigKeyPair generate(String filename, ArgonOptions encryptionOptions, KeyVaultOptions keyVaultOptions); } diff --git a/key-generation/src/main/java/com/quorum/tessera/key/generation/KeyVaultOptions.java b/key-generation/src/main/java/com/quorum/tessera/key/generation/KeyVaultOptions.java new file mode 100644 index 0000000000..738c879eb5 --- /dev/null +++ b/key-generation/src/main/java/com/quorum/tessera/key/generation/KeyVaultOptions.java @@ -0,0 +1,13 @@ +package com.quorum.tessera.key.generation; + +public class KeyVaultOptions { + private String secretEngineName; + + public KeyVaultOptions(String secretEngineName) { + this.secretEngineName = secretEngineName; + } + + public String getSecretEngineName() { + return secretEngineName; + } +} diff --git a/key-generation/src/test/java/com/quorum/tessera/key/generation/AzureVaultKeyGeneratorTest.java b/key-generation/src/test/java/com/quorum/tessera/key/generation/AzureVaultKeyGeneratorTest.java index 4ea799654e..a6fa763f0f 100644 --- a/key-generation/src/test/java/com/quorum/tessera/key/generation/AzureVaultKeyGeneratorTest.java +++ b/key-generation/src/test/java/com/quorum/tessera/key/generation/AzureVaultKeyGeneratorTest.java @@ -48,7 +48,7 @@ public void keysSavedInVaultWithProvidedVaultIdAndCorrectSuffix() { final String pubVaultId = vaultId + "Pub"; final String privVaultId = vaultId + "Key"; - final AzureVaultKeyPair result = azureVaultKeyGenerator.generate(vaultId, null); + final AzureVaultKeyPair result = azureVaultKeyGenerator.generate(vaultId, null, null); final ArgumentCaptor captor = ArgumentCaptor.forClass(AzureSetSecretData.class); @@ -76,7 +76,7 @@ public void vaultIdIsFinalComponentOfFilePath() { final String privVaultId = vaultId + "Key"; final String path = "/some/path/" + vaultId; - azureVaultKeyGenerator.generate(path, null); + azureVaultKeyGenerator.generate(path, null, null); final ArgumentCaptor captor = ArgumentCaptor.forClass(AzureSetSecretData.class); @@ -95,7 +95,7 @@ public void vaultIdIsFinalComponentOfFilePath() { @Test public void ifNoVaultIdProvidedThenSuffixOnlyIsUsed() { - azureVaultKeyGenerator.generate(null, null); + azureVaultKeyGenerator.generate(null, null, null); final ArgumentCaptor captor = ArgumentCaptor.forClass(AzureSetSecretData.class); @@ -116,7 +116,7 @@ public void ifNoVaultIdProvidedThenSuffixOnlyIsUsed() { public void allowedCharactersUsedInVaultIdDoesNotThrowException() { final String allowedId = "abcdefghijklmnopqrstuvwxyz-ABCDEFDGHIJKLMNOPQRSTUVWXYZ-0123456789"; - azureVaultKeyGenerator.generate(allowedId, null); + azureVaultKeyGenerator.generate(allowedId, null, null); verify(keyVaultService, times(2)).setSecret(any(AzureSetSecretData.class)); } @@ -126,7 +126,7 @@ public void exceptionThrownIfDisallowedCharactersUsedInVaultId() { final String invalidId = "/tmp/abc@+"; final Throwable throwable = catchThrowable( - () -> azureVaultKeyGenerator.generate(invalidId, null) + () -> azureVaultKeyGenerator.generate(invalidId, null, null) ); assertThat(throwable).isInstanceOf(UnsupportedCharsetException.class); @@ -139,7 +139,7 @@ public void exceptionThrownIfDisallowedCharactersUsedInVaultId() { public void encryptionIsNotUsedWhenSavingToVault() { final ArgonOptions argonOptions = mock(ArgonOptions.class); - azureVaultKeyGenerator.generate("vaultId", argonOptions); + azureVaultKeyGenerator.generate("vaultId", argonOptions, null); verifyNoMoreInteractions(argonOptions); } diff --git a/key-generation/src/test/java/com/quorum/tessera/key/generation/FileKeyGeneratorTest.java b/key-generation/src/test/java/com/quorum/tessera/key/generation/FileKeyGeneratorTest.java index 7a9820cbad..c1cf4f6995 100644 --- a/key-generation/src/test/java/com/quorum/tessera/key/generation/FileKeyGeneratorTest.java +++ b/key-generation/src/test/java/com/quorum/tessera/key/generation/FileKeyGeneratorTest.java @@ -8,6 +8,8 @@ import com.quorum.tessera.config.keys.KeyEncryptor; import com.quorum.tessera.config.util.PasswordReader; import com.quorum.tessera.encryption.KeyPair; +import com.quorum.tessera.encryption.PrivateKey; +import com.quorum.tessera.encryption.PublicKey; import com.quorum.tessera.nacl.NaclFacade; import org.junit.After; import org.junit.Before; @@ -21,8 +23,6 @@ import java.util.UUID; import static com.quorum.tessera.config.PrivateKeyType.UNLOCKED; -import com.quorum.tessera.encryption.PrivateKey; -import com.quorum.tessera.encryption.PublicKey; import static java.nio.charset.StandardCharsets.UTF_8; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; @@ -74,7 +74,7 @@ public void generateFromKeyDataUnlockedPrivateKey() throws IOException { String filename = UUID.randomUUID().toString(); - final FilesystemKeyPair generated = generator.generate(filename, null); + final FilesystemKeyPair generated = generator.generate(filename, null, null); assertThat(generated).isInstanceOf(FilesystemKeyPair.class); assertThat(generated.getPublicKey()).isEqualTo("cHVibGljS2V5"); @@ -114,7 +114,7 @@ public void generateFromKeyDataLockedPrivateKey() throws IOException { doReturn(encryptedKey).when(keyEncryptor).encryptPrivateKey(any(PrivateKey.class), anyString(), eq(null)); - final FilesystemKeyPair generated = generator.generate(keyFilesName, null); + final FilesystemKeyPair generated = generator.generate(keyFilesName, null, null); final KeyDataConfig pkd = generated.getInlineKeypair().getPrivateKeyConfig(); assertThat(generated.getPublicKey()).isEqualTo("cHVibGljS2V5"); @@ -134,7 +134,7 @@ public void providingPathSavesToFile() throws IOException { doReturn(keyPair).when(nacl).generateNewKeys(); - final FilesystemKeyPair generated = generator.generate(keyFilesName, null); + final FilesystemKeyPair generated = generator.generate(keyFilesName, null, null); assertThat(Files.exists(tempFolder.resolve("providingPathSavesToFile.pub"))).isTrue(); assertThat(Files.exists(tempFolder.resolve("providingPathSavesToFile.key"))).isTrue(); @@ -149,7 +149,7 @@ public void providingNoPathSavesToFileInSameDirectory() throws IOException { doReturn(keyPair).when(nacl).generateNewKeys(); - final FilesystemKeyPair generated = generator.generate("", null); + final FilesystemKeyPair generated = generator.generate("", null, null); assertThat(Files.exists(Paths.get(".pub"))).isTrue(); assertThat(Files.exists(Paths.get(".key"))).isTrue(); @@ -172,7 +172,7 @@ public void providingPathThatExistsThrowsError() throws IOException { .when(keyEncryptor) .encryptPrivateKey(any(PrivateKey.class), anyString(), eq(null)); - final Throwable throwable = catchThrowable(() -> generator.generate(keyFilesName, null)); + final Throwable throwable = catchThrowable(() -> generator.generate(keyFilesName, null, null)); assertThat(throwable).isInstanceOf(UncheckedIOException.class); diff --git a/key-generation/src/test/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGeneratorTest.java b/key-generation/src/test/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGeneratorTest.java index f0efbf0f11..0cebb3fc1f 100644 --- a/key-generation/src/test/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGeneratorTest.java +++ b/key-generation/src/test/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGeneratorTest.java @@ -42,16 +42,36 @@ public void setUp() { @Test(expected = NullPointerException.class) public void nullFilenameThrowsException() { - hashicorpVaultKeyGenerator.generate(null, null); + KeyVaultOptions keyVaultOptions = mock(KeyVaultOptions.class); + when(keyVaultOptions.getSecretEngineName()).thenReturn("secretEngine"); + + hashicorpVaultKeyGenerator.generate(null, null, keyVaultOptions); + } + + @Test(expected = NullPointerException.class) + public void nullKeyVaultOptionsThrowsException() { + hashicorpVaultKeyGenerator.generate("filename", null, null); + } + + @Test(expected = NullPointerException.class) + public void nullSecretEngineNameThrowsException() { + KeyVaultOptions keyVaultOptions = mock(KeyVaultOptions.class); + when(keyVaultOptions.getSecretEngineName()).thenReturn(null); + + hashicorpVaultKeyGenerator.generate("filename", null, keyVaultOptions); } @Test public void generatedKeyPairIsSavedToSpecifiedPathInVaultWithIds() { - String filename = "secret/path"; + String secretEngine = "secretEngine"; + String filename = "secretName"; + + KeyVaultOptions keyVaultOptions = mock(KeyVaultOptions.class); + when(keyVaultOptions.getSecretEngineName()).thenReturn(secretEngine); - HashicorpVaultKeyPair result = hashicorpVaultKeyGenerator.generate(filename, null); + HashicorpVaultKeyPair result = hashicorpVaultKeyGenerator.generate(filename, null, keyVaultOptions); - HashicorpVaultKeyPair expected = new HashicorpVaultKeyPair("publicKey", "privateKey", filename); + HashicorpVaultKeyPair expected = new HashicorpVaultKeyPair("publicKey", "privateKey", secretEngine, filename); assertThat(result).isEqualToComparingFieldByField(expected); final ArgumentCaptor captor = ArgumentCaptor.forClass(HashicorpSetSecretData.class); @@ -64,7 +84,7 @@ public void generatedKeyPairIsSavedToSpecifiedPathInVaultWithIds() { expectedNameValuePairs.put("publicKey", pub.encodeToBase64()); expectedNameValuePairs.put("privateKey", priv.encodeToBase64()); - HashicorpSetSecretData expectedData = new HashicorpSetSecretData(filename, expectedNameValuePairs); + HashicorpSetSecretData expectedData = new HashicorpSetSecretData(secretEngine, filename, expectedNameValuePairs); assertThat(capturedArg).isEqualToComparingFieldByFieldRecursively(expectedData); diff --git a/key-generation/src/test/java/com/quorum/tessera/key/generation/KeyVaultOptionsTest.java b/key-generation/src/test/java/com/quorum/tessera/key/generation/KeyVaultOptionsTest.java new file mode 100644 index 0000000000..9486507349 --- /dev/null +++ b/key-generation/src/test/java/com/quorum/tessera/key/generation/KeyVaultOptionsTest.java @@ -0,0 +1,18 @@ +package com.quorum.tessera.key.generation; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class KeyVaultOptionsTest { + + @Test + public void getters() { + String secretEngineName = "secretEngineName"; + + KeyVaultOptions keyVaultOptions = new KeyVaultOptions(secretEngineName); + + assertThat(keyVaultOptions.getSecretEngineName()).isEqualTo(secretEngineName); + } + +} diff --git a/key-generation/src/test/java/com/quorum/tessera/key/generation/MockAzureKeyVaultServiceFactory.java b/key-generation/src/test/java/com/quorum/tessera/key/generation/MockAzureKeyVaultServiceFactory.java index ae0eeba0ac..da258e4b7f 100644 --- a/key-generation/src/test/java/com/quorum/tessera/key/generation/MockAzureKeyVaultServiceFactory.java +++ b/key-generation/src/test/java/com/quorum/tessera/key/generation/MockAzureKeyVaultServiceFactory.java @@ -4,7 +4,6 @@ import com.quorum.tessera.config.KeyVaultType; import com.quorum.tessera.config.util.EnvironmentVariableProvider; import com.quorum.tessera.config.vault.data.AzureGetSecretData; -import com.quorum.tessera.key.vault.KeyVaultClientFactory; import com.quorum.tessera.key.vault.KeyVaultService; import com.quorum.tessera.key.vault.KeyVaultServiceFactory; @@ -14,7 +13,7 @@ public class MockAzureKeyVaultServiceFactory implements KeyVaultServiceFactory { @Override - public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, KeyVaultClientFactory keyVaultClientFactory) { + public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider) { KeyVaultService mock = mock(KeyVaultService.class); when(mock.getSecret(any(AzureGetSecretData.class))) diff --git a/key-pair-converter/src/main/java/com/quorum/tessera/keypairconverter/KeyPairConverter.java b/key-pair-converter/src/main/java/com/quorum/tessera/keypairconverter/KeyPairConverter.java index 5eb8fd6fcd..997eff7bc8 100644 --- a/key-pair-converter/src/main/java/com/quorum/tessera/keypairconverter/KeyPairConverter.java +++ b/key-pair-converter/src/main/java/com/quorum/tessera/keypairconverter/KeyPairConverter.java @@ -32,7 +32,7 @@ public KeyPairConverter(Config config, EnvironmentVariableProvider envProvider) this.envProvider = envProvider; } - Collection convert(Collection configKeyPairs) { + public Collection convert(Collection configKeyPairs) { return configKeyPairs .stream() .map(this::convert) From 2459912c208cfa4954c5c5cec2d9082205baec10 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Fri, 14 Dec 2018 08:43:17 +0000 Subject: [PATCH 51/57] Remove unnecessary classes and fix tests broken after previous changes --- .../config/cli/DefaultCliAdapterTest.java | 10 +++--- .../tessera/config/cli/OverrideUtilTest.java | 10 +++--- .../cli/parsers/KeyGenerationParserTest.java | 33 +++++++++---------- .../util/EnvironmentVariableProvider.java | 6 +++- .../util/EnvironmentVariableProviderTest.java | 8 +++++ .../AzureKeyVaultServiceFactoryTest.java | 16 ++++----- key-vault/hashicorp-key-vault/pom.xml | 10 ++---- .../key/vault/hashicorp/SslConfigFactory.java | 11 ------- .../key/vault/hashicorp/VaultCallback.java | 21 ------------ .../vault/hashicorp/VaultConfigFactory.java | 11 ------- .../vault/hashicorp/SslConfigFactoryTest.java | 19 ----------- .../vault/hashicorp/VaultCallbackTest.java | 22 ------------- .../hashicorp/VaultConfigFactoryTest.java | 19 ----------- 13 files changed, 51 insertions(+), 145 deletions(-) delete mode 100644 key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/SslConfigFactory.java delete mode 100644 key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/VaultCallback.java delete mode 100644 key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/VaultConfigFactory.java delete mode 100644 key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/SslConfigFactoryTest.java delete mode 100644 key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/VaultCallbackTest.java delete mode 100644 key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/VaultConfigFactoryTest.java diff --git a/config-cli/src/test/java/com/quorum/tessera/config/cli/DefaultCliAdapterTest.java b/config-cli/src/test/java/com/quorum/tessera/config/cli/DefaultCliAdapterTest.java index 9992ecf1a7..765a835071 100644 --- a/config-cli/src/test/java/com/quorum/tessera/config/cli/DefaultCliAdapterTest.java +++ b/config-cli/src/test/java/com/quorum/tessera/config/cli/DefaultCliAdapterTest.java @@ -122,7 +122,7 @@ public void keygenWithConfig() throws Exception { Files.write(publicKeyPath, Arrays.asList("SOMEDATA")); FilesystemKeyPair keypair = new FilesystemKeyPair(publicKeyPath, privateKeyPath); - when(keyGenerator.generate(anyString(), eq(null))).thenReturn(keypair); + when(keyGenerator.generate(anyString(), eq(null), eq(null))).thenReturn(keypair); Path unixSocketPath = Files.createTempFile(UUID.randomUUID().toString(), ".ipc"); @@ -143,7 +143,7 @@ public void keygenWithConfig() throws Exception { assertThat(result.getConfig()).isNotNull(); assertThat(result.isSuppressStartup()).isFalse(); - verify(keyGenerator).generate(anyString(), eq(null)); + verify(keyGenerator).generate(anyString(), eq(null), eq(null)); verifyNoMoreInteractions(keyGenerator); } @@ -191,7 +191,7 @@ public void output() throws Exception { Files.write(publicKeyPath, Arrays.asList("SOMEDATA")); FilesystemKeyPair keypair = new FilesystemKeyPair(publicKeyPath, privateKeyPath); - when(keyGenerator.generate(anyString(), eq(null))).thenReturn(keypair); + when(keyGenerator.generate(anyString(), eq(null), eq(null))).thenReturn(keypair); Path generatedKey = Paths.get("/tmp/" + UUID.randomUUID().toString()); @@ -376,7 +376,7 @@ public void allowStartupForKeygenAndConfigfileOptions() throws Exception { Files.write(publicKeyPath, Arrays.asList("SOMEDATA")); FilesystemKeyPair keypair = new FilesystemKeyPair(publicKeyPath, privateKeyPath); - when(keyGenerator.generate(anyString(), eq(null))).thenReturn(keypair); + when(keyGenerator.generate(anyString(), eq(null), eq(null))).thenReturn(keypair); final Path configFile = createAndPopulatePaths(getClass().getResource("/sample-config.json")); @@ -389,7 +389,7 @@ public void allowStartupForKeygenAndConfigfileOptions() throws Exception { public void suppressStartupForKeygenAndVaultUrlAndConfigfileOptions() throws Exception { final KeyGenerator keyGenerator = MockKeyGeneratorFactory.getMockKeyGenerator(); final FilesystemKeyPair keypair = new FilesystemKeyPair(Paths.get(""), Paths.get("")); - when(keyGenerator.generate(anyString(), eq(null))).thenReturn(keypair); + when(keyGenerator.generate(anyString(), eq(null), eq(null))).thenReturn(keypair); final Path configFile = createAndPopulatePaths(getClass().getResource("/sample-config.json")); final String vaultUrl = "https://test.vault.azure.net"; diff --git a/config-cli/src/test/java/com/quorum/tessera/config/cli/OverrideUtilTest.java b/config-cli/src/test/java/com/quorum/tessera/config/cli/OverrideUtilTest.java index 4a0ca7637d..05bbd4f812 100644 --- a/config-cli/src/test/java/com/quorum/tessera/config/cli/OverrideUtilTest.java +++ b/config-cli/src/test/java/com/quorum/tessera/config/cli/OverrideUtilTest.java @@ -1,6 +1,9 @@ package com.quorum.tessera.config.cli; -import com.quorum.tessera.config.*; +import com.quorum.tessera.config.Config; +import com.quorum.tessera.config.KeyConfiguration; +import com.quorum.tessera.config.Peer; +import com.quorum.tessera.config.SslAuthenticationMode; import com.quorum.tessera.config.util.JaxbUtil; import org.junit.Ignore; import org.junit.Test; @@ -43,9 +46,8 @@ public void buildOptions() { "keys.keyData.privateKeyPath", "keys.azureKeyVaultConfig.url", "keys.hashicorpKeyVaultConfig.approlePath", - "keys.hashicorpKeyVaultConfig.tlsCertificatePath", - "keys.hashicorpKeyVaultConfig.tlsServerCertificatePath", - "keys.hashicorpKeyVaultConfig.tlsKeyPath", + "keys.hashicorpKeyVaultConfig.tlsKeyStorePath", + "keys.hashicorpKeyVaultConfig.tlsTrustStorePath", "keys.hashicorpKeyVaultConfig.url", "alwaysSendTo", "unixSocketFile", diff --git a/config-cli/src/test/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParserTest.java b/config-cli/src/test/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParserTest.java index 8f624d1340..d0c98e161f 100644 --- a/config-cli/src/test/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParserTest.java +++ b/config-cli/src/test/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParserTest.java @@ -45,7 +45,7 @@ public void notProvidingArgonOptionsGivesNull() throws Exception { final KeyGenerator keyGenerator = MockKeyGeneratorFactory.getMockKeyGenerator(); final ArgumentCaptor captor = ArgumentCaptor.forClass(ArgonOptions.class); - verify(keyGenerator).generate(eq(keyLocation.toString()), captor.capture()); + verify(keyGenerator).generate(eq(keyLocation.toString()), captor.capture(), eq(null)); assertThat(captor.getAllValues()).hasSize(1); assertThat(captor.getValue()).isNull(); @@ -72,7 +72,7 @@ public void providingArgonOptionsGetSentCorrectly() throws Exception { final KeyGenerator keyGenerator = MockKeyGeneratorFactory.getMockKeyGenerator(); final ArgumentCaptor captor = ArgumentCaptor.forClass(ArgonOptions.class); - verify(keyGenerator).generate(eq(keyLocation.toString()), captor.capture()); + verify(keyGenerator).generate(eq(keyLocation.toString()), captor.capture(), eq(null)); assertThat(captor.getAllValues()).hasSize(1); assertThat(captor.getValue().getAlgorithm()).isEqualTo("id"); @@ -93,7 +93,7 @@ public void keygenWithNoName() throws Exception { assertThat(result).isNotNull().hasSize(1); final KeyGenerator keyGenerator = MockKeyGeneratorFactory.getMockKeyGenerator(); - verify(keyGenerator).generate("", null); + verify(keyGenerator).generate("", null, null); } @Test @@ -229,22 +229,22 @@ public void ifAllVaultOptionsAndFilenameProvidedForHashicorpThenOkay() throws Ex when(commandLine.getOptionValue("keygenvaulttype")).thenReturn("HASHICORP"); when(commandLine.getOptionValue("filename")).thenReturn("secret/path"); when(commandLine.getOptionValue("keygenvaultapprole")).thenReturn("approle"); + when(commandLine.getOptionValue("keygenvaultsecretengine")).thenReturn("secretEngine"); Path tempPath = Files.createTempFile(UUID.randomUUID().toString(), ""); tempPath.toFile().deleteOnExit(); - when(commandLine.getOptionValue("keygenvaultcert")).thenReturn(tempPath.toString()); - when(commandLine.getOptionValue("keygenvaultcertkey")).thenReturn(tempPath.toString()); - when(commandLine.getOptionValue("keygenvaultservercert")).thenReturn(tempPath.toString()); + when(commandLine.getOptionValue("keygenvaultkeystore")).thenReturn(tempPath.toString()); + when(commandLine.getOptionValue("keygenvaulttruststore")).thenReturn(tempPath.toString()); this.parser.parse(commandLine); verify(commandLine, times(1)).getOptionValue("keygenvaulttype"); verify(commandLine, times(1)).getOptionValue("keygenvaulturl"); verify(commandLine, times(1)).getOptionValue("keygenvaultapprole"); - verify(commandLine, times(1)).getOptionValue("keygenvaultcert"); - verify(commandLine, times(1)).getOptionValue("keygenvaultcertkey"); - verify(commandLine, times(1)).getOptionValue("keygenvaultservercert"); + verify(commandLine, times(1)).getOptionValue("keygenvaultkeystore"); + verify(commandLine, times(1)).getOptionValue("keygenvaulttruststore"); + verify(commandLine, times(1)).getOptionValue("keygenvaultsecretengine"); } @Test @@ -255,31 +255,30 @@ public void ifHashicorpTlsOptionsProvidedButPathsDontExistThenValidationExceptio when(commandLine.getOptionValue("keygenvaulturl")).thenReturn("someurl"); when(commandLine.hasOption("filename")).thenReturn(true); when(commandLine.getOptionValue("filename")).thenReturn("secret/path"); + when(commandLine.getOptionValue("keygenvaultsecretengine")).thenReturn("secretEngine"); when(commandLine.getOptionValue("keygenvaultapprole")).thenReturn("approle"); - when(commandLine.getOptionValue("keygenvaultcert")).thenReturn("non/existent/path"); - when(commandLine.getOptionValue("keygenvaultcertkey")).thenReturn("non/existent/path"); - when(commandLine.getOptionValue("keygenvaultservercert")).thenReturn("non/existent/path"); + when(commandLine.getOptionValue("keygenvaultkeystore")).thenReturn("non/existent/path"); + when(commandLine.getOptionValue("keygenvaulttruststore")).thenReturn("non/existent/path"); Throwable ex = catchThrowable(() -> this.parser.parse(commandLine)); verify(commandLine, times(1)).getOptionValue("keygenvaulttype"); verify(commandLine, times(1)).getOptionValue("keygenvaulturl"); verify(commandLine, times(1)).getOptionValue("keygenvaultapprole"); - verify(commandLine, times(1)).getOptionValue("keygenvaultcert"); - verify(commandLine, times(1)).getOptionValue("keygenvaultcertkey"); - verify(commandLine, times(1)).getOptionValue("keygenvaultservercert"); + verify(commandLine, times(1)).getOptionValue("keygenvaultkeystore"); + verify(commandLine, times(1)).getOptionValue("keygenvaulttruststore"); + verify(commandLine, times(1)).getOptionValue("keygenvaultsecretengine"); assertThat(ex).isInstanceOf(ConstraintViolationException.class); Set> violations = ((ConstraintViolationException) ex).getConstraintViolations(); - assertThat(violations.size()).isEqualTo(3); + assertThat(violations.size()).isEqualTo(2); Iterator> iterator = violations.iterator(); assertThat(iterator.next().getMessage()).isEqualTo("File does not exist"); assertThat(iterator.next().getMessage()).isEqualTo("File does not exist"); - assertThat(iterator.next().getMessage()).isEqualTo("File does not exist"); } } diff --git a/config/src/main/java/com/quorum/tessera/config/util/EnvironmentVariableProvider.java b/config/src/main/java/com/quorum/tessera/config/util/EnvironmentVariableProvider.java index 7641be5928..cdd64aefab 100644 --- a/config/src/main/java/com/quorum/tessera/config/util/EnvironmentVariableProvider.java +++ b/config/src/main/java/com/quorum/tessera/config/util/EnvironmentVariableProvider.java @@ -1,5 +1,7 @@ package com.quorum.tessera.config.util; +import java.util.Optional; + //Provide a mockable wrapper for environment variable retrieval public class EnvironmentVariableProvider { @@ -8,7 +10,9 @@ public String getEnv(String name) { } public char[] getEnvAsCharArray(String name) { - return System.getenv(name).toCharArray(); + return Optional.ofNullable(System.getenv(name)) + .map(String::toCharArray) + .orElse(null); } } diff --git a/config/src/test/java/com/quorum/tessera/config/util/EnvironmentVariableProviderTest.java b/config/src/test/java/com/quorum/tessera/config/util/EnvironmentVariableProviderTest.java index cc7e8d51ef..dac0620e47 100644 --- a/config/src/test/java/com/quorum/tessera/config/util/EnvironmentVariableProviderTest.java +++ b/config/src/test/java/com/quorum/tessera/config/util/EnvironmentVariableProviderTest.java @@ -14,4 +14,12 @@ public void getEnv() { //returns null as env variables not set in test environment assertThat(provider.getEnv("env")).isNull(); } + + @Test + public void getEnvAsCharArray() { + EnvironmentVariableProvider provider = new EnvironmentVariableProvider(); + + //returns null as env variables not set in test environment + assertThat(provider.getEnvAsCharArray("env")).isNull(); + } } diff --git a/key-vault/azure-key-vault/src/test/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactoryTest.java b/key-vault/azure-key-vault/src/test/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactoryTest.java index f021593d75..6f9f4da2cb 100644 --- a/key-vault/azure-key-vault/src/test/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactoryTest.java +++ b/key-vault/azure-key-vault/src/test/java/com/quorum/tessera/key/vault/azure/AzureKeyVaultServiceFactoryTest.java @@ -29,17 +29,17 @@ public void setUp() { @Test(expected = NullPointerException.class) public void nullConfigThrowsException() { - azureKeyVaultServiceFactory.create(null, envProvider, null); + azureKeyVaultServiceFactory.create(null, envProvider); } @Test(expected = NullPointerException.class) public void nullEnvVarProviderThrowsException() { - azureKeyVaultServiceFactory.create(config, null, null); + azureKeyVaultServiceFactory.create(config, null); } @Test public void clientIdEnvironmentVariableNotSetThrowsException() { - Throwable ex = catchThrowable(() -> azureKeyVaultServiceFactory.create(config, envProvider, null)); + Throwable ex = catchThrowable(() -> azureKeyVaultServiceFactory.create(config, envProvider)); when(envProvider.getEnv("AZURE_CLIENT_ID")).thenReturn(null); when(envProvider.getEnv("AZURE_CLIENT_SECRET")).thenReturn("secret"); @@ -50,7 +50,7 @@ public void clientIdEnvironmentVariableNotSetThrowsException() { @Test public void clientSecretEnvironmentVariableNotSetThrowsException() { - Throwable ex = catchThrowable(() -> azureKeyVaultServiceFactory.create(config, envProvider, null)); + Throwable ex = catchThrowable(() -> azureKeyVaultServiceFactory.create(config, envProvider)); when(envProvider.getEnv("AZURE_CLIENT_ID")).thenReturn("id"); when(envProvider.getEnv("AZURE_CLIENT_SECRET")).thenReturn(null); @@ -61,7 +61,7 @@ public void clientSecretEnvironmentVariableNotSetThrowsException() { @Test public void bothClientIdAndClientSecretEnvironmentVariablesNotSetThrowsException() { - Throwable ex = catchThrowable(() -> azureKeyVaultServiceFactory.create(config, envProvider, null)); + Throwable ex = catchThrowable(() -> azureKeyVaultServiceFactory.create(config, envProvider)); when(envProvider.getEnv("AZURE_CLIENT_ID")).thenReturn(null); when(envProvider.getEnv("AZURE_CLIENT_SECRET")).thenReturn(null); @@ -75,7 +75,7 @@ public void nullKeyConfigurationThrowsException() { when(envProvider.getEnv(anyString())).thenReturn("envVar"); when(config.getKeys()).thenReturn(null); - Throwable ex = catchThrowable(() -> azureKeyVaultServiceFactory.create(config, envProvider, null)); + Throwable ex = catchThrowable(() -> azureKeyVaultServiceFactory.create(config, envProvider)); assertThat(ex).isExactlyInstanceOf(ConfigException.class); assertThat(ex.getMessage()).contains("Trying to create Azure key vault connection but no Azure configuration provided"); @@ -88,7 +88,7 @@ public void nullKeyVaultConfigurationThrowsException() { when(keyConfiguration.getAzureKeyVaultConfig()).thenReturn(null); when(config.getKeys()).thenReturn(keyConfiguration); - Throwable ex = catchThrowable(() -> azureKeyVaultServiceFactory.create(config, envProvider, null)); + Throwable ex = catchThrowable(() -> azureKeyVaultServiceFactory.create(config, envProvider)); assertThat(ex).isExactlyInstanceOf(ConfigException.class); assertThat(ex.getMessage()).contains("Trying to create Azure key vault connection but no Azure configuration provided"); @@ -102,7 +102,7 @@ public void envVarsAndKeyVaultConfigProvidedCreatesAzureKeyVaultService() { when(keyConfiguration.getAzureKeyVaultConfig()).thenReturn(keyVaultConfig); when(config.getKeys()).thenReturn(keyConfiguration); - KeyVaultService result = azureKeyVaultServiceFactory.create(config, envProvider, null); + KeyVaultService result = azureKeyVaultServiceFactory.create(config, envProvider); assertThat(result).isInstanceOf(AzureKeyVaultService.class); } diff --git a/key-vault/hashicorp-key-vault/pom.xml b/key-vault/hashicorp-key-vault/pom.xml index a6fcdd0b9e..a3a00ab520 100644 --- a/key-vault/hashicorp-key-vault/pom.xml +++ b/key-vault/hashicorp-key-vault/pom.xml @@ -13,11 +13,6 @@ - - com.bettercloud - vault-java-driver - - com.quorum.tessera key-vault-api @@ -30,8 +25,9 @@ - io.netty - netty-all + com.squareup.okhttp3 + okhttp + 3.12.0 diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/SslConfigFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/SslConfigFactory.java deleted file mode 100644 index 7ef24f67db..0000000000 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/SslConfigFactory.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.quorum.tessera.key.vault.hashicorp; - -import com.bettercloud.vault.SslConfig; - -//Ensures a newly instantiated SslConfig object is used in the HashicorpKeyVaultClientFactory -class SslConfigFactory { - - public SslConfig create() { - return new SslConfig(); - } -} diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/VaultCallback.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/VaultCallback.java deleted file mode 100644 index a9fecfdb3d..0000000000 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/VaultCallback.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.quorum.tessera.key.vault.hashicorp; - -import com.bettercloud.vault.VaultException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@FunctionalInterface -public interface VaultCallback { - Logger LOGGER = LoggerFactory.getLogger(VaultCallback.class); - - T doExecute() throws VaultException; - - static T execute(VaultCallback callback) { - try { - return callback.doExecute(); - } catch (final VaultException ex) { - LOGGER.debug(null, ex); - throw new HashicorpVaultException(ex); - } - } -} diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/VaultConfigFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/VaultConfigFactory.java deleted file mode 100644 index 156c135041..0000000000 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/VaultConfigFactory.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.quorum.tessera.key.vault.hashicorp; - -import com.bettercloud.vault.VaultConfig; - -//Ensures a newly instantiated VaultConfig object is used in the HashicorpKeyVaultClientFactory -class VaultConfigFactory { - - VaultConfig create() { - return new VaultConfig(); - } -} diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/SslConfigFactoryTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/SslConfigFactoryTest.java deleted file mode 100644 index 4b688468c2..0000000000 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/SslConfigFactoryTest.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.quorum.tessera.key.vault.hashicorp; - -import com.bettercloud.vault.SslConfig; -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class SslConfigFactoryTest { - - @Test - public void create() { - SslConfigFactory sslConfigFactory = new SslConfigFactory(); - SslConfig emptySslConfig = new SslConfig(); - - SslConfig result = sslConfigFactory.create(); - - assertThat(result).isEqualToComparingFieldByField(emptySslConfig); - } -} diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/VaultCallbackTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/VaultCallbackTest.java deleted file mode 100644 index 44a1df93ba..0000000000 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/VaultCallbackTest.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.quorum.tessera.key.vault.hashicorp; - -import com.bettercloud.vault.VaultException; -import org.junit.Test; - -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; - -public class VaultCallbackTest { - - @Test(expected = HashicorpVaultException.class) - public void executeThrowsSQLException() throws Exception { - - VaultCallback callback = mock(VaultCallback.class); - - doThrow(VaultException.class).when(callback).doExecute(); - - VaultCallback.execute(callback); - - } - -} diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/VaultConfigFactoryTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/VaultConfigFactoryTest.java deleted file mode 100644 index 5437bb7735..0000000000 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/VaultConfigFactoryTest.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.quorum.tessera.key.vault.hashicorp; - -import com.bettercloud.vault.VaultConfig; -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class VaultConfigFactoryTest { - - @Test - public void create() { - VaultConfigFactory vaultConfigFactory = new VaultConfigFactory(); - VaultConfig emptyVaultConfig = new VaultConfig(); - - VaultConfig result = vaultConfigFactory.create(); - - assertThat(result).isEqualToComparingFieldByField(emptyVaultConfig); - } -} From bb7a5074088ef099e4dd84d8ecfb1d274d2b7b9e Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Fri, 14 Dec 2018 12:05:48 +0000 Subject: [PATCH 52/57] Allow previous versions of a Hashicorp secret to be retrieved from Vault --- .../com/quorum/tessera/config/KeyData.java | 14 ++++- .../config/adapters/KeyDataAdapter.java | 19 +++++- .../constraints/PositiveIntegerValidator.java | 36 +++++++++++ .../constraints/ValidPositiveInteger.java | 29 +++++++++ .../keypairs/HashicorpVaultKeyPair.java | 21 ++++++- .../config/keypairs/UnsupportedKeyPair.java | 14 ++++- .../vault/data/HashicorpGetSecretData.java | 8 ++- .../resources/ValidationMessages.properties | 1 + .../quorum/tessera/config/ValidationTest.java | 6 +- .../config/adapters/KeyDataAdapterTest.java | 4 +- .../PositiveIntegerValidatorTest.java | 63 +++++++++++++++++++ .../keypairs/HashicorpVaultKeyPairTest.java | 17 ++++- .../keypairs/UnsupportedKeyPairTest.java | 11 +++- .../data/HashicorpGetSecretDataTest.java | 3 +- .../HashicorpVaultKeyGenerator.java | 2 +- .../HashicorpVaultKeyGeneratorTest.java | 2 +- .../keypairconverter/KeyPairConverter.java | 4 +- .../KeyPairConverterTest.java | 2 +- .../hashicorp/KeyValueOperationsDelegate.java | 3 +- ...shicorpKeyVaultServiceFactoryUtilTest.java | 4 +- .../HashicorpKeyVaultServiceTest.java | 6 +- .../KeyValueOperationsDelegateTest.java | 5 +- 22 files changed, 246 insertions(+), 28 deletions(-) create mode 100644 config/src/main/java/com/quorum/tessera/config/constraints/PositiveIntegerValidator.java create mode 100644 config/src/main/java/com/quorum/tessera/config/constraints/ValidPositiveInteger.java create mode 100644 config/src/test/java/com/quorum/tessera/config/constraints/PositiveIntegerValidatorTest.java diff --git a/config/src/main/java/com/quorum/tessera/config/KeyData.java b/config/src/main/java/com/quorum/tessera/config/KeyData.java index 4f96fcae1b..b48b1823c5 100644 --- a/config/src/main/java/com/quorum/tessera/config/KeyData.java +++ b/config/src/main/java/com/quorum/tessera/config/KeyData.java @@ -55,7 +55,10 @@ public class KeyData extends ConfigItem { @XmlElement private String hashicorpVaultSecretName; - public KeyData(KeyDataConfig config, String privateKey, String publicKey, Path privateKeyPath, Path publicKeyPath, String azureVaultPublicKeyId, String azureVaultPrivateKeyId, String hashicorpVaultPublicKeyId, String hashicorpVaultPrivateKeyId, String hashicorpVaultSecretEngineName, String hashicorpVaultSecretName) { + @XmlElement + private String hashicorpVaultSecretVersion; + + public KeyData(KeyDataConfig config, String privateKey, String publicKey, Path privateKeyPath, Path publicKeyPath, String azureVaultPublicKeyId, String azureVaultPrivateKeyId, String hashicorpVaultPublicKeyId, String hashicorpVaultPrivateKeyId, String hashicorpVaultSecretEngineName, String hashicorpVaultSecretName, String hashicorpVaultSecretVersion) { this.config = config; this.privateKey = privateKey; this.publicKey = publicKey; @@ -67,6 +70,7 @@ public KeyData(KeyDataConfig config, String privateKey, String publicKey, Path p this.hashicorpVaultPrivateKeyId = hashicorpVaultPrivateKeyId; this.hashicorpVaultSecretEngineName = hashicorpVaultSecretEngineName; this.hashicorpVaultSecretName = hashicorpVaultSecretName; + this.hashicorpVaultSecretVersion = hashicorpVaultSecretVersion; } public KeyData() { @@ -117,6 +121,10 @@ public String getHashicorpVaultSecretName() { return hashicorpVaultSecretName; } + public String getHashicorpVaultSecretVersion() { + return hashicorpVaultSecretVersion; + } + public void setConfig(KeyDataConfig config) { this.config = config; } @@ -160,4 +168,8 @@ public void setHashicorpVaultSecretEngineName(String hashicorpVaultSecretEngineN public void setHashicorpVaultSecretName(String hashicorpVaultSecretName) { this.hashicorpVaultSecretName = hashicorpVaultSecretName; } + + public void setHashicorpVaultSecretVersion(String hashicorpVaultSecretVersion) { + this.hashicorpVaultSecretVersion = hashicorpVaultSecretVersion; + } } diff --git a/config/src/main/java/com/quorum/tessera/config/adapters/KeyDataAdapter.java b/config/src/main/java/com/quorum/tessera/config/adapters/KeyDataAdapter.java index 38021b3b6c..02ee470e33 100644 --- a/config/src/main/java/com/quorum/tessera/config/adapters/KeyDataAdapter.java +++ b/config/src/main/java/com/quorum/tessera/config/adapters/KeyDataAdapter.java @@ -31,7 +31,7 @@ public ConfigKeyPair unmarshal(final KeyData keyData) { //case 4, the Hashicorp Vault data is provided if(keyData.getHashicorpVaultPublicKeyId() != null && keyData.getHashicorpVaultPrivateKeyId() != null && keyData.getHashicorpVaultSecretEngineName() != null && keyData.getHashicorpVaultSecretName() != null) { - return new HashicorpVaultKeyPair(keyData.getHashicorpVaultPublicKeyId(), keyData.getHashicorpVaultPrivateKeyId(), keyData.getHashicorpVaultSecretEngineName(), keyData.getHashicorpVaultSecretName()); + return new HashicorpVaultKeyPair(keyData.getHashicorpVaultPublicKeyId(), keyData.getHashicorpVaultPrivateKeyId(), keyData.getHashicorpVaultSecretEngineName(), keyData.getHashicorpVaultSecretName(), keyData.getHashicorpVaultSecretVersion()); } //case 5, the keys are provided inside a file @@ -51,7 +51,8 @@ public ConfigKeyPair unmarshal(final KeyData keyData) { keyData.getHashicorpVaultPublicKeyId(), keyData.getHashicorpVaultPrivateKeyId(), keyData.getHashicorpVaultSecretEngineName(), - keyData.getHashicorpVaultSecretName() + keyData.getHashicorpVaultSecretName(), + keyData.getHashicorpVaultSecretVersion() ); } @@ -104,7 +105,19 @@ public KeyData marshal(final ConfigKeyPair keyPair) { if(keyPair instanceof UnsupportedKeyPair) { UnsupportedKeyPair kp = (UnsupportedKeyPair) keyPair; - return new KeyData(kp.getConfig(), kp.getPrivateKey(), kp.getPublicKey(), kp.getPrivateKeyPath(), kp.getPublicKeyPath(), kp.getAzureVaultPrivateKeyId(), kp.getAzureVaultPublicKeyId(), kp.getHashicorpVaultPrivateKeyId(), kp.getHashicorpVaultPublicKeyId(), kp.getHashicorpVaultSecretEngineName(), kp.getHashicorpVaultSecretName()); + return new KeyData( + kp.getConfig(), + kp.getPrivateKey(), + kp.getPublicKey(), + kp.getPrivateKeyPath(), + kp.getPublicKeyPath(), + kp.getAzureVaultPrivateKeyId(), + kp.getAzureVaultPublicKeyId(), + kp.getHashicorpVaultPrivateKeyId(), + kp.getHashicorpVaultPublicKeyId(), + kp.getHashicorpVaultSecretEngineName(), + kp.getHashicorpVaultSecretName(), + kp.getHashicorpVaultSecretVersion()); } throw new UnsupportedOperationException("The keypair type " + keyPair.getClass() + " is not allowed"); diff --git a/config/src/main/java/com/quorum/tessera/config/constraints/PositiveIntegerValidator.java b/config/src/main/java/com/quorum/tessera/config/constraints/PositiveIntegerValidator.java new file mode 100644 index 0000000000..de99aec063 --- /dev/null +++ b/config/src/main/java/com/quorum/tessera/config/constraints/PositiveIntegerValidator.java @@ -0,0 +1,36 @@ +package com.quorum.tessera.config.constraints; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +public class PositiveIntegerValidator implements ConstraintValidator { + + + private ValidPositiveInteger validPositiveInteger; + + @Override + public void initialize(ValidPositiveInteger constraintAnnotation) { + this.validPositiveInteger = constraintAnnotation; + } + + @Override + public boolean isValid(String secretVersion, ConstraintValidatorContext constraintValidatorContext) { + if(secretVersion == null) { + return true; + } + + Integer i; + + try { + i = Integer.valueOf(secretVersion); + } catch(NumberFormatException e) { + return false; + } + + if(i < 0) { + return false; + } + + return true; + } +} diff --git a/config/src/main/java/com/quorum/tessera/config/constraints/ValidPositiveInteger.java b/config/src/main/java/com/quorum/tessera/config/constraints/ValidPositiveInteger.java new file mode 100644 index 0000000000..9b6b436931 --- /dev/null +++ b/config/src/main/java/com/quorum/tessera/config/constraints/ValidPositiveInteger.java @@ -0,0 +1,29 @@ +package com.quorum.tessera.config.constraints; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE,TYPE_PARAMETER, TYPE_USE}) +@Retention(RUNTIME) +@Constraint(validatedBy = PositiveIntegerValidator.class) +@Documented +public @interface ValidPositiveInteger { + + String message() default "{ValidPositiveInteger.message}"; + + Class[] groups() default {}; + + Class[] payload() default {}; + + boolean checkExists() default false; + + boolean checkCanCreate() default false; + + +} diff --git a/config/src/main/java/com/quorum/tessera/config/keypairs/HashicorpVaultKeyPair.java b/config/src/main/java/com/quorum/tessera/config/keypairs/HashicorpVaultKeyPair.java index a00f16c507..18a90a4f68 100644 --- a/config/src/main/java/com/quorum/tessera/config/keypairs/HashicorpVaultKeyPair.java +++ b/config/src/main/java/com/quorum/tessera/config/keypairs/HashicorpVaultKeyPair.java @@ -1,5 +1,7 @@ package com.quorum.tessera.config.keypairs; +import com.quorum.tessera.config.constraints.ValidPositiveInteger; + import javax.validation.constraints.NotNull; import javax.xml.bind.annotation.XmlElement; @@ -21,11 +23,16 @@ public class HashicorpVaultKeyPair implements ConfigKeyPair { @XmlElement private String secretName; - public HashicorpVaultKeyPair(String publicKeyId, String privateKeyId, String secretEngineName, String secretName) { + @ValidPositiveInteger + @XmlElement + private String secretVersion; + + public HashicorpVaultKeyPair(String publicKeyId, String privateKeyId, String secretEngineName, String secretName, String secretVersion) { this.publicKeyId = publicKeyId; this.privateKeyId = privateKeyId; this.secretEngineName = secretEngineName; this.secretName = secretName; + this.secretVersion = secretVersion; } public String getPublicKeyId() { @@ -44,6 +51,18 @@ public String getSecretName() { return secretName; } + public String getSecretVersion() { + return secretVersion; + } + + public Integer getSecretVersionAsInt() { + if(secretVersion == null) { + return 0; + } else { + return Integer.parseInt(secretVersion); + } + } + @Override public String getPublicKey() { //keys are not fetched from vault yet so return null diff --git a/config/src/main/java/com/quorum/tessera/config/keypairs/UnsupportedKeyPair.java b/config/src/main/java/com/quorum/tessera/config/keypairs/UnsupportedKeyPair.java index 0523d33ee7..3a1a015680 100644 --- a/config/src/main/java/com/quorum/tessera/config/keypairs/UnsupportedKeyPair.java +++ b/config/src/main/java/com/quorum/tessera/config/keypairs/UnsupportedKeyPair.java @@ -46,7 +46,10 @@ public class UnsupportedKeyPair implements ConfigKeyPair { @XmlElement private String hashicorpVaultSecretName; - public UnsupportedKeyPair(KeyDataConfig config, String privateKey, String publicKey, Path privateKeyPath, Path publicKeyPath, String azureVaultPublicKeyId, String azureVaultPrivateKeyId, String hashicorpVaultPublicKeyId, String hashicorpVaultPrivateKeyId, String hashicorpVaultSecretEngineName, String hashicorpVaultSecretName) { + @XmlElement + private String hashicorpVaultSecretVersion; + + public UnsupportedKeyPair(KeyDataConfig config, String privateKey, String publicKey, Path privateKeyPath, Path publicKeyPath, String azureVaultPublicKeyId, String azureVaultPrivateKeyId, String hashicorpVaultPublicKeyId, String hashicorpVaultPrivateKeyId, String hashicorpVaultSecretEngineName, String hashicorpVaultSecretName, String hashicorpVaultSecretVersion) { this.config = config; this.privateKey = privateKey; this.publicKey = publicKey; @@ -58,6 +61,7 @@ public UnsupportedKeyPair(KeyDataConfig config, String privateKey, String public this.hashicorpVaultPrivateKeyId = hashicorpVaultPrivateKeyId; this.hashicorpVaultSecretEngineName = hashicorpVaultSecretEngineName; this.hashicorpVaultSecretName = hashicorpVaultSecretName; + this.hashicorpVaultSecretVersion = hashicorpVaultSecretVersion; } public UnsupportedKeyPair() { @@ -110,6 +114,10 @@ public String getHashicorpVaultSecretName() { return hashicorpVaultSecretName; } + public String getHashicorpVaultSecretVersion() { + return hashicorpVaultSecretVersion; + } + @Override public void withPassword(String password) { //do nothing as password not used with this keypair type @@ -163,4 +171,8 @@ public void setHashicorpVaultSecretEngineName(String hashicorpVaultSecretEngineN public void setHashicorpVaultSecretName(String hashicorpVaultSecretName) { this.hashicorpVaultSecretName = hashicorpVaultSecretName; } + + public void setHashicorpVaultSecretVersion(String hashicorpVaultSecretVersion) { + this.hashicorpVaultSecretVersion = hashicorpVaultSecretVersion; + } } diff --git a/config/src/main/java/com/quorum/tessera/config/vault/data/HashicorpGetSecretData.java b/config/src/main/java/com/quorum/tessera/config/vault/data/HashicorpGetSecretData.java index e4aef894e7..103840317c 100644 --- a/config/src/main/java/com/quorum/tessera/config/vault/data/HashicorpGetSecretData.java +++ b/config/src/main/java/com/quorum/tessera/config/vault/data/HashicorpGetSecretData.java @@ -6,11 +6,13 @@ public class HashicorpGetSecretData implements GetSecretData { private final String secretEngineName; private final String secretName; private final String valueId; + private final int secretVersion; - public HashicorpGetSecretData(String secretEngineName, String secretName, String valueId) { + public HashicorpGetSecretData(String secretEngineName, String secretName, String valueId, int secretVersion) { this.secretEngineName = secretEngineName; this.secretName = secretName; this.valueId = valueId; + this.secretVersion = secretVersion; } public String getSecretEngineName() { @@ -25,6 +27,10 @@ public String getValueId() { return valueId; } + public int getSecretVersion() { + return secretVersion; + } + @Override public KeyVaultType getType() { return KeyVaultType.HASHICORP; diff --git a/config/src/main/resources/ValidationMessages.properties b/config/src/main/resources/ValidationMessages.properties index 4aebb52539..86f97ec2e2 100644 --- a/config/src/main/resources/ValidationMessages.properties +++ b/config/src/main/resources/ValidationMessages.properties @@ -24,3 +24,4 @@ ValidKeyConfiguration.message=A password file and inline passwords were provided ValidKeyVaultConfiguration.message=No key vault configuration was specified but vault key data was provided ValidKeyVaultConfiguration.azure.message=No azureKeyVaultConfig was specified but azureVaultPublicKeyId and azureVaultPrivateKeyId were provided ValidKeyVaultConfiguration.hashicorp.message=No hashicorpKeyVaultConfig was specified but hashicorpVaultPublicKeyId, hashicorpVaultPrivateKeyId and hashicorpVaultSecretPath were provided +ValidSecretVersion.message=The value provided must be an integer equal to 0 or greater \ No newline at end of file diff --git a/config/src/test/java/com/quorum/tessera/config/ValidationTest.java b/config/src/test/java/com/quorum/tessera/config/ValidationTest.java index 68b8241361..961a7b0d66 100644 --- a/config/src/test/java/com/quorum/tessera/config/ValidationTest.java +++ b/config/src/test/java/com/quorum/tessera/config/ValidationTest.java @@ -309,7 +309,7 @@ public void azureKeyPairProvidedWithHashicorpKeyVaultConfigCreatesViolation() { @Test public void hashicorpKeyPairProvidedWithoutKeyVaultConfigCreatesViolation() { - HashicorpVaultKeyPair keyPair = new HashicorpVaultKeyPair("pubId", "privdId", "secretEngine", "secretName"); + HashicorpVaultKeyPair keyPair = new HashicorpVaultKeyPair("pubId", "privdId", "secretEngine", "secretName", null); KeyConfiguration keyConfiguration = new KeyConfiguration(); keyConfiguration.setKeyData(singletonList(keyPair)); @@ -327,7 +327,7 @@ public void hashicorpKeyPairProvidedWithoutKeyVaultConfigCreatesViolation() { @Test public void hashicorpKeyPairProvidedWithAzureKeyVaultConfigCreatesViolation() { - HashicorpVaultKeyPair keyPair = new HashicorpVaultKeyPair("pubId", "privdId", "secretEngine", "secretName"); + HashicorpVaultKeyPair keyPair = new HashicorpVaultKeyPair("pubId", "privdId", "secretEngine", "secretName", null); KeyConfiguration keyConfiguration = new KeyConfiguration(); keyConfiguration.setKeyData(singletonList(keyPair)); @@ -407,7 +407,7 @@ public void hashicorpVaultConfigWithNoUrlCreatesNotNullViolation() { @Test public void hashicorpVaultKeyPairProvidedButKeyVaultConfigHasNullUrlCreatesNotNullViolation() { - HashicorpVaultKeyPair keyPair = new HashicorpVaultKeyPair("pubId", "privId", "secretEngine", "secretName"); + HashicorpVaultKeyPair keyPair = new HashicorpVaultKeyPair("pubId", "privId", "secretEngine", "secretName", null); HashicorpKeyVaultConfig keyVaultConfig = new HashicorpKeyVaultConfig(); KeyConfiguration keyConfiguration = new KeyConfiguration(null, null, singletonList(keyPair), null, keyVaultConfig); diff --git a/config/src/test/java/com/quorum/tessera/config/adapters/KeyDataAdapterTest.java b/config/src/test/java/com/quorum/tessera/config/adapters/KeyDataAdapterTest.java index e5a2ae876a..b51c3bd739 100644 --- a/config/src/test/java/com/quorum/tessera/config/adapters/KeyDataAdapterTest.java +++ b/config/src/test/java/com/quorum/tessera/config/adapters/KeyDataAdapterTest.java @@ -73,7 +73,7 @@ public void marshallAzureKeys() { @Test public void marshallHashicorpKeys() { - final HashicorpVaultKeyPair keyPair = new HashicorpVaultKeyPair("pubId", "privId", "secretEngineName", "secretName"); + final HashicorpVaultKeyPair keyPair = new HashicorpVaultKeyPair("pubId", "privId", "secretEngineName", "secretName", "0"); final KeyData expected = new KeyData(); expected.setHashicorpVaultPublicKeyId("pubId"); @@ -90,7 +90,7 @@ public void marshallHashicorpKeys() { public void marshallUnsupportedKeys() { final KeyDataConfig keyDataConfig = mock(KeyDataConfig.class); final Path path = mock(Path.class); - final UnsupportedKeyPair keyPair = new UnsupportedKeyPair(keyDataConfig, "priv", null, path, null, null, null, null, null, null, null); + final UnsupportedKeyPair keyPair = new UnsupportedKeyPair(keyDataConfig, "priv", null, path, null, null, null, null, null, null, null, null); final KeyData expected = new KeyData(); //set a random selection of values that are not sufficient to make a complete key pair of any type diff --git a/config/src/test/java/com/quorum/tessera/config/constraints/PositiveIntegerValidatorTest.java b/config/src/test/java/com/quorum/tessera/config/constraints/PositiveIntegerValidatorTest.java new file mode 100644 index 0000000000..e0d489268b --- /dev/null +++ b/config/src/test/java/com/quorum/tessera/config/constraints/PositiveIntegerValidatorTest.java @@ -0,0 +1,63 @@ +package com.quorum.tessera.config.constraints; + +import org.junit.Before; +import org.junit.Test; + +import javax.validation.ConstraintValidatorContext; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +public class PositiveIntegerValidatorTest { + + private PositiveIntegerValidator validator; + + private ConstraintValidatorContext context; + + @Before + public void setUp() { + this.validator = new PositiveIntegerValidator(); + this.context = mock(ConstraintValidatorContext.class); + } + + @Test + public void nullIsValid() { + assertThat(validator.isValid(null, context)).isTrue(); + } + + @Test + public void positiveIntegerIsValid() { + assertThat(validator.isValid("10", context)).isTrue(); + } + + @Test + public void zeroIsValid() { + assertThat(validator.isValid("0", context)).isTrue(); + } + + @Test + public void nonNumericCharactersAreInvalid() { + assertThat(validator.isValid("letters", context)).isFalse(); + } + + @Test + public void nonNumericCharactersFollowingNumbersIsInvalid() { + assertThat(validator.isValid("10letters", context)).isFalse(); + } + + @Test + public void decimalIsInvalid() { + assertThat(validator.isValid("1.0", context)).isFalse(); + } + + @Test + public void negativeIsInvalid() { + assertThat(validator.isValid("-10", context)).isFalse(); + } + + @Test + public void initialize() { + ValidPositiveInteger constraintAnnotation = mock(ValidPositiveInteger.class); + validator.initialize(constraintAnnotation); + } +} diff --git a/config/src/test/java/com/quorum/tessera/config/keypairs/HashicorpVaultKeyPairTest.java b/config/src/test/java/com/quorum/tessera/config/keypairs/HashicorpVaultKeyPairTest.java index 600faa10f5..6b3d10cc41 100644 --- a/config/src/test/java/com/quorum/tessera/config/keypairs/HashicorpVaultKeyPairTest.java +++ b/config/src/test/java/com/quorum/tessera/config/keypairs/HashicorpVaultKeyPairTest.java @@ -11,7 +11,7 @@ public class HashicorpVaultKeyPairTest { @Before public void setUp() { - keyPair = new HashicorpVaultKeyPair("pubId", "privId", "secretEngine", "secretName"); + keyPair = new HashicorpVaultKeyPair("pubId", "privId", "secretEngine", "secretName", "0"); } @Test @@ -23,6 +23,21 @@ public void getters() { assertThat(keyPair.getPublicKey()).isEqualTo(null); assertThat(keyPair.getPrivateKey()).isEqualTo(null); assertThat(keyPair.getPassword()).isEqualTo(""); + assertThat(keyPair.getSecretVersion()).isEqualTo("0"); + } + + @Test + public void getSecretVersionAsInt() { + keyPair = new HashicorpVaultKeyPair("pubId", "privId", "secretEngine", "secretName", "10"); + + assertThat(keyPair.getSecretVersionAsInt()).isEqualTo(10); + } + + @Test + public void getSecretVersionAsIntReturns0IfNull() { + keyPair = new HashicorpVaultKeyPair("pubId", "privId", "secretEngine", "secretName", null); + + assertThat(keyPair.getSecretVersionAsInt()).isEqualTo(0); } @Test diff --git a/config/src/test/java/com/quorum/tessera/config/keypairs/UnsupportedKeyPairTest.java b/config/src/test/java/com/quorum/tessera/config/keypairs/UnsupportedKeyPairTest.java index 7a094b9da4..3171ea9c42 100644 --- a/config/src/test/java/com/quorum/tessera/config/keypairs/UnsupportedKeyPairTest.java +++ b/config/src/test/java/com/quorum/tessera/config/keypairs/UnsupportedKeyPairTest.java @@ -11,7 +11,7 @@ public class UnsupportedKeyPairTest { @Before public void setUp() { - this.keyPair = new UnsupportedKeyPair(null, null, null, null, null, null, null, null, null, null, null); + this.keyPair = new UnsupportedKeyPair(null, null, null, null, null, null, null, null, null, null, null, null); } @Test @@ -23,4 +23,13 @@ public void getPasswordAlwaysReturnsNull() { assertThat(keyPair.getPassword()).isNull(); } + @Test + public void setHashicorpVaultSecretVersion() { + assertThat(keyPair.getHashicorpVaultSecretVersion()).isNull(); + + keyPair.setHashicorpVaultSecretVersion("1"); + + assertThat(keyPair.getHashicorpVaultSecretVersion()).isEqualTo("1"); + } + } diff --git a/config/src/test/java/com/quorum/tessera/config/vault/data/HashicorpGetSecretDataTest.java b/config/src/test/java/com/quorum/tessera/config/vault/data/HashicorpGetSecretDataTest.java index 54402da5a9..0358e958ed 100644 --- a/config/src/test/java/com/quorum/tessera/config/vault/data/HashicorpGetSecretDataTest.java +++ b/config/src/test/java/com/quorum/tessera/config/vault/data/HashicorpGetSecretDataTest.java @@ -12,7 +12,7 @@ public class HashicorpGetSecretDataTest { @Before public void setUp() { - this.getSecretData = new HashicorpGetSecretData("secret", "secretName", "keyId"); + this.getSecretData = new HashicorpGetSecretData("secret", "secretName", "keyId", 1); } @Test @@ -20,6 +20,7 @@ public void getters() { assertThat(getSecretData.getSecretEngineName()).isEqualTo("secret"); assertThat(getSecretData.getSecretName()).isEqualTo("secretName"); assertThat(getSecretData.getValueId()).isEqualTo("keyId"); + assertThat(getSecretData.getSecretVersion()).isEqualTo(1); } @Test diff --git a/key-generation/src/main/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGenerator.java b/key-generation/src/main/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGenerator.java index dc35b18757..09ea28ae87 100644 --- a/key-generation/src/main/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGenerator.java +++ b/key-generation/src/main/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGenerator.java @@ -47,6 +47,6 @@ public HashicorpVaultKeyPair generate(String filename, ArgonOptions encryptionOp LOGGER.debug("Key {} saved to vault with path {} and id {}", keyPairData.get(privId), filename, privId); LOGGER.info("Key saved to vault with path {} and id {}", filename, privId); - return new HashicorpVaultKeyPair(pubId, privId, keyVaultOptions.getSecretEngineName(), filename); + return new HashicorpVaultKeyPair(pubId, privId, keyVaultOptions.getSecretEngineName(), filename, null); } } diff --git a/key-generation/src/test/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGeneratorTest.java b/key-generation/src/test/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGeneratorTest.java index 0cebb3fc1f..aa302f860a 100644 --- a/key-generation/src/test/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGeneratorTest.java +++ b/key-generation/src/test/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGeneratorTest.java @@ -71,7 +71,7 @@ public void generatedKeyPairIsSavedToSpecifiedPathInVaultWithIds() { HashicorpVaultKeyPair result = hashicorpVaultKeyGenerator.generate(filename, null, keyVaultOptions); - HashicorpVaultKeyPair expected = new HashicorpVaultKeyPair("publicKey", "privateKey", secretEngine, filename); + HashicorpVaultKeyPair expected = new HashicorpVaultKeyPair("publicKey", "privateKey", secretEngine, filename, null); assertThat(result).isEqualToComparingFieldByField(expected); final ArgumentCaptor captor = ArgumentCaptor.forClass(HashicorpSetSecretData.class); diff --git a/key-pair-converter/src/main/java/com/quorum/tessera/keypairconverter/KeyPairConverter.java b/key-pair-converter/src/main/java/com/quorum/tessera/keypairconverter/KeyPairConverter.java index 997eff7bc8..90b94d970b 100644 --- a/key-pair-converter/src/main/java/com/quorum/tessera/keypairconverter/KeyPairConverter.java +++ b/key-pair-converter/src/main/java/com/quorum/tessera/keypairconverter/KeyPairConverter.java @@ -65,8 +65,8 @@ else if(configKeyPair instanceof HashicorpVaultKeyPair) { HashicorpVaultKeyPair hkp = (HashicorpVaultKeyPair) configKeyPair; - GetSecretData getPublicKeyData = new HashicorpGetSecretData(hkp.getSecretEngineName(), hkp.getSecretName(), hkp.getPublicKeyId()); - GetSecretData getPrivateKeyData = new HashicorpGetSecretData(hkp.getSecretEngineName(), hkp.getSecretName(), hkp.getPrivateKeyId()); + GetSecretData getPublicKeyData = new HashicorpGetSecretData(hkp.getSecretEngineName(), hkp.getSecretName(), hkp.getPublicKeyId(), hkp.getSecretVersionAsInt()); + GetSecretData getPrivateKeyData = new HashicorpGetSecretData(hkp.getSecretEngineName(), hkp.getSecretName(), hkp.getPrivateKeyId(), hkp.getSecretVersionAsInt()); base64PublicKey = keyVaultService.getSecret(getPublicKeyData); base64PrivateKey = keyVaultService.getSecret(getPrivateKeyData); diff --git a/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/KeyPairConverterTest.java b/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/KeyPairConverterTest.java index 459c971b8a..0214e0ed4f 100644 --- a/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/KeyPairConverterTest.java +++ b/key-pair-converter/src/test/java/com/quorum/tessera/keypairconverter/KeyPairConverterTest.java @@ -94,7 +94,7 @@ public void convertSingleAzureVaultKeyPair() { @Test public void convertSingleHashicorpVaultKeyPair() { - final HashicorpVaultKeyPair keyPair = new HashicorpVaultKeyPair("pub", "priv", "engine", "secretName"); + final HashicorpVaultKeyPair keyPair = new HashicorpVaultKeyPair("pub", "priv", "engine", "secretName", "10"); Collection result = converter.convert(Collections.singletonList(keyPair)); diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/KeyValueOperationsDelegate.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/KeyValueOperationsDelegate.java index 67a22cf376..841a6e48fd 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/KeyValueOperationsDelegate.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/KeyValueOperationsDelegate.java @@ -16,7 +16,8 @@ class KeyValueOperationsDelegate { } Versioned> get(HashicorpGetSecretData getSecretData) { - return keyValueOperations.get(getSecretData.getSecretName()); + //if version 0 then latest version retrieved + return keyValueOperations.get(getSecretData.getSecretName(), Versioned.Version.from(getSecretData.getSecretVersion())); } Versioned.Metadata set(HashicorpSetSecretData setSecretData) { diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryUtilTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryUtilTest.java index d78fbcbd6b..5f1da7766a 100644 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryUtilTest.java +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryUtilTest.java @@ -5,7 +5,7 @@ import org.junit.Before; import org.junit.Test; import org.springframework.http.client.ClientHttpRequestFactory; -import org.springframework.http.client.Netty4ClientHttpRequestFactory; +import org.springframework.http.client.OkHttp3ClientHttpRequestFactory; import org.springframework.vault.authentication.AppRoleAuthentication; import org.springframework.vault.authentication.ClientAuthentication; import org.springframework.vault.authentication.TokenAuthentication; @@ -98,7 +98,7 @@ public void createClientHttpRequestFactory() { ClientHttpRequestFactory result = util.createClientHttpRequestFactory(clientOptions, sslConfiguration); - assertThat(result).isInstanceOf(Netty4ClientHttpRequestFactory.class); + assertThat(result).isInstanceOf(OkHttp3ClientHttpRequestFactory.class); } @Test diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java index 3677a643f6..49fc1c1c6e 100644 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceTest.java @@ -74,7 +74,7 @@ public void getSecretThrowsExceptionIfProvidedDataIsNotCorrectType() { @Test public void getSecretThrowsExceptionIfNullRetrievedFromVault() { - HashicorpGetSecretData getSecretData = new HashicorpGetSecretData("engine", "secretName", "id"); + HashicorpGetSecretData getSecretData = new HashicorpGetSecretData("engine", "secretName", "id", 0); when(delegate.get(getSecretData)).thenReturn(null); @@ -86,7 +86,7 @@ public void getSecretThrowsExceptionIfNullRetrievedFromVault() { @Test public void getSecretThrowsExceptionIfNoDataRetrievedFromVault() { - HashicorpGetSecretData getSecretData = new HashicorpGetSecretData("engine", "secretName", "id"); + HashicorpGetSecretData getSecretData = new HashicorpGetSecretData("engine", "secretName", "id", 0); Versioned versionedResponse = mock(Versioned.class); when(versionedResponse.hasData()).thenReturn(false); @@ -102,7 +102,7 @@ public void getSecretThrowsExceptionIfNoDataRetrievedFromVault() { @Test public void getSecretThrowsExceptionIfValueNotFoundForGivenId() { - HashicorpGetSecretData getSecretData = new HashicorpGetSecretData("engine", "secretName", "id"); + HashicorpGetSecretData getSecretData = new HashicorpGetSecretData("engine", "secretName", "id", 0); Versioned versionedResponse = mock(Versioned.class); when(versionedResponse.hasData()).thenReturn(true); diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/KeyValueOperationsDelegateTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/KeyValueOperationsDelegateTest.java index 0e8092ca3b..9aadf8a9fc 100644 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/KeyValueOperationsDelegateTest.java +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/KeyValueOperationsDelegateTest.java @@ -30,13 +30,14 @@ public void get() { HashicorpGetSecretData getSecretData = mock(HashicorpGetSecretData.class); when(getSecretData.getSecretName()).thenReturn(secretName); + when(getSecretData.getSecretVersion()).thenReturn(0); Versioned versionedResponse = mock(Versioned.class); - when(keyValueOperations.get(secretName)).thenReturn(versionedResponse); + when(keyValueOperations.get(secretName, Versioned.Version.from(0))).thenReturn(versionedResponse); Versioned result = delegate.get(getSecretData); - verify(keyValueOperations).get(secretName); + verify(keyValueOperations).get(secretName, Versioned.Version.unversioned()); assertThat(result).isEqualTo(versionedResponse); } From 64c4523435dbef60e6fd39c19e6cb430f02eaa46 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Fri, 14 Dec 2018 14:55:37 +0000 Subject: [PATCH 53/57] Add vault dependencies to dep mgmt and minor changes/tests --- .../vault/data/HashicorpGetSecretData.java | 2 +- .../resources/ValidationMessages.properties | 2 +- .../HashicorpVaultKeyGenerator.java | 12 +++++----- key-vault/azure-key-vault/pom.xml | 2 -- key-vault/hashicorp-key-vault/pom.xml | 2 -- .../HashicorpKeyVaultServiceFactoryTest.java | 22 +++++++++++++++++ pom.xml | 24 +++++++++++++++++++ 7 files changed, 54 insertions(+), 12 deletions(-) diff --git a/config/src/main/java/com/quorum/tessera/config/vault/data/HashicorpGetSecretData.java b/config/src/main/java/com/quorum/tessera/config/vault/data/HashicorpGetSecretData.java index 103840317c..597d926991 100644 --- a/config/src/main/java/com/quorum/tessera/config/vault/data/HashicorpGetSecretData.java +++ b/config/src/main/java/com/quorum/tessera/config/vault/data/HashicorpGetSecretData.java @@ -12,7 +12,7 @@ public HashicorpGetSecretData(String secretEngineName, String secretName, String this.secretEngineName = secretEngineName; this.secretName = secretName; this.valueId = valueId; - this.secretVersion = secretVersion; + this.secretVersion = secretVersion; } public String getSecretEngineName() { diff --git a/config/src/main/resources/ValidationMessages.properties b/config/src/main/resources/ValidationMessages.properties index 86f97ec2e2..4b72e8ae41 100644 --- a/config/src/main/resources/ValidationMessages.properties +++ b/config/src/main/resources/ValidationMessages.properties @@ -24,4 +24,4 @@ ValidKeyConfiguration.message=A password file and inline passwords were provided ValidKeyVaultConfiguration.message=No key vault configuration was specified but vault key data was provided ValidKeyVaultConfiguration.azure.message=No azureKeyVaultConfig was specified but azureVaultPublicKeyId and azureVaultPrivateKeyId were provided ValidKeyVaultConfiguration.hashicorp.message=No hashicorpKeyVaultConfig was specified but hashicorpVaultPublicKeyId, hashicorpVaultPrivateKeyId and hashicorpVaultSecretPath were provided -ValidSecretVersion.message=The value provided must be an integer equal to 0 or greater \ No newline at end of file +ValidPositiveInteger.message=The value provided must be an integer equal to 0 or greater \ No newline at end of file diff --git a/key-generation/src/main/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGenerator.java b/key-generation/src/main/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGenerator.java index 09ea28ae87..e7defba913 100644 --- a/key-generation/src/main/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGenerator.java +++ b/key-generation/src/main/java/com/quorum/tessera/key/generation/HashicorpVaultKeyGenerator.java @@ -28,8 +28,8 @@ public HashicorpVaultKeyGenerator(final NaclFacade nacl, KeyVaultService keyVaul @Override public HashicorpVaultKeyPair generate(String filename, ArgonOptions encryptionOptions, KeyVaultOptions keyVaultOptions) { Objects.requireNonNull(filename); - Objects.requireNonNull(keyVaultOptions); - Objects.requireNonNull(keyVaultOptions.getSecretEngineName()); + Objects.requireNonNull(keyVaultOptions, "-keygenvaultsecretengine must be provided if using the Hashicorp vault type"); + Objects.requireNonNull(keyVaultOptions.getSecretEngineName(), "-keygenvaultsecretengine must be provided if using the Hashicorp vault type"); final KeyPair keys = this.nacl.generateNewKeys(); @@ -42,10 +42,10 @@ public HashicorpVaultKeyPair generate(String filename, ArgonOptions encryptionOp SetSecretData setSecretData = new HashicorpSetSecretData(keyVaultOptions.getSecretEngineName(), filename, keyPairData); keyVaultService.setSecret(setSecretData); - LOGGER.debug("Key {} saved to vault with path {} and id {}", keyPairData.get(pubId), filename, pubId); - LOGGER.info("Key saved to vault with path {} and id {}", filename, pubId); - LOGGER.debug("Key {} saved to vault with path {} and id {}", keyPairData.get(privId), filename, privId); - LOGGER.info("Key saved to vault with path {} and id {}", filename, privId); + LOGGER.debug("Key {} saved to vault secret engine {} with name {} and id {}", keyPairData.get(pubId), keyVaultOptions.getSecretEngineName(), filename, pubId); + LOGGER.info("Key saved to vault secret engine {} with name {} and id {}", keyVaultOptions.getSecretEngineName(), filename, pubId); + LOGGER.debug("Key {} saved to vault secret engine {} with name {} and id {}", keyPairData.get(privId), keyVaultOptions.getSecretEngineName(), filename, privId); + LOGGER.info("Key saved to vault secret engine {} with name {} and id {}", keyVaultOptions.getSecretEngineName(), filename, privId); return new HashicorpVaultKeyPair(pubId, privId, keyVaultOptions.getSecretEngineName(), filename, null); } diff --git a/key-vault/azure-key-vault/pom.xml b/key-vault/azure-key-vault/pom.xml index 780c072edc..b39277602f 100644 --- a/key-vault/azure-key-vault/pom.xml +++ b/key-vault/azure-key-vault/pom.xml @@ -19,13 +19,11 @@ com.microsoft.azure azure-keyvault - 1.1.2 com.microsoft.azure adal4j - 1.6.3 diff --git a/key-vault/hashicorp-key-vault/pom.xml b/key-vault/hashicorp-key-vault/pom.xml index a3a00ab520..41a2ab782c 100644 --- a/key-vault/hashicorp-key-vault/pom.xml +++ b/key-vault/hashicorp-key-vault/pom.xml @@ -21,13 +21,11 @@ org.springframework.vault spring-vault-core - 2.1.1.RELEASE com.squareup.okhttp3 okhttp - 3.12.0 diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java index 3d84d1bffb..06e3beaf8d 100644 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java @@ -267,4 +267,26 @@ public void returnedValueIsCorrectType() { assertThat(result).isInstanceOf(HashicorpKeyVaultService.class); } + @Test + public void returnedValueIsCorrectTypeUsing2ArgConstructor() { + when(envProvider.getEnv("HASHICORP_ROLE_ID")).thenReturn("role-id"); + when(envProvider.getEnv("HASHICORP_SECRET_ID")).thenReturn("secret-id"); + when(envProvider.getEnv("HASHICORP_TOKEN")).thenReturn("token"); + + KeyConfiguration keyConfiguration = mock(KeyConfiguration.class); + when(config.getKeys()).thenReturn(keyConfiguration); + + HashicorpKeyVaultConfig keyVaultConfig = mock(HashicorpKeyVaultConfig.class); + when(keyConfiguration.getHashicorpKeyVaultConfig()).thenReturn(keyVaultConfig); + + when(keyVaultConfig.getUrl()).thenReturn("http://someurl"); + when(keyVaultConfig.getApprolePath()).thenReturn("approle"); + + setUpUtilMocks(keyVaultConfig); + + KeyVaultService result = keyVaultServiceFactory.create(config, envProvider); + + assertThat(result).isInstanceOf(HashicorpKeyVaultService.class); + } + } diff --git a/pom.xml b/pom.xml index 800fb9e5e4..5e0acaad96 100644 --- a/pom.xml +++ b/pom.xml @@ -822,6 +822,30 @@ 1.3.2 + + com.microsoft.azure + azure-keyvault + 1.1.2 + + + + com.microsoft.azure + adal4j + 1.6.3 + + + + org.springframework.vault + spring-vault-core + 2.1.1.RELEASE + + + + com.squareup.okhttp3 + okhttp + 3.12.0 + + From d52402c4fdb01f8c2255d3ab52e0c6989eccc5f1 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Fri, 14 Dec 2018 16:16:10 +0000 Subject: [PATCH 54/57] Minor clean up --- .../key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java | 4 ++-- .../com.quorum.tessera.key.vault.KeyVaultClientFactory | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) delete mode 100644 key-vault/key-vault-api/src/test/resources/META-INF/services/com.quorum.tessera.key.vault.KeyVaultClientFactory diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java index 4e56b18e24..df1d7c4837 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java @@ -35,8 +35,8 @@ public KeyVaultService create(Config config, EnvironmentVariableProvider envProv return this.create(config, envProvider, util); } - //This method should not be called directly. It has been left public to enable injection of util during testing - public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, HashicorpKeyVaultServiceFactoryUtil util) { + //This method should not be called directly. It has been left package-private to enable injection of util during testing + KeyVaultService create(Config config, EnvironmentVariableProvider envProvider, HashicorpKeyVaultServiceFactoryUtil util) { Objects.requireNonNull(config); Objects.requireNonNull(envProvider); Objects.requireNonNull(util); diff --git a/key-vault/key-vault-api/src/test/resources/META-INF/services/com.quorum.tessera.key.vault.KeyVaultClientFactory b/key-vault/key-vault-api/src/test/resources/META-INF/services/com.quorum.tessera.key.vault.KeyVaultClientFactory deleted file mode 100644 index b470a210d7..0000000000 --- a/key-vault/key-vault-api/src/test/resources/META-INF/services/com.quorum.tessera.key.vault.KeyVaultClientFactory +++ /dev/null @@ -1 +0,0 @@ -com.quorum.tessera.key.vault.MockHashicorpKeyVaultClientFactory \ No newline at end of file From b6acba6163b6ae4f9cd6f6f5ee1a74d250f9e40a Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Fri, 14 Dec 2018 17:00:03 +0000 Subject: [PATCH 55/57] Remove unused files & allow lowercase vault type in keygen CLI --- .../cli/parsers/KeyGenerationParser.java | 7 +- .../cli/parsers/KeyGenerationParserTest.java | 89 ++++++++++++++++++- .../resources/ValidationMessages.properties | 2 +- ...rum.tessera.key.vault.GetSecretDataFactory | 1 - ...rum.tessera.key.vault.SetSecretDataFactory | 1 - ...rum.tessera.key.vault.GetSecretDataFactory | 1 - ...rum.tessera.key.vault.SetSecretDataFactory | 1 - pom.xml | 6 -- 8 files changed, 93 insertions(+), 15 deletions(-) delete mode 100644 key-vault/azure-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.GetSecretDataFactory delete mode 100644 key-vault/azure-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.SetSecretDataFactory delete mode 100644 key-vault/hashicorp-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.GetSecretDataFactory delete mode 100644 key-vault/hashicorp-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.SetSecretDataFactory diff --git a/config-cli/src/main/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParser.java b/config-cli/src/main/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParser.java index 909273724b..730b497408 100644 --- a/config-cli/src/main/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParser.java +++ b/config-cli/src/main/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParser.java @@ -98,9 +98,12 @@ private Optional keyVaultConfig(CommandLine commandLine) { KeyVaultType keyVaultType; try { - keyVaultType = KeyVaultType.valueOf(t); + keyVaultType = KeyVaultType.valueOf( + t.trim() + .toUpperCase() + ); } catch(IllegalArgumentException | NullPointerException e) { - throw new CliException("Key vault type either not provided or not recognised. Ensure provided value is UPPERCASE and has no leading or trailing whitespace characters"); + throw new CliException("Key vault type either not provided or not recognised"); } String keyVaultUrl = commandLine.getOptionValue("keygenvaulturl"); diff --git a/config-cli/src/test/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParserTest.java b/config-cli/src/test/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParserTest.java index d0c98e161f..f8171ffa85 100644 --- a/config-cli/src/test/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParserTest.java +++ b/config-cli/src/test/java/com/quorum/tessera/config/cli/parsers/KeyGenerationParserTest.java @@ -192,7 +192,7 @@ public void ifOnlyVaultUrlOptionProvidedThenException() { Throwable ex = catchThrowable(() -> this.parser.parse(commandLine)); assertThat(ex).isInstanceOf(CliException.class); - assertThat(ex.getMessage()).isEqualTo("Key vault type either not provided or not recognised. Ensure provided value is UPPERCASE and has no leading or trailing whitespace characters"); + assertThat(ex.getMessage()).isEqualTo("Key vault type either not provided or not recognised"); } @Test @@ -204,7 +204,7 @@ public void ifVaultOptionsProvidedButTypeUnknownThenException() { Throwable ex = catchThrowable(() -> this.parser.parse(commandLine)); assertThat(ex).isInstanceOf(CliException.class); - assertThat(ex.getMessage()).isEqualTo("Key vault type either not provided or not recognised. Ensure provided value is UPPERCASE and has no leading or trailing whitespace characters"); + assertThat(ex.getMessage()).isEqualTo("Key vault type either not provided or not recognised"); } @Test @@ -281,4 +281,89 @@ public void ifHashicorpTlsOptionsProvidedButPathsDontExistThenValidationExceptio assertThat(iterator.next().getMessage()).isEqualTo("File does not exist"); } + @Test + public void lowercaseVaultTypeIsOkay() throws Exception { + when(commandLine.hasOption("keygenvaulttype")).thenReturn(true); + when(commandLine.hasOption("keygenvaulturl")).thenReturn(true); + when(commandLine.hasOption("filename")).thenReturn(true); + when(commandLine.getOptionValue("keygenvaulturl")).thenReturn("someurl"); + when(commandLine.getOptionValue("filename")).thenReturn("secret/path"); + when(commandLine.getOptionValue("keygenvaultapprole")).thenReturn("approle"); + when(commandLine.getOptionValue("keygenvaultsecretengine")).thenReturn("secretEngine"); + + when(commandLine.getOptionValue("keygenvaulttype")).thenReturn("hashicorp"); + + Path tempPath = Files.createTempFile(UUID.randomUUID().toString(), ""); + tempPath.toFile().deleteOnExit(); + + when(commandLine.getOptionValue("keygenvaultkeystore")).thenReturn(tempPath.toString()); + when(commandLine.getOptionValue("keygenvaulttruststore")).thenReturn(tempPath.toString()); + + this.parser.parse(commandLine); + + verify(commandLine, times(1)).getOptionValue("keygenvaulttype"); + verify(commandLine, times(1)).getOptionValue("keygenvaulturl"); + verify(commandLine, times(1)).getOptionValue("keygenvaultapprole"); + verify(commandLine, times(1)).getOptionValue("keygenvaultkeystore"); + verify(commandLine, times(1)).getOptionValue("keygenvaulttruststore"); + verify(commandLine, times(1)).getOptionValue("keygenvaultsecretengine"); + } + + @Test + public void leadingWhitespaceVaultTypeIsOkay() throws Exception { + when(commandLine.hasOption("keygenvaulttype")).thenReturn(true); + when(commandLine.hasOption("keygenvaulturl")).thenReturn(true); + when(commandLine.hasOption("filename")).thenReturn(true); + when(commandLine.getOptionValue("keygenvaulturl")).thenReturn("someurl"); + when(commandLine.getOptionValue("filename")).thenReturn("secret/path"); + when(commandLine.getOptionValue("keygenvaultapprole")).thenReturn("approle"); + when(commandLine.getOptionValue("keygenvaultsecretengine")).thenReturn("secretEngine"); + + when(commandLine.getOptionValue("keygenvaulttype")).thenReturn(" HASHICORP"); + + Path tempPath = Files.createTempFile(UUID.randomUUID().toString(), ""); + tempPath.toFile().deleteOnExit(); + + when(commandLine.getOptionValue("keygenvaultkeystore")).thenReturn(tempPath.toString()); + when(commandLine.getOptionValue("keygenvaulttruststore")).thenReturn(tempPath.toString()); + + this.parser.parse(commandLine); + + verify(commandLine, times(1)).getOptionValue("keygenvaulttype"); + verify(commandLine, times(1)).getOptionValue("keygenvaulturl"); + verify(commandLine, times(1)).getOptionValue("keygenvaultapprole"); + verify(commandLine, times(1)).getOptionValue("keygenvaultkeystore"); + verify(commandLine, times(1)).getOptionValue("keygenvaulttruststore"); + verify(commandLine, times(1)).getOptionValue("keygenvaultsecretengine"); + } + + @Test + public void trailingWhitespaceVaultTypeIsOkay() throws Exception { + when(commandLine.hasOption("keygenvaulttype")).thenReturn(true); + when(commandLine.hasOption("keygenvaulturl")).thenReturn(true); + when(commandLine.hasOption("filename")).thenReturn(true); + when(commandLine.getOptionValue("keygenvaulturl")).thenReturn("someurl"); + when(commandLine.getOptionValue("filename")).thenReturn("secret/path"); + when(commandLine.getOptionValue("keygenvaultapprole")).thenReturn("approle"); + when(commandLine.getOptionValue("keygenvaultsecretengine")).thenReturn("secretEngine"); + + when(commandLine.getOptionValue("keygenvaulttype")).thenReturn("HASHICORP "); + + Path tempPath = Files.createTempFile(UUID.randomUUID().toString(), ""); + tempPath.toFile().deleteOnExit(); + + when(commandLine.getOptionValue("keygenvaultkeystore")).thenReturn(tempPath.toString()); + when(commandLine.getOptionValue("keygenvaulttruststore")).thenReturn(tempPath.toString()); + + this.parser.parse(commandLine); + + verify(commandLine, times(1)).getOptionValue("keygenvaulttype"); + verify(commandLine, times(1)).getOptionValue("keygenvaulturl"); + verify(commandLine, times(1)).getOptionValue("keygenvaultapprole"); + verify(commandLine, times(1)).getOptionValue("keygenvaultkeystore"); + verify(commandLine, times(1)).getOptionValue("keygenvaulttruststore"); + verify(commandLine, times(1)).getOptionValue("keygenvaultsecretengine"); + } + + } diff --git a/config/src/main/resources/ValidationMessages.properties b/config/src/main/resources/ValidationMessages.properties index 4b72e8ae41..301defbee8 100644 --- a/config/src/main/resources/ValidationMessages.properties +++ b/config/src/main/resources/ValidationMessages.properties @@ -23,5 +23,5 @@ InlineKeyData.message=A locked key was provided without a password.\n Please ens ValidKeyConfiguration.message=A password file and inline passwords were provided. Please choose one or the other ValidKeyVaultConfiguration.message=No key vault configuration was specified but vault key data was provided ValidKeyVaultConfiguration.azure.message=No azureKeyVaultConfig was specified but azureVaultPublicKeyId and azureVaultPrivateKeyId were provided -ValidKeyVaultConfiguration.hashicorp.message=No hashicorpKeyVaultConfig was specified but hashicorpVaultPublicKeyId, hashicorpVaultPrivateKeyId and hashicorpVaultSecretPath were provided +ValidKeyVaultConfiguration.hashicorp.message=No hashicorpKeyVaultConfig was specified but Hashicorp keyData was provided ValidPositiveInteger.message=The value provided must be an integer equal to 0 or greater \ No newline at end of file diff --git a/key-vault/azure-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.GetSecretDataFactory b/key-vault/azure-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.GetSecretDataFactory deleted file mode 100644 index 213a426a6a..0000000000 --- a/key-vault/azure-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.GetSecretDataFactory +++ /dev/null @@ -1 +0,0 @@ -com.quorum.tessera.key.vault.azure.AzureGetSecretDataFactory \ No newline at end of file diff --git a/key-vault/azure-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.SetSecretDataFactory b/key-vault/azure-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.SetSecretDataFactory deleted file mode 100644 index cbedee051f..0000000000 --- a/key-vault/azure-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.SetSecretDataFactory +++ /dev/null @@ -1 +0,0 @@ -com.quorum.tessera.key.vault.azure.AzureSetSecretDataFactory \ No newline at end of file diff --git a/key-vault/hashicorp-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.GetSecretDataFactory b/key-vault/hashicorp-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.GetSecretDataFactory deleted file mode 100644 index 15eea9b68a..0000000000 --- a/key-vault/hashicorp-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.GetSecretDataFactory +++ /dev/null @@ -1 +0,0 @@ -com.quorum.tessera.key.vault.hashicorp.HashicorpGetSecretDataFactory \ No newline at end of file diff --git a/key-vault/hashicorp-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.SetSecretDataFactory b/key-vault/hashicorp-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.SetSecretDataFactory deleted file mode 100644 index 779360d1ac..0000000000 --- a/key-vault/hashicorp-key-vault/src/main/resources/META-INF/services/com.quorum.tessera.key.vault.SetSecretDataFactory +++ /dev/null @@ -1 +0,0 @@ -com.quorum.tessera.key.vault.hashicorp.HashicorpSetSecretDataFactory \ No newline at end of file diff --git a/pom.xml b/pom.xml index 5e0acaad96..961632632d 100644 --- a/pom.xml +++ b/pom.xml @@ -537,12 +537,6 @@ ${spring.version} - - com.bettercloud - vault-java-driver - 3.1.0 - - org.bouncycastle bcpkix-jdk15on From 1444b9928b77ae22c281252305fb620570845860 Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Mon, 17 Dec 2018 10:50:14 +0000 Subject: [PATCH 56/57] Remove use of PrivateKey toString in file keygen --- .../quorum/tessera/key/generation/FileKeyGenerator.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/key-generation/src/main/java/com/quorum/tessera/key/generation/FileKeyGenerator.java b/key-generation/src/main/java/com/quorum/tessera/key/generation/FileKeyGenerator.java index 8c190c651a..35c57bf3eb 100644 --- a/key-generation/src/main/java/com/quorum/tessera/key/generation/FileKeyGenerator.java +++ b/key-generation/src/main/java/com/quorum/tessera/key/generation/FileKeyGenerator.java @@ -68,8 +68,6 @@ public FilesystemKeyPair generate(final String filename, final ArgonOptions encr PrivateKeyType.LOCKED ) ); - finalKeys.setPrivateKey(generated.getPrivateKey().toString()); - finalKeys.setPublicKey(publicKeyBase64); LOGGER.info("Newly generated private key has been encrypted"); @@ -83,11 +81,12 @@ public FilesystemKeyPair generate(final String filename, final ArgonOptions encr PrivateKeyType.UNLOCKED ) ); - finalKeys.setPrivateKey(generated.getPrivateKey().toString()); - finalKeys.setPublicKey(publicKeyBase64); } + finalKeys.setPrivateKey(generated.getPrivateKey().encodeToBase64()); + finalKeys.setPublicKey(publicKeyBase64); + final String privateKeyJson = JaxbUtil.marshalToString(finalKeys.getConfig()); final Path resolvedPath = Paths.get(filename).toAbsolutePath(); From e1bad424ad58b009c046319be086262ac0cf713c Mon Sep 17 00:00:00 2001 From: chris-j-h Date: Mon, 17 Dec 2018 10:57:20 +0000 Subject: [PATCH 57/57] Use more specific exception types --- .../vault/hashicorp/HashicorpKeyVaultServiceFactory.java | 2 +- .../hashicorp/HashicorpKeyVaultServiceFactoryUtil.java | 4 ++-- .../hashicorp/HashicorpKeyVaultServiceFactoryTest.java | 8 ++++---- .../HashicorpKeyVaultServiceFactoryUtilTest.java | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java index df1d7c4837..cdfec98703 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactory.java @@ -61,7 +61,7 @@ else if(isOnlyOneInputNull(roleId, secretId)) { try { vaultEndpoint = VaultEndpoint.from(new URI(keyVaultConfig.getUrl())); } catch (URISyntaxException | IllegalArgumentException e) { - throw new RuntimeException("Provided Hashicorp Vault url is incorrectly formatted", e); + throw new ConfigException(new RuntimeException("Provided Hashicorp Vault url is incorrectly formatted", e)); } SslConfiguration sslConfiguration = util.configureSsl(keyVaultConfig, envProvider); diff --git a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryUtil.java b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryUtil.java index 16d9eeb1cf..5031465f14 100644 --- a/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryUtil.java +++ b/key-vault/hashicorp-key-vault/src/main/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryUtil.java @@ -85,11 +85,11 @@ ClientAuthentication configureClientAuthentication(HashicorpKeyVaultConfig keyVa } else if (Objects.isNull(roleId) != Objects.isNull(secretId)) { - throw new RuntimeException("Both " + roleIdEnvVar + " and " + secretIdEnvVar + " environment variables must be set to use the AppRole authentication method"); + throw new HashicorpCredentialNotSetException("Both " + roleIdEnvVar + " and " + secretIdEnvVar + " environment variables must be set to use the AppRole authentication method"); } else if (authToken == null){ - throw new RuntimeException("Both " + roleIdEnvVar + " and " + secretIdEnvVar + " environment variables must be set to use the AppRole authentication method. Alternatively set " + authTokenEnvVar + " to authenticate using the Token method"); + throw new HashicorpCredentialNotSetException("Both " + roleIdEnvVar + " and " + secretIdEnvVar + " environment variables must be set to use the AppRole authentication method. Alternatively set " + authTokenEnvVar + " to authenticate using the Token method"); } return new TokenAuthentication(authToken); diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java index 06e3beaf8d..ca6ef29e1e 100644 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryTest.java @@ -199,8 +199,8 @@ public void exceptionThrownIfKeyVaultConfigUrlSyntaxIncorrect() { Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider, keyVaultServiceFactoryUtil)); - assertThat(ex).isExactlyInstanceOf(RuntimeException.class); - assertThat(ex.getMessage()).isEqualTo("Provided Hashicorp Vault url is incorrectly formatted"); + assertThat(ex).isExactlyInstanceOf(ConfigException.class); + assertThat(ex.getMessage()).contains("Provided Hashicorp Vault url is incorrectly formatted"); } @Test @@ -222,8 +222,8 @@ public void exceptionThrownIfKeyVaultConfigUrlIsMalformed() { Throwable ex = catchThrowable(() -> keyVaultServiceFactory.create(config, envProvider, keyVaultServiceFactoryUtil)); - assertThat(ex).isExactlyInstanceOf(RuntimeException.class); - assertThat(ex.getMessage()).isEqualTo("Provided Hashicorp Vault url is incorrectly formatted"); + assertThat(ex).isExactlyInstanceOf(ConfigException.class); + assertThat(ex.getMessage()).contains("Provided Hashicorp Vault url is incorrectly formatted"); } private void setUpUtilMocks(HashicorpKeyVaultConfig keyVaultConfig) { diff --git a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryUtilTest.java b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryUtilTest.java index 5f1da7766a..c654be2942 100644 --- a/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryUtilTest.java +++ b/key-vault/hashicorp-key-vault/src/test/java/com/quorum/tessera/key/vault/hashicorp/HashicorpKeyVaultServiceFactoryUtilTest.java @@ -151,7 +151,7 @@ public void configureClientAuthenticationIfOnlyRoleIdSetThenException() { Throwable ex = catchThrowable(() -> util.configureClientAuthentication(keyVaultConfig, envProvider, clientHttpRequestFactory, vaultEndpoint)); - assertThat(ex).isExactlyInstanceOf(RuntimeException.class); + assertThat(ex).isExactlyInstanceOf(HashicorpCredentialNotSetException.class); assertThat(ex.getMessage()).isEqualTo("Both " + roleIdEnvVar + " and " + secretIdEnvVar + " environment variables must be set to use the AppRole authentication method"); } @@ -168,7 +168,7 @@ public void configureClientAuthenticationIfOnlySecretIdSetThenException() { Throwable ex = catchThrowable(() -> util.configureClientAuthentication(keyVaultConfig, envProvider, clientHttpRequestFactory, vaultEndpoint)); - assertThat(ex).isExactlyInstanceOf(RuntimeException.class); + assertThat(ex).isExactlyInstanceOf(HashicorpCredentialNotSetException.class); assertThat(ex.getMessage()).isEqualTo("Both " + roleIdEnvVar + " and " + secretIdEnvVar + " environment variables must be set to use the AppRole authentication method"); } @@ -201,7 +201,7 @@ public void configureClientAuthenticationIfNoEnvVarSetThenException() { Throwable ex = catchThrowable(() -> util.configureClientAuthentication(keyVaultConfig, envProvider, clientHttpRequestFactory, vaultEndpoint)); - assertThat(ex).isExactlyInstanceOf(RuntimeException.class); + assertThat(ex).isExactlyInstanceOf(HashicorpCredentialNotSetException.class); assertThat(ex.getMessage()).isEqualTo("Both " + roleIdEnvVar + " and " + secretIdEnvVar + " environment variables must be set to use the AppRole authentication method. Alternatively set " + authTokenEnvVar + " to authenticate using the Token method"); }