Skip to content

Commit 504f93d

Browse files
committed
Polishing.
Use PropertyMapper to map optional properties. Introduce mutate() method to VaultTransformContext to avoid external copy usage. Original pull request: gh-897 See gh-894
1 parent 596e3b4 commit 504f93d

File tree

5 files changed

+75
-60
lines changed

5 files changed

+75
-60
lines changed

spring-vault-core/src/main/java/org/springframework/vault/core/PropertyMapper.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ public <T> Source<T> from(Supplier<T> supplier) {
9999
* @param value the value
100100
* @return a {@link Source} that can be used to complete the mapping
101101
*/
102-
public <T> Source<T> from(T value) {
102+
public <T> Source<T> from(@Nullable T value) {
103103
return from(() -> value);
104104
}
105105

spring-vault-core/src/main/java/org/springframework/vault/core/VaultTransformTemplate.java

+24-44
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import java.util.Map;
2424

2525
import org.springframework.util.Assert;
26-
import org.springframework.util.ObjectUtils;
2726
import org.springframework.util.StringUtils;
2827
import org.springframework.vault.VaultException;
2928
import org.springframework.vault.support.TransformCiphertext;
@@ -38,6 +37,7 @@
3837
*
3938
* @author Lauren Voswinkel
4039
* @author Mark Paluch
40+
* @author Roopesh Chandran
4141
* @since 2.3
4242
*/
4343
public class VaultTransformTemplate implements VaultTransformOperations {
@@ -174,17 +174,11 @@ public List<VaultTransformDecodeResult> decode(String roleName, List<TransformCi
174174

175175
private static void applyTransformOptions(VaultTransformContext context, Map<String, String> request) {
176176

177-
if (!ObjectUtils.isEmpty(context.getTransformation())) {
178-
request.put("transformation", context.getTransformation());
179-
}
177+
PropertyMapper mapper = PropertyMapper.get();
180178

181-
if (!ObjectUtils.isEmpty(context.getTweak())) {
182-
request.put("tweak", Base64.getEncoder().encodeToString(context.getTweak()));
183-
}
184-
// NEW: pass "reference" in each item, if present
185-
if (StringUtils.hasText(context.getReference())) {
186-
request.put("reference", context.getReference());
187-
}
179+
mapper.from(context.getTransformation()).whenNotEmpty().to("transformation", request);
180+
mapper.from(context.getTweak()).whenNotEmpty().as(Base64.getEncoder()::encodeToString).to("tweak", request);
181+
mapper.from(context.getReference()).whenNotEmpty().to("reference", request);
188182
}
189183

190184
private static List<VaultTransformEncodeResult> toEncodedResults(VaultResponse vaultResponse,
@@ -250,29 +244,10 @@ private static VaultTransformDecodeResult getDecryptionResult(Map<String, String
250244

251245
if (StringUtils.hasText(data.get("decoded_value"))) {
252246

253-
// 1. Read reference from Vault's response (if present).
254-
String returnedRef = data.get("reference");
255-
256-
// 2. Build an updated context that merges the existing transformation/tweak
257-
// with the newly-returned reference. If no reference is returned, keep the
258-
// old one. Note:- Relying on reference from originalContext is aimed at
259-
// providing a
260-
// fallback strategy, if vault does not return the reference, in any
261-
// circumstance.
262-
VaultTransformContext originalContext = ciphertext.getContext();
263-
VaultTransformContext updatedContext = VaultTransformContext.builder()
264-
.transformation(originalContext.getTransformation())
265-
.tweak(originalContext.getTweak())
266-
.reference(returnedRef != null ? returnedRef : originalContext.getReference())
267-
.build();
268-
269-
// 3. Attach that updated context to the newly decoded plaintext.
247+
VaultTransformContext updatedContext = postProcessTransformContext(data, ciphertext.getContext());
270248
TransformPlaintext decodedPlaintext = TransformPlaintext.of(data.get("decoded_value")).with(updatedContext);
271249

272250
return new VaultTransformDecodeResult(decodedPlaintext);
273-
274-
// return new VaultTransformDecodeResult(
275-
// TransformPlaintext.of(data.get("decoded_value")).with(ciphertext.getContext()));
276251
}
277252

278253
return new VaultTransformDecodeResult(TransformPlaintext.empty().with(ciphertext.getContext()));
@@ -281,24 +256,29 @@ private static VaultTransformDecodeResult getDecryptionResult(Map<String, String
281256
private static TransformCiphertext toCiphertext(Map<String, ?> data, VaultTransformContext context) {
282257

283258
String ciphertext = (String) data.get("encoded_value");
284-
285-
// if Vault returns "reference" in batch_results,capturing it for co-relation.
286-
String returnedRef = (String) data.get("reference");
287-
288-
VaultTransformContext contextToUse = context;
289-
if (data.containsKey("tweak")) {
290-
byte[] tweak = Base64.getDecoder().decode((String) data.get("tweak"));
291-
contextToUse = VaultTransformContext.builder()
292-
.transformation(context.getTransformation())
293-
.tweak(tweak)
294-
.reference(returnedRef != null ? returnedRef : context.getReference())
295-
.build();
296-
}
259+
VaultTransformContext contextToUse = postProcessTransformContext(data, context);
297260

298261
return contextToUse.isEmpty() ? TransformCiphertext.of(ciphertext)
299262
: TransformCiphertext.of(ciphertext).with(contextToUse);
300263
}
301264

265+
private static VaultTransformContext postProcessTransformContext(Map<String, ?> data,
266+
VaultTransformContext context) {
267+
268+
if (data.containsKey("tweak") || data.containsKey("reference")) {
269+
270+
PropertyMapper mapper = PropertyMapper.get();
271+
VaultTransformContext.VaultTransformRequestBuilder builder = context.mutate();
272+
273+
mapper.from((String) data.get("tweak")).whenNotEmpty().as(Base64.getDecoder()::decode).to(builder::tweak);
274+
mapper.from((String) data.get("reference")).whenNotEmpty().to(builder::reference);
275+
276+
return builder.build();
277+
}
278+
279+
return context;
280+
}
281+
302282
@SuppressWarnings("unchecked")
303283
private static List<Map<String, String>> getBatchData(VaultResponse vaultResponse) {
304284
return (List<Map<String, String>>) vaultResponse.getRequiredData().get("batch_results");

spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransformContext.java

+31-14
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@
1717

1818
import java.util.Arrays;
1919

20+
import org.springframework.lang.Nullable;
2021
import org.springframework.util.Assert;
2122
import org.springframework.util.ObjectUtils;
2223

2324
/**
2425
* Transform backend encode/decode context object.
2526
*
2627
* @author Lauren Voswinkel
28+
* @author Roopesh Chandran
2729
* @since 2.3
2830
*/
2931
public class VaultTransformContext {
@@ -32,15 +34,15 @@ public class VaultTransformContext {
3234
* Empty (default) {@link VaultTransformContext} without a {@literal context} and
3335
* {@literal nonce}.
3436
*/
35-
private static final VaultTransformContext EMPTY = new VaultTransformContext("", new byte[0], "");
37+
private static final VaultTransformContext EMPTY = new VaultTransformContext("", new byte[0], null);
3638

3739
private final String transformation;
3840

3941
private final byte[] tweak;
4042

41-
private final String reference;
43+
private final @Nullable String reference;
4244

43-
private VaultTransformContext(String transformation, byte[] tweak, String reference) {
45+
private VaultTransformContext(String transformation, byte[] tweak, @Nullable String reference) {
4446
this.transformation = transformation;
4547
this.tweak = tweak;
4648
this.reference = reference;
@@ -84,7 +86,8 @@ public static VaultTransformContext fromTweak(byte[] tweak) {
8486
* @return {@code true} if this object is empty.
8587
*/
8688
public boolean isEmpty() {
87-
return ObjectUtils.isEmpty(this.transformation) && ObjectUtils.isEmpty(this.tweak);
89+
return ObjectUtils.isEmpty(this.transformation) && ObjectUtils.isEmpty(this.tweak)
90+
&& ObjectUtils.isEmpty(this.reference);
8891
}
8992

9093
/**
@@ -102,25 +105,36 @@ public byte[] getTweak() {
102105
}
103106

104107
/**
105-
* @return The reference identifier for batch operations
108+
* @return the reference identifier for batch operations.
109+
* @since 3.2
106110
*/
107-
public String getReference() {
111+
public @Nullable String getReference() {
108112
return this.reference;
109113
}
110114

115+
/**
116+
* Return a builder to create a new {@code VaultTransformContext} whose settings are
117+
* replicated from the current {@code VaultTransformContext}.
118+
*/
119+
public VaultTransformRequestBuilder mutate() {
120+
return new VaultTransformRequestBuilder(this);
121+
}
122+
111123
@Override
112124
public boolean equals(Object o) {
113125
if (this == o)
114126
return true;
115127
if (!(o instanceof VaultTransformContext that))
116128
return false;
117-
return this.transformation.equals(that.transformation) && Arrays.equals(this.tweak, that.tweak);
129+
return this.transformation.equals(that.transformation) && Arrays.equals(this.tweak, that.tweak)
130+
&& ObjectUtils.nullSafeEquals(this.reference, that.reference);
118131
}
119132

120133
@Override
121134
public int hashCode() {
122135
int result = this.transformation.hashCode();
123136
result = 31 * result + Arrays.hashCode(this.tweak);
137+
result = 31 * result + ObjectUtils.nullSafeHash(this.reference);
124138
return result;
125139
}
126140

@@ -136,20 +150,22 @@ public static class VaultTransformRequestBuilder {
136150
/**
137151
* A user-defined identifier that can be used to correlate items in a batch
138152
* request with their corresponding results in Vault's {@code batch_results}.
139-
* <br/>
140-
* <br/>
141-
*
142153
* <p>
143154
* If set, Vault echoes this value in the response so clients can match inputs to
144155
* outputs reliably. If Vault does not return the {@code reference}, the original
145156
* client-supplied reference remains available for correlation.
146-
* </p>
147157
*/
148-
private String reference = "";
158+
private @Nullable String reference;
149159

150160
private VaultTransformRequestBuilder() {
151161
}
152162

163+
private VaultTransformRequestBuilder(VaultTransformContext context) {
164+
this.transformation = context.transformation;
165+
this.tweak = Arrays.copyOf(context.tweak, context.tweak.length);
166+
this.reference = context.reference;
167+
}
168+
153169
/**
154170
* Configure a transformation to be used with the {@code transform} operation.
155171
* @param transformation name, provided as a String.
@@ -185,8 +201,9 @@ public VaultTransformRequestBuilder tweak(byte[] tweak) {
185201
* Set a user-defined reference identifier. This reference is placed into each
186202
* item of a batch request and, if supported by Vault, echoed in the batch
187203
* results.
188-
* @param reference the correlation identifier; can be {@code null} or empty.
189-
* @return {@code this} builder instance .
204+
* @param reference the correlation identifier; can be or empty.
205+
* @return {@code this} builder instance.
206+
* @since 3.2
190207
*/
191208
public VaultTransformRequestBuilder reference(String reference) {
192209
this.reference = reference;

spring-vault-core/src/test/java/org/springframework/vault/core/VaultTransformTemplateIntegrationTests.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ void batchEncodeAndDecodeYieldsStartingResultsForInternalWithNoContext() {
251251

252252
@Test
253253
void batchEncodeAndDecodeWithReference() {
254+
254255
// Prepare test data
255256
List<TransformPlaintext> batch = new ArrayList<>();
256257
batch.add(TransformPlaintext.of("123-45-6789")
@@ -282,4 +283,4 @@ void batchEncodeAndDecodeWithReference() {
282283
assertThat(decodeResults.get(1).get().getContext().getReference()).isEqualTo("ref-2");
283284
}
284285

285-
}
286+
}

spring-vault-core/src/test/java/org/springframework/vault/support/VaultTransformContextUnitTests.java

+17
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
* Unit tests for {@link VaultTransitContext}.
2424
*
2525
* @author Lauren Voswinkel
26+
* @author Roopesh Chandran
27+
* @author Mark Paluch
2628
*/
2729
class VaultTransformContextUnitTests {
2830

@@ -61,6 +63,19 @@ void createsFromTweak() {
6163
@Test
6264
void createsContextWithReference() {
6365

66+
String referenceValue = "my-reference";
67+
68+
VaultTransformContext context = VaultTransformContext.builder().reference(referenceValue).build();
69+
70+
assertThat(context.getReference()).isEqualTo(referenceValue);
71+
assertThat(context).isEqualTo(context).isNotEqualTo(VaultTransformContext.fromTweak(new byte[] { 1 }));
72+
assertThat(context).hasSameHashCodeAs(context)
73+
.doesNotHaveSameHashCodeAs(VaultTransformContext.fromTweak(new byte[] { 1 }));
74+
}
75+
76+
@Test
77+
void appliesMutation() {
78+
6479
String transformName = "some_transformation";
6580
byte[] tweak = { 1, 2, 3, 4, 5, 6, 7 };
6681
String referenceValue = "my-reference";
@@ -69,6 +84,8 @@ void createsContextWithReference() {
6984
.transformation(transformName)
7085
.tweak(tweak)
7186
.reference(referenceValue)
87+
.build()
88+
.mutate()
7289
.build();
7390

7491
assertThat(context.getTransformation()).isEqualTo(transformName);

0 commit comments

Comments
 (0)