Add support for custom kv2 metadata.
Original pull request: gh-808 Closes gh-789
This commit is contained in:
committed by
Mark Paluch
parent
5cc5254b38
commit
c5a087acef
@@ -98,6 +98,7 @@ class VaultKeyValueMetadataTemplate implements VaultKeyValueMetadataOperations {
|
||||
.oldestVersion(Integer.parseInt(String.valueOf(metadataResponse.get("oldest_version"))))
|
||||
.updatedTime(toInstant((String) metadataResponse.get("updated_time")))
|
||||
.versions(buildVersions((Map) metadataResponse.get("versions")))
|
||||
.customMetadata((Map) metadataResponse.get("custom_metadata"))
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -115,13 +116,18 @@ class VaultKeyValueMetadataTemplate implements VaultKeyValueMetadataOperations {
|
||||
Instant deletionTime = toInstant((String) versionData.get("deletion_time"));
|
||||
boolean destroyed = (Boolean) versionData.get("destroyed");
|
||||
Versioned.Version kvVersion = Versioned.Version.from(Integer.parseInt(version));
|
||||
|
||||
return Versioned.Metadata.builder()
|
||||
Versioned.Metadata.MetadataBuilder builder = Versioned.Metadata.builder()
|
||||
.createdAt(createdTime)
|
||||
.deletedAt(deletionTime)
|
||||
.destroyed(destroyed)
|
||||
.version(kvVersion)
|
||||
.build();
|
||||
.version(kvVersion);
|
||||
|
||||
if (versionData.get("custom_metadata") != null) {
|
||||
Map<String, String> customMetadata = (Map<String, String>) versionData.get("custom_metadata");
|
||||
builder.customMetadata(customMetadata);
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -178,6 +178,11 @@ public class VaultVersionedKeyValueTemplate extends VaultKeyValue2Accessor imple
|
||||
Integer version = (Integer) responseMetadata.get("version");
|
||||
builder.version(Version.from(version));
|
||||
|
||||
if (responseMetadata.get("custom_metadata") != null) {
|
||||
Map<String, String> customMetadata = (Map<String, String>) responseMetadata.get("custom_metadata");
|
||||
builder.customMetadata(customMetadata);
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.vault.support;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@@ -41,11 +42,16 @@ public class VaultMetadataRequest {
|
||||
@JsonProperty("delete_version_after")
|
||||
private final String deleteVersionAfter;
|
||||
|
||||
private VaultMetadataRequest(int maxVersions, boolean casRequired, @Nullable Duration deleteVersionAfter) {
|
||||
@JsonProperty("custom_metadata")
|
||||
private final Map<String, String> customMetadata;
|
||||
|
||||
private VaultMetadataRequest(int maxVersions, boolean casRequired, @Nullable Duration deleteVersionAfter,
|
||||
@Nullable Map<String, String> customMetadata) {
|
||||
this.maxVersions = maxVersions;
|
||||
this.casRequired = casRequired;
|
||||
this.deleteVersionAfter = DurationParser
|
||||
.formatDuration(deleteVersionAfter != null ? deleteVersionAfter : Duration.ZERO);
|
||||
this.customMetadata = customMetadata;
|
||||
}
|
||||
|
||||
public static VaultMetadataRequestBuilder builder() {
|
||||
@@ -75,6 +81,11 @@ public class VaultMetadataRequest {
|
||||
return this.deleteVersionAfter;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Map<String, String> getCustomMetadata() {
|
||||
return this.customMetadata;
|
||||
}
|
||||
|
||||
public static class VaultMetadataRequestBuilder {
|
||||
|
||||
private int maxVersions;
|
||||
@@ -84,6 +95,9 @@ public class VaultMetadataRequest {
|
||||
@Nullable
|
||||
private Duration deleteVersionAfter;
|
||||
|
||||
@Nullable
|
||||
private Map<String, String> customMetadata;
|
||||
|
||||
/**
|
||||
* Set the number of versions to keep per key.
|
||||
* @param maxVersions
|
||||
@@ -115,11 +129,22 @@ public class VaultMetadataRequest {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the custom Metadata for the metadatarequest
|
||||
* @param customMetadata
|
||||
* @return {@link VaultMetadataRequest}
|
||||
*/
|
||||
public VaultMetadataRequestBuilder customMetadata(Map<String, String> customMetadata) {
|
||||
this.customMetadata = customMetadata;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a new {@link VaultMetadataRequest}
|
||||
*/
|
||||
public VaultMetadataRequest build() {
|
||||
return new VaultMetadataRequest(this.maxVersions, this.casRequired, this.deleteVersionAfter);
|
||||
return new VaultMetadataRequest(this.maxVersions, this.casRequired, this.deleteVersionAfter,
|
||||
this.customMetadata);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.Period;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
@@ -46,9 +47,11 @@ public class VaultMetadataResponse {
|
||||
|
||||
private final List<Versioned.Metadata> versions;
|
||||
|
||||
private final Map<String, String> customMetadata;
|
||||
|
||||
private VaultMetadataResponse(boolean casRequired, Instant createdTime, int currentVersion,
|
||||
Duration deleteVersionAfter, int maxVersions, int oldestVersion, Instant updatedTime,
|
||||
List<Versioned.Metadata> versions) {
|
||||
List<Versioned.Metadata> versions, Map<String, String> customMetadata) {
|
||||
this.casRequired = casRequired;
|
||||
this.createdTime = createdTime;
|
||||
this.currentVersion = currentVersion;
|
||||
@@ -57,6 +60,7 @@ public class VaultMetadataResponse {
|
||||
this.oldestVersion = oldestVersion;
|
||||
this.updatedTime = updatedTime;
|
||||
this.versions = versions;
|
||||
this.customMetadata = customMetadata;
|
||||
}
|
||||
|
||||
public static VaultMetadataResponseBuilder builder() {
|
||||
@@ -93,6 +97,14 @@ public class VaultMetadataResponse {
|
||||
return this.deleteVersionAfter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return KV of customMetadata. Entries can be any arbitrary key-value pairs
|
||||
*/
|
||||
@Nullable
|
||||
public Map<String, String> getCustomMetadata() {
|
||||
return this.customMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return max secret versions accepted by this key
|
||||
*/
|
||||
@@ -145,6 +157,8 @@ public class VaultMetadataResponse {
|
||||
|
||||
private List<Versioned.Metadata> versions;
|
||||
|
||||
private Map<String, String> customMetadata;
|
||||
|
||||
public VaultMetadataResponseBuilder casRequired(boolean casRequired) {
|
||||
this.casRequired = casRequired;
|
||||
return this;
|
||||
@@ -185,9 +199,15 @@ public class VaultMetadataResponse {
|
||||
return this;
|
||||
}
|
||||
|
||||
public VaultMetadataResponseBuilder customMetadata(Map<String, String> customMetadata) {
|
||||
this.customMetadata = customMetadata;
|
||||
return this;
|
||||
}
|
||||
|
||||
public VaultMetadataResponse build() {
|
||||
return new VaultMetadataResponse(this.casRequired, this.createdTime, this.currentVersion,
|
||||
this.deleteVersionAfter, this.maxVersions, this.oldestVersion, this.updatedTime, this.versions);
|
||||
this.deleteVersionAfter, this.maxVersions, this.oldestVersion, this.updatedTime, this.versions,
|
||||
this.customMetadata);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.vault.support;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -32,7 +33,7 @@ import org.springframework.util.Assert;
|
||||
* <li>Versioned secrets with {@link Metadata} attached
|
||||
* {@link Versioned#create(Object, Metadata)}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* Versioned secrets follow a lifecycle that spans from creation to destruction:
|
||||
*
|
||||
* <ol>
|
||||
@@ -44,9 +45,9 @@ import org.springframework.util.Assert;
|
||||
* </ol>
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.1
|
||||
* @see Version
|
||||
* @see Metadata
|
||||
* @since 2.1
|
||||
*/
|
||||
public class Versioned<T> {
|
||||
|
||||
@@ -223,11 +224,15 @@ public class Versioned<T> {
|
||||
|
||||
private final Version version;
|
||||
|
||||
private Metadata(Instant createdAt, @Nullable Instant deletedAt, boolean destroyed, Version version) {
|
||||
private final @Nullable Map<String, String> customMetadata;
|
||||
|
||||
private Metadata(Instant createdAt, @Nullable Instant deletedAt, boolean destroyed, Version version,
|
||||
@Nullable Map<String, String> customMetadata) {
|
||||
this.createdAt = createdAt;
|
||||
this.deletedAt = deletedAt;
|
||||
this.destroyed = destroyed;
|
||||
this.version = version;
|
||||
this.customMetadata = customMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -275,11 +280,29 @@ public class Versioned<T> {
|
||||
return this.destroyed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Metadata .
|
||||
*/
|
||||
@Nullable
|
||||
public Map<String, String> getCustomMetadata() {
|
||||
return customMetadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
String customMetadataString = "";
|
||||
if (customMetadata != null && customMetadata.keySet().size() > 0) {
|
||||
StringBuilder metadataPrintBuilder = new StringBuilder(", customMetadata=Map[");
|
||||
for (String key : customMetadata.keySet()) {
|
||||
metadataPrintBuilder.append(key).append(":").append(customMetadata.get(key)).append(" ");
|
||||
}
|
||||
metadataPrintBuilder.append("]");
|
||||
customMetadataString = metadataPrintBuilder.toString();
|
||||
}
|
||||
|
||||
return getClass().getSimpleName() + " [createdAt=" + this.createdAt + ", deletedAt=" + this.deletedAt
|
||||
+ ", destroyed=" + this.destroyed + ", version=" + this.version + ']';
|
||||
+ ", destroyed=" + this.destroyed + ", version=" + this.version + customMetadataString + ']';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -295,6 +318,8 @@ public class Versioned<T> {
|
||||
|
||||
private @Nullable Version version;
|
||||
|
||||
private @Nullable Map<String, String> customMetadata;
|
||||
|
||||
private MetadataBuilder() {
|
||||
}
|
||||
|
||||
@@ -354,6 +379,15 @@ public class Versioned<T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
public MetadataBuilder customMetadata(Map<String, String> customMetadata) {
|
||||
|
||||
Assert.notNull(customMetadata, "customMetadata should not be null");
|
||||
Assert.notEmpty(customMetadata.keySet(), "customMetadata should have at least one key");
|
||||
Assert.notEmpty(customMetadata.values(), "customMetadata should have at least one value");
|
||||
this.customMetadata = customMetadata;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the {@link Versioned.Metadata} object. Requires
|
||||
* {@link #createdAt(Instant)} and {@link #version(Versioned.Version)} to be
|
||||
@@ -365,7 +399,7 @@ public class Versioned<T> {
|
||||
Assert.notNull(this.createdAt, "CreatedAt must not be null");
|
||||
Assert.notNull(this.version, "Version must not be null");
|
||||
|
||||
return new Metadata(this.createdAt, this.deletedAt, this.destroyed, this.version);
|
||||
return new Metadata(this.createdAt, this.deletedAt, this.destroyed, this.version, this.customMetadata);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.springframework.vault.core;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -29,6 +30,7 @@ import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import org.springframework.vault.VaultException;
|
||||
import org.springframework.vault.domain.Person;
|
||||
import org.springframework.vault.support.VaultMetadataRequest;
|
||||
import org.springframework.vault.support.Versioned;
|
||||
import org.springframework.vault.support.Versioned.Metadata;
|
||||
import org.springframework.vault.support.Versioned.Version;
|
||||
@@ -103,6 +105,30 @@ class VaultVersionedKeyValueTemplateIntegrationTests extends IntegrationTestSupp
|
||||
.hasMessageContaining("check-and-set parameter did not match the current version");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWriteSecretWithCustomMetadata() {
|
||||
Person person = new Person();
|
||||
person.setFirstname("Walter");
|
||||
person.setLastname("White");
|
||||
|
||||
String key = UUID.randomUUID().toString();
|
||||
|
||||
Map<String, String> customMetadata = new HashMap<>();
|
||||
customMetadata.put("foo", "bar");
|
||||
customMetadata.put("uid", "werwer");
|
||||
|
||||
this.versionedOperations.put(key, Versioned.create(person));
|
||||
|
||||
VaultMetadataRequest request = VaultMetadataRequest.builder().customMetadata(customMetadata).build();
|
||||
|
||||
this.versionedOperations.opsForKeyValueMetadata().put(key, request);
|
||||
|
||||
Versioned<Person> versioned = this.versionedOperations.get(key, Person.class);
|
||||
|
||||
assertThat(versioned.getMetadata().getCustomMetadata().get("foo")).isEqualTo("bar");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReadAndWriteVersionedSecret() {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user