From 451a594e89e4a4ae3df86f28b65572a6dd212fab Mon Sep 17 00:00:00 2001
From: Dima Gutzeit <11374909+innovationhub-asia@users.noreply.github.com>
Date: Mon, 20 Nov 2017 23:05:37 +0800
Subject: [PATCH 01/10] CryptoClient implementation
- Add support for creating keys
- Add support for querying for symmetic keys
- Add support for querying asymmetric keys
---
build.gradle | 7 +-
gradle/dependencies.gradle | 4 +-
gradle/wrapper/gradle-wrapper.properties | 2 +-
.../nike/vault/client/VaultAdminClient.java | 14 --
.../com/nike/vault/client/VaultClient.java | 14 ++
.../nike/vault/client/VaultClientFactory.java | 183 ++++++++++++++++++
.../nike/vault/client/VaultCryptoClient.java | 99 ++++++++++
.../model/VaultAsymmetricKeyResponse.java | 94 +++++++++
.../client/model/VaultCreateKeyRequest.java | 87 +++++++++
.../vault/client/model/VaultKeyResponse.java | 133 +++++++++++++
.../model/VaultSymmetricKeyResponse.java | 60 ++++++
.../vault/client/VaultCryptoClientTest.java | 120 ++++++++++++
.../com/nike/vault/client/asymmetric_key.json | 1 +
.../com/nike/vault/client/symmetric_key.json | 1 +
14 files changed, 799 insertions(+), 20 deletions(-)
create mode 100644 src/main/java/com/nike/vault/client/VaultCryptoClient.java
create mode 100644 src/main/java/com/nike/vault/client/model/VaultAsymmetricKeyResponse.java
create mode 100644 src/main/java/com/nike/vault/client/model/VaultCreateKeyRequest.java
create mode 100644 src/main/java/com/nike/vault/client/model/VaultKeyResponse.java
create mode 100644 src/main/java/com/nike/vault/client/model/VaultSymmetricKeyResponse.java
create mode 100644 src/test/java/com/nike/vault/client/VaultCryptoClientTest.java
create mode 100644 src/test/resources/com/nike/vault/client/asymmetric_key.json
create mode 100644 src/test/resources/com/nike/vault/client/symmetric_key.json
diff --git a/build.gradle b/build.gradle
index 6e9beaf..52e99a9 100644
--- a/build.gradle
+++ b/build.gradle
@@ -22,9 +22,10 @@ apply plugin: 'java'
apply plugin: 'maven'
apply plugin: 'com.jfrog.bintray'
apply plugin: 'maven-publish'
+apply plugin: 'idea'
-sourceCompatibility = 1.7
-targetCompatibility = 1.7
+sourceCompatibility = 1.8
+targetCompatibility = 1.8
task copyProjectVersion() {
def releaseVersion = version
@@ -39,4 +40,4 @@ apply from: file('gradle/check.gradle')
apply from: file('gradle/integration.gradle')
apply from: file('gradle/bintray.gradle')
-group = groupId
\ No newline at end of file
+group = groupId
diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle
index 018e90b..b8f8e5d 100644
--- a/gradle/dependencies.gradle
+++ b/gradle/dependencies.gradle
@@ -20,7 +20,7 @@ repositories {
dependencies {
compile "org.apache.commons:commons-lang3:3.4"
- compile "com.squareup.okhttp3:okhttp:3.9.0"
+ compile "com.squareup.okhttp3:okhttp:3.9.1"
compile "com.google.code.gson:gson:2.5"
compile "com.google.code.findbugs:jsr305:3.0.1"
compile "org.slf4j:slf4j-api:1.7.25"
@@ -32,4 +32,4 @@ dependencies {
testCompile "org.assertj:assertj-core:2.3.0"
testCompile "com.squareup.okhttp3:mockwebserver:3.7.0"
testCompile "commons-io:commons-io:2.4"
-}
\ No newline at end of file
+}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 3b1fc4f..c6d4df5 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -19,4 +19,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.14-bin.zip
+distributionUrl=http\://services.gradle.org/distributions/gradle-4.1-bin.zip
diff --git a/src/main/java/com/nike/vault/client/VaultAdminClient.java b/src/main/java/com/nike/vault/client/VaultAdminClient.java
index bd8fa63..e970a32 100644
--- a/src/main/java/com/nike/vault/client/VaultAdminClient.java
+++ b/src/main/java/com/nike/vault/client/VaultAdminClient.java
@@ -337,18 +337,4 @@ public void disableAuditBackend(final String path) {
parseAndThrowErrorResponse(response);
}
}
-
- /**
- * Barebones method that can be used to make any call to Vault. The caller is responsible for interpreting
- * and de-serializing the response. The Gson instance used by the client is accessible via {@link #getGson()}
- *
- * @param path Path to the resource
- * @param method HTTP method
- * @param requestBody Request body to be serialized as JSON. Set to null if no request body
- * @return HTTP response object
- */
- public Response execute(final String path, final String method, final Object requestBody) {
- final HttpUrl url = buildUrl("", path);
- return execute(url, method, requestBody);
- }
}
diff --git a/src/main/java/com/nike/vault/client/VaultClient.java b/src/main/java/com/nike/vault/client/VaultClient.java
index 5e5fd85..f4f935e 100644
--- a/src/main/java/com/nike/vault/client/VaultClient.java
+++ b/src/main/java/com/nike/vault/client/VaultClient.java
@@ -424,4 +424,18 @@ protected String responseBodyAsString(Response response) {
return "ERROR failed to print response body as str: " + ioe.getMessage();
}
}
+
+ /**
+ * Barebones method that can be used to make any call to Vault. The caller is responsible for interpreting
+ * and de-serializing the response. The Gson instance used by the client is accessible via {@link #getGson()}
+ *
+ * @param path Path to the resource
+ * @param method HTTP method
+ * @param requestBody Request body to be serialized as JSON. Set to null if no request body
+ * @return HTTP response object
+ */
+ public Response execute(final String path, final String method, final Object requestBody) {
+ final HttpUrl url = buildUrl("", path);
+ return execute(url, method, requestBody);
+ }
}
diff --git a/src/main/java/com/nike/vault/client/VaultClientFactory.java b/src/main/java/com/nike/vault/client/VaultClientFactory.java
index 3946b49..4dcfcc1 100644
--- a/src/main/java/com/nike/vault/client/VaultClientFactory.java
+++ b/src/main/java/com/nike/vault/client/VaultClientFactory.java
@@ -308,4 +308,187 @@ public static VaultAdminClient getAdminClient(final UrlResolver vaultUrlResolver
.build(),
headers.build());
}
+
+
+
+
+ /**
+ * Basic factory method that will build a Vault admin client that
+ * looks up the Vault URL from one of the following places:
+ *
+ * - Environment Variable -
VAULT_ADDR
+ * - Java System Property -
vault.addr
+ *
+ * Default recommended credential provider and http client are used.
+ *
+ * @return Vault admin client
+ */
+ public static VaultCryptoClient getCryptoClient() {
+ return getCryptoClient(new DefaultVaultUrlResolver(),
+ new DefaultVaultCredentialsProviderChain(),
+ DEFAULT_MAX_REQUESTS,
+ DEFAULT_MAX_REQUESTS,
+ DEFAULT_TIMEOUT,
+ DEFAULT_TIMEOUT,
+ DEFAULT_TIMEOUT,
+ DEFAULT_HEADERS
+ );
+ }
+
+ /**
+ * Factory method allows setting of the Vault URL resolver, but will use
+ * the default recommended credentials provider chain and http client.
+ *
+ * @param vaultUrlResolver URL resolver for Vault
+ * @return Vault admin client
+ */
+ public static VaultCryptoClient getCryptoClient(final UrlResolver vaultUrlResolver) {
+ return getCryptoClient(vaultUrlResolver,
+ new DefaultVaultCredentialsProviderChain(),
+ DEFAULT_MAX_REQUESTS,
+ DEFAULT_MAX_REQUESTS,
+ DEFAULT_TIMEOUT,
+ DEFAULT_TIMEOUT,
+ DEFAULT_TIMEOUT,
+ DEFAULT_HEADERS
+ );
+ }
+
+ /**
+ * Factory method that allows for a user defined Vault URL resolver and credentials provider.
+ *
+ * @param vaultUrlResolver URL resolver for Vault
+ * @param vaultCredentialsProvider Credential provider for acquiring a token for interacting with Vault
+ * @return Vault admin client
+ */
+ public static VaultCryptoClient getCryptoClient(final UrlResolver vaultUrlResolver,
+ final VaultCredentialsProvider vaultCredentialsProvider) {
+ return getCryptoClient(vaultUrlResolver,
+ vaultCredentialsProvider,
+ DEFAULT_MAX_REQUESTS,
+ DEFAULT_MAX_REQUESTS,
+ DEFAULT_TIMEOUT,
+ DEFAULT_TIMEOUT,
+ DEFAULT_TIMEOUT,
+ DEFAULT_HEADERS
+ );
+ }
+
+ /**
+ * Factory method that allows for a user defined Vault URL resolver and credentials provider.
+ *
+ * @param vaultUrlResolver URL resolver for Vault
+ * @param vaultCredentialsProvider Credential provider for acquiring a token for interacting with Vault
+ * @param maxRequestsPerHost Max Requests per Host used by the dispatcher
+ * @return Vault admin client
+ */
+ public static VaultCryptoClient getCryptoClient(final UrlResolver vaultUrlResolver,
+ final VaultCredentialsProvider vaultCredentialsProvider,
+ final int maxRequestsPerHost) {
+ return getCryptoClient(vaultUrlResolver,
+ vaultCredentialsProvider,
+ DEFAULT_MAX_REQUESTS,
+ maxRequestsPerHost,
+ DEFAULT_TIMEOUT,
+ DEFAULT_TIMEOUT,
+ DEFAULT_TIMEOUT,
+ DEFAULT_HEADERS);
+ }
+
+ /**
+ * Factory method that allows a user to define default HTTP headers to be added to every HTTP request made from the
+ * VaultClient. The user can also define their Vault URL resolver and credentials provider.
+ *
+ * @param vaultUrlResolver URL resolver for Vault
+ * @param vaultCredentialsProvider Credential provider for acquiring a token for interacting with Vault
+ * @param defaultHeaders Map of default header names and values to add to every HTTP request
+ * @return Vault client
+ */
+ public static VaultCryptoClient getCryptoClient(final UrlResolver vaultUrlResolver,
+ final VaultCredentialsProvider vaultCredentialsProvider,
+ final Map defaultHeaders) {
+ return getCryptoClient(vaultUrlResolver,
+ vaultCredentialsProvider,
+ DEFAULT_MAX_REQUESTS,
+ DEFAULT_MAX_REQUESTS,
+ DEFAULT_TIMEOUT,
+ DEFAULT_TIMEOUT,
+ DEFAULT_TIMEOUT,
+ defaultHeaders);
+ }
+
+ /**
+ * Factory method that allows the user to completely configure the VaultClient.
+ *
+ * @param vaultUrlResolver URL resolver for Vault
+ * @param vaultCredentialsProvider Credential provider for acquiring a token for interacting with Vault
+ * @param maxRequestsPerHost Max Requests per Host used by the dispatcher
+ * @param defaultHeaders Map of default header names and values to add to every HTTP request
+ * @return Vault admin client
+ */
+ public static VaultCryptoClient getCryptoClient(final UrlResolver vaultUrlResolver,
+ final VaultCredentialsProvider vaultCredentialsProvider,
+ final int maxRequestsPerHost,
+ final Map defaultHeaders) {
+ return getCryptoClient(vaultUrlResolver,
+ vaultCredentialsProvider,
+ DEFAULT_MAX_REQUESTS,
+ maxRequestsPerHost,
+ DEFAULT_TIMEOUT,
+ DEFAULT_TIMEOUT,
+ DEFAULT_TIMEOUT,
+ defaultHeaders);
+ }
+
+ /**
+ * Factory method that allows the user to completely configure the VaultClient.
+ *
+ * @param vaultUrlResolver URL resolver for Vault
+ * @param vaultCredentialsProvider Credential provider for acquiring a token for interacting with Vault
+ * @param maxRequests Max HTTP Requests allowed in-flight
+ * @param maxRequestsPerHost Max HTTP Requests per Host
+ * @param connectTimeoutMillis HTTP connect timeout in milliseconds
+ * @param readTimeoutMillis HTTP read timeout in milliseconds
+ * @param writeTimeoutMillis HTTP write timeout in milliseconds
+ * @param defaultHeaders Map of default header names and values to add to every HTTP request
+ * @return Vault admin client
+ */
+ public static VaultCryptoClient getCryptoClient(final UrlResolver vaultUrlResolver,
+ final VaultCredentialsProvider vaultCredentialsProvider,
+ final int maxRequests,
+ final int maxRequestsPerHost,
+ final int connectTimeoutMillis,
+ final int readTimeoutMillis,
+ final int writeTimeoutMillis,
+ final Map defaultHeaders) {
+ if (defaultHeaders == null) {
+ throw new IllegalArgumentException("Default headers cannot be null.");
+ }
+
+ Dispatcher dispatcher = new Dispatcher();
+ dispatcher.setMaxRequests(maxRequests);
+ dispatcher.setMaxRequestsPerHost(maxRequestsPerHost);
+
+
+ List connectionSpecs = new ArrayList<>();
+ connectionSpecs.add(TLS_1_2_OR_NEWER);
+ // for unit tests
+ connectionSpecs.add(CLEARTEXT);
+
+ Headers.Builder headers = new Headers.Builder();
+ for (Map.Entry header : defaultHeaders.entrySet()) {
+ headers.add(header.getKey(), header.getValue());
+ }
+
+ return new VaultCryptoClient(vaultUrlResolver,
+ vaultCredentialsProvider,
+ new OkHttpClient.Builder()
+ .connectTimeout(connectTimeoutMillis, DEFAULT_TIMEOUT_UNIT)
+ .writeTimeout(writeTimeoutMillis, DEFAULT_TIMEOUT_UNIT)
+ .readTimeout(readTimeoutMillis, DEFAULT_TIMEOUT_UNIT)
+ .dispatcher(dispatcher)
+ .connectionSpecs(connectionSpecs)
+ .build(),
+ headers.build());
+ }
}
diff --git a/src/main/java/com/nike/vault/client/VaultCryptoClient.java b/src/main/java/com/nike/vault/client/VaultCryptoClient.java
new file mode 100644
index 0000000..485e88b
--- /dev/null
+++ b/src/main/java/com/nike/vault/client/VaultCryptoClient.java
@@ -0,0 +1,99 @@
+package com.nike.vault.client;
+
+import static com.nike.vault.client.model.VaultCreateKeyRequest.TYPE_AES256_GCM96;
+
+import com.google.gson.reflect.TypeToken;
+import com.nike.vault.client.auth.VaultCredentialsProvider;
+import com.nike.vault.client.http.HttpMethod;
+import com.nike.vault.client.http.HttpStatus;
+import com.nike.vault.client.model.VaultAsymmetricKeyResponse;
+import com.nike.vault.client.model.VaultCreateKeyRequest;
+import com.nike.vault.client.model.VaultKeyResponse;
+import com.nike.vault.client.model.VaultSymmetricKeyResponse;
+import java.lang.reflect.Type;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import okhttp3.Headers;
+import okhttp3.HttpUrl;
+import okhttp3.OkHttpClient;
+import okhttp3.Response;
+
+public class VaultCryptoClient extends VaultClient {
+
+ private static final String TRANSIT_PATH_PREFIX = "v1/transit/";
+
+ /**
+ * Explicit constructor that allows for full control over construction of the Vault client.
+ *
+ * @param vaultUrlResolver URL resolver for Vault
+ * @param credentialsProvider Credential provider for acquiring a token for interacting with Vault
+ * @param httpClient HTTP client for calling Vault
+ */
+ public VaultCryptoClient(final UrlResolver vaultUrlResolver,
+ final VaultCredentialsProvider credentialsProvider,
+ final OkHttpClient httpClient) {
+ super(vaultUrlResolver, credentialsProvider, httpClient);
+ }
+
+ /**
+ * Explicit constructor that allows for full control over construction of the Vault client.
+ *
+ * @param vaultUrlResolver URL resolver for Vault
+ * @param credentialsProvider Credential provider for acquiring a token for interacting with Vault
+ * @param httpClient HTTP client for calling Vault
+ * @param defaultHeaders Default HTTP headers to be included in each request made by the returned VaultClient
+ */
+ public VaultCryptoClient(final UrlResolver vaultUrlResolver,
+ final VaultCredentialsProvider credentialsProvider,
+ final OkHttpClient httpClient,
+ final Headers defaultHeaders) {
+ super(vaultUrlResolver, credentialsProvider, httpClient, defaultHeaders);
+ }
+
+ /**
+ * Creates a new key.
+ *
+ * @param name Key name
+ * @param vaultCreateKeyRequest Request object with optional parameters
+ * @return Auth response with the token and details
+ */
+ public void createKey(@Nonnull final String name, @Nonnull final VaultCreateKeyRequest vaultCreateKeyRequest) {
+ final HttpUrl url = buildUrl(TRANSIT_PATH_PREFIX, "keys/" + name);
+ final Response response = execute(url, HttpMethod.POST, vaultCreateKeyRequest);
+
+ if (response.code() != HttpStatus.NO_CONTENT) {
+ parseAndThrowErrorResponse(response);
+ }
+ }
+
+ /**
+ * Retrieve key information
+ *
+ * @param name Name of the key to lookup
+ * @return Key information response, can be either Symmetric or Asymmetric keys
+ */
+ public VaultKeyResponse getKeyInfo(@Nonnull final String name) {
+ final HttpUrl url = buildUrl(TRANSIT_PATH_PREFIX, "keys/" + name);
+ final Response response = execute(url, HttpMethod.GET, null);
+
+ if (response.code() != HttpStatus.OK) {
+ parseAndThrowErrorResponse(response);
+ }
+
+ final Type mapType = new TypeToken