Polishing
Add author tags. Return generated tweak from encode and batch encode methods. Switch simple encode/decode methods to default interface methods. Introduce VaultTransformContext.isEmpty() method to check if the context is empty. Align equals/hashCode methods with Spring style. Reformat code. Resolves gh-570.
This commit is contained in:
@@ -34,6 +34,7 @@ import org.springframework.web.client.RestClientException;
|
||||
* the {@link #doWithVault(RestOperationsCallback) without a session}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Lauren Voswinkel
|
||||
* @see #doWithSession(RestOperationsCallback)
|
||||
* @see #doWithVault(RestOperationsCallback)
|
||||
* @see org.springframework.web.client.RestOperations
|
||||
|
||||
@@ -15,21 +15,29 @@
|
||||
*/
|
||||
package org.springframework.vault.core;
|
||||
|
||||
import org.springframework.vault.support.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.vault.support.Ciphertext;
|
||||
import org.springframework.vault.support.Plaintext;
|
||||
import org.springframework.vault.support.TransformCiphertext;
|
||||
import org.springframework.vault.support.TransformPlaintext;
|
||||
import org.springframework.vault.support.VaultTransformContext;
|
||||
import org.springframework.vault.support.VaultTransformDecodeResult;
|
||||
import org.springframework.vault.support.VaultTransformEncodeResult;
|
||||
|
||||
/**
|
||||
* Interface that specifies operations using the {@code transform} backend.
|
||||
*
|
||||
* @author Lauren Voswinkel
|
||||
* @author Mark Paluch
|
||||
* @since 2.3
|
||||
* @see <a href="https://www.vaultproject.io/docs/secrets/transform/index.html">Transform
|
||||
* Secrets Engine</a>
|
||||
* @since 2.3
|
||||
*/
|
||||
public interface VaultTransformOperations {
|
||||
|
||||
/**
|
||||
* Encodes the provided plaintext using the named role.
|
||||
* Encode the provided plaintext using the named role.
|
||||
* @param roleName must not be empty or {@literal null}.
|
||||
* @param plaintext must not be empty or {@literal null}.
|
||||
* @return cipher text.
|
||||
@@ -37,7 +45,7 @@ public interface VaultTransformOperations {
|
||||
String encode(String roleName, String plaintext);
|
||||
|
||||
/**
|
||||
* Encodes the provided plaintext using the named role.
|
||||
* Encode the provided plaintext using the named role.
|
||||
* @param roleName must not be empty or {@literal null}.
|
||||
* @param plaintext must not be {@literal null}.
|
||||
* @return cipher text.
|
||||
@@ -45,14 +53,16 @@ public interface VaultTransformOperations {
|
||||
TransformCiphertext encode(String roleName, TransformPlaintext plaintext);
|
||||
|
||||
/**
|
||||
* Encodes the provided plaintext using the named role.
|
||||
* Encode the provided plaintext using the named role.
|
||||
* @param roleName must not be empty or {@literal null}.
|
||||
* @param plaintext must not be empty or {@literal null}.
|
||||
* @param transformRequest must not be {@literal null}. Use
|
||||
* {@link VaultTransformContext#empty()} if no request options provided.
|
||||
* @return cipher text.
|
||||
*/
|
||||
String encode(String roleName, byte[] plaintext, VaultTransformContext transformRequest);
|
||||
default TransformCiphertext encode(String roleName, byte[] plaintext, VaultTransformContext transformRequest) {
|
||||
return encode(roleName, TransformPlaintext.of(plaintext).with(transformRequest));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode the provided batch of plaintext using the role given and transformation in
|
||||
@@ -71,7 +81,9 @@ public interface VaultTransformOperations {
|
||||
* @param ciphertext must not be empty or {@literal null}.
|
||||
* @return plain text.
|
||||
*/
|
||||
String decode(String roleName, String ciphertext);
|
||||
default String decode(String roleName, String ciphertext) {
|
||||
return decode(roleName, TransformCiphertext.of(ciphertext)).asString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the provided ciphertext using the named role.
|
||||
@@ -101,4 +113,5 @@ public interface VaultTransformOperations {
|
||||
* @return the decrypted result in the order of {@code batchRequest} ciphertexts.
|
||||
*/
|
||||
List<VaultTransformDecodeResult> decode(String roleName, List<TransformCiphertext> batchRequest);
|
||||
|
||||
}
|
||||
|
||||
@@ -15,20 +15,29 @@
|
||||
*/
|
||||
package org.springframework.vault.core;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.Base64Utils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.vault.VaultException;
|
||||
import org.springframework.vault.support.*;
|
||||
|
||||
import java.util.*;
|
||||
import org.springframework.vault.support.TransformCiphertext;
|
||||
import org.springframework.vault.support.TransformPlaintext;
|
||||
import org.springframework.vault.support.VaultResponse;
|
||||
import org.springframework.vault.support.VaultTransformContext;
|
||||
import org.springframework.vault.support.VaultTransformDecodeResult;
|
||||
import org.springframework.vault.support.VaultTransformEncodeResult;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link VaultTransformOperations}.
|
||||
*
|
||||
* @author Lauren Voswinkel
|
||||
* @author Mark Paluch
|
||||
* @since 2.3
|
||||
*/
|
||||
public class VaultTransformTemplate implements VaultTransformOperations {
|
||||
@@ -72,27 +81,16 @@ public class VaultTransformTemplate implements VaultTransformOperations {
|
||||
Assert.hasText(roleName, "Role name must not be empty");
|
||||
Assert.notNull(plaintext, "Plaintext must not be null");
|
||||
|
||||
String ciphertext = encode(roleName, plaintext.getPlaintext(), plaintext.getContext());
|
||||
|
||||
return toCiphertext(ciphertext, plaintext.getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encode(String roleName, byte[] plaintext, VaultTransformContext transformContext) {
|
||||
|
||||
Assert.hasText(roleName, "Role name must not be empty");
|
||||
Assert.notNull(plaintext, "Plaintext must not be null");
|
||||
Assert.notNull(transformContext, "VaultTransformContext must not be null");
|
||||
|
||||
Map<String, String> request = new LinkedHashMap<>();
|
||||
|
||||
String value = new String(plaintext);
|
||||
request.put("value", value);
|
||||
request.put("value", plaintext.asString());
|
||||
|
||||
applyTransformOptions(transformContext, request);
|
||||
applyTransformOptions(plaintext.getContext(), request);
|
||||
|
||||
return (String) this.vaultOperations.write(String.format("%s/encode/%s", this.path, roleName), request)
|
||||
.getRequiredData().get("encoded_value");
|
||||
Map<String, Object> data = this.vaultOperations
|
||||
.write(String.format("%s/encode/%s", this.path, roleName), request).getRequiredData();
|
||||
|
||||
return toCiphertext(data, plaintext.getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -109,9 +107,7 @@ public class VaultTransformTemplate implements VaultTransformOperations {
|
||||
|
||||
vaultRequest.put("value", request.asString());
|
||||
|
||||
if (request.getContext() != null) {
|
||||
applyTransformOptions(request.getContext(), vaultRequest);
|
||||
}
|
||||
applyTransformOptions(request.getContext(), vaultRequest);
|
||||
|
||||
batch.add(vaultRequest);
|
||||
}
|
||||
@@ -122,22 +118,6 @@ public class VaultTransformTemplate implements VaultTransformOperations {
|
||||
return toEncodedResults(vaultResponse, batchRequest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decode(String roleName, String ciphertext) {
|
||||
|
||||
Assert.hasText(roleName, "Key name must not be empty");
|
||||
Assert.hasText(ciphertext, "Ciphertext must not be empty");
|
||||
|
||||
Map<String, String> request = new LinkedHashMap<>();
|
||||
|
||||
request.put("value", ciphertext);
|
||||
|
||||
String plaintext = (String) this.vaultOperations
|
||||
.write(String.format("%s/decode/%s", this.path, roleName), request).getRequiredData().get("decoded_value");
|
||||
|
||||
return new String(plaintext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransformPlaintext decode(String roleName, TransformCiphertext ciphertext) {
|
||||
|
||||
@@ -162,10 +142,8 @@ public class VaultTransformTemplate implements VaultTransformOperations {
|
||||
|
||||
applyTransformOptions(transformContext, request);
|
||||
|
||||
String plaintext = (String) this.vaultOperations
|
||||
.write(String.format("%s/decode/%s", this.path, roleName), request).getRequiredData().get("decoded_value");
|
||||
|
||||
return plaintext;
|
||||
return (String) this.vaultOperations.write(String.format("%s/decode/%s", this.path, roleName), request)
|
||||
.getRequiredData().get("decoded_value");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -181,10 +159,7 @@ public class VaultTransformTemplate implements VaultTransformOperations {
|
||||
Map<String, String> vaultRequest = new LinkedHashMap<>(2);
|
||||
|
||||
vaultRequest.put("value", request.getCiphertext());
|
||||
|
||||
if (request.getContext() != null) {
|
||||
applyTransformOptions(request.getContext(), vaultRequest);
|
||||
}
|
||||
applyTransformOptions(request.getContext(), vaultRequest);
|
||||
|
||||
batch.add(vaultRequest);
|
||||
}
|
||||
@@ -207,7 +182,7 @@ public class VaultTransformTemplate implements VaultTransformOperations {
|
||||
}
|
||||
|
||||
private static List<VaultTransformEncodeResult> toEncodedResults(VaultResponse vaultResponse,
|
||||
List<TransformPlaintext> batchRequest) {
|
||||
List<TransformPlaintext> batchRequest) {
|
||||
|
||||
List<VaultTransformEncodeResult> result = new ArrayList<>(batchRequest.size());
|
||||
List<Map<String, String>> batchData = getBatchData(vaultResponse);
|
||||
@@ -223,7 +198,7 @@ public class VaultTransformTemplate implements VaultTransformOperations {
|
||||
encoded = new VaultTransformEncodeResult(new VaultException(data.get("error")));
|
||||
}
|
||||
else {
|
||||
encoded = new VaultTransformEncodeResult(toCiphertext(data.get("encoded_value"), plaintext.getContext()));
|
||||
encoded = new VaultTransformEncodeResult(toCiphertext(data, plaintext.getContext()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -260,7 +235,8 @@ public class VaultTransformTemplate implements VaultTransformOperations {
|
||||
return result;
|
||||
}
|
||||
|
||||
private static VaultTransformDecodeResult getDecryptionResult(Map<String, String> data, TransformCiphertext ciphertext) {
|
||||
private static VaultTransformDecodeResult getDecryptionResult(Map<String, String> data,
|
||||
TransformCiphertext ciphertext) {
|
||||
|
||||
if (StringUtils.hasText(data.get("error"))) {
|
||||
return new VaultTransformDecodeResult(new VaultException(data.get("error")));
|
||||
@@ -268,15 +244,26 @@ public class VaultTransformTemplate implements VaultTransformOperations {
|
||||
|
||||
if (StringUtils.hasText(data.get("decoded_value"))) {
|
||||
|
||||
byte[] plaintext = data.get("decoded_value").getBytes();
|
||||
return new VaultTransformDecodeResult(TransformPlaintext.of(plaintext).with(ciphertext.getContext()));
|
||||
return new VaultTransformDecodeResult(
|
||||
TransformPlaintext.of(data.get("decoded_value")).with(ciphertext.getContext()));
|
||||
}
|
||||
|
||||
return new VaultTransformDecodeResult(TransformPlaintext.empty().with(ciphertext.getContext()));
|
||||
}
|
||||
|
||||
private static TransformCiphertext toCiphertext(String ciphertext, @Nullable VaultTransformContext context) {
|
||||
return context != null ? TransformCiphertext.of(ciphertext).with(context) : TransformCiphertext.of(ciphertext);
|
||||
private static TransformCiphertext toCiphertext(Map<String, ?> data, VaultTransformContext context) {
|
||||
|
||||
String ciphertext = (String) data.get("encoded_value");
|
||||
|
||||
VaultTransformContext contextToUse = context;
|
||||
if (data.containsKey("tweak")) {
|
||||
byte[] tweak = Base64Utils.decodeFromString((String) data.get("tweak"));
|
||||
contextToUse = VaultTransformContext.builder().transformation(context.getTransformation()).tweak(tweak)
|
||||
.build();
|
||||
}
|
||||
|
||||
return contextToUse.isEmpty() ? TransformCiphertext.of(ciphertext)
|
||||
: TransformCiphertext.of(ciphertext).with(contextToUse);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -284,14 +271,4 @@ public class VaultTransformTemplate implements VaultTransformOperations {
|
||||
return (List<Map<String, String>>) vaultResponse.getRequiredData().get("batch_results");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append(getClass().getSimpleName());
|
||||
sb.append(" [vaultOperations=").append(this.vaultOperations);
|
||||
sb.append(", path='").append(this.path).append('\'');
|
||||
sb.append(']');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -38,7 +38,8 @@ public class TransformCiphertext {
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create {@link TransformCiphertext} from the given {@code ciphertext}.
|
||||
* Factory method to create {@link TransformCiphertext} from the given
|
||||
* {@code ciphertext}.
|
||||
* @param ciphertext the ciphertext to decrypt, must not be {@literal null} or empty.
|
||||
* @return the {@link TransformCiphertext} for {@code ciphertext}.
|
||||
*/
|
||||
@@ -58,8 +59,8 @@ public class TransformCiphertext {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link TransformCiphertext} object from this ciphertext associated with the
|
||||
* given {@link VaultTransformContext}.
|
||||
* Create a new {@link TransformCiphertext} object from this ciphertext associated
|
||||
* with the given {@link VaultTransformContext}.
|
||||
* @param context transit context, must not be {@literal null}.
|
||||
* @return the new {@link TransformCiphertext} object.
|
||||
*/
|
||||
|
||||
@@ -16,9 +16,7 @@
|
||||
package org.springframework.vault.support;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Value object representing plaintext with an optional {@link VaultTransformContext}.
|
||||
@@ -44,7 +42,6 @@ public class TransformPlaintext {
|
||||
/**
|
||||
* Factory method to create an empty {@link TransformPlaintext}.
|
||||
* @return the empty {@link TransformPlaintext} object.
|
||||
* @since 1.1.2
|
||||
*/
|
||||
public static TransformPlaintext empty() {
|
||||
return EMPTY;
|
||||
@@ -93,12 +90,15 @@ public class TransformPlaintext {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link TransformPlaintext} object from this plaintext associated with the given
|
||||
* {@link VaultTransformContext}.
|
||||
* Create a new {@link TransformPlaintext} object from this plaintext associated with
|
||||
* the given {@link VaultTransformContext}.
|
||||
* @param context transform context.
|
||||
* @return the new {@link TransformPlaintext} object.
|
||||
*/
|
||||
public TransformPlaintext with(VaultTransformContext context) {
|
||||
|
||||
Assert.notNull(context, "VaultTransformContext must not be null");
|
||||
|
||||
return new TransformPlaintext(getPlaintext(), context);
|
||||
}
|
||||
|
||||
@@ -116,14 +116,17 @@ public class TransformPlaintext {
|
||||
return true;
|
||||
if (!(o instanceof TransformPlaintext))
|
||||
return false;
|
||||
TransformPlaintext plaintext1 = (TransformPlaintext) o;
|
||||
return Arrays.equals(this.plaintext, plaintext1.plaintext) && this.context.equals(plaintext1.context);
|
||||
TransformPlaintext that = (TransformPlaintext) o;
|
||||
if (!ObjectUtils.nullSafeEquals(this.plaintext, that.plaintext)) {
|
||||
return false;
|
||||
}
|
||||
return ObjectUtils.nullSafeEquals(this.context, that.context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = Objects.hash(this.context);
|
||||
result = 31 * result + Arrays.hashCode(this.plaintext);
|
||||
int result = ObjectUtils.nullSafeHashCode(this.plaintext);
|
||||
result = 31 * result + ObjectUtils.nullSafeHashCode(this.context);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,10 +15,12 @@
|
||||
*/
|
||||
package org.springframework.vault.support;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Transform backend encode/decode context object.
|
||||
*
|
||||
@@ -37,7 +39,7 @@ public class VaultTransformContext {
|
||||
|
||||
private final byte[] tweak;
|
||||
|
||||
VaultTransformContext(String transformation, byte[] tweak) {
|
||||
private VaultTransformContext(String transformation, byte[] tweak) {
|
||||
this.transformation = transformation;
|
||||
this.tweak = tweak;
|
||||
}
|
||||
@@ -74,6 +76,15 @@ public class VaultTransformContext {
|
||||
return builder().tweak(tweak).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this object is an empty one. That is, transformation and tweak are
|
||||
* both empty.
|
||||
* @return {@code true} if this object is empty.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return StringUtils.isEmpty(this.transformation) && ObjectUtils.isEmpty(this.tweak);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the transformation name.
|
||||
*/
|
||||
@@ -114,7 +125,7 @@ public class VaultTransformContext {
|
||||
|
||||
private byte[] tweak = new byte[0];
|
||||
|
||||
VaultTransformRequestBuilder() {
|
||||
private VaultTransformRequestBuilder() {
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,8 +30,8 @@ public class VaultTransformDecodeResult extends AbstractResult<TransformPlaintex
|
||||
private final @Nullable TransformPlaintext plaintext;
|
||||
|
||||
/**
|
||||
* Create {@link VaultTransformDecodeResult} for a successfully decrypted {@link TransformPlaintext}
|
||||
* .
|
||||
* Create {@link VaultTransformDecodeResult} for a successfully decrypted
|
||||
* {@link TransformPlaintext} .
|
||||
* @param plaintext must not be {@literal null}.
|
||||
*/
|
||||
public VaultTransformDecodeResult(TransformPlaintext plaintext) {
|
||||
|
||||
@@ -66,4 +66,5 @@ public class VaultTransformEncodeResult extends AbstractResult<TransformCipherte
|
||||
TransformCiphertext ciphertext = get();
|
||||
return ciphertext == null ? null : ciphertext.getCiphertext();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.junit.jupiter.api.Assumptions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
@@ -49,7 +50,9 @@ class VaultTemplateTransformIntegrationTests extends IntegrationTestSupport {
|
||||
|
||||
@BeforeEach
|
||||
void before() {
|
||||
Assumptions.assumeTrue(prepare().getVersion().isEnterprise(), "Transform Secrets Engine requires enterprise version");
|
||||
|
||||
Assumptions.assumeTrue(prepare().getVersion().isEnterprise(),
|
||||
"Transform Secrets Engine requires enterprise version");
|
||||
|
||||
VaultSysOperations adminOperations = this.vaultOperations.opsForSys();
|
||||
|
||||
@@ -60,7 +63,8 @@ class VaultTemplateTransformIntegrationTests extends IntegrationTestSupport {
|
||||
}
|
||||
|
||||
// Write a transformation/role
|
||||
this.vaultOperations.write("transform/transformation/myssn", "{\"type\": \"fpe\", \"template\": \"builtin/socialsecuritynumber\", \"allowed_roles\": [\"myrole\"]}");
|
||||
this.vaultOperations.write("transform/transformation/myssn",
|
||||
"{\"type\": \"fpe\", \"template\": \"builtin/socialsecuritynumber\", \"allowed_roles\": [\"myrole\"]}");
|
||||
this.vaultOperations.write("transform/role/myrole", "{\"transformations\": [\"myssn\"]}");
|
||||
}
|
||||
|
||||
@@ -73,9 +77,8 @@ class VaultTemplateTransformIntegrationTests extends IntegrationTestSupport {
|
||||
@Test
|
||||
void shouldEncode() {
|
||||
|
||||
VaultResponse response = this.vaultOperations.write("transform/encode/myrole",
|
||||
String.format("{\"value\": \"123-45-6789\", \"tweak\": \"%s\"}",
|
||||
Base64Utils.encodeToString("somenum".getBytes())));
|
||||
VaultResponse response = this.vaultOperations.write("transform/encode/myrole", String.format(
|
||||
"{\"value\": \"123-45-6789\", \"tweak\": \"%s\"}", Base64Utils.encodeToString("somenum".getBytes())));
|
||||
|
||||
assertThat((String) response.getRequiredData().get("encoded_value")).isNotEmpty();
|
||||
}
|
||||
@@ -84,19 +87,14 @@ class VaultTemplateTransformIntegrationTests extends IntegrationTestSupport {
|
||||
void shouldEncodeAndDecode() {
|
||||
|
||||
String value = "123-45-6789";
|
||||
VaultResponse response = this.vaultOperations.write("transform/encode/myrole",
|
||||
String.format("{\"value\": \"%s\", \"tweak\": \"%s\"}",
|
||||
value,
|
||||
Base64Utils.encodeToString("somenum".getBytes())));
|
||||
VaultResponse response = this.vaultOperations.write("transform/encode/myrole", String.format(
|
||||
"{\"value\": \"%s\", \"tweak\": \"%s\"}", value, Base64Utils.encodeToString("somenum".getBytes())));
|
||||
|
||||
String encoded = (String) response.getRequiredData().get("encoded_value");
|
||||
VaultResponse decoded = this.vaultOperations.write("transform/decode/myrole",
|
||||
String.format("{\"value\": \"%s\", \"tweak\": \"%s\"}",
|
||||
encoded,
|
||||
Base64Utils.encodeToString("somenum".getBytes())));
|
||||
VaultResponse decoded = this.vaultOperations.write("transform/decode/myrole", String.format(
|
||||
"{\"value\": \"%s\", \"tweak\": \"%s\"}", encoded, Base64Utils.encodeToString("somenum".getBytes())));
|
||||
|
||||
assertThat((String) decoded.getRequiredData().get("decoded_value"))
|
||||
.isEqualTo(value);
|
||||
assertThat((String) decoded.getRequiredData().get("decoded_value")).isEqualTo(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,17 +15,22 @@
|
||||
*/
|
||||
package org.springframework.vault.core;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assumptions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import org.springframework.vault.VaultException;
|
||||
import org.springframework.vault.support.TransformPlaintext;
|
||||
import org.springframework.vault.support.TransformCiphertext;
|
||||
import org.springframework.vault.support.TransformPlaintext;
|
||||
import org.springframework.vault.support.VaultMount;
|
||||
import org.springframework.vault.support.VaultTransformContext;
|
||||
import org.springframework.vault.support.VaultTransformDecodeResult;
|
||||
@@ -34,16 +39,14 @@ import org.springframework.vault.util.IntegrationTestSupport;
|
||||
import org.springframework.vault.util.RequiresVaultVersion;
|
||||
import org.springframework.vault.util.Version;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link VaultTemplate} using the {@code transform} backend.
|
||||
*
|
||||
* @author Lauren Voswinkel
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ContextConfiguration(classes = VaultIntegrationTestConfiguration.class)
|
||||
@@ -59,7 +62,9 @@ class VaultTransformTemplateIntegrationTests extends IntegrationTestSupport {
|
||||
|
||||
@BeforeEach
|
||||
void before() {
|
||||
Assumptions.assumeTrue(prepare().getVersion().isEnterprise(), "Transform Secrets Engine requires enterprise version");
|
||||
|
||||
Assumptions.assumeTrue(prepare().getVersion().isEnterprise(),
|
||||
"Transform Secrets Engine requires enterprise version");
|
||||
|
||||
VaultSysOperations adminOperations = this.vaultOperations.opsForSys();
|
||||
this.transformOperations = this.vaultOperations.opsForTransform();
|
||||
@@ -71,38 +76,44 @@ class VaultTransformTemplateIntegrationTests extends IntegrationTestSupport {
|
||||
}
|
||||
|
||||
// Write a transformation/role
|
||||
this.vaultOperations.write("transform/transformation/myssn", "{" +
|
||||
"\"type\": \"fpe\", " +
|
||||
"\"template\": \"builtin/socialsecuritynumber\", " +
|
||||
"\"allowed_roles\": [\"myrole\"]}"
|
||||
);
|
||||
this.vaultOperations.write("transform/transformation/myssn", "{" + "\"type\": \"fpe\", "
|
||||
+ "\"template\": \"builtin/socialsecuritynumber\", " + "\"allowed_roles\": [\"myrole\"]}");
|
||||
this.vaultOperations.write("transform/role/myrole", "{\"transformations\": [\"myssn\", \"internalssn\"]}");
|
||||
|
||||
this.vaultOperations.write("transform/transformation/internalssn", "{" +
|
||||
"\"type\": \"fpe\", " +
|
||||
"\"tweak_source\": \"internal\", " +
|
||||
"\"template\": \"builtin/socialsecuritynumber\", " +
|
||||
"\"allowed_roles\": [\"myrole\", \"internalrole\"]}"
|
||||
);
|
||||
this.vaultOperations.write("transform/transformation/internalssn",
|
||||
"{" + "\"type\": \"fpe\", " + "\"tweak_source\": \"internal\", "
|
||||
+ "\"template\": \"builtin/socialsecuritynumber\", "
|
||||
+ "\"allowed_roles\": [\"myrole\", \"internalrole\"]}");
|
||||
|
||||
this.vaultOperations.write("transform/transformation/generatedssn",
|
||||
"{" + "\"type\": \"fpe\", " + "\"tweak_source\": \"generated\", "
|
||||
+ "\"template\": \"builtin/socialsecuritynumber\", "
|
||||
+ "\"allowed_roles\": [\"generatedrole\"]}");
|
||||
|
||||
this.vaultOperations.write("transform/role/internalrole", "{\"transformations\": [\"internalssn\"]}");
|
||||
this.vaultOperations.write("transform/role/generatedrole", "{\"transformations\": [\"generatedssn\"]}");
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
this.vaultOperations.delete("transform/role/myrole");
|
||||
this.vaultOperations.delete("transform/role/internalrole");
|
||||
this.vaultOperations.delete("transform/role/generatedrole");
|
||||
this.vaultOperations.delete("transform/transformation/myssn");
|
||||
this.vaultOperations.delete("transform/transformation/internalssn");
|
||||
this.vaultOperations.delete("transform/transformation/generatedssn");
|
||||
}
|
||||
|
||||
@Test
|
||||
void encodeCreatesCiphertextWithTransformationAndTweak() {
|
||||
void encodeCreatesCiphertextWithTransformationAndProvidedTweak() {
|
||||
|
||||
VaultTransformContext transformRequest = VaultTransformContext.builder().transformation("myssn")
|
||||
.tweak("somenum".getBytes())
|
||||
.build();
|
||||
.tweak("somenum".getBytes()).build();
|
||||
|
||||
String response = this.transformOperations.encode("myrole", "123-45-6789".getBytes(), transformRequest);
|
||||
assertThat(response).matches("[0-9]{3}-[0-9]{2}-[0-9]{4}");
|
||||
TransformCiphertext ciphertext = this.transformOperations.encode("myrole", "123-45-6789".getBytes(),
|
||||
transformRequest);
|
||||
assertThat(ciphertext.getCiphertext()).matches("[0-9]{3}-[0-9]{2}-[0-9]{4}");
|
||||
assertThat(new String(ciphertext.getContext().getTweak())).isEqualTo("somenum");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -110,14 +121,9 @@ class VaultTransformTemplateIntegrationTests extends IntegrationTestSupport {
|
||||
|
||||
VaultTransformContext transformRequest = VaultTransformContext.builder().transformation("myssn").build();
|
||||
|
||||
Exception exception = assertThrows(VaultException.class, () -> {
|
||||
assertThatExceptionOfType(VaultException.class).isThrownBy(() -> {
|
||||
this.transformOperations.encode("myrole", "123-45-6789".getBytes(), transformRequest);
|
||||
});
|
||||
|
||||
String expectedMessage = "incorrect tweak size provided";
|
||||
String actualMessage = exception.getMessage();
|
||||
|
||||
assertThat(actualMessage).contains(expectedMessage);
|
||||
}).withMessageContaining("incorrect tweak size provided");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -125,45 +131,64 @@ class VaultTransformTemplateIntegrationTests extends IntegrationTestSupport {
|
||||
|
||||
VaultTransformContext transformRequest = VaultTransformContext.builder().transformation("internalssn").build();
|
||||
|
||||
String response = this.transformOperations.encode("myrole", "123-45-6789".getBytes(), transformRequest);
|
||||
assertThat(response).matches("[0-9]{3}-[0-9]{2}-[0-9]{4}");
|
||||
TransformCiphertext ciphertext = this.transformOperations.encode("myrole", "123-45-6789".getBytes(),
|
||||
transformRequest);
|
||||
assertThat(ciphertext.getCiphertext()).matches("[0-9]{3}-[0-9]{2}-[0-9]{4}");
|
||||
}
|
||||
|
||||
@Test
|
||||
void encodeAndDecodeYieldsStartingResultWithSameTweakValueProvided() {
|
||||
VaultTransformContext transformRequest = VaultTransformContext.builder().transformation("myssn").tweak("somenum".getBytes()).build();
|
||||
|
||||
VaultTransformContext transformRequest = VaultTransformContext.builder().transformation("myssn")
|
||||
.tweak("somenum".getBytes()).build();
|
||||
String targetValue = "123-45-6789";
|
||||
|
||||
String response = this.transformOperations.encode("myrole", targetValue.getBytes(), transformRequest);
|
||||
assertThat(response).matches("[0-9]{3}-[0-9]{2}-[0-9]{4}");
|
||||
assertThat(response).isNotEqualTo(targetValue);
|
||||
TransformCiphertext ciphertext = this.transformOperations.encode("myrole", targetValue.getBytes(),
|
||||
transformRequest);
|
||||
assertThat(ciphertext.getCiphertext()).matches("[0-9]{3}-[0-9]{2}-[0-9]{4}").isNotEqualTo(targetValue);
|
||||
|
||||
String decodeResponse = this.transformOperations.decode("myrole", response, transformRequest);
|
||||
String decodeResponse = this.transformOperations.decode("myrole", ciphertext.getCiphertext(), transformRequest);
|
||||
assertThat(decodeResponse).isEqualTo(targetValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
void encodeAndDecodeDoesNotYieldStartingResultWithDifferentTweakValueProvided() {
|
||||
VaultTransformContext transformRequest = VaultTransformContext.builder().transformation("myssn").tweak("somenum".getBytes()).build();
|
||||
|
||||
VaultTransformContext transformRequest = VaultTransformContext.builder().transformation("myssn")
|
||||
.tweak("somenum".getBytes()).build();
|
||||
String targetValue = "123-45-6789";
|
||||
|
||||
String response = this.transformOperations.encode("myrole", targetValue.getBytes(), transformRequest);
|
||||
assertThat(response).matches("[0-9]{3}-[0-9]{2}-[0-9]{4}");
|
||||
assertThat(response).isNotEqualTo(targetValue);
|
||||
TransformCiphertext ciphertext = this.transformOperations.encode("myrole", targetValue.getBytes(),
|
||||
transformRequest);
|
||||
assertThat(ciphertext.getCiphertext()).matches("[0-9]{3}-[0-9]{2}-[0-9]{4}").isNotEqualTo(targetValue);
|
||||
|
||||
VaultTransformContext decodeRequest = VaultTransformContext.builder().transformation("myssn").tweak("numsome".getBytes()).build();
|
||||
VaultTransformContext otherDecodeRequest = VaultTransformContext.builder().transformation("myssn")
|
||||
.tweak("numsome".getBytes()).build();
|
||||
|
||||
String decodeResponse = this.transformOperations.decode("myrole", response, decodeRequest);
|
||||
String decodeResponse = this.transformOperations.decode("myrole", ciphertext.getCiphertext(),
|
||||
otherDecodeRequest);
|
||||
assertThat(decodeResponse).isNotEqualTo(targetValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
void encodeAndDecodeWithoutContextWorksForGeneratedTweakSource() {
|
||||
|
||||
VaultTransformContext transformRequest = VaultTransformContext.builder().transformation("generatedssn").build();
|
||||
|
||||
TransformCiphertext ciphertext = this.transformOperations.encode("generatedrole", "123-45-6789".getBytes(),
|
||||
transformRequest);
|
||||
|
||||
TransformPlaintext plaintext = this.transformOperations.decode("generatedrole", ciphertext);
|
||||
assertThat(plaintext.asString()).isEqualTo("123-45-6789");
|
||||
}
|
||||
|
||||
@Test
|
||||
void encodeAndDecodeWithoutContextWorksForInternalTweakSource() {
|
||||
|
||||
String targetValue = "123-45-6789";
|
||||
|
||||
String response = this.transformOperations.encode("internalrole", targetValue);
|
||||
assertThat(response).matches("[0-9]{3}-[0-9]{2}-[0-9]{4}");
|
||||
assertThat(response).isNotEqualTo(targetValue);
|
||||
assertThat(response).matches("[0-9]{3}-[0-9]{2}-[0-9]{4}").isNotEqualTo(targetValue);
|
||||
|
||||
String decodeResponse = this.transformOperations.decode("internalrole", response);
|
||||
assertThat(decodeResponse).isEqualTo(targetValue);
|
||||
@@ -171,35 +196,19 @@ class VaultTransformTemplateIntegrationTests extends IntegrationTestSupport {
|
||||
|
||||
@Test
|
||||
void batchEncodeAndDecodeYieldsStartingResults() {
|
||||
VaultTransformContext transformRequest = VaultTransformContext.builder().transformation("myssn").tweak("somenum".getBytes()).build();
|
||||
|
||||
List<String> ssns = Arrays.asList(
|
||||
"123-01-4567",
|
||||
"123-02-4567",
|
||||
"123-03-4567",
|
||||
"123-04-4567",
|
||||
"123-05-4567"
|
||||
);
|
||||
VaultTransformContext transformRequest = VaultTransformContext.builder().transformation("myssn")
|
||||
.tweak("somenum".getBytes()).build();
|
||||
|
||||
List<String> ssns = Arrays.asList("123-01-4567", "123-02-4567", "123-03-4567", "123-04-4567", "123-05-4567");
|
||||
|
||||
List<VaultTransformEncodeResult> encoded = this.transformOperations.encode("myrole",
|
||||
Arrays.asList(
|
||||
TransformPlaintext.of(ssns.get(0)).with(transformRequest),
|
||||
TransformPlaintext.of(ssns.get(1)).with(transformRequest),
|
||||
TransformPlaintext.of(ssns.get(2)).with(transformRequest),
|
||||
TransformPlaintext.of(ssns.get(3)).with(transformRequest),
|
||||
TransformPlaintext.of(ssns.get(4)).with(transformRequest)
|
||||
)
|
||||
);
|
||||
ssns.stream().map(TransformPlaintext::of).map(plaintext -> plaintext.with(transformRequest))
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
List<VaultTransformDecodeResult> decoded = this.transformOperations.decode("myrole",
|
||||
Arrays.asList(
|
||||
TransformCiphertext.of(encoded.get(0).getAsString()).with(transformRequest),
|
||||
TransformCiphertext.of(encoded.get(1).getAsString()).with(transformRequest),
|
||||
TransformCiphertext.of(encoded.get(2).getAsString()).with(transformRequest),
|
||||
TransformCiphertext.of(encoded.get(3).getAsString()).with(transformRequest),
|
||||
TransformCiphertext.of(encoded.get(4).getAsString()).with(transformRequest)
|
||||
)
|
||||
);
|
||||
encoded.stream().map(VaultTransformEncodeResult::getAsString).map(TransformCiphertext::of)
|
||||
.map(ciphertext -> ciphertext.with(transformRequest)).collect(Collectors.toList()));
|
||||
|
||||
for (int i = 0; i < decoded.size(); i++) {
|
||||
assertThat(decoded.get(i).getAsString()).isEqualTo(ssns.get(i));
|
||||
@@ -209,36 +218,18 @@ class VaultTransformTemplateIntegrationTests extends IntegrationTestSupport {
|
||||
@Test
|
||||
void batchEncodeAndDecodeYieldsStartingResultsForInternalWithNoContext() {
|
||||
|
||||
List<String> ssns = Arrays.asList(
|
||||
"123-01-4567",
|
||||
"123-02-4567",
|
||||
"123-03-4567",
|
||||
"123-04-4567",
|
||||
"123-05-4567"
|
||||
);
|
||||
List<String> ssns = Arrays.asList("123-01-4567", "123-02-4567", "123-03-4567", "123-04-4567", "123-05-4567");
|
||||
|
||||
List<VaultTransformEncodeResult> encoded = this.transformOperations.encode("internalrole",
|
||||
Arrays.asList(
|
||||
TransformPlaintext.of(ssns.get(0)),
|
||||
TransformPlaintext.of(ssns.get(1)),
|
||||
TransformPlaintext.of(ssns.get(2)),
|
||||
TransformPlaintext.of(ssns.get(3)),
|
||||
TransformPlaintext.of(ssns.get(4))
|
||||
)
|
||||
);
|
||||
ssns.stream().map(TransformPlaintext::of).collect(Collectors.toList()));
|
||||
|
||||
List<VaultTransformDecodeResult> decoded = this.transformOperations.decode("internalrole",
|
||||
Arrays.asList(
|
||||
TransformCiphertext.of(encoded.get(0).getAsString()),
|
||||
TransformCiphertext.of(encoded.get(1).getAsString()),
|
||||
TransformCiphertext.of(encoded.get(2).getAsString()),
|
||||
TransformCiphertext.of(encoded.get(3).getAsString()),
|
||||
TransformCiphertext.of(encoded.get(4).getAsString())
|
||||
)
|
||||
);
|
||||
encoded.stream().map(VaultTransformEncodeResult::getAsString).map(TransformCiphertext::of)
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
for (int i = 0; i < decoded.size(); i++) {
|
||||
assertThat(decoded.get(i).getAsString()).isEqualTo(ssns.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
* Support for PEM-encoded certificates for keystore and truststore usage.
|
||||
* `ReactiveVaultEndpointProvider` for non-blocking lookup of `VaultEndpoint`.
|
||||
* `VaultKeyValueMetadataOperations` for Key-Value metadata interaction.
|
||||
* Support for `transform` backend (Enterprise Feature).
|
||||
|
||||
[[new-features.2-2-0]]
|
||||
=== What's new in Spring Vault 2.2
|
||||
|
||||
Reference in New Issue
Block a user