Polishing.

Align JavaDoc wording. Provide builders for Transit operation value objects. Refactor objects to interfaces for pure response data structures.
This commit is contained in:
Mark Paluch
2016-10-06 10:03:11 +02:00
parent 76a4ada567
commit c39da5e567
20 changed files with 477 additions and 350 deletions

View File

@@ -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";

View File

@@ -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}.

View File

@@ -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}.
*/

View File

@@ -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}.

View File

@@ -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<VaultInitializationResponse> response = client.putForEntity("sys/init",
vaultInitializationRequest, VaultInitializationResponse.class);
VaultResponseEntity<VaultInitializationResponseImpl> 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<VaultUnsealStatus> response = client.putForEntity("sys/unseal",
Collections.singletonMap("key", keyShare), VaultUnsealStatus.class);
VaultResponseEntity<VaultUnsealStatusImpl> 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<VaultUnsealStatus> response = client.getForEntity("sys/seal-status", VaultUnsealStatus.class);
VaultResponseEntity<VaultUnsealStatusImpl> 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<VaultHealth> healthResponse = restTemplate.exchange(uri, HttpMethod.GET, null,
VaultHealth.class);
ResponseEntity<VaultHealthImpl> 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<String> keys = new ArrayList<String>();
@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;
}
}
}

View File

@@ -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);
}

View File

@@ -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<VaultTransitKey> result = vaultOperations.read(String.format("%s/keys/%s", path, keyName),
VaultTransitKey.class);
VaultResponseSupport<VaultTransitKeyImpl> 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<String, String> request) {
private void applyTransitOptions(VaultTransitContext transitRequest, Map<String, String> 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<String, Long> 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;
}
}
}

View File

@@ -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}.
*/

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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<String> keys = new ArrayList<String>();
/**
* @return {@link List} of unseal keys.
*/
List<String> getKeys();
@JsonProperty("root_token") private String rootToken;
public VaultInitializationResponse() {}
public List<String> getKeys() {
return keys;
}
public void setKeys(List<String> keys) {
this.keys = keys;
}
public String getRootToken() {
return rootToken;
}
public void setRootToken(String rootToken) {
this.rootToken = rootToken;
}
/**
* @return the root {@link VaultToken}.
*/
VaultToken getRootToken();
}

View File

@@ -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}.
*/

View File

@@ -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);
}
}
}

View File

@@ -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<String, Long> getKeys();
private boolean derived;
/**
* @return {@literal true} if the key represents the latest version.
*/
boolean isLatestVersion();
private Map<String, Long> 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<String, Long> getKeys() {
return keys;
}
public void setKeys(Map<String, Long> 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();
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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");

View File

@@ -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();
}
/**