diff --git a/src/main/java/com/bettercloud/vault/api/Auth.java b/src/main/java/com/bettercloud/vault/api/Auth.java index f696f096..515f2664 100644 --- a/src/main/java/com/bettercloud/vault/api/Auth.java +++ b/src/main/java/com/bettercloud/vault/api/Auth.java @@ -6,12 +6,14 @@ import com.bettercloud.vault.json.Json; import com.bettercloud.vault.json.JsonObject; import com.bettercloud.vault.response.AuthResponse; +import com.bettercloud.vault.response.LogicalResponse; import com.bettercloud.vault.response.LookupResponse; -import com.bettercloud.vault.rest.RestResponse; import com.bettercloud.vault.rest.Rest; +import com.bettercloud.vault.rest.RestResponse; import lombok.Getter; import java.io.Serializable; +import java.net.URI; import java.util.List; import java.util.Map; import java.util.UUID; @@ -1084,17 +1086,17 @@ public AuthResponse renewSelf(final long increment, final String tokenAuthMount) /** *
Returns information about the current client token.
- * + * * @return The response information returned from Vault * @throws VaultException If any error occurs, or unexpected response received from Vault */ public LookupResponse lookupSelf() throws VaultException { return lookupSelf("token"); } - + /** *Returns information about the current client token.
- * + * * @param tokenAuthMount The mount name of the token authentication back end. If null, defaults to "token" * @return The response information returned from Vault * @throws VaultException If any error occurs, or unexpected response received from Vault @@ -1142,6 +1144,68 @@ public LookupResponse lookupSelf(final String tokenAuthMount) throws VaultExcept } } + /** + *Returns information about the current client token for a wrapped token, for which the lookup endpoint is + * different at "sys/wrapping/lookup". Example usage:
+ * + *
+ * {@code
+ * final String wrappingToken = "...";
+ * final VaultConfig config = new VaultConfig().address(...).token(wrappingToken).build();
+ * final Vault vault = new Vault(config);
+ * final LogicalResponse response = vault.auth().lookupWarp();
+ * // Then you can validate "path" for example ...
+ * final String path = response.getData().get("path");
+ * }
+ *
+ *
+ * @return The response information returned from Vault
+ * @throws VaultException If any error occurs, or unexpected response received from Vault
+ */
+ public LogicalResponse lookupWrap() throws VaultException {
+ int retryCount = 0;
+ while (true) {
+ try {
+ // HTTP request to Vault
+ final RestResponse restResponse = new Rest()//NOPMD
+ .url(config.getAddress() + "/v1/sys/wrapping/lookup")
+ .header("X-Vault-Token", config.getToken())
+ .connectTimeoutSeconds(config.getOpenTimeout())
+ .readTimeoutSeconds(config.getReadTimeout())
+ .sslVerification(config.getSslConfig().isVerify())
+ .sslContext(config.getSslConfig().getSslContext())
+ .get();
+ // Validate restResponse
+ if (restResponse.getStatus() != 200) {
+ throw new VaultException("Vault responded with HTTP status code: " + restResponse.getStatus(),
+ restResponse.getStatus());
+ }
+ final String mimeType = restResponse.getMimeType();
+ if (mimeType == null || !"application/json".equals(mimeType)) {
+ throw new VaultException("Vault responded with MIME type: " + mimeType, restResponse.getStatus());
+ }
+ return new LogicalResponse(restResponse, retryCount);
+ } catch (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(); //NOPMD
+ }
+ } else if (e instanceof VaultException) { //NOPMD
+ // ... otherwise, give up.
+ throw (VaultException) e;
+ } else {
+ throw new VaultException(e);
+ }
+ }
+ }
+ }
+
/**
* Revokes current client token.
* @@ -1196,4 +1260,111 @@ public void revokeSelf(final String tokenAuthMount) throws VaultException { } } + /** + *Returns the original response inside the wrapped auth token. This method is useful if you need to unwrap a + * token without being authenticated. See {@link #unwrap(String)} if you need to do that authenticated.
+ * + *In the example below, you cannot use twice the {@code VaultConfig}, since + * after the first usage of the {@code wrappingToken}, it is not usable anymore. You need to use the + * {@code unwrappedToken} in a new vault configuration to continue. Example usage:
+ * + *
+ * {@code
+ * final String wrappingToken = "...";
+ * final VaultConfig config = new VaultConfig().address(...).token(wrappingToken).build();
+ * final Vault vault = new Vault(config);
+ * final AuthResponse response = vault.auth().unwrap();
+ * final String unwrappedToken = response.getAuthClientToken();
+ * }
+ *
+ *
+ * @return The response information returned from Vault
+ * @throws VaultException If any error occurs, or unexpected response received from Vault
+ * @see #unwrap(String)
+ */
+ public AuthResponse 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.
+ * + *In the example below, {@code authToken} is NOT your wrapped token, and should have unwrapping permissions. + * The unwrapped token in {@code unwrappedToken}. Example usage:
+ * + *
+ * {@code
+ * final String authToken = "...";
+ * 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();
+ * }
+ *
+ *
+ * @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}
+ * @return The response information returned from Vault
+ * @throws VaultException If any error occurs, or unexpected response received from Vault
+ * @see #unwrap()
+ */
+ public AuthResponse unwrap(final String wrappedToken) throws VaultException {
+ int retryCount = 0;
+ while (true) {
+ try {
+ // Parse parameters to JSON
+ final JsonObject jsonObject = Json.object();
+ if (wrappedToken != null) {
+ jsonObject.add("token", wrappedToken);
+ }
+
+ final String requestJson = jsonObject.toString();
+ final String url = config.getAddress() + "/v1/sys/wrapping/unwrap";
+
+ // HTTP request to Vault
+ final RestResponse restResponse = new Rest()
+ .url(url)
+ .header("X-Vault-Token", config.getToken())
+ .body(requestJson.getBytes("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(),
+ 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 AuthResponse(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/test/java/com/bettercloud/vault/vault/VaultTestUtils.java b/src/test/java/com/bettercloud/vault/vault/VaultTestUtils.java
index 2747730d..7958959b 100644
--- a/src/test/java/com/bettercloud/vault/vault/VaultTestUtils.java
+++ b/src/test/java/com/bettercloud/vault/vault/VaultTestUtils.java
@@ -1,6 +1,9 @@
package com.bettercloud.vault.vault;
+import com.bettercloud.vault.json.Json;
+import com.bettercloud.vault.json.JsonObject;
import com.bettercloud.vault.vault.mock.MockVault;
+import org.apache.commons.io.IOUtils;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
@@ -10,6 +13,15 @@
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.util.ssl.SslContextFactory;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Optional;
+
+import static java.util.function.Function.identity;
+import static java.util.stream.Collectors.toMap;
+
/**
* Utilities used by all of the Vault-related unit test classes under
* src/test/java/com/bettercloud/vault, to setup and shutdown mock Vault server implementations.
This class is used to mock out a Vault server in unit tests involving retry logic. As it extends Jetty's
@@ -22,7 +29,7 @@
* server.setHandler( new MockVault(200, "{\"data\":{\"value\":\"mock\"}}") );
* server.start();
*
- * final VaultConfig vaultConfig = new VaultConfig("http://127.0.0.1:8999", "mock_token");
+ * final VaultConfig vaultConfig = new VaultConfig().address("http://127.0.0.1:8999").token("mock_token").build();
* final Vault vault = new Vault(vaultConfig);
* final LogicalResponse response = vault.logical().read("secret/hello");
*
@@ -37,15 +44,18 @@ public class MockVault extends AbstractHandler {
private int mockStatus;
private String mockResponse;
+ private JsonObject requestBody;
+ private Map