Add operations for interaction with kv metadata
Original pull request: gh-561. See gh-432.
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
package org.springframework.vault.core;
|
||||
|
||||
import org.springframework.vault.support.VaultMetadataRequest;
|
||||
import org.springframework.vault.support.VaultMetadataResponse;
|
||||
|
||||
/**
|
||||
* Interface that specifies kv metadata related operations
|
||||
*
|
||||
* @author Zakaria Amine
|
||||
* @see <a href="https://www.vaultproject.io/api-docs/secret/kv/kv-v2#update-metadata">kv backend metadata api docs</a>
|
||||
*/
|
||||
public interface VaultKeyValueMetadataOperations {
|
||||
|
||||
/**
|
||||
* permanently deletes the key metadata and all version data for the specified key. All version history will be removed.
|
||||
* @param path the secret path, must not be null or empty
|
||||
*/
|
||||
void delete(String path);
|
||||
|
||||
/**
|
||||
* retrieves the metadata and versions for the secret at the specified path.
|
||||
* @param path the secret path, must not be null or empty
|
||||
* @return {@link VaultMetadataResponse}
|
||||
*/
|
||||
VaultMetadataResponse get(String path);
|
||||
|
||||
/**
|
||||
* Updates the secret metadata, or creates new metadata if not present.
|
||||
*
|
||||
* @param path the secret path, must not be null or empty
|
||||
* @param body {@link VaultMetadataRequest}
|
||||
*/
|
||||
void put(String path, VaultMetadataRequest body);
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package org.springframework.vault.core;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Spliterator;
|
||||
import java.util.Spliterators;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.vault.client.VaultResponses;
|
||||
import org.springframework.vault.support.VaultMetadataRequest;
|
||||
import org.springframework.vault.support.VaultMetadataResponse;
|
||||
import org.springframework.vault.support.Versioned;
|
||||
import org.springframework.web.client.HttpStatusCodeException;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
public class VaultKeyValueMetadataTemplate implements VaultKeyValueMetadataOperations {
|
||||
|
||||
private final VaultOperations vaultOperations;
|
||||
|
||||
private final String basePath;
|
||||
|
||||
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||
|
||||
public VaultKeyValueMetadataTemplate(VaultOperations vaultOperations, String basePath) {
|
||||
Assert.notNull(vaultOperations, "VaultOperations must not be null");
|
||||
this.vaultOperations = vaultOperations;
|
||||
this.basePath = basePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String path) {
|
||||
Assert.hasText(path, "Path must not be empty");
|
||||
vaultOperations.delete("/"+this.basePath+"/metadata/" + path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VaultMetadataResponse get(String path) {
|
||||
Assert.hasText(path, "Path must not be empty");
|
||||
Map<String, Object> metadataResponse =
|
||||
vaultOperations.read("/" + this.basePath + "/metadata/" + path, Map.class).getData();
|
||||
|
||||
return fromMap(metadataResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String path, VaultMetadataRequest body) {
|
||||
Assert.hasText(path, "Path must not be empty");
|
||||
Assert.notNull(body, "Body must not be null");
|
||||
vaultOperations.doWithSession(restOperations -> {
|
||||
try {
|
||||
restOperations.put("/"+this.basePath+"/metadata/" + path, body);
|
||||
return null;
|
||||
}
|
||||
catch (HttpStatusCodeException e) {
|
||||
throw VaultResponses.buildException(e, path);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private VaultMetadataResponse fromMap(Map<String, Object> metadataResponse) {
|
||||
return VaultMetadataResponse.builder()
|
||||
.casRequired(Boolean.parseBoolean(String.valueOf(metadataResponse.get("cas_required"))))
|
||||
.createdTime(toInstant(metadataResponse.get("created_time")))
|
||||
.currentVersion(Integer.parseInt(String.valueOf(metadataResponse.get("current_version"))))
|
||||
.deleteVersionAfter(String.valueOf(metadataResponse.get("delete_version_after")))
|
||||
.maxVersions(Integer.parseInt(String.valueOf(metadataResponse.get("max_versions"))))
|
||||
.oldestVersion(Integer.parseInt(String.valueOf(metadataResponse.get("oldest_version"))))
|
||||
.updatedTime(toInstant(metadataResponse.get("updated_time")))
|
||||
.versions(buildVersions(metadataResponse.get("versions")))
|
||||
.build();
|
||||
}
|
||||
|
||||
private static List<Versioned.Metadata> buildVersions(Object versions) {
|
||||
try {
|
||||
JsonNode kvVersions = OBJECT_MAPPER.readTree(OBJECT_MAPPER.writeValueAsString(versions));
|
||||
|
||||
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(kvVersions.fieldNames(), Spliterator.DISTINCT), false)
|
||||
.map(version -> fromJsonNode(kvVersions.get(version), version))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
private static Versioned.Metadata fromJsonNode(JsonNode versionData, String version) {
|
||||
Instant createdTime = toInstant(versionData.get("created_time").asText());
|
||||
Instant deletionTime = Objects.equals(versionData.get("deletion_time").asText(), "") ? null : toInstant(versionData.get("deletion_time").asText());
|
||||
boolean destroyed = versionData.get("destroyed").asBoolean();
|
||||
Versioned.Version kvVersion = Versioned.Version.from(Integer.parseInt(version));
|
||||
|
||||
return Versioned.Metadata.builder().createdAt(createdTime).deletedAt(deletionTime).destroyed(destroyed).version(kvVersion).build();
|
||||
}
|
||||
|
||||
private static Instant toInstant(Object date) {
|
||||
return Instant.from(DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(String.valueOf(date)));
|
||||
}
|
||||
}
|
||||
@@ -120,4 +120,11 @@ public interface VaultVersionedKeyValueOperations extends VaultKeyValueOperation
|
||||
* @param versionsToDelete must not be {@literal null} or empty.
|
||||
*/
|
||||
void destroy(String path, Version... versionsToDelete);
|
||||
|
||||
/**
|
||||
* Return {@link VaultKeyValueMetadataOperations}
|
||||
*
|
||||
* @return the operations interface to interact with the Vault Key/Value metadata backend
|
||||
*/
|
||||
VaultKeyValueMetadataOperations opsForKeyValueMetadata();
|
||||
}
|
||||
|
||||
@@ -53,6 +53,8 @@ public class VaultVersionedKeyValueTemplate extends VaultKeyValue2Accessor
|
||||
|
||||
private final VaultOperations vaultOperations;
|
||||
|
||||
private final String path;
|
||||
|
||||
/**
|
||||
* Create a new {@link VaultVersionedKeyValueTemplate} given {@link VaultOperations}
|
||||
* and the mount {@code path}.
|
||||
@@ -65,6 +67,7 @@ public class VaultVersionedKeyValueTemplate extends VaultKeyValue2Accessor
|
||||
super(vaultOperations, path);
|
||||
|
||||
this.vaultOperations = vaultOperations;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -241,6 +244,11 @@ public class VaultVersionedKeyValueTemplate extends VaultKeyValue2Accessor
|
||||
Collections.singletonMap("versions", versions));
|
||||
}
|
||||
|
||||
@Override
|
||||
public VaultKeyValueMetadataOperations opsForKeyValueMetadata() {
|
||||
return new VaultKeyValueMetadataTemplate(vaultOperations, path);
|
||||
}
|
||||
|
||||
private static class VersionedResponse
|
||||
extends VaultResponseSupport<VaultResponseSupport<JsonNode>> {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
package org.springframework.vault.support;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* Value object to bind Vault HTTP kv metadata update API requests.
|
||||
*
|
||||
* @author Zakaria Amine
|
||||
* @see <a href="https://www.vaultproject.io/api-docs/secret/kv/kv-v2#update-metadata">Update Metadata</a>
|
||||
*/
|
||||
public class VaultMetadataRequest {
|
||||
|
||||
@JsonProperty("max_versions")
|
||||
private int maxVersions;
|
||||
|
||||
@JsonProperty("cas_required")
|
||||
private boolean casRequired;
|
||||
|
||||
@JsonProperty("delete_version_after")
|
||||
private String deleteVersionAfter;
|
||||
|
||||
VaultMetadataRequest(int maxVersions, boolean casRequired, String deleteVersionAfter) {
|
||||
this.maxVersions = maxVersions;
|
||||
this.casRequired = casRequired;
|
||||
this.deleteVersionAfter = deleteVersionAfter;
|
||||
}
|
||||
|
||||
public static VaultMetadataRequestBuilder builder() {
|
||||
return new VaultMetadataRequestBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The number of versions to keep per key.
|
||||
*/
|
||||
public int getMaxVersions() {
|
||||
return maxVersions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return If true all keys will require the cas parameter to be set on all write requests.
|
||||
*/
|
||||
public boolean isCasRequired() {
|
||||
return casRequired;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the deletion_time for all new versions written to this key. Accepts <a href="https://golang.org/pkg/time/#ParseDuration">Go duration format string</a>.
|
||||
*/
|
||||
public String getDeleteVersionAfter() {
|
||||
return deleteVersionAfter;
|
||||
}
|
||||
|
||||
public static class VaultMetadataRequestBuilder {
|
||||
|
||||
private int maxVersions;
|
||||
private boolean casRequired;
|
||||
private String deleteVersionAfter;
|
||||
|
||||
/**
|
||||
*
|
||||
* sets the number of versions to keep per key.
|
||||
*
|
||||
* @param maxVersions
|
||||
* @return {@link VaultMetadataRequest}
|
||||
*/
|
||||
public VaultMetadataRequestBuilder maxVersions(int maxVersions) {
|
||||
this.maxVersions = maxVersions;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* sets the cas_required parameter. If true all keys will require the cas parameter to be set on all write requests.
|
||||
*
|
||||
* @param casRequired
|
||||
* @return {@link VaultMetadataRequest}
|
||||
*/
|
||||
public VaultMetadataRequestBuilder casRequired(boolean casRequired) {
|
||||
this.casRequired = casRequired;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* sets the deletion_time for all new versions written to this key. Accepts <a href="https://golang.org/pkg/time/#ParseDuration">Go duration format string</a>.
|
||||
*
|
||||
* @param deleteVersionAfter
|
||||
* @return {@link VaultMetadataRequest}
|
||||
*/
|
||||
public VaultMetadataRequestBuilder deleteVersionAfter(String deleteVersionAfter) {
|
||||
this.deleteVersionAfter = deleteVersionAfter;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a new {@link VaultMetadataRequest}
|
||||
*/
|
||||
public VaultMetadataRequest build() {
|
||||
return new VaultMetadataRequest(maxVersions, casRequired, deleteVersionAfter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
package org.springframework.vault.support;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Value object to bind Vault HTTP kv read metadata API responses.
|
||||
*
|
||||
* @author Zakaria Amine
|
||||
*/
|
||||
public class VaultMetadataResponse {
|
||||
|
||||
private boolean casRequired;
|
||||
|
||||
private Instant createdTime;
|
||||
|
||||
private int currentVersion;
|
||||
|
||||
private String deleteVersionAfter;
|
||||
|
||||
private int maxVersions;
|
||||
|
||||
private int oldestVersion;
|
||||
|
||||
private Instant updatedTime;
|
||||
|
||||
private List<Versioned.Metadata> versions;
|
||||
|
||||
VaultMetadataResponse(boolean casRequired, Instant createdTime, int currentVersion, String deleteVersionAfter,
|
||||
int maxVersions, int oldestVersion, Instant updatedTime, List<Versioned.Metadata> versions) {
|
||||
this.casRequired = casRequired;
|
||||
this.createdTime = createdTime;
|
||||
this.currentVersion = currentVersion;
|
||||
this.deleteVersionAfter = deleteVersionAfter;
|
||||
this.maxVersions = maxVersions;
|
||||
this.oldestVersion = oldestVersion;
|
||||
this.updatedTime = updatedTime;
|
||||
this.versions = versions;
|
||||
}
|
||||
|
||||
public static VaultMetadataResponseBuilder builder() {return new VaultMetadataResponseBuilder();}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isCasRequired() {
|
||||
return casRequired;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the metadata creation time
|
||||
*/
|
||||
public Instant getCreatedTime() {
|
||||
return createdTime;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the active secret version
|
||||
*/
|
||||
public int getCurrentVersion() {
|
||||
return currentVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the duration after which a secret is to be deleted. 0 for unlimited duration. follows <a href="https://golang.org/pkg/time/#ParseDuration">Go duration format string</a>.
|
||||
*/
|
||||
public String getDeleteVersionAfter() {
|
||||
return deleteVersionAfter;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return max secret versions accepted by this key
|
||||
*/
|
||||
public int getMaxVersions() {
|
||||
return maxVersions;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return oldest key version
|
||||
*/
|
||||
public int getOldestVersion() {
|
||||
return oldestVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the metadata update time
|
||||
*/
|
||||
public Instant getUpdatedTime() {
|
||||
return updatedTime;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Follows the following format.
|
||||
*
|
||||
* "versions": {
|
||||
* "1": {
|
||||
* "created_time": "2020-05-18T12:23:09.895587932Z",
|
||||
* "deletion_time": "2020-05-18T12:31:00.66257744Z",
|
||||
* "destroyed": false
|
||||
* },
|
||||
* "2": {
|
||||
* "created_time": "2020-05-18T12:23:10.122081788Z",
|
||||
* "deletion_time": "",
|
||||
* "destroyed": false
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @return the key versions and their details
|
||||
*/
|
||||
public List<Versioned.Metadata> getVersions() {
|
||||
return versions;
|
||||
}
|
||||
|
||||
|
||||
public static class VaultMetadataResponseBuilder {
|
||||
|
||||
private boolean casRequired;
|
||||
private Instant createdTime;
|
||||
private int currentVersion;
|
||||
private String deleteVersionAfter;
|
||||
private int maxVersions;
|
||||
private int oldestVersion;
|
||||
private Instant updatedTime;
|
||||
private List<Versioned.Metadata> versions;
|
||||
|
||||
public VaultMetadataResponseBuilder casRequired(boolean casRequired) {
|
||||
this.casRequired = casRequired;
|
||||
return this;
|
||||
}
|
||||
|
||||
public VaultMetadataResponseBuilder createdTime(Instant createdTime) {
|
||||
this.createdTime = createdTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
public VaultMetadataResponseBuilder currentVersion(int currentVersion) {
|
||||
this.currentVersion = currentVersion;
|
||||
return this;
|
||||
}
|
||||
|
||||
public VaultMetadataResponseBuilder deleteVersionAfter(String deleteVersionAfter) {
|
||||
this.deleteVersionAfter = deleteVersionAfter;
|
||||
return this;
|
||||
}
|
||||
|
||||
public VaultMetadataResponseBuilder maxVersions(int maxVersions) {
|
||||
this.maxVersions = maxVersions;
|
||||
return this;
|
||||
}
|
||||
|
||||
public VaultMetadataResponseBuilder oldestVersion(int oldestVersion) {
|
||||
this.oldestVersion = oldestVersion;
|
||||
return this;
|
||||
}
|
||||
|
||||
public VaultMetadataResponseBuilder updatedTime(Instant updatedTime) {
|
||||
this.updatedTime = updatedTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
public VaultMetadataResponseBuilder versions(List<Versioned.Metadata> versions) {
|
||||
this.versions = versions;
|
||||
return this;
|
||||
}
|
||||
|
||||
public VaultMetadataResponse build() {
|
||||
return new VaultMetadataResponse(casRequired, createdTime, currentVersion, deleteVersionAfter, maxVersions,
|
||||
oldestVersion, updatedTime, versions);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -331,9 +331,6 @@ public class Versioned<T> {
|
||||
* @return {@code this} {@link MetadataBuilder}.
|
||||
*/
|
||||
public MetadataBuilder deletedAt(Instant deletedAt) {
|
||||
|
||||
Assert.notNull(deletedAt, "Deleted at must not be null");
|
||||
|
||||
this.deletedAt = deletedAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
package org.springframework.vault.core;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import org.springframework.vault.support.VaultMetadataRequest;
|
||||
import org.springframework.vault.support.VaultMetadataResponse;
|
||||
import org.springframework.vault.support.VaultResponse;
|
||||
import org.springframework.vault.support.Versioned;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ContextConfiguration(classes = VaultIntegrationTestConfiguration.class)
|
||||
public class VaultKeyValueMetadataTemplateIntegrationTests extends AbstractVaultKeyValueTemplateIntegrationTests {
|
||||
|
||||
private static final String SECRET_NAME = "test";
|
||||
private VaultKeyValueMetadataOperations vaultKeyValueMetadataOperations;
|
||||
|
||||
VaultKeyValueMetadataTemplateIntegrationTests() {
|
||||
super("versioned", VaultKeyValueOperationsSupport.KeyValueBackend.versioned());
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
Map<String, Object> secret = new HashMap<>();
|
||||
secret.put("key", "value");
|
||||
|
||||
kvOperations.put(SECRET_NAME, secret);
|
||||
vaultKeyValueMetadataOperations = vaultOperations.opsForVersionedKeyValue("versioned").opsForKeyValueMetadata();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReadMetadataForANewKVEntry() {
|
||||
|
||||
VaultMetadataResponse metadataResponse = vaultKeyValueMetadataOperations.get(SECRET_NAME);
|
||||
|
||||
assertThat(metadataResponse.getMaxVersions()).isEqualTo(0);
|
||||
assertThat(metadataResponse.getCurrentVersion()).isEqualTo(1);
|
||||
assertThat(metadataResponse.getVersions()).hasSize(1);
|
||||
assertThat(metadataResponse.isCasRequired()).isFalse();
|
||||
assertThat(metadataResponse.getDeleteVersionAfter()).isEqualTo("0s");
|
||||
assertThat(metadataResponse.getCreatedTime().isBefore(Instant.now())).isTrue();
|
||||
assertThat(metadataResponse.getUpdatedTime().isBefore(Instant.now())).isTrue();
|
||||
|
||||
Versioned.Metadata version1 = metadataResponse.getVersions().get(0);
|
||||
|
||||
assertThat(version1.getDeletedAt()).isNull();
|
||||
assertThat(version1.getCreatedAt()).isBefore(Instant.now());
|
||||
assertThat(version1.getVersion().getVersion()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldUpdateMetadataVersions() {
|
||||
Map<String, Object> secret = new HashMap<>();
|
||||
secret.put("newkey", "newvalue");
|
||||
kvOperations.put(SECRET_NAME, secret);
|
||||
|
||||
VaultMetadataResponse metadataResponse = vaultKeyValueMetadataOperations.get(SECRET_NAME);
|
||||
|
||||
assertThat(metadataResponse.getCurrentVersion()).isEqualTo(2);
|
||||
assertThat(metadataResponse.getVersions()).hasSize(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldUpdateKVMetadata() {
|
||||
VaultMetadataRequest request = VaultMetadataRequest.builder().casRequired(true).deleteVersionAfter("6h30m0s").maxVersions(20).build();
|
||||
|
||||
vaultKeyValueMetadataOperations.put(SECRET_NAME, request);
|
||||
|
||||
VaultMetadataResponse metadataResponseAfterUpdate = vaultKeyValueMetadataOperations.get(SECRET_NAME);
|
||||
|
||||
assertThat(metadataResponseAfterUpdate.isCasRequired()).isEqualTo(request.isCasRequired());
|
||||
assertThat(metadataResponseAfterUpdate.getMaxVersions()).isEqualTo(request.getMaxVersions());
|
||||
assertThat(metadataResponseAfterUpdate.getDeleteVersionAfter()).isEqualTo(request.getDeleteVersionAfter());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldDeleteMetadata() {
|
||||
kvOperations.delete(SECRET_NAME);
|
||||
VaultMetadataResponse metadataResponse = vaultKeyValueMetadataOperations.get(SECRET_NAME);
|
||||
Versioned.Metadata version1 = metadataResponse.getVersions().get(0);
|
||||
assertThat(version1.getDeletedAt()).isBefore(Instant.now());
|
||||
|
||||
vaultKeyValueMetadataOperations.delete(SECRET_NAME);
|
||||
|
||||
VaultResponse response = kvOperations.get(SECRET_NAME);
|
||||
|
||||
assertThat(response).isNull();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
vaultKeyValueMetadataOperations.delete(SECRET_NAME);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user