diff --git a/src/main/java/de/stklcode/jvault/connector/HTTPVaultConnector.java b/src/main/java/de/stklcode/jvault/connector/HTTPVaultConnector.java index 0d07d0a..759b5a3 100644 --- a/src/main/java/de/stklcode/jvault/connector/HTTPVaultConnector.java +++ b/src/main/java/de/stklcode/jvault/connector/HTTPVaultConnector.java @@ -504,6 +504,19 @@ public class HTTPVaultConnector implements VaultConnector { return request.get(mount + PATH_METADATA + key, new HashMap<>(), token, MetadataResponse.class); } + @Override + public void updateSecretMetadata(final String mount, final String key, final Integer maxVersions, final boolean casRequired) throws VaultConnectorException { + requireAuth(); + + Map payload = new HashMap<>(); + if (maxVersions != null) { + payload.put("max_versions", maxVersions); + } + payload.put("cas_required", casRequired); + + write(mount + PATH_METADATA + key, payload); + } + @Override public final SecretVersionResponse writeSecretData(final String mount, final String key, final Map data, final Integer cas) throws VaultConnectorException { requireAuth(); diff --git a/src/main/java/de/stklcode/jvault/connector/VaultConnector.java b/src/main/java/de/stklcode/jvault/connector/VaultConnector.java index fd310c3..a268afd 100644 --- a/src/main/java/de/stklcode/jvault/connector/VaultConnector.java +++ b/src/main/java/de/stklcode/jvault/connector/VaultConnector.java @@ -520,7 +520,7 @@ public interface VaultConnector extends AutoCloseable, Serializable { /** * Retrieve secret metadata from Vault. - * Prefix "secret/metadata" is automatically added to key. Only available for KV v2 secrets. + * Prefix "metadata" is automatically added to key. Only available for KV v2 secrets. * * @param mount Secret store mountpoint (without leading or trailing slash). * @param key Secret identifier @@ -530,6 +530,31 @@ public interface VaultConnector extends AutoCloseable, Serializable { */ MetadataResponse readSecretMetadata(final String mount, final String key) throws VaultConnectorException; + /** + * Update secret metadata. + * Prefix "secret/metadata" is automatically added to key. Only available for KV v2 secrets. + * + * @param key Secret identifier + * @throws VaultConnectorException on error + * @since 0.8 + */ + default void updateSecretMetadata(final String key, final Integer maxVersions, final boolean casRequired) throws VaultConnectorException { + updateSecretMetadata(PATH_SECRET, key, maxVersions, casRequired); + } + + /** + * Update secret metadata. + * Prefix "metadata" is automatically added to key. Only available for KV v2 secrets. + * + * @param mount Secret store mountpoint (without leading or trailing slash). + * @param key Secret identifier + * @param maxVersions Maximum number of versions (fallback to backend default if {@code null}) + * @param casRequired Specify if Check-And-Set is required for this secret. + * @throws VaultConnectorException on error + * @since 0.8 + */ + void updateSecretMetadata(final String mount, final String key, final Integer maxVersions, final boolean casRequired) throws VaultConnectorException; + /** * List available nodes from Vault. * diff --git a/src/test/java/de/stklcode/jvault/connector/HTTPVaultConnectorTest.java b/src/test/java/de/stklcode/jvault/connector/HTTPVaultConnectorTest.java index 1c20afe..27e2427 100644 --- a/src/test/java/de/stklcode/jvault/connector/HTTPVaultConnectorTest.java +++ b/src/test/java/de/stklcode/jvault/connector/HTTPVaultConnectorTest.java @@ -830,6 +830,41 @@ public class HTTPVaultConnectorTest { authUser(); assumeTrue(connector.isAuthorized()); + // Read current metadata first. + Integer maxVersions = -1; + try { + MetadataResponse res = connector.readSecretMetadata(MOUNT_KV2, SECRET2_KEY); + maxVersions = res.getMetadata().getMaxVersions(); + assumeThat("Unexpected maximum number of versions", res.getMetadata().getMaxVersions(), is(10)); + } catch (VaultConnectorException e) { + fail("Reading secret metadata failed: " + e.getMessage()); + } + + // Now update the metadata. + try { + ++maxVersions; + connector.updateSecretMetadata(MOUNT_KV2, SECRET2_KEY, maxVersions, true); + } catch (VaultConnectorException e) { + fail("Updating secret metadata failed: " + e.getMessage()); + } + + // And verify the result. + try { + MetadataResponse res = connector.readSecretMetadata(MOUNT_KV2, SECRET2_KEY); + assertThat("Unexpected maximum number of versions", res.getMetadata().getMaxVersions(), is(maxVersions)); + } catch (VaultConnectorException e) { + fail("Reading secret metadata failed: " + e.getMessage()); + } + } + + /** + * Test updating secret metadata in KV v2 store. + */ + @Test + public void updateSecretMetadataTest() { + authUser(); + assumeTrue(connector.isAuthorized()); + // Try to read accessible path with known value. try { MetadataResponse res = connector.readSecretMetadata(MOUNT_KV2, SECRET2_KEY); @@ -838,6 +873,7 @@ public class HTTPVaultConnectorTest { assertThat("Unexpected number of secret versions", res.getMetadata().getVersions().size(), is(2)); assertThat("Creation date should be present", res.getMetadata().getCreatedTime(), is(notNullValue())); assertThat("Update date should be present", res.getMetadata().getUpdatedTime(), is(notNullValue())); + assertThat("Unexpected maximum number of versions", res.getMetadata().getMaxVersions(), is(10)); } catch (VaultConnectorException e) { fail("Valid secret path could not be read: " + e.getMessage()); }