diff --git a/spring-vault-core/src/main/java/org/springframework/vault/annotation/VaultPropertySource.java b/spring-vault-core/src/main/java/org/springframework/vault/annotation/VaultPropertySource.java index caa6106a..63479b89 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/annotation/VaultPropertySource.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/annotation/VaultPropertySource.java @@ -81,7 +81,7 @@ public @interface VaultPropertySource { String[] value(); /** - * Configures the name of the {@link org.springframework.vault.core.VaultTemplate} bean to be used with the property + * Configure the name of the {@link org.springframework.vault.core.VaultTemplate} bean to be used with the property * sources. */ String vaultTemplateRef() default "vaultTemplate"; diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/AppIdAuthenticationOptions.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/AppIdAuthenticationOptions.java index d55134f8..4b6bc0c6 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/authentication/AppIdAuthenticationOptions.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/AppIdAuthenticationOptions.java @@ -96,7 +96,7 @@ public class AppIdAuthenticationOptions { AppIdAuthenticationOptionsBuilder() {} /** - * Configures the mount path. + * Configure the mount path. * * @param path must not be empty or {@literal null}. * @return {@code this} {@link AppIdAuthenticationOptionsBuilder}. @@ -111,7 +111,7 @@ public class AppIdAuthenticationOptions { } /** - * Configures the AppId. + * Configure the AppId. * * @param appId must not be empty or {@literal null}. * @return {@code this} {@link AppIdAuthenticationOptionsBuilder}. @@ -125,7 +125,7 @@ public class AppIdAuthenticationOptions { } /** - * Configures the {@link AppIdUserIdMechanism}. + * Configure the {@link AppIdUserIdMechanism}. * * @param userIdMechanism must not be {@literal null}. * @return {@code this} {@link AppIdAuthenticationOptionsBuilder}. @@ -139,7 +139,7 @@ public class AppIdAuthenticationOptions { } /** - * Builds a new {@link AppIdAuthenticationOptions} instance. Requires {@link #userIdMechanism(AppIdUserIdMechanism)} + * Build a new {@link AppIdAuthenticationOptions} instance. Requires {@link #userIdMechanism(AppIdUserIdMechanism)} * to be configured. * * @return a new {@link AppIdAuthenticationOptions}. diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/AwsEc2AuthenticationOptions.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/AwsEc2AuthenticationOptions.java index 690f6d32..7bfd4d09 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/authentication/AwsEc2AuthenticationOptions.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/AwsEc2AuthenticationOptions.java @@ -109,7 +109,7 @@ public class AwsEc2AuthenticationOptions { AwsEc2AuthenticationOptionsBuilder() {} /** - * Configures the mount path. + * Configure the mount path. * * @param path must not be empty or {@literal null}. * @return {@code this} {@link AwsEc2AuthenticationOptionsBuilder}. @@ -123,7 +123,7 @@ public class AwsEc2AuthenticationOptions { } /** - * Configures the Identity Document {@link URI}. + * Configure the Identity Document {@link URI}. * * @param identityDocumentUri must not be empty or {@literal null}. * @return {@code this} {@link AwsEc2AuthenticationOptionsBuilder}. @@ -136,7 +136,7 @@ public class AwsEc2AuthenticationOptions { } /** - * Configures the name of the role against which the login is being attempted.If role is not specified, then the + * Configure the name of the role against which the login is being attempted.If role is not specified, then the * login endpoint looks for a role bearing the name of the AMI ID of the EC2 instance that is trying to login. * * @param role may be empty or {@literal null}. @@ -149,7 +149,7 @@ public class AwsEc2AuthenticationOptions { } /** - * Builds a new {@link AwsEc2AuthenticationOptions} instance. + * Build a new {@link AwsEc2AuthenticationOptions} instance. * * @return a new {@link AppIdAuthenticationOptions}. */ diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/CubbyholeAuthenticationOptions.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/CubbyholeAuthenticationOptions.java index 66bbff4e..646fd19b 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/authentication/CubbyholeAuthenticationOptions.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/CubbyholeAuthenticationOptions.java @@ -95,7 +95,7 @@ public class CubbyholeAuthenticationOptions { CubbyholeAuthenticationOptionsBuilder() {} /** - * Configures the initial {@link VaultToken} to access Cubbyhole. + * Configure the initial {@link VaultToken} to access Cubbyhole. * * @param initialToken must not be {@literal null}. * @return {@code this} {@link CubbyholeAuthenticationOptionsBuilder}. @@ -109,7 +109,7 @@ public class CubbyholeAuthenticationOptions { } /** - * Configures the cubbyhole path, such as {@code cubbyhole/token}. Expects a token in the {@code data} response. + * Configure the cubbyhole path, such as {@code cubbyhole/token}. Expects a token in the {@code data} response. * * @param path must not be empty or {@literal null}. * @return {@code this} {@link CubbyholeAuthenticationOptionsBuilder}. @@ -123,7 +123,7 @@ public class CubbyholeAuthenticationOptions { } /** - * Configures whether to use wrapped token responses. + * Configure whether to use wrapped token responses. * * @return {@code this} {@link CubbyholeAuthenticationOptionsBuilder}. */ @@ -135,7 +135,7 @@ public class CubbyholeAuthenticationOptions { } /** - * Builds a new {@link CubbyholeAuthenticationOptions} instance. Requires {@link #path(String)} or + * Build a new {@link CubbyholeAuthenticationOptions} instance. Requires {@link #path(String)} or * {@link #wrapped()} to be configured. * * @return a new {@link CubbyholeAuthenticationOptions}. diff --git a/spring-vault-core/src/main/java/org/springframework/vault/core/VaultSysTemplate.java b/spring-vault-core/src/main/java/org/springframework/vault/core/VaultSysTemplate.java index 08c5297a..1672235d 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/core/VaultSysTemplate.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/core/VaultSysTemplate.java @@ -16,10 +16,13 @@ package org.springframework.vault.core; import java.net.URI; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; @@ -36,14 +39,18 @@ import org.springframework.vault.support.VaultInitializationRequest; import org.springframework.vault.support.VaultInitializationResponse; import org.springframework.vault.support.VaultMount; import org.springframework.vault.support.VaultResponseSupport; +import org.springframework.vault.support.VaultToken; import org.springframework.vault.support.VaultUnsealStatus; import org.springframework.web.client.HttpStatusCodeException; import org.springframework.web.client.RestTemplate; import com.fasterxml.jackson.annotation.JsonAnySetter; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.Data; + /** * Default implementation of {@link VaultSysOperations}. * @@ -104,8 +111,8 @@ public class VaultSysTemplate implements VaultSysOperations { @Override public VaultInitializationResponse doWithVault(VaultClient client) { - VaultResponseEntity response = client.putForEntity("sys/init", - vaultInitializationRequest, VaultInitializationResponse.class); + VaultResponseEntity response = client.putForEntity("sys/init", + vaultInitializationRequest, VaultInitializationResponseImpl.class); if (response.isSuccessful() && response.hasBody()) { return response.getBody(); @@ -129,8 +136,8 @@ public class VaultSysTemplate implements VaultSysOperations { @Override public VaultUnsealStatus doWithVault(VaultClient client) { - VaultResponseEntity response = client.putForEntity("sys/unseal", - Collections.singletonMap("key", keyShare), VaultUnsealStatus.class); + VaultResponseEntity response = client.putForEntity("sys/unseal", + Collections.singletonMap("key", keyShare), VaultUnsealStatusImpl.class); if (response.isSuccessful() && response.hasBody()) { return response.getBody(); @@ -209,7 +216,8 @@ public class VaultSysTemplate implements VaultSysOperations { @Override public VaultUnsealStatus doWithVault(VaultClient client) { - VaultResponseEntity response = client.getForEntity("sys/seal-status", VaultUnsealStatus.class); + VaultResponseEntity response = client.getForEntity("sys/seal-status", + VaultUnsealStatusImpl.class); if (response.isSuccessful() && response.hasBody()) { return response.getBody(); @@ -303,18 +311,61 @@ public class VaultSysTemplate implements VaultSysOperations { try { - ResponseEntity healthResponse = restTemplate.exchange(uri, HttpMethod.GET, null, - VaultHealth.class); + ResponseEntity healthResponse = restTemplate.exchange(uri, HttpMethod.GET, null, + VaultHealthImpl.class); return healthResponse.getBody(); } catch (HttpStatusCodeException responseError) { try { ObjectMapper mapper = new ObjectMapper(); - return mapper.readValue(responseError.getResponseBodyAsString(), VaultHealth.class); + return mapper.readValue(responseError.getResponseBodyAsString(), VaultHealthImpl.class); } catch (Exception jsonError) { throw responseError; } } } } + + @Data + static class VaultInitializationResponseImpl implements VaultInitializationResponse { + + private List keys = new ArrayList(); + + @JsonProperty("root_token") private String rootToken; + + public VaultToken getRootToken() { + return VaultToken.of(rootToken); + } + } + + @Data + static class VaultUnsealStatusImpl implements VaultUnsealStatus { + + private boolean sealed; + + @JsonProperty("t") private int secretThreshold; + + @JsonProperty("n") private int secretShares; + + private int progress; + } + + @Data + @JsonIgnoreProperties(ignoreUnknown = true) + static class VaultHealthImpl implements VaultHealth { + + private final boolean initialized; + private final boolean sealed; + private final boolean standby; + private final int serverTimeUtc; + + private VaultHealthImpl(@JsonProperty("initialized") boolean initialized, @JsonProperty("sealed") boolean sealed, + @JsonProperty("standby") boolean standby, @JsonProperty("server_time_utc") int serverTimeUtc) { + + this.initialized = initialized; + this.sealed = sealed; + this.standby = standby; + this.serverTimeUtc = serverTimeUtc; + } + } } diff --git a/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTransitOperations.java b/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTransitOperations.java index 586a7f73..97266b23 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTransitOperations.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTransitOperations.java @@ -18,7 +18,7 @@ package org.springframework.vault.core; import org.springframework.vault.support.VaultTransitKey; import org.springframework.vault.support.VaultTransitKeyConfiguration; import org.springframework.vault.support.VaultTransitKeyCreationRequest; -import org.springframework.vault.support.VaultTransitRequest; +import org.springframework.vault.support.VaultTransitContext; /** * Interface that specifies operations using the {@code transit} backend. @@ -94,7 +94,7 @@ public interface VaultTransitOperations { * @param transitRequest may be {@literal null} if no request options provided. * @return cipher text. */ - String encrypt(String keyName, byte[] plaintext, VaultTransitRequest transitRequest); + String encrypt(String keyName, byte[] plaintext, VaultTransitContext transitRequest); /** * Decrypts the provided plaintext using the named key. @@ -113,7 +113,7 @@ public interface VaultTransitOperations { * @param transitRequest may be {@literal null} if no request options provided. * @return plain text. */ - byte[] decrypt(String keyName, String ciphertext, VaultTransitRequest transitRequest); + byte[] decrypt(String keyName, String ciphertext, VaultTransitContext transitRequest); /** * Rewrap the provided ciphertext using the latest version of the named key. Because this never returns plaintext, it @@ -136,5 +136,5 @@ public interface VaultTransitOperations { * @return cipher text. * @see #rotate(String) */ - String rewrap(String keyName, String ciphertext, VaultTransitRequest transitRequest); + String rewrap(String keyName, String ciphertext, VaultTransitContext transitRequest); } diff --git a/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTransitTemplate.java b/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTransitTemplate.java index b013be64..5d2faa19 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTransitTemplate.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTransitTemplate.java @@ -24,7 +24,11 @@ import org.springframework.vault.support.VaultResponseSupport; import org.springframework.vault.support.VaultTransitKey; import org.springframework.vault.support.VaultTransitKeyConfiguration; import org.springframework.vault.support.VaultTransitKeyCreationRequest; -import org.springframework.vault.support.VaultTransitRequest; +import org.springframework.vault.support.VaultTransitContext; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.Data; /** * Default implementation of {@link VaultTransitOperations}. @@ -47,7 +51,7 @@ public class VaultTransitTemplate implements VaultTransitOperations { } @Override - public void createKey(final String keyName) { + public void createKey(String keyName) { Assert.hasText(keyName, "KeyName must not be empty"); @@ -55,7 +59,7 @@ public class VaultTransitTemplate implements VaultTransitOperations { } @Override - public void createKey(final String keyName, final VaultTransitKeyCreationRequest createKeyRequest) { + public void createKey(String keyName, VaultTransitKeyCreationRequest createKeyRequest) { Assert.hasText(keyName, "KeyName must not be empty"); Assert.notNull(createKeyRequest, "VaultTransitKeyCreationRequest must not be empty"); @@ -64,7 +68,7 @@ public class VaultTransitTemplate implements VaultTransitOperations { } @Override - public void configureKey(final String keyName, final VaultTransitKeyConfiguration keyConfiguration) { + public void configureKey(String keyName, VaultTransitKeyConfiguration keyConfiguration) { Assert.hasText(keyName, "KeyName must not be empty"); Assert.notNull(keyConfiguration, "VaultKeyConfiguration must not be empty"); @@ -73,12 +77,12 @@ public class VaultTransitTemplate implements VaultTransitOperations { } @Override - public VaultTransitKey getKey(final String keyName) { + public VaultTransitKey getKey(String keyName) { Assert.hasText(keyName, "KeyName must not be empty"); - VaultResponseSupport result = vaultOperations.read(String.format("%s/keys/%s", path, keyName), - VaultTransitKey.class); + VaultResponseSupport result = vaultOperations.read(String.format("%s/keys/%s", path, keyName), + VaultTransitKeyImpl.class); if (result != null) { return result.getData(); @@ -118,7 +122,7 @@ public class VaultTransitTemplate implements VaultTransitOperations { } @Override - public String encrypt(String keyName, byte[] plaintext, VaultTransitRequest transitRequest) { + public String encrypt(String keyName, byte[] plaintext, VaultTransitContext transitRequest) { Assert.hasText(keyName, "KeyName must not be empty"); Assert.notNull(plaintext, "Plain text must not be null"); @@ -152,7 +156,7 @@ public class VaultTransitTemplate implements VaultTransitOperations { } @Override - public byte[] decrypt(String keyName, String ciphertext, VaultTransitRequest transitRequest) { + public byte[] decrypt(String keyName, String ciphertext, VaultTransitContext transitRequest) { Assert.hasText(keyName, "KeyName must not be empty"); Assert.hasText(keyName, "Cipher text must not be empty"); @@ -185,7 +189,7 @@ public class VaultTransitTemplate implements VaultTransitOperations { } @Override - public String rewrap(String keyName, String ciphertext, VaultTransitRequest transitRequest) { + public String rewrap(String keyName, String ciphertext, VaultTransitContext transitRequest) { Assert.hasText(keyName, "KeyName must not be empty"); Assert.hasText(ciphertext, "Cipher text must not be empty"); @@ -202,7 +206,7 @@ public class VaultTransitTemplate implements VaultTransitOperations { .get("ciphertext"); } - private void applyTransitOptions(VaultTransitRequest transitRequest, Map request) { + private void applyTransitOptions(VaultTransitContext transitRequest, Map request) { if (transitRequest.getContext() != null) { request.put("context", Base64Utils.encodeToString(transitRequest.getContext())); @@ -212,4 +216,34 @@ public class VaultTransitTemplate implements VaultTransitOperations { request.put("nonce", Base64Utils.encodeToString(transitRequest.getNonce())); } } + + @Data + static class VaultTransitKeyImpl implements VaultTransitKey { + + @JsonProperty("cipher_mode") private String cipherMode; + + @JsonProperty("type") private String type; + + @JsonProperty("deletion_allowed") private boolean deletionAllowed; + + private boolean derived; + + private Map keys; + + @JsonProperty("latest_version") private boolean latestVersion; + + @JsonProperty("min_decryption_version") private int minDecryptionVersion; + + private String name; + + public String getType() { + + if (type != null) { + return type; + } + + return cipherMode; + } + + } } diff --git a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultCertificateRequest.java b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultCertificateRequest.java index 17bf8564..67048ce2 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultCertificateRequest.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultCertificateRequest.java @@ -127,7 +127,7 @@ public class VaultCertificateRequest { } /** - * Configures alternative names. Replaces previously configured alt names + * Configure alternative names. Replaces previously configured alt names * * @param altNames must not be {@literal null}. * @return {@code this} {@link VaultCertificateRequestBuilder}. @@ -155,7 +155,7 @@ public class VaultCertificateRequest { } /** - * Configures IP subject alternative names. Replaces previously configured IP subject alt names. + * Configure IP subject alternative names. Replaces previously configured IP subject alt names. * * @param ipSubjectAltNames must not be {@literal null}. * @return {@code this} {@link VaultCertificateRequestBuilder}. @@ -183,7 +183,7 @@ public class VaultCertificateRequest { } /** - * Configures a TTL. + * Configure a TTL. * * @param ttl the TTL, must be a positive number. * @return {@code this} {@link VaultCertificateRequestBuilder}. @@ -197,7 +197,7 @@ public class VaultCertificateRequest { } /** - * Configures a TTL. + * Configure a TTL. * * @param ttl the TTL, must be a positive number. * @param timeUnit must not be {@literal null} @@ -213,7 +213,7 @@ public class VaultCertificateRequest { } /** - * The given common name will not be included in DNS or Email Subject Alternate Names (as appropriate). Useful if + * Exclude the given common name from DNS or Email Subject Alternate Names (as appropriate). Useful if * the CN is not a hostname or email address, but is instead some human-readable identifier. * * @return {@code this} {@link VaultCertificateRequestBuilder}. @@ -225,7 +225,7 @@ public class VaultCertificateRequest { } /** - * Builds a new {@link VaultCertificateRequest} instance. Requires {@link #commonName(String)} to be configured. + * Build a new {@link VaultCertificateRequest} instance. Requires {@link #commonName(String)} to be configured. * * @return a new {@link VaultCertificateRequest}. */ diff --git a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultHealth.java b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultHealth.java index 54985150..41ba1e42 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultHealth.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultHealth.java @@ -15,72 +15,34 @@ */ package org.springframework.vault.support; -import com.fasterxml.jackson.annotation.JsonProperty; - /** - * Value object to bind HTTP API responses for sys/health. Instances of this class are immutable. + * Vault health state. * * @author Stuart Ingram * @author Bill Koch + * @author Mark Paluch */ -public class VaultHealth { - - /** - * Reports whether the Vault instance is initialized. - */ - private final boolean initialized; - - /** - * Reports whether the Vault instance is sealed. - */ - private final boolean sealed; - - /** - * Reports whether the Vault instance is in stand-by mode if running using High-Availability. - */ - private final boolean standby; - - /** - * The server time in seconds, UTC. - */ - private final int serverTimeUtc; - - private VaultHealth(@JsonProperty("initialized") boolean initialized, @JsonProperty("sealed") boolean sealed, - @JsonProperty("standby") boolean standby, @JsonProperty("server_time_utc") int serverTimeUtc) { - - this.initialized = initialized; - this.sealed = sealed; - this.standby = standby; - this.serverTimeUtc = serverTimeUtc; - } +public interface VaultHealth { /** * @return {@literal true} if the Vault instance is initialized, otherwise {@literal false}. */ - public boolean isInitialized() { - return initialized; - } + boolean isInitialized(); /** * @return {@literal true} if the Vault instance is sealed, otherwise {@literal false} if the Vault instance is * unsealed. */ - public boolean isSealed() { - return sealed; - } + boolean isSealed(); /** * @return {@literal true} if the Vault instance is in standby mode, otherwise {@literal false} if the Vault instance * is active. */ - public boolean isStandby() { - return standby; - } + boolean isStandby(); /** * @return the server time in seconds, UTC. */ - public int getServerTimeUtc() { - return serverTimeUtc; - } + int getServerTimeUtc(); } diff --git a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultInitializationRequest.java b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultInitializationRequest.java index 85e6639f..81e61a31 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultInitializationRequest.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultInitializationRequest.java @@ -24,30 +24,38 @@ import com.fasterxml.jackson.annotation.JsonProperty; */ public class VaultInitializationRequest { - @JsonProperty("secret_shares") private int secretShares; + @JsonProperty("secret_shares") private final int secretShares; - @JsonProperty("secret_threshold") private int secretThreshold; + @JsonProperty("secret_threshold") private final int secretThreshold; - public VaultInitializationRequest() {} - - public VaultInitializationRequest(int secretShares, int secretThreshold) { + private VaultInitializationRequest(int secretShares, int secretThreshold) { this.secretShares = secretShares; this.secretThreshold = secretThreshold; } + /** + * Create a new {@literal VaultInitializationRequest} given {@code secretShares} and {@code secretThreshold}. + * + * @param secretShares + * @param secretThreshold + * @return a new {@link VaultInitializationRequest}. + */ + public static VaultInitializationRequest create(int secretShares, int secretThreshold) { + return new VaultInitializationRequest(secretShares, secretThreshold); + } + + /** + * @return number of secret shares. + */ public int getSecretShares() { return secretShares; } - public void setSecretShares(int secretShares) { - this.secretShares = secretShares; - } - + /** + * @return required number of secret shares to unseal Vault. + */ public int getSecretThreshold() { return secretThreshold; } - public void setSecretThreshold(int secretThreshold) { - this.secretThreshold = secretThreshold; - } } diff --git a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultInitializationResponse.java b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultInitializationResponse.java index 435b59ed..2c0cdaa8 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultInitializationResponse.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultInitializationResponse.java @@ -15,37 +15,22 @@ */ package org.springframework.vault.support; -import java.util.ArrayList; import java.util.List; -import com.fasterxml.jackson.annotation.JsonProperty; - /** - * Value object to bind Vault HTTP Initialization API responses. + * Vault initialization response. * * @author Mark Paluch */ -public class VaultInitializationResponse { +public interface VaultInitializationResponse { - private List keys = new ArrayList(); + /** + * @return {@link List} of unseal keys. + */ + List getKeys(); - @JsonProperty("root_token") private String rootToken; - - public VaultInitializationResponse() {} - - public List getKeys() { - return keys; - } - - public void setKeys(List keys) { - this.keys = keys; - } - - public String getRootToken() { - return rootToken; - } - - public void setRootToken(String rootToken) { - this.rootToken = rootToken; - } + /** + * @return the root {@link VaultToken}. + */ + VaultToken getRootToken(); } diff --git a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultMount.java b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultMount.java index e157c865..7277d35c 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultMount.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultMount.java @@ -142,7 +142,7 @@ public class VaultMount { } /** - * Builds a new {@link VaultMount} instance. Requires {@link #type(String)} to be configured. + * Build a new {@link VaultMount} instance. Requires {@link #type(String)} to be configured. * * @return a new {@link VaultMount}. */ diff --git a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitContext.java b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitContext.java new file mode 100644 index 00000000..2d3ac371 --- /dev/null +++ b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitContext.java @@ -0,0 +1,98 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.vault.support; + +/** + * Transit backend encryption/decryption/rewrapping context. + * + * @author Mark Paluch + */ +public class VaultTransitContext { + + private final byte[] context; + + private final byte[] nonce; + + VaultTransitContext(byte[] context, byte[] nonce) { + this.context = context; + this.nonce = nonce; + } + + /** + * @return a new {@link VaultTransitRequestBuilder}. + */ + public static VaultTransitRequestBuilder builder() { + return new VaultTransitRequestBuilder(); + } + + /** + * @return the key derivation context. + */ + public byte[] getContext() { + return context; + } + + /** + * @return the + */ + public byte[] getNonce() { + return nonce; + } + + /** + * Builder for {@link VaultTransitContext}. + */ + public static class VaultTransitRequestBuilder { + + private byte[] context; + private byte[] nonce; + + VaultTransitRequestBuilder() {} + + /** + * Configure a key derivation context for the {@code transit} operation. + * + * @param context key derivation context, provided as a binary data. Must be provided if derivation is enabled. + * @return {@code this} {@link VaultTransitRequestBuilder}. + */ + public VaultTransitContext.VaultTransitRequestBuilder context(byte[] context) { + this.context = context; + return this; + } + + /** + * Configure the nonce value for a {@code transit} operation. Must be provided if convergent encryption is enabled + * for this key and the key was generated with Vault 0.6.1. Not required for keys created in 0.6.2+. + * + * @param nonce value must be exactly 96 bits (12 bytes) long and the user must ensure that for any given context + * (and thus, any given encryption key) this nonce value is never reused + * @return {@code this} {@link VaultTransitRequestBuilder}. + */ + public VaultTransitContext.VaultTransitRequestBuilder nonce(byte[] nonce) { + this.nonce = nonce; + return this; + } + + /** + * Build a new {@link VaultTransitContext} instance. + * + * @return a new {@link VaultTransitContext}. + */ + public VaultTransitContext build() { + return new VaultTransitContext(context, nonce); + } + } +} diff --git a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitKey.java b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitKey.java index 04c2e040..514f1c0b 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitKey.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitKey.java @@ -17,92 +17,45 @@ package org.springframework.vault.support; import java.util.Map; -import com.fasterxml.jackson.annotation.JsonProperty; - /** - * Value object to bind Vault HTTP Transit Key API responses. + * A key inside Vault's {@code transit} backend. * * @author Mark Paluch */ -public class VaultTransitKey { +public interface VaultTransitKey { - @JsonProperty("cipher_mode") private String cipherMode; + /** + * @return {@literal true} if deletion of the key is allowed. Key deletion must be turned on to make keys deletable. + */ + boolean isDeletionAllowed(); - @JsonProperty("type") private String type; + /** + * @return {@literal true} if if key derivation MUST be used. + */ + boolean isDerived(); - @JsonProperty("deletion_allowed") private boolean deletionAllowed; + /** + * @return a {@link Map} of key version to its creation timestamp. + */ + Map getKeys(); - private boolean derived; + /** + * @return {@literal true} if the key represents the latest version. + */ + boolean isLatestVersion(); - private Map keys; + /** + * @return required key version to still be able to decrypt data. + */ + int getMinDecryptionVersion(); - @JsonProperty("latest_version") private boolean latestVersion; + /** + * @return name of the key + */ + String getName(); - @JsonProperty("min_decryption_version") private int minDecryptionVersion; - - private String name; - - public String getCipherMode() { - return cipherMode; - } - - public void setCipherMode(String cipherMode) { - this.cipherMode = cipherMode; - } - - public boolean isDeletionAllowed() { - return deletionAllowed; - } - - public void setDeletionAllowed(boolean deletionAllowed) { - this.deletionAllowed = deletionAllowed; - } - - public boolean isDerived() { - return derived; - } - - public void setDerived(boolean derived) { - this.derived = derived; - } - - public Map getKeys() { - return keys; - } - - public void setKeys(Map keys) { - this.keys = keys; - } - - public boolean isLatestVersion() { - return latestVersion; - } - - public void setLatestVersion(boolean latestVersion) { - this.latestVersion = latestVersion; - } - - public int getMinDecryptionVersion() { - return minDecryptionVersion; - } - - public void setMinDecryptionVersion(int minDecryptionVersion) { - this.minDecryptionVersion = minDecryptionVersion; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } + /** + * @return the key type ({@code aes-gcm}, {@code ecdsa-p256}, ...). + */ + String getType(); } diff --git a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitKeyConfiguration.java b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitKeyConfiguration.java index fb01cd72..4fdff654 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitKeyConfiguration.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitKeyConfiguration.java @@ -24,31 +24,76 @@ import com.fasterxml.jackson.annotation.JsonProperty; */ public class VaultTransitKeyConfiguration { - @JsonProperty("deletion_allowed") private Boolean deletionAllowed; + @JsonProperty("deletion_allowed") private final Boolean deletionAllowed; - @JsonProperty("latest_version") private Integer latestVersion; + @JsonProperty("latest_version") private final Integer latestVersion; - public VaultTransitKeyConfiguration() { - } - - public VaultTransitKeyConfiguration(Boolean deletionAllowed, Integer latestVersion) { + private VaultTransitKeyConfiguration(Boolean deletionAllowed, Integer latestVersion) { this.deletionAllowed = deletionAllowed; this.latestVersion = latestVersion; } + /** + * @return a new {@link VaultTransitKeyConfigurationBuilder}. + */ + public static VaultTransitKeyConfigurationBuilder builder() { + return new VaultTransitKeyConfigurationBuilder(); + } + + /** + * @return whether key deletion is configured + */ public Boolean getDeletionAllowed() { return deletionAllowed; } - public void setDeletionAllowed(Boolean deletionAllowed) { - this.deletionAllowed = deletionAllowed; - } - + /** + * @return latest key version + */ public Integer getLatestVersion() { return latestVersion; } - public void setLatestVersion(Integer latestVersion) { - this.latestVersion = latestVersion; + /** + * Builder for {@link VaultTransitKeyConfiguration}. + */ + public static class VaultTransitKeyConfigurationBuilder { + + private Boolean deletionAllowed; + private Integer latestVersion; + + VaultTransitKeyConfigurationBuilder() {} + + /** + * Set whether key deletion is allowed. + * + * @param deletionAllowed {@literal true} if key deletion should be allowed. + * @return {@code this} {@link VaultTransitKeyConfigurationBuilder}. + */ + public VaultTransitKeyConfigurationBuilder deletionAllowed(boolean deletionAllowed) { + this.deletionAllowed = deletionAllowed; + return this; + } + + /** + * Set the latest key version. + * + * @param latestVersion key version. + * @return {@code this} {@link VaultTransitKeyConfigurationBuilder}. + */ + public VaultTransitKeyConfigurationBuilder latestVersion(int latestVersion) { + this.latestVersion = latestVersion; + return this; + } + + /** + * Build a new {@link VaultTransitKeyConfiguration} instance. + * + * @return a new {@link VaultTransitKeyConfiguration}. + */ + public VaultTransitKeyConfiguration build() { + return new VaultTransitKeyConfiguration(deletionAllowed, latestVersion); + } + } } diff --git a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitKeyCreationRequest.java b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitKeyCreationRequest.java index e8faa7fa..500d2af5 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitKeyCreationRequest.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitKeyCreationRequest.java @@ -15,6 +15,8 @@ */ package org.springframework.vault.support; +import org.springframework.util.Assert; + import com.fasterxml.jackson.annotation.JsonProperty; /** @@ -24,42 +26,98 @@ import com.fasterxml.jackson.annotation.JsonProperty; */ public class VaultTransitKeyCreationRequest { - private Boolean derived; + private final Boolean derived; - @JsonProperty("convergent_encryption") private Boolean convergentEncryption; + @JsonProperty("type") private final String type; - /** - * Creates a new {@link VaultTransitKeyCreationRequest}. - */ - public VaultTransitKeyCreationRequest() {} - - /** - * Creates a new {@link VaultTransitKeyCreationRequest} and sets {@code derived} and {@code convergentEncryption} - * properties. - * - * @param derived {@literal true} if key derivation MUST be used. - * @param convergentEncryption {@literal true} if key will support convergent encryption, where the same plaintext - * creates the same ciphertext. - */ - public VaultTransitKeyCreationRequest(boolean derived, boolean convergentEncryption) { + @JsonProperty("convergent_encryption") private final Boolean convergentEncryption; + private VaultTransitKeyCreationRequest(Boolean derived, String type, Boolean convergentEncryption) { this.derived = derived; + this.type = type; this.convergentEncryption = convergentEncryption; } + /** + * @return a new {@link VaultTransitKeyCreationRequestBuilder}. + */ + public static VaultTransitKeyCreationRequestBuilder builder() { + return new VaultTransitKeyCreationRequestBuilder(); + } + public Boolean getDerived() { return derived; } - public void setDerived(Boolean derived) { - this.derived = derived; - } - public Boolean getConvergentEncryption() { return convergentEncryption; } - public void setConvergentEncryption(Boolean convergentEncryption) { - this.convergentEncryption = convergentEncryption; + public String getType() { + return type; + } + + /** + * Builder for {@link VaultTransitKeyCreationRequest}. + */ + public static class VaultTransitKeyCreationRequestBuilder { + + private Boolean derived; + private String type = "aes256-gcm96"; + private Boolean convergentEncryption; + + VaultTransitKeyCreationRequestBuilder() {} + + /** + * Configure key derivation. + * + * @param type the type of key to create, must not be empty or {@literal null}. + * @return {@code this} {@link VaultTransitKeyCreationRequestBuilder}. + */ + public VaultTransitKeyCreationRequestBuilder type(String type) { + + Assert.hasText(type, "Type must not be empty"); + + this.type = type; + return this; + } + + /** + * Configure key derivation. + * + * @param derived {@literal true} if key derivation MUST be used. If enabled, all encrypt/decrypt requests to this + * named key must provide a context which is used for key derivation. Defaults to {@literal false}. + * @return {@code this} {@link VaultTransitKeyCreationRequestBuilder}. + */ + public VaultTransitKeyCreationRequestBuilder derived(boolean derived) { + + this.derived = derived; + return this; + } + + /** + * Configure convergent encryption where the same plaintext creates the same ciphertext. Requires + * {@link #derived(boolean)} to be {@literal true}. + * + * @param convergentEncryption {@literal true} the same plaintext creates the same ciphertext. Defaults to + * {@literal false}. + * @return {@code this} {@link VaultTransitKeyCreationRequestBuilder}. + */ + public VaultTransitKeyCreationRequestBuilder convergentEncryption(boolean convergentEncryption) { + this.convergentEncryption = convergentEncryption; + return this; + } + + /** + * Build a new {@link VaultTransitKeyCreationRequest} instance. Requires {@link #type(String)} to be configured. + * + * @return a new {@link VaultTransitKeyCreationRequest}. + */ + public VaultTransitKeyCreationRequest build() { + + Assert.hasText(type, "Type must not be empty"); + + return new VaultTransitKeyCreationRequest(derived, type, convergentEncryption); + } } } diff --git a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitRequest.java b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitRequest.java deleted file mode 100644 index 8427ecf4..00000000 --- a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitRequest.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.vault.support; - -/** - * Transit backend encryption/decryption/rewrapping request options. - * - * @author Mark Paluch - */ -public class VaultTransitRequest { - - private byte[] context; - - private byte[] nonce; - - public byte[] getContext() { - return context; - } - - public void setContext(byte[] context) { - this.context = context; - } - - public byte[] getNonce() { - return nonce; - } - - public void setNonce(byte[] nonce) { - this.nonce = nonce; - } -} diff --git a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultUnsealStatus.java b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultUnsealStatus.java index 12767eda..47affb8f 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultUnsealStatus.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultUnsealStatus.java @@ -15,54 +15,30 @@ */ package org.springframework.vault.support; -import com.fasterxml.jackson.annotation.JsonProperty; - /** - * Value object to bind Vault HTTP Unseal API responses. + * Vault unseal status. * * @author Mark Paluch */ -public class VaultUnsealStatus { +public interface VaultUnsealStatus { - private boolean sealed; + /** + * @return total number of existing keyshares. + */ + int getSecretShares(); - @JsonProperty("t") private int secretThreshold; + /** + * @return required number of keyshares to unseal Vault. + */ + int getSecretThreshold(); - @JsonProperty("n") private int secretShares; + /** + * @return {@literal true} if Vault is sealed. + */ + boolean isSealed(); - private int progress; - - public VaultUnsealStatus() {} - - public int getSecretShares() { - return secretShares; - } - - public void setSecretShares(int secretShares) { - this.secretShares = secretShares; - } - - public int getSecretThreshold() { - return secretThreshold; - } - - public void setSecretThreshold(int secretThreshold) { - this.secretThreshold = secretThreshold; - } - - public boolean isSealed() { - return sealed; - } - - public void setSealed(boolean sealed) { - this.sealed = sealed; - } - - public int getProgress() { - return progress; - } - - public void setProgress(int progress) { - this.progress = progress; - } + /** + * @return number of remaining required keyshares to unseal Vault. + */ + int getProgress(); } diff --git a/spring-vault-core/src/test/java/org/springframework/vault/core/VaultTransitTemplateIntegrationTests.java b/spring-vault-core/src/test/java/org/springframework/vault/core/VaultTransitTemplateIntegrationTests.java index 43f30992..1acbf12a 100644 --- a/spring-vault-core/src/test/java/org/springframework/vault/core/VaultTransitTemplateIntegrationTests.java +++ b/spring-vault-core/src/test/java/org/springframework/vault/core/VaultTransitTemplateIntegrationTests.java @@ -15,7 +15,8 @@ */ package org.springframework.vault.core; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; import org.junit.Before; import org.junit.Test; @@ -25,10 +26,10 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.vault.client.VaultException; import org.springframework.vault.support.VaultMount; +import org.springframework.vault.support.VaultTransitContext; import org.springframework.vault.support.VaultTransitKey; import org.springframework.vault.support.VaultTransitKeyConfiguration; import org.springframework.vault.support.VaultTransitKeyCreationRequest; -import org.springframework.vault.support.VaultTransitRequest; import org.springframework.vault.util.IntegrationTestSupport; /** @@ -52,7 +53,7 @@ public class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport } try { - transitOperations.configureKey("mykey", new VaultTransitKeyConfiguration(true, null)); + transitOperations.configureKey("mykey", VaultTransitKeyConfiguration.builder().deletionAllowed(true).build()); } catch (Exception e) {} try { @@ -67,13 +68,7 @@ public class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport VaultTransitKey mykey = transitOperations.getKey("mykey"); - if (mykey.getCipherMode() != null) { - // <= Vault 0.6.1 - assertThat(mykey.getCipherMode()).isEqualTo("aes-gcm"); - } else { - // Vault 0.6.2+ - assertThat(mykey.getType()).startsWith("aes256-gcm"); - } + assertThat(mykey.getType()).startsWith("aes"); assertThat(mykey.getName()).isEqualTo("mykey"); assertThat(mykey.isDeletionAllowed()).isFalse(); @@ -85,9 +80,10 @@ public class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport @Test public void createKeyShouldCreateKeyWithOptions() throws Exception { - VaultTransitKeyCreationRequest request = new VaultTransitKeyCreationRequest(); - request.setConvergentEncryption(true); - request.setDerived(true); + VaultTransitKeyCreationRequest request = VaultTransitKeyCreationRequest.builder() // + .convergentEncryption(true) // + .derived(true) // + .build(); transitOperations.createKey("mykey", request); @@ -122,7 +118,7 @@ public class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport public void deleteKeyShouldDeleteKey() throws Exception { transitOperations.createKey("mykey"); - transitOperations.configureKey("mykey", new VaultTransitKeyConfiguration(true, null)); + transitOperations.configureKey("mykey", VaultTransitKeyConfiguration.builder().deletionAllowed(true).build()); transitOperations.deleteKey("mykey"); assertThat(transitOperations.getKey("mykey")).isNull(); @@ -140,11 +136,12 @@ public class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport @Test public void encryptShouldCreateCiphertextWithNonceAndContext() throws Exception { - transitOperations.createKey("mykey", new VaultTransitKeyCreationRequest(true, true)); + transitOperations.createKey("mykey", + VaultTransitKeyCreationRequest.builder().convergentEncryption(true).derived(true).build()); - VaultTransitRequest transitRequest = new VaultTransitRequest(); - transitRequest.setContext("blubb".getBytes()); - transitRequest.setNonce("123456789012".getBytes()); + VaultTransitContext transitRequest = VaultTransitContext.builder().context("blubb".getBytes()) // + .nonce("123456789012".getBytes()) // + .build(); String ciphertext = transitOperations.encrypt("mykey", "hello-world".getBytes(), transitRequest); assertThat(ciphertext).startsWith("vault:v1:"); @@ -164,11 +161,13 @@ public class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport @Test public void decryptShouldCreatePlaintextWithNonceAndContext() throws Exception { - transitOperations.createKey("mykey", new VaultTransitKeyCreationRequest(true, true)); + transitOperations.createKey("mykey", + VaultTransitKeyCreationRequest.builder().convergentEncryption(true).derived(true).build()); - VaultTransitRequest transitRequest = new VaultTransitRequest(); - transitRequest.setContext("blubb".getBytes()); - transitRequest.setNonce("123456789012".getBytes()); + VaultTransitContext transitRequest = VaultTransitContext.builder() // + .context("blubb".getBytes()) // + .nonce("123456789012".getBytes()) // + .build(); String ciphertext = transitOperations.encrypt("mykey", "hello-world".getBytes(), transitRequest); @@ -192,11 +191,13 @@ public class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport @Test public void encryptAndRewrapShouldCreateCiphertextWithNonceAndContext() throws Exception { - transitOperations.createKey("mykey", new VaultTransitKeyCreationRequest(true, true)); + transitOperations.createKey("mykey", + VaultTransitKeyCreationRequest.builder().convergentEncryption(true).derived(true).build()); - VaultTransitRequest transitRequest = new VaultTransitRequest(); - transitRequest.setContext("blubb".getBytes()); - transitRequest.setNonce("123456789012".getBytes()); + VaultTransitContext transitRequest = VaultTransitContext.builder() // + .context("blubb".getBytes()) // + .nonce("123456789012".getBytes()) // + .build(); String ciphertext = transitOperations.encrypt("mykey", "hello-world".getBytes(), transitRequest); transitOperations.rotate("mykey"); diff --git a/spring-vault-core/src/test/java/org/springframework/vault/util/PrepareVault.java b/spring-vault-core/src/test/java/org/springframework/vault/util/PrepareVault.java index 99cce702..b9e75028 100644 --- a/spring-vault-core/src/test/java/org/springframework/vault/util/PrepareVault.java +++ b/spring-vault-core/src/test/java/org/springframework/vault/util/PrepareVault.java @@ -60,7 +60,7 @@ public class PrepareVault { int requiredKeys = 2; VaultInitializationResponse initialized = vaultOperations.opsForSys() - .initialize(new VaultInitializationRequest(createKeys, requiredKeys)); + .initialize(VaultInitializationRequest.create(createKeys, requiredKeys)); for (int i = 0; i < requiredKeys; i++) { @@ -71,7 +71,7 @@ public class PrepareVault { } } - return VaultToken.of(initialized.getRootToken()); + return initialized.getRootToken(); } /**