Do not revoke batch tokens.
We now track the token type and no longer attempt revoking batch tokens. Also, refactor code duplicates to use LoginTokenUtil. Closes gh-764
This commit is contained in:
@@ -44,15 +44,15 @@ import org.springframework.web.client.RestOperations;
|
||||
* <p>
|
||||
* This {@link SessionManager} also implements {@link DisposableBean} to revoke the
|
||||
* {@link LoginToken} once it's not required anymore. Token revocation will stop regular
|
||||
* token refresh. Tokens are only revoked only if the associated
|
||||
* {@link ClientAuthentication} returns a {@link LoginToken}.
|
||||
* token refresh. Tokens are only revoked if the associated {@link ClientAuthentication}
|
||||
* returns a {@link LoginToken#isServiceToken() service token}.
|
||||
* <p>
|
||||
* If Token renewal runs into a client-side error, it assumes the token was
|
||||
* revoked/expired. It discards the token state so the next attempt will lead to another
|
||||
* login attempt.
|
||||
* <p>
|
||||
* By default, {@link VaultToken} are looked up in Vault to determine renewability and the
|
||||
* remaining TTL, see {@link #setTokenSelfLookupEnabled(boolean)}.
|
||||
* By default, {@link VaultToken} are looked up in Vault to determine renewability,
|
||||
* remaining TTL, accessor and type, see {@link #setTokenSelfLookupEnabled(boolean)}.
|
||||
* <p>
|
||||
* The session manager dispatches authentication events to {@link AuthenticationListener}
|
||||
* and {@link AuthenticationErrorListener}. Event notifications are dispatched either on
|
||||
@@ -390,7 +390,12 @@ public class LifecycleAwareSessionManager extends LifecycleAwareSessionManagerSu
|
||||
}
|
||||
|
||||
public boolean isRevocable() {
|
||||
return this.revocable;
|
||||
|
||||
if (token instanceof LoginToken login && login.isServiceToken()) {
|
||||
return this.revocable;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
package org.springframework.vault.authentication;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.vault.support.VaultToken;
|
||||
|
||||
@@ -34,12 +36,29 @@ public class LoginToken extends VaultToken {
|
||||
*/
|
||||
private final Duration leaseDuration;
|
||||
|
||||
private LoginToken(char[] token, Duration duration, boolean renewable) {
|
||||
@Nullable
|
||||
private final String accessor;
|
||||
|
||||
@Nullable
|
||||
private final String type;
|
||||
|
||||
private LoginToken(char[] token, Duration duration, boolean renewable, @Nullable String accessor,
|
||||
@Nullable String type) {
|
||||
|
||||
super(token);
|
||||
|
||||
this.leaseDuration = duration;
|
||||
this.renewable = renewable;
|
||||
this.accessor = accessor;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a new {@link LoginTokenBuilder}.
|
||||
* @since 3.0.2
|
||||
*/
|
||||
public static LoginTokenBuilder builder() {
|
||||
return new LoginTokenBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,7 +98,7 @@ public class LoginToken extends VaultToken {
|
||||
Assert.notNull(leaseDuration, "Lease duration must not be null");
|
||||
Assert.isTrue(!leaseDuration.isNegative(), "Lease duration must not be negative");
|
||||
|
||||
return new LoginToken(token, leaseDuration, false);
|
||||
return new LoginToken(token, leaseDuration, false, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,7 +129,7 @@ public class LoginToken extends VaultToken {
|
||||
Assert.notNull(leaseDuration, "Lease duration must not be null");
|
||||
Assert.isTrue(!leaseDuration.isNegative(), "Lease duration must not be negative");
|
||||
|
||||
return new LoginToken(token, leaseDuration, true);
|
||||
return new LoginToken(token, leaseDuration, true, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -127,14 +146,169 @@ public class LoginToken extends VaultToken {
|
||||
return this.renewable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the token accessor.
|
||||
* @since 3.0.2
|
||||
*/
|
||||
@Nullable
|
||||
public String getAccessor() {
|
||||
return accessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the token type.
|
||||
* @since 3.0.2
|
||||
* @see #isBatchToken()
|
||||
* @see #isServiceToken())
|
||||
*/
|
||||
@Nullable
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@literal true} if the token is a batch token.
|
||||
* @since 3.0.2
|
||||
*/
|
||||
public boolean isBatchToken() {
|
||||
return "batch".equals(this.type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@literal true} if the token is a service token.
|
||||
* @since 3.0.2
|
||||
*/
|
||||
public boolean isServiceToken() {
|
||||
return this.type == null || "service".equals(this.type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append(getClass().getSimpleName());
|
||||
sb.append(" [renewable=").append(this.renewable);
|
||||
sb.append(", leaseDuration=").append(this.leaseDuration);
|
||||
sb.append(", type=").append(this.type);
|
||||
sb.append(']');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for {@link LoginToken}.
|
||||
*
|
||||
* @since 3.0.2
|
||||
*/
|
||||
public static class LoginTokenBuilder {
|
||||
|
||||
@Nullable
|
||||
private char[] token;
|
||||
|
||||
private boolean renewable;
|
||||
|
||||
/**
|
||||
* Duration in seconds.
|
||||
*/
|
||||
private Duration leaseDuration = Duration.ZERO;
|
||||
|
||||
@Nullable
|
||||
private String accessor;
|
||||
|
||||
@Nullable
|
||||
private String type;
|
||||
|
||||
private LoginTokenBuilder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the token value. This is a required builder property. Without this
|
||||
* property, you cannot {@link #build()} a {@link LoginToken}.
|
||||
* @param token must not be empty or {@literal null}.
|
||||
* @return {@code this} {@link LoginTokenBuilder}.
|
||||
*/
|
||||
public LoginTokenBuilder token(String token) {
|
||||
|
||||
Assert.hasText(token, "Token must not be empty");
|
||||
|
||||
return token(token.toCharArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the token value. This is a required builder property. Without this
|
||||
* property, you cannot {@link #build()} a {@link LoginToken}.
|
||||
* @param token must not be empty or {@literal null}.
|
||||
* @return {@code this} {@link LoginTokenBuilder}.
|
||||
*/
|
||||
public LoginTokenBuilder token(char[] token) {
|
||||
|
||||
Assert.notNull(token, "Token must not be null");
|
||||
Assert.isTrue(token.length > 0, "Token must not be empty");
|
||||
|
||||
this.token = token;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure whether the token is renewable.
|
||||
* @param renewable
|
||||
* @return {@code this} {@link LoginTokenBuilder}.
|
||||
*/
|
||||
public LoginTokenBuilder renewable(boolean renewable) {
|
||||
|
||||
this.renewable = renewable;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the lease duration.
|
||||
* @param leaseDuration must not be {@literal null}.
|
||||
* @return {@code this} {@link LoginTokenBuilder}.
|
||||
*/
|
||||
public LoginTokenBuilder leaseDuration(Duration leaseDuration) {
|
||||
|
||||
Assert.notNull(leaseDuration, "Lease duration must not be empty");
|
||||
|
||||
this.leaseDuration = leaseDuration;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the token accessor.
|
||||
* @param accessor must not be empty or {@literal null}.
|
||||
* @return {@code this} {@link LoginTokenBuilder}.
|
||||
*/
|
||||
public LoginTokenBuilder accessor(String accessor) {
|
||||
|
||||
Assert.hasText(accessor, "Token accessor must not be empty");
|
||||
|
||||
this.accessor = accessor;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the token type.
|
||||
* @param type must not be empty or {@literal null}.
|
||||
* @return {@code this} {@link LoginTokenBuilder}.
|
||||
*/
|
||||
public LoginTokenBuilder type(String type) {
|
||||
|
||||
Assert.hasText(type, "Token type must not be empty");
|
||||
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a new {@link LoginToken} instance. {@link #token} must be configured.
|
||||
* @return a new {@link LoginToken} instance.
|
||||
*/
|
||||
public LoginToken build() {
|
||||
|
||||
Assert.notNull(token, "Token must not be null");
|
||||
|
||||
return new LoginToken(Arrays.copyOf(this.token, this.token.length), this.leaseDuration, this.renewable,
|
||||
this.accessor, this.type);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,13 +15,11 @@
|
||||
*/
|
||||
package org.springframework.vault.authentication;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.vault.VaultException;
|
||||
import org.springframework.vault.client.VaultHttpHeaders;
|
||||
@@ -35,7 +33,7 @@ import org.springframework.web.client.RestOperations;
|
||||
/**
|
||||
* Adapts tokens created by a {@link ClientAuthentication} to a {@link LoginToken}. Allows
|
||||
* decoration of a {@link ClientAuthentication} object to perform a self-lookup after
|
||||
* token retrieval to obtain the remaining TTL and renewability.
|
||||
* token retrieval to obtain the remaining TTL, renewability, accessor and token type.
|
||||
* <p>
|
||||
* Using this adapter decrements the usage counter for the created token.
|
||||
*
|
||||
@@ -77,14 +75,7 @@ public class LoginTokenAdapter implements ClientAuthentication {
|
||||
|
||||
Map<String, Object> data = lookupSelf(restOperations, token);
|
||||
|
||||
Boolean renewable = (Boolean) data.get("renewable");
|
||||
Number ttl = (Number) data.get("ttl");
|
||||
|
||||
if (renewable != null && renewable) {
|
||||
return LoginToken.renewable(token.toCharArray(), getLeaseDuration(ttl));
|
||||
}
|
||||
|
||||
return LoginToken.of(token.toCharArray(), getLeaseDuration(ttl));
|
||||
return LoginTokenUtil.from(token.toCharArray(), data);
|
||||
}
|
||||
|
||||
private static Map<String, Object> lookupSelf(RestOperations restOperations, VaultToken token) {
|
||||
@@ -106,8 +97,4 @@ public class LoginTokenAdapter implements ClientAuthentication {
|
||||
}
|
||||
}
|
||||
|
||||
static Duration getLeaseDuration(@Nullable Number ttl) {
|
||||
return ttl == null ? Duration.ZERO : Duration.ofSeconds(ttl.longValue());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import java.time.Duration;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Utility class for {@link LoginToken}.
|
||||
@@ -51,26 +52,43 @@ final class LoginTokenUtil {
|
||||
* @return the {@link LoginToken}
|
||||
* @since 2.0
|
||||
*/
|
||||
static LoginToken from(char[] token, Map<String, Object> auth) {
|
||||
static LoginToken from(char[] token, Map<String, ?> auth) {
|
||||
|
||||
Assert.notNull(auth, "Authentication must not be null");
|
||||
|
||||
Boolean renewable = (Boolean) auth.get("renewable");
|
||||
Number leaseDuration = (Number) auth.get("lease_duration");
|
||||
String accessor = (String) auth.get("accessor");
|
||||
String type = (String) auth.get("type");
|
||||
|
||||
if (leaseDuration == null) {
|
||||
leaseDuration = (Number) auth.get("ttl");
|
||||
}
|
||||
|
||||
if (renewable != null && renewable) {
|
||||
return LoginToken.renewable(token, Duration.ofSeconds(leaseDuration.longValue()));
|
||||
if (type == null) {
|
||||
type = (String) auth.get("token_type");
|
||||
}
|
||||
|
||||
LoginToken.LoginTokenBuilder builder = LoginToken.builder();
|
||||
builder.token(token);
|
||||
|
||||
if (StringUtils.hasText(accessor)) {
|
||||
builder.accessor(accessor);
|
||||
}
|
||||
|
||||
if (leaseDuration != null) {
|
||||
return LoginToken.of(token, Duration.ofSeconds(leaseDuration.longValue()));
|
||||
builder.leaseDuration(Duration.ofSeconds(leaseDuration.longValue()));
|
||||
}
|
||||
|
||||
return LoginToken.of(token);
|
||||
if (renewable != null) {
|
||||
builder.renewable(renewable);
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(type)) {
|
||||
builder.type(type);
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -47,15 +47,15 @@ import org.springframework.web.reactive.function.client.WebClientResponseExcepti
|
||||
* <p>
|
||||
* This {@link ReactiveSessionManager} also implements {@link DisposableBean} to revoke
|
||||
* the {@link LoginToken} once it's not required anymore. Token revocation will stop
|
||||
* regular token refresh. Tokens are only revoked only if the associated
|
||||
* {@link VaultTokenSupplier} returns a {@link LoginToken}.
|
||||
* regular token refresh. Tokens are only revoked if the associated
|
||||
* {@link VaultTokenSupplier} returns a {@link LoginToken#isServiceToken() service token}.
|
||||
* <p>
|
||||
* If Token renewal runs into a client-side error, it assumes the token was
|
||||
* revoked/expired. It discards the token state so the next attempt will lead to another
|
||||
* login attempt.
|
||||
* <p>
|
||||
* By default, {@link VaultToken} are looked up in Vault to determine renewability and the
|
||||
* remaining TTL, see {@link #setTokenSelfLookupEnabled(boolean)}.
|
||||
* By default, {@link VaultToken} are looked up in Vault to determine renewability,
|
||||
* remaining TTL, accessor and type, see {@link #setTokenSelfLookupEnabled(boolean)}.
|
||||
* <p>
|
||||
* The session manager dispatches authentication events to {@link AuthenticationListener}
|
||||
* and {@link AuthenticationErrorListener}.
|
||||
@@ -371,17 +371,7 @@ public class ReactiveLifecycleAwareSessionManager extends LifecycleAwareSessionM
|
||||
|
||||
Mono<Map<String, Object>> data = lookupSelf(webClient, token);
|
||||
|
||||
return data.map(it -> {
|
||||
|
||||
Boolean renewable = (Boolean) it.get("renewable");
|
||||
Number ttl = (Number) it.get("ttl");
|
||||
|
||||
if (renewable != null && renewable) {
|
||||
return LoginToken.renewable(token.toCharArray(), LoginTokenAdapter.getLeaseDuration(ttl));
|
||||
}
|
||||
|
||||
return LoginToken.of(token.toCharArray(), LoginTokenAdapter.getLeaseDuration(ttl));
|
||||
});
|
||||
return data.map(it -> LoginTokenUtil.from(token.toCharArray(), it));
|
||||
}
|
||||
|
||||
private static Mono<Map<String, Object>> lookupSelf(WebClient webClient, VaultToken token) {
|
||||
@@ -431,7 +421,12 @@ public class ReactiveLifecycleAwareSessionManager extends LifecycleAwareSessionM
|
||||
}
|
||||
|
||||
public boolean isRevocable() {
|
||||
return this.revocable;
|
||||
|
||||
if (token instanceof LoginToken login && login.isServiceToken()) {
|
||||
return this.revocable;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ public class VaultToken {
|
||||
* @since 1.1
|
||||
*/
|
||||
public char[] toCharArray() {
|
||||
return this.token;
|
||||
return Arrays.copyOf(this.token, this.token.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -89,6 +89,7 @@ public class VaultToken {
|
||||
return Arrays.hashCode(this.token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName();
|
||||
}
|
||||
|
||||
@@ -197,6 +197,22 @@ class LifecycleAwareSessionManagerUnitTests {
|
||||
verifyNoMoreInteractions(this.listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotRevokeBatchToken() {
|
||||
|
||||
LoginToken batchToken = LoginToken.builder().token("login").type("batch").build();
|
||||
|
||||
when(this.clientAuthentication.login()).thenReturn(batchToken);
|
||||
|
||||
this.sessionManager.setTokenSelfLookupEnabled(false);
|
||||
this.sessionManager.renewToken();
|
||||
this.sessionManager.destroy();
|
||||
|
||||
verifyNoInteractions(this.restOperations);
|
||||
verify(this.listener).onAuthenticationEvent(any(AfterLoginEvent.class));
|
||||
verifyNoMoreInteractions(this.listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void shouldNotThrowExceptionsOnRevokeErrors() {
|
||||
|
||||
@@ -39,11 +39,41 @@ class LoginTokenUnitTests {
|
||||
@Test
|
||||
void toStringShouldPrintFields() {
|
||||
|
||||
assertThat(LoginToken.of("token").toString()).isEqualTo("LoginToken [renewable=false, leaseDuration=PT0S]");
|
||||
assertThat(LoginToken.of("token".toCharArray(), Duration.ofSeconds(1)).toString())
|
||||
.isEqualTo("LoginToken [renewable=false, leaseDuration=PT1S]");
|
||||
assertThat(LoginToken.renewable("token".toCharArray(), Duration.ofSeconds(1)).toString())
|
||||
.isEqualTo("LoginToken [renewable=true, leaseDuration=PT1S]");
|
||||
assertThat(LoginToken.of("token")).hasToString("LoginToken [renewable=false, leaseDuration=PT0S, type=null]");
|
||||
assertThat(LoginToken.of("token".toCharArray(), Duration.ofSeconds(1)))
|
||||
.hasToString("LoginToken [renewable=false, leaseDuration=PT1S, type=null]");
|
||||
assertThat(LoginToken.renewable("token".toCharArray(), Duration.ofSeconds(1)))
|
||||
.hasToString("LoginToken [renewable=true, leaseDuration=PT1S, type=null]");
|
||||
assertThat(LoginToken.builder().token("foo").type("service").build())
|
||||
.hasToString("LoginToken [renewable=false, leaseDuration=PT0S, type=service]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldConstructTokenWithAccessor() {
|
||||
|
||||
assertThat(LoginToken.of("token").getAccessor()).isNull();
|
||||
|
||||
LoginToken loginToken = LoginToken.builder().token("token").accessor("acc").build();
|
||||
assertThat(loginToken.getToken()).isEqualTo("token");
|
||||
assertThat(loginToken.getAccessor()).isEqualTo("acc");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldConstructServiceToken() {
|
||||
|
||||
assertThat(LoginToken.of("token").isServiceToken()).isTrue();
|
||||
|
||||
LoginToken loginToken = LoginToken.builder().token("token").type("service").build();
|
||||
assertThat(loginToken.isServiceToken()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldConstructBatchToken() {
|
||||
|
||||
assertThat(LoginToken.of("token").isBatchToken()).isFalse();
|
||||
|
||||
LoginToken loginToken = LoginToken.builder().token("token").type("batch").build();
|
||||
assertThat(loginToken.isBatchToken()).isTrue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link LoginTokenUtil}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class LoginTokenUtilUnitTests {
|
||||
|
||||
@Test
|
||||
void shouldCreateServiceToken() {
|
||||
|
||||
Map<String, ? extends Serializable> response = Map.of("type", "service", "ttl", 100, "accessor",
|
||||
"B6oixijqmeR4bsLOJH88Ska9");
|
||||
|
||||
LoginToken loginToken = LoginTokenUtil.from("foo".toCharArray(), response);
|
||||
|
||||
assertThat(loginToken.isServiceToken()).isTrue();
|
||||
assertThat(loginToken.getAccessor()).isEqualTo("B6oixijqmeR4bsLOJH88Ska9");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateBatchToken() {
|
||||
|
||||
Map<String, ? extends Serializable> response = Map.of("type", "batch", "ttl", 100, "accessor",
|
||||
"B6oixijqmeR4bsLOJH88Ska9");
|
||||
|
||||
LoginToken loginToken = LoginTokenUtil.from("foo".toCharArray(), response);
|
||||
|
||||
assertThat(loginToken.isBatchToken()).isTrue();
|
||||
assertThat(loginToken.getAccessor()).isEqualTo("B6oixijqmeR4bsLOJH88Ska9");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -258,6 +258,26 @@ class ReactiveLifecycleAwareSessionManagerUnitTests {
|
||||
verifyNoMoreInteractions(this.listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotRevokeBatchTokenOnDestroy() {
|
||||
|
||||
LoginToken batchToken = LoginToken.builder().token("login").type("batch").build();
|
||||
|
||||
mockToken(batchToken);
|
||||
|
||||
this.sessionManager.setTokenSelfLookupEnabled(false);
|
||||
this.sessionManager.renewToken() //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNextCount(1) //
|
||||
.verifyComplete();
|
||||
this.sessionManager.destroy();
|
||||
|
||||
verify(this.webClient, never()).post();
|
||||
verify(this.webClient.post(), never()).uri("auth/token/revoke-self");
|
||||
verify(this.listener).onAuthenticationEvent(any(AfterLoginEvent.class));
|
||||
verifyNoMoreInteractions(this.listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotThrowExceptionsOnRevokeErrors() {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user