Introduce AuthenticationEventMulticaster interface.
Closes gh-816
This commit is contained in:
@@ -22,6 +22,7 @@ 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.AuthenticationEventMulticaster;
|
||||
import org.springframework.vault.authentication.event.AuthenticationListener;
|
||||
|
||||
/**
|
||||
@@ -31,13 +32,13 @@ import org.springframework.vault.authentication.event.AuthenticationListener;
|
||||
* {@link AuthenticationErrorListener}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.2
|
||||
* @see AuthenticationEvent
|
||||
* @see AuthenticationErrorEvent
|
||||
* @see AuthenticationListener
|
||||
* @see AuthenticationErrorListener
|
||||
* @since 2.2
|
||||
*/
|
||||
public abstract class AuthenticationEventPublisher {
|
||||
public abstract class AuthenticationEventPublisher implements AuthenticationEventMulticaster {
|
||||
|
||||
private final Set<AuthenticationListener> listeners = new CopyOnWriteArraySet<>();
|
||||
|
||||
@@ -46,8 +47,9 @@ public abstract class AuthenticationEventPublisher {
|
||||
/**
|
||||
* Add a {@link AuthenticationListener}. The listener starts receiving events as soon
|
||||
* as possible.
|
||||
* @param listener lease listener, must not be {@literal null}.
|
||||
* @param listener the listener, must not be {@literal null}.
|
||||
*/
|
||||
@Override
|
||||
public void addAuthenticationListener(AuthenticationListener listener) {
|
||||
|
||||
Assert.notNull(listener, "AuthenticationEventListener must not be null");
|
||||
@@ -57,8 +59,9 @@ public abstract class AuthenticationEventPublisher {
|
||||
|
||||
/**
|
||||
* Remove a {@link AuthenticationListener}.
|
||||
* @param listener must not be {@literal null}.
|
||||
* @param listener the listener, must not be {@literal null}.
|
||||
*/
|
||||
@Override
|
||||
public void removeAuthenticationListener(AuthenticationListener listener) {
|
||||
this.listeners.remove(listener);
|
||||
}
|
||||
@@ -66,8 +69,9 @@ public abstract class AuthenticationEventPublisher {
|
||||
/**
|
||||
* Add a {@link AuthenticationErrorListener}. The listener starts receiving events as
|
||||
* soon as possible.
|
||||
* @param listener lease listener, must not be {@literal null}.
|
||||
* @param listener the listener, must not be {@literal null}.
|
||||
*/
|
||||
@Override
|
||||
public void addErrorListener(AuthenticationErrorListener listener) {
|
||||
|
||||
Assert.notNull(listener, "AuthenticationEventErrorListener must not be null");
|
||||
@@ -77,31 +81,24 @@ public abstract class AuthenticationEventPublisher {
|
||||
|
||||
/**
|
||||
* Remove a {@link AuthenticationErrorListener}.
|
||||
* @param listener must not be {@literal null}.
|
||||
* @param listener the listener, must not be {@literal null}.
|
||||
*/
|
||||
@Override
|
||||
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) {
|
||||
|
||||
@Override
|
||||
public void multicastEvent(AuthenticationEvent event) {
|
||||
for (AuthenticationListener listener : this.listeners) {
|
||||
listener.onAuthenticationEvent(authenticationEvent);
|
||||
listener.onAuthenticationEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch the event to all {@link AuthenticationErrorListener}s.
|
||||
* @param authenticationEvent the event to dispatch.
|
||||
*/
|
||||
void dispatch(AuthenticationErrorEvent authenticationEvent) {
|
||||
|
||||
@Override
|
||||
public void multicastEvent(AuthenticationErrorEvent event) {
|
||||
for (AuthenticationErrorListener listener : this.errorListeners) {
|
||||
listener.onAuthenticationError(authenticationEvent);
|
||||
listener.onAuthenticationError(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -169,10 +169,10 @@ public class LifecycleAwareSessionManager extends LifecycleAwareSessionManagerSu
|
||||
protected void revoke(VaultToken token) {
|
||||
|
||||
try {
|
||||
dispatch(new BeforeLoginTokenRevocationEvent(token));
|
||||
multicastEvent(new BeforeLoginTokenRevocationEvent(token));
|
||||
this.restOperations.postForObject("auth/token/revoke-self", new HttpEntity<>(VaultHttpHeaders.from(token)),
|
||||
Map.class);
|
||||
dispatch(new AfterLoginTokenRevocationEvent(token));
|
||||
multicastEvent(new AfterLoginTokenRevocationEvent(token));
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
if (LoginToken.hasAccessor(token)) {
|
||||
@@ -183,7 +183,7 @@ public class LifecycleAwareSessionManager extends LifecycleAwareSessionManagerSu
|
||||
else {
|
||||
this.logger.warn("Cannot revoke VaultToken", e);
|
||||
}
|
||||
dispatch(new LoginTokenRevocationFailedEvent(token, e));
|
||||
multicastEvent(new LoginTokenRevocationFailedEvent(token, e));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,14 +229,14 @@ public class LifecycleAwareSessionManager extends LifecycleAwareSessionManagerSu
|
||||
this.logger.warn(exception.getMessage());
|
||||
}
|
||||
|
||||
dispatch(new LoginTokenRenewalFailedEvent(tokenWrapper.getToken(), exception));
|
||||
multicastEvent(new LoginTokenRenewalFailedEvent(tokenWrapper.getToken(), exception));
|
||||
return shouldDrop ? RenewOutcome.TERMINAL_ERROR : RenewOutcome.RENEWABLE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
private RenewOutcome doRenew(TokenWrapper wrapper) {
|
||||
|
||||
dispatch(new BeforeLoginTokenRenewedEvent(wrapper.getToken()));
|
||||
multicastEvent(new BeforeLoginTokenRenewedEvent(wrapper.getToken()));
|
||||
VaultResponse vaultResponse = this.restOperations.postForObject("auth/token/renew-self",
|
||||
new HttpEntity<>(VaultHttpHeaders.from(wrapper.token)), VaultResponse.class);
|
||||
|
||||
@@ -254,12 +254,12 @@ public class LifecycleAwareSessionManager extends LifecycleAwareSessionManagerSu
|
||||
}
|
||||
|
||||
setToken(Optional.empty());
|
||||
dispatch(new LoginTokenExpiredEvent(renewed));
|
||||
multicastEvent(new LoginTokenExpiredEvent(renewed));
|
||||
return RenewOutcome.TERMINAL_ERROR;
|
||||
}
|
||||
|
||||
setToken(Optional.of(new TokenWrapper(renewed, wrapper.revocable)));
|
||||
dispatch(new AfterLoginTokenRenewedEvent(renewed));
|
||||
multicastEvent(new AfterLoginTokenRenewedEvent(renewed));
|
||||
|
||||
return RenewOutcome.SUCCESS;
|
||||
}
|
||||
@@ -292,7 +292,7 @@ public class LifecycleAwareSessionManager extends LifecycleAwareSessionManagerSu
|
||||
token = this.clientAuthentication.login();
|
||||
}
|
||||
catch (VaultException e) {
|
||||
dispatch(new LoginFailedEvent(this.clientAuthentication, e));
|
||||
multicastEvent(new LoginFailedEvent(this.clientAuthentication, e));
|
||||
throw e;
|
||||
}
|
||||
|
||||
@@ -305,12 +305,12 @@ public class LifecycleAwareSessionManager extends LifecycleAwareSessionManagerSu
|
||||
}
|
||||
catch (VaultTokenLookupException e) {
|
||||
this.logger.warn(String.format("Cannot enhance VaultToken to a LoginToken: %s", e.getMessage()));
|
||||
dispatch(new AuthenticationErrorEvent(token, e));
|
||||
multicastEvent(new AuthenticationErrorEvent(token, e));
|
||||
}
|
||||
}
|
||||
|
||||
setToken(Optional.of(wrapper));
|
||||
dispatch(new AfterLoginEvent(token));
|
||||
multicastEvent(new AfterLoginEvent(token));
|
||||
|
||||
if (isTokenRenewable()) {
|
||||
scheduleRenewal();
|
||||
@@ -356,7 +356,7 @@ public class LifecycleAwareSessionManager extends LifecycleAwareSessionManagerSu
|
||||
}
|
||||
catch (Exception e) {
|
||||
this.logger.error("Cannot renew VaultToken", e);
|
||||
dispatch(new LoginTokenRenewalFailedEvent(token, e));
|
||||
multicastEvent(new LoginTokenRenewalFailedEvent(token, e));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -185,8 +185,8 @@ public class ReactiveLifecycleAwareSessionManager extends LifecycleAwareSessionM
|
||||
})
|
||||
.retrieve()
|
||||
.bodyToMono(String.class)
|
||||
.doOnSubscribe(ignore -> dispatch(new BeforeLoginTokenRevocationEvent(token)))
|
||||
.doOnNext(ignore -> dispatch(new AfterLoginTokenRevocationEvent(token)))
|
||||
.doOnSubscribe(ignore -> multicastEvent(new BeforeLoginTokenRevocationEvent(token)))
|
||||
.doOnNext(ignore -> multicastEvent(new AfterLoginTokenRevocationEvent(token)))
|
||||
.onErrorResume(WebClientResponseException.class, e -> onRevokeFailed(token, e))
|
||||
.onErrorResume(Exception.class, e -> onRevokeFailed(token, e))
|
||||
.then();
|
||||
@@ -202,7 +202,7 @@ public class ReactiveLifecycleAwareSessionManager extends LifecycleAwareSessionM
|
||||
this.logger.warn("Cannot revoke VaultToken", e);
|
||||
}
|
||||
|
||||
dispatch(new LoginTokenRevocationFailedEvent(token, e));
|
||||
multicastEvent(new LoginTokenRevocationFailedEvent(token, e));
|
||||
|
||||
return Mono.empty();
|
||||
}
|
||||
@@ -251,7 +251,7 @@ public class ReactiveLifecycleAwareSessionManager extends LifecycleAwareSessionM
|
||||
this.logger.warn(exception.getMessage());
|
||||
}
|
||||
|
||||
dispatch(new LoginTokenRenewalFailedEvent(wrapper.getToken(), exception));
|
||||
multicastEvent(new LoginTokenRenewalFailedEvent(wrapper.getToken(), exception));
|
||||
return shouldDrop ? EMPTY : Mono.just(wrapper);
|
||||
});
|
||||
}
|
||||
@@ -264,14 +264,15 @@ public class ReactiveLifecycleAwareSessionManager extends LifecycleAwareSessionM
|
||||
.retrieve()
|
||||
.bodyToMono(VaultResponse.class);
|
||||
|
||||
return exchange.doOnSubscribe(ignore -> dispatch(new BeforeLoginTokenRenewedEvent(tokenWrapper.getToken())))
|
||||
return exchange
|
||||
.doOnSubscribe(ignore -> multicastEvent(new BeforeLoginTokenRenewedEvent(tokenWrapper.getToken())))
|
||||
.handle((response, sink) -> {
|
||||
|
||||
LoginToken renewed = LoginTokenUtil.from(response.getRequiredAuth());
|
||||
|
||||
if (!isExpired(renewed)) {
|
||||
sink.next(new TokenWrapper(renewed, tokenWrapper.revocable));
|
||||
dispatch(new AfterLoginTokenRenewedEvent(renewed));
|
||||
multicastEvent(new AfterLoginTokenRenewedEvent(renewed));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -287,7 +288,7 @@ public class ReactiveLifecycleAwareSessionManager extends LifecycleAwareSessionM
|
||||
}
|
||||
|
||||
dropCurrentToken();
|
||||
dispatch(new LoginTokenExpiredEvent(renewed));
|
||||
multicastEvent(new LoginTokenExpiredEvent(renewed));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -310,7 +311,7 @@ public class ReactiveLifecycleAwareSessionManager extends LifecycleAwareSessionM
|
||||
Mono<TokenWrapper> obtainToken = this.clientAuthentication.getVaultToken()
|
||||
.flatMap(this::doSelfLookup) //
|
||||
.onErrorMap(it -> {
|
||||
dispatch(new LoginFailedEvent(this.clientAuthentication, it));
|
||||
multicastEvent(new LoginFailedEvent(this.clientAuthentication, it));
|
||||
return it;
|
||||
})
|
||||
.doOnNext(it -> {
|
||||
@@ -319,7 +320,7 @@ public class ReactiveLifecycleAwareSessionManager extends LifecycleAwareSessionM
|
||||
scheduleRenewal(it.getToken());
|
||||
}
|
||||
|
||||
dispatch(new AfterLoginEvent(it.getToken()));
|
||||
multicastEvent(new AfterLoginEvent(it.getToken()));
|
||||
});
|
||||
|
||||
this.token.compareAndSet(tokenWrapper, obtainToken.cache());
|
||||
@@ -339,7 +340,7 @@ public class ReactiveLifecycleAwareSessionManager extends LifecycleAwareSessionM
|
||||
return loginTokenMono.onErrorResume(e -> {
|
||||
|
||||
this.logger.warn(String.format("Cannot enhance VaultToken to a LoginToken: %s", e.getMessage()));
|
||||
dispatch(new AuthenticationErrorEvent(token, e));
|
||||
multicastEvent(new AuthenticationErrorEvent(token, e));
|
||||
return Mono.just(token);
|
||||
}).map(it -> new TokenWrapper(it, false));
|
||||
}
|
||||
@@ -379,13 +380,13 @@ public class ReactiveLifecycleAwareSessionManager extends LifecycleAwareSessionM
|
||||
if (isTokenRenewable(token)) {
|
||||
renewToken().subscribe(this::scheduleRenewal, e -> {
|
||||
this.logger.error("Cannot renew VaultToken", e);
|
||||
dispatch(new LoginTokenRenewalFailedEvent(token, e));
|
||||
multicastEvent(new LoginTokenRenewalFailedEvent(token, e));
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
this.logger.error("Cannot renew VaultToken", e);
|
||||
dispatch(new LoginTokenRenewalFailedEvent(token, e));
|
||||
multicastEvent(new LoginTokenRenewalFailedEvent(token, e));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2023 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;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by objects that can manage a number of
|
||||
* {@link AuthenticationEvent} and {@link AuthenticationErrorEvent} objects and publish
|
||||
* events to them.
|
||||
* <p>
|
||||
* An {@link org.springframework.vault.authentication.AuthenticationEventPublisher},
|
||||
* typically a lifecycle-aware
|
||||
* {@link org.springframework.vault.authentication.SessionManager}, can use an
|
||||
* {@code AuthenticationEventMulticaster} as a delegate for actually publishing events.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @see AuthenticationListener
|
||||
* @see AuthenticationErrorListener
|
||||
* @since 3.1
|
||||
*/
|
||||
public interface AuthenticationEventMulticaster {
|
||||
|
||||
/**
|
||||
* Add a {@link AuthenticationListener}.
|
||||
* @param listener the listener, must not be {@literal null}.
|
||||
*/
|
||||
void addAuthenticationListener(AuthenticationListener listener);
|
||||
|
||||
/**
|
||||
* Remove a {@link AuthenticationListener}.
|
||||
* @param listener the listener, must not be {@literal null}.
|
||||
*/
|
||||
void removeAuthenticationListener(AuthenticationListener listener);
|
||||
|
||||
/**
|
||||
* Add a {@link AuthenticationErrorListener}.
|
||||
* @param listener the listener, must not be {@literal null}.
|
||||
*/
|
||||
void addErrorListener(AuthenticationErrorListener listener);
|
||||
|
||||
/**
|
||||
* Remove a {@link AuthenticationErrorListener}.
|
||||
* @param listener the listener, must not be {@literal null}.
|
||||
*/
|
||||
void removeErrorListener(AuthenticationErrorListener listener);
|
||||
|
||||
/**
|
||||
* Multicast the given application event to appropriate listeners.
|
||||
* @param event the event to multicast.
|
||||
*/
|
||||
void multicastEvent(AuthenticationEvent event);
|
||||
|
||||
/**
|
||||
* Multicast the given application event to appropriate listeners.
|
||||
* @param event the event to multicast.
|
||||
*/
|
||||
void multicastEvent(AuthenticationErrorEvent event);
|
||||
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.client.reactive.ClientHttpConnector;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.vault.authentication.AuthenticationEventPublisher;
|
||||
import org.springframework.vault.authentication.AuthenticationStepsFactory;
|
||||
import org.springframework.vault.authentication.AuthenticationStepsOperator;
|
||||
import org.springframework.vault.authentication.CachingVaultTokenSupplier;
|
||||
@@ -33,6 +34,11 @@ import org.springframework.vault.authentication.ReactiveSessionManager;
|
||||
import org.springframework.vault.authentication.SessionManager;
|
||||
import org.springframework.vault.authentication.TokenAuthentication;
|
||||
import org.springframework.vault.authentication.VaultTokenSupplier;
|
||||
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.AuthenticationEventMulticaster;
|
||||
import org.springframework.vault.authentication.event.AuthenticationListener;
|
||||
import org.springframework.vault.client.ClientHttpConnectorFactory;
|
||||
import org.springframework.vault.client.ReactiveVaultClients;
|
||||
import org.springframework.vault.client.ReactiveVaultEndpointProvider;
|
||||
@@ -70,7 +76,6 @@ public abstract class AbstractReactiveVaultConfiguration extends AbstractVaultCo
|
||||
/**
|
||||
* @return a {@link ReactiveVaultEndpointProvider} returning the value of
|
||||
* {@link #vaultEndpointProvider()}.
|
||||
*
|
||||
* @see #vaultEndpoint()
|
||||
* @see #vaultEndpointProvider()
|
||||
* @since 2.3
|
||||
@@ -153,7 +158,9 @@ public abstract class AbstractReactiveVaultConfiguration extends AbstractVaultCo
|
||||
@Bean
|
||||
@Override
|
||||
public SessionManager sessionManager() {
|
||||
return new ReactiveSessionManagerAdapter(getReactiveSessionManager());
|
||||
ReactiveSessionManager rsm = getReactiveSessionManager();
|
||||
return rsm instanceof AuthenticationEventPublisher ? new ReactiveMulticastingSessionManagerAdapter(rsm)
|
||||
: new ReactiveSessionManagerAdapter(rsm);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -251,4 +258,50 @@ public abstract class AbstractReactiveVaultConfiguration extends AbstractVaultCo
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension to {@link ReactiveSessionManagerAdapter} that can multicast
|
||||
* {@link AuthenticationEvent}s.
|
||||
*/
|
||||
static class ReactiveMulticastingSessionManagerAdapter extends ReactiveSessionManagerAdapter
|
||||
implements AuthenticationEventMulticaster {
|
||||
|
||||
private final AuthenticationEventMulticaster delegate;
|
||||
|
||||
public ReactiveMulticastingSessionManagerAdapter(ReactiveSessionManager sessionManager) {
|
||||
super(sessionManager);
|
||||
this.delegate = (AuthenticationEventMulticaster) sessionManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAuthenticationListener(AuthenticationListener listener) {
|
||||
delegate.addAuthenticationListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAuthenticationListener(AuthenticationListener listener) {
|
||||
delegate.removeAuthenticationListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addErrorListener(AuthenticationErrorListener listener) {
|
||||
delegate.addErrorListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeErrorListener(AuthenticationErrorListener listener) {
|
||||
delegate.removeErrorListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void multicastEvent(AuthenticationEvent event) {
|
||||
delegate.multicastEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void multicastEvent(AuthenticationErrorEvent event) {
|
||||
delegate.multicastEvent(event);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user