Introduce VaultLoginException.

We now throw a more specific exception (VaultLoginException) in cases login fails. Exception creation considers HTTP and other causes extracting relevant details and pulls this into a single method rather than spreading message construction in various places.

See gh-203.
This commit is contained in:
Mark Paluch
2018-04-23 15:47:13 +02:00
parent d701e89731
commit 199aa7ebf9
18 changed files with 132 additions and 74 deletions

View File

@@ -22,11 +22,9 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
import org.springframework.vault.VaultException;
import org.springframework.vault.client.VaultResponses;
import org.springframework.vault.support.VaultResponse;
import org.springframework.vault.support.VaultToken;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestOperations;
/**
@@ -111,9 +109,8 @@ public class AppIdAuthentication implements ClientAuthentication,
return LoginTokenUtil.from(response.getAuth());
}
catch (HttpStatusCodeException e) {
throw new VaultException(String.format("Cannot login using app-id: %s",
VaultResponses.getError(e.getResponseBodyAsString())));
catch (RestClientException e) {
throw VaultLoginException.create("app-id", e);
}
}

View File

@@ -28,7 +28,6 @@ import org.springframework.http.ResponseEntity;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.vault.VaultException;
import org.springframework.vault.authentication.AppRoleAuthenticationOptions.RoleId;
import org.springframework.vault.authentication.AppRoleAuthenticationOptions.SecretId;
import org.springframework.vault.authentication.AppRoleTokens.AbsentSecretId;
@@ -40,6 +39,7 @@ import org.springframework.vault.client.VaultResponses;
import org.springframework.vault.support.VaultResponse;
import org.springframework.vault.support.VaultToken;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestOperations;
import static org.springframework.vault.authentication.AuthenticationSteps.HttpRequestBuilder.get;
@@ -225,13 +225,12 @@ public class AppRoleAuthentication implements ClientAuthentication,
return LoginTokenUtil.from(response.getAuth());
}
catch (HttpStatusCodeException e) {
throw new VaultException(String.format("Cannot login using AppRole: %s",
VaultResponses.getError(e.getResponseBodyAsString())));
catch (RestClientException e) {
throw VaultLoginException.create("AppRole", e);
}
}
private String getRoleId(RoleId roleId) {
private String getRoleId(RoleId roleId) throws VaultLoginException {
if (roleId instanceof Provided) {
return ((Provided) roleId).getValue();
@@ -250,9 +249,9 @@ public class AppRoleAuthentication implements ClientAuthentication,
return (String) entity.getBody().getRequiredData().get("role_id");
}
catch (HttpStatusCodeException e) {
throw new VaultException(String.format(
throw new VaultLoginException(String.format(
"Cannot get Role id using AppRole: %s",
VaultResponses.getError(e.getResponseBodyAsString())));
VaultResponses.getError(e.getResponseBodyAsString())), e);
}
}
@@ -273,16 +272,16 @@ public class AppRoleAuthentication implements ClientAuthentication,
return (String) response.getRequiredData().get("role_id");
}
catch (HttpStatusCodeException e) {
throw new VaultException(String.format(
throw new VaultLoginException(String.format(
"Cannot unwrap Role id using AppRole: %s",
VaultResponses.getError(e.getResponseBodyAsString())));
VaultResponses.getError(e.getResponseBodyAsString())), e);
}
}
throw new IllegalArgumentException("Unknown RoleId configuration: " + roleId);
}
private String getSecretId(SecretId secretId) {
private String getSecretId(SecretId secretId) throws VaultLoginException {
if (secretId instanceof Provided) {
return ((Provided) secretId).getValue();
@@ -299,9 +298,9 @@ public class AppRoleAuthentication implements ClientAuthentication,
return (String) response.getRequiredData().get("secret_id");
}
catch (HttpStatusCodeException e) {
throw new VaultException(String.format(
throw new VaultLoginException(String.format(
"Cannot get Secret id using AppRole: %s",
VaultResponses.getError(e.getResponseBodyAsString())));
VaultResponses.getError(e.getResponseBodyAsString())), e);
}
}
@@ -322,9 +321,9 @@ public class AppRoleAuthentication implements ClientAuthentication,
return (String) response.getRequiredData().get("secret_id");
}
catch (HttpStatusCodeException e) {
throw new VaultException(String.format(
throw new VaultLoginException(String.format(
"Cannot unwrap Role id using AppRole: %s",
VaultResponses.getError(e.getResponseBodyAsString())));
VaultResponses.getError(e.getResponseBodyAsString())), e);
}
}

View File

@@ -104,13 +104,13 @@ public class AuthenticationStepsExecutor implements ClientAuthentication {
}
}
catch (HttpStatusCodeException e) {
throw new VaultException(String.format(
throw new VaultLoginException(String.format(
"HTTP request %s in state %s failed with Status %s and body %s",
o, state, e.getStatusCode(),
VaultResponses.getError(e.getResponseBodyAsString())));
VaultResponses.getError(e.getResponseBodyAsString())), e);
}
catch (RuntimeException e) {
throw new VaultException(String.format(
throw new VaultLoginException(String.format(
"Authentication execution failed in %s", o), e);
}
}

View File

@@ -133,7 +133,7 @@ public class AuthenticationStepsOperator implements VaultTokenSupplier {
stateObject));
})
.onErrorMap(
t -> new VaultException(
t -> new VaultLoginException(
"Cannot retrieve VaultToken from authentication chain", t));
}

View File

@@ -28,10 +28,8 @@ import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.vault.VaultException;
import org.springframework.vault.authentication.AuthenticationSteps.HttpRequestBuilder;
import org.springframework.vault.client.VaultResponses;
import org.springframework.vault.support.VaultResponse;
import org.springframework.vault.support.VaultToken;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestOperations;
@@ -183,9 +181,8 @@ public class AwsEc2Authentication implements ClientAuthentication,
return LoginTokenUtil.from(response.getAuth());
}
catch (HttpStatusCodeException e) {
throw new VaultException(String.format("Cannot login using AWS-EC2: %s",
VaultResponses.getError(e.getResponseBodyAsString())));
catch (RestClientException e) {
throw VaultLoginException.create("AWS-EC2", e);
}
}
@@ -213,7 +210,7 @@ public class AwsEc2Authentication implements ClientAuthentication,
return login;
}
catch (RestClientException e) {
throw new VaultException(String.format(
throw new VaultLoginException(String.format(
"Cannot obtain Identity Document from %s",
options.getIdentityDocumentUri()), e);
}

View File

@@ -36,10 +36,9 @@ import org.springframework.util.Assert;
import org.springframework.util.Base64Utils;
import org.springframework.util.StringUtils;
import org.springframework.vault.VaultException;
import org.springframework.vault.client.VaultResponses;
import org.springframework.vault.support.VaultResponse;
import org.springframework.vault.support.VaultToken;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestOperations;
/**
@@ -134,16 +133,15 @@ public class AwsIamAuthentication implements ClientAuthentication {
return LoginTokenUtil.from(response.getAuth());
}
catch (HttpStatusCodeException e) {
throw new VaultException(String.format("Cannot login using AWS-IAM: %s",
VaultResponses.getError(e.getResponseBodyAsString())));
catch (RestClientException e) {
throw VaultLoginException.create("AWS-IAM", e);
}
}
/**
* Create the request body to perform a Vault login using the AWS-IAM authentication
* method.
*
*
* @param options must not be {@literal null}.
* @return the map containing body key-value pairs.
*/

View File

@@ -21,11 +21,9 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
import org.springframework.vault.VaultException;
import org.springframework.vault.client.VaultResponses;
import org.springframework.vault.support.VaultResponse;
import org.springframework.vault.support.VaultToken;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestOperations;
import static org.springframework.vault.authentication.AuthenticationSteps.HttpRequestBuilder.post;
@@ -87,10 +85,8 @@ public class ClientCertificateAuthentication implements ClientAuthentication,
return LoginTokenUtil.from(response.getAuth());
}
catch (HttpStatusCodeException e) {
throw new VaultException(String.format(
"Cannot login using TLS certificates: %s",
VaultResponses.getError(e.getResponseBodyAsString())));
catch (RestClientException e) {
throw VaultLoginException.create("TLS Certificates", e);
}
}
}

View File

@@ -32,7 +32,7 @@ import org.springframework.vault.client.VaultResponses;
import org.springframework.vault.support.VaultResponse;
import org.springframework.vault.support.VaultResponseSupport;
import org.springframework.vault.support.VaultToken;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestOperations;
import static org.springframework.vault.authentication.AuthenticationSteps.HttpRequestBuilder.get;
@@ -222,10 +222,8 @@ public class CubbyholeAuthentication implements ClientAuthentication,
return entity.getBody().getData();
}
catch (HttpStatusCodeException e) {
throw new VaultException(String.format(
"Cannot retrieve Token from Cubbyhole: %s %s", e.getStatusCode(),
VaultResponses.getError(e.getResponseBodyAsString())));
catch (RestClientException e) {
throw VaultLoginException.create("Cubbyhole", e);
}
}
@@ -263,7 +261,7 @@ public class CubbyholeAuthentication implements ClientAuthentication,
}
if (data == null || data.isEmpty()) {
throw new VaultException(
throw new VaultLoginException(
String.format(
"Cannot retrieve Token from Cubbyhole: Response at %s does not contain a token",
options.getPath()));
@@ -274,7 +272,7 @@ public class CubbyholeAuthentication implements ClientAuthentication,
return VaultToken.of(token);
}
throw new VaultException(
throw new VaultLoginException(
String.format(
"Cannot retrieve Token from Cubbyhole: Response at %s does not contain an unique token",
options.getPath()));

View File

@@ -153,7 +153,7 @@ public class GcpComputeAuthentication extends GcpJwtAuthenticationSupport implem
return response.getBody();
}
catch (HttpStatusCodeException e) {
throw new VaultException("Cannot obtain signed identity", e);
throw new VaultLoginException("Cannot obtain signed identity", e);
}
}

View File

@@ -150,7 +150,7 @@ public class GcpIamAuthentication extends GcpJwtAuthenticationSupport implements
return response.getSignedJwt();
}
catch (IOException e) {
throw new VaultException("Cannot sign JWT", e);
throw new VaultLoginException("Cannot sign JWT", e);
}
}

View File

@@ -22,11 +22,9 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
import org.springframework.vault.VaultException;
import org.springframework.vault.client.VaultResponses;
import org.springframework.vault.support.VaultResponse;
import org.springframework.vault.support.VaultToken;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestOperations;
/**
@@ -89,10 +87,8 @@ public abstract class GcpJwtAuthenticationSupport {
return LoginTokenUtil.from(response.getAuth());
}
catch (HttpStatusCodeException e) {
throw new VaultException(String.format("Cannot login using %s: %s",
authenticationName,
VaultResponses.getError(e.getResponseBodyAsString())));
catch (RestClientException e) {
throw VaultLoginException.create(authenticationName, e);
}
}

View File

@@ -23,10 +23,9 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
import org.springframework.vault.VaultException;
import org.springframework.vault.client.VaultResponses;
import org.springframework.vault.support.VaultResponse;
import org.springframework.vault.support.VaultToken;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestOperations;
/**
@@ -104,9 +103,8 @@ public class KubernetesAuthentication implements ClientAuthentication,
return LoginTokenUtil.from(response.getAuth());
}
catch (HttpStatusCodeException e) {
throw new VaultException(String.format("Cannot login using kubernetes: %s",
VaultResponses.getError(e.getResponseBodyAsString())));
catch (RestClientException e) {
throw VaultLoginException.create("Kubernetes", e);
}
}

View File

@@ -29,6 +29,7 @@ import org.springframework.vault.client.VaultResponses;
import org.springframework.vault.support.VaultResponse;
import org.springframework.vault.support.VaultToken;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestOperations;
/**
@@ -106,6 +107,9 @@ public class LoginTokenAdapter implements ClientAuthentication {
"Token self-lookup failed: %s %s", e.getStatusCode(),
VaultResponses.getError(e.getResponseBodyAsString())));
}
catch (RestClientException e) {
throw new VaultTokenLookupException("Token self-lookup failed", e);
}
}
static Duration getLeaseDuration(@Nullable Number ttl) {

View File

@@ -15,9 +15,6 @@
*/
package org.springframework.vault.authentication;
import java.time.Duration;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.vault.authentication.AuthenticationSteps.HttpRequest;
import org.springframework.vault.client.VaultHttpHeaders;
@@ -100,8 +97,4 @@ public class TokenAuthentication implements ClientAuthentication,
public AuthenticationSteps getAuthenticationSteps() {
return createAuthenticationSteps(this.token, false);
}
private static Duration getLeaseDuration(@Nullable Number ttl) {
return ttl == null ? Duration.ZERO : Duration.ofSeconds(ttl.longValue());
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2018 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.authentication;
import org.springframework.vault.VaultException;
import org.springframework.vault.client.VaultResponses;
import org.springframework.web.client.RestClientResponseException;
/**
* Exception thrown if Vault login fails. The root cause is typically attached as cause.
*
* @author Mark Paluch
* @since 2.1
*/
public class VaultLoginException extends VaultException {
/**
* Create a {@code VaultLoginException} with the specified detail message.
*
* @param msg the detail message.
*/
public VaultLoginException(String msg) {
super(msg);
}
/**
* Create a {@code VaultLoginException} with the specified detail message and nested
* exception.
*
* @param msg the detail message.
* @param cause the nested exception.
*/
public VaultLoginException(String msg, Throwable cause) {
super(msg, cause);
}
/**
* Create a {@link VaultLoginException} given {@code authMethod} and a
* {@link Throwable cause}.
*
* @param authMethod must not be {@literal null}.
* @param cause must not be {@literal null}.
* @return the {@link VaultLoginException}.
*/
public static VaultLoginException create(String authMethod, Throwable cause) {
if (cause instanceof RestClientResponseException) {
String response = ((RestClientResponseException) cause)
.getResponseBodyAsString();
return new VaultLoginException(String.format("Cannot login using %s: %s",
authMethod, VaultResponses.getError(response)), cause);
}
return new VaultLoginException(String.format("Cannot login using %s", cause));
}
}

View File

@@ -26,11 +26,23 @@ import org.springframework.vault.VaultException;
public class VaultTokenLookupException extends VaultException {
/**
* Create a {@code VaultException} with the specified detail message.
* Create a {@code VaultTokenLookupException} with the specified detail message.
*
* @param msg the detail message.
*/
public VaultTokenLookupException(String msg) {
super(msg);
}
/**
* Create a {@code VaultTokenLookupException} with the specified detail message and
* nested exception.
*
* @param msg the detail message.
* @param cause the nested exception.
* @since 2.1
*/
public VaultTokenLookupException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -108,7 +108,7 @@ public class AuthenticationStepsExecutorUnitTests {
assertThatExceptionOfType(VaultException.class)
.isThrownBy(() -> login(steps))
.withMessage(
.withMessageContaining(
"HTTP request POST /auth/{path}/login AS class org.springframework.vault.support.VaultResponse "
+ "in state null failed with Status 400 and body foo");
}

View File

@@ -70,7 +70,7 @@ public class CubbyholeAuthenticationIntegrationTests extends
fail("Missing VaultException");
}
catch (VaultException e) {
assertThat(e).hasMessageContaining("Cannot retrieve Token from Cubbyhole")
assertThat(e).hasMessageContaining("Cannot login using Cubbyhole")
.hasMessageContaining("permission denied");
}
}