Add support for authentication events.
LifecycleAwareSessionManager and ReactiveLifecycleAwareSessionManager now emit AuthenticationEvents for token renewal. Listeners can be registered through the common base class AuthenticationEventPublisher. Closes gh-431.
This commit is contained in:
@@ -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.
|
||||
* <p>
|
||||
* 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<AuthenticationListener> listeners = new CopyOnWriteArraySet<>();
|
||||
|
||||
private final Set<AuthenticationErrorListener> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)}.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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> 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));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)}.
|
||||
* <p>
|
||||
* The session manager dispatches authentication events to {@link AuthenticationListener}
|
||||
* and {@link AuthenticationErrorListener}.
|
||||
* <p>
|
||||
* 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<Void> 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> tokenWrapper = ReactiveLifecycleAwareSessionManager.this.token
|
||||
.get();
|
||||
Mono<TokenWrapper> 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<TokenWrapper> 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<TokenWrapper> 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<TokenWrapper> 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> tokenWrapper = ReactiveLifecycleAwareSessionManager.this.token
|
||||
.get();
|
||||
|
||||
if (tokenWrapper == EMPTY || tokenWrapper == TERMINATED) {
|
||||
if (tokenWrapper == Mono.<TokenWrapper> 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));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
* <p>
|
||||
* 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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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}.
|
||||
* <p>
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Support classes for authentication application events.
|
||||
*/
|
||||
@org.springframework.lang.NonNullApi
|
||||
@org.springframework.lang.NonNullFields
|
||||
package org.springframework.vault.authentication.event;
|
||||
|
||||
@@ -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<AuthenticationEvent> 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<Object>(VaultHttpHeaders.from(LoginToken
|
||||
.of("login")))), any(Class.class));
|
||||
verify(restOperations).postForObject(eq("auth/token/revoke-self"),
|
||||
eq(new HttpEntity<>(VaultHttpHeaders.from(LoginToken.of("login")))),
|
||||
any(Class.class));
|
||||
|
||||
verify(listener)
|
||||
.onAuthenticationEvent(any(BeforeLoginTokenRevocationEvent.class));
|
||||
verify(listener).onAuthenticationEvent(any(AfterLoginTokenRevocationEvent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -167,6 +211,8 @@ public class LifecycleAwareSessionManagerUnitTests {
|
||||
sessionManager.destroy();
|
||||
|
||||
verifyZeroInteractions(restOperations);
|
||||
verify(listener).onAuthenticationEvent(any(AfterLoginEvent.class));
|
||||
verifyNoMoreInteractions(listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -182,11 +228,15 @@ public class LifecycleAwareSessionManagerUnitTests {
|
||||
sessionManager.renewToken();
|
||||
sessionManager.destroy();
|
||||
|
||||
verify(restOperations)
|
||||
.postForObject(
|
||||
eq("auth/token/revoke-self"),
|
||||
eq(new HttpEntity<Object>(VaultHttpHeaders.from(LoginToken
|
||||
.of("login")))), any(Class.class));
|
||||
verify(restOperations).postForObject(eq("auth/token/revoke-self"),
|
||||
eq(new HttpEntity<>(VaultHttpHeaders.from(LoginToken.of("login")))),
|
||||
any(Class.class));
|
||||
verify(listener).onAuthenticationEvent(any(AfterLoginEvent.class));
|
||||
verify(listener)
|
||||
.onAuthenticationEvent(any(BeforeLoginTokenRevocationEvent.class));
|
||||
verifyNoMoreInteractions(listener);
|
||||
verify(errorListener).onAuthenticationError(
|
||||
any(LoginTokenRevocationFailedEvent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -205,6 +255,10 @@ public class LifecycleAwareSessionManagerUnitTests {
|
||||
|
||||
when(clientAuthentication.login()).thenReturn(
|
||||
LoginToken.renewable("login".toCharArray(), Duration.ofSeconds(5)));
|
||||
when(restOperations.postForObject(anyString(), any(), eq(VaultResponse.class)))
|
||||
.thenReturn(
|
||||
fromToken(LoginToken.of("foo".toCharArray(),
|
||||
Duration.ofSeconds(10))));
|
||||
|
||||
ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
|
||||
|
||||
@@ -218,6 +272,8 @@ public class LifecycleAwareSessionManagerUnitTests {
|
||||
eq(new HttpEntity<Object>(VaultHttpHeaders.from(LoginToken.renewable(
|
||||
"login", 5)))), any(Class.class));
|
||||
verify(clientAuthentication, times(1)).login();
|
||||
verify(listener).onAuthenticationEvent(any(BeforeLoginTokenRenewedEvent.class));
|
||||
verify(listener).onAuthenticationEvent(any(AfterLoginTokenRenewedEvent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -280,6 +336,8 @@ public class LifecycleAwareSessionManagerUnitTests {
|
||||
LoginToken.renewable("bar".toCharArray(), Duration.ofSeconds(5)));
|
||||
|
||||
verify(clientAuthentication, times(2)).login();
|
||||
verify(listener, times(2)).onAuthenticationEvent(any(AfterLoginEvent.class));
|
||||
verify(listener).onAuthenticationEvent(any(LoginTokenExpiredEvent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import reactor.core.publisher.Mono;
|
||||
@@ -31,6 +32,16 @@ import reactor.test.StepVerifier;
|
||||
|
||||
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.support.VaultResponse;
|
||||
import org.springframework.vault.support.VaultToken;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
@@ -47,6 +58,8 @@ import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.never;
|
||||
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;
|
||||
|
||||
/**
|
||||
@@ -81,6 +94,15 @@ public class ReactiveLifecycleAwareSessionManagerUnitTests {
|
||||
@Mock
|
||||
ResponseSpec responseSpec;
|
||||
|
||||
@Mock
|
||||
private AuthenticationListener listener;
|
||||
|
||||
@Mock
|
||||
private AuthenticationErrorListener errorListener;
|
||||
|
||||
@Captor
|
||||
private ArgumentCaptor<AuthenticationEvent> captor;
|
||||
|
||||
private ReactiveLifecycleAwareSessionManager sessionManager;
|
||||
|
||||
@Before
|
||||
@@ -100,6 +122,8 @@ public class ReactiveLifecycleAwareSessionManagerUnitTests {
|
||||
|
||||
sessionManager = new ReactiveLifecycleAwareSessionManager(tokenSupplier,
|
||||
taskScheduler, webClient);
|
||||
sessionManager.addAuthenticationListener(listener);
|
||||
sessionManager.addErrorListener(errorListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -109,6 +133,18 @@ public class ReactiveLifecycleAwareSessionManagerUnitTests {
|
||||
|
||||
sessionManager.getSessionToken().as(StepVerifier::create)
|
||||
.expectNext(LoginToken.of("login")).verifyComplete();
|
||||
verify(listener).onAuthenticationEvent(any(AfterLoginEvent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginShouldFail() {
|
||||
|
||||
when(tokenSupplier.getVaultToken()).thenReturn(
|
||||
Mono.error(new VaultLoginException("foo")));
|
||||
|
||||
sessionManager.getSessionToken().as(StepVerifier::create).verifyError();
|
||||
verifyZeroInteractions(listener);
|
||||
verify(errorListener).onAuthenticationError(any(LoginFailedEvent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -134,6 +170,9 @@ public class ReactiveLifecycleAwareSessionManagerUnitTests {
|
||||
}).verifyComplete();
|
||||
|
||||
verify(webClient.get()).uri("auth/token/lookup-self");
|
||||
verify(listener).onAuthenticationEvent(captor.capture());
|
||||
AfterLoginEvent event = (AfterLoginEvent) captor.getValue();
|
||||
assertThat(event.getSource()).isInstanceOf(LoginToken.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -152,6 +191,8 @@ public class ReactiveLifecycleAwareSessionManagerUnitTests {
|
||||
sessionManager.getSessionToken().as(StepVerifier::create).assertNext(it -> {
|
||||
assertThat(it).isExactlyInstanceOf(VaultToken.class);
|
||||
}).verifyComplete();
|
||||
verify(listener).onAuthenticationEvent(any(AfterLoginEvent.class));
|
||||
verify(errorListener).onAuthenticationError(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -181,6 +222,26 @@ public class ReactiveLifecycleAwareSessionManagerUnitTests {
|
||||
@Test
|
||||
public void shouldRevokeLoginTokenOnDestroy() {
|
||||
|
||||
VaultResponse vaultResponse = new VaultResponse();
|
||||
vaultResponse.setData(Collections.singletonMap("ttl", 100));
|
||||
|
||||
mockToken(LoginToken.of("login"));
|
||||
when(responseSpec.bodyToMono(String.class)).thenReturn(Mono.just("OK"));
|
||||
|
||||
sessionManager.getVaultToken().as(StepVerifier::create).expectNextCount(1)
|
||||
.verifyComplete();
|
||||
|
||||
sessionManager.destroy();
|
||||
|
||||
verify(webClient.post()).uri("auth/token/revoke-self");
|
||||
verify(listener)
|
||||
.onAuthenticationEvent(any(BeforeLoginTokenRevocationEvent.class));
|
||||
verify(listener).onAuthenticationEvent(any(AfterLoginTokenRevocationEvent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotRevokeRegularTokenOnDestroy() {
|
||||
|
||||
mockToken(VaultToken.of("login"));
|
||||
|
||||
sessionManager.setTokenSelfLookupEnabled(false);
|
||||
@@ -190,6 +251,8 @@ public class ReactiveLifecycleAwareSessionManagerUnitTests {
|
||||
|
||||
verify(webClient, never()).post();
|
||||
verify(webClient.post(), never()).uri("auth/token/revoke-self");
|
||||
verify(listener).onAuthenticationEvent(any(AfterLoginEvent.class));
|
||||
verifyNoMoreInteractions(listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -245,6 +308,8 @@ public class ReactiveLifecycleAwareSessionManagerUnitTests {
|
||||
verify(webClient.post()).uri("auth/token/renew-self");
|
||||
|
||||
verify(tokenSupplier, times(1)).getVaultToken();
|
||||
verify(listener).onAuthenticationEvent(any(BeforeLoginTokenRenewedEvent.class));
|
||||
verify(listener).onAuthenticationEvent(any(AfterLoginTokenRenewedEvent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -313,6 +378,8 @@ public class ReactiveLifecycleAwareSessionManagerUnitTests {
|
||||
.verifyComplete();
|
||||
|
||||
verify(tokenSupplier, times(2)).getVaultToken();
|
||||
verify(listener, times(2)).onAuthenticationEvent(any(AfterLoginEvent.class));
|
||||
verify(listener).onAuthenticationEvent(any(LoginTokenExpiredEvent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
* Support for Key-Value v2 (versioned backend) secrets through `@VaultPropertySource`.
|
||||
* SpEL support in `@Secret`.
|
||||
* Add support for Jetty as reactive HttpClient.
|
||||
* `LifecycleAwareSessionManager` and `ReactiveLifecycleAwareSessionManager` emit now ``AuthenticationEvent``s.
|
||||
|
||||
[[new-features.2-1-0]]
|
||||
=== What's new in Spring Vault 2.1
|
||||
|
||||
Reference in New Issue
Block a user