Skip to content

Commit 1e943b8

Browse files
committed
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
1 parent 0ef52da commit 1e943b8

File tree

10 files changed

+352
-50
lines changed

10 files changed

+352
-50
lines changed

spring-vault-core/src/main/java/org/springframework/vault/authentication/LifecycleAwareSessionManager.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,15 @@
4444
* <p>
4545
* This {@link SessionManager} also implements {@link DisposableBean} to revoke the
4646
* {@link LoginToken} once it's not required anymore. Token revocation will stop regular
47-
* token refresh. Tokens are only revoked only if the associated
48-
* {@link ClientAuthentication} returns a {@link LoginToken}.
47+
* token refresh. Tokens are only revoked if the associated {@link ClientAuthentication}
48+
* returns a {@link LoginToken#isServiceToken() service token}.
4949
* <p>
5050
* If Token renewal runs into a client-side error, it assumes the token was
5151
* revoked/expired. It discards the token state so the next attempt will lead to another
5252
* login attempt.
5353
* <p>
54-
* By default, {@link VaultToken} are looked up in Vault to determine renewability and the
55-
* remaining TTL, see {@link #setTokenSelfLookupEnabled(boolean)}.
54+
* By default, {@link VaultToken} are looked up in Vault to determine renewability,
55+
* remaining TTL, accessor and type, see {@link #setTokenSelfLookupEnabled(boolean)}.
5656
* <p>
5757
* The session manager dispatches authentication events to {@link AuthenticationListener}
5858
* and {@link AuthenticationErrorListener}. Event notifications are dispatched either on
@@ -390,7 +390,12 @@ public VaultToken getToken() {
390390
}
391391

392392
public boolean isRevocable() {
393-
return this.revocable;
393+
394+
if (token instanceof LoginToken login && login.isServiceToken()) {
395+
return this.revocable;
396+
}
397+
398+
return false;
394399
}
395400

396401
}

spring-vault-core/src/main/java/org/springframework/vault/authentication/LoginToken.java

Lines changed: 177 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
package org.springframework.vault.authentication;
1717

1818
import java.time.Duration;
19+
import java.util.Arrays;
1920

21+
import org.springframework.lang.Nullable;
2022
import org.springframework.util.Assert;
2123
import org.springframework.vault.support.VaultToken;
2224

@@ -34,12 +36,29 @@ public class LoginToken extends VaultToken {
3436
*/
3537
private final Duration leaseDuration;
3638

37-
private LoginToken(char[] token, Duration duration, boolean renewable) {
39+
@Nullable
40+
private final String accessor;
41+
42+
@Nullable
43+
private final String type;
44+
45+
private LoginToken(char[] token, Duration duration, boolean renewable, @Nullable String accessor,
46+
@Nullable String type) {
3847

3948
super(token);
4049

4150
this.leaseDuration = duration;
4251
this.renewable = renewable;
52+
this.accessor = accessor;
53+
this.type = type;
54+
}
55+
56+
/**
57+
* @return a new {@link LoginTokenBuilder}.
58+
* @since 3.0.2
59+
*/
60+
public static LoginTokenBuilder builder() {
61+
return new LoginTokenBuilder();
4362
}
4463

4564
/**
@@ -79,7 +98,7 @@ public static LoginToken of(char[] token, Duration leaseDuration) {
7998
Assert.notNull(leaseDuration, "Lease duration must not be null");
8099
Assert.isTrue(!leaseDuration.isNegative(), "Lease duration must not be negative");
81100

82-
return new LoginToken(token, leaseDuration, false);
101+
return new LoginToken(token, leaseDuration, false, null, null);
83102
}
84103

85104
/**
@@ -110,7 +129,7 @@ public static LoginToken renewable(char[] token, Duration leaseDuration) {
110129
Assert.notNull(leaseDuration, "Lease duration must not be null");
111130
Assert.isTrue(!leaseDuration.isNegative(), "Lease duration must not be negative");
112131

113-
return new LoginToken(token, leaseDuration, true);
132+
return new LoginToken(token, leaseDuration, true, null, null);
114133
}
115134

116135
/**
@@ -127,14 +146,169 @@ public boolean isRenewable() {
127146
return this.renewable;
128147
}
129148

149+
/**
150+
* @return the token accessor.
151+
* @since 3.0.2
152+
*/
153+
@Nullable
154+
public String getAccessor() {
155+
return accessor;
156+
}
157+
158+
/**
159+
* @return the token type.
160+
* @since 3.0.2
161+
* @see #isBatchToken()
162+
* @see #isServiceToken())
163+
*/
164+
@Nullable
165+
public String getType() {
166+
return type;
167+
}
168+
169+
/**
170+
* @return {@literal true} if the token is a batch token.
171+
* @since 3.0.2
172+
*/
173+
public boolean isBatchToken() {
174+
return "batch".equals(this.type);
175+
}
176+
177+
/**
178+
* @return {@literal true} if the token is a service token.
179+
* @since 3.0.2
180+
*/
181+
public boolean isServiceToken() {
182+
return this.type == null || "service".equals(this.type);
183+
}
184+
130185
@Override
131186
public String toString() {
132187
StringBuffer sb = new StringBuffer();
133188
sb.append(getClass().getSimpleName());
134189
sb.append(" [renewable=").append(this.renewable);
135190
sb.append(", leaseDuration=").append(this.leaseDuration);
191+
sb.append(", type=").append(this.type);
136192
sb.append(']');
137193
return sb.toString();
138194
}
139195

196+
/**
197+
* Builder for {@link LoginToken}.
198+
*
199+
* @since 3.0.2
200+
*/
201+
public static class LoginTokenBuilder {
202+
203+
@Nullable
204+
private char[] token;
205+
206+
private boolean renewable;
207+
208+
/**
209+
* Duration in seconds.
210+
*/
211+
private Duration leaseDuration = Duration.ZERO;
212+
213+
@Nullable
214+
private String accessor;
215+
216+
@Nullable
217+
private String type;
218+
219+
private LoginTokenBuilder() {
220+
}
221+
222+
/**
223+
* Configure the token value. This is a required builder property. Without this
224+
* property, you cannot {@link #build()} a {@link LoginToken}.
225+
* @param token must not be empty or {@literal null}.
226+
* @return {@code this} {@link LoginTokenBuilder}.
227+
*/
228+
public LoginTokenBuilder token(String token) {
229+
230+
Assert.hasText(token, "Token must not be empty");
231+
232+
return token(token.toCharArray());
233+
}
234+
235+
/**
236+
* Configure the token value. This is a required builder property. Without this
237+
* property, you cannot {@link #build()} a {@link LoginToken}.
238+
* @param token must not be empty or {@literal null}.
239+
* @return {@code this} {@link LoginTokenBuilder}.
240+
*/
241+
public LoginTokenBuilder token(char[] token) {
242+
243+
Assert.notNull(token, "Token must not be null");
244+
Assert.isTrue(token.length > 0, "Token must not be empty");
245+
246+
this.token = token;
247+
return this;
248+
}
249+
250+
/**
251+
* Configure whether the token is renewable.
252+
* @param renewable
253+
* @return {@code this} {@link LoginTokenBuilder}.
254+
*/
255+
public LoginTokenBuilder renewable(boolean renewable) {
256+
257+
this.renewable = renewable;
258+
return this;
259+
}
260+
261+
/**
262+
* Configure the lease duration.
263+
* @param leaseDuration must not be {@literal null}.
264+
* @return {@code this} {@link LoginTokenBuilder}.
265+
*/
266+
public LoginTokenBuilder leaseDuration(Duration leaseDuration) {
267+
268+
Assert.notNull(leaseDuration, "Lease duration must not be empty");
269+
270+
this.leaseDuration = leaseDuration;
271+
return this;
272+
}
273+
274+
/**
275+
* Configure the token accessor.
276+
* @param accessor must not be empty or {@literal null}.
277+
* @return {@code this} {@link LoginTokenBuilder}.
278+
*/
279+
public LoginTokenBuilder accessor(String accessor) {
280+
281+
Assert.hasText(accessor, "Token accessor must not be empty");
282+
283+
this.accessor = accessor;
284+
return this;
285+
}
286+
287+
/**
288+
* Configure the token type.
289+
* @param type must not be empty or {@literal null}.
290+
* @return {@code this} {@link LoginTokenBuilder}.
291+
*/
292+
public LoginTokenBuilder type(String type) {
293+
294+
Assert.hasText(type, "Token type must not be empty");
295+
296+
this.type = type;
297+
return this;
298+
}
299+
300+
/**
301+
* Build a new {@link LoginToken} instance. {@link #token} must be configured.
302+
* @return a new {@link LoginToken} instance.
303+
*/
304+
public LoginToken build() {
305+
306+
Assert.notNull(token, "Token must not be null");
307+
308+
return new LoginToken(Arrays.copyOf(this.token, this.token.length), this.leaseDuration, this.renewable,
309+
this.accessor, this.type);
310+
}
311+
312+
}
313+
140314
}

spring-vault-core/src/main/java/org/springframework/vault/authentication/LoginTokenAdapter.java

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,11 @@
1515
*/
1616
package org.springframework.vault.authentication;
1717

18-
import java.time.Duration;
1918
import java.util.Map;
2019

2120
import org.springframework.http.HttpEntity;
2221
import org.springframework.http.HttpMethod;
2322
import org.springframework.http.ResponseEntity;
24-
import org.springframework.lang.Nullable;
2523
import org.springframework.util.Assert;
2624
import org.springframework.vault.VaultException;
2725
import org.springframework.vault.client.VaultHttpHeaders;
@@ -35,7 +33,7 @@
3533
/**
3634
* Adapts tokens created by a {@link ClientAuthentication} to a {@link LoginToken}. Allows
3735
* decoration of a {@link ClientAuthentication} object to perform a self-lookup after
38-
* token retrieval to obtain the remaining TTL and renewability.
36+
* token retrieval to obtain the remaining TTL, renewability, accessor and token type.
3937
* <p>
4038
* Using this adapter decrements the usage counter for the created token.
4139
*
@@ -77,14 +75,7 @@ static LoginToken augmentWithSelfLookup(RestOperations restOperations, VaultToke
7775

7876
Map<String, Object> data = lookupSelf(restOperations, token);
7977

80-
Boolean renewable = (Boolean) data.get("renewable");
81-
Number ttl = (Number) data.get("ttl");
82-
83-
if (renewable != null && renewable) {
84-
return LoginToken.renewable(token.toCharArray(), getLeaseDuration(ttl));
85-
}
86-
87-
return LoginToken.of(token.toCharArray(), getLeaseDuration(ttl));
78+
return LoginTokenUtil.from(token.toCharArray(), data);
8879
}
8980

9081
private static Map<String, Object> lookupSelf(RestOperations restOperations, VaultToken token) {
@@ -106,8 +97,4 @@ private static Map<String, Object> lookupSelf(RestOperations restOperations, Vau
10697
}
10798
}
10899

109-
static Duration getLeaseDuration(@Nullable Number ttl) {
110-
return ttl == null ? Duration.ZERO : Duration.ofSeconds(ttl.longValue());
111-
}
112-
113100
}

spring-vault-core/src/main/java/org/springframework/vault/authentication/LoginTokenUtil.java

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.Map;
2020

2121
import org.springframework.util.Assert;
22+
import org.springframework.util.StringUtils;
2223

2324
/**
2425
* Utility class for {@link LoginToken}.
@@ -51,26 +52,43 @@ static LoginToken from(Map<String, Object> auth) {
5152
* @return the {@link LoginToken}
5253
* @since 2.0
5354
*/
54-
static LoginToken from(char[] token, Map<String, Object> auth) {
55+
static LoginToken from(char[] token, Map<String, ?> auth) {
5556

5657
Assert.notNull(auth, "Authentication must not be null");
5758

5859
Boolean renewable = (Boolean) auth.get("renewable");
5960
Number leaseDuration = (Number) auth.get("lease_duration");
61+
String accessor = (String) auth.get("accessor");
62+
String type = (String) auth.get("type");
6063

6164
if (leaseDuration == null) {
6265
leaseDuration = (Number) auth.get("ttl");
6366
}
6467

65-
if (renewable != null && renewable) {
66-
return LoginToken.renewable(token, Duration.ofSeconds(leaseDuration.longValue()));
68+
if (type == null) {
69+
type = (String) auth.get("token_type");
70+
}
71+
72+
LoginToken.LoginTokenBuilder builder = LoginToken.builder();
73+
builder.token(token);
74+
75+
if (StringUtils.hasText(accessor)) {
76+
builder.accessor(accessor);
6777
}
6878

6979
if (leaseDuration != null) {
70-
return LoginToken.of(token, Duration.ofSeconds(leaseDuration.longValue()));
80+
builder.leaseDuration(Duration.ofSeconds(leaseDuration.longValue()));
81+
}
82+
83+
if (renewable != null) {
84+
builder.renewable(renewable);
85+
}
86+
87+
if (StringUtils.hasText(type)) {
88+
builder.type(type);
7189
}
7290

73-
return LoginToken.of(token);
91+
return builder.build();
7492
}
7593

7694
}

0 commit comments

Comments
 (0)