Skip to content

Commit 867c56c

Browse files
committed
Add expiry Predicate to SecretLeaseContainer to determine whether a Lease is expired.
Closes gh-809
1 parent 9fd2e60 commit 867c56c

File tree

2 files changed

+77
-2
lines changed

2 files changed

+77
-2
lines changed

spring-vault-core/src/main/java/org/springframework/vault/core/lease/SecretLeaseContainer.java

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import java.util.concurrent.atomic.AtomicReference;
3535
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
3636
import java.util.function.BiConsumer;
37+
import java.util.function.Predicate;
3738

3839
import org.apache.commons.logging.Log;
3940
import org.apache.commons.logging.LogFactory;
@@ -127,6 +128,16 @@ public class SecretLeaseContainer extends SecretLeaseEventPublisher
127128
private static final AtomicIntegerFieldUpdater<SecretLeaseContainer> UPDATER = AtomicIntegerFieldUpdater
128129
.newUpdater(SecretLeaseContainer.class, "status");
129130

131+
/**
132+
* {@link Predicate} to test whether a {@link Lease} has no lease identifier.
133+
*/
134+
public static Predicate<Lease> NO_LEASE_ID = Predicate.not(Lease::hasLeaseId);
135+
136+
/**
137+
* {@link Predicate} to test whether a {@link Lease} has no lease identifier.
138+
*/
139+
public static Predicate<Lease> NO_LEASE_DURATION = forDuration(Duration::isZero);
140+
130141
private static final AtomicInteger poolId = new AtomicInteger();
131142

132143
private static final int STATUS_INITIAL = 0;
@@ -154,6 +165,10 @@ public class SecretLeaseContainer extends SecretLeaseEventPublisher
154165

155166
private Duration minRenewal = Duration.ofSeconds(10);
156167

168+
private @Nullable Predicate<Lease> isExpired;
169+
170+
private Predicate<Lease> isExpiredFallback = createIsExpiredPredicate(this.minRenewal);
171+
157172
private Duration expiryThreshold = Duration.ofSeconds(60);
158173

159174
private LeaseStrategy leaseStrategy = LeaseStrategy.dropOnError();
@@ -241,6 +256,20 @@ public void setMinRenewal(Duration minRenewal) {
241256
Assert.isTrue(!minRenewal.isNegative(), "Minimal renewal time must not be negative");
242257

243258
this.minRenewal = minRenewal;
259+
this.isExpiredFallback = createIsExpiredPredicate(this.minRenewal);
260+
}
261+
262+
/**
263+
* Sets the {@link Predicate} to determine whether a {@link Lease} is expired.
264+
* Defaults to comparing whether a lease {@link Lease#hasLeaseId() has no identifier},
265+
* its remaining TTL is zero or less or equal to {@code minRenewal}.
266+
* @since 3.2
267+
*/
268+
public void setExpiryPredicate(Predicate<Lease> isExpired) {
269+
270+
Assert.notNull(isExpired, "Expiry predicate must not be null");
271+
272+
this.isExpired = isExpired;
244273
}
245274

246275
/**
@@ -737,8 +766,7 @@ protected Lease doRenewLease(RequestedSecret requestedSecret, Lease lease) {
737766
try {
738767
Lease renewed = lease.hasLeaseId() ? doRenew(lease) : lease;
739768

740-
if (!renewed.hasLeaseId() || renewed.getLeaseDuration().isZero()
741-
|| renewed.getLeaseDuration().getSeconds() < this.minRenewal.getSeconds()) {
769+
if (isExpired(renewed)) {
742770

743771
onLeaseExpired(requestedSecret, lease);
744772
return Lease.none();
@@ -778,6 +806,10 @@ protected Lease doRenewLease(RequestedSecret requestedSecret, Lease lease) {
778806
}
779807
}
780808

809+
boolean isExpired(Lease lease) {
810+
return isExpired == null ? isExpiredFallback.test(lease) : isExpired.test(lease);
811+
}
812+
781813
@Nullable
782814
private HttpStatusCodeException potentiallyUnwrapHttpStatusCodeException(RuntimeException e) {
783815

@@ -857,6 +889,18 @@ protected void doRevokeLease(RequestedSecret requestedSecret, Lease lease) {
857889
}
858890
}
859891

892+
private Predicate<Lease> createIsExpiredPredicate(Duration minRenewal) {
893+
return NO_LEASE_ID.or(NO_LEASE_DURATION).or(forDuration(isLessOrEqual(minRenewal)));
894+
}
895+
896+
private static <T extends Comparable<T>> Predicate<T> isLessOrEqual(T other) {
897+
return it -> it.compareTo(other) <= 0;
898+
}
899+
900+
private static Predicate<Lease> forDuration(Predicate<Duration> predicate) {
901+
return lease -> predicate.test(lease.getLeaseDuration());
902+
}
903+
860904
/**
861905
* Abstracts scheduled lease renewal. A {@link LeaseRenewalScheduler} can be accessed
862906
* concurrently to schedule lease renewal. Each renewal run checks if the previously

spring-vault-core/src/test/java/org/springframework/vault/core/lease/SecretLeaseContainerUnitTests.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.util.concurrent.ScheduledFuture;
2626
import java.util.concurrent.TimeUnit;
2727
import java.util.concurrent.atomic.AtomicInteger;
28+
import java.util.function.Predicate;
2829

2930
import org.junit.jupiter.api.BeforeEach;
3031
import org.junit.jupiter.api.Test;
@@ -112,6 +113,36 @@ void shouldSetProperties() {
112113
assertThat(this.secretLeaseContainer.getExpiryThresholdSeconds()).isEqualTo(180);
113114
}
114115

116+
@Test
117+
void defaultIsExpiredShouldCalculateCorrectResult() {
118+
119+
assertThat((Predicate<Lease>) secretLeaseContainer::isExpired).accepts(Lease.none())
120+
.accepts(Lease.of("foo", Duration.ZERO, false))
121+
.accepts(Lease.fromTimeToLive(Duration.ofHours(1)))
122+
.accepts(Lease.of("foo", Duration.ofSeconds(9), false))
123+
.accepts(Lease.of("foo", Duration.ofSeconds(10), false))
124+
.rejects(Lease.of("foo", Duration.ofSeconds(11), false));
125+
126+
this.secretLeaseContainer.setMinRenewal(Duration.ofMinutes(2));
127+
128+
assertThat((Predicate<Lease>) secretLeaseContainer::isExpired).accepts(Lease.none())
129+
.accepts(Lease.of("foo", Duration.ZERO, false))
130+
.accepts(Lease.fromTimeToLive(Duration.ofHours(1)))
131+
.accepts(Lease.of("foo", Duration.ofSeconds(9), false))
132+
.accepts(Lease.of("foo", Duration.ofSeconds(120), false))
133+
.rejects(Lease.of("foo", Duration.ofSeconds(121), false));
134+
}
135+
136+
@Test
137+
void configuredExpiryPredicateShouldBeEvaluated() {
138+
139+
secretLeaseContainer.setExpiryPredicate(lease -> "expired".equals(lease.getLeaseId()));
140+
141+
assertThat((Predicate<Lease>) secretLeaseContainer::isExpired).rejects(Lease.none())
142+
.rejects(Lease.of("not-expired", Duration.ZERO, false))
143+
.accepts(Lease.of("expired", Duration.ZERO, false));
144+
}
145+
115146
@Test
116147
void shouldWorkIfNoSecretsRequested() {
117148

0 commit comments

Comments
 (0)