diff --git a/src/main/java/com/premiumminds/datagrip/vault/DynamicSecretValue.java b/src/main/java/com/premiumminds/datagrip/vault/DynamicSecretValue.java deleted file mode 100644 index ceea1a6..0000000 --- a/src/main/java/com/premiumminds/datagrip/vault/DynamicSecretValue.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.premiumminds.datagrip.vault; - -import java.time.Instant; - -public class DynamicSecretValue { - - private final Instant issueTime; - private final DynamicSecretResponse response; - - public DynamicSecretValue(Instant issueTime, DynamicSecretResponse response) { - this.issueTime = issueTime; - this.response = response; - } - - public Instant getIssueTime() { - return issueTime; - } - - public DynamicSecretResponse getResponse() { - return response; - } - - public Instant getExpireTime() { - return issueTime.plusSeconds(response.getLeaseDuration()); - } -} diff --git a/src/main/java/com/premiumminds/datagrip/vault/LeaseRequest.java b/src/main/java/com/premiumminds/datagrip/vault/LeaseRequest.java new file mode 100644 index 0000000..a617dcc --- /dev/null +++ b/src/main/java/com/premiumminds/datagrip/vault/LeaseRequest.java @@ -0,0 +1,14 @@ +package com.premiumminds.datagrip.vault; + +public class LeaseRequest { + + private String leaseId; + + public String getLeaseId() { + return leaseId; + } + + public void setLeaseId(String leaseId) { + this.leaseId = leaseId; + } +} diff --git a/src/main/java/com/premiumminds/datagrip/vault/LeaseResponse.java b/src/main/java/com/premiumminds/datagrip/vault/LeaseResponse.java new file mode 100644 index 0000000..871aca1 --- /dev/null +++ b/src/main/java/com/premiumminds/datagrip/vault/LeaseResponse.java @@ -0,0 +1,107 @@ +package com.premiumminds.datagrip.vault; + +public class LeaseResponse { + + public static class Data { + private String expireTime; + private String id; + private String issueTime; + private String lastRenewal; + private Boolean renewable; + private Long ttl; + + public String getExpireTime() { + return expireTime; + } + + public void setExpireTime(String expireTime) { + this.expireTime = expireTime; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getIssueTime() { + return issueTime; + } + + public void setIssueTime(String issueTime) { + this.issueTime = issueTime; + } + + public String getLastRenewal() { + return lastRenewal; + } + + public void setLastRenewal(String lastRenewal) { + this.lastRenewal = lastRenewal; + } + + public Boolean getRenewable() { + return renewable; + } + + public void setRenewable(Boolean renewable) { + this.renewable = renewable; + } + + public Long getTtl() { + return ttl; + } + + public void setTtl(Long ttl) { + this.ttl = ttl; + } + } + + private String requestId; + private String leaseId; + private Boolean renewable; + private Long leaseDuration; + private Data data; + + public String getRequestId() { + return requestId; + } + + public void setRequestId(String requestId) { + this.requestId = requestId; + } + + public String getLeaseId() { + return leaseId; + } + + public void setLeaseId(String leaseId) { + this.leaseId = leaseId; + } + + public Boolean getRenewable() { + return renewable; + } + + public void setRenewable(Boolean renewable) { + this.renewable = renewable; + } + + public Long getLeaseDuration() { + return leaseDuration; + } + + public void setLeaseDuration(Long leaseDuration) { + this.leaseDuration = leaseDuration; + } + + public Data getData() { + return data; + } + + public void setData(Data data) { + this.data = data; + } +} diff --git a/src/main/java/com/premiumminds/datagrip/vault/VaultDatabaseAuthProvider.java b/src/main/java/com/premiumminds/datagrip/vault/VaultDatabaseAuthProvider.java index d084a1d..0a65f48 100644 --- a/src/main/java/com/premiumminds/datagrip/vault/VaultDatabaseAuthProvider.java +++ b/src/main/java/com/premiumminds/datagrip/vault/VaultDatabaseAuthProvider.java @@ -14,8 +14,8 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; -import java.time.Instant; import java.util.Map; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.ConcurrentHashMap; @@ -57,7 +57,7 @@ public class VaultDatabaseAuthProvider implements DatabaseAuthProvider { .connectTimeout(Duration.ofSeconds(10)) .build(); - private static final Map secretsCache = new ConcurrentHashMap<>(); + private static final Map secretsCache = new ConcurrentHashMap<>(); @Override public @NonNls @NotNull String getId() { @@ -85,18 +85,27 @@ public boolean isApplicable(@NotNull LocalDataSource dataSource, @NotNull Applic logger.info("Secret used: " + secret); DynamicSecretKey key = new DynamicSecretKey(address, secret); - DynamicSecretValue value = secretsCache.get(key); + DynamicSecretResponse value = secretsCache.get(key); - if (value == null || value.getExpireTime().isBefore(Instant.now())) { + if (value == null){ final var response = getCredentialsFromVault(protoConnection, address, secret); - value = new DynamicSecretValue(Instant.now(), response); + value = response; secretsCache.put(key, value); + } else { + final var lease = getLeaseFromVault(protoConnection, address, value.getLeaseId()); + if (!lease.isPresent()) { + + final var response = getCredentialsFromVault(protoConnection, address, secret); + + value = response; + secretsCache.put(key, value); + } } - logger.info("Username used " + value.getResponse().getData().getUsername()); + logger.info("Username used " + value.getData().getUsername()); - protoConnection.getConnectionProperties().put("user", value.getResponse().getData().getUsername()); - protoConnection.getConnectionProperties().put("password", value.getResponse().getData().getPassword()); + protoConnection.getConnectionProperties().put("user", value.getData().getUsername()); + protoConnection.getConnectionProperties().put("password", value.getData().getPassword()); } catch (IOException | InterruptedException e) { throw new RuntimeException("Problem connecting to Vault: " + e.getMessage(), e); @@ -110,6 +119,40 @@ public boolean isApplicable(@NotNull LocalDataSource dataSource, @NotNull Applic return new VaultWidget(); } + + private Optional getLeaseFromVault( + ProtoConnection protoConnection, + final String address, + final String leaseId) + throws IOException, InterruptedException + { + final var token = getToken(protoConnection, address); + + final var uri = URI.create(address).resolve("/v1/sys/leases/lookup"); + + final var gson = new GsonBuilder() + .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) + .create(); + + final var leaseRequest = new LeaseRequest(); + leaseRequest.setLeaseId(leaseId); + + final var request = HttpRequest.newBuilder() + .POST(HttpRequest.BodyPublishers.ofString(gson.toJson(leaseRequest))) + .header("X-Vault-Token", token) + .uri(uri) + .build(); + + final var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + + if (response.statusCode() != HttpURLConnection.HTTP_OK) { + logger.info("No lease found for " + leaseId); + return Optional.empty(); + } + + return Optional.of(gson.fromJson(response.body(), LeaseResponse.class)); + } + private DynamicSecretResponse getCredentialsFromVault( ProtoConnection protoConnection, final String address,