From 60d9534c9ccfc0c80f1f77af24ce42ee7d3fa5d8 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 20 Mar 2018 11:45:17 +0100 Subject: [PATCH] Drop VaultToken on infrastructure errors during renewal. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We now drop the VaultToken if an infrastructure error (connection refused/connection timeout/read timeout/…) occurs. Dropping the token prevents usage of an expired token. Closes gh-222. --- .../LifecycleAwareSessionManager.java | 23 +++++++++------ .../ReactiveLifecycleAwareSessionManager.java | 27 +++++++++++++----- ...LifecycleAwareSessionManagerUnitTests.java | 21 ++++++++++++++ ...LifecycleAwareSessionManagerUnitTests.java | 28 +++++++++++++++++++ 4 files changed, 84 insertions(+), 15 deletions(-) diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/LifecycleAwareSessionManager.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/LifecycleAwareSessionManager.java index 70a6bedf..78f8b2be 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/authentication/LifecycleAwareSessionManager.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/LifecycleAwareSessionManager.java @@ -33,7 +33,6 @@ 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; /** @@ -213,18 +212,26 @@ public class LifecycleAwareSessionManager extends LifecycleAwareSessionManagerSu } catch (HttpStatusCodeException e) { + logger.debug(String.format( + "Cannot renew token, resetting token and performing re-login: %s", + VaultResponses.getError(e.getResponseBodyAsString()))); + this.token = Optional.empty(); + if (e.getStatusCode().is4xxClientError()) { - logger.debug(String - .format("Cannot refresh token, resetting token and performing re-login: %s", - VaultResponses.getError(e.getResponseBodyAsString()))); - this.token = Optional.empty(); return false; } - throw new VaultException(VaultResponses.getError(e.getResponseBodyAsString())); + throw new VaultException(String.format("Cannot renew token: %s", + VaultResponses.getError(e.getResponseBodyAsString()))); } - catch (RestClientException e) { - throw new VaultException("Cannot refresh token", e); + catch (RuntimeException e) { + + logger.debug(String.format( + "Cannot renew token, resetting token and performing re-login: %s", + e.toString())); + this.token = Optional.empty(); + + throw new VaultException("Cannot renew token", e); } } diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/ReactiveLifecycleAwareSessionManager.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/ReactiveLifecycleAwareSessionManager.java index 4f63d7eb..2135c7dd 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/authentication/ReactiveLifecycleAwareSessionManager.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/ReactiveLifecycleAwareSessionManager.java @@ -211,23 +211,36 @@ public class ReactiveLifecycleAwareSessionManager extends WebClientResponseException.class, e -> { + dropCurrentToken(); + if (e.getStatusCode().is4xxClientError()) { logger.debug(String - .format("Cannot refresh token, resetting token and performing re-login: %s", + .format("Cannot renew token, resetting token and performing re-login on next token access: %s", VaultResponses.getError(e .getResponseBodyAsString()))); - dropCurrentToken(); return EMPTY; } - return Mono.error(new VaultException(VaultResponses - .getError(e.getResponseBodyAsString()))); + logger.debug(String + .format("Cannot renew token, resetting token and performing re-login on next token access: %s", + e.toString())); + + return Mono.error(new VaultException(String.format( + "Cannot renew token: %s", + VaultResponses.getError(e.getResponseBodyAsString())))); }) - .onErrorMap(WebClientException.class, - e -> new VaultException("Cannot refresh token", e)) - .map(TokenWrapper::getToken); + .onErrorMap( + e -> { + + dropCurrentToken(); + logger.debug(String + .format("Cannot renew token, resetting token and performing re-login on next token access: %s", + e.toString())); + + return new VaultException("Cannot renew token", e); + }).map(TokenWrapper::getToken); } private Mono doRenew(TokenWrapper tokenWrapper) { diff --git a/spring-vault-core/src/test/java/org/springframework/vault/authentication/LifecycleAwareSessionManagerUnitTests.java b/spring-vault-core/src/test/java/org/springframework/vault/authentication/LifecycleAwareSessionManagerUnitTests.java index f89ce722..746b14cc 100644 --- a/spring-vault-core/src/test/java/org/springframework/vault/authentication/LifecycleAwareSessionManagerUnitTests.java +++ b/spring-vault-core/src/test/java/org/springframework/vault/authentication/LifecycleAwareSessionManagerUnitTests.java @@ -39,6 +39,7 @@ import org.springframework.vault.support.VaultResponse; import org.springframework.vault.support.VaultToken; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.HttpServerErrorException; +import org.springframework.web.client.ResourceAccessException; import org.springframework.web.client.RestOperations; import static org.assertj.core.api.Assertions.assertThat; @@ -263,6 +264,26 @@ public class LifecycleAwareSessionManagerUnitTests { verify(clientAuthentication, times(2)).login(); } + @Test + public void shouldReLoginIfRenewalFails() { + + when(clientAuthentication.login()).thenReturn( + LoginToken.renewable("login".toCharArray(), Duration.ofSeconds(5)), + LoginToken.renewable("bar".toCharArray(), Duration.ofSeconds(5))); + when(restOperations.postForObject(anyString(), any(), eq(VaultResponse.class))) + .thenThrow(new ResourceAccessException("Connection refused")); + + ArgumentCaptor runnableCaptor = ArgumentCaptor.forClass(Runnable.class); + sessionManager.getSessionToken(); + verify(taskScheduler).schedule(runnableCaptor.capture(), any(Trigger.class)); + runnableCaptor.getValue().run(); + + assertThat(sessionManager.getSessionToken()).isEqualTo( + LoginToken.renewable("bar".toCharArray(), Duration.ofSeconds(5))); + + verify(clientAuthentication, times(2)).login(); + } + @Test public void shouldUseTaskScheduler() { diff --git a/spring-vault-core/src/test/java/org/springframework/vault/authentication/ReactiveLifecycleAwareSessionManagerUnitTests.java b/spring-vault-core/src/test/java/org/springframework/vault/authentication/ReactiveLifecycleAwareSessionManagerUnitTests.java index 0117e542..4864c503 100644 --- a/spring-vault-core/src/test/java/org/springframework/vault/authentication/ReactiveLifecycleAwareSessionManagerUnitTests.java +++ b/spring-vault-core/src/test/java/org/springframework/vault/authentication/ReactiveLifecycleAwareSessionManagerUnitTests.java @@ -291,6 +291,34 @@ public class ReactiveLifecycleAwareSessionManagerUnitTests { verify(tokenSupplier, times(2)).getVaultToken(); } + @Test + public void shouldReLoginIfRenewFails() { + + when(tokenSupplier.getVaultToken()) + .thenReturn( + Mono.just(LoginToken.renewable("login".toCharArray(), + Duration.ofSeconds(5))), + Mono.just(LoginToken.renewable("bar".toCharArray(), + Duration.ofSeconds(5)))); + when(responseSpec.bodyToMono(VaultResponse.class)).thenReturn( + Mono.error(new RuntimeException("foo"))); + + ArgumentCaptor runnableCaptor = ArgumentCaptor.forClass(Runnable.class); + sessionManager.getSessionToken().as(StepVerifier::create).expectNextCount(1) + .verifyComplete(); + verify(taskScheduler).schedule(runnableCaptor.capture(), any(Trigger.class)); + runnableCaptor.getValue().run(); + + sessionManager + .getSessionToken() + .as(StepVerifier::create) + .expectNext( + LoginToken.renewable("bar".toCharArray(), Duration.ofSeconds(5))) + .verifyComplete(); + + verify(tokenSupplier, times(2)).getVaultToken(); + } + private static VaultResponse fromToken(LoginToken loginToken) { Map auth = new HashMap<>();