From 1de44d3d283a135462bf5881e9ee781c83669b93 Mon Sep 17 00:00:00 2001 From: Sascha Isele Date: Wed, 31 Jul 2024 17:38:28 +0200 Subject: [PATCH 1/4] feat(vault): implements custom secret folder config Signed-off-by: Sascha Isele --- .../hashicorp/HashicorpVaultExtension.java | 5 +++ .../client/HashicorpVaultClient.java | 6 +++ .../client/HashicorpVaultSettings.java | 11 +++++ .../client/HashicorpVaultClientTest.java | 42 +++++++++++++++++++ .../client/HashicorpVaultSettingsTest.java | 1 + 5 files changed, 65 insertions(+) diff --git a/extensions/common/vault/vault-hashicorp/src/main/java/org/eclipse/edc/vault/hashicorp/HashicorpVaultExtension.java b/extensions/common/vault/vault-hashicorp/src/main/java/org/eclipse/edc/vault/hashicorp/HashicorpVaultExtension.java index 28429a29dd4..abda5d94fa9 100644 --- a/extensions/common/vault/vault-hashicorp/src/main/java/org/eclipse/edc/vault/hashicorp/HashicorpVaultExtension.java +++ b/extensions/common/vault/vault-hashicorp/src/main/java/org/eclipse/edc/vault/hashicorp/HashicorpVaultExtension.java @@ -71,6 +71,9 @@ public class HashicorpVaultExtension implements ServiceExtension { @Setting(value = "The URL path of the vault's /secret endpoint", defaultValue = VAULT_API_SECRET_PATH_DEFAULT) public static final String VAULT_API_SECRET_PATH = "edc.vault.hashicorp.api.secret.path"; + @Setting(value = "The path of the folder that the secret is stored in", required = false, defaultValue = "") + public static final String VAULT_FOLDER_PATH = "edc.vault.hashicorp.folder"; + @Inject private EdcHttpClient httpClient; @@ -144,6 +147,7 @@ private HashicorpVaultSettings getSettings(ServiceExtensionContext context) { var ttl = context.getSetting(VAULT_TOKEN_TTL, VAULT_TOKEN_TTL_DEFAULT); var renewBuffer = context.getSetting(VAULT_TOKEN_RENEW_BUFFER, VAULT_TOKEN_RENEW_BUFFER_DEFAULT); var secretPath = context.getSetting(VAULT_API_SECRET_PATH, VAULT_API_SECRET_PATH_DEFAULT); + var folderPath = context.getSetting(VAULT_FOLDER_PATH, ""); return HashicorpVaultSettings.Builder.newInstance() .url(url) @@ -155,6 +159,7 @@ private HashicorpVaultSettings getSettings(ServiceExtensionContext context) { .ttl(ttl) .renewBuffer(renewBuffer) .secretPath(secretPath) + .folderPath(folderPath) .build(); } } diff --git a/extensions/common/vault/vault-hashicorp/src/main/java/org/eclipse/edc/vault/hashicorp/client/HashicorpVaultClient.java b/extensions/common/vault/vault-hashicorp/src/main/java/org/eclipse/edc/vault/hashicorp/client/HashicorpVaultClient.java index 82b78d0ab63..ad648347372 100644 --- a/extensions/common/vault/vault-hashicorp/src/main/java/org/eclipse/edc/vault/hashicorp/client/HashicorpVaultClient.java +++ b/extensions/common/vault/vault-hashicorp/src/main/java/org/eclipse/edc/vault/hashicorp/client/HashicorpVaultClient.java @@ -276,11 +276,17 @@ private HttpUrl getSecretUrl(String key, String entryType) { key = key.replace("%2F", "/"); var vaultApiPath = settings.secretPath(); + var folderPath = settings.getFolderPath(); + + if (folderPath == null) { + folderPath = ""; + } return settings.url() .newBuilder() .addPathSegments(PathUtil.trimLeadingOrEndingSlash(vaultApiPath)) .addPathSegment(entryType) + .addPathSegments(PathUtil.trimLeadingOrEndingSlash(folderPath)) .addPathSegments(key) .build(); } diff --git a/extensions/common/vault/vault-hashicorp/src/main/java/org/eclipse/edc/vault/hashicorp/client/HashicorpVaultSettings.java b/extensions/common/vault/vault-hashicorp/src/main/java/org/eclipse/edc/vault/hashicorp/client/HashicorpVaultSettings.java index 18422c997b4..0795cddffe5 100644 --- a/extensions/common/vault/vault-hashicorp/src/main/java/org/eclipse/edc/vault/hashicorp/client/HashicorpVaultSettings.java +++ b/extensions/common/vault/vault-hashicorp/src/main/java/org/eclipse/edc/vault/hashicorp/client/HashicorpVaultSettings.java @@ -33,6 +33,8 @@ public class HashicorpVaultSettings { private long renewBuffer; private String secretPath; + private String folderPath; + private HashicorpVaultSettings() { } @@ -72,6 +74,10 @@ public String secretPath() { return secretPath; } + public String getFolderPath() { + return folderPath; + } + public static class Builder { private final HashicorpVaultSettings values; @@ -129,6 +135,11 @@ public Builder secretPath(String secretPath) { return this; } + public Builder folderPath(String folderPath) { + values.folderPath = folderPath; + return this; + } + public HashicorpVaultSettings build() { requireNonNull(values.url, "Vault url must be valid"); requireNonNull(values.healthCheckPath, "Vault health check path must not be null"); diff --git a/extensions/common/vault/vault-hashicorp/src/test/java/org/eclipse/edc/vault/hashicorp/client/HashicorpVaultClientTest.java b/extensions/common/vault/vault-hashicorp/src/test/java/org/eclipse/edc/vault/hashicorp/client/HashicorpVaultClientTest.java index 015fed46479..37efd6dd2af 100644 --- a/extensions/common/vault/vault-hashicorp/src/test/java/org/eclipse/edc/vault/hashicorp/client/HashicorpVaultClientTest.java +++ b/extensions/common/vault/vault-hashicorp/src/test/java/org/eclipse/edc/vault/hashicorp/client/HashicorpVaultClientTest.java @@ -63,6 +63,7 @@ class HashicorpVaultClientTest { private static final String VAULT_URL = "https://mock.url"; + private static final String SECRET_FOLDER = "/foo"; private static final String HEALTH_PATH = "sys/health"; private static final String VAULT_TOKEN = UUID.randomUUID().toString(); private static final long VAULT_TOKEN_TTL = 5L; @@ -87,6 +88,17 @@ class HashicorpVaultClientTest { .secretPath(CUSTOM_SECRET_PATH) .build(); + private static final HashicorpVaultSettings HASHICORP_VAULT_CLIENT_CONFIG_VALUES_WITH_FOLDER = HashicorpVaultSettings.Builder.newInstance() + .url(VAULT_URL) + .healthCheckPath(HEALTH_PATH) + .healthStandbyOk(false) + .token(VAULT_TOKEN) + .ttl(VAULT_TOKEN_TTL) + .renewBuffer(RENEW_BUFFER) + .secretPath(CUSTOM_SECRET_PATH) + .folderPath(SECRET_FOLDER) + .build(); + private final EdcHttpClient httpClient = mock(); private final Monitor monitor = mock(); private final HashicorpVaultClient vaultClient = new HashicorpVaultClient( @@ -95,6 +107,12 @@ class HashicorpVaultClientTest { monitor, HASHICORP_VAULT_CLIENT_CONFIG_VALUES); + private final HashicorpVaultClient vaultClientWithFolder = new HashicorpVaultClient( + httpClient, + OBJECT_MAPPER, + monitor, + HASHICORP_VAULT_CLIENT_CONFIG_VALUES_WITH_FOLDER); + @Nested class HealthCheck { @Test @@ -393,6 +411,30 @@ void getSecret_whenApiReturns200_shouldSucceed() throws IOException { request.url().encodedPathSegments().contains(KEY))); } + @Test + void getSecret_with_folder_whenApiReturns200_shouldSucceed() throws IOException { + var ow = new ObjectMapper().writer(); + var data = GetEntryResponsePayloadGetVaultEntryData.Builder.newInstance().data(new HashMap<>(0)).build(); + var body = GetEntryResponsePayload.Builder.newInstance().data(data).build(); + var bodyString = ow.writeValueAsString(body); + var response = new Response.Builder() + .code(200) + .message("any") + .body(ResponseBody.create(bodyString, MediaType.get("application/json"))) + .protocol(Protocol.HTTP_1_1) + .request(new Request.Builder().url("http://any").build()) + .build(); + + when(httpClient.execute(any(Request.class))).thenReturn(response); + + var result = vaultClientWithFolder.getSecretValue(KEY); + + assertNotNull(result); + verify(httpClient).execute(argThat(request -> request.method().equalsIgnoreCase("GET") && + request.url().encodedPath().contains(CUSTOM_SECRET_PATH + "/data" + SECRET_FOLDER) && + request.url().encodedPathSegments().contains(KEY))); + } + @Test void setSecret_whenApiReturns200_shouldSucceed() throws IOException { var ow = new ObjectMapper().writer(); diff --git a/extensions/common/vault/vault-hashicorp/src/test/java/org/eclipse/edc/vault/hashicorp/client/HashicorpVaultSettingsTest.java b/extensions/common/vault/vault-hashicorp/src/test/java/org/eclipse/edc/vault/hashicorp/client/HashicorpVaultSettingsTest.java index 88ec1d862da..70f216e7921 100644 --- a/extensions/common/vault/vault-hashicorp/src/test/java/org/eclipse/edc/vault/hashicorp/client/HashicorpVaultSettingsTest.java +++ b/extensions/common/vault/vault-hashicorp/src/test/java/org/eclipse/edc/vault/hashicorp/client/HashicorpVaultSettingsTest.java @@ -48,6 +48,7 @@ void createSettings_withDefaultValues_shouldSucceed() { assertThat(settings.ttl()).isEqualTo(VAULT_TOKEN_TTL_DEFAULT); assertThat(settings.renewBuffer()).isEqualTo(VAULT_TOKEN_RENEW_BUFFER_DEFAULT); assertThat(settings.secretPath()).isEqualTo(SECRET_PATH); + assertThat(settings.getFolderPath()).isNull(); } @Test From 9b53e682d28ca63b5449292e92376919ed95b29d Mon Sep 17 00:00:00 2001 From: Sascha Isele Date: Mon, 9 Sep 2024 17:13:08 +0200 Subject: [PATCH 2/4] feat(vault): review suggestions Signed-off-by: Sascha Isele --- .../eclipse/edc/vault/hashicorp/HashicorpVaultExtension.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/common/vault/vault-hashicorp/src/main/java/org/eclipse/edc/vault/hashicorp/HashicorpVaultExtension.java b/extensions/common/vault/vault-hashicorp/src/main/java/org/eclipse/edc/vault/hashicorp/HashicorpVaultExtension.java index abda5d94fa9..d7e192b1ac4 100644 --- a/extensions/common/vault/vault-hashicorp/src/main/java/org/eclipse/edc/vault/hashicorp/HashicorpVaultExtension.java +++ b/extensions/common/vault/vault-hashicorp/src/main/java/org/eclipse/edc/vault/hashicorp/HashicorpVaultExtension.java @@ -71,7 +71,7 @@ public class HashicorpVaultExtension implements ServiceExtension { @Setting(value = "The URL path of the vault's /secret endpoint", defaultValue = VAULT_API_SECRET_PATH_DEFAULT) public static final String VAULT_API_SECRET_PATH = "edc.vault.hashicorp.api.secret.path"; - @Setting(value = "The path of the folder that the secret is stored in", required = false, defaultValue = "") + @Setting(value = "The path of the folder that the secret is stored in, relative to VAULT_FOLDER_PATH") public static final String VAULT_FOLDER_PATH = "edc.vault.hashicorp.folder"; @Inject @@ -147,7 +147,7 @@ private HashicorpVaultSettings getSettings(ServiceExtensionContext context) { var ttl = context.getSetting(VAULT_TOKEN_TTL, VAULT_TOKEN_TTL_DEFAULT); var renewBuffer = context.getSetting(VAULT_TOKEN_RENEW_BUFFER, VAULT_TOKEN_RENEW_BUFFER_DEFAULT); var secretPath = context.getSetting(VAULT_API_SECRET_PATH, VAULT_API_SECRET_PATH_DEFAULT); - var folderPath = context.getSetting(VAULT_FOLDER_PATH, ""); + var folderPath = context.getSetting(VAULT_FOLDER_PATH, null); return HashicorpVaultSettings.Builder.newInstance() .url(url) From 22f5da42f1867714b2f24d7a2e042d8a270edc58 Mon Sep 17 00:00:00 2001 From: Sascha Isele Date: Mon, 16 Sep 2024 18:11:05 +0200 Subject: [PATCH 3/4] feat(vault): more efficient null handling --- .../edc/vault/hashicorp/client/HashicorpVaultClient.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/extensions/common/vault/vault-hashicorp/src/main/java/org/eclipse/edc/vault/hashicorp/client/HashicorpVaultClient.java b/extensions/common/vault/vault-hashicorp/src/main/java/org/eclipse/edc/vault/hashicorp/client/HashicorpVaultClient.java index ad648347372..22a6c695af6 100644 --- a/extensions/common/vault/vault-hashicorp/src/main/java/org/eclipse/edc/vault/hashicorp/client/HashicorpVaultClient.java +++ b/extensions/common/vault/vault-hashicorp/src/main/java/org/eclipse/edc/vault/hashicorp/client/HashicorpVaultClient.java @@ -279,7 +279,12 @@ private HttpUrl getSecretUrl(String key, String entryType) { var folderPath = settings.getFolderPath(); if (folderPath == null) { - folderPath = ""; + return settings.url() + .newBuilder() + .addPathSegments(PathUtil.trimLeadingOrEndingSlash(vaultApiPath)) + .addPathSegment(entryType) + .addPathSegments(key) + .build(); } return settings.url() From 931e7a47efec9fce47b29df4a67b8004d73a8132 Mon Sep 17 00:00:00 2001 From: Sascha Isele Date: Thu, 19 Sep 2024 10:48:36 +0200 Subject: [PATCH 4/4] feat(vault): avoid duplication --- .../client/HashicorpVaultClient.java | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/extensions/common/vault/vault-hashicorp/src/main/java/org/eclipse/edc/vault/hashicorp/client/HashicorpVaultClient.java b/extensions/common/vault/vault-hashicorp/src/main/java/org/eclipse/edc/vault/hashicorp/client/HashicorpVaultClient.java index 22a6c695af6..01301b1c902 100644 --- a/extensions/common/vault/vault-hashicorp/src/main/java/org/eclipse/edc/vault/hashicorp/client/HashicorpVaultClient.java +++ b/extensions/common/vault/vault-hashicorp/src/main/java/org/eclipse/edc/vault/hashicorp/client/HashicorpVaultClient.java @@ -278,20 +278,16 @@ private HttpUrl getSecretUrl(String key, String entryType) { var vaultApiPath = settings.secretPath(); var folderPath = settings.getFolderPath(); - if (folderPath == null) { - return settings.url() - .newBuilder() - .addPathSegments(PathUtil.trimLeadingOrEndingSlash(vaultApiPath)) - .addPathSegment(entryType) - .addPathSegments(key) - .build(); - } - - return settings.url() + var builder = settings.url() .newBuilder() .addPathSegments(PathUtil.trimLeadingOrEndingSlash(vaultApiPath)) - .addPathSegment(entryType) - .addPathSegments(PathUtil.trimLeadingOrEndingSlash(folderPath)) + .addPathSegment(entryType); + + if (folderPath != null) { + builder.addPathSegments(PathUtil.trimLeadingOrEndingSlash(folderPath)); + } + + return builder .addPathSegments(key) .build(); }