diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/AuthenticationEventPublisher.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/AuthenticationEventPublisher.java
new file mode 100644
index 00000000..06f7e920
--- /dev/null
+++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/AuthenticationEventPublisher.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2019 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
+ *
+ * https://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 java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+import org.springframework.util.Assert;
+import org.springframework.vault.authentication.event.AuthenticationErrorEvent;
+import org.springframework.vault.authentication.event.AuthenticationErrorListener;
+import org.springframework.vault.authentication.event.AuthenticationEvent;
+import org.springframework.vault.authentication.event.AuthenticationListener;
+
+/**
+ * Publisher for {@link AuthenticationEvent}s.
+ *
+ * This publisher dispatches events to {@link AuthenticationListener} and
+ * {@link AuthenticationErrorListener}.
+ *
+ * @author Mark Paluch
+ * @since 2.2
+ * @see AuthenticationEvent
+ * @see AuthenticationErrorEvent
+ * @see AuthenticationListener
+ * @see AuthenticationErrorListener
+ */
+public abstract class AuthenticationEventPublisher {
+
+ private final Set listeners = new CopyOnWriteArraySet<>();
+
+ private final Set errorListeners = new CopyOnWriteArraySet<>();
+
+ /**
+ * Add a {@link AuthenticationListener}. The listener starts receiving events as soon
+ * as possible.
+ *
+ * @param listener lease listener, must not be {@literal null}.
+ */
+ public void addAuthenticationListener(AuthenticationListener listener) {
+
+ Assert.notNull(listener, "AuthenticationEventListener must not be null");
+
+ this.listeners.add(listener);
+ }
+
+ /**
+ * Remove a {@link AuthenticationListener}.
+ *
+ * @param listener must not be {@literal null}.
+ */
+ public void removeAuthenticationListener(AuthenticationListener listener) {
+ this.listeners.remove(listener);
+ }
+
+ /**
+ * Add a {@link AuthenticationErrorListener}. The listener starts receiving events as
+ * soon as possible.
+ *
+ * @param listener lease listener, must not be {@literal null}.
+ */
+ public void addErrorListener(AuthenticationErrorListener listener) {
+
+ Assert.notNull(listener, "AuthenticationEventErrorListener must not be null");
+
+ this.errorListeners.add(listener);
+ }
+
+ /**
+ * Remove a {@link AuthenticationErrorListener}.
+ *
+ * @param listener must not be {@literal null}.
+ */
+ public void removeErrorListener(AuthenticationErrorListener listener) {
+ this.errorListeners.remove(listener);
+ }
+
+ /**
+ * Dispatch the event to all {@link AuthenticationListener}s.
+ *
+ * @param authenticationEvent the event to dispatch.
+ */
+ void dispatch(AuthenticationEvent authenticationEvent) {
+
+ for (AuthenticationListener listener : listeners) {
+ listener.onAuthenticationEvent(authenticationEvent);
+ }
+ }
+
+ /**
+ * Dispatch the event to all {@link AuthenticationErrorListener}s.
+ *
+ * @param authenticationEvent the event to dispatch.
+ */
+ void dispatch(AuthenticationErrorEvent authenticationEvent) {
+
+ for (AuthenticationErrorListener listener : errorListeners) {
+ listener.onAuthenticationError(authenticationEvent);
+ }
+ }
+}
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 ea7e9c51..c35e9408 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
@@ -27,6 +27,19 @@ import org.springframework.http.HttpEntity;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
+import org.springframework.vault.VaultException;
+import org.springframework.vault.authentication.event.AfterLoginEvent;
+import org.springframework.vault.authentication.event.AfterLoginTokenRenewedEvent;
+import org.springframework.vault.authentication.event.AfterLoginTokenRevocationEvent;
+import org.springframework.vault.authentication.event.AuthenticationErrorEvent;
+import org.springframework.vault.authentication.event.AuthenticationErrorListener;
+import org.springframework.vault.authentication.event.AuthenticationListener;
+import org.springframework.vault.authentication.event.BeforeLoginTokenRenewedEvent;
+import org.springframework.vault.authentication.event.BeforeLoginTokenRevocationEvent;
+import org.springframework.vault.authentication.event.LoginFailedEvent;
+import org.springframework.vault.authentication.event.LoginTokenExpiredEvent;
+import org.springframework.vault.authentication.event.LoginTokenRenewalFailedEvent;
+import org.springframework.vault.authentication.event.LoginTokenRevocationFailedEvent;
import org.springframework.vault.client.VaultHttpHeaders;
import org.springframework.vault.client.VaultResponses;
import org.springframework.vault.support.VaultResponse;
@@ -54,6 +67,10 @@ import org.springframework.web.client.RestOperations;
* By default, {@link VaultToken} are looked up in Vault to determine renewability and the
* remaining TTL, see {@link #setTokenSelfLookupEnabled(boolean)}.
*
+ * The session manager dispatches authentication events to {@link AuthenticationListener}
+ * and {@link AuthenticationErrorListener}. Event notifications are dispatched either on
+ * the calling {@link Thread} or worker threads used for background renewal.
+ *
* This class is thread-safe.
*
* @author Mark Paluch
@@ -61,6 +78,7 @@ import org.springframework.web.client.RestOperations;
* @see LoginToken
* @see SessionManager
* @see TaskScheduler
+ * @see AuthenticationEventPublisher
*/
public class LifecycleAwareSessionManager extends LifecycleAwareSessionManagerSupport
implements SessionManager, DisposableBean {
@@ -161,14 +179,14 @@ public class LifecycleAwareSessionManager extends LifecycleAwareSessionManagerSu
protected void revoke(VaultToken token) {
try {
+ dispatch(new BeforeLoginTokenRevocationEvent(token));
restOperations.postForObject("auth/token/revoke-self", new HttpEntity<>(
VaultHttpHeaders.from(token)), Map.class);
- }
- catch (HttpStatusCodeException e) {
- logger.warn(format("Cannot revoke VaultToken", e));
+ dispatch(new AfterLoginTokenRevocationEvent(token));
}
catch (RuntimeException e) {
logger.warn("Cannot revoke VaultToken: %s", e);
+ dispatch(new LoginTokenRevocationFailedEvent(token, e));
}
}
@@ -191,8 +209,9 @@ public class LifecycleAwareSessionManager extends LifecycleAwareSessionManagerSu
return false;
}
+ TokenWrapper tokenWrapper = token.get();
try {
- return doRenew(token.get());
+ return doRenew(tokenWrapper);
}
catch (HttpStatusCodeException e) {
@@ -201,7 +220,9 @@ public class LifecycleAwareSessionManager extends LifecycleAwareSessionManagerSu
String message = "Cannot renew token, resetting token and performing re-login";
if (e.getStatusCode().is4xxClientError()) {
+
logger.warn(format(message, e));
+ dispatch(new LoginTokenRenewalFailedEvent(tokenWrapper.getToken(), e));
return false;
}
@@ -222,6 +243,7 @@ public class LifecycleAwareSessionManager extends LifecycleAwareSessionManagerSu
private boolean doRenew(TokenWrapper wrapper) {
+ dispatch(new BeforeLoginTokenRenewedEvent(wrapper.getToken()));
VaultResponse vaultResponse = restOperations.postForObject(
"auth/token/renew-self",
new HttpEntity<>(VaultHttpHeaders.from(wrapper.token)),
@@ -229,10 +251,11 @@ public class LifecycleAwareSessionManager extends LifecycleAwareSessionManagerSu
LoginToken renewed = LoginTokenUtil.from(vaultResponse.getRequiredAuth());
- Duration validTtlThreshold = getRefreshTrigger().getValidTtlThreshold(renewed);
- if (renewed.getLeaseDuration().compareTo(validTtlThreshold) <= 0) {
+ if (isExpired(renewed)) {
if (logger.isDebugEnabled()) {
+ Duration validTtlThreshold = getRefreshTrigger().getValidTtlThreshold(
+ renewed);
logger.info(String
.format("Token TTL (%s) exceeded validity TTL threshold (%s). Dropping token.",
renewed.getLeaseDuration(), validTtlThreshold));
@@ -242,10 +265,12 @@ public class LifecycleAwareSessionManager extends LifecycleAwareSessionManagerSu
}
setToken(Optional.empty());
+ dispatch(new LoginTokenExpiredEvent(renewed));
return false;
}
setToken(Optional.of(new TokenWrapper(renewed, wrapper.revocable)));
+ dispatch(new AfterLoginTokenRenewedEvent(renewed));
return true;
}
@@ -269,7 +294,15 @@ public class LifecycleAwareSessionManager extends LifecycleAwareSessionManagerSu
private void doGetSessionToken() {
- VaultToken token = clientAuthentication.login();
+ VaultToken token;
+
+ try {
+ token = clientAuthentication.login();
+ }
+ catch (VaultException e) {
+ dispatch(new LoginFailedEvent(clientAuthentication, e));
+ throw e;
+ }
TokenWrapper wrapper = new TokenWrapper(token, token instanceof LoginToken);
@@ -283,10 +316,12 @@ public class LifecycleAwareSessionManager extends LifecycleAwareSessionManagerSu
catch (VaultTokenLookupException e) {
logger.warn(String.format(
"Cannot enhance VaultToken to a LoginToken: %s", e.getMessage()));
+ dispatch(new AuthenticationErrorEvent(token, e));
}
}
setToken(Optional.of(wrapper));
+ dispatch(new AfterLoginEvent(token));
if (isTokenRenewable()) {
scheduleRenewal();
@@ -318,8 +353,16 @@ public class LifecycleAwareSessionManager extends LifecycleAwareSessionManagerSu
logger.info("Scheduling Token renewal");
Runnable task = () -> {
+ Optional tokenWrapper = getToken();
+
+ if (!tokenWrapper.isPresent()) {
+ return;
+ }
+
+ VaultToken token = tokenWrapper.get().getToken();
+
try {
- if (getToken().isPresent() && isTokenRenewable()) {
+ if (isTokenRenewable()) {
if (renewToken()) {
scheduleRenewal();
}
@@ -327,6 +370,7 @@ public class LifecycleAwareSessionManager extends LifecycleAwareSessionManagerSu
}
catch (Exception e) {
logger.error("Cannot renew VaultToken", e);
+ dispatch(new LoginTokenRenewalFailedEvent(token, e));
}
};
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/LifecycleAwareSessionManagerSupport.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/LifecycleAwareSessionManagerSupport.java
index b6c2c3eb..8c375cc8 100644
--- a/spring-vault-core/src/main/java/org/springframework/vault/authentication/LifecycleAwareSessionManagerSupport.java
+++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/LifecycleAwareSessionManagerSupport.java
@@ -41,7 +41,8 @@ import org.springframework.vault.support.VaultToken;
* @author Mark Paluch
* @since 2.0
*/
-public abstract class LifecycleAwareSessionManagerSupport {
+public abstract class LifecycleAwareSessionManagerSupport extends
+ AuthenticationEventPublisher {
/**
* Refresh 5 seconds before the token expires.
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 127eef00..d782ac80 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
@@ -29,6 +29,18 @@ import org.springframework.scheduling.TaskScheduler;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.vault.VaultException;
+import org.springframework.vault.authentication.event.AfterLoginEvent;
+import org.springframework.vault.authentication.event.AfterLoginTokenRenewedEvent;
+import org.springframework.vault.authentication.event.AfterLoginTokenRevocationEvent;
+import org.springframework.vault.authentication.event.AuthenticationErrorEvent;
+import org.springframework.vault.authentication.event.AuthenticationErrorListener;
+import org.springframework.vault.authentication.event.AuthenticationListener;
+import org.springframework.vault.authentication.event.BeforeLoginTokenRenewedEvent;
+import org.springframework.vault.authentication.event.BeforeLoginTokenRevocationEvent;
+import org.springframework.vault.authentication.event.LoginFailedEvent;
+import org.springframework.vault.authentication.event.LoginTokenExpiredEvent;
+import org.springframework.vault.authentication.event.LoginTokenRenewalFailedEvent;
+import org.springframework.vault.authentication.event.LoginTokenRevocationFailedEvent;
import org.springframework.vault.client.VaultHttpHeaders;
import org.springframework.vault.client.VaultResponses;
import org.springframework.vault.support.VaultResponse;
@@ -58,6 +70,9 @@ import org.springframework.web.reactive.function.client.WebClientResponseExcepti
* By default, {@link VaultToken} are looked up in Vault to determine renewability and the
* remaining TTL, see {@link #setTokenSelfLookupEnabled(boolean)}.
*
+ * The session manager dispatches authentication events to {@link AuthenticationListener}
+ * and {@link AuthenticationErrorListener}.
+ *
* This class is thread-safe and uses lock-free synchronization.
*
* @author Mark Paluch
@@ -65,6 +80,7 @@ import org.springframework.web.reactive.function.client.WebClientResponseExcepti
* @see LoginToken
* @see ReactiveSessionManager
* @see TaskScheduler
+ * @see AuthenticationEventPublisher
*/
public class ReactiveLifecycleAwareSessionManager extends
LifecycleAwareSessionManagerSupport implements ReactiveSessionManager,
@@ -168,17 +184,27 @@ public class ReactiveLifecycleAwareSessionManager extends
*/
protected Mono revoke(VaultToken token) {
- return webClient.post().uri("auth/token/revoke-self").headers(httpHeaders -> {
- httpHeaders.addAll(VaultHttpHeaders.from(token));
- }).retrieve().bodyToMono(String.class).then()
+ return webClient
+ .post()
+ .uri("auth/token/revoke-self")
+ .headers(httpHeaders -> {
+ httpHeaders.addAll(VaultHttpHeaders.from(token));
+ })
+ .retrieve()
+ .bodyToMono(String.class)
+ .doOnSubscribe(
+ ignore -> dispatch(new BeforeLoginTokenRevocationEvent(token)))
+ .doOnNext(ignore -> dispatch(new AfterLoginTokenRevocationEvent(token)))
.onErrorResume(WebClientResponseException.class, e -> {
logger.warn(format("Could not revoke token", e));
+ dispatch(new LoginTokenRevocationFailedEvent(token, e));
return Mono.empty();
}).onErrorResume(Exception.class, e -> {
logger.warn("Could not revoke token", e);
+ dispatch(new LoginTokenRevocationFailedEvent(token, e));
return Mono.empty();
}).then();
@@ -198,8 +224,7 @@ public class ReactiveLifecycleAwareSessionManager extends
logger.info("Renewing token");
- Mono tokenWrapper = ReactiveLifecycleAwareSessionManager.this.token
- .get();
+ Mono tokenWrapper = this.token.get();
if (tokenWrapper == TERMINATED) {
return tokenWrapper.map(TokenWrapper::getToken);
@@ -209,8 +234,12 @@ public class ReactiveLifecycleAwareSessionManager extends
return getVaultToken();
}
- return tokenWrapper
- .flatMap(this::doRenew)
+ return tokenWrapper.flatMap(this::doRenewToken).map(TokenWrapper::getToken);
+ }
+
+ private Mono doRenewToken(TokenWrapper wrapper) {
+
+ return doRenew(wrapper)
.onErrorResume(
WebClientResponseException.class,
e -> {
@@ -222,6 +251,8 @@ public class ReactiveLifecycleAwareSessionManager extends
if (e.getStatusCode().is4xxClientError()) {
logger.warn(format(message, e));
+ dispatch(new LoginTokenRenewalFailedEvent(wrapper
+ .getToken(), e));
return EMPTY;
}
@@ -231,7 +262,7 @@ public class ReactiveLifecycleAwareSessionManager extends
"Cannot renew token", e), e));
})
.onErrorMap(
- it -> !VaultTokenRenewalException.class.isInstance(it),
+ it -> !(it instanceof VaultTokenRenewalException),
e -> {
dropCurrentToken();
@@ -240,7 +271,7 @@ public class ReactiveLifecycleAwareSessionManager extends
e.toString()));
return new VaultTokenRenewalException("Cannot renew token", e);
- }).map(TokenWrapper::getToken);
+ });
}
private Mono doRenew(TokenWrapper tokenWrapper) {
@@ -254,13 +285,17 @@ public class ReactiveLifecycleAwareSessionManager extends
.bodyToMono(VaultResponse.class);
return exchange
- .flatMap(response -> {
+ .doOnSubscribe(
+ ignore -> dispatch(new BeforeLoginTokenRenewedEvent(tokenWrapper
+ .getToken())))
+ .handle((response, sink) -> {
LoginToken renewed = LoginTokenUtil.from(response.getRequiredAuth());
if (!isExpired(renewed)) {
- return Mono
- .just(new TokenWrapper(renewed, tokenWrapper.revocable));
+ sink.next(new TokenWrapper(renewed, tokenWrapper.revocable));
+ dispatch(new AfterLoginTokenRenewedEvent(renewed));
+ return;
}
if (logger.isDebugEnabled()) {
@@ -276,8 +311,7 @@ public class ReactiveLifecycleAwareSessionManager extends
}
dropCurrentToken();
-
- return EMPTY;
+ dispatch(new LoginTokenExpiredEvent(renewed));
});
}
@@ -299,11 +333,16 @@ public class ReactiveLifecycleAwareSessionManager extends
Mono obtainToken = clientAuthentication.getVaultToken()
.flatMap(this::doSelfLookup) //
- .doOnNext(it -> {
+ .onErrorMap(it -> {
+ dispatch(new LoginFailedEvent(clientAuthentication, it));
+ return it;
+ }).doOnNext(it -> {
if (isTokenRenewable(it.getToken())) {
scheduleRenewal(it.getToken());
}
+
+ dispatch(new AfterLoginEvent(it.getToken()));
});
this.token.compareAndSet(tokenWrapper, obtainToken.cache());
@@ -327,7 +366,7 @@ public class ReactiveLifecycleAwareSessionManager extends
logger.warn(String.format(
"Cannot enhance VaultToken to a LoginToken: %s",
e.getMessage()));
-
+ dispatch(new AuthenticationErrorEvent(token, e));
return Mono.just(token);
}).map(it -> new TokenWrapper(it, false));
}
@@ -361,17 +400,21 @@ public class ReactiveLifecycleAwareSessionManager extends
Mono tokenWrapper = ReactiveLifecycleAwareSessionManager.this.token
.get();
- if (tokenWrapper == EMPTY || tokenWrapper == TERMINATED) {
+ if (tokenWrapper == Mono. empty()
+ || tokenWrapper == TERMINATED) {
return;
}
if (isTokenRenewable(token)) {
- renewToken().subscribe(this::scheduleRenewal,
- e -> logger.error("Cannot renew VaultToken", e));
+ renewToken().subscribe(this::scheduleRenewal, e -> {
+ logger.error("Cannot renew VaultToken", e);
+ dispatch(new LoginTokenRenewalFailedEvent(token, e));
+ });
}
}
catch (Exception e) {
logger.error("Cannot renew VaultToken", e);
+ dispatch(new LoginTokenRenewalFailedEvent(token, e));
}
};
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/AfterLoginEvent.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/AfterLoginEvent.java
new file mode 100644
index 00000000..a5b6b27e
--- /dev/null
+++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/AfterLoginEvent.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2019 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
+ *
+ * https://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.event;
+
+import org.springframework.context.ApplicationEvent;
+import org.springframework.vault.support.VaultToken;
+
+/**
+ * Event published after logging into Vault.
+ *
+ * @author Mark Paluch
+ * @since 2.2
+ * @see ApplicationEvent
+ */
+public class AfterLoginEvent extends AuthenticationEvent {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Create a new {@link AfterLoginEvent} given {@link VaultToken}.
+ *
+ * @param source the {@link VaultToken} associated with this event, must not be
+ * {@literal null}.
+ */
+ public AfterLoginEvent(VaultToken source) {
+ super(source);
+ }
+}
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/AfterLoginTokenRenewedEvent.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/AfterLoginTokenRenewedEvent.java
new file mode 100644
index 00000000..3a001a10
--- /dev/null
+++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/AfterLoginTokenRenewedEvent.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2019 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
+ *
+ * https://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.event;
+
+import org.springframework.context.ApplicationEvent;
+import org.springframework.vault.support.VaultToken;
+
+/**
+ * Event published after renewing a {@link VaultToken login token}.
+ *
+ * @author Mark Paluch
+ * @since 2.2
+ * @see ApplicationEvent
+ */
+public class AfterLoginTokenRenewedEvent extends AuthenticationEvent {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Create a new {@link AfterLoginTokenRenewedEvent} given {@link VaultToken}.
+ *
+ * @param source the {@link VaultToken} associated with this event, must not be
+ * {@literal null}.
+ */
+ public AfterLoginTokenRenewedEvent(VaultToken source) {
+ super(source);
+ }
+}
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/AfterLoginTokenRevocationEvent.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/AfterLoginTokenRevocationEvent.java
new file mode 100644
index 00000000..12fa526d
--- /dev/null
+++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/AfterLoginTokenRevocationEvent.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2019 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
+ *
+ * https://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.event;
+
+import org.springframework.context.ApplicationEvent;
+import org.springframework.vault.support.VaultToken;
+
+/**
+ * Event published after revoking a {@link VaultToken login token}.
+ *
+ * @author Mark Paluch
+ * @since 2.2
+ * @see ApplicationEvent
+ */
+public class AfterLoginTokenRevocationEvent extends AuthenticationEvent {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Create a new {@link AfterLoginTokenRevocationEvent} given {@link VaultToken}.
+ *
+ * @param source the {@link VaultToken} associated with this event, must not be
+ * {@literal null}.
+ */
+ public AfterLoginTokenRevocationEvent(VaultToken source) {
+ super(source);
+ }
+}
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/AuthenticationErrorEvent.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/AuthenticationErrorEvent.java
new file mode 100644
index 00000000..6a01d1a9
--- /dev/null
+++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/AuthenticationErrorEvent.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2019 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
+ *
+ * https://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.event;
+
+import org.springframework.context.ApplicationEvent;
+
+/**
+ * Generic event class for authentication error events. These can be generic failures or
+ * specific ones such as renewal or login errors.
+ *
+ * @author Mark Paluch
+ * @since 2.2
+ * @see LoginFailedEvent
+ * @see LoginTokenRenewalFailedEvent
+ * @see LoginTokenRevocationFailedEvent
+ * @see AuthenticationErrorListener
+ */
+public class AuthenticationErrorEvent extends ApplicationEvent {
+
+ private static final long serialVersionUID = 1L;
+
+ private final Throwable exception;
+
+ /**
+ * Create a new {@link AuthenticationErrorEvent} given {@code source} and
+ * {@link Exception}.
+ *
+ * @param source must not be {@literal null}.
+ * @param exception must not be {@literal null}.
+ */
+ public AuthenticationErrorEvent(Object source, Throwable exception) {
+ super(source);
+ this.exception = exception;
+ }
+
+ public Throwable getException() {
+ return exception;
+ }
+}
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/AuthenticationErrorListener.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/AuthenticationErrorListener.java
new file mode 100644
index 00000000..2ed98819
--- /dev/null
+++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/AuthenticationErrorListener.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2019 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
+ *
+ * https://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.event;
+
+/**
+ * Listener for Vault exceptional {@link AuthenticationEvent}s.
+ *
+ * Error events can occur during login, login token renewal and login token revocation.
+ *
+ * @author Mark Paluch
+ * @since 2.2
+ */
+@FunctionalInterface
+public interface AuthenticationErrorListener {
+
+ /**
+ * Callback for a {@link AuthenticationErrorEvent}.
+ *
+ * @param authenticationEvent the event object, must not be {@literal null}.
+ */
+ void onAuthenticationError(AuthenticationErrorEvent authenticationEvent);
+}
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/AuthenticationEvent.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/AuthenticationEvent.java
new file mode 100644
index 00000000..5c72e0a2
--- /dev/null
+++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/AuthenticationEvent.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2019 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
+ *
+ * https://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.event;
+
+import org.springframework.context.ApplicationEvent;
+import org.springframework.vault.support.VaultToken;
+
+/**
+ * Abstract base class for authentication events.
+ *
+ * @author Mark Paluch
+ * @since 2.2
+ * @see ApplicationEvent
+ */
+public abstract class AuthenticationEvent extends ApplicationEvent {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Create a new {@link AuthenticationEvent} given {@link VaultToken}.
+ *
+ * @param source the {@link VaultToken} associated with this event, must not be
+ * {@literal null}.
+ */
+ protected AuthenticationEvent(VaultToken source) {
+ super(source);
+ }
+
+ @Override
+ public VaultToken getSource() {
+ return (VaultToken) super.getSource();
+ }
+}
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/AuthenticationListener.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/AuthenticationListener.java
new file mode 100644
index 00000000..4121c27d
--- /dev/null
+++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/AuthenticationListener.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2019 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
+ *
+ * https://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.event;
+
+/**
+ * Listener for Vault {@link AuthenticationEvent}s.
+ *
+ * @author Mark Paluch
+ * @since 2.2
+ * @see AuthenticationEvent
+ */
+@FunctionalInterface
+public interface AuthenticationListener {
+
+ /**
+ * Callback for a {@link AuthenticationEvent}
+ *
+ * @param leaseEvent the event object, must not be {@literal null}.
+ */
+ void onAuthenticationEvent(AuthenticationEvent leaseEvent);
+}
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/BeforeLoginTokenRenewedEvent.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/BeforeLoginTokenRenewedEvent.java
new file mode 100644
index 00000000..4396dba9
--- /dev/null
+++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/BeforeLoginTokenRenewedEvent.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2019 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
+ *
+ * https://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.event;
+
+import org.springframework.context.ApplicationEvent;
+import org.springframework.vault.support.VaultToken;
+
+/**
+ * Event published before renewing a {@link VaultToken login token}.
+ *
+ * @author Mark Paluch
+ * @since 2.2
+ * @see ApplicationEvent
+ */
+public class BeforeLoginTokenRenewedEvent extends AuthenticationEvent {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Create a new {@link BeforeLoginTokenRenewedEvent} given {@link VaultToken}.
+ *
+ * @param source the {@link VaultToken} associated with this event, must not be
+ * {@literal null}.
+ */
+ public BeforeLoginTokenRenewedEvent(VaultToken source) {
+ super(source);
+ }
+}
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/BeforeLoginTokenRevocationEvent.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/BeforeLoginTokenRevocationEvent.java
new file mode 100644
index 00000000..2a47454a
--- /dev/null
+++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/BeforeLoginTokenRevocationEvent.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2019 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
+ *
+ * https://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.event;
+
+import org.springframework.context.ApplicationEvent;
+import org.springframework.vault.support.VaultToken;
+
+/**
+ * Event published before revoking a {@link VaultToken login token}.
+ *
+ * @author Mark Paluch
+ * @since 2.2
+ * @see ApplicationEvent
+ */
+public class BeforeLoginTokenRevocationEvent extends AuthenticationEvent {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Create a new {@link BeforeLoginTokenRevocationEvent} given {@link VaultToken}.
+ *
+ * @param source the {@link VaultToken} associated with this event, must not be
+ * {@literal null}.
+ */
+ public BeforeLoginTokenRevocationEvent(VaultToken source) {
+ super(source);
+ }
+}
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/LoginFailedEvent.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/LoginFailedEvent.java
new file mode 100644
index 00000000..08235dad
--- /dev/null
+++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/LoginFailedEvent.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2019 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
+ *
+ * https://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.event;
+
+import org.springframework.context.ApplicationEvent;
+import org.springframework.vault.authentication.ClientAuthentication;
+import org.springframework.vault.authentication.VaultTokenSupplier;
+import org.springframework.vault.support.VaultToken;
+
+/**
+ * Event published before renewing a {@link VaultToken login token}.
+ *
+ * Provides {@link ClientAuthentication} or {@link VaultTokenSupplier} as
+ * {@link #getSource() source}.
+ *
+ * @author Mark Paluch
+ * @since 2.2
+ * @see ApplicationEvent
+ */
+public class LoginFailedEvent extends AuthenticationErrorEvent {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Create a new {@link LoginFailedEvent} given {@link Exception}.
+ *
+ * @param source the {@link ClientAuthentication} or {@link VaultTokenSupplier}
+ * associated with this event, must not be {@literal null}.
+ * @param exception must not be {@literal null}.
+ */
+ public LoginFailedEvent(Object source, Throwable exception) {
+ super(source, exception);
+ }
+}
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/LoginTokenExpiredEvent.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/LoginTokenExpiredEvent.java
new file mode 100644
index 00000000..4b0bfc20
--- /dev/null
+++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/LoginTokenExpiredEvent.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2019 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
+ *
+ * https://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.event;
+
+import org.springframework.context.ApplicationEvent;
+import org.springframework.vault.support.VaultToken;
+
+/**
+ * Event published when dropping an expired {@link VaultToken login token}.
+ *
+ * @author Mark Paluch
+ * @since 2.2
+ * @see ApplicationEvent
+ */
+public class LoginTokenExpiredEvent extends AuthenticationEvent {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Create a new {@link LoginTokenExpiredEvent} given {@link VaultToken}.
+ *
+ * @param source the {@link VaultToken} associated with this event, must not be
+ * {@literal null}.
+ */
+ public LoginTokenExpiredEvent(VaultToken source) {
+ super(source);
+ }
+}
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/LoginTokenRenewalFailedEvent.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/LoginTokenRenewalFailedEvent.java
new file mode 100644
index 00000000..46bdf6f7
--- /dev/null
+++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/LoginTokenRenewalFailedEvent.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2019 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
+ *
+ * https://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.event;
+
+import org.springframework.context.ApplicationEvent;
+import org.springframework.vault.support.VaultToken;
+
+/**
+ * Generic event class for authentication error events.
+ *
+ * @author Mark Paluch
+ * @since 2.2
+ * @see ApplicationEvent
+ */
+public class LoginTokenRenewalFailedEvent extends AuthenticationErrorEvent {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Create a new {@link LoginTokenRenewalFailedEvent} given {@link VaultToken} and
+ * {@link Exception}.
+ *
+ * @param source the {@link VaultToken} associated with this event, must not be
+ * {@literal null}.
+ * @param exception must not be {@literal null}.
+ */
+ public LoginTokenRenewalFailedEvent(VaultToken source, Throwable exception) {
+ super(source, exception);
+ }
+
+ public VaultToken getSource() {
+ return (VaultToken) super.getSource();
+ }
+}
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/LoginTokenRevocationFailedEvent.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/LoginTokenRevocationFailedEvent.java
new file mode 100644
index 00000000..282769a4
--- /dev/null
+++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/LoginTokenRevocationFailedEvent.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2019 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
+ *
+ * https://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.event;
+
+import org.springframework.context.ApplicationEvent;
+import org.springframework.vault.support.VaultToken;
+
+/**
+ * Generic event class for authentication error events.
+ *
+ * @author Mark Paluch
+ * @since 2.2
+ * @see ApplicationEvent
+ */
+public class LoginTokenRevocationFailedEvent extends AuthenticationErrorEvent {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Create a new {@link LoginTokenRevocationFailedEvent} given {@link VaultToken} and
+ * {@link Exception}.
+ *
+ * @param source the {@link VaultToken} associated with this event, must not be
+ * {@literal null}.
+ * @param exception must not be {@literal null}.
+ */
+ public LoginTokenRevocationFailedEvent(VaultToken source, Throwable exception) {
+ super(source, exception);
+ }
+
+ public VaultToken getSource() {
+ return (VaultToken) super.getSource();
+ }
+}
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/package-info.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/package-info.java
new file mode 100644
index 00000000..f343d03e
--- /dev/null
+++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/event/package-info.java
@@ -0,0 +1,7 @@
+/**
+ * Support classes for authentication application events.
+ */
+@org.springframework.lang.NonNullApi
+@org.springframework.lang.NonNullFields
+package org.springframework.vault.authentication.event;
+
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 f0eb7afe..400f97e3 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
@@ -25,6 +25,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@@ -34,6 +35,17 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.Trigger;
+import org.springframework.vault.authentication.event.AfterLoginEvent;
+import org.springframework.vault.authentication.event.AfterLoginTokenRenewedEvent;
+import org.springframework.vault.authentication.event.AfterLoginTokenRevocationEvent;
+import org.springframework.vault.authentication.event.AuthenticationErrorListener;
+import org.springframework.vault.authentication.event.AuthenticationEvent;
+import org.springframework.vault.authentication.event.AuthenticationListener;
+import org.springframework.vault.authentication.event.BeforeLoginTokenRenewedEvent;
+import org.springframework.vault.authentication.event.BeforeLoginTokenRevocationEvent;
+import org.springframework.vault.authentication.event.LoginFailedEvent;
+import org.springframework.vault.authentication.event.LoginTokenExpiredEvent;
+import org.springframework.vault.authentication.event.LoginTokenRevocationFailedEvent;
import org.springframework.vault.client.VaultHttpHeaders;
import org.springframework.vault.support.VaultResponse;
import org.springframework.vault.support.VaultToken;
@@ -49,6 +61,7 @@ import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
@@ -69,12 +82,23 @@ public class LifecycleAwareSessionManagerUnitTests {
@Mock
private RestOperations restOperations;
+ @Mock
+ private AuthenticationListener listener;
+
+ @Mock
+ private AuthenticationErrorListener errorListener;
+
+ @Captor
+ private ArgumentCaptor captor;
+
private LifecycleAwareSessionManager sessionManager;
@Before
public void before() {
sessionManager = new LifecycleAwareSessionManager(clientAuthentication,
taskScheduler, restOperations);
+ sessionManager.addAuthenticationListener(listener);
+ sessionManager.addErrorListener(errorListener);
}
@Test
@@ -83,6 +107,18 @@ public class LifecycleAwareSessionManagerUnitTests {
when(clientAuthentication.login()).thenReturn(LoginToken.of("login"));
assertThat(sessionManager.getSessionToken()).isEqualTo(LoginToken.of("login"));
+ verify(listener).onAuthenticationEvent(any(AfterLoginEvent.class));
+ }
+
+ @Test
+ public void loginShouldFail() {
+
+ when(clientAuthentication.login()).thenThrow(new VaultLoginException("foo"));
+
+ assertThatThrownBy(() -> sessionManager.getSessionToken()).isInstanceOf(
+ VaultLoginException.class);
+ verifyZeroInteractions(listener);
+ verify(errorListener).onAuthenticationError(any(LoginFailedEvent.class));
}
@Test
@@ -105,6 +141,10 @@ public class LifecycleAwareSessionManagerUnitTests {
verify(restOperations).exchange(eq("auth/token/lookup-self"), eq(HttpMethod.GET),
eq(new HttpEntity<>(VaultHttpHeaders.from(LoginToken.of("login")))),
any(Class.class));
+
+ verify(listener).onAuthenticationEvent(captor.capture());
+ AfterLoginEvent event = (AfterLoginEvent) captor.getValue();
+ assertThat(event.getSource()).isSameAs(sessionToken);
}
@Test
@@ -123,6 +163,8 @@ public class LifecycleAwareSessionManagerUnitTests {
VaultToken sessionToken = sessionManager.getSessionToken();
assertThat(sessionToken).isExactlyInstanceOf(VaultToken.class);
+ verify(listener).onAuthenticationEvent(any(AfterLoginEvent.class));
+ verify(errorListener).onAuthenticationError(any());
}
@Test
@@ -150,11 +192,13 @@ public class LifecycleAwareSessionManagerUnitTests {
sessionManager.renewToken();
sessionManager.destroy();
- verify(restOperations)
- .postForObject(
- eq("auth/token/revoke-self"),
- eq(new HttpEntity