|
6 | 6 | import io.github.jopenlibs.vault.json.JsonObject; |
7 | 7 | import io.github.jopenlibs.vault.json.JsonValue; |
8 | 8 | import io.github.jopenlibs.vault.response.LogicalResponse; |
9 | | -import io.github.jopenlibs.vault.rest.Rest; |
10 | 9 | import io.github.jopenlibs.vault.rest.RestResponse; |
11 | 10 | import java.nio.charset.StandardCharsets; |
12 | 11 | import java.util.Arrays; |
|
30 | 29 | */ |
31 | 30 | public class Logical extends OperationsBase { |
32 | 31 |
|
| 32 | + private static final WriteOptions DEFAULT_WRITE_OPTIONS = new WriteOptions().build(); |
| 33 | + |
33 | 34 | private String nameSpace; |
34 | 35 |
|
35 | 36 | public enum logicalOperations {authentication, deleteV1, deleteV2, destroy, listV1, listV2, readV1, readV2, writeV1, writeV2, unDelete, mount} |
@@ -85,7 +86,7 @@ private LogicalResponse read(final String path, final logicalOperations operatio |
85 | 86 | throws VaultException { |
86 | 87 | return retry(attempt -> { |
87 | 88 | // Make an HTTP request to Vault |
88 | | - final RestResponse restResponse = new Rest()//NOPMD |
| 89 | + final RestResponse restResponse = getRest()//NOPMD |
89 | 90 | .url(config.getAddress() + "/v1/" + adjustPathForReadOrWrite(path, |
90 | 91 | config.getPrefixPathDepth(), operation)) |
91 | 92 | .header("X-Vault-Token", config.getToken()) |
@@ -142,7 +143,7 @@ public LogicalResponse read(final String path, Boolean shouldRetry, final Intege |
142 | 143 | attempt -> { |
143 | 144 | // Make an HTTP request to Vault |
144 | 145 | final RestResponse restResponse = |
145 | | - new Rest() //NOPMD |
| 146 | + getRest() //NOPMD |
146 | 147 | .url(config.getAddress() + "/v1/" + adjustPathForReadOrWrite( |
147 | 148 | path, |
148 | 149 | config.getPrefixPathDepth(), logicalOperations.readV2)) |
@@ -202,9 +203,11 @@ public LogicalResponse read(final String path, Boolean shouldRetry, final Intege |
202 | 203 | public LogicalResponse write(final String path, final Map<String, Object> nameValuePairs) |
203 | 204 | throws VaultException { |
204 | 205 | if (engineVersionForSecretPath(path).equals(2)) { |
205 | | - return write(path, nameValuePairs, logicalOperations.writeV2, null); |
| 206 | + return write(path, nameValuePairs, logicalOperations.writeV2, null, |
| 207 | + DEFAULT_WRITE_OPTIONS); |
206 | 208 | } else { |
207 | | - return write(path, nameValuePairs, logicalOperations.writeV1, null); |
| 209 | + return write(path, nameValuePairs, logicalOperations.writeV1, null, |
| 210 | + DEFAULT_WRITE_OPTIONS); |
208 | 211 | } |
209 | 212 | } |
210 | 213 |
|
@@ -239,47 +242,50 @@ public LogicalResponse write(final String path, final Map<String, Object> nameVa |
239 | 242 | final Integer wrapTTL) |
240 | 243 | throws VaultException { |
241 | 244 | if (engineVersionForSecretPath(path).equals(2)) { |
242 | | - return write(path, nameValuePairs, logicalOperations.writeV2, wrapTTL); |
| 245 | + return write(path, nameValuePairs, logicalOperations.writeV2, wrapTTL, |
| 246 | + DEFAULT_WRITE_OPTIONS); |
243 | 247 | } else { |
244 | | - return write(path, nameValuePairs, logicalOperations.writeV1, wrapTTL); |
| 248 | + return write(path, nameValuePairs, logicalOperations.writeV1, wrapTTL, |
| 249 | + DEFAULT_WRITE_OPTIONS); |
| 250 | + } |
| 251 | + } |
| 252 | + |
| 253 | + /** |
| 254 | + * <p>Operation to store secrets with the ability to specify additional write options |
| 255 | + * See {@link #write(String, Map, Integer) write} for common behavior |
| 256 | + * </p> |
| 257 | + * |
| 258 | + * @param path The Vault key value to which to write (e.g. <code>secret/hello</code>) |
| 259 | + * @param nameValuePairs Secret name and value pairs to store under this Vault key (can be |
| 260 | + * @param wrapTTL Time (in seconds) which secret is wrapped |
| 261 | + * @param writeOptions Additional options to be used for the write operation |
| 262 | + * @return The response information received from Vault |
| 263 | + * @throws VaultException If invalid engine version or if errors occurs with the REST request, |
| 264 | + * and the maximum number of retries is exceeded. |
| 265 | + */ |
| 266 | + public LogicalResponse write(final String path, final Map<String, Object> nameValuePairs, |
| 267 | + final Integer wrapTTL, final WriteOptions writeOptions) |
| 268 | + throws VaultException { |
| 269 | + if (!this.engineVersionForSecretPath(path).equals(2)) { |
| 270 | + throw new VaultException("Write options are only supported in KV Engine version 2."); |
245 | 271 | } |
| 272 | + return write(path, nameValuePairs, logicalOperations.writeV2, wrapTTL, writeOptions); |
246 | 273 | } |
247 | 274 |
|
248 | 275 | private LogicalResponse write(final String path, final Map<String, Object> nameValuePairs, |
249 | | - final logicalOperations operation, final Integer wrapTTL) throws VaultException { |
| 276 | + final logicalOperations operation, final Integer wrapTTL, |
| 277 | + final WriteOptions writeOptions) |
| 278 | + throws VaultException { |
250 | 279 |
|
251 | 280 | return retry(attempt -> { |
252 | | - JsonObject requestJson = Json.object(); |
253 | | - if (nameValuePairs != null) { |
254 | | - for (final Map.Entry<String, Object> pair : nameValuePairs.entrySet()) { |
255 | | - final Object value = pair.getValue(); |
256 | | - if (value == null) { |
257 | | - requestJson = requestJson.add(pair.getKey(), (String) null); |
258 | | - } else if (value instanceof Boolean) { |
259 | | - requestJson = requestJson.add(pair.getKey(), (Boolean) pair.getValue()); |
260 | | - } else if (value instanceof Integer) { |
261 | | - requestJson = requestJson.add(pair.getKey(), (Integer) pair.getValue()); |
262 | | - } else if (value instanceof Long) { |
263 | | - requestJson = requestJson.add(pair.getKey(), (Long) pair.getValue()); |
264 | | - } else if (value instanceof Float) { |
265 | | - requestJson = requestJson.add(pair.getKey(), (Float) pair.getValue()); |
266 | | - } else if (value instanceof Double) { |
267 | | - requestJson = requestJson.add(pair.getKey(), (Double) pair.getValue()); |
268 | | - } else if (value instanceof JsonValue) { |
269 | | - requestJson = requestJson.add(pair.getKey(), |
270 | | - (JsonValue) pair.getValue()); |
271 | | - } else { |
272 | | - requestJson = requestJson.add(pair.getKey(), |
273 | | - pair.getValue().toString()); |
274 | | - } |
275 | | - } |
276 | | - } |
277 | | - // Make an HTTP request to Vault |
278 | | - final RestResponse restResponse = new Rest()//NOPMD |
| 281 | + JsonObject dataJson = buildJsonFromMap(nameValuePairs); |
| 282 | + JsonObject optionsJson = buildJsonFromMap(writeOptions.getOptionsMap()); |
| 283 | + // Make an HTTP request to Vault |
| 284 | + final RestResponse restResponse = getRest()//NOPMD |
279 | 285 | .url(config.getAddress() + "/v1/" + adjustPathForReadOrWrite(path, |
280 | 286 | config.getPrefixPathDepth(), operation)) |
281 | | - .body(jsonObjectToWriteFromEngineVersion(operation, requestJson).toString() |
282 | | - .getBytes(StandardCharsets.UTF_8)) |
| 287 | + .body(jsonObjectToWriteFromEngineVersion(operation, dataJson, optionsJson) |
| 288 | + .toString().getBytes(StandardCharsets.UTF_8)) |
283 | 289 | .header("X-Vault-Token", config.getToken()) |
284 | 290 | .header("X-Vault-Namespace", this.nameSpace) |
285 | 291 | .header("X-Vault-Request", "true") |
@@ -368,7 +374,7 @@ private LogicalResponse delete(final String path, final Logical.logicalOperation |
368 | 374 | throws VaultException { |
369 | 375 | return retry(attempt -> { |
370 | 376 | // Make an HTTP request to Vault |
371 | | - final RestResponse restResponse = new Rest()//NOPMD |
| 377 | + final RestResponse restResponse = getRest()//NOPMD |
372 | 378 | .url(config.getAddress() + "/v1/" + adjustPathForDelete(path, |
373 | 379 | config.getPrefixPathDepth(), operation)) |
374 | 380 | .header("X-Vault-Token", config.getToken()) |
@@ -418,7 +424,7 @@ public LogicalResponse delete(final String path, final int[] versions) throws Va |
418 | 424 | return retry(attempt -> { |
419 | 425 | // Make an HTTP request to Vault |
420 | 426 | JsonObject versionsToDelete = new JsonObject().add("versions", versions); |
421 | | - final RestResponse restResponse = new Rest()//NOPMD |
| 427 | + final RestResponse restResponse = getRest()//NOPMD |
422 | 428 | .url(config.getAddress() + "/v1/" + adjustPathForVersionDelete(path, |
423 | 429 | config.getPrefixPathDepth())) |
424 | 430 | .header("X-Vault-Token", config.getToken()) |
@@ -478,7 +484,7 @@ public LogicalResponse unDelete(final String path, final int[] versions) throws |
478 | 484 | return retry(attempt -> { |
479 | 485 | // Make an HTTP request to Vault |
480 | 486 | JsonObject versionsToUnDelete = new JsonObject().add("versions", versions); |
481 | | - final RestResponse restResponse = new Rest() //NOPMD |
| 487 | + final RestResponse restResponse = getRest() //NOPMD |
482 | 488 | .url(config.getAddress() + "/v1/" + adjustPathForVersionUnDelete(path, |
483 | 489 | config.getPrefixPathDepth())) |
484 | 490 | .header("X-Vault-Token", config.getToken()) |
@@ -525,7 +531,7 @@ public LogicalResponse destroy(final String path, final int[] versions) throws V |
525 | 531 | return retry(attempt -> { |
526 | 532 | // Make an HTTP request to Vault |
527 | 533 | JsonObject versionsToDestroy = new JsonObject().add("versions", versions); |
528 | | - final RestResponse restResponse = new Rest()//NOPMD |
| 534 | + final RestResponse restResponse = getRest()//NOPMD |
529 | 535 | .url(config.getAddress() + "/v1/" + adjustPathForVersionDestroy(path, |
530 | 536 | config.getPrefixPathDepth())) |
531 | 537 | .header("X-Vault-Token", config.getToken()) |
@@ -562,7 +568,7 @@ public LogicalResponse upgrade(final String kvPath) throws VaultException { |
562 | 568 | // Make an HTTP request to Vault |
563 | 569 | JsonObject kvToUpgrade = new JsonObject().add("options", |
564 | 570 | new JsonObject().add("version", 2)); |
565 | | - final RestResponse restResponse = new Rest()//NOPMD |
| 571 | + final RestResponse restResponse = getRest()//NOPMD |
566 | 572 | .url(config.getAddress() + "/v1/sys/mounts/" + (kvPath.replaceAll("/", "") |
567 | 573 | + "/tune")) |
568 | 574 | .header("X-Vault-Token", config.getToken()) |
@@ -608,4 +614,34 @@ private Integer engineVersionForSecretPath(final String secretPath) { |
608 | 614 | public Integer getEngineVersionForSecretPath(final String path) { |
609 | 615 | return this.engineVersionForSecretPath(path); |
610 | 616 | } |
| 617 | + |
| 618 | + private JsonObject buildJsonFromMap(Map<String, Object> nameValuePairs) { |
| 619 | + JsonObject jsonObject = Json.object(); |
| 620 | + if (nameValuePairs != null) { |
| 621 | + for (final Map.Entry<String, Object> pair : nameValuePairs.entrySet()) { |
| 622 | + final Object value = pair.getValue(); |
| 623 | + if (value == null) { |
| 624 | + jsonObject = jsonObject.add(pair.getKey(), (String) null); |
| 625 | + } else if (value instanceof Boolean) { |
| 626 | + jsonObject = jsonObject.add(pair.getKey(), (Boolean) pair.getValue()); |
| 627 | + } else if (value instanceof Integer) { |
| 628 | + jsonObject = jsonObject.add(pair.getKey(), (Integer) pair.getValue()); |
| 629 | + } else if (value instanceof Long) { |
| 630 | + jsonObject = jsonObject.add(pair.getKey(), (Long) pair.getValue()); |
| 631 | + } else if (value instanceof Float) { |
| 632 | + jsonObject = jsonObject.add(pair.getKey(), (Float) pair.getValue()); |
| 633 | + } else if (value instanceof Double) { |
| 634 | + jsonObject = jsonObject.add(pair.getKey(), (Double) pair.getValue()); |
| 635 | + } else if (value instanceof JsonValue) { |
| 636 | + jsonObject = jsonObject.add(pair.getKey(), |
| 637 | + (JsonValue) pair.getValue()); |
| 638 | + } else { |
| 639 | + jsonObject = jsonObject.add(pair.getKey(), |
| 640 | + pair.getValue().toString()); |
| 641 | + } |
| 642 | + } |
| 643 | + } |
| 644 | + return jsonObject; |
| 645 | + } |
| 646 | + |
611 | 647 | } |
0 commit comments