diff --git a/build.gradle b/build.gradle index 72ba1697..a5bef1bc 100644 --- a/build.gradle +++ b/build.gradle @@ -203,6 +203,11 @@ if (hasProperty("publish")) { id 'tledkov' name 'Taras Ledkov' email 'tledkov@apache.org' + }, + developer { + id 'henryx' + name 'Enrico Bianchi' + email 'enrico.bianchi@gmail.com' } ] } diff --git a/src/main/java/io/github/jopenlibs/vault/api/Leases.java b/src/main/java/io/github/jopenlibs/vault/api/Leases.java index 77cadd74..b9bbe360 100644 --- a/src/main/java/io/github/jopenlibs/vault/api/Leases.java +++ b/src/main/java/io/github/jopenlibs/vault/api/Leases.java @@ -11,7 +11,8 @@ /** *

The implementing class for operations on REST endpoints, under the "Leases" section of the - * Vault HTTP API docs (https://www.vaultproject.io/docs/http/index.html).

+ * Vault HTTP API docs ( + * https://www.vaultproject.io/docs/http/index.html).

* *

This class is not intended to be constructed directly. Rather, it is meant to used by way of * Vault in a DSL-style builder pattern. See the Javadoc comments of each @@ -51,22 +52,18 @@ public Leases withNameSpace(final String nameSpace) { */ public VaultResponse revoke(final String leaseId) throws VaultException { return retry(attempt -> { - /** - * 2019-03-21 - * Changed the Lease revoke url due to invalid path. Vault deprecated the original - * path (/v1/sys/revoke) in favor of a new leases mount point (/v1/sys/leases/revoke) - * https://github.com/hashicorp/vault/blob/master/CHANGELOG.md#080-august-9th-2017 - */ + final String requestJson = Json.object().add("lease_id", leaseId).toString(); final RestResponse restResponse = new Rest()//NOPMD - .url(config.getAddress() + "/v1/sys/leases/revoke/" + leaseId) + .url(config.getAddress() + "/v1/sys/leases/revoke") .header("X-Vault-Token", config.getToken()) .header("X-Vault-Namespace", this.nameSpace) .header("X-Vault-Request", "true") + .body(requestJson.getBytes(StandardCharsets.UTF_8)) .connectTimeoutSeconds(config.getOpenTimeout()) .readTimeoutSeconds(config.getReadTimeout()) .sslVerification(config.getSslConfig().isVerify()) .sslContext(config.getSslConfig().getSslContext()) - .put(); + .post(); // Validate response if (restResponse.getStatus() != 204) { @@ -98,7 +95,7 @@ public VaultResponse revoke(final String leaseId) throws VaultException { public VaultResponse revokePrefix(final String prefix) throws VaultException { return retry(attempt -> { final RestResponse restResponse = new Rest()//NOPMD - .url(config.getAddress() + "/v1/sys/revoke-prefix/" + prefix) + .url(config.getAddress() + "/v1/sys/leases/revoke-prefix/" + prefix) .header("X-Vault-Token", config.getToken()) .header("X-Vault-Namespace", this.nameSpace) .header("X-Vault-Request", "true") @@ -106,7 +103,7 @@ public VaultResponse revokePrefix(final String prefix) throws VaultException { .readTimeoutSeconds(config.getReadTimeout()) .sslVerification(config.getSslConfig().isVerify()) .sslContext(config.getSslConfig().getSslContext()) - .put(); + .post(); // Validate response if (restResponse.getStatus() != 204) { @@ -140,7 +137,7 @@ public VaultResponse revokePrefix(final String prefix) throws VaultException { public VaultResponse revokeForce(final String prefix) throws VaultException { return retry(attempt -> { final RestResponse restResponse = new Rest()//NOPMD - .url(config.getAddress() + "/v1/sys/revoke-force/" + prefix) + .url(config.getAddress() + "/v1/sys/leases/revoke-force/" + prefix) .header("X-Vault-Token", config.getToken()) .header("X-Vault-Namespace", this.nameSpace) .header("X-Vault-Request", "true") @@ -148,7 +145,7 @@ public VaultResponse revokeForce(final String prefix) throws VaultException { .readTimeoutSeconds(config.getReadTimeout()) .sslVerification(config.getSslConfig().isVerify()) .sslContext(config.getSslConfig().getSslContext()) - .put(); + .post(); // Validate response if (restResponse.getStatus() != 204) { @@ -188,7 +185,7 @@ public VaultResponse renew(final String leaseId, final long increment) throws Va return retry(attempt -> { final String requestJson = Json.object().add("increment", increment).toString(); final RestResponse restResponse = new Rest()//NOPMD - .url(config.getAddress() + "/v1/sys/renew/" + leaseId) + .url(config.getAddress() + "/v1/sys/leases/renew/" + leaseId) .header("X-Vault-Token", config.getToken()) .header("X-Vault-Namespace", this.nameSpace) .header("X-Vault-Request", "true") diff --git a/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/LeasesTests.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/LeasesTests.java index 4578cf4a..f8cc6c07 100644 --- a/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/LeasesTests.java +++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/LeasesTests.java @@ -2,36 +2,34 @@ import io.github.jopenlibs.vault.Vault; import io.github.jopenlibs.vault.VaultException; +import io.github.jopenlibs.vault.api.database.DatabaseRoleOptions; +import io.github.jopenlibs.vault.response.DatabaseResponse; import io.github.jopenlibs.vault.response.VaultResponse; +import io.github.jopenlibs.vault.v1_11_4.util.DbContainer; import io.github.jopenlibs.vault.v1_11_4.util.VaultContainer; import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import org.junit.Before; import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Test; import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertTrue; /** - *

Integration tests for the basic (i.e. "sys") Vault API operations.

+ *

Integration tests for the leases (i.e. "sys/leases") Vault API operations.

* - *

Unfortunately, it's not really possible to fully test these endpoints with the current integration testing - * strategy. The "generic" backend, used by the dev server, does not issue leases at all. You CAN obtain leases - * by using the PKI backend, but those aren't renewable:

- * - *

https://github.com/hashicorp/vault/issues/877

- * - *

Therefore, these revocation tests are basically just testing that the Vault server returns a 204 status - * code. Which isn't much of a test, since Vault routinely returns 204 even if you pass a non-existent lease ID.

- * - *

In the future, we may be shifting to an integration testing approach that uses a "real" Vault server - * instance, running in a Docker container (see: https://github.com/BetterCloud/vault-java-driver/pull/25). At - * that time, these tests should be re-visited and better implemented.

- * - * TODO: Revisit, now that we're using testcontainers + * According to the Vault documentation, it is possible to use a dynamic secret like database to + * test these methods */ public class LeasesTests { + @ClassRule + public static final DbContainer dbContainer = new DbContainer(); + @ClassRule public static final VaultContainer container = new VaultContainer(); @@ -40,6 +38,7 @@ public class LeasesTests { @BeforeClass public static void setupClass() throws IOException, InterruptedException { container.initAndUnsealVault(); + container.setupBackendDatabase(DbContainer.hostname); } @Before @@ -47,22 +46,61 @@ public void setup() throws VaultException { vault = container.getRootVault(); } + public DatabaseResponse generateCredentials() throws VaultException { + List creationStatements = new ArrayList<>(); + creationStatements.add( + "CREATE USER \"{{name}}\" WITH PASSWORD '{{password}}';" + + "GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";"); + + DatabaseResponse databaseResponse = vault.database().createOrUpdateRole("new-role", + new DatabaseRoleOptions().dbName("postgres") + .creationStatements(creationStatements)); + assertEquals(204, databaseResponse.getRestResponse().getStatus()); + + DatabaseResponse credsResponse = vault.database().creds("new-role"); + assertEquals(200, credsResponse.getRestResponse().getStatus()); + + assertTrue(credsResponse.getCredential().getUsername().contains("new-role")); + + return credsResponse; + } + @Test public void testRevoke() throws VaultException { - final VaultResponse response = vault.leases().revoke("sys/revoke-prefix/dummy"); + DatabaseResponse credsResponse = this.generateCredentials(); + + final VaultResponse response = vault.leases().revoke(credsResponse.getLeaseId()); assertEquals(204, response.getRestResponse().getStatus()); } @Test public void testRevokePrefix() throws VaultException { - final VaultResponse response = vault.leases().revokePrefix("sys/revoke-prefix/dummy"); + DatabaseResponse credsResponse = this.generateCredentials(); + + String prefix = Arrays.stream(credsResponse.getLeaseId().split("([^/]+)$")) + .map(str -> str.substring(0, str.length() - 1)).findFirst().get(); + + final VaultResponse response = vault.leases().revokePrefix(prefix); assertEquals(204, response.getRestResponse().getStatus()); } @Test public void testRevokeForce() throws VaultException { - final VaultResponse response = vault.leases().revokeForce("sys/revoke-prefix/dummy"); + DatabaseResponse credsResponse = this.generateCredentials(); + + String prefix = Arrays.stream(credsResponse.getLeaseId().split("([^/]+)$")) + .map(str -> str.substring(0, str.length() - 1)).findFirst().get(); + + final VaultResponse response = vault.leases().revokeForce(prefix); assertEquals(204, response.getRestResponse().getStatus()); } + @Test + public void testRenew() throws VaultException { + DatabaseResponse credsResponse = this.generateCredentials(); + + final VaultResponse response = vault.leases().renew(credsResponse.getLeaseId(), + credsResponse.getLeaseDuration()); + assertEquals(200, response.getRestResponse().getStatus()); + } }