Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,11 @@ if (hasProperty("publish")) {
id 'tledkov'
name 'Taras Ledkov'
email '[email protected]'
},
developer {
id 'henryx'
name 'Enrico Bianchi'
email '[email protected]'
}
]
}
Expand Down
25 changes: 11 additions & 14 deletions src/main/java/io/github/jopenlibs/vault/api/Leases.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@

/**
* <p>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).</p>
* Vault HTTP API docs (<a href="https://www.vaultproject.io/docs/http/index.html">
* https://www.vaultproject.io/docs/http/index.html</a>).</p>
*
* <p>This class is not intended to be constructed directly. Rather, it is meant to used by way of
* <code>Vault</code> in a DSL-style builder pattern. See the Javadoc comments of each
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -98,15 +95,15 @@ 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")
.connectTimeoutSeconds(config.getOpenTimeout())
.readTimeoutSeconds(config.getReadTimeout())
.sslVerification(config.getSslConfig().isVerify())
.sslContext(config.getSslConfig().getSslContext())
.put();
.post();

// Validate response
if (restResponse.getStatus() != 204) {
Expand Down Expand Up @@ -140,15 +137,15 @@ 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")
.connectTimeoutSeconds(config.getOpenTimeout())
.readTimeoutSeconds(config.getReadTimeout())
.sslVerification(config.getSslConfig().isVerify())
.sslContext(config.getSslConfig().getSslContext())
.put();
.post();

// Validate response
if (restResponse.getStatus() != 204) {
Expand Down Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
* <p>Integration tests for the basic (i.e. "sys") Vault API operations.</p>
* <p>Integration tests for the leases (i.e. "sys/leases") Vault API operations.</p>
*
* <p>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:</p>
*
* <p>https://github.com/hashicorp/vault/issues/877</p>
*
* <p>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.</p>
*
* <p>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.</p>
*
* 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();

Expand All @@ -40,29 +38,69 @@ public class LeasesTests {
@BeforeClass
public static void setupClass() throws IOException, InterruptedException {
container.initAndUnsealVault();
container.setupBackendDatabase(DbContainer.hostname);
}

@Before
public void setup() throws VaultException {
vault = container.getRootVault();
}

public DatabaseResponse generateCredentials() throws VaultException {
List<String> 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());
}
}