From 27d81a6bd7fb8ff54e200f469963901370b99c7b Mon Sep 17 00:00:00 2001
From: Taras Ledkov
Date: Sun, 9 Oct 2022 18:44:41 +0300
Subject: [PATCH] VJD-3 Fix parse Auth.unwrap response without auth data, add
Auth.wrap method (#4)
---
.travis.yml | 3 -
build.gradle | 34 +-
.../io/github/jopenlibs/vault/api/Auth.java | 197 ++++++--
.../jopenlibs/vault/api/LogicalUtilities.java | 7 +-
.../github/jopenlibs/vault/api/pki/Pki.java | 4 +-
.../vault/response/AuthResponse.java | 50 +-
.../vault/response/LogicalResponse.java | 1 -
.../vault/response/UnwrapResponse.java | 25 +
.../vault/response/WrapResponse.java | 98 ++++
.../v1_11_4/api/AuthBackendAppIdTests.java | 43 ++
.../v1_11_4/api/AuthBackendAppRoleTests.java | 56 +++
.../v1_11_4/api/AuthBackendCertTests.java | 88 ++++
.../v1_11_4/api/AuthBackendDatabaseTests.java | 107 +++++
.../v1_11_4/api/AuthBackendPkiTests.java | 205 ++++++++
.../v1_11_4/api/AuthBackendTokenTests.java | 143 ++++++
.../v1_11_4/api/AuthBackendUserPassTests.java | 42 ++
.../vault/v1_11_4/api/DebugTests.java | 78 +++
.../vault/v1_11_4/api/LeasesTests.java | 68 +++
.../vault/v1_11_4/api/LogicalTests.java | 447 ++++++++++++++++++
.../vault/v1_11_4/api/MountsTests.java | 199 ++++++++
.../vault/v1_11_4/api/SealTests.java | 56 +++
.../vault/v1_11_4/api/VaultAgentTests.java | 71 +++
.../vault/v1_11_4/api/WrapUnwrapTests.java | 58 +++
.../vault/v1_11_4/util/DbContainer.java | 34 ++
.../vault/v1_11_4/util/SSLUtils.java | 293 ++++++++++++
.../vault/v1_11_4/util/TestConstants.java | 40 ++
.../v1_11_4/util/VaultAgentContainer.java | 78 +++
.../vault/v1_11_4/util/VaultContainer.java | 368 ++++++++++++++
.../api/AuthBackendAppIdTests.java | 4 +-
.../api/AuthBackendAppRoleTests.java | 4 +-
.../api/AuthBackendCertTests.java | 8 +-
.../api/AuthBackendDatabaseTests.java | 6 +-
.../{ => v1_1_3}/api/AuthBackendPkiTests.java | 6 +-
.../api/AuthBackendTokenTests.java | 13 +-
.../api/AuthBackendUserPassTests.java | 4 +-
.../vault/{ => v1_1_3}/api/DebugTests.java | 4 +-
.../vault/{ => v1_1_3}/api/LeasesTests.java | 4 +-
.../vault/{ => v1_1_3}/api/LogicalTests.java | 4 +-
.../vault/{ => v1_1_3}/api/MountsTests.java | 4 +-
.../vault/{ => v1_1_3}/api/SealTests.java | 4 +-
.../{ => v1_1_3}/api/VaultAgentTests.java | 6 +-
.../vault/v1_1_3/api/WrapUnwrapTests.java | 58 +++
.../vault/{ => v1_1_3}/util/DbContainer.java | 2 +-
.../vault/{ => v1_1_3}/util/SSLUtils.java | 4 +-
.../{ => v1_1_3}/util/TestConstants.java | 3 +-
.../util/VaultAgentContainer.java | 2 +-
.../{ => v1_1_3}/util/VaultContainer.java | 2 +-
src/test-integration/resources/startup.sh | 7 +-
.../AuthUnwrapWithoutAuthResponseTest.java | 81 ++++
.../vault/vault/api/AuthWrapTest.java | 67 +++
50 files changed, 3070 insertions(+), 120 deletions(-)
create mode 100644 src/main/java/io/github/jopenlibs/vault/response/UnwrapResponse.java
create mode 100644 src/main/java/io/github/jopenlibs/vault/response/WrapResponse.java
create mode 100644 src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/AuthBackendAppIdTests.java
create mode 100644 src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/AuthBackendAppRoleTests.java
create mode 100644 src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/AuthBackendCertTests.java
create mode 100644 src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/AuthBackendDatabaseTests.java
create mode 100644 src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/AuthBackendPkiTests.java
create mode 100644 src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/AuthBackendTokenTests.java
create mode 100644 src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/AuthBackendUserPassTests.java
create mode 100644 src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/DebugTests.java
create mode 100644 src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/LeasesTests.java
create mode 100644 src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/LogicalTests.java
create mode 100644 src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/MountsTests.java
create mode 100644 src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/SealTests.java
create mode 100644 src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/VaultAgentTests.java
create mode 100644 src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/WrapUnwrapTests.java
create mode 100644 src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/util/DbContainer.java
create mode 100644 src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/util/SSLUtils.java
create mode 100644 src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/util/TestConstants.java
create mode 100644 src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/util/VaultAgentContainer.java
create mode 100644 src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/util/VaultContainer.java
rename src/test-integration/java/io/github/jopenlibs/vault/{ => v1_1_3}/api/AuthBackendAppIdTests.java (91%)
rename src/test-integration/java/io/github/jopenlibs/vault/{ => v1_1_3}/api/AuthBackendAppRoleTests.java (94%)
rename src/test-integration/java/io/github/jopenlibs/vault/{ => v1_1_3}/api/AuthBackendCertTests.java (93%)
rename src/test-integration/java/io/github/jopenlibs/vault/{ => v1_1_3}/api/AuthBackendDatabaseTests.java (96%)
rename src/test-integration/java/io/github/jopenlibs/vault/{ => v1_1_3}/api/AuthBackendPkiTests.java (98%)
rename src/test-integration/java/io/github/jopenlibs/vault/{ => v1_1_3}/api/AuthBackendTokenTests.java (94%)
rename src/test-integration/java/io/github/jopenlibs/vault/{ => v1_1_3}/api/AuthBackendUserPassTests.java (91%)
rename src/test-integration/java/io/github/jopenlibs/vault/{ => v1_1_3}/api/DebugTests.java (96%)
rename src/test-integration/java/io/github/jopenlibs/vault/{ => v1_1_3}/api/LeasesTests.java (95%)
rename src/test-integration/java/io/github/jopenlibs/vault/{ => v1_1_3}/api/LogicalTests.java (99%)
rename src/test-integration/java/io/github/jopenlibs/vault/{ => v1_1_3}/api/MountsTests.java (98%)
rename src/test-integration/java/io/github/jopenlibs/vault/{ => v1_1_3}/api/SealTests.java (94%)
rename src/test-integration/java/io/github/jopenlibs/vault/{ => v1_1_3}/api/VaultAgentTests.java (93%)
create mode 100644 src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/WrapUnwrapTests.java
rename src/test-integration/java/io/github/jopenlibs/vault/{ => v1_1_3}/util/DbContainer.java (96%)
rename src/test-integration/java/io/github/jopenlibs/vault/{ => v1_1_3}/util/SSLUtils.java (99%)
rename src/test-integration/java/io/github/jopenlibs/vault/{ => v1_1_3}/util/TestConstants.java (97%)
rename src/test-integration/java/io/github/jopenlibs/vault/{ => v1_1_3}/util/VaultAgentContainer.java (98%)
rename src/test-integration/java/io/github/jopenlibs/vault/{ => v1_1_3}/util/VaultContainer.java (99%)
create mode 100644 src/test/java/io/github/jopenlibs/vault/vault/api/AuthUnwrapWithoutAuthResponseTest.java
create mode 100644 src/test/java/io/github/jopenlibs/vault/vault/api/AuthWrapTest.java
diff --git a/.travis.yml b/.travis.yml
index df548f09..1c5058e3 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,6 +25,3 @@ script:
notifications:
email: false
-
-after-script:
- curl -s curl -s https://raw.githubusercontent.com/monperrus/gmtft/master/give-me-the-failing-tests.py | python
diff --git a/build.gradle b/build.gradle
index a27040bb..6f9bfbca 100644
--- a/build.gradle
+++ b/build.gradle
@@ -67,7 +67,6 @@ task compileModuleInfoJava(type: JavaCompile) {
compileModuleInfoJava.dependsOn compileJava
classes.dependsOn compileModuleInfoJava
-
// End of Java 9 compatibility config
@@ -137,15 +136,6 @@ if (!hasProperty('ossrhUsername')) {
if (!hasProperty('ossrhPassword')) {
ext.ossrhPassword = ''
}
-if (!hasProperty('nexusUrl')) {
- ext.nexusUrl = ''
-}
-if (!hasProperty('nexusUsername')) {
- ext.nexusUsername = ''
-}
-if (!hasProperty('nexusPassword')) {
- ext.nexusPassword = ''
-}
artifacts {
archives javadocJar, sourcesJar
@@ -161,15 +151,12 @@ uploadArchives {
mavenDeployer {
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
- repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") {
+ repository(url: "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/") {
authentication(userName: ossrhUsername, password: ossrhPassword)
}
-// snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") {
-// authentication(userName: ossrhUsername, password: ossrhPassword)
-// }
- snapshotRepository(url: nexusUrl) {
- authentication(userName: nexusUsername, password: nexusPassword)
+ snapshotRepository(url: "https://s01.oss.sonatype.org/content/repositories/snapshots/") {
+ authentication(userName: ossrhUsername, password: ossrhPassword)
}
pom.project {
@@ -177,18 +164,18 @@ uploadArchives {
packaging 'jar'
// optionally artifactId can be defined here
description 'Zero-dependency Java client for HashiCorp\'s Vault'
- url 'https://github.com/BetterCloud/vault-java-driver'
+ url 'https://github.com/jopenlibs/vault-java-driver'
scm {
- connection 'https://github.com/BetterCloud/vault-java-driver.git'
- developerConnection 'https://github.com/BetterCloud/vault-java-driver.git'
- url 'https://github.com/BetterCloud/vault-java-driver'
+ connection 'https://github.com/jopenlibs/vault-java-driver.git'
+ developerConnection 'https://github.com/jopenlibs/vault-java-driver.git'
+ url 'https://github.com/jopenlibs/vault-java-driver'
}
licenses {
license {
name 'MIT'
- url 'https://github.com/BetterCloud/vault-java-driver/blob/master/README.md'
+ url 'https://github.com/jopenlibs/vault-java-driver/blob/master/README.md'
}
}
@@ -207,6 +194,11 @@ uploadArchives {
id 'jarrodcodes'
name 'Jarrod Young'
email 'jarrodsy@gmail.com'
+ },
+ developer {
+ id 'tledkov'
+ name 'Taras Ledkov'
+ email 'tledkov@apache.org'
}
]}
}
diff --git a/src/main/java/io/github/jopenlibs/vault/api/Auth.java b/src/main/java/io/github/jopenlibs/vault/api/Auth.java
index 9637b7e6..90818d4d 100644
--- a/src/main/java/io/github/jopenlibs/vault/api/Auth.java
+++ b/src/main/java/io/github/jopenlibs/vault/api/Auth.java
@@ -8,12 +8,15 @@
import io.github.jopenlibs.vault.response.AuthResponse;
import io.github.jopenlibs.vault.response.LogicalResponse;
import io.github.jopenlibs.vault.response.LookupResponse;
+import io.github.jopenlibs.vault.response.UnwrapResponse;
+import io.github.jopenlibs.vault.response.WrapResponse;
import io.github.jopenlibs.vault.rest.Rest;
import io.github.jopenlibs.vault.rest.RestResponse;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.UUID;
@@ -308,36 +311,39 @@ public AuthResponse createToken(final TokenRequest tokenRequest, final String to
// Parse parameters to JSON
final JsonObject jsonObject = Json.object();
- if (tokenRequest.id != null) jsonObject.add("id", tokenRequest.id.toString());
- if (tokenRequest.polices != null && !tokenRequest.polices.isEmpty()) {
- jsonObject.add("policies", Json.array(tokenRequest.polices.toArray(new String[tokenRequest.polices.size()])));//NOPMD
+ if (tokenRequest.getId() != null) jsonObject.add("id", tokenRequest.getId().toString());
+ if (tokenRequest.getPolices() != null && !tokenRequest.getPolices().isEmpty()) {
+ jsonObject.add(
+ "policies",
+ Json.array(tokenRequest.getPolices().toArray(new String[0]))
+ ); //NOPMD
}
- if (tokenRequest.meta != null && !tokenRequest.meta.isEmpty()) {
+ if (tokenRequest.getMeta() != null && !tokenRequest.getMeta().isEmpty()) {
final JsonObject metaMap = Json.object();
- for (final Map.Entry entry : tokenRequest.meta.entrySet()) {
+ for (final Map.Entry entry : tokenRequest.getMeta().entrySet()) {
metaMap.add(entry.getKey(), entry.getValue());
}
jsonObject.add("meta", metaMap);
}
- if (tokenRequest.noParent != null) jsonObject.add("no_parent", tokenRequest.noParent);
- if (tokenRequest.noDefaultPolicy != null)
- jsonObject.add("no_default_policy", tokenRequest.noDefaultPolicy);
- if (tokenRequest.ttl != null) jsonObject.add("ttl", tokenRequest.ttl);
- if (tokenRequest.displayName != null) jsonObject.add("display_name", tokenRequest.displayName);
- if (tokenRequest.numUses != null) jsonObject.add("num_uses", tokenRequest.numUses);
- if (tokenRequest.renewable != null) jsonObject.add("renewable", tokenRequest.renewable);
- if (tokenRequest.type != null) jsonObject.add("type", tokenRequest.type);
- if (tokenRequest.explicitMaxTtl != null) jsonObject.add("explicit_max_ttl", tokenRequest.explicitMaxTtl);
- if (tokenRequest.period != null) jsonObject.add("period", tokenRequest.period);
- if (tokenRequest.entityAlias != null) jsonObject.add("entity_alias", tokenRequest.entityAlias);
+ if (tokenRequest.getNoParent() != null) jsonObject.add("no_parent", tokenRequest.getNoParent());
+ if (tokenRequest.getNoDefaultPolicy() != null)
+ jsonObject.add("no_default_policy", tokenRequest.getNoDefaultPolicy());
+ if (tokenRequest.getTtl() != null) jsonObject.add("ttl", tokenRequest.getTtl());
+ if (tokenRequest.getDisplayName() != null) jsonObject.add("display_name", tokenRequest.getDisplayName());
+ if (tokenRequest.getNumUses() != null) jsonObject.add("num_uses", tokenRequest.getNumUses());
+ if (tokenRequest.getRenewable() != null) jsonObject.add("renewable", tokenRequest.getRenewable());
+ if (tokenRequest.getType() != null) jsonObject.add("type", tokenRequest.getType());
+ if (tokenRequest.getExplicitMaxTtl() != null) jsonObject.add("explicit_max_ttl", tokenRequest.getExplicitMaxTtl());
+ if (tokenRequest.getPeriod() != null) jsonObject.add("period", tokenRequest.getPeriod());
+ if (tokenRequest.getEntityAlias() != null) jsonObject.add("entity_alias", tokenRequest.getEntityAlias());
final String requestJson = jsonObject.toString();
final StringBuilder urlBuilder = new StringBuilder(config.getAddress())//NOPMD
.append("/v1/auth/")
.append(mount)
.append("/create");
- if (tokenRequest.role != null) {
- urlBuilder.append("/").append(tokenRequest.role);
+ if (tokenRequest.getRole() != null) {
+ urlBuilder.append("/").append(tokenRequest.getRole());
}
final String url = urlBuilder.toString();
@@ -1502,18 +1508,27 @@ public void revokeSelf(final String tokenAuthMount) throws VaultException {
* @throws VaultException If any error occurs, or unexpected response received from Vault
* @see #unwrap(String)
*/
- public AuthResponse unwrap() throws VaultException {
+ public UnwrapResponse unwrap() throws VaultException {
return unwrap(null);
}
/**
- * Returns the original response inside the given wrapped auth token. This method is useful if you need to unwrap
- * a token, while being already authenticated. Do NOT authenticate in vault with your wrapping token, since it will
- * both fail authentication and invalidate the wrapping token at the same time. See {@link #unwrap()} if you need to
- * do that without being authenticated.
+ * Provide access to the {@code /sys/wrapping/unwrap} endpoint.
+ *
+ * Returns the original response inside the given wrapping token. Unlike simply reading
+ * {@code cubbyhole/response} (which is deprecated), this endpoint provides additional
+ * validation checks on the token, returns the original value on the wire rather than
+ * a JSON string representation of it, and ensures that the response is properly audit-logged.
+ *
+ * This endpoint can be used by using a wrapping token as the client token in the API call,
+ * in which case the token parameter is not required; or, a different token with permissions
+ * to access this endpoint can make the call and pass in the wrapping token in
+ * the token parameter. Do not use the wrapping token in both locations;
+ * this will cause the wrapping token to be revoked but the value to be unable to be looked up,
+ * as it will basically be a double-use of the token!
*
* In the example below, {@code authToken} is NOT your wrapped token, and should have unwrapping permissions.
- * The unwrapped token in {@code unwrappedToken}. Example usage:
+ * The unwrapped data in {@link UnwrapResponse#getData()}. Example usage:
*
*
* {@code
@@ -1521,18 +1536,30 @@ public AuthResponse unwrap() throws VaultException {
* final String wrappingToken = "...";
* final VaultConfig config = new VaultConfig().address(...).token(authToken).build();
* final Vault vault = new Vault(config);
- * final AuthResponse response = vault.auth().unwrap(wrappingToken);
- * final String unwrappedToken = response.getAuthClientToken();
+ *
+ * final WrapResponse wrapResponse = vault.auth().wrap(
+ * // Data to wrap
+ * new JsonObject()
+ * .add("foo", "bar")
+ * .add("zoo", "zar"),
+ *
+ * // TTL of the response-wrapping token
+ * 60
+ * );
+ *
+ * final UnwrapResponse unwrapResponse = vault.auth().unwrap(wrapResponse.getToken());
+ * final JsonObject unwrappedData = response.getData(); // original data
* }
*
*
- * @param wrappedToken Specifies the wrapping token ID, do NOT also put this in your {@link VaultConfig#token},
- * if token is {@code null}, this method will unwrap the auth token in {@link VaultConfig#token}
+ * @param wrappedToken Specifies the wrapping token ID, do NOT also put this in your {@link VaultConfig#getToken()},
+ * if token is {@code null}, this method will unwrap the auth token in {@link VaultConfig#getToken()}
* @return The response information returned from Vault
* @throws VaultException If any error occurs, or unexpected response received from Vault
+ * @see #wrap(JsonObject, int)
* @see #unwrap()
*/
- public AuthResponse unwrap(final String wrappedToken) throws VaultException {
+ public UnwrapResponse unwrap(final String wrappedToken) throws VaultException {
int retryCount = 0;
while (true) {
try {
@@ -1567,7 +1594,7 @@ public AuthResponse unwrap(final String wrappedToken) throws VaultException {
if (!mimeType.equals("application/json")) {
throw new VaultException("Vault responded with MIME type: " + mimeType, restResponse.getStatus());
}
- return new AuthResponse(restResponse, retryCount);
+ return new UnwrapResponse(restResponse, retryCount);
} catch (final Exception e) {
// If there are retries to perform, then pause for the configured interval and then execute the
// loop again...
@@ -1589,4 +1616,114 @@ public AuthResponse unwrap(final String wrappedToken) throws VaultException {
}
}
+ /**
+ * Provide access to the {@code /sys/wrapping/wrap} endpoint.
+ *
+ * This provides a powerful mechanism for information sharing in many environments.
+ * In the types of scenarios, often the best practical option is to provide cover
+ * for the secret information, be able to detect malfeasance (interception, tampering),
+ * and limit lifetime of the secret's exposure.
+ * Response wrapping performs all three of these duties:
+ *
+ *
+ * - It provides cover by ensuring that the value being transmitted across the wire is
+ * not the actual secret but a reference to such a secret, namely the response-wrapping token.
+ * Information stored in logs or captured along the way do not directly see the sensitive information.
+ *
+ * - It provides malfeasance detection by ensuring that only a single party can ever
+ * unwrap the token and see what's inside. A client receiving a token that cannot be unwrapped
+ * can trigger an immediate security incident. In addition, a client can inspect
+ * a given token before unwrapping to ensure that its origin is from the expected
+ * location in Vault.
+ *
+ * - It limits the lifetime of secret exposure because the response-wrapping token has
+ * a lifetime that is separate from the wrapped secret (and often can be much shorter),
+ * so if a client fails to come up and unwrap the token, the token can expire very quickly.
+ *
+ *
+ *
+ *
+ * {@code
+ * final String authToken = "...";
+ * final String wrappingToken = "...";
+ * final VaultConfig config = new VaultConfig().address(...).token(authToken).build();
+ * final Vault vault = new Vault(config);
+ *
+ * final WrapResponse wrapResponse = vault.auth().wrap(
+ * // Data to wrap
+ * new JsonObject()
+ * .add("foo", "bar")
+ * .add("zoo", "zar"),
+ *
+ * // TTL of the response-wrapping token
+ * 60
+ * );
+ *
+ * final UnwrapResponse unwrapResponse = vault.auth().unwrap(wrapResponse.getToken());
+ * final JsonObject unwrappedData = response.getData(); // original data
+ * }
+ *
+ *
+ * @param jsonObject User data to wrap.
+ * @param ttlInSec Wrap TTL in seconds
+ * @return The response information returned from Vault
+ * @throws VaultException If any error occurs, or unexpected response received from Vault
+ * @see #unwrap(String)
+ */
+ public WrapResponse wrap(final JsonObject jsonObject, int ttlInSec) throws VaultException {
+ Objects.requireNonNull(jsonObject);
+
+ int retryCount = 0;
+ while (true) {
+ try {
+ // Parse parameters to JSON
+ final String requestJson = jsonObject.toString();
+ final String url = config.getAddress() + "/v1/sys/wrapping/wrap";
+
+ // HTTP request to Vault
+ final RestResponse restResponse = new Rest()
+ .url(url)
+ .header("X-Vault-Token", config.getToken())
+ .header("X-Vault-Wrap-TTL", Integer.toString(ttlInSec))
+ .header("X-Vault-Namespace", this.nameSpace)
+ .body(requestJson.getBytes(StandardCharsets.UTF_8))
+ .connectTimeoutSeconds(config.getOpenTimeout())
+ .readTimeoutSeconds(config.getReadTimeout())
+ .sslVerification(config.getSslConfig().isVerify())
+ .sslContext(config.getSslConfig().getSslContext())
+ .post();
+
+ // Validate restResponse
+ if (restResponse.getStatus() != 200) {
+ throw new VaultException("Vault responded with HTTP status code: " + restResponse.getStatus()
+ + "\nResponse body: " + new String(restResponse.getBody(), StandardCharsets.UTF_8),
+ restResponse.getStatus());
+ }
+
+ final String mimeType = restResponse.getMimeType() == null ? "null" : restResponse.getMimeType();
+ if (!mimeType.equals("application/json")) {
+ throw new VaultException("Vault responded with MIME type: " + mimeType, restResponse.getStatus());
+ }
+
+ return new WrapResponse(restResponse, retryCount);
+ } catch (final Exception e) {
+ // If there are retries to perform, then pause for the configured interval and then execute the
+ // loop again...
+ if (retryCount < config.getMaxRetries()) {
+ retryCount++;
+ try {
+ final int retryIntervalMilliseconds = config.getRetryIntervalMilliseconds();
+ Thread.sleep(retryIntervalMilliseconds);
+ } catch (InterruptedException e1) {
+ e1.printStackTrace();
+ }
+ } else if (e instanceof VaultException) {
+ // ... otherwise, give up.
+ throw (VaultException) e;
+ } else {
+ throw new VaultException(e);
+ }
+ }
+ }
+ }
}
diff --git a/src/main/java/io/github/jopenlibs/vault/api/LogicalUtilities.java b/src/main/java/io/github/jopenlibs/vault/api/LogicalUtilities.java
index 50a81f42..cb7f2be5 100644
--- a/src/main/java/io/github/jopenlibs/vault/api/LogicalUtilities.java
+++ b/src/main/java/io/github/jopenlibs/vault/api/LogicalUtilities.java
@@ -7,7 +7,12 @@
public class LogicalUtilities {
-
+ /**
+ * Prevent creation an instance of a utility class.
+ */
+ private LogicalUtilities() {
+ // No-op.
+ }
/**
* Convenience method to split a Vault path into its path segments.
*
diff --git a/src/main/java/io/github/jopenlibs/vault/api/pki/Pki.java b/src/main/java/io/github/jopenlibs/vault/api/pki/Pki.java
index 0cd32fab..a8d35baa 100644
--- a/src/main/java/io/github/jopenlibs/vault/api/pki/Pki.java
+++ b/src/main/java/io/github/jopenlibs/vault/api/pki/Pki.java
@@ -124,9 +124,11 @@ public PkiResponse createOrUpdateRole(final String roleName, final RoleOptions o
.post();
// Validate restResponse
- if (restResponse.getStatus() != 204) {
+ // TODO: handle warnings
+ if (restResponse.getStatus() != 204 && restResponse.getStatus() != 200) {
throw new VaultException("Vault responded with HTTP status code: " + restResponse.getStatus(), restResponse.getStatus());
}
+
return new PkiResponse(restResponse, retryCount);
} catch (Exception e) {
// If there are retries to perform, then pause for the configured interval and then execute the loop again...
diff --git a/src/main/java/io/github/jopenlibs/vault/response/AuthResponse.java b/src/main/java/io/github/jopenlibs/vault/response/AuthResponse.java
index 2c9ef5dd..d1ef4aed 100644
--- a/src/main/java/io/github/jopenlibs/vault/response/AuthResponse.java
+++ b/src/main/java/io/github/jopenlibs/vault/response/AuthResponse.java
@@ -14,7 +14,6 @@
* This class is a container for the information returned by Vault in auth backend operations.
*/
public class AuthResponse extends VaultResponse {
-
private Boolean renewable;
private String authClientToken;
private String tokenAccessor;
@@ -26,6 +25,8 @@ public class AuthResponse extends VaultResponse {
private String username;
private String nonce;
+ protected JsonObject jsonResponse;
+
/**
* This constructor simply exposes the common base class constructor.
*
@@ -37,27 +38,36 @@ public AuthResponse(final RestResponse restResponse, final int retries) {
try {
final String responseJson = new String(restResponse.getBody(), StandardCharsets.UTF_8);
- final JsonObject jsonObject = Json.parse(responseJson).asObject();
- final JsonObject authJsonObject = jsonObject.get("auth").asObject();
-
- renewable = jsonObject.get("renewable").asBoolean();
- authLeaseDuration = authJsonObject.getInt("lease_duration", 0);
- authRenewable = authJsonObject.getBoolean("renewable", false);
- if (authJsonObject.get("metadata") != null && !authJsonObject.get("metadata").toString().equalsIgnoreCase("null")) {
- final JsonObject metadata = authJsonObject.get("metadata").asObject();
- appId = metadata.getString("app-id", "");
- userId = metadata.getString("user-id", "");
- username = metadata.getString("username", "");
- nonce = metadata.getString("nonce", "");
- }
- authClientToken = authJsonObject.getString("client_token", "");
- tokenAccessor = authJsonObject.getString("accessor", "");
- final JsonArray authPoliciesJsonArray = authJsonObject.get("policies").asArray();
- authPolicies = new ArrayList<>();
- for (final JsonValue authPolicy : authPoliciesJsonArray) {
- authPolicies.add(authPolicy.asString());
+ jsonResponse = Json.parse(responseJson).asObject();
+ JsonValue authJsonVal = jsonResponse.get("auth");
+ final JsonObject authJsonObject = authJsonVal != null && !authJsonVal.isNull() ? authJsonVal.asObject() : null;
+
+ if (authJsonObject != null) {
+ authLeaseDuration = authJsonObject.getInt("lease_duration", 0);
+ authRenewable = authJsonObject.getBoolean("renewable", false);
+ if (authJsonObject.get("metadata") != null && !authJsonObject.get("metadata")
+ .toString().equalsIgnoreCase("null")) {
+ final JsonObject metadata = authJsonObject.get("metadata").asObject();
+ appId = metadata.getString("app-id", "");
+ userId = metadata.getString("user-id", "");
+ username = metadata.getString("username", "");
+ nonce = metadata.getString("nonce", "");
+ }
+
+ authClientToken = authJsonObject.getString("client_token", "");
+ tokenAccessor = authJsonObject.getString("accessor", "");
+
+ final JsonArray authPoliciesJsonArray = authJsonObject.get("policies").asArray();
+ authPolicies = new ArrayList<>();
+
+ for (final JsonValue authPolicy : authPoliciesJsonArray) {
+ authPolicies.add(authPolicy.asString());
+ }
}
+
+ renewable = jsonResponse.get("renewable").asBoolean();
} catch (ParseException e) {
+ // No-op.
}
}
diff --git a/src/main/java/io/github/jopenlibs/vault/response/LogicalResponse.java b/src/main/java/io/github/jopenlibs/vault/response/LogicalResponse.java
index 0e28b449..15708e3c 100644
--- a/src/main/java/io/github/jopenlibs/vault/response/LogicalResponse.java
+++ b/src/main/java/io/github/jopenlibs/vault/response/LogicalResponse.java
@@ -17,7 +17,6 @@
* operations (e.g. read, write).
*/
public class LogicalResponse extends VaultResponse {
-
private Map data = new HashMap<>();
private List listData = new ArrayList<>();
private JsonObject dataObject = null;
diff --git a/src/main/java/io/github/jopenlibs/vault/response/UnwrapResponse.java b/src/main/java/io/github/jopenlibs/vault/response/UnwrapResponse.java
new file mode 100644
index 00000000..c4840ffb
--- /dev/null
+++ b/src/main/java/io/github/jopenlibs/vault/response/UnwrapResponse.java
@@ -0,0 +1,25 @@
+package io.github.jopenlibs.vault.response;
+
+import io.github.jopenlibs.vault.json.JsonObject;
+import io.github.jopenlibs.vault.rest.RestResponse;
+
+/**
+ * This class is a container for the information returned by Vault in unwrap backend operations.
+ */
+public class UnwrapResponse extends AuthResponse {
+ /**
+ * This constructor simply exposes the common base class constructor.
+ *
+ * @param restResponse The raw HTTP response from Vault.
+ * @param retries The number of retry attempts that occurred during the API call (can be zero).
+ */
+ public UnwrapResponse(final RestResponse restResponse, final int retries) {
+ super(restResponse, retries);
+ }
+
+ public JsonObject getData() {
+ assert jsonResponse.get("data").isObject();
+
+ return jsonResponse.get("data").asObject();
+ }
+}
diff --git a/src/main/java/io/github/jopenlibs/vault/response/WrapResponse.java b/src/main/java/io/github/jopenlibs/vault/response/WrapResponse.java
new file mode 100644
index 00000000..74698e91
--- /dev/null
+++ b/src/main/java/io/github/jopenlibs/vault/response/WrapResponse.java
@@ -0,0 +1,98 @@
+package io.github.jopenlibs.vault.response;
+
+import io.github.jopenlibs.vault.json.Json;
+import io.github.jopenlibs.vault.json.JsonObject;
+import io.github.jopenlibs.vault.json.JsonValue;
+import io.github.jopenlibs.vault.json.ParseException;
+import io.github.jopenlibs.vault.rest.RestResponse;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * When a response is wrapped, the normal API response from Vault does not contain the original secret,
+ * but rather contains a set of information related to the response-wrapping token.
+ */
+public class WrapResponse extends VaultResponse {
+ private Boolean renewable;
+ private String token;
+ private String accessor;
+ private int ttl;
+ private String creationTime;
+ private String creationPath;
+
+ /**
+ * Parse response-wrapping and create an instance of response.
+ *
+ * @param restResponse The raw HTTP response from Vault.
+ * @param retries The number of retry attempts that occurred during the API call (can be zero).
+ */
+ public WrapResponse(final RestResponse restResponse, final int retries) {
+ super(restResponse, retries);
+
+ try {
+ final String responseJson = new String(restResponse.getBody(), StandardCharsets.UTF_8);
+ JsonObject jsonResponse = Json.parse(responseJson).asObject();
+ JsonValue wrapInfoJsonVal = jsonResponse.get("wrap_info");
+ if (wrapInfoJsonVal != null && !wrapInfoJsonVal.isNull()) {
+ final JsonObject wrapInfoJsonObject = wrapInfoJsonVal.asObject();
+ token = wrapInfoJsonObject.getString("token", null);
+ accessor = wrapInfoJsonObject.getString("accessor", null);
+ ttl = wrapInfoJsonObject.getInt("ttl", 0);
+ creationTime = wrapInfoJsonObject.getString("creation_time", null);
+ creationPath = wrapInfoJsonObject.getString("creation_path", null);
+ }
+
+ renewable = jsonResponse.get("renewable").asBoolean();
+ } catch (ParseException e) {
+ // No-op.
+ }
+ }
+
+ public Boolean getRenewable() {
+ return renewable;
+ }
+
+ /**
+ * Get response-wrapped token
+ *
+ * @return response-wrapped token
+ */
+ public String getToken() {
+ return token;
+ }
+
+ /**
+ * If the wrapped response is an authentication response containing a Vault token,
+ * this is the value of the wrapped token's accessor. This is useful for orchestration
+ * systems (such as Nomad) to be able to control the lifetime of secrets based on
+ * their knowledge of the lifetime of jobs, without having to actually unwrap
+ * the response-wrapping token or gain knowledge of the token ID inside
+ *
+ * @return Wrapped Accessor.
+ */
+ public String getAccessor() {
+ return accessor;
+ }
+
+ /** */
+ public int getTtl() {
+ return ttl;
+ }
+
+ /**
+ * Get the time that the response-wrapping token was created
+ *
+ * @return The time that the response-wrapping token was created;
+ */
+ public String getCreationTime() {
+ return creationTime;
+ }
+
+ /**
+ * Get the API path that was called in the original request
+ *
+ * @return The API path that was called in the original request
+ */
+ public String getCreationPath() {
+ return creationPath;
+ }
+}
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/AuthBackendAppIdTests.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/AuthBackendAppIdTests.java
new file mode 100644
index 00000000..8463106b
--- /dev/null
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/AuthBackendAppIdTests.java
@@ -0,0 +1,43 @@
+package io.github.jopenlibs.vault.v1_11_4.api;
+
+import io.github.jopenlibs.vault.Vault;
+import io.github.jopenlibs.vault.VaultException;
+import io.github.jopenlibs.vault.v1_11_4.util.VaultContainer;
+import java.io.IOException;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+
+/**
+ * Integration tests for the AppId auth backend.
+ */
+public class AuthBackendAppIdTests {
+
+ @ClassRule
+ public static final VaultContainer container = new VaultContainer();
+
+ @BeforeClass
+ public static void setupClass() throws IOException, InterruptedException {
+ container.initAndUnsealVault();
+ container.setupBackendAppId();
+ }
+
+ /**
+ * Test Authentication with app-id auth backend
+ */
+ @Test
+ public void testLoginByAuthId() throws VaultException {
+ final Vault vault = container.getVault();
+ final String path = "app-id/login";
+ @SuppressWarnings("deprecation") // used for testing
+ final String token = vault.auth().loginByAppID(path, VaultContainer.APP_ID, VaultContainer.USER_ID)
+ .getAuthClientToken();
+
+ assertNotNull(token);
+ assertNotSame("", token.trim());
+ }
+
+}
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/AuthBackendAppRoleTests.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/AuthBackendAppRoleTests.java
new file mode 100644
index 00000000..9682e2e3
--- /dev/null
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/AuthBackendAppRoleTests.java
@@ -0,0 +1,56 @@
+package io.github.jopenlibs.vault.v1_11_4.api;
+
+import io.github.jopenlibs.vault.Vault;
+import io.github.jopenlibs.vault.VaultConfig;
+import io.github.jopenlibs.vault.VaultException;
+import io.github.jopenlibs.vault.response.LogicalResponse;
+import io.github.jopenlibs.vault.v1_11_4.util.VaultContainer;
+import java.io.IOException;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+
+/**
+ * Integration tests for the AppRole auth backend.
+ */
+public class AuthBackendAppRoleTests {
+
+ private static String appRoleId;
+ private static String secretId;
+
+ @ClassRule
+ public static final VaultContainer container = new VaultContainer();
+
+ @BeforeClass
+ public static void setupClass() throws IOException, InterruptedException, VaultException {
+ container.initAndUnsealVault();
+ container.setupBackendAppRole();
+
+ final Vault vault = container.getRootVaultWithCustomVaultConfig(new VaultConfig().engineVersion(1));
+
+ final LogicalResponse roleIdResponse = vault.logical().read("auth/approle/role/testrole/role-id");
+ appRoleId = roleIdResponse.getData().get("role_id");
+ final LogicalResponse secretIdResponse = vault.logical().write("auth/approle/role/testrole/secret-id", null);
+ secretId = secretIdResponse.getData().get("secret_id");
+
+ assertNotNull(appRoleId);
+ assertNotNull(secretId);
+ }
+
+ /**
+ * Tests authentication with the app role auth backend
+ */
+ @Test
+ public void testLoginByAppRole() throws VaultException {
+ final Vault vault = container.getVault();
+ final String path = "approle";
+ final String token = vault.auth().loginByAppRole(path, appRoleId, secretId).getAuthClientToken();
+
+ assertNotNull(token);
+ assertNotSame("", token.trim());
+ }
+
+}
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/AuthBackendCertTests.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/AuthBackendCertTests.java
new file mode 100644
index 00000000..98d5a7a7
--- /dev/null
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/AuthBackendCertTests.java
@@ -0,0 +1,88 @@
+package io.github.jopenlibs.vault.v1_11_4.api;
+
+import io.github.jopenlibs.vault.SslConfig;
+import io.github.jopenlibs.vault.Vault;
+import io.github.jopenlibs.vault.VaultConfig;
+import io.github.jopenlibs.vault.VaultException;
+import io.github.jopenlibs.vault.v1_11_4.util.SSLUtils;
+import io.github.jopenlibs.vault.v1_11_4.util.TestConstants;
+import io.github.jopenlibs.vault.v1_11_4.util.VaultContainer;
+import java.io.File;
+import java.io.IOException;
+import java.security.KeyStore;
+import java.util.HashMap;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+
+/**
+ * Integration tests for the TLS Certificate auth backend.
+ *
+ * Note that {@link VaultContainer#getVault()} and the other convenience builders in that class construct
+ * {@link Vault} instances that are configured for basic SSL only. So in order to test client auth, test methods here
+ * must manually construct Vault
instances themselves.
+ */
+public class AuthBackendCertTests {
+
+ @ClassRule
+ public static final VaultContainer container = new VaultContainer();
+ private static HashMap clientCertAndKey;
+ private static String cert;
+
+ @BeforeClass
+ public static void setupClass() throws IOException, InterruptedException {
+ clientCertAndKey = SSLUtils.createClientCertAndKey();
+ cert = (String) clientCertAndKey.get("cert");
+ container.initAndUnsealVault();
+ container.setupBackendCert(cert);
+ }
+
+ @Test
+ public void testLoginByCert_usingJksConfig() throws VaultException {
+ final VaultConfig config =
+ new VaultConfig()
+ .address(container.getAddress())
+ .openTimeout(5)
+ .readTimeout(30)
+ .sslConfig(
+ new SslConfig()
+ .keyStore((KeyStore) clientCertAndKey.get("clientKeystore"), TestConstants.PASSWORD)
+ .trustStore((KeyStore) clientCertAndKey.get("clientTrustStore"))
+ .build()
+ )
+ .build();
+ final Vault vault = container.getVault(config, VaultContainer.MAX_RETRIES, VaultContainer.RETRY_MILLIS);
+
+ final String token = vault.auth().loginByCert().getAuthClientToken();
+
+ assertNotNull(token);
+ assertNotSame("", token.trim());
+ }
+
+ @Test
+ public void testLoginByCert_usingPemConfig() throws VaultException {
+ final VaultConfig config =
+ new VaultConfig()
+ .address(container.getAddress())
+ .openTimeout(5)
+ .readTimeout(30)
+ .sslConfig(
+ new SslConfig()
+ .pemFile(new File(VaultContainer.CERT_PEMFILE))
+ .clientPemUTF8(cert)
+ .clientKeyPemUTF8((String) clientCertAndKey.get("privateKey"))
+ .build()
+ )
+ .build();
+ final Vault vault = container.getVault(config, VaultContainer.MAX_RETRIES, VaultContainer.RETRY_MILLIS);
+
+ final String token = vault.auth().loginByCert().getAuthClientToken();
+
+ assertNotNull(token);
+ assertNotSame("", token.trim());
+ }
+
+}
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/AuthBackendDatabaseTests.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/AuthBackendDatabaseTests.java
new file mode 100644
index 00000000..4d682097
--- /dev/null
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/AuthBackendDatabaseTests.java
@@ -0,0 +1,107 @@
+package io.github.jopenlibs.vault.v1_11_4.api;
+
+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.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.List;
+import junit.framework.TestCase;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertTrue;
+
+public class AuthBackendDatabaseTests {
+ @ClassRule
+ public static final DbContainer dbContainer = new DbContainer();
+
+ @ClassRule
+ public static final VaultContainer container = new VaultContainer().dependsOn(dbContainer);
+
+
+ @BeforeClass
+ public static void setupClass() throws IOException, InterruptedException {
+ container.initAndUnsealVault();
+ container.setupBackendDatabase(DbContainer.hostname);
+ }
+
+ @Test
+ public void testRoleCreation() throws VaultException {
+ final Vault vault = container.getRootVault();
+
+ List creationStatements = new ArrayList<>();
+ creationStatements.add("CREATE USER \"{{name}}\" WITH PASSWORD '{{password}}'; GRANT ALL PRIVILEGES ON DATABASE \"postgres\" to \"{{name}}\";");
+
+ DatabaseRoleOptions roleToCreate = new DatabaseRoleOptions().dbName("postgres").creationStatements(creationStatements);
+
+ DatabaseResponse response = vault.database().createOrUpdateRole("test-role", roleToCreate);
+ TestCase.assertEquals(204, response.getRestResponse().getStatus());
+
+ DatabaseResponse role = vault.database().getRole("test-role");
+ TestCase.assertEquals(200, role.getRestResponse().getStatus());
+
+ assertTrue(compareRoleOptions(role.getRoleOptions(), roleToCreate));
+ }
+
+ @Test
+ public void testDeleteRole() throws VaultException {
+ final Vault vault = container.getRootVault();
+
+ List creationStatements = new ArrayList<>();
+ creationStatements.add("CREATE USER \"{{name}}\" WITH PASSWORD '{{password}}'; GRANT ALL PRIVILEGES ON DATABASE \"postgres\" to \"{{name}}\";");
+
+ DatabaseRoleOptions roleToCreate = new DatabaseRoleOptions().dbName("postgres").creationStatements(creationStatements);
+
+ DatabaseResponse response = vault.database().createOrUpdateRole("delete-role", roleToCreate);
+ TestCase.assertEquals(204, response.getRestResponse().getStatus());
+
+ DatabaseResponse deletedRole = vault.database().deleteRole("delete-role");
+ TestCase.assertEquals(204, deletedRole.getRestResponse().getStatus());
+
+ try {
+ DatabaseResponse role = vault.database().getRole("delete-role");
+ } catch (VaultException e) {
+ assertEquals("This should have failed", 404, e.getHttpStatusCode());
+ }
+ }
+
+ @Test
+ public void testRoleNotFound() throws VaultException {
+ final Vault vault = container.getRootVault();
+
+ try {
+ DatabaseResponse role = vault.database().getRole("i-do-not-exist");
+ } catch (VaultException e) {
+ assertEquals("This should have failed", 404, e.getHttpStatusCode());
+ }
+ }
+
+ @Test
+ public void testGetCredentials() throws VaultException {
+ final Vault vault = container.getRootVault();
+
+ List creationStatements = new ArrayList<>();
+ creationStatements.add("CREATE USER \"{{name}}\" WITH PASSWORD '{{password}}'; GRANT ALL PRIVILEGES ON DATABASE \"postgres\" to \"{{name}}\";");
+
+ DatabaseResponse response = vault.database().createOrUpdateRole("new-role", new DatabaseRoleOptions().dbName("postgres").creationStatements(creationStatements));
+ TestCase.assertEquals(204, response.getRestResponse().getStatus());
+
+ DatabaseResponse credsResponse = vault.database().creds("new-role");
+ TestCase.assertEquals(200, credsResponse.getRestResponse().getStatus());
+
+ TestCase.assertTrue(credsResponse.getCredential().getUsername().contains("new-role"));
+ }
+
+ private boolean compareRoleOptions(DatabaseRoleOptions expected, DatabaseRoleOptions actual) {
+ return expected.getCreationStatements().size() == actual.getCreationStatements().size() &&
+ expected.getRenewStatements().size() == actual.getRenewStatements().size() &&
+ expected.getRevocationStatements().size() == actual.getRevocationStatements().size() &&
+ expected.getRollbackStatements().size() == actual.getRollbackStatements().size();
+ }
+}
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/AuthBackendPkiTests.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/AuthBackendPkiTests.java
new file mode 100644
index 00000000..ad6d71da
--- /dev/null
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/AuthBackendPkiTests.java
@@ -0,0 +1,205 @@
+package io.github.jopenlibs.vault.v1_11_4.api;
+
+import io.github.jopenlibs.vault.Vault;
+import io.github.jopenlibs.vault.VaultException;
+import io.github.jopenlibs.vault.api.pki.CredentialFormat;
+import io.github.jopenlibs.vault.api.pki.RoleOptions;
+import io.github.jopenlibs.vault.response.PkiResponse;
+import io.github.jopenlibs.vault.rest.RestResponse;
+import io.github.jopenlibs.vault.v1_11_4.util.SSLUtils;
+import io.github.jopenlibs.vault.v1_11_4.util.VaultContainer;
+import java.io.IOException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import junit.framework.TestCase;
+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 for operations on Vault's /v1/pki/*
REST endpoints.
+ */
+public class AuthBackendPkiTests {
+
+ @ClassRule
+ public static final VaultContainer container = new VaultContainer();
+
+ @BeforeClass
+ public static void setupClass() throws IOException, InterruptedException {
+ container.initAndUnsealVault();
+ container.setupBackendPki();
+ }
+
+ @Before
+ public void setup() throws VaultException {
+ final Vault vault = container.getRootVault();
+
+ final PkiResponse defaultResponse = vault.pki().deleteRole("testRole");
+ final RestResponse defaultRestResponse = defaultResponse.getRestResponse();
+ assertEquals(204, defaultRestResponse.getStatus());
+
+ final PkiResponse customResponse = vault.pki("other-pki").deleteRole("testRole");
+ final RestResponse customRestResponse = customResponse.getRestResponse();
+ assertEquals(204, customRestResponse.getStatus());
+ }
+
+ @Test
+ public void testCreateRole_Defaults() throws VaultException {
+ final Vault vault = container.getRootVault();
+
+ vault.pki().createOrUpdateRole("testRole");
+ final PkiResponse response = vault.pki().getRole("testRole");
+ assertTrue(compareRoleOptions(new RoleOptions(), response.getRoleOptions()));
+ }
+
+ @Test
+ public void testCreateRole_WithOptions() throws VaultException {
+ final Vault vault = container.getRootVault();
+
+ final RoleOptions options = new RoleOptions().allowAnyName(true);
+ vault.pki().createOrUpdateRole("testRole", options);
+ final PkiResponse response = vault.pki().getRole("testRole");
+ assertTrue(compareRoleOptions(options, response.getRoleOptions()));
+ }
+
+ @Test
+ public void testDeleteRole() throws VaultException {
+ final Vault vault = container.getRootVault();
+
+ testCreateRole_Defaults();
+ final PkiResponse deleteResponse = vault.pki().deleteRole("testRole");
+ TestCase.assertEquals(204, deleteResponse.getRestResponse().getStatus());
+ final PkiResponse getResponse = vault.pki().getRole("testRole");
+ TestCase.assertEquals(404, getResponse.getRestResponse().getStatus());
+ }
+
+ @Test
+ public void testIssueCredential() throws VaultException, InterruptedException {
+ final Vault vault = container.getRootVault();
+
+ // Create a role
+ final PkiResponse createRoleResponse = vault.pki().createOrUpdateRole("testRole",
+ new RoleOptions()
+ .allowedDomains(new ArrayList<>() {{
+ add("myvault.com");
+ }})
+ .allowSubdomains(true)
+ .maxTtl("9h")
+ );
+ TestCase.assertEquals(204, createRoleResponse.getRestResponse().getStatus());
+ Thread.sleep(3000);
+
+ // Issue cert
+ final PkiResponse issueResponse = vault.pki().issue("testRole", "test.myvault.com", null, null, "1h", CredentialFormat.PEM);
+ TestCase.assertNotNull(issueResponse.getCredential().getCertificate());
+ TestCase.assertNotNull(issueResponse.getCredential().getPrivateKey());
+ TestCase.assertNotNull(issueResponse.getCredential().getSerialNumber());
+ TestCase.assertEquals("rsa", issueResponse.getCredential().getPrivateKeyType());
+ TestCase.assertNotNull(issueResponse.getCredential().getIssuingCa());
+ }
+
+ @Test
+ public void testIssueCredentialWithCsr() throws VaultException, InterruptedException, NoSuchAlgorithmException {
+
+ KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
+ kpg.initialize(2048);
+ KeyPair kp = kpg.generateKeyPair();
+ PublicKey pub = kp.getPublic();
+ PrivateKey pvt = kp.getPrivate();
+ String csr = null;
+ try {
+ csr = SSLUtils.generatePKCS10(kp, "", "", "", "", "", "");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ final Vault vault = container.getRootVault();
+
+ // Create a role
+ final PkiResponse createRoleResponse = vault.pki().createOrUpdateRole("testRole",
+ new RoleOptions()
+ .allowedDomains(new ArrayList<>() {{
+ add("myvault.com");
+ }})
+ .allowSubdomains(true)
+ .maxTtl("9h")
+ );
+ TestCase.assertEquals(204, createRoleResponse.getRestResponse().getStatus());
+ Thread.sleep(3000);
+
+ // Issue cert
+ final PkiResponse issueResponse = vault.pki().issue("testRole", "test.myvault.com", null, null, "1h", CredentialFormat.PEM, csr);
+ TestCase.assertNotNull(issueResponse.getCredential().getCertificate());
+ TestCase.assertNull(issueResponse.getCredential().getPrivateKey());
+ TestCase.assertNotNull(issueResponse.getCredential().getSerialNumber());
+ TestCase.assertNotNull(issueResponse.getCredential().getIssuingCa());
+ }
+
+ @Test
+ public void testRevocation() throws VaultException, InterruptedException {
+ final Vault vault = container.getRootVault();
+
+ // Create a role
+ final PkiResponse createRoleResponse = vault.pki().createOrUpdateRole("testRole",
+ new RoleOptions()
+ .allowedDomains(new ArrayList<>() {{
+ add("myvault.com");
+ }})
+ .allowSubdomains(true)
+ .maxTtl("9h")
+ );
+ TestCase.assertEquals(204, createRoleResponse.getRestResponse().getStatus());
+ Thread.sleep(3000);
+ // Issue cert
+ final PkiResponse issueResponse = vault.pki().issue("testRole", "test.myvault.com", null, null, "1h", CredentialFormat.PEM);
+ TestCase.assertNotNull(issueResponse.getCredential().getSerialNumber());
+ vault.pki().revoke(issueResponse.getCredential().getSerialNumber());
+ }
+
+ @Test
+ public void testCustomMountPath() throws VaultException {
+ final Vault vault = container.getRootVault();
+
+ vault.pki("other-pki").createOrUpdateRole("testRole");
+ final PkiResponse response = vault.pki("other-pki").getRole("testRole");
+ assertTrue(compareRoleOptions(new RoleOptions(), response.getRoleOptions()));
+ }
+
+ private boolean compareRoleOptions(final RoleOptions expected, final RoleOptions actual) {
+ if (expected.getAllowAnyName() != null && !expected.getAllowAnyName().equals(actual.getAllowAnyName()))
+ return false;
+ if (expected.getAllowBareDomains() != null && !expected.getAllowBareDomains().equals(actual.getAllowBareDomains()))
+ return false;
+ if (expected.getAllowedDomains() != null) {
+ if (!expected.getAllowedDomains().containsAll(actual.getAllowedDomains())
+ || !actual.getAllowedDomains().containsAll(expected.getAllowedDomains())) {
+ return false;
+ }
+ }
+ if (expected.getAllowIpSans() != null && !expected.getAllowIpSans().equals(actual.getAllowIpSans()))
+ return false;
+ if (expected.getAllowLocalhost() != null && !expected.getAllowLocalhost().equals(actual.getAllowLocalhost()))
+ return false;
+ if (expected.getAllowSubdomains() != null && !expected.getAllowSubdomains().equals(actual.getAllowSubdomains()))
+ return false;
+ if (expected.getClientFlag() != null && !expected.getClientFlag().equals(actual.getClientFlag())) return false;
+ if (expected.getCodeSigningFlag() != null && !expected.getCodeSigningFlag().equals(actual.getCodeSigningFlag()))
+ return false;
+ if (expected.getEmailProtectionFlag() != null && !expected.getEmailProtectionFlag().equals(actual.getEmailProtectionFlag()))
+ return false;
+ if (expected.getKeyBits() != null && !expected.getKeyBits().equals(actual.getKeyBits())) return false;
+ if (expected.getKeyType() != null && !expected.getKeyType().equals(actual.getKeyType())) return false;
+ if (expected.getMaxTtl() != null && !expected.getMaxTtl().equals(actual.getMaxTtl())) return false;
+ if (expected.getServerFlag() != null && !expected.getServerFlag().equals(actual.getServerFlag())) return false;
+ if (expected.getTtl() != null && !expected.getTtl().equals(actual.getTtl())) return false;
+ return expected.getUseCsrCommonName() == null || expected.getUseCsrCommonName().equals(actual.getUseCsrCommonName());
+ }
+
+}
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/AuthBackendTokenTests.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/AuthBackendTokenTests.java
new file mode 100644
index 00000000..3950c887
--- /dev/null
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/AuthBackendTokenTests.java
@@ -0,0 +1,143 @@
+package io.github.jopenlibs.vault.v1_11_4.api;
+
+import io.github.jopenlibs.vault.Vault;
+import io.github.jopenlibs.vault.VaultException;
+import io.github.jopenlibs.vault.api.Auth.TokenRequest;
+import io.github.jopenlibs.vault.json.Json;
+import io.github.jopenlibs.vault.response.AuthResponse;
+import io.github.jopenlibs.vault.response.LookupResponse;
+import io.github.jopenlibs.vault.v1_11_4.util.VaultContainer;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.UUID;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Integration tests for the token auth backend.
+ */
+public class AuthBackendTokenTests {
+ @ClassRule
+ public static final VaultContainer container = new VaultContainer();
+
+ @BeforeClass
+ public static void setupClass() throws IOException, InterruptedException {
+ container.initAndUnsealVault();
+ container.setupBackendAppRole();
+ }
+
+ /**
+ * Test creation of a new client auth token via a TokenRequest, using the Vault root token
+ */
+ @Test
+ public void testCreateTokenWithRequest() throws VaultException {
+ final Vault vault = container.getRootVault();
+
+ final AuthResponse response = vault.auth().createToken(
+ new TokenRequest()
+ .id(UUID.randomUUID())
+ .polices(Arrays.asList("policy"))
+ .noParent(true)
+ .noDefaultPolicy(false)
+ .ttl("1h")
+ .displayName("display name")
+ .numUses(1L)
+ .renewable(true)
+ .type("service")
+ .explicitMaxTtl("2h")
+ .period("2h")
+ );
+ final String token = response.getAuthClientToken();
+ final String accessor = response.getTokenAccessor();
+
+ assertNotNull(accessor);
+ assertNotNull(token);
+ assertEquals(2, response.getAuthPolicies().size());
+ assertEquals("default", response.getAuthPolicies().get(0));
+ assertEquals("policy", response.getAuthPolicies().get(1));
+ assertEquals(7200, response.getAuthLeaseDuration());
+ }
+
+ /**
+ * Tests token self-renewal for the token auth backend.
+ */
+ @Test
+ public void testRenewSelf() throws VaultException {
+ // Generate a client token
+ final Vault authVault = container.getRootVault();
+ final AuthResponse createResponse = authVault.auth().createToken(new TokenRequest().ttl("1h"));
+ final String token = createResponse.getAuthClientToken();
+
+ assertNotNull(token);
+ assertNotSame("", token.trim());
+
+ // Renew the client token
+ final Vault renewVault = container.getVault(token);
+ final AuthResponse renewResponse = renewVault.auth().renewSelf();
+ final String renewToken = renewResponse.getAuthClientToken();
+
+ assertEquals(token, renewToken);
+
+ // Renew the auth token, with an explicit increment value
+ final Vault explicitVault = container.getVault(token);
+ final AuthResponse explicitResponse = explicitVault.auth().renewSelf(20);
+ final String explicitToken = explicitResponse.getAuthClientToken();
+
+ assertEquals(token, explicitToken);
+
+ final String explicitJson = new String(explicitResponse.getRestResponse().getBody(), StandardCharsets.UTF_8);
+ final long explicitLeaseDuration = Json.parse(explicitJson).asObject().get("auth").asObject().get("lease_duration").asLong();
+
+ assertEquals(20, explicitLeaseDuration);
+ }
+
+ /**
+ * Tests token lookup-self for the token auth backend.
+ */
+ @Test
+ public void testLookupSelf() throws VaultException {
+ // Generate a client token
+ final Vault authVault = container.getRootVault();
+ final AuthResponse createResponse = authVault.auth().createToken(new TokenRequest().ttl("1h"));
+ final String token = createResponse.getAuthClientToken();
+
+ assertNotNull(token);
+ assertNotSame("", token.trim());
+
+ // Lookup the client token
+ final Vault lookupVault = container.getVault(token);
+ final LookupResponse lookupResponse = lookupVault.auth().lookupSelf();
+
+ assertEquals(token, lookupResponse.getId());
+ assertEquals(3600, lookupResponse.getCreationTTL());
+ assertTrue(lookupResponse.getTTL() <= 3600);
+ }
+
+ /**
+ * Tests token revoke-self for the token auth backend.
+ */
+ @Test(expected = VaultException.class)
+ public void testRevokeSelf() throws VaultException {
+ // Generate a client token
+ final Vault authVault = container.getRootVault();
+ final AuthResponse createResponse = authVault.auth().createToken(new TokenRequest().ttl("1h"));
+ final String token = createResponse.getAuthClientToken();
+
+ assertNotNull(token);
+ assertNotSame("", token.trim());
+
+ // Revoke the client token
+ container.getVault(token).auth().revokeSelf();
+ // Lookup the client token
+ final Vault lookupVault = container.getVault(token);
+ lookupVault.auth().lookupSelf();
+ }
+
+}
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/AuthBackendUserPassTests.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/AuthBackendUserPassTests.java
new file mode 100644
index 00000000..726ad478
--- /dev/null
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/AuthBackendUserPassTests.java
@@ -0,0 +1,42 @@
+package io.github.jopenlibs.vault.v1_11_4.api;
+
+import io.github.jopenlibs.vault.Vault;
+import io.github.jopenlibs.vault.VaultException;
+import io.github.jopenlibs.vault.response.AuthResponse;
+import io.github.jopenlibs.vault.v1_11_4.util.VaultContainer;
+import java.io.IOException;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+
+/**
+ * Integration tests for the Username/Password auth backend.
+ */
+public class AuthBackendUserPassTests {
+
+ @ClassRule
+ public static final VaultContainer container = new VaultContainer();
+
+ @BeforeClass
+ public static void setupClass() throws IOException, InterruptedException {
+ container.initAndUnsealVault();
+ container.setupBackendUserPass();
+ }
+
+ /**
+ * Test Authentication with new userpass auth backend
+ */
+ @Test
+ public void testLoginByUserPass() throws VaultException {
+ final Vault vault = container.getVault();
+ final AuthResponse response = vault.auth().loginByUserPass(VaultContainer.USER_ID, VaultContainer.PASSWORD);
+ final String token = response.getAuthClientToken();
+
+ assertNotNull(token);
+ assertNotSame("", token.trim());
+ }
+
+}
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/DebugTests.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/DebugTests.java
new file mode 100644
index 00000000..7fb2b0e5
--- /dev/null
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/DebugTests.java
@@ -0,0 +1,78 @@
+package io.github.jopenlibs.vault.v1_11_4.api;
+
+import io.github.jopenlibs.vault.Vault;
+import io.github.jopenlibs.vault.VaultException;
+import io.github.jopenlibs.vault.response.HealthResponse;
+import io.github.jopenlibs.vault.v1_11_4.util.VaultContainer;
+import java.io.IOException;
+import junit.framework.TestCase;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertNotNull;
+import static junit.framework.TestCase.assertNull;
+import static junit.framework.TestCase.assertTrue;
+
+/**
+ * Integration tests for the debug-related operations on the Vault HTTP API's.
+ */
+public class DebugTests {
+ @ClassRule
+ public static final VaultContainer container = new VaultContainer();
+
+ private Vault vault;
+
+ @BeforeClass
+ public static void setupClass() throws IOException, InterruptedException {
+ container.initAndUnsealVault();
+ }
+
+ @Before
+ public void setup() throws VaultException {
+ vault = container.getVault();
+ }
+
+ @Test
+ public void testHealth_Plain() throws VaultException {
+ final HealthResponse response = vault.debug().health();
+
+ assertTrue(response.getInitialized());
+ assertFalse(response.getSealed());
+ assertFalse(response.getStandby());
+ assertNotNull(response.getServerTimeUTC());
+ TestCase.assertEquals(200, response.getRestResponse().getStatus());
+ }
+
+ @Test
+ public void testHealth_WithParams() throws VaultException {
+ final HealthResponse response = vault.debug().health(null, 212, null, null);
+ assertTrue(response.getInitialized());
+ assertFalse(response.getSealed());
+ assertFalse(response.getStandby());
+ assertNotNull(response.getServerTimeUTC());
+ TestCase.assertEquals(212, response.getRestResponse().getStatus());
+ }
+
+ /**
+ * Altering the default HTTP status codes with optional parameters can cause Vault to return an empty JSON
+ * payload, depending on which replacement HTTP status code you specify.
+ *
+ * For example... Vault still returns a valid JSON payload when you change activeCode to 212 (see test above),
+ * but returns an empty payload when you set it to use 204.
+ *
+ * @throws VaultException
+ */
+ @Test
+ public void testHealth_WonkyActiveCode() throws VaultException {
+ final HealthResponse response = vault.debug().health(null, 204, null,
+ null);
+ assertNull(response.getInitialized());
+ assertNull(response.getSealed());
+ assertNull(response.getStandby());
+ assertNull(response.getServerTimeUTC());
+ TestCase.assertEquals(204, response.getRestResponse().getStatus());
+ }
+}
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
new file mode 100644
index 00000000..4578cf4a
--- /dev/null
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/LeasesTests.java
@@ -0,0 +1,68 @@
+package io.github.jopenlibs.vault.v1_11_4.api;
+
+import io.github.jopenlibs.vault.Vault;
+import io.github.jopenlibs.vault.VaultException;
+import io.github.jopenlibs.vault.response.VaultResponse;
+import io.github.jopenlibs.vault.v1_11_4.util.VaultContainer;
+import java.io.IOException;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import static junit.framework.TestCase.assertEquals;
+
+/**
+ * Integration tests for the basic (i.e. "sys") 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
+ */
+public class LeasesTests {
+
+ @ClassRule
+ public static final VaultContainer container = new VaultContainer();
+
+ private Vault vault;
+
+ @BeforeClass
+ public static void setupClass() throws IOException, InterruptedException {
+ container.initAndUnsealVault();
+ }
+
+ @Before
+ public void setup() throws VaultException {
+ vault = container.getRootVault();
+ }
+
+ @Test
+ public void testRevoke() throws VaultException {
+ final VaultResponse response = vault.leases().revoke("sys/revoke-prefix/dummy");
+ assertEquals(204, response.getRestResponse().getStatus());
+ }
+
+ @Test
+ public void testRevokePrefix() throws VaultException {
+ final VaultResponse response = vault.leases().revokePrefix("sys/revoke-prefix/dummy");
+ assertEquals(204, response.getRestResponse().getStatus());
+ }
+
+ @Test
+ public void testRevokeForce() throws VaultException {
+ final VaultResponse response = vault.leases().revokeForce("sys/revoke-prefix/dummy");
+ assertEquals(204, response.getRestResponse().getStatus());
+ }
+
+}
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/LogicalTests.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/LogicalTests.java
new file mode 100644
index 00000000..682e809f
--- /dev/null
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/LogicalTests.java
@@ -0,0 +1,447 @@
+package io.github.jopenlibs.vault.v1_11_4.api;
+
+import io.github.jopenlibs.vault.Vault;
+import io.github.jopenlibs.vault.VaultConfig;
+import io.github.jopenlibs.vault.VaultException;
+import io.github.jopenlibs.vault.response.AuthResponse;
+import io.github.jopenlibs.vault.response.LogicalResponse;
+import io.github.jopenlibs.vault.v1_11_4.util.VaultContainer;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import junit.framework.TestCase;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertTrue;
+
+/**
+ * Integration tests for the basic (i.e. "logical") Vault API operations.
+ */
+public class LogicalTests {
+
+ @ClassRule
+ public static final VaultContainer container = new VaultContainer();
+
+ private static String NONROOT_TOKEN;
+
+ @BeforeClass
+ public static void setupClass() throws IOException, InterruptedException, VaultException {
+ container.initAndUnsealVault();
+ container.setupBackendUserPass();
+ container.setEngineVersions();
+ final Vault vault = container.getVault();
+ final AuthResponse response = vault.auth().loginByUserPass(VaultContainer.USER_ID, VaultContainer.PASSWORD);
+ NONROOT_TOKEN = response.getAuthClientToken();
+ }
+
+ /**
+ * Write a secret and verify that it can be read, using KV Secrets engine version 2.
+ *
+ * @throws VaultException On error.
+ */
+ @Test
+ public void testWriteAndRead() throws VaultException {
+ final String pathToWrite = "secret/hello";
+ final String pathToRead = "secret/hello";
+
+ final String value = "world";
+ final Vault vault = container.getRootVault();
+
+ final Map testMap = new HashMap<>();
+ testMap.put("value", value);
+
+ vault.logical().write(pathToWrite, testMap);
+
+ final String valueRead = vault.logical().read(pathToRead).getData().get("value");
+ assertEquals(value, valueRead);
+ }
+
+ /**
+ * Write a secret and verify that it can be read, using KV Secrets engine version 1.
+ *
+ * @throws VaultException On error.
+ */
+ @Test
+ public void testWriteAndReadKVEngineV1() throws VaultException {
+ final String pathToWrite = "kv-v1/hello";
+ final String pathToRead = "kv-v1/hello";
+
+ final String value = "world";
+ final Vault vault = container.getRootVaultWithCustomVaultConfig(new VaultConfig().engineVersion(1));
+
+ final Map testMap = new HashMap<>();
+ testMap.put("value", value);
+
+ vault.logical().write(pathToWrite, testMap);
+
+ final String valueRead = vault.logical().read(pathToRead).getData().get("value");
+ assertEquals(value, valueRead);
+ }
+
+
+ /**
+ * Write a secret and verify that a specific version can be read, using KV Secrets Engine version 2.
+ *
+ * @throws VaultException On error.
+ */
+ @Test
+ public void testWriteAndReadSpecificVersions() throws VaultException {
+ final String pathToWrite = "secret/hello";
+ final String pathToRead = "secret/hello";
+
+ final String value = "world";
+ final Vault vault = container.getRootVault();
+
+ final Map testMap = new HashMap<>();
+ testMap.put("value", value);
+
+ vault.logical().write(pathToWrite, testMap);
+
+ final String valueRead = vault.logical().read(pathToRead, true, 1).getData().get("value");
+ assertEquals(value, valueRead);
+ }
+
+
+ /**
+ * Write a secret and verify that it can be read containing a null value, using KV Engine version 2.
+ *
+ * @throws VaultException On error.
+ */
+ @Test
+ public void testWriteAndReadNull() throws VaultException {
+ final String pathToWrite = "secret/null";
+ final String pathToRead = "secret/null";
+ final String value = null;
+ final Vault vault = container.getRootVault();
+
+ final Map testMap = new HashMap<>();
+ testMap.put("value", value);
+
+ vault.logical().write(pathToWrite, testMap);
+
+ final String valueRead = vault.logical().read(pathToRead).getData().get("value");
+ assertEquals(value, valueRead);
+ }
+
+ /**
+ * Write a secret and verify that it can be read containing a null value, using KV Engine version 1.
+ *
+ * @throws VaultException On error.
+ */
+ @Test
+ public void testWriteAndReadNullKVEngineV1() throws VaultException {
+ final String pathToWrite = "kv-v1/null";
+ final String pathToRead = "kv-v1/null";
+ final String value = null;
+ final Vault vault = container.getRootVaultWithCustomVaultConfig(new VaultConfig().engineVersion(1));
+
+ final Map testMap = new HashMap<>();
+ testMap.put("value", value);
+
+ vault.logical().write(pathToWrite, testMap);
+
+ final String valueRead = vault.logical().read(pathToRead).getData().get("value");
+ assertEquals(value, valueRead);
+ }
+
+ /**
+ * Write a secret, and then verify that its key shows up in the list, using KV Engine version 2.
+ *
+ * @throws VaultException On error.
+ */
+ @Test
+ public void testList() throws VaultException {
+ final Vault vault = container.getRootVault();
+ final Map testMap = new HashMap<>();
+ testMap.put("value", "world");
+
+ vault.logical().write("secret/hello", testMap);
+ final List keys = vault.logical().list("secret").getListData();
+ assertTrue(keys.contains("hello"));
+ }
+
+ /**
+ * Write a secret, and then verify that its key shows up in the list, using KV Engine version 1.
+ *
+ * @throws VaultException On error.
+ */
+ @Test
+ public void testListKVEngineV1() throws VaultException {
+ final Vault vault = container.getRootVaultWithCustomVaultConfig(new VaultConfig().engineVersion(1));
+ final Map testMap = new HashMap<>();
+ testMap.put("value", "world");
+
+ vault.logical().write("kv-v1/hello", testMap);
+ final List keys = vault.logical().list("kv-v1").getListData();
+ assertTrue(keys.contains("hello"));
+ }
+
+ /**
+ * Write a secret, and then verify that is is successfully deleted, using KV Engine version 2.
+ *
+ * @throws VaultException On error.
+ */
+ @Test
+ public void testDelete() throws VaultException {
+ final Vault vault = container.getRootVault();
+ final Map testMap = new HashMap<>();
+ testMap.put("value", "world");
+
+ vault.logical().write("secret/hello", testMap);
+ assertTrue(vault.logical().list("secret").getListData().contains("hello"));
+ vault.logical().delete("secret/hello");
+ assertFalse(vault.logical().list("secret").getListData().contains("hello"));
+ }
+
+ /**
+ * Write a secret, and then verify that is is successfully deleted, using KV Engine version 1.
+ *
+ * @throws VaultException On error.
+ */
+ @Test
+ public void testDeleteKVEngineV1() throws VaultException {
+ final Vault vault = container.getRootVaultWithCustomVaultConfig(new VaultConfig().engineVersion(1));
+ final Map testMap = new HashMap<>();
+ testMap.put("value", "world");
+
+ vault.logical().write("kv-v1/hello", testMap);
+ assertTrue(vault.logical().list("kv-v1").getListData().contains("hello"));
+ vault.logical().delete("kv-v1/hello");
+ assertFalse(vault.logical().list("kv-v1").getListData().contains("hello"));
+ }
+
+ /**
+ * Write a secret multiple times, and have multiple versions of the secret, and then verify that the specified version of
+ * them are successfully destroyed.
+ *
+ * @throws VaultException On error.
+ */
+ @Test
+ public void testDestroy() throws VaultException {
+ final Vault vault = container.getRootVault();
+ final Map testMap = new HashMap<>();
+ testMap.put("value", "world");
+
+ vault.logical().write("secret/hello", testMap);
+ vault.logical().write("secret/hello", testMap);
+ vault.logical().write("secret/hello", testMap);
+ assertTrue(vault.logical().read("secret/hello").getData().containsKey("value"));
+ vault.logical().destroy("secret/hello", new int[]{1});
+ assertTrue(vault.logical().read("secret/hello").getData().containsKey("value"));
+ try {
+ vault.logical().read("secret/hello", true, 1);
+ } catch (VaultException e) {
+ Assert.assertEquals(e.getHttpStatusCode(), 404);
+ }
+ }
+
+ @Rule
+ public ExpectedException expectedEx = ExpectedException.none();
+
+ /**
+ * Tests that exception message includes errors returned by Vault
+ *
+ * @throws VaultException On error.
+ */
+ @Test
+ public void testReadPermissionDeniedReturnedByVault() throws VaultException {
+ final Vault vault = container.getVault(NONROOT_TOKEN);
+ LogicalResponse read = vault.logical().read("secret/null");
+ TestCase.assertEquals(403, read.getRestResponse().getStatus());
+ }
+
+ /**
+ * Tests that exception message includes errors returned by Vault
+ *
+ * @throws VaultException On error.
+ */
+ @Test
+ public void testWritePermissionDeniedReturnedByVault() throws VaultException {
+ final Vault vault = container.getVault(NONROOT_TOKEN);
+ final Map testMap = new HashMap<>();
+ testMap.put("value", null);
+ LogicalResponse write = vault.logical().write("secret/null", testMap);
+ TestCase.assertEquals(403, write.getRestResponse().getStatus());
+ }
+
+ /**
+ * Tests that exception message includes errors returned by Vault
+ *
+ * @throws VaultException On error.
+ */
+ @Test
+ public void testDeleteExceptionMessageIncludesErrorsReturnedByVault() throws VaultException {
+ expectedEx.expect(VaultException.class);
+ expectedEx.expectMessage("permission denied");
+
+ final Vault vault = container.getVault(NONROOT_TOKEN);
+ LogicalResponse delete = vault.logical().delete("secret/null");
+ TestCase.assertEquals(403, delete.getRestResponse().getStatus());
+ }
+
+ /**
+ * Tests that exception message includes errors returned by Vault
+ *
+ * @throws VaultException On error.
+ */
+ @Test
+ public void testListPermissionDeniedReturnedByVault() throws VaultException {
+ final Vault vault = container.getVault(NONROOT_TOKEN);
+ LogicalResponse response = vault.logical().list("secret/null");
+ TestCase.assertEquals(403, response.getRestResponse().getStatus());
+ }
+
+ /**
+ * Tests that the various supported data types are correctly marshaled and unmarshaled to and from Vault.
+ *
+ * @throws VaultException On error.
+ */
+ @Test
+ public void testReadReturnedByVaultOn404() throws VaultException {
+ final Vault vault = container.getRootVault();
+ final String path = "secret/" + UUID.randomUUID();
+ LogicalResponse read = vault.logical().read(path);
+ TestCase.assertEquals(404, read.getRestResponse().getStatus());
+ }
+
+ /**
+ * Tests that the various supported data types are marshaled/unmarshaled to and from Vault, using KV Engine version 2.
+ *
+ * @throws VaultException On error.
+ */
+ @Test
+ public void testWriteAndReadAllDataTypes() throws VaultException {
+ final String path = "secret/hello";
+
+ final Map nameValuePairs = new HashMap<>();
+ nameValuePairs.put("testBoolean", true);
+ nameValuePairs.put("testInt", 1001);
+ nameValuePairs.put("testFloat", 123.456);
+ nameValuePairs.put("testString", "Hello world!");
+ nameValuePairs.put("testObject", "{ \"nestedBool\": true, \"nestedInt\": 123, \"nestedFloat\": 123.456, \"nestedString\": \"foobar\", \"nestedArray\": [\"foo\", \"bar\"], \"nestedObject\": { \"foo\": \"bar\" } }");
+
+ final Vault vault = container.getRootVault();
+ vault.logical().write(path, nameValuePairs);
+
+ final Map valuesRead = vault.logical().read(path).getData();
+ for (final Map.Entry entry : valuesRead.entrySet()) {
+ System.out.println(entry.getKey() + " - " + entry.getValue());
+ }
+ }
+
+ /**
+ * Tests that the various supported data types are marshaled/unmarshaled to and from Vault, using KV Engine version 1.
+ *
+ * @throws VaultException On error.
+ */
+ @Test
+ public void testWriteAndReadAllDataTypesKVEngineV1() throws VaultException {
+ final String path = "kv-v1/hello";
+
+ final Map nameValuePairs = new HashMap<>();
+ nameValuePairs.put("testBoolean", true);
+ nameValuePairs.put("testInt", 1001);
+ nameValuePairs.put("testFloat", 123.456);
+ nameValuePairs.put("testString", "Hello world!");
+ nameValuePairs.put("testObject", "{ \"nestedBool\": true, \"nestedInt\": 123, \"nestedFloat\": 123.456, \"nestedString\": \"foobar\", \"nestedArray\": [\"foo\", \"bar\"], \"nestedObject\": { \"foo\": \"bar\" } }");
+
+ final Vault vault = container.getRootVaultWithCustomVaultConfig(new VaultConfig().engineVersion(1));
+ vault.logical().write(path, nameValuePairs);
+
+ final Map valuesRead = vault.logical().read(path).getData();
+ for (final Map.Entry entry : valuesRead.entrySet()) {
+ System.out.println(entry.getKey() + " - " + entry.getValue());
+ }
+ }
+
+ /**
+ * Tests that the Vault KV Engine Version API call is successful when attempting to discover the KV Engine versions.
+ *
+ * @throws VaultException On error.
+ */
+ @Test
+ public void testVaultKVEnginePathsCanBeDiscovered() throws VaultException {
+ final Vault vault = container.getRootVault();
+ Map secretPaths = vault.getSecretEngineVersions();
+ Assert.assertEquals(secretPaths.get("secret/"), "2");
+ Assert.assertEquals(secretPaths.get("kv-v1/"), "1");
+ Assert.assertNull(secretPaths.get("notInMap"));
+ }
+
+ /**
+ * Tests that a specific version of a secret can be deleted.
+ *
+ * @throws VaultException On error.
+ */
+ @Test
+ public void testVaultDeleteASpecificVersion() throws VaultException {
+ final String pathToWrite = "secret/hello";
+ final String pathToRead = "secret/hello";
+ final String version1Value = "world";
+ final String version2Value = "world2";
+ final Vault vault = container.getRootVault();
+ final Map testMap = new HashMap<>();
+ testMap.put("value", version1Value);
+ vault.logical().write(pathToWrite, testMap);
+ testMap.put("value", version2Value);
+ vault.logical().write(pathToWrite, testMap);
+ vault.logical().delete(pathToWrite, new int[]{1});
+ try {
+ vault.logical().read(pathToRead, true, 1).getData().get("value");
+ } catch (VaultException e) {
+ Assert.assertEquals(e.getHttpStatusCode(), 404);
+ }
+ final String valueRead = vault.logical().read(pathToRead, true, 2).getData().get("value");
+ Assert.assertEquals(valueRead, version2Value);
+ }
+
+ /**
+ * Tests that a specific version of a secret can be undeleted.
+ *
+ * @throws VaultException On error.
+ */
+ @Test
+ public void testVaultUnDeleteASpecificVersion() throws VaultException {
+ final String pathToWrite = "secret/hello";
+ final String pathToRead = "secret/hello";
+ final String version1Value = "world";
+ final Vault vault = container.getRootVault();
+ final Map testMap = new HashMap<>();
+ testMap.put("value", version1Value);
+ vault.logical().write(pathToWrite, testMap);
+ vault.logical().delete(pathToWrite, new int[]{1});
+ try {
+ vault.logical().read(pathToRead, true, 1).getData().get("value");
+ } catch (VaultException e) {
+ Assert.assertEquals(e.getHttpStatusCode(), 404);
+ }
+ vault.logical().unDelete(pathToRead, new int[]{1});
+ final String valueRead = vault.logical().read(pathToRead, true, 1).getData().get("value");
+ Assert.assertEquals(valueRead, version1Value);
+ }
+
+ /**
+ * Tests that a specific KV engine can be upgraded from Version 1 to Version 2.
+ *
+ * @throws VaultException On error.
+ */
+ @Test
+ public void testVaultUpgrade() throws VaultException {
+ final String kvToUpgrade = "kv-v1-Upgrade-Test/";
+ final Vault vault = container.getRootVaultWithCustomVaultConfig(new VaultConfig().engineVersion(1));
+ String kVOriginalVersion = vault.getSecretEngineVersions().get("kv-v1-Upgrade-Test/");
+ vault.logical().upgrade(kvToUpgrade);
+ String kVUpgradedVersion = vault.getSecretEngineVersions().get("kv-v1-Upgrade-Test/");
+ Assert.assertEquals(kVOriginalVersion, "1");
+ Assert.assertEquals(kVUpgradedVersion, "2");
+ }
+}
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/MountsTests.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/MountsTests.java
new file mode 100644
index 00000000..1b9f67b1
--- /dev/null
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/MountsTests.java
@@ -0,0 +1,199 @@
+package io.github.jopenlibs.vault.v1_11_4.api;
+
+import io.github.jopenlibs.vault.Vault;
+import io.github.jopenlibs.vault.VaultException;
+import io.github.jopenlibs.vault.api.mounts.Mount;
+import io.github.jopenlibs.vault.api.mounts.MountConfig;
+import io.github.jopenlibs.vault.api.mounts.MountPayload;
+import io.github.jopenlibs.vault.api.mounts.MountType;
+import io.github.jopenlibs.vault.api.mounts.TimeToLive;
+import io.github.jopenlibs.vault.response.MountResponse;
+import io.github.jopenlibs.vault.v1_11_4.util.VaultContainer;
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import junit.framework.TestCase;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static junit.framework.TestCase.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/** Integration tests for for operations on Vault's /v1/sys/mounts/*
REST endpoints. */
+public class MountsTests {
+
+ @ClassRule
+ public static final VaultContainer container = new VaultContainer();
+
+ @Rule
+ public ExpectedException expectedEx = ExpectedException.none();
+
+ @BeforeClass
+ public static void setupClass() throws IOException, InterruptedException {
+ container.initAndUnsealVault();
+ container.setupBackendPki();
+ }
+
+ @Test
+ public void testList() throws VaultException {
+ final Vault vault = container.getRootVault();
+
+ final MountResponse response = vault.mounts().list();
+ final Map mounts = response.getMounts();
+
+ assertTrue(mounts.containsKey("pki-custom-path-1/"));
+ assertTrue(mounts.containsKey("pki-custom-path-2/"));
+ assertTrue(mounts.containsKey("pki-custom-path-3/"));
+ }
+
+ @Test
+ public void testEnable() throws VaultException {
+ final Vault vault = container.getRootVault();
+
+ final MountPayload payload = new MountPayload()
+ .defaultLeaseTtl(TimeToLive.of(12, TimeUnit.HOURS))
+ .maxLeaseTtl(TimeToLive.of(12, TimeUnit.HOURS))
+ .description("description for pki engine");
+
+ final MountResponse response = vault.mounts().enable("pki-itest-path-1", MountType.PKI, payload);
+
+ TestCase.assertEquals(204, response.getRestResponse().getStatus());
+ }
+
+ @Test
+ public void testEnableExceptionAlreadyExist() throws VaultException {
+ expectedEx.expect(VaultException.class);
+ expectedEx.expectMessage("path is already in use");
+
+ final Vault vault = container.getRootVault();
+
+ final MountPayload payload = new MountPayload()
+ .defaultLeaseTtl(TimeToLive.of(168, TimeUnit.HOURS))
+ .maxLeaseTtl(TimeToLive.of(168, TimeUnit.HOURS))
+ .description("description for pki engine");
+
+ vault.mounts().enable("pki-custom-path-1", MountType.PKI, payload);
+ }
+
+ @Test
+ public void testEnableExceptionNullType() throws VaultException {
+ expectedEx.expect(VaultException.class);
+ expectedEx.expectMessage("Mount type is missing");
+
+ final Vault vault = container.getRootVault();
+
+ final MountPayload payload = new MountPayload()
+ .defaultLeaseTtl(TimeToLive.of(30, TimeUnit.MINUTES))
+ .maxLeaseTtl(TimeToLive.of(30, TimeUnit.MINUTES))
+ .description("description for pki engine");
+
+ vault.mounts().enable("pki-itest-path-2", null, payload);
+ }
+
+ @Test
+ public void testEnableExceptionNullTimeUnit() throws VaultException {
+ expectedEx.expect(NullPointerException.class);
+
+ final Vault vault = container.getRootVault();
+
+ final MountPayload payload = new MountPayload()
+ .defaultLeaseTtl(TimeToLive.of(7, null));
+
+ vault.mounts().enable("pki-itest-path-3", MountType.PKI, payload);
+ }
+
+ @Test
+ public void testEnableExceptionInvalidTimeUnit() throws VaultException {
+ expectedEx.expect(VaultException.class);
+ expectedEx.expectMessage("is not a vaild TimeUnit for Vault");
+
+ final Vault vault = container.getRootVault();
+
+ final MountPayload payload = new MountPayload()
+ .defaultLeaseTtl(TimeToLive.of(7, TimeUnit.DAYS));
+
+ vault.mounts().enable("pki-itest-path-4", MountType.PKI, payload);
+ }
+
+ @Test
+ public void testDisable() throws VaultException {
+ final Vault vault = container.getRootVault();
+
+ final MountResponse response = vault.mounts().disable("pki-custom-path-3");
+
+ TestCase.assertEquals(204, response.getRestResponse().getStatus());
+ }
+
+ @Test
+ public void testRead() throws VaultException {
+ final Vault vault = container.getRootVault();
+
+ final MountPayload payload = new MountPayload()
+ .defaultLeaseTtl(TimeToLive.of(360, TimeUnit.MINUTES))
+ .maxLeaseTtl(TimeToLive.of(360, TimeUnit.MINUTES));
+
+ vault.mounts().enable("pki-predefined-path-1", MountType.PKI, payload);
+
+ final MountResponse response = vault.mounts().read("pki-predefined-path-1");
+ final Mount mount = response.getMount();
+ final MountConfig config = mount.getConfig();
+
+ TestCase.assertEquals(200, response.getRestResponse().getStatus());
+
+ assertEquals(Integer.valueOf(21600), config.getDefaultLeaseTtl());
+ assertEquals(Integer.valueOf(21600), config.getMaxLeaseTtl());
+ }
+
+ @Test
+ public void testReadExceptionNotFound() throws VaultException {
+ expectedEx.expect(VaultException.class);
+ expectedEx.expectMessage("cannot fetch sysview for path");
+
+ final Vault vault = container.getRootVault();
+
+ vault.mounts().read("pki-non-existing-path");
+ }
+
+ @Test
+ public void testTune() throws VaultException {
+ final Vault vault = container.getRootVault();
+
+ final MountPayload enablePayload = new MountPayload()
+ .defaultLeaseTtl(TimeToLive.of(6, TimeUnit.HOURS))
+ .maxLeaseTtl(TimeToLive.of(6, TimeUnit.HOURS));
+
+ vault.mounts().enable("pki-predefined-path-2", MountType.PKI, enablePayload);
+
+ final MountPayload tunePayload = new MountPayload()
+ .defaultLeaseTtl(TimeToLive.of(12, TimeUnit.HOURS))
+ .maxLeaseTtl(TimeToLive.of(12, TimeUnit.HOURS));
+
+ final MountResponse tuneResponse = vault.mounts().tune("pki-predefined-path-2", tunePayload);
+
+ TestCase.assertEquals(204, tuneResponse.getRestResponse().getStatus());
+
+ final MountResponse response = vault.mounts().read("pki-predefined-path-2");
+ final Mount mount = response.getMount();
+ final MountConfig config = mount.getConfig();
+
+ assertEquals(Integer.valueOf(43200), config.getDefaultLeaseTtl());
+ assertEquals(Integer.valueOf(43200), config.getMaxLeaseTtl());
+ }
+
+ @Test
+ public void testTuneExceptionNotFound() throws VaultException {
+ expectedEx.expect(VaultException.class);
+ expectedEx.expectMessage("no mount entry found");
+
+ final Vault vault = container.getRootVault();
+
+ final MountPayload tunePayload = new MountPayload()
+ .defaultLeaseTtl(TimeToLive.of(24, TimeUnit.HOURS))
+ .maxLeaseTtl(TimeToLive.of(24, TimeUnit.HOURS));
+
+ vault.mounts().tune("pki-non-existing-path", tunePayload);
+ }
+}
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/SealTests.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/SealTests.java
new file mode 100644
index 00000000..2018fab0
--- /dev/null
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/SealTests.java
@@ -0,0 +1,56 @@
+package io.github.jopenlibs.vault.v1_11_4.api;
+
+import io.github.jopenlibs.vault.VaultException;
+import io.github.jopenlibs.vault.response.SealResponse;
+import io.github.jopenlibs.vault.v1_11_4.util.VaultContainer;
+import java.io.IOException;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Integration tests for the seal-related (i.e. "seal") Vault API operations.
+ */
+public class SealTests {
+
+ @ClassRule
+ public static final VaultContainer container = new VaultContainer();
+
+ private static String unsealKey = null;
+
+ @BeforeClass
+ public static void setupClass() throws IOException, InterruptedException {
+ container.initAndUnsealVault();
+ unsealKey = container.getUnsealKey();
+ }
+
+ @Test
+ public void testSealStatus_returnsFalse_fromInitialUnsealedState() throws VaultException {
+ // Due to the "setupClass()" static method, the Vault instance should be in an unsealed state
+ // at the start of this test suite.
+ final SealResponse response = container.getVault().seal().sealStatus();
+
+ assertFalse(response.getSealed());
+ assertEquals(1, response.getNumberOfShares().longValue());
+ assertEquals(1, response.getThreshold().longValue());
+ assertEquals(0, response.getProgress().longValue());
+ }
+
+ @Test
+ public void testSealAndUnseal_togglesAndRestoresUnsealedState() throws VaultException {
+ // Seal Vault and verify its status
+ container.getRootVault().seal().seal();
+ final SealResponse sealResponse = container.getRootVault().seal().sealStatus();
+ assertTrue(sealResponse.getSealed());
+
+ // Unseal Vault again, and verify its status
+ container.getRootVault().seal().unseal(unsealKey);
+ final SealResponse unsealResponse = container.getRootVault().seal().sealStatus();
+ assertFalse(unsealResponse.getSealed());
+ }
+
+}
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/VaultAgentTests.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/VaultAgentTests.java
new file mode 100644
index 00000000..8ee7c1da
--- /dev/null
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/VaultAgentTests.java
@@ -0,0 +1,71 @@
+package io.github.jopenlibs.vault.v1_11_4.api;
+
+import io.github.jopenlibs.vault.Vault;
+import io.github.jopenlibs.vault.VaultConfig;
+import io.github.jopenlibs.vault.VaultException;
+import io.github.jopenlibs.vault.response.LogicalResponse;
+import io.github.jopenlibs.vault.v1_11_4.util.VaultAgentContainer;
+import io.github.jopenlibs.vault.v1_11_4.util.VaultContainer;
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import static junit.framework.TestCase.assertEquals;
+import static org.apache.commons.io.FileUtils.writeStringToFile;
+import static org.junit.Assert.assertNotNull;
+
+public class VaultAgentTests {
+ @ClassRule
+ public static final VaultContainer container = new VaultContainer();
+ @ClassRule
+ public static final TemporaryFolder temp = new TemporaryFolder();
+ @ClassRule
+ public static VaultAgentContainer vaultAgentContainer;
+
+ @BeforeClass
+ public static void setupClass() throws IOException, InterruptedException, VaultException {
+ container.initAndUnsealVault();
+ container.setupBackendAppRole();
+ container.setEngineVersions();
+
+ final Vault vault = container.getRootVaultWithCustomVaultConfig(new VaultConfig().engineVersion(1));
+
+ final LogicalResponse roleIdResponse = vault.logical().read("auth/approle/role/testrole/role-id");
+ String appRoleId = roleIdResponse.getData().get("role_id");
+ final LogicalResponse secretIdResponse = vault.logical().write("auth/approle/role/testrole/secret-id", null);
+ String secretId = secretIdResponse.getData().get("secret_id");
+
+ assertNotNull(appRoleId);
+ assertNotNull(secretId);
+
+ temp.create();
+ File role_id = temp.newFile("role_id");
+ File secret_id = temp.newFile("secret_id");
+ writeStringToFile(role_id, appRoleId);
+ writeStringToFile(secret_id, secretId);
+ vaultAgentContainer = new VaultAgentContainer(role_id.toPath(), secret_id.toPath());
+ vaultAgentContainer.start();
+ }
+
+ @Test
+ public void testWriteAndReadFromAgent() throws VaultException {
+ final String pathToWrite = "secret/hello";
+ final String pathToRead = "secret/hello";
+
+ final String value = "world";
+ final Vault vault = vaultAgentContainer.getVault();
+
+ final Map testMap = new HashMap<>();
+ testMap.put("value", value);
+
+ vault.logical().write(pathToWrite, testMap);
+
+ final String valueRead = vault.logical().read(pathToRead).getData().get("value");
+ assertEquals(value, valueRead);
+ }
+}
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/WrapUnwrapTests.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/WrapUnwrapTests.java
new file mode 100644
index 00000000..6316dee8
--- /dev/null
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/api/WrapUnwrapTests.java
@@ -0,0 +1,58 @@
+package io.github.jopenlibs.vault.v1_11_4.api;
+
+import io.github.jopenlibs.vault.Vault;
+import io.github.jopenlibs.vault.VaultException;
+import io.github.jopenlibs.vault.json.JsonObject;
+import io.github.jopenlibs.vault.response.AuthResponse;
+import io.github.jopenlibs.vault.response.UnwrapResponse;
+import io.github.jopenlibs.vault.response.WrapResponse;
+import io.github.jopenlibs.vault.v1_11_4.util.VaultContainer;
+import java.io.IOException;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Integration tests for the wrap/unwrap data.
+ */
+public class WrapUnwrapTests {
+
+ @ClassRule
+ public static final VaultContainer container = new VaultContainer();
+
+ private static String NONROOT_TOKEN;
+
+ @BeforeClass
+ public static void setupClass() throws IOException, InterruptedException, VaultException {
+ container.initAndUnsealVault();
+ container.setupBackendUserPass();
+
+ final Vault vault = container.getVault();
+ final AuthResponse response = vault.auth()
+ .loginByUserPass(VaultContainer.USER_ID, VaultContainer.PASSWORD);
+
+ NONROOT_TOKEN = response.getAuthClientToken();
+ }
+
+ /**
+ * Tests wrap/unwrap data.
+ */
+ @Test
+ public void testWrapUnwrap() throws Exception {
+ final Vault vault = container.getVault(NONROOT_TOKEN);
+
+ WrapResponse wrapResponse = vault.auth().wrap(
+ new JsonObject()
+ .add("foo", "bar")
+ .add("zoo", "zar"),
+ 60
+ );
+
+ UnwrapResponse unwrapResponse = vault.auth().unwrap(wrapResponse.getToken());
+
+ assertEquals("bar", unwrapResponse.getData().get("foo").asString());
+ assertEquals("zar", unwrapResponse.getData().get("zoo").asString());
+ }
+}
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/util/DbContainer.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/util/DbContainer.java
new file mode 100644
index 00000000..da937a3f
--- /dev/null
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/util/DbContainer.java
@@ -0,0 +1,34 @@
+package io.github.jopenlibs.vault.v1_11_4.util;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.output.Slf4jLogConsumer;
+import org.testcontainers.containers.wait.strategy.HostPortWaitStrategy;
+import org.testcontainers.lifecycle.TestDescription;
+import org.testcontainers.lifecycle.TestLifecycleAware;
+
+import static org.junit.Assume.assumeTrue;
+
+public class DbContainer extends GenericContainer implements TestConstants, TestLifecycleAware {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(DbContainer.class);
+
+ public static final String hostname = "postgres";
+
+ public DbContainer() {
+ super("postgres:11.3-alpine");
+ this.withNetwork(CONTAINER_NETWORK)
+ .withNetworkAliases(hostname)
+ .withEnv("POSTGRES_PASSWORD", POSTGRES_PASSWORD)
+ .withEnv("POSTGRES_USER", POSTGRES_USER)
+ .withExposedPorts(5432)
+ .withLogConsumer(new Slf4jLogConsumer(LOGGER))
+ .waitingFor(new HostPortWaitStrategy());
+ }
+
+ @Override
+ public void beforeTest(TestDescription description) {
+ assumeTrue(DOCKER_AVAILABLE);
+ }
+}
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/util/SSLUtils.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/util/SSLUtils.java
new file mode 100644
index 00000000..bc61bd39
--- /dev/null
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/util/SSLUtils.java
@@ -0,0 +1,293 @@
+package io.github.jopenlibs.vault.v1_11_4.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.Security;
+import java.security.SignatureException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Base64;
+import java.util.Date;
+import java.util.HashMap;
+import javax.security.auth.x500.X500Principal;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.cert.CertIOException;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.util.PrivateKeyFactory;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.openssl.PEMParser;
+import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
+import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.bc.BcContentSignerBuilder;
+import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+import org.bouncycastle.pkcs.PKCS10CertificationRequest;
+import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder;
+import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
+
+/**
+ * Static utility methods for generating client-side SSL certs and keys, for tests that use Vault's TLS Certificate
+ * auth backend. Right now, all such code is isolated to `AuthBackendCertTests`.
+ */
+public class SSLUtils implements TestConstants {
+
+ private SSLUtils() {
+ }
+
+ /**
+ * Constructs a Java truststore in JKS format, containing the Vault server certificate, so that Vault clients
+ * configured with this JKS will trust that certificate.
+ *
+ * Also constructs a JKS keystore, with a client certificate to use for authentication with Vault's TLS
+ * Certificate auth backend. Stores this cert as a PEM file as well, so that can be registered with Vault
+ * as a recognized certificate in {@link VaultContainer#setupBackendCert(String)}.
+ *
+ * This method must be called AFTER {@link VaultContainer#initAndUnsealVault()}, and BEFORE
+ * {@link VaultContainer#setupBackendCert(String)}.
+ *
+ * @throws IOException When certificate was not created
+ * @return
+ */
+ public static HashMap createClientCertAndKey() throws IOException {
+
+ Security.addProvider(new BouncyCastleProvider());
+ final X509CertificateHolder certificateHolder = getX509CertificateHolder();
+ final X509Certificate vaultCertificate = getCertificate(certificateHolder);
+
+ KeyStore clientTrustStore = getClientTrustStore(vaultCertificate);
+
+ // Store the Vault's server certificate as a trusted cert in the truststore
+
+ // Generate a client certificate, and store it in a Java keystore
+ final KeyPair keyPair = generateKeyPair();
+ final X509Certificate clientCertificate = generateCert(keyPair);
+ if (clientCertificate == null) {
+ throw new IOException("Failed to generate certificate");
+ }
+ final KeyStore clientKeystore = getClientKeystore(keyPair, clientCertificate);
+
+ // Also write the client certificate to a PEM file, so it can be registered with Vault
+ String certToPem = certToPem(clientCertificate);
+ String privateKeyToPem = privateKeyToPem(keyPair.getPrivate());
+ return new HashMap() {
+ {
+ put("clientKeystore", clientKeystore);
+ put("clientTrustStore", clientTrustStore);
+ put("cert", certToPem);
+ put("privateKey", privateKeyToPem);
+ }
+ };
+ }
+
+ private static KeyStore getClientTrustStore(X509Certificate vaultCertificate) throws IOException {
+ final KeyStore trustStore = emptyStore();
+ try {
+ trustStore.setCertificateEntry("cert", vaultCertificate);
+ } catch (KeyStoreException e) {
+ throw new IOException("Cannot create trust keystore.", e);
+ }
+ return trustStore;
+ }
+
+ private static KeyStore getClientKeystore(KeyPair keyPair, X509Certificate clientCertificate) {
+ try {
+ final KeyStore keyStore = emptyStore();
+ keyStore.setKeyEntry("privatekey", keyPair.getPrivate(), PASSWORD.toCharArray(), new Certificate[]{clientCertificate});
+ keyStore.setCertificateEntry("cert", clientCertificate);
+ return keyStore;
+ } catch (KeyStoreException | IOException e) {
+ return null;
+ }
+ }
+
+ private static X509CertificateHolder getX509CertificateHolder() {
+ final PEMParser pemParser;
+ try (FileReader fileReader = new FileReader(CERT_PEMFILE)) {
+ pemParser = new PEMParser(fileReader);
+ return (X509CertificateHolder) pemParser.readObject();
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ private static X509Certificate getCertificate(X509CertificateHolder certificateHolder) {
+ try {
+ return new JcaX509CertificateConverter()
+ .setProvider(BouncyCastleProvider.PROVIDER_NAME)
+ .getCertificate(certificateHolder);
+ } catch (CertificateException e) {
+ return null;
+ }
+ }
+
+ /**
+ * See https://www.cryptoworkshop.com/guide/, chapter 3
+ *
+ * @return A 4096-bit RSA key pair
+ */
+ private static KeyPair generateKeyPair() throws IOException {
+ try {
+ final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", new BouncyCastleProvider());
+ keyPairGenerator.initialize(4096);
+ KeyPair keyPair = keyPairGenerator.genKeyPair();
+ if (keyPair == null) {
+ throw new IOException("Failed to generate keypair");
+ }
+ return keyPair;
+ } catch (NoSuchAlgorithmException e) {
+ throw new IOException("Failed to generate keypair", e);
+ }
+ }
+
+ /**
+ * See http://www.programcreek.com/java-api-examples/index.php?api=org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder
+ *
+ * @param keyPair The RSA keypair with which to generate the certificate
+ * @return An X509 certificate
+ */
+ private static X509Certificate generateCert(final KeyPair keyPair) {
+ String issuer = "C=AU, O=The Legion of the Bouncy Castle, OU=Client Certificate, CN=localhost";
+ final X509v3CertificateBuilder certificateBuilder = new X509v3CertificateBuilder(
+ new X500Name(issuer),
+ BigInteger.ONE,
+ new Date(System.currentTimeMillis() - 1000L * 60 * 60 * 24 * 30),
+ new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 30)),
+ new X500Name(issuer),
+ SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded())
+ );
+
+ final GeneralNames subjectAltNames = new GeneralNames(new GeneralName(GeneralName.iPAddress, "127.0.0.1"));
+ try {
+ certificateBuilder.addExtension(Extension.subjectAlternativeName, false, subjectAltNames);
+ } catch (CertIOException e) {
+ e.printStackTrace();
+ return null;
+ }
+
+ final AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find("SHA1WithRSAEncryption");
+ final AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
+ final BcContentSignerBuilder signerBuilder = new BcRSAContentSignerBuilder(sigAlgId, digAlgId);
+ final X509CertificateHolder x509CertificateHolder;
+ try {
+ final AsymmetricKeyParameter keyp = PrivateKeyFactory.createKey(keyPair.getPrivate().getEncoded());
+ final ContentSigner signer = signerBuilder.build(keyp);
+ x509CertificateHolder = certificateBuilder.build(signer);
+ } catch (IOException | OperatorCreationException e) {
+ e.printStackTrace();
+ return null;
+ }
+
+ final X509Certificate certificate;
+ try {
+ certificate = new JcaX509CertificateConverter().getCertificate(x509CertificateHolder);
+ certificate.checkValidity(new Date());
+ certificate.verify(keyPair.getPublic());
+ } catch (CertificateException | SignatureException | InvalidKeyException | NoSuchAlgorithmException | NoSuchProviderException e) {
+ e.printStackTrace();
+ return null;
+ }
+
+ return certificate;
+ }
+
+ /**
+ * See https://stackoverflow.com/questions/3313020/write-x509-certificate-into-pem-formatted-string-in-java
+ *
+ * @param certificate An X509 certificate
+ * @return String certificate in pem format
+ */
+ private static String certToPem(final X509Certificate certificate) throws IOException {
+ final Base64.Encoder encoder = Base64.getMimeEncoder();
+
+ final String certHeader = "-----BEGIN CERTIFICATE-----\n";
+ final String certFooter = "\n-----END CERTIFICATE-----";
+ final byte[] certBytes;
+ try {
+ certBytes = certificate.getEncoded();
+ } catch (CertificateEncodingException e) {
+ throw new IOException("Failed to encode certificate", e);
+ }
+ final String certContents = new String(encoder.encode(certBytes));
+ return certHeader + certContents + certFooter;
+ }
+
+ /**
+ * See https://stackoverflow.com/questions/3313020/write-x509-certificate-into-pem-formatted-string-in-java
+ *
+ * @param key An RSA private key
+ * @return String private key in pem format
+ */
+ private static String privateKeyToPem(final PrivateKey key) {
+ final Base64.Encoder encoder = Base64.getMimeEncoder();
+
+ final String keyHeader = "-----BEGIN PRIVATE KEY-----\n";
+ final String keyFooter = "\n-----END PRIVATE KEY-----";
+ final byte[] keyBytes = key.getEncoded();
+ final String keyContents = new String(encoder.encode(keyBytes));
+ return keyHeader + keyContents + keyFooter;
+ }
+
+ /**
+ * @param CN Common Name, is X.509 speak for the name that distinguishes
+ * the Certificate best, and ties it to your Organization
+ * @param OU Organizational unit
+ * @param O Organization NAME
+ * @param L Location
+ * @param S State
+ * @param C Country
+ * @return
+ * @throws Exception
+ */
+ public static String generatePKCS10(KeyPair kp, String CN, String OU, String O,
+ String L, String S, String C) throws IOException, OperatorCreationException {
+ X500Principal subject = new X500Principal(String.format("C=%s, ST=%s, L=%s, O=%s, OU=%s, CN=%S", C, S, L, O, OU, CN));
+ ContentSigner signGen = new JcaContentSignerBuilder("SHA256withRSA").build(kp.getPrivate());
+ PKCS10CertificationRequestBuilder builder = new JcaPKCS10CertificationRequestBuilder(subject, kp.getPublic());
+ PKCS10CertificationRequest csr = builder.build(signGen);
+ try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
+ try (Writer osWriter = new OutputStreamWriter(output)) {
+ try (JcaPEMWriter pem = new JcaPEMWriter(osWriter)) {
+ pem.writeObject(csr);
+ }
+ }
+ return new String(output.toByteArray());
+ }
+ }
+
+ public static KeyStore emptyStore() throws IOException {
+ try {
+ KeyStore ks = KeyStore.getInstance("JKS");
+
+ // Loading creates the store, can't do anything with it until it's loaded
+ ks.load(null, PASSWORD.toCharArray());
+ return ks;
+ } catch (KeyStoreException | CertificateException | NoSuchAlgorithmException | IOException e) {
+ throw new IOException("Cannot create empty keystore.", e);
+ }
+ }
+}
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/util/TestConstants.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/util/TestConstants.java
new file mode 100644
index 00000000..342762b1
--- /dev/null
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/util/TestConstants.java
@@ -0,0 +1,40 @@
+package io.github.jopenlibs.vault.v1_11_4.util;
+
+import java.io.File;
+import org.testcontainers.containers.Network;
+import org.testcontainers.utility.TestEnvironment;
+
+/**
+ * Various constants used throughout the integration test suite, but primarily by {@link VaultContainer}
+ * and {@link SSLUtils}. Mostly username/password credentials and other Vault configuration values, and
+ * path locations for SSL artifacts.
+ */
+public interface TestConstants {
+ String POSTGRES_PASSWORD = "superpassword1";
+ String POSTGRES_USER = "superuser1";
+
+ String APP_ID = "fake_app";
+ String USER_ID = "fake_user";
+ String PASSWORD = "fake_password";
+ int MAX_RETRIES = 5;
+ int RETRY_MILLIS = 1000;
+
+ String CURRENT_WORKING_DIRECTORY = System.getProperty("user.dir");
+ String SSL_DIRECTORY = CURRENT_WORKING_DIRECTORY + File.separator + "ssl";
+ String CERT_PEMFILE = SSL_DIRECTORY + File.separator + "root-cert.pem";
+
+ String CLIENT_CERT_PEMFILE = SSL_DIRECTORY + File.separator + "client-cert.pem";
+
+ String CONTAINER_STARTUP_SCRIPT = "/vault/config/startup.sh";
+ String CONTAINER_CONFIG_FILE = "/vault/config/config.json";
+ String CONTAINER_OPENSSL_CONFIG_FILE = "/vault/config/libressl.conf";
+ String CONTAINER_SSL_DIRECTORY = "/vault/config/ssl";
+ String CONTAINER_CERT_PEMFILE = CONTAINER_SSL_DIRECTORY + "/vault-cert.pem";
+ String CONTAINER_CLIENT_CERT_PEMFILE = CONTAINER_SSL_DIRECTORY + "/client-cert.pem";
+
+ String AGENT_CONFIG_FILE = "/home/vault/agent.hcl";
+ String APPROLE_POLICY_FILE = "/home/vault/approlePolicy.hcl";
+
+ Network CONTAINER_NETWORK = Network.newNetwork();
+ boolean DOCKER_AVAILABLE = TestEnvironment.dockerApiAtLeast("1.10");
+}
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/util/VaultAgentContainer.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/util/VaultAgentContainer.java
new file mode 100644
index 00000000..54c47b74
--- /dev/null
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/util/VaultAgentContainer.java
@@ -0,0 +1,78 @@
+package io.github.jopenlibs.vault.v1_11_4.util;
+
+import com.github.dockerjava.api.model.Capability;
+import io.github.jopenlibs.vault.Vault;
+import io.github.jopenlibs.vault.VaultConfig;
+import io.github.jopenlibs.vault.VaultException;
+import java.nio.file.Path;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testcontainers.containers.BindMode;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.output.Slf4jLogConsumer;
+import org.testcontainers.containers.wait.strategy.Wait;
+import org.testcontainers.lifecycle.TestDescription;
+import org.testcontainers.lifecycle.TestLifecycleAware;
+
+import static org.junit.Assume.assumeTrue;
+import static org.testcontainers.utility.MountableFile.forHostPath;
+
+public class VaultAgentContainer extends GenericContainer implements
+ TestConstants, TestLifecycleAware {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(VaultAgentContainer.class);
+
+ /**
+ * Establishes a running Docker container, hosting a Vault agent instance.
+ */
+ public VaultAgentContainer(
+ Path roleId,
+ Path secretId) {
+ super("vault:1.11.4");
+ this.withNetwork(CONTAINER_NETWORK)
+ .withNetworkAliases("agent")
+ .withClasspathResourceMapping("/agent.hcl", AGENT_CONFIG_FILE, BindMode.READ_ONLY)
+ .withFileSystemBind(SSL_DIRECTORY, CONTAINER_SSL_DIRECTORY, BindMode.READ_ONLY)
+ .withCreateContainerCmdModifier(command -> command.withCapAdd(Capability.IPC_LOCK))
+ .withCopyFileToContainer(forHostPath(roleId), "/home/vault/role_id")
+ .withCopyFileToContainer(forHostPath(secretId), "/home/vault/secret_id")
+ .withExposedPorts(8100)
+ .withEnv("VAULT_CACERT", CONTAINER_CERT_PEMFILE)
+ .withCommand(String.format("vault agent -config=%s", AGENT_CONFIG_FILE))
+ .withLogConsumer(new Slf4jLogConsumer(LOGGER))
+ .waitingFor(Wait.forLogMessage(".*renewed auth token.*", 1));
+ }
+
+ /**
+ * Constructs an instance of the Vault driver, using sensible defaults.
+ *
+ * @return Vault client instance.
+ * @throws VaultException On error.
+ */
+ public Vault getVault() throws VaultException {
+ final VaultConfig config =
+ new VaultConfig()
+ .address(getAddress())
+ .openTimeout(5)
+ .readTimeout(30)
+ .build();
+ return new Vault(config);
+ }
+
+ /**
+ * The Docker container uses bridged networking. Meaning that Vault listens on port 8200 inside the container,
+ * but the tests running on the host machine cannot reach that port directly. Instead, the Vault connection
+ * config has to use a port that is mapped to the container's port 8200. There is no telling what the mapped
+ * port will be until runtime, so this method is necessary to build a Vault connection URL with the appropriate
+ * values.
+ *
+ * @return The URL of the Vault instance
+ */
+ public String getAddress() {
+ return String.format("http://%s:%d", getContainerIpAddress(), getMappedPort(8100));
+ }
+
+ @Override public void beforeTest(TestDescription description) {
+ assumeTrue(DOCKER_AVAILABLE);
+ }
+}
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/util/VaultContainer.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/util/VaultContainer.java
new file mode 100644
index 00000000..0d010a10
--- /dev/null
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_11_4/util/VaultContainer.java
@@ -0,0 +1,368 @@
+package io.github.jopenlibs.vault.v1_11_4.util;
+
+import com.github.dockerjava.api.model.Capability;
+import io.github.jopenlibs.vault.SslConfig;
+import io.github.jopenlibs.vault.Vault;
+import io.github.jopenlibs.vault.VaultConfig;
+import io.github.jopenlibs.vault.VaultException;
+import io.github.jopenlibs.vault.json.Json;
+import io.github.jopenlibs.vault.json.JsonObject;
+import java.io.File;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testcontainers.containers.BindMode;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.output.Slf4jLogConsumer;
+import org.testcontainers.containers.wait.strategy.HttpWaitStrategy;
+import org.testcontainers.lifecycle.TestDescription;
+import org.testcontainers.lifecycle.TestLifecycleAware;
+
+import static org.junit.Assume.assumeTrue;
+
+/**
+ * Sets up and exposes utilities for dealing with a Docker-hosted instance of Vault, for integration tests.
+ */
+public class VaultContainer extends GenericContainer implements TestConstants, TestLifecycleAware {
+ private static final Logger LOGGER = LoggerFactory.getLogger(VaultContainer.class);
+
+ public static final String DEFAULT_IMAGE_AND_TAG = "vault:1.11.4";
+
+ private String rootToken;
+ private String unsealKey;
+
+ /**
+ * Establishes a running Docker container, hosting a Vault server instance.
+ */
+ public VaultContainer(String image) {
+ super(image);
+ this.withNetwork(CONTAINER_NETWORK)
+ .withNetworkAliases("vault")
+ .withClasspathResourceMapping("/startup.sh", CONTAINER_STARTUP_SCRIPT, BindMode.READ_ONLY)
+ .withClasspathResourceMapping("/config.json", CONTAINER_CONFIG_FILE, BindMode.READ_ONLY)
+ .withClasspathResourceMapping("/libressl.conf", CONTAINER_OPENSSL_CONFIG_FILE, BindMode.READ_ONLY)
+ .withClasspathResourceMapping("/approlePolicy.hcl", APPROLE_POLICY_FILE, BindMode.READ_ONLY)
+ .withFileSystemBind(SSL_DIRECTORY, CONTAINER_SSL_DIRECTORY, BindMode.READ_WRITE)
+ .withCreateContainerCmdModifier(command -> command.withCapAdd(Capability.IPC_LOCK))
+ .withExposedPorts(8200, 8280)
+ .withCommand("/bin/sh " + CONTAINER_STARTUP_SCRIPT)
+ .withLogConsumer(new Slf4jLogConsumer(LOGGER))
+ .waitingFor(
+ // All of the tests in this integration test suite use HTTPS connections. However, Vault
+ // is configured to run a plain HTTP listener on port 8280, purely for purposes of detecting
+ // when the Docker container is fully ready.
+ //
+ // Unfortunately, we can't use HTTPS at this point in the flow. Because that would require
+ // configuring SSL to trust the self-signed cert that's generated inside of the Docker
+ // container. A chicken-and-egg problem, as we need to wait for the container to be fully
+ // ready before we access that cert.
+ new HttpWaitStrategy()
+ .forPort(8280)
+ .forPath("/v1/sys/seal-status")
+ .forStatusCode(HttpURLConnection.HTTP_OK) // The expected response when "vault init" has not yet run
+ );
+ }
+
+ public VaultContainer() {
+ this(DEFAULT_IMAGE_AND_TAG);
+ }
+
+ /**
+ * To be called by a test class method annotated with {@link org.junit.BeforeClass}.
+ * This logic doesn't work when placed inside of the constructor, presumably
+ * because the Docker container spawned by TestContainers is not ready to accept commands until after those
+ * methods complete.
+ *
+ * This method initializes the Vault server, capturing the unseal key and root token that are displayed on the
+ * console. It then uses the key to unseal the Vault instance, and stores the token in a member field so it
+ * will be available to other methods.
+ *
+ * @throws IOException
+ * @throws InterruptedException
+ */
+ public void initAndUnsealVault() throws IOException, InterruptedException {
+ // Initialize the Vault server
+ final ExecResult initResult = runCommand("vault", "operator", "init", "-ca-cert=" +
+ CONTAINER_CERT_PEMFILE, "-key-shares=1", "-key-threshold=1", "-format=json");
+ final String stdout = initResult.getStdout().replaceAll("\\r?\\n", "");
+ JsonObject initJson = Json.parse(stdout).asObject();
+ this.unsealKey = initJson.get("unseal_keys_b64").asArray().get(0).asString();
+ this.rootToken = initJson.get("root_token").asString();
+
+ System.out.println("Root token: " + rootToken);
+
+ // Unseal the Vault server
+ runCommand("vault", "operator", "unseal", "-ca-cert=" + CONTAINER_CERT_PEMFILE, unsealKey);
+ }
+
+ /**
+ * Prepares the Vault server for testing of the AppID auth backend (i.e. mounts the backend and populates test
+ * data).
+ *
+ * @throws IOException
+ * @throws InterruptedException
+ */
+ public void setupBackendAppId() throws IOException, InterruptedException {
+ runCommand("vault", "login", "-ca-cert=" + CONTAINER_CERT_PEMFILE, rootToken);
+
+ runCommand("vault", "auth", "enable", "-ca-cert=" + CONTAINER_CERT_PEMFILE, "app-id");
+ runCommand("vault", "write", "-ca-cert=" + CONTAINER_CERT_PEMFILE, "auth/app-id/map/app-id/" + APP_ID,
+ "display_name=" + APP_ID);
+ runCommand("vault", "write", "-ca-cert=" + CONTAINER_CERT_PEMFILE, "auth/app-id/map/user-id/" + USER_ID,
+ "value=" + APP_ID);
+ }
+
+ /**
+ * Prepares the Vault server for testing of the Username and Password auth backend (i.e. mounts the backend and
+ * populates test data).
+ *
+ * @throws IOException
+ * @throws InterruptedException
+ */
+ public void setupBackendUserPass() throws IOException, InterruptedException {
+ runCommand("vault", "login", "-ca-cert=" + CONTAINER_CERT_PEMFILE, rootToken);
+
+ runCommand("vault", "auth", "enable", "-ca-cert=" + CONTAINER_CERT_PEMFILE, "userpass");
+ runCommand("vault", "write", "-ca-cert=" + CONTAINER_CERT_PEMFILE, "auth/userpass/users/" + USER_ID,
+ "password=" + PASSWORD);
+ }
+
+ /**
+ * Prepares the Vault server for testing of the AppRole auth backend (i.e. mounts the backend and populates test
+ * data).
+ *
+ * @throws IOException
+ * @throws InterruptedException
+ */
+ public void setupBackendAppRole() throws IOException, InterruptedException {
+ runCommand("vault", "login", "-ca-cert=" + CONTAINER_CERT_PEMFILE, rootToken);
+
+ runCommand("vault", "policy", "write", "-ca-cert=" + CONTAINER_CERT_PEMFILE, "testerrole", APPROLE_POLICY_FILE);
+ runCommand("vault", "auth", "enable", "-ca-cert=" + CONTAINER_CERT_PEMFILE, "approle");
+ runCommand("vault", "write", "-ca-cert=" + CONTAINER_CERT_PEMFILE, "auth/approle/role/testrole",
+ "secret_id_ttl=10m", "token_ttl=20m", "token_max_ttl=30m", "secret_id_num_uses=40", "policies=testerrole");
+ }
+
+ /**
+ * Prepares the Vault server for testing of the PKI auth backend (i.e. mounts the backend and populates test data).
+ *
+ * @throws IOException
+ * @throws InterruptedException
+ */
+ public void setupBackendPki() throws IOException, InterruptedException {
+ runCommand("vault", "login", "-ca-cert=" + CONTAINER_CERT_PEMFILE, rootToken);
+
+ runCommand("vault", "secrets", "enable", "-ca-cert=" + CONTAINER_CERT_PEMFILE, "-path=pki", "pki");
+ runCommand("vault", "secrets", "enable", "-ca-cert=" + CONTAINER_CERT_PEMFILE, "-path=other-pki", "pki");
+
+ runCommand("vault", "secrets", "enable", "-ca-cert=" + CONTAINER_CERT_PEMFILE, "-path=pki-custom-path-1", "pki");
+ runCommand("vault", "secrets", "enable", "-ca-cert=" + CONTAINER_CERT_PEMFILE, "-path=pki-custom-path-2", "pki");
+ runCommand("vault", "secrets", "enable", "-ca-cert=" + CONTAINER_CERT_PEMFILE, "-path=pki-custom-path-3", "pki");
+
+ runCommand("vault", "write", "-ca-cert=" + CONTAINER_CERT_PEMFILE, "pki/root/generate/internal",
+ "common_name=myvault.com", "ttl=99h");
+ }
+
+ /**
+ * Prepares the Vault server for testing of the TLS Certificate auth backend (i.e. mounts the backend and registers
+ * the certificate and private key for client auth).
+ *
+ * @throws IOException
+ * @throws InterruptedException
+ * @param cert
+ */
+ public void setupBackendCert(String cert) throws IOException, InterruptedException {
+ runCommand("vault", "login", "-ca-cert=" + CONTAINER_CERT_PEMFILE, rootToken);
+ runCommand("sh", "-c", "cat <> " + CONTAINER_CLIENT_CERT_PEMFILE + "\n" + cert + "\nEOL");
+ runCommand("vault", "auth", "enable", "-ca-cert=" + CONTAINER_CERT_PEMFILE, "cert");
+ runCommand("vault", "write", "-ca-cert=" + CONTAINER_CERT_PEMFILE, "auth/cert/certs/web", "display_name=web",
+ "policies=web,prod", "certificate=@" + CONTAINER_CLIENT_CERT_PEMFILE, "ttl=3600");
+ }
+
+ /**
+ * Prepares the Vault server for testing of the Database Backend using Postgres
+ *
+ * @throws IOException
+ * @throws InterruptedException
+ */
+ public void setupBackendDatabase(String databaseIp) throws IOException, InterruptedException {
+ runCommand("vault", "login", "-ca-cert=" + CONTAINER_CERT_PEMFILE, rootToken);
+
+ runCommand("vault", "secrets", "enable", "-ca-cert=" + CONTAINER_CERT_PEMFILE, "database");
+ runCommand("vault", "write", "-ca-cert=" + CONTAINER_CERT_PEMFILE, "database/config/postgres",
+ "plugin_name=postgresql-database-plugin",
+ "allowed_roles=*",
+ "connection_url=postgresql://{{username}}:{{password}}@" + databaseIp + ":5432/postgres?sslmode=disable",
+ "password=" + POSTGRES_PASSWORD,
+ "username=" + POSTGRES_USER);
+ }
+
+ public void setEngineVersions() throws IOException, InterruptedException {
+ // Upgrade default secrets/ Engine to V2, set a new V1 secrets path at "kv-v1/"
+ runCommand("vault", "kv", "enable-versioning", "-ca-cert=" + CONTAINER_CERT_PEMFILE, "secret/");
+ runCommand("vault", "secrets", "enable", "-ca-cert=" + CONTAINER_CERT_PEMFILE, "-path=secret", "-version=2", "kv");
+ runCommand("vault", "secrets", "enable", "-ca-cert=" + CONTAINER_CERT_PEMFILE, "-path=kv-v1", "-version=1", "kv");
+ runCommand("vault", "secrets", "enable", "-ca-cert=" + CONTAINER_CERT_PEMFILE, "-path=kv-v1-Upgrade-Test", "-version=1", "kv");
+ }
+
+ /**
+ * Constructs an instance of the Vault driver, providing maximum flexibility to control all options
+ * explicitly.
+ *
+ * If maxRetries
and retryMillis
are BOTH null, then the Vault
+ * instance will be constructed with retry logic disabled. If one OR the other are null, the the class-level
+ * default value will be used in place of the missing one.
+ *
+ * @param config
+ * @param maxRetries
+ * @param retryMillis
+ * @return
+ */
+ public Vault getVault(final VaultConfig config, final Integer maxRetries, final Integer retryMillis) {
+ Vault vault = new Vault(config);
+ if (maxRetries != null && retryMillis != null) {
+ vault = vault.withRetries(maxRetries, retryMillis);
+ } else if (maxRetries != null) {
+ vault = vault.withRetries(maxRetries, RETRY_MILLIS);
+ } else if (retryMillis != null) {
+ vault = vault.withRetries(MAX_RETRIES, retryMillis);
+ }
+ return vault;
+ }
+
+ /**
+ * Constructs an instance of the Vault driver, using sensible defaults.
+ *
+ * @return
+ * @throws VaultException
+ */
+ public Vault getVault() throws VaultException {
+ final VaultConfig config =
+ new VaultConfig()
+ .address(getAddress())
+ .openTimeout(5)
+ .readTimeout(30)
+ .sslConfig(new SslConfig().pemFile(new File(CERT_PEMFILE)).build())
+ .build();
+ return getVault(config, MAX_RETRIES, RETRY_MILLIS);
+ }
+
+ /**
+ * Constructs a VaultConfig that can be used to configure your own tests
+ *
+ * @return
+ * @throws VaultException
+ */
+
+ public VaultConfig getVaultConfig() throws VaultException {
+ return new VaultConfig()
+ .address(getAddress())
+ .openTimeout(5)
+ .readTimeout(30)
+ .sslConfig(new SslConfig().pemFile(new File(CERT_PEMFILE)).build())
+ .build();
+ }
+
+ /**
+ * Constructs an instance of the Vault driver with sensible defaults, configured to use the supplied token
+ * for authentication.
+ *
+ * @param token
+ * @return
+ * @throws VaultException
+ */
+ public Vault getVault(final String token) throws VaultException {
+ final VaultConfig config =
+ new VaultConfig()
+ .address(getAddress())
+ .token(token)
+ .openTimeout(5)
+ .readTimeout(30)
+ .sslConfig(new SslConfig().pemFile(new File(CERT_PEMFILE)).build())
+ .build();
+ return new Vault(config).withRetries(MAX_RETRIES, RETRY_MILLIS);
+ }
+
+ /**
+ * Constructs an instance of the Vault driver using a custom Vault config.
+ *
+ * @return
+ * @throws VaultException
+ */
+ public Vault getRootVaultWithCustomVaultConfig(VaultConfig vaultConfig) throws VaultException {
+ final VaultConfig config =
+ vaultConfig
+ .address(getAddress())
+ .token(rootToken)
+ .openTimeout(5)
+ .readTimeout(30)
+ .sslConfig(new SslConfig().pemFile(new File(CERT_PEMFILE)).build())
+ .build();
+ return new Vault(config).withRetries(MAX_RETRIES, RETRY_MILLIS);
+ }
+
+ /**
+ * Constructs an instance of the Vault driver with sensible defaults, configured to the use the root token
+ * for authentication.
+ *
+ * @return
+ * @throws VaultException
+ */
+ public Vault getRootVault() throws VaultException {
+ return getVault(rootToken).withRetries(MAX_RETRIES, RETRY_MILLIS);
+ }
+
+ /**
+ * The Docker container uses bridged networking. Meaning that Vault listens on port 8200 inside the container,
+ * but the tests running on the host machine cannot reach that port directly. Instead, the Vault connection
+ * config has to use a port that is mapped to the container's port 8200. There is no telling what the mapped
+ * port will be until runtime, so this method is necessary to build a Vault connection URL with the appropriate
+ * values.
+ *
+ * @return The URL of the Vault instance
+ */
+ public String getAddress() {
+ return String.format("https://%s:%d", getContainerIpAddress(), getMappedPort(8200));
+ }
+
+ /**
+ * Returns the master key for unsealing the Vault instance. This method should really ONLY be used by tests
+ * specifically for sealing and unsealing functionality (i.e. SealTests.java). Generally, tests should
+ * retrieve Vault instances from the "getVault(...)" or "getRootVault()" methods here, and never directly
+ * concern themselves with the root token or unseal key at all.
+ *
+ * @return The master key for unsealing this Vault instance
+ */
+ public String getUnsealKey() {
+ return unsealKey;
+ }
+
+ /**
+ * Runs the specified command from within the Docker container.
+ *
+ * @param command The command to run, broken up by whitespace
+ * (e.g. "vault mount -path=pki pki" becomes "vault", "mount", "-path=pki", "pki")
+ * @return
+ * @throws IOException
+ * @throws InterruptedException
+ */
+ private ExecResult runCommand(final String... command) throws IOException, InterruptedException {
+ LOGGER.info("Command: {}", String.join(" ", command));
+ final ExecResult result = execInContainer(command);
+ final String out = result.getStdout();
+ final String err = result.getStderr();
+ if (out != null && !out.isEmpty()) {
+ LOGGER.info("Command stdout: {}", result.getStdout());
+ }
+ if (err != null && !err.isEmpty()) {
+ LOGGER.info("Command stderr: {}", result.getStderr());
+ }
+ return result;
+ }
+
+ @Override
+ public void beforeTest(TestDescription description) {
+ assumeTrue(DOCKER_AVAILABLE);
+ }
+}
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/api/AuthBackendAppIdTests.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/AuthBackendAppIdTests.java
similarity index 91%
rename from src/test-integration/java/io/github/jopenlibs/vault/api/AuthBackendAppIdTests.java
rename to src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/AuthBackendAppIdTests.java
index 8bdea405..efc703b1 100644
--- a/src/test-integration/java/io/github/jopenlibs/vault/api/AuthBackendAppIdTests.java
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/AuthBackendAppIdTests.java
@@ -1,8 +1,8 @@
-package io.github.jopenlibs.vault.api;
+package io.github.jopenlibs.vault.v1_1_3.api;
import io.github.jopenlibs.vault.Vault;
import io.github.jopenlibs.vault.VaultException;
-import io.github.jopenlibs.vault.util.VaultContainer;
+import io.github.jopenlibs.vault.v1_1_3.util.VaultContainer;
import java.io.IOException;
import org.junit.BeforeClass;
import org.junit.ClassRule;
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/api/AuthBackendAppRoleTests.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/AuthBackendAppRoleTests.java
similarity index 94%
rename from src/test-integration/java/io/github/jopenlibs/vault/api/AuthBackendAppRoleTests.java
rename to src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/AuthBackendAppRoleTests.java
index 4a9ca17a..fe3aac9f 100644
--- a/src/test-integration/java/io/github/jopenlibs/vault/api/AuthBackendAppRoleTests.java
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/AuthBackendAppRoleTests.java
@@ -1,10 +1,10 @@
-package io.github.jopenlibs.vault.api;
+package io.github.jopenlibs.vault.v1_1_3.api;
import io.github.jopenlibs.vault.Vault;
import io.github.jopenlibs.vault.VaultConfig;
import io.github.jopenlibs.vault.VaultException;
import io.github.jopenlibs.vault.response.LogicalResponse;
-import io.github.jopenlibs.vault.util.VaultContainer;
+import io.github.jopenlibs.vault.v1_1_3.util.VaultContainer;
import java.io.IOException;
import org.junit.BeforeClass;
import org.junit.ClassRule;
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/api/AuthBackendCertTests.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/AuthBackendCertTests.java
similarity index 93%
rename from src/test-integration/java/io/github/jopenlibs/vault/api/AuthBackendCertTests.java
rename to src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/AuthBackendCertTests.java
index 5a037f38..6da3fd8d 100644
--- a/src/test-integration/java/io/github/jopenlibs/vault/api/AuthBackendCertTests.java
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/AuthBackendCertTests.java
@@ -1,12 +1,12 @@
-package io.github.jopenlibs.vault.api;
+package io.github.jopenlibs.vault.v1_1_3.api;
import io.github.jopenlibs.vault.SslConfig;
import io.github.jopenlibs.vault.Vault;
import io.github.jopenlibs.vault.VaultConfig;
import io.github.jopenlibs.vault.VaultException;
-import io.github.jopenlibs.vault.util.SSLUtils;
-import io.github.jopenlibs.vault.util.TestConstants;
-import io.github.jopenlibs.vault.util.VaultContainer;
+import io.github.jopenlibs.vault.v1_1_3.util.SSLUtils;
+import io.github.jopenlibs.vault.v1_1_3.util.TestConstants;
+import io.github.jopenlibs.vault.v1_1_3.util.VaultContainer;
import java.io.File;
import java.io.IOException;
import java.security.KeyStore;
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/api/AuthBackendDatabaseTests.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/AuthBackendDatabaseTests.java
similarity index 96%
rename from src/test-integration/java/io/github/jopenlibs/vault/api/AuthBackendDatabaseTests.java
rename to src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/AuthBackendDatabaseTests.java
index d5a34123..f5981489 100644
--- a/src/test-integration/java/io/github/jopenlibs/vault/api/AuthBackendDatabaseTests.java
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/AuthBackendDatabaseTests.java
@@ -1,11 +1,11 @@
-package io.github.jopenlibs.vault.api;
+package io.github.jopenlibs.vault.v1_1_3.api;
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.util.DbContainer;
-import io.github.jopenlibs.vault.util.VaultContainer;
+import io.github.jopenlibs.vault.v1_1_3.util.DbContainer;
+import io.github.jopenlibs.vault.v1_1_3.util.VaultContainer;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/api/AuthBackendPkiTests.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/AuthBackendPkiTests.java
similarity index 98%
rename from src/test-integration/java/io/github/jopenlibs/vault/api/AuthBackendPkiTests.java
rename to src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/AuthBackendPkiTests.java
index ac5492de..a1ec37d1 100644
--- a/src/test-integration/java/io/github/jopenlibs/vault/api/AuthBackendPkiTests.java
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/AuthBackendPkiTests.java
@@ -1,4 +1,4 @@
-package io.github.jopenlibs.vault.api;
+package io.github.jopenlibs.vault.v1_1_3.api;
import io.github.jopenlibs.vault.Vault;
import io.github.jopenlibs.vault.VaultException;
@@ -6,8 +6,8 @@
import io.github.jopenlibs.vault.api.pki.RoleOptions;
import io.github.jopenlibs.vault.response.PkiResponse;
import io.github.jopenlibs.vault.rest.RestResponse;
-import io.github.jopenlibs.vault.util.SSLUtils;
-import io.github.jopenlibs.vault.util.VaultContainer;
+import io.github.jopenlibs.vault.v1_1_3.util.SSLUtils;
+import io.github.jopenlibs.vault.v1_1_3.util.VaultContainer;
import java.io.IOException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/api/AuthBackendTokenTests.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/AuthBackendTokenTests.java
similarity index 94%
rename from src/test-integration/java/io/github/jopenlibs/vault/api/AuthBackendTokenTests.java
rename to src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/AuthBackendTokenTests.java
index 454343f6..524fbfc5 100644
--- a/src/test-integration/java/io/github/jopenlibs/vault/api/AuthBackendTokenTests.java
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/AuthBackendTokenTests.java
@@ -1,11 +1,12 @@
-package io.github.jopenlibs.vault.api;
+package io.github.jopenlibs.vault.v1_1_3.api;
import io.github.jopenlibs.vault.Vault;
import io.github.jopenlibs.vault.VaultException;
+import io.github.jopenlibs.vault.api.Auth.TokenRequest;
import io.github.jopenlibs.vault.json.Json;
import io.github.jopenlibs.vault.response.AuthResponse;
import io.github.jopenlibs.vault.response.LookupResponse;
-import io.github.jopenlibs.vault.util.VaultContainer;
+import io.github.jopenlibs.vault.v1_1_3.util.VaultContainer;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
@@ -40,7 +41,7 @@ public void testCreateTokenWithRequest() throws VaultException {
final Vault vault = container.getRootVault();
final AuthResponse response = vault.auth().createToken(
- new Auth.TokenRequest()
+ new TokenRequest()
.id(UUID.randomUUID())
.polices(Arrays.asList("policy"))
.noParent(true)
@@ -72,7 +73,7 @@ public void testCreateTokenWithRequest() throws VaultException {
public void testRenewSelf() throws VaultException {
// Generate a client token
final Vault authVault = container.getRootVault();
- final AuthResponse createResponse = authVault.auth().createToken(new Auth.TokenRequest().ttl("1h"));
+ final AuthResponse createResponse = authVault.auth().createToken(new TokenRequest().ttl("1h"));
final String token = createResponse.getAuthClientToken();
assertNotNull(token);
@@ -105,7 +106,7 @@ public void testRenewSelf() throws VaultException {
public void testLookupSelf() throws VaultException {
// Generate a client token
final Vault authVault = container.getRootVault();
- final AuthResponse createResponse = authVault.auth().createToken(new Auth.TokenRequest().ttl("1h"));
+ final AuthResponse createResponse = authVault.auth().createToken(new TokenRequest().ttl("1h"));
final String token = createResponse.getAuthClientToken();
assertNotNull(token);
@@ -127,7 +128,7 @@ public void testLookupSelf() throws VaultException {
public void testRevokeSelf() throws VaultException {
// Generate a client token
final Vault authVault = container.getRootVault();
- final AuthResponse createResponse = authVault.auth().createToken(new Auth.TokenRequest().ttl("1h"));
+ final AuthResponse createResponse = authVault.auth().createToken(new TokenRequest().ttl("1h"));
final String token = createResponse.getAuthClientToken();
assertNotNull(token);
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/api/AuthBackendUserPassTests.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/AuthBackendUserPassTests.java
similarity index 91%
rename from src/test-integration/java/io/github/jopenlibs/vault/api/AuthBackendUserPassTests.java
rename to src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/AuthBackendUserPassTests.java
index 69c86ed7..ea3b76a7 100644
--- a/src/test-integration/java/io/github/jopenlibs/vault/api/AuthBackendUserPassTests.java
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/AuthBackendUserPassTests.java
@@ -1,9 +1,9 @@
-package io.github.jopenlibs.vault.api;
+package io.github.jopenlibs.vault.v1_1_3.api;
import io.github.jopenlibs.vault.Vault;
import io.github.jopenlibs.vault.VaultException;
import io.github.jopenlibs.vault.response.AuthResponse;
-import io.github.jopenlibs.vault.util.VaultContainer;
+import io.github.jopenlibs.vault.v1_1_3.util.VaultContainer;
import java.io.IOException;
import org.junit.BeforeClass;
import org.junit.ClassRule;
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/api/DebugTests.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/DebugTests.java
similarity index 96%
rename from src/test-integration/java/io/github/jopenlibs/vault/api/DebugTests.java
rename to src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/DebugTests.java
index 32e3edca..a1c9cd27 100644
--- a/src/test-integration/java/io/github/jopenlibs/vault/api/DebugTests.java
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/DebugTests.java
@@ -1,9 +1,9 @@
-package io.github.jopenlibs.vault.api;
+package io.github.jopenlibs.vault.v1_1_3.api;
import io.github.jopenlibs.vault.Vault;
import io.github.jopenlibs.vault.VaultException;
import io.github.jopenlibs.vault.response.HealthResponse;
-import io.github.jopenlibs.vault.util.VaultContainer;
+import io.github.jopenlibs.vault.v1_1_3.util.VaultContainer;
import java.io.IOException;
import junit.framework.TestCase;
import org.junit.Before;
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/api/LeasesTests.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/LeasesTests.java
similarity index 95%
rename from src/test-integration/java/io/github/jopenlibs/vault/api/LeasesTests.java
rename to src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/LeasesTests.java
index 81a0962c..c4327a29 100644
--- a/src/test-integration/java/io/github/jopenlibs/vault/api/LeasesTests.java
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/LeasesTests.java
@@ -1,9 +1,9 @@
-package io.github.jopenlibs.vault.api;
+package io.github.jopenlibs.vault.v1_1_3.api;
import io.github.jopenlibs.vault.Vault;
import io.github.jopenlibs.vault.VaultException;
import io.github.jopenlibs.vault.response.VaultResponse;
-import io.github.jopenlibs.vault.util.VaultContainer;
+import io.github.jopenlibs.vault.v1_1_3.util.VaultContainer;
import java.io.IOException;
import org.junit.Before;
import org.junit.BeforeClass;
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/api/LogicalTests.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/LogicalTests.java
similarity index 99%
rename from src/test-integration/java/io/github/jopenlibs/vault/api/LogicalTests.java
rename to src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/LogicalTests.java
index a94e5e06..5b2c1f50 100644
--- a/src/test-integration/java/io/github/jopenlibs/vault/api/LogicalTests.java
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/LogicalTests.java
@@ -1,11 +1,11 @@
-package io.github.jopenlibs.vault.api;
+package io.github.jopenlibs.vault.v1_1_3.api;
import io.github.jopenlibs.vault.Vault;
import io.github.jopenlibs.vault.VaultConfig;
import io.github.jopenlibs.vault.VaultException;
import io.github.jopenlibs.vault.response.AuthResponse;
import io.github.jopenlibs.vault.response.LogicalResponse;
-import io.github.jopenlibs.vault.util.VaultContainer;
+import io.github.jopenlibs.vault.v1_1_3.util.VaultContainer;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/api/MountsTests.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/MountsTests.java
similarity index 98%
rename from src/test-integration/java/io/github/jopenlibs/vault/api/MountsTests.java
rename to src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/MountsTests.java
index 10d6f1fb..4ad7e50d 100644
--- a/src/test-integration/java/io/github/jopenlibs/vault/api/MountsTests.java
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/MountsTests.java
@@ -1,4 +1,4 @@
-package io.github.jopenlibs.vault.api;
+package io.github.jopenlibs.vault.v1_1_3.api;
import io.github.jopenlibs.vault.Vault;
import io.github.jopenlibs.vault.VaultException;
@@ -8,7 +8,7 @@
import io.github.jopenlibs.vault.api.mounts.MountType;
import io.github.jopenlibs.vault.api.mounts.TimeToLive;
import io.github.jopenlibs.vault.response.MountResponse;
-import io.github.jopenlibs.vault.util.VaultContainer;
+import io.github.jopenlibs.vault.v1_1_3.util.VaultContainer;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/api/SealTests.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/SealTests.java
similarity index 94%
rename from src/test-integration/java/io/github/jopenlibs/vault/api/SealTests.java
rename to src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/SealTests.java
index dd30172a..4ac580be 100644
--- a/src/test-integration/java/io/github/jopenlibs/vault/api/SealTests.java
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/SealTests.java
@@ -1,8 +1,8 @@
-package io.github.jopenlibs.vault.api;
+package io.github.jopenlibs.vault.v1_1_3.api;
import io.github.jopenlibs.vault.VaultException;
import io.github.jopenlibs.vault.response.SealResponse;
-import io.github.jopenlibs.vault.util.VaultContainer;
+import io.github.jopenlibs.vault.v1_1_3.util.VaultContainer;
import java.io.IOException;
import org.junit.BeforeClass;
import org.junit.ClassRule;
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/api/VaultAgentTests.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/VaultAgentTests.java
similarity index 93%
rename from src/test-integration/java/io/github/jopenlibs/vault/api/VaultAgentTests.java
rename to src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/VaultAgentTests.java
index 0f013a15..b6725f1f 100644
--- a/src/test-integration/java/io/github/jopenlibs/vault/api/VaultAgentTests.java
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/VaultAgentTests.java
@@ -1,11 +1,11 @@
-package io.github.jopenlibs.vault.api;
+package io.github.jopenlibs.vault.v1_1_3.api;
import io.github.jopenlibs.vault.Vault;
import io.github.jopenlibs.vault.VaultConfig;
import io.github.jopenlibs.vault.VaultException;
import io.github.jopenlibs.vault.response.LogicalResponse;
-import io.github.jopenlibs.vault.util.VaultAgentContainer;
-import io.github.jopenlibs.vault.util.VaultContainer;
+import io.github.jopenlibs.vault.v1_1_3.util.VaultAgentContainer;
+import io.github.jopenlibs.vault.v1_1_3.util.VaultContainer;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/WrapUnwrapTests.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/WrapUnwrapTests.java
new file mode 100644
index 00000000..5ec10d04
--- /dev/null
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/api/WrapUnwrapTests.java
@@ -0,0 +1,58 @@
+package io.github.jopenlibs.vault.v1_1_3.api;
+
+import io.github.jopenlibs.vault.Vault;
+import io.github.jopenlibs.vault.VaultException;
+import io.github.jopenlibs.vault.json.JsonObject;
+import io.github.jopenlibs.vault.response.AuthResponse;
+import io.github.jopenlibs.vault.response.UnwrapResponse;
+import io.github.jopenlibs.vault.response.WrapResponse;
+import io.github.jopenlibs.vault.v1_1_3.util.VaultContainer;
+import java.io.IOException;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Integration tests for the wrap/unwrap data.
+ */
+public class WrapUnwrapTests {
+
+ @ClassRule
+ public static final VaultContainer container = new VaultContainer();
+
+ private static String NONROOT_TOKEN;
+
+ @BeforeClass
+ public static void setupClass() throws IOException, InterruptedException, VaultException {
+ container.initAndUnsealVault();
+ container.setupBackendUserPass();
+
+ final Vault vault = container.getVault();
+ final AuthResponse response = vault.auth()
+ .loginByUserPass(VaultContainer.USER_ID, VaultContainer.PASSWORD);
+
+ NONROOT_TOKEN = response.getAuthClientToken();
+ }
+
+ /**
+ * Tests wrap/unwrap data.
+ */
+ @Test
+ public void testWrapUnwrap() throws Exception {
+ final Vault vault = container.getVault(NONROOT_TOKEN);
+
+ WrapResponse wrapResponse = vault.auth().wrap(
+ new JsonObject()
+ .add("foo", "bar")
+ .add("zoo", "zar"),
+ 60
+ );
+
+ UnwrapResponse unwrapResponse = vault.auth().unwrap(wrapResponse.getToken());
+
+ assertEquals("bar", unwrapResponse.getData().asObject().get("foo").asString());
+ assertEquals("zar", unwrapResponse.getData().asObject().get("zoo").asString());
+ }
+}
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/util/DbContainer.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/util/DbContainer.java
similarity index 96%
rename from src/test-integration/java/io/github/jopenlibs/vault/util/DbContainer.java
rename to src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/util/DbContainer.java
index 5f5f12c1..3dfd118d 100644
--- a/src/test-integration/java/io/github/jopenlibs/vault/util/DbContainer.java
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/util/DbContainer.java
@@ -1,4 +1,4 @@
-package io.github.jopenlibs.vault.util;
+package io.github.jopenlibs.vault.v1_1_3.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/util/SSLUtils.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/util/SSLUtils.java
similarity index 99%
rename from src/test-integration/java/io/github/jopenlibs/vault/util/SSLUtils.java
rename to src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/util/SSLUtils.java
index 431ad762..36781933 100644
--- a/src/test-integration/java/io/github/jopenlibs/vault/util/SSLUtils.java
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/util/SSLUtils.java
@@ -1,6 +1,6 @@
-package io.github.jopenlibs.vault.util;
+package io.github.jopenlibs.vault.v1_1_3.util;
-import io.github.jopenlibs.vault.api.AuthBackendCertTests;
+import io.github.jopenlibs.vault.v1_1_3.api.AuthBackendCertTests;
import java.io.ByteArrayOutputStream;
import java.io.FileReader;
import java.io.IOException;
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/util/TestConstants.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/util/TestConstants.java
similarity index 97%
rename from src/test-integration/java/io/github/jopenlibs/vault/util/TestConstants.java
rename to src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/util/TestConstants.java
index bb4c8fce..2e4aaf29 100644
--- a/src/test-integration/java/io/github/jopenlibs/vault/util/TestConstants.java
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/util/TestConstants.java
@@ -1,4 +1,4 @@
-package io.github.jopenlibs.vault.util;
+package io.github.jopenlibs.vault.v1_1_3.util;
import java.io.File;
import org.testcontainers.containers.Network;
@@ -10,7 +10,6 @@
* path locations for SSL artifacts.
*/
public interface TestConstants {
-
String POSTGRES_PASSWORD = "superpassword1";
String POSTGRES_USER = "superuser1";
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/util/VaultAgentContainer.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/util/VaultAgentContainer.java
similarity index 98%
rename from src/test-integration/java/io/github/jopenlibs/vault/util/VaultAgentContainer.java
rename to src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/util/VaultAgentContainer.java
index faf40f5e..ac651537 100644
--- a/src/test-integration/java/io/github/jopenlibs/vault/util/VaultAgentContainer.java
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/util/VaultAgentContainer.java
@@ -1,4 +1,4 @@
-package io.github.jopenlibs.vault.util;
+package io.github.jopenlibs.vault.v1_1_3.util;
import com.github.dockerjava.api.model.Capability;
import io.github.jopenlibs.vault.Vault;
diff --git a/src/test-integration/java/io/github/jopenlibs/vault/util/VaultContainer.java b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/util/VaultContainer.java
similarity index 99%
rename from src/test-integration/java/io/github/jopenlibs/vault/util/VaultContainer.java
rename to src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/util/VaultContainer.java
index 5469ae68..cf7429f4 100644
--- a/src/test-integration/java/io/github/jopenlibs/vault/util/VaultContainer.java
+++ b/src/test-integration/java/io/github/jopenlibs/vault/v1_1_3/util/VaultContainer.java
@@ -1,4 +1,4 @@
-package io.github.jopenlibs.vault.util;
+package io.github.jopenlibs.vault.v1_1_3.util;
import com.github.dockerjava.api.model.Capability;
import io.github.jopenlibs.vault.SslConfig;
diff --git a/src/test-integration/resources/startup.sh b/src/test-integration/resources/startup.sh
index 2010c713..9dade6e6 100644
--- a/src/test-integration/resources/startup.sh
+++ b/src/test-integration/resources/startup.sh
@@ -4,6 +4,7 @@
# (1) Install SSL dependencies
#####
apk add --no-cache libressl
+apk add --no-cache openssl
#####
@@ -14,16 +15,21 @@ apk add --no-cache libressl
cd /vault/config/ssl
rm -Rf *
cp ../libressl.conf .
+
# Create a CA root certificate and key
openssl req -newkey rsa:2048 -days 3650 -x509 -nodes -out root-cert.pem -keyout root-privkey.pem -subj '/C=US/ST=GA/L=Atlanta/O=BetterCloud/CN=localhost'
+
# Create a private key, and a certificate-signing request
openssl req -newkey rsa:1024 -nodes -out vault-csr.pem -keyout vault-privkey.pem -subj '/C=US/ST=GA/L=Atlanta/O=BetterCloud/CN=localhost'
+
# Create an X509 certificate for the Vault server
echo 000a > serialfile
touch certindex
openssl ca -batch -config libressl.conf -notext -in vault-csr.pem -out vault-cert.pem
+
# Configure SSL at the OS level to trust the new certs
cp root-cert.pem vault-cert.pem /usr/local/share/ca-certificates
+
# Clean up temp files
rm 0A.pem certindex certindex.attr certindex.old libressl.conf serialfile serialfile.old vault-csr.pem
@@ -32,4 +38,3 @@ rm 0A.pem certindex certindex.attr certindex.old libressl.conf serialfile serial
# (3) Start Vault
#####
vault server -config /vault/config/config.json
-
diff --git a/src/test/java/io/github/jopenlibs/vault/vault/api/AuthUnwrapWithoutAuthResponseTest.java b/src/test/java/io/github/jopenlibs/vault/vault/api/AuthUnwrapWithoutAuthResponseTest.java
new file mode 100644
index 00000000..f6eeec19
--- /dev/null
+++ b/src/test/java/io/github/jopenlibs/vault/vault/api/AuthUnwrapWithoutAuthResponseTest.java
@@ -0,0 +1,81 @@
+package io.github.jopenlibs.vault.vault.api;
+
+import io.github.jopenlibs.vault.Vault;
+import io.github.jopenlibs.vault.VaultConfig;
+import io.github.jopenlibs.vault.json.Json;
+import io.github.jopenlibs.vault.json.JsonObject;
+import io.github.jopenlibs.vault.response.UnwrapResponse;
+import io.github.jopenlibs.vault.vault.VaultTestUtils;
+import io.github.jopenlibs.vault.vault.mock.MockVault;
+import org.eclipse.jetty.server.Server;
+import org.junit.After;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class AuthUnwrapWithoutAuthResponseTest {
+
+ private Server server;
+ private MockVault vaultServer;
+
+ @After
+ public void after() throws Exception {
+ VaultTestUtils.shutdownMockVault(server);
+ }
+
+ @Test
+ public void unwrap_response_without_auth() throws Exception {
+ startServer(
+ new JsonObject()
+ .add("renewable", false)
+ .add("lease_duration", 0)
+ .add("data", new JsonObject()
+ .add("foo", "bar")
+ .add("zip", "zar"))
+ .toString()
+ );
+
+ VaultConfig vaultConfig = new VaultConfig().address("http://127.0.0.1:8999")
+ .token("wrappedToken").build();
+ Vault vault = new Vault(vaultConfig);
+ UnwrapResponse response = vault.auth().unwrap("wrappedToken");
+
+ assertEquals(200, response.getRestResponse().getStatus());
+
+ assertEquals("wrappedToken", vaultServer.getRequestHeaders().get("X-Vault-Token"));
+ assertEquals("bar", response.getData().get("foo").asString());
+ assertEquals("zar", response.getData().get("zip").asString());
+ }
+
+
+ @Test
+ public void unwrap_response_without_implicit_null_auth() throws Exception {
+ startServer(
+ new JsonObject()
+ .add("renewable", false)
+ .add("lease_duration", 0)
+ .add("auth", Json.NULL)
+ .add("data", new JsonObject()
+ .add("foo", "bar")
+ .add("zip", "zar"))
+ .toString()
+ );
+
+ VaultConfig vaultConfig = new VaultConfig().address("http://127.0.0.1:8999")
+ .token("wrappedToken").build();
+ Vault vault = new Vault(vaultConfig);
+ UnwrapResponse response = vault.auth().unwrap("wrappedToken");
+
+ assertEquals(200, response.getRestResponse().getStatus());
+
+ assertEquals("wrappedToken", vaultServer.getRequestHeaders().get("X-Vault-Token"));
+ assertEquals("bar", response.getData().get("foo").asString());
+ assertEquals("zar", response.getData().get("zip").asString());
+ }
+
+ private void startServer(String mockResponse) throws Exception {
+ vaultServer = new MockVault(200, mockResponse);
+ server = VaultTestUtils.initHttpMockVault(vaultServer);
+ server.start();
+ }
+}
diff --git a/src/test/java/io/github/jopenlibs/vault/vault/api/AuthWrapTest.java b/src/test/java/io/github/jopenlibs/vault/vault/api/AuthWrapTest.java
new file mode 100644
index 00000000..fdade352
--- /dev/null
+++ b/src/test/java/io/github/jopenlibs/vault/vault/api/AuthWrapTest.java
@@ -0,0 +1,67 @@
+package io.github.jopenlibs.vault.vault.api;
+
+import io.github.jopenlibs.vault.Vault;
+import io.github.jopenlibs.vault.VaultConfig;
+import io.github.jopenlibs.vault.json.JsonObject;
+import io.github.jopenlibs.vault.response.WrapResponse;
+import io.github.jopenlibs.vault.vault.VaultTestUtils;
+import io.github.jopenlibs.vault.vault.mock.MockVault;
+import org.eclipse.jetty.server.Server;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class AuthWrapTest {
+ private static final JsonObject RESPONSE_AUTH_WRAP = new JsonObject()
+ .add("renewable", false)
+ .add("wrap_info", new JsonObject()
+ .add("token", "wrappedToken")
+ .add("accessor", "accessor_value")
+ .add("ttl", 60)
+ .add("creation_time", "2022-10-09T12:38:27.217414477Z")
+ .add("creation_path", "sys/wrapping/wrap"));
+
+ private Server server;
+ private MockVault vaultServer;
+
+ @Before
+ public void before() throws Exception {
+ vaultServer = new MockVault(200, RESPONSE_AUTH_WRAP.toString());
+ server = VaultTestUtils.initHttpMockVault(vaultServer);
+ server.start();
+ }
+
+ @After
+ public void after() throws Exception {
+ VaultTestUtils.shutdownMockVault(server);
+ }
+
+ @Test
+ public void check_wrap_request_response() throws Exception {
+ VaultConfig vaultConfig = new VaultConfig().address("http://127.0.0.1:8999").token("wrappedToken").build();
+ Vault vault = new Vault(vaultConfig);
+
+ WrapResponse response = vault.auth().wrap(
+ new JsonObject()
+ .add("foo", "bar")
+ .add("zoo", "zar"),
+ 60
+ );
+
+ assertEquals(200, response.getRestResponse().getStatus());
+
+ // Assert request body should NOT have token body (wrapped is in header)
+ assertEquals("bar", vaultServer.getRequestBody().get().get("foo").asString());
+ assertEquals("zar", vaultServer.getRequestBody().get().get("zoo").asString());
+ assertEquals("wrappedToken", vaultServer.getRequestHeaders().get("X-Vault-Token"));
+
+ // Assert response should have the unwrapped token in the client_token key
+ assertEquals("wrappedToken", response.getToken());
+ assertEquals("accessor_value", response.getAccessor());
+ assertEquals(60, response.getTtl());
+ assertEquals("2022-10-09T12:38:27.217414477Z", response.getCreationTime());
+ assertEquals("sys/wrapping/wrap", response.getCreationPath());
+ }
+}