Skip to content

Commit 0bc5513

Browse files
committed
Fix Lease revocation on SecretLeaseContainer destroy.
Closes gh-844
1 parent 47028c9 commit 0bc5513

File tree

2 files changed

+54
-7
lines changed

2 files changed

+54
-7
lines changed

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

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import java.util.concurrent.atomic.AtomicInteger;
3333
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
3434
import java.util.concurrent.atomic.AtomicReference;
35+
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
3536
import java.util.function.BiConsumer;
3637

3738
import org.apache.commons.logging.Log;
@@ -410,6 +411,9 @@ else if (isRotatingGenericSecret(requestedSecret, secrets)) {
410411
else if (renewalScheduler.isLeaseRotateOnly(lease, requestedSecret)) {
411412
scheduleLeaseRotation(requestedSecret, lease, renewalScheduler);
412413
}
414+
else if (lease.hasLeaseId()) {
415+
renewalScheduler.associateLease(lease);
416+
}
413417

414418
callback.accept(secrets, lease);
415419
}
@@ -502,11 +506,16 @@ public void destroy() throws Exception {
502506
for (Entry<RequestedSecret, LeaseRenewalScheduler> entry : this.renewals.entrySet()) {
503507

504508
Lease lease = entry.getValue().getLease();
509+
Lease previousLease = entry.getValue().getPreviousLease();
505510
entry.getValue().disableScheduleRenewal();
506511

507512
if (lease != null && lease.hasLeaseId()) {
508513
doRevokeLease(entry.getKey(), lease);
509514
}
515+
516+
if (previousLease != null && previousLease.hasLeaseId()) {
517+
doRevokeLease(entry.getKey(), previousLease);
518+
}
510519
}
511520
this.renewals.clear();
512521

@@ -844,12 +853,19 @@ protected void doRevokeLease(RequestedSecret requestedSecret, Lease lease) {
844853
*/
845854
static class LeaseRenewalScheduler {
846855

856+
private static final AtomicReferenceFieldUpdater<LeaseRenewalScheduler, Lease> CURRENT_UPDATER = AtomicReferenceFieldUpdater
857+
.newUpdater(LeaseRenewalScheduler.class, Lease.class, "currentLeaseRef");
858+
847859
@SuppressWarnings("FieldMayBeFinal") // allow setting via reflection.
848860
private static Log logger = LogFactory.getLog(LeaseRenewalScheduler.class);
849861

850862
private final TaskScheduler taskScheduler;
851863

852-
final AtomicReference<Lease> currentLeaseRef = new AtomicReference<>();
864+
@Nullable
865+
volatile Lease currentLeaseRef;
866+
867+
@Nullable
868+
volatile Lease previousLeaseRef;
853869

854870
final Map<Lease, ScheduledFuture<?>> schedules = new ConcurrentHashMap<>();
855871

@@ -884,8 +900,8 @@ void scheduleRenewal(RequestedSecret requestedSecret, RenewLease renewLease, Lea
884900
}
885901
}
886902

887-
Lease currentLease = this.currentLeaseRef.get();
888-
this.currentLeaseRef.set(lease);
903+
Lease currentLease = CURRENT_UPDATER.get(this);
904+
CURRENT_UPDATER.set(this, lease);
889905

890906
if (currentLease != null) {
891907
cancelSchedule(currentLease);
@@ -898,7 +914,7 @@ public void run() {
898914

899915
LeaseRenewalScheduler.this.schedules.remove(lease);
900916

901-
if (LeaseRenewalScheduler.this.currentLeaseRef.get() != lease) {
917+
if (CURRENT_UPDATER.get(LeaseRenewalScheduler.this) != lease) {
902918
logger.debug("Current lease has changed. Skipping renewal");
903919
return;
904920
}
@@ -918,7 +934,7 @@ public void run() {
918934
// Renew lease may call scheduleRenewal(…) with a different lease
919935
// Id to alter set up its own renewal schedule. If it's the old
920936
// lease, then renewLease() outcome controls the current LeaseId.
921-
LeaseRenewalScheduler.this.currentLeaseRef.compareAndSet(lease, renewLease.renewLease(lease));
937+
CURRENT_UPDATER.compareAndSet(LeaseRenewalScheduler.this, lease, renewLease.renewLease(lease));
922938
}
923939
catch (Exception e) {
924940
logger.error(String.format("Cannot renew lease %s", lease.getLeaseId()), e);
@@ -932,6 +948,10 @@ public void run() {
932948
this.schedules.put(lease, scheduledFuture);
933949
}
934950

951+
void associateLease(Lease lease) {
952+
CURRENT_UPDATER.set(this, lease);
953+
}
954+
935955
private void cancelSchedule(Lease lease) {
936956

937957
ScheduledFuture<?> scheduledFuture = this.schedules.get(lease);
@@ -951,7 +971,8 @@ private void cancelSchedule(Lease lease) {
951971
*/
952972
void disableScheduleRenewal() {
953973

954-
this.currentLeaseRef.set(null);
974+
// capture the previous lease to revoke it
975+
this.previousLeaseRef = CURRENT_UPDATER.getAndSet(this, null);
955976
Set<Lease> leases = new HashSet<>(this.schedules.keySet());
956977

957978
for (Lease lease : leases) {
@@ -984,7 +1005,12 @@ private boolean isLeaseRenewable(@Nullable Lease lease, RequestedSecret requeste
9841005

9851006
@Nullable
9861007
public Lease getLease() {
987-
return this.currentLeaseRef.get();
1008+
return CURRENT_UPDATER.get(this);
1009+
}
1010+
1011+
@Nullable
1012+
public Lease getPreviousLease() {
1013+
return this.previousLeaseRef;
9881014
}
9891015

9901016
private boolean isLeaseRotateOnly(Lease lease, RequestedSecret requestedSecret) {

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,27 @@ void shouldNotRevokeSecretsWithoutLease() throws Exception {
630630
verify(this.leaseListenerAdapter, never()).onLeaseEvent(any(AfterSecretLeaseRevocationEvent.class));
631631
}
632632

633+
@Test
634+
void shouldRevokeSecretsOnDestroy() throws Exception {
635+
636+
VaultResponse secrets = new VaultResponse();
637+
secrets.setData(Collections.singletonMap("key", (Object) "value"));
638+
secrets.setLeaseId("1234");
639+
secrets.setLeaseDuration(1000);
640+
641+
when(this.vaultOperations.read(this.requestedSecret.getPath())).thenReturn(secrets);
642+
643+
this.secretLeaseContainer.addRequestedSecret(this.requestedSecret);
644+
this.secretLeaseContainer.start();
645+
this.secretLeaseContainer.stop();
646+
647+
this.secretLeaseContainer.destroy();
648+
649+
verify(this.leaseListenerAdapter).onLeaseEvent(any(SecretLeaseCreatedEvent.class));
650+
verify(this.leaseListenerAdapter).onLeaseEvent(any(BeforeSecretLeaseRevocationEvent.class));
651+
verify(this.leaseListenerAdapter).onLeaseEvent(any(AfterSecretLeaseRevocationEvent.class));
652+
}
653+
633654
@Test
634655
void shouldRequestRotatingGenericSecrets() {
635656

0 commit comments

Comments
 (0)