Polishing.

Update documentation. Provide builder for VaultMount. Rename VaultHealthResponse to VaultHealth. Make VaultMount and VaultHealth immutable.
This commit is contained in:
Mark Paluch
2016-09-18 00:08:15 +02:00
parent a3ff656c4d
commit 5637f440f3
15 changed files with 294 additions and 134 deletions

View File

@@ -18,10 +18,13 @@ package org.springframework.vault.authentication;
import org.springframework.util.Assert;
/**
* Authentication options for {@link AppIdAuthentication}. Authentication options provide the path, appId and a
* {@link AppIdUserIdMechanism}. {@link AppIdAuthentication} can be constructed using {@link #builder()}.
* Authentication options for {@link AppIdAuthentication}.
* <p>
* Authentication options provide the path, appId and a {@link AppIdUserIdMechanism}. {@link AppIdAuthentication} can be
* constructed using {@link #builder()}. Instances of this class are immutable once constructed.
*
* @author Mark Paluch
* @see AppIdAuthentication
* @see AppIdUserIdMechanism
* @see #builder()
*/
@@ -29,10 +32,19 @@ public class AppIdAuthenticationOptions {
public final static String DEFAULT_APPID_AUTHENTICATION_PATH = "app-id";
/**
* Path of the appid authentication backend mount.
*/
private final String path;
/**
* The AppId
*/
private final String appId;
/**
* {@link AppIdUserIdMechanism} instance to obtain a userId.
*/
private final AppIdUserIdMechanism userIdMechanism;
private AppIdAuthenticationOptions(String path, String appId, AppIdUserIdMechanism userIdMechanism) {
@@ -76,7 +88,9 @@ public class AppIdAuthenticationOptions {
public static class AppIdAuthenticationOptionsBuilder {
private String path = DEFAULT_APPID_AUTHENTICATION_PATH;
private String appId;
private AppIdUserIdMechanism userIdMechanism;
AppIdAuthenticationOptionsBuilder() {}

View File

@@ -17,9 +17,10 @@
package org.springframework.vault.authentication;
/**
* Interface to obtain a UserId for AppId authentication.
* Interface to obtain a UserId for AppId authentication. Implementations are used by {@link AppIdAuthentication}.
*
* @author Mark Paluch
* @see AppIdAuthentication
*/
public interface AppIdUserIdMechanism {

View File

@@ -33,8 +33,11 @@ import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
/**
* AWS-EC2 login implementation. AWS-EC2 login uses the EC2 identity document and a nonce to login into Vault. AWS-EC2
* login obtains the PKCS#7 signed EC2 identity document and generates a {@link #createNonce() nonce}.
* AWS-EC2 login implementation.
* <p>
* AWS-EC2 login uses the EC2 identity document and a nonce to login into Vault. AWS-EC2 login obtains the PKCS#7 signed
* EC2 identity document and generates a {@link #createNonce() nonce}. Instances of this class are immutable once
* constructed.
*
* @author Mark Paluch
* @see AwsEc2AuthenticationOptions
@@ -45,8 +48,11 @@ public class AwsEc2Authentication implements ClientAuthentication {
private final static Logger logger = LoggerFactory.getLogger(AwsEc2Authentication.class);
private final AwsEc2AuthenticationOptions options;
private final VaultClient vaultClient;
private final RestTemplate restTemplate;
private final AtomicReference<char[]> nonce = new AtomicReference<char[]>();
/**

View File

@@ -20,8 +20,11 @@ import java.net.URI;
import org.springframework.util.Assert;
/**
* Authentication options for {@link AwsEc2Authentication}. Authentication options provide the path, the Identity
* Document URI and an optional role. {@link AwsEc2AuthenticationOptions} can be constructed using {@link #builder()}.
* Authentication options for {@link AwsEc2Authentication}.
* <p>
* Authentication options provide the path, the Identity Document URI and an optional role.
* {@link AwsEc2AuthenticationOptions} can be constructed using {@link #builder()}. Instances of this class are
* immutable once constructed.
*
* @author Mark Paluch
* @see AwsEc2Authentication
@@ -40,10 +43,19 @@ public class AwsEc2AuthenticationOptions {
*/
public final static AwsEc2AuthenticationOptions DEFAULT = new AwsEc2AuthenticationOptions();
/**
* Path of the aws-ec2 authentication backend mount.
*/
private final String path;
/**
* {@link URI} to the AWS EC2 PKCS#7-signed identity document.
*/
private final URI identityDocumentUri;
/**
* EC2 instance role name. May be {@literal null} if none.
*/
private final String role;
private AwsEc2AuthenticationOptions() {
@@ -65,21 +77,21 @@ public class AwsEc2AuthenticationOptions {
}
/**
* @return the mount path.
* @return the path of the aws-ec2 authentication backend mount.
*/
public String getPath() {
return path;
}
/**
* @return the {@link URI} to the Identity Document.
* @return the {@link URI} to the AWS EC2 PKCS#7-signed identity document.
*/
public URI getIdentityDocumentUri() {
return identityDocumentUri;
}
/**
* @return the role, may be {@literal null}.
* @return the role, may be {@literal null} if none.
*/
public String getRole() {
return role;
@@ -115,6 +127,7 @@ public class AwsEc2AuthenticationOptions {
* @see #DEFAULT_PKCS7_IDENTITY_DOCUMENT_URI
*/
public AwsEc2AuthenticationOptionsBuilder identityDocumentUri(URI identityDocumentUri) {
this.identityDocumentUri = identityDocumentUri;
return this;
}

View File

@@ -18,7 +18,7 @@ package org.springframework.vault.core;
import java.util.Map;
import org.springframework.vault.client.VaultException;
import org.springframework.vault.support.VaultHealthResponse;
import org.springframework.vault.support.VaultHealth;
import org.springframework.vault.support.VaultInitializationRequest;
import org.springframework.vault.support.VaultInitializationResponse;
import org.springframework.vault.support.VaultMount;
@@ -34,19 +34,23 @@ public interface VaultSysOperations {
/**
* @return {@literal true} if Vault is initialized.
* @see <a href="https://www.vaultproject.io/docs/http/sys-init.html">GET /sys/init</a>
*/
boolean isInitialized() throws VaultException;
/**
* Initializes Vault with a {@link VaultInitializationRequest}.
* Initialize Vault with a {@link VaultInitializationRequest}.
*
* @param vaultInitializationRequest must not be {@literal null}.
* @return the {@link VaultInitializationResponse}.
* @see <a href="https://www.vaultproject.io/docs/http/sys-init.html">PUT /sys/init</a>
*/
VaultInitializationResponse initialize(VaultInitializationRequest vaultInitializationRequest) throws VaultException;
/**
* Seals vault.
* Seal vault.
*
* @see <a href="https://www.vaultproject.io/docs/http/sys-seal.html">PUT /sys/seal</a>
*/
void seal() throws VaultException;
@@ -55,11 +59,13 @@ public interface VaultSysOperations {
*
* @param keyShare must not be empty and not {@literal null}.
* @return the {@link VaultUnsealStatus}.
* @see <a href="https://www.vaultproject.io/docs/http/sys-unseal.html">PUT /sys/unseal</a>
*/
VaultUnsealStatus unseal(String keyShare) throws VaultException;
/**
* @return the {@link VaultUnsealStatus}.
* @see <a href="https://www.vaultproject.io/docs/http/sys-unseal.html">GET /sys/unseal</a>
*/
VaultUnsealStatus getUnsealStatus() throws VaultException;
@@ -68,11 +74,13 @@ public interface VaultSysOperations {
*
* @param path must not be empty or {@literal null}.
* @param vaultMount must not be {@literal null}.
* @see <a href="https://www.vaultproject.io/docs/http/sys-mounts.html">POST /sys/mounts/{mount}</a>
*/
void mount(String path, VaultMount vaultMount) throws VaultException;
/**
* @return {@link Map} of all secret backend {@link VaultMount mounts}.
* @see <a href="https://www.vaultproject.io/docs/http/sys-mounts.html">GET /sys/mounts/</a>
*/
Map<String, VaultMount> getMounts() throws VaultException;
@@ -80,6 +88,7 @@ public interface VaultSysOperations {
* Unmounts the secret backend mount at {@code path}.
*
* @param path must not be empty or {@literal null}.
* @see <a href="https://www.vaultproject.io/docs/http/sys-mounts.html">DELETE /sys/mounts/{mount}</a>
*/
void unmount(String path) throws VaultException;
@@ -88,11 +97,13 @@ public interface VaultSysOperations {
*
* @param path must not be empty or {@literal null}.
* @param vaultMount must not be {@literal null}.
* @see <a href="https://www.vaultproject.io/docs/http/sys-auth.html">POST /sys/auth/{mount}</a>
*/
void authMount(String path, VaultMount vaultMount) throws VaultException;
/**
* @return {@link Map} of all auth backend {@link VaultMount mounts}.
* @see <a href="https://www.vaultproject.io/docs/http/sys-auth.html">GET /sys/auth/</a>
*/
Map<String, VaultMount> getAuthMounts() throws VaultException;
@@ -100,11 +111,15 @@ public interface VaultSysOperations {
* Unmounts the auth backend mount at {@code path}.
*
* @param path must not be empty or {@literal null}.
* @see <a href="https://www.vaultproject.io/docs/http/sys-auth.html">DELETE /sys/auth/{mount}</a>
*/
void authUnmount(String path) throws VaultException;
/**
* @return the {@link VaultHealthResponse}.
* Returns the health status of Vault.
*
* @return the {@link VaultHealth}.
* @see <a href="https://www.vaultproject.io/docs/http/sys-health.html">GET /sys/health</a>
*/
VaultHealthResponse health() throws VaultException;
VaultHealth health() throws VaultException;
}

View File

@@ -31,7 +31,7 @@ import org.springframework.vault.client.VaultException;
import org.springframework.vault.client.VaultResponseEntity;
import org.springframework.vault.core.VaultOperations.ClientCallback;
import org.springframework.vault.core.VaultOperations.SessionCallback;
import org.springframework.vault.support.VaultHealthResponse;
import org.springframework.vault.support.VaultHealth;
import org.springframework.vault.support.VaultInitializationRequest;
import org.springframework.vault.support.VaultInitializationResponse;
import org.springframework.vault.support.VaultMount;
@@ -191,7 +191,7 @@ public class VaultSysTemplate implements VaultSysOperations {
}
@Override
public VaultHealthResponse health() {
public VaultHealth health() {
return vaultOperations.doWithRestTemplate("sys/health", Collections.<String, Object> emptyMap(), HEALTH);
}
@@ -283,9 +283,10 @@ public class VaultSysTemplate implements VaultSysOperations {
if (map.containsKey("type")) {
VaultMount vaultMount = new VaultMount((String) map.get("type"));
vaultMount.setDescription((String) map.get("description"));
vaultMount.setConfig((Map) map.get("config"));
VaultMount vaultMount = VaultMount.builder() //
.type((String) map.get("type")) //
.description((String) map.get("description")) //
.config((Map) map.get("config")).build();
topLevelMounts.put(name, vaultMount);
}
@@ -295,21 +296,21 @@ public class VaultSysTemplate implements VaultSysOperations {
}
private static class Health implements VaultAccessor.RestTemplateCallback<VaultHealthResponse> {
private static class Health implements VaultAccessor.RestTemplateCallback<VaultHealth> {
@Override
public VaultHealthResponse doWithRestTemplate(URI uri, RestTemplate restTemplate) {
public VaultHealth doWithRestTemplate(URI uri, RestTemplate restTemplate) {
try {
ResponseEntity<VaultHealthResponse> healthResponse = restTemplate.exchange(uri, HttpMethod.GET, null,
VaultHealthResponse.class);
ResponseEntity<VaultHealth> healthResponse = restTemplate.exchange(uri, HttpMethod.GET, null,
VaultHealth.class);
return healthResponse.getBody();
} catch (HttpStatusCodeException responseError) {
try {
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(responseError.getResponseBodyAsString(), VaultHealthResponse.class);
return mapper.readValue(responseError.getResponseBodyAsString(), VaultHealth.class);
} catch (Exception jsonError) {
throw responseError;
}

View File

@@ -24,58 +24,66 @@ import org.springframework.vault.support.VaultTokenResponse;
* Interface that specifies token-related operations.
*
* @author Mark Paluch
* @see <a href="https://www.vaultproject.io/docs/auth/token.html">Auth Backend: Token</a>
*/
public interface VaultTokenOperations {
/**
* Creates a new token.
* Create a new token.
*
* @return a {@link VaultTokenResponse}
* @see <a href="https://www.vaultproject.io/docs/auth/token.html">POST /auth/token/create</a>
*/
VaultTokenResponse create() throws VaultException;
/**
* Creates a new token for the given {@link VaultTokenRequest}.
* Create a new token for the given {@link VaultTokenRequest}.
*
* @param request must not be {@literal null}.
* @return a {@link VaultTokenResponse}
* @see <a href="https://www.vaultproject.io/docs/auth/token.html">POST /auth/token/create</a>
*/
VaultTokenResponse create(VaultTokenRequest request) throws VaultException;
/**
* Creates a new orphan token.
* Create a new orphan token.
*
* @return a {@link VaultTokenResponse}
* @see <a href="https://www.vaultproject.io/docs/auth/token.html">POST /auth/token/create-orphan</a>
*/
VaultTokenResponse createOrphan();
/**
* Creates a new orphan token for the given {@link VaultTokenRequest}.
* Create a new orphan token for the given {@link VaultTokenRequest}.
*
* @param request must not be {@literal null}.
* @return a {@link VaultTokenResponse}
* @see <a href="https://www.vaultproject.io/docs/auth/token.html">POST /auth/token/create-orphan</a>
*/
VaultTokenResponse createOrphan(VaultTokenRequest request);
/**
* Renews a {@link VaultToken}.
* Renew a {@link VaultToken}.
*
* @param vaultToken must not be {@literal null}.
* @return a {@link VaultTokenResponse}
* @see <a href="https://www.vaultproject.io/docs/auth/token.html">POST /auth/token/renew/{token}</a>
*/
VaultTokenResponse renew(VaultToken vaultToken);
/**
* Revokes a {@link VaultToken}.
* Revoke a {@link VaultToken}.
*
* @param vaultToken must not be {@literal null}.
* @see <a href="https://www.vaultproject.io/docs/auth/token.html">POST /auth/token/revoke/{token}</a>
*/
void revoke(VaultToken vaultToken);
/**
* Revokes a {@link VaultToken} but not its child tokens.
* Revoke a {@link VaultToken} but not its child tokens.
*
* @param vaultToken must not be {@literal null}.
* @see <a href="https://www.vaultproject.io/docs/auth/token.html">POST /auth/token/revoke-orphan/{token}</a>
*/
void revokeOrphan(VaultToken vaultToken);
}

View File

@@ -0,0 +1,86 @@
/*
* 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;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Value object to bind HTTP API responses for sys/health. Instances of this class are immutable.
*
* @author Stuart Ingram
* @author Bill Koch
*/
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;
}
/**
* @return {@literal true} if the Vault instance is initialized, otherwise {@literal false}.
*/
public boolean isInitialized() {
return initialized;
}
/**
* @return {@literal true} if the Vault instance is sealed, otherwise {@literal false} if the Vault instance is
* unsealed.
*/
public boolean isSealed() {
return sealed;
}
/**
* @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;
}
/**
* @return the server time in seconds, UTC.
*/
public int getServerTimeUtc() {
return serverTimeUtc;
}
}

View File

@@ -1,68 +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;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Value object to bind HTTP API responses for sys/health.
*
* @author Stuart Ingram
* @author Bill Koch
*/
public class VaultHealthResponse {
private boolean initialized;
private boolean sealed;
private boolean standby;
@JsonProperty("server_time_utc") private int serverTimeUtc;
public boolean isInitialized() {
return initialized;
}
public void setInitialized(boolean initialized) {
this.initialized = initialized;
}
public boolean isSealed() {
return sealed;
}
public void setSealed(boolean sealed) {
this.sealed = sealed;
}
public boolean isStandby() {
return standby;
}
public void setStandby(boolean standby) {
this.standby = standby;
}
public int getServerTimeUtc() {
return serverTimeUtc;
}
public void setServerTimeUtc(int serverTimeUtc) {
this.serverTimeUtc = serverTimeUtc;
}
}

View File

@@ -19,57 +19,137 @@ import java.util.Map;
import org.springframework.util.Assert;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Value object to bind Vault HTTP Mount API requests/responses.
*
* <p>
* A {@link VaultMount} represents an auth or secret mount with its config details. Instances of this class are
* immutable once constructed.
*
* @author Mark Paluch
* @see #builder()
*/
public class VaultMount {
private String type;
private String description;
private Map<String, Object> config;
/**
* Backend type. Can be an auth or secret backend.
*/
private final String type;
/**
* Creates a new {@link VaultMount}.
* Human readable description of the mount.
*/
public VaultMount() {}
private final String description;
/**
* Additional configuration.
*/
private final Map<String, Object> config;
private VaultMount(@JsonProperty("type") String type, @JsonProperty("description") String description,
@JsonProperty("config") Map<String, Object> config) {
this.type = type;
this.description = description;
this.config = config;
}
/**
* Creates a new {@link VaultMount} given a {@code type}.
*
* @param type must not be empty or {@literal null}.
*
* @param type backend type, must not be empty or {@literal null}.
*/
public VaultMount(String type) {
Assert.hasText(type, "Type must not be empty");
this.type = type;
public static VaultMount create(String type) {
return builder().type(type).build();
}
/**
* @return a new {@link VaultMountBuilder}.
*/
public static VaultMountBuilder builder() {
return new VaultMountBuilder();
}
/**
* @return the backend type.
*/
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
/**
* @return human readable description of this mount.
*/
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
/**
* @return additional configuration details.
*/
public Map<String, Object> getConfig() {
return config;
}
public void setConfig(Map<String, Object> config) {
this.config = config;
/**
* Builder to build a {@link VaultMount}.
*/
public static class VaultMountBuilder {
private String type;
private String description;
private Map<String, Object> config;
VaultMountBuilder() {}
/**
* Configure the backend type.
*
* @param type the backend type, must not be empty or {@literal null}.
* @return {@literal this} {@link VaultMountBuilder}.
*/
public VaultMount.VaultMountBuilder type(String type) {
Assert.hasText(type, "Type must not be empty or null");
this.type = type;
return this;
}
/**
* Configure a human readable description of this mount.
*
* @param description a human readable description of this mount.
* @return {@literal this} {@link VaultMountBuilder}.
*/
public VaultMount.VaultMountBuilder description(String description) {
this.description = description;
return this;
}
/**
* Set additional configuration details for this mount.
*
* @param config additional configuration details for this mount.
* @return {@literal this} {@link VaultMountBuilder}.
*/
public VaultMount.VaultMountBuilder config(Map<String, Object> config) {
this.config = config;
return this;
}
/**
* Builds a new {@link VaultMount} instance. Requires {@link #type(String)} to be configured.
*
* @return a new {@link VaultMount}.
*/
public VaultMount build() {
Assert.hasText(type, "Type must not be empty or null");
return new VaultMount(type, description, config);
}
}
}

View File

@@ -40,6 +40,7 @@ import org.springframework.vault.util.IntegrationTestSupport;
public class VaultSysTemplateIntegrationTests extends IntegrationTestSupport {
@Autowired private VaultOperations vaultOperations;
private VaultSysOperations adminOperations;
@Before
@@ -66,9 +67,9 @@ public class VaultSysTemplateIntegrationTests extends IntegrationTestSupport {
adminOperations.unmount("other");
}
VaultMount mount = new VaultMount("generic");
mount.setConfig(Collections.singletonMap("default_lease_ttl", (Object) "1h"));
mount.setDescription("hello, world");
VaultMount mount = VaultMount.builder().type("generic") //
.config(Collections.singletonMap("default_lease_ttl", (Object) "1h")) //
.description("hello, world").build();
adminOperations.mount("other", mount);
@@ -101,8 +102,7 @@ public class VaultSysTemplateIntegrationTests extends IntegrationTestSupport {
adminOperations.authUnmount("other");
}
VaultMount mount = new VaultMount("userpass");
mount.setDescription("hello, world");
VaultMount mount = VaultMount.builder().type("userpass").description("hello, world").build();
adminOperations.authMount("other", mount);

View File

@@ -47,7 +47,7 @@ public class VaultTemplateTransitIntegrationTests extends IntegrationTestSupport
VaultSysOperations adminOperations = vaultOperations.opsForSys();
if (!adminOperations.getMounts().containsKey("transit/")) {
adminOperations.mount("transit", new VaultMount("transit"));
adminOperations.mount("transit", VaultMount.create("transit"));
vaultOperations.write("transit/keys/mykey", null);
vaultOperations.write("transit/keys/derived", Collections.singletonMap("derived", true));

View File

@@ -48,7 +48,7 @@ public class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport
transitOperations = vaultOperations.opsForTransit();
if (!vaultOperations.opsForSys().getMounts().containsKey("transit/")) {
vaultOperations.opsForSys().mount("transit", new VaultMount("transit"));
vaultOperations.opsForSys().mount("transit", VaultMount.create("transit"));
}
try {

View File

@@ -29,11 +29,14 @@ import org.springframework.vault.support.VaultTokenResponse;
import org.springframework.vault.support.VaultUnsealStatus;
/**
* Vault preparation utility class. This class allows preparing Vault for integration tests.
*
* @author Mark Paluch
*/
public class PrepareVault {
private final VaultOperations vaultOperations;
private final VaultSysOperations adminOperations;
public PrepareVault(VaultOperations vaultOperations) {
@@ -105,7 +108,7 @@ public class PrepareVault {
Assert.hasText(authBackend, "AuthBackend must not be empty");
adminOperations.authMount(authBackend, new VaultMount(authBackend));
adminOperations.authMount(authBackend, VaultMount.create(authBackend));
}
/**
@@ -130,7 +133,7 @@ public class PrepareVault {
Assert.hasText(secretBackend, "SecretBackend must not be empty");
adminOperations.mount(secretBackend, new VaultMount(secretBackend));
adminOperations.mount(secretBackend, VaultMount.create(secretBackend));
}
/**

View File

@@ -38,6 +38,7 @@ import org.springframework.vault.support.VaultToken;
public class VaultRule extends ExternalResource {
private final VaultEndpoint vaultEndpoint;
private final PrepareVault prepareVault;
private VaultToken token;