diff --git a/src/main/java/io/github/jopenlibs/vault/api/Logical.java b/src/main/java/io/github/jopenlibs/vault/api/Logical.java index 6347dd2b..bbff53e4 100644 --- a/src/main/java/io/github/jopenlibs/vault/api/Logical.java +++ b/src/main/java/io/github/jopenlibs/vault/api/Logical.java @@ -202,14 +202,51 @@ public LogicalResponse read(final String path, Boolean shouldRetry, final Intege public LogicalResponse write(final String path, final Map nameValuePairs) throws VaultException { if (engineVersionForSecretPath(path).equals(2)) { - return write(path, nameValuePairs, logicalOperations.writeV2); + return write(path, nameValuePairs, logicalOperations.writeV2, null); } else { - return write(path, nameValuePairs, logicalOperations.writeV1); + return write(path, nameValuePairs, logicalOperations.writeV1, null); + } + } + + /** + *

Basic operation to store secrets. Multiple name value pairs can be stored under the same + * secret key. E.g.:

+ * + *
+ *
{@code
+     * final Map nameValuePairs = new HashMap();
+     * nameValuePairs.put("value", "foo");
+     * nameValuePairs.put("other_value", "bar");
+     *
+     * final LogicalResponse response = vault.logical().write("secret/hello", nameValuePairs);
+     * }
+ *
+ * + *

The values in these name-value pairs may be booleans, numerics, strings, or nested JSON + * objects. However, be aware that this method does not recursively parse any nested + * structures. If you wish to write arbitrary JSON objects to Vault... then you should parse + * them to JSON outside of this method, and pass them here as JSON strings.

+ * + * @param path The Vault key value to which to write (e.g. secret/hello) + * @param nameValuePairs Secret name and value pairs to store under this Vault key (can be + * @param wrapTTL Time (in seconds) which secret is wrapped + * null for writing to keys that do not need or expect any fields to be specified) + * @return The response information received from Vault + * @throws VaultException If any errors occurs with the REST request, and the maximum number of + * retries is exceeded. + */ + public LogicalResponse write(final String path, final Map nameValuePairs, + final Integer wrapTTL) + throws VaultException { + if (engineVersionForSecretPath(path).equals(2)) { + return write(path, nameValuePairs, logicalOperations.writeV2, wrapTTL); + } else { + return write(path, nameValuePairs, logicalOperations.writeV1, wrapTTL); } } private LogicalResponse write(final String path, final Map nameValuePairs, - final logicalOperations operation) throws VaultException { + final logicalOperations operation, final Integer wrapTTL) throws VaultException { return retry(attempt -> { JsonObject requestJson = Json.object(); @@ -246,6 +283,7 @@ private LogicalResponse write(final String path, final Map nameV .header("X-Vault-Token", config.getToken()) .header("X-Vault-Namespace", this.nameSpace) .header("X-Vault-Request", "true") + .header("X-Vault-Wrap-TTL", wrapTTL != null ? wrapTTL.toString() : null) .connectTimeoutSeconds(config.getOpenTimeout()) .readTimeoutSeconds(config.getReadTimeout()) .sslVerification(config.getSslConfig().isVerify()) diff --git a/src/main/java/io/github/jopenlibs/vault/response/LogicalResponse.java b/src/main/java/io/github/jopenlibs/vault/response/LogicalResponse.java index b405765b..ace83c93 100644 --- a/src/main/java/io/github/jopenlibs/vault/response/LogicalResponse.java +++ b/src/main/java/io/github/jopenlibs/vault/response/LogicalResponse.java @@ -22,6 +22,7 @@ public class LogicalResponse extends VaultResponse { private List listData = new ArrayList<>(); private JsonObject dataObject = null; private String leaseId; + private WrapResponse wrapResponse; private Boolean renewable; private Long leaseDuration; @@ -61,6 +62,10 @@ public Long getLeaseDuration() { return leaseDuration; } + public WrapResponse getWrapResponse() { + return wrapResponse; + } + private void parseMetadataFields() { try { final String jsonString = new String(getRestResponse().getBody(), @@ -70,6 +75,8 @@ private void parseMetadataFields() { this.leaseId = jsonObject.get("lease_id").asString(); this.renewable = jsonObject.get("renewable").asBoolean(); this.leaseDuration = jsonObject.get("lease_duration").asLong(); + + this.wrapResponse = new WrapResponse(getRestResponse(), getRetries()); } catch (Exception ignored) { } } diff --git a/src/test-integration/java/io/github/jopenlibs/vault/api/LogicalTests.java b/src/test-integration/java/io/github/jopenlibs/vault/api/LogicalTests.java index 8b3207db..32608990 100644 --- a/src/test-integration/java/io/github/jopenlibs/vault/api/LogicalTests.java +++ b/src/test-integration/java/io/github/jopenlibs/vault/api/LogicalTests.java @@ -5,6 +5,7 @@ import io.github.jopenlibs.vault.VaultException; import io.github.jopenlibs.vault.response.AuthResponse; import io.github.jopenlibs.vault.response.LogicalResponse; +import io.github.jopenlibs.vault.response.WrapResponse; import io.github.jopenlibs.vault.util.VaultContainer; import java.io.IOException; import java.util.HashMap; @@ -21,6 +22,8 @@ import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertFalse; +import static junit.framework.TestCase.assertNotNull; +import static junit.framework.TestCase.assertNotSame; import static junit.framework.TestCase.assertTrue; /** @@ -66,6 +69,34 @@ public void testWriteAndRead() throws VaultException { assertEquals(value, valueRead); } + /** + * Write a wrapped secret and verify that it can be read, using KV Secrets engine version 2. + * + * @throws VaultException On error. + */ + @Test + public void testWriteAndReadWrapped() throws VaultException { + final String pathToWrite = "secret/hellowrapped"; + final String pathToRead = "secret/hellowrapped"; + final int wrapTTL = 60; + + final String value = "world"; + final Vault vault = container.getRootVault(); + + final Map testMap = new HashMap<>(); + testMap.put("value", value); + + LogicalResponse response = vault.logical().write(pathToWrite, testMap, wrapTTL); + + final String valueRead = vault.logical().read(pathToRead).getData().get("value"); + WrapResponse wrapResponse = response.getWrapResponse(); + assertNotNull(response.getWrapResponse()); + + assertNotSame("", wrapResponse.getToken()); + assertEquals(wrapTTL, wrapResponse.getTtl()); + assertEquals(value, valueRead); + } + /** * Write a secret and verify that it can be read, using KV Secrets engine version 1. *