diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/LifecycleAwareSessionManager.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/LifecycleAwareSessionManager.java
index 3771d854..dbe7d3c1 100644
--- a/spring-vault-core/src/main/java/org/springframework/vault/authentication/LifecycleAwareSessionManager.java
+++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/LifecycleAwareSessionManager.java
@@ -44,15 +44,15 @@ import org.springframework.web.client.RestOperations;
*
* 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}.
*
* 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.
*
- * 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)}.
*
* 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;
}
}
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/LoginToken.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/LoginToken.java
index 0f0d2f58..a9da638b 100644
--- a/spring-vault-core/src/main/java/org/springframework/vault/authentication/LoginToken.java
+++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/LoginToken.java
@@ -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);
+ }
+
+ }
+
}
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/LoginTokenAdapter.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/LoginTokenAdapter.java
index b5a30c92..b6733d46 100644
--- a/spring-vault-core/src/main/java/org/springframework/vault/authentication/LoginTokenAdapter.java
+++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/LoginTokenAdapter.java
@@ -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.
*
* Using this adapter decrements the usage counter for the created token.
*
@@ -77,14 +75,7 @@ public class LoginTokenAdapter implements ClientAuthentication {
Map 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 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());
- }
-
}
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/LoginTokenUtil.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/LoginTokenUtil.java
index b6816541..b9ce76ca 100644
--- a/spring-vault-core/src/main/java/org/springframework/vault/authentication/LoginTokenUtil.java
+++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/LoginTokenUtil.java
@@ -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 auth) {
+ static LoginToken from(char[] token, Map 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();
}
}
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/ReactiveLifecycleAwareSessionManager.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/ReactiveLifecycleAwareSessionManager.java
index b83c0586..a418157d 100644
--- a/spring-vault-core/src/main/java/org/springframework/vault/authentication/ReactiveLifecycleAwareSessionManager.java
+++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/ReactiveLifecycleAwareSessionManager.java
@@ -47,15 +47,15 @@ import org.springframework.web.reactive.function.client.WebClientResponseExcepti
*
* 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}.
*
* 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.
*
- * 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)}.
*
* The session manager dispatches authentication events to {@link AuthenticationListener}
* and {@link AuthenticationErrorListener}.
@@ -371,17 +371,7 @@ public class ReactiveLifecycleAwareSessionManager extends LifecycleAwareSessionM
Mono