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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ Once you have initialized a `VaultConfig` object, you can use it to construct an
the `Vault` primary driver class:

```
final Vault vault = new Vault(config);
final Vault vault = Vault.create(config);
```

Key Value Secret Engine Config
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/io/github/jopenlibs/vault/SslConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -699,7 +699,7 @@ private static String inputStreamToUTF8(final InputStream input) throws IOExcept
}

/**
* <p>There was a community request to make {@link Vault} and its config class serializable
* <p>There was a community request to make {@link VaultImpl} and its config class serializable
* (<a
* href="https://github.com/BetterCloud/vault-java-driver/pull/51">https://github.com/BetterCloud/vault-java-driver/pull/51</a>).
* However, this SslConfig class now contains a member field of type {@link SSLContext}, which
Expand Down
247 changes: 38 additions & 209 deletions src/main/java/io/github/jopenlibs/vault/Vault.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

import io.github.jopenlibs.vault.api.Auth;
import io.github.jopenlibs.vault.api.Debug;
import io.github.jopenlibs.vault.api.Leases;
import io.github.jopenlibs.vault.api.Logical;
import io.github.jopenlibs.vault.api.Seal;
import io.github.jopenlibs.vault.api.database.Database;
import io.github.jopenlibs.vault.api.mounts.Mounts;
import io.github.jopenlibs.vault.api.pki.Pki;
import io.github.jopenlibs.vault.api.sys.Leases;
import io.github.jopenlibs.vault.api.sys.Seal;
import io.github.jopenlibs.vault.api.sys.Sys;
import io.github.jopenlibs.vault.api.sys.mounts.Mounts;
import io.github.jopenlibs.vault.json.Json;
import io.github.jopenlibs.vault.json.JsonObject;
import io.github.jopenlibs.vault.json.JsonValue;
Expand All @@ -17,70 +18,11 @@
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;

/**
* <p>The Vault driver class, the primary interface through which dependent applications will
* access Vault.</p>
*
* <p>This driver exposes a DSL, compartmentalizing the various endpoints of the HTTP API (e.g.
* "/", "sys/init", "sys/seal") into separate implementation classes (e.g. <code>Logical</code>,
* <code>Init</code>, etc).</p>
*
* <p>Example usage:</p>
*
* <blockquote>
* <pre>{@code
* final VaultConfig config = new VaultConfig
* .address("http://127.0.0.1:8200")
* .token("eace6676-4d78-c687-4e54-03cad00e3abf")
* .build();
* final Vault vault = new Vault(config);
*
* ...
*
* final Map<String, String> secrets = new HashMap<String, String>();
* secrets.put("value", "world");
* secrets.put("other_value", "You can store multiple name/value pairs under a given key");
*
* final LogicalResponse writeResponse = vault
* .withRetries(5, 1000) // optional
* .logical()
* .write("secret/hello", secrets);
*
* ...
*
* final String value = vault.logical()
* .read("secret/hello")
* .getData().get("value");
* }</pre>
* </blockquote>
*/
public class Vault {
public interface Vault {

private final VaultConfig vaultConfig;
private Logger logger = Logger.getLogger(Vault.class.getCanonicalName());

/**
* Construct a Vault driver instance with the provided config settings.
*
* @param vaultConfig Configuration settings for Vault interaction (e.g. server address, token,
* etc) If the VaultConfig Engine version path map is not supplied in the config, default to
* global KV engine version 2.
*/
public Vault(final VaultConfig vaultConfig) {
this.vaultConfig = vaultConfig;
if (this.vaultConfig.getNameSpace() != null && !this.vaultConfig.getNameSpace().isEmpty()) {
logger.info(String.format(
"The NameSpace %s has been bound to this Vault instance. Please keep this in mind when running operations.",
this.vaultConfig.getNameSpace()));
}
if (this.vaultConfig.getSecretsEnginePathMap().isEmpty()
&& this.vaultConfig.getGlobalEngineVersion() == null) {
logger.info(
"Constructing a Vault instance with no provided Engine version, defaulting to version 2.");
this.vaultConfig.setEngineVersion(2);
}
static Vault create(VaultConfig vaultConfig){
return new VaultImpl(vaultConfig);
}

/**
Expand All @@ -92,19 +34,8 @@ public Vault(final VaultConfig vaultConfig) {
* @param engineVersion Which version of the Key/Value Secret Engine to use globally (i.e. 1 or
* 2)
*/
public Vault(final VaultConfig vaultConfig, final Integer engineVersion) {
if (engineVersion < 1 || engineVersion > 2) {
throw new IllegalArgumentException(
"The Engine version must be '1' or '2', the version supplied was: '"
+ engineVersion + "'.");
}
vaultConfig.setEngineVersion(engineVersion);
this.vaultConfig = vaultConfig;
if (this.vaultConfig.getNameSpace() != null && !this.vaultConfig.getNameSpace().isEmpty()) {
logger.info(String.format(
"The Namespace %s has been bound to this Vault instance. Please keep this in mind when running operations.",
this.vaultConfig.getNameSpace()));
}
static Vault create(final VaultConfig vaultConfig, final Integer engineVersion) {
return new VaultImpl(vaultConfig, engineVersion);
}

/**
Expand All @@ -120,31 +51,10 @@ public Vault(final VaultConfig vaultConfig, final Integer engineVersion) {
* fallback.
* @throws VaultException If any error occurs
*/
public Vault(final VaultConfig vaultConfig, final Boolean useSecretsEnginePathMap,
static Vault create(final VaultConfig vaultConfig, final Boolean useSecretsEnginePathMap,
final Integer globalFallbackVersion)
throws VaultException {
this.vaultConfig = vaultConfig;
if (this.vaultConfig.getNameSpace() != null && !this.vaultConfig.getNameSpace().isEmpty()) {
logger.info(String.format(
"The Namespace %s has been bound to this Vault instance. Please keep this in mind when running operations.",
this.vaultConfig.getNameSpace()));
}
this.vaultConfig.setEngineVersion(globalFallbackVersion);
if (useSecretsEnginePathMap && this.vaultConfig.getSecretsEnginePathMap().isEmpty()) {
try {
logger.info(
"No secrets Engine version map was supplied, attempting to generate one.");
final Map<String, String> secretsEnginePathMap = collectSecretEngineVersions();
assert secretsEnginePathMap != null;
this.vaultConfig.getSecretsEnginePathMap().putAll(secretsEnginePathMap);
} catch (Exception e) {
throw new VaultException(String.format(
"An Engine KV version map was not supplied, and unable to determine " +
"KV Engine " +
"version, " + "due to exception: %s",
e.getMessage() + ". Do you have admin rights?"));
}
}
return new VaultImpl(vaultConfig, useSecretsEnginePathMap, globalFallbackVersion);
}

/**
Expand All @@ -157,40 +67,38 @@ public Vault(final VaultConfig vaultConfig, final Boolean useSecretsEnginePathMa
* between retries
* @return This object, with maxRetries and retryIntervalMilliseconds populated
*/
public Vault withRetries(final int maxRetries, final int retryIntervalMilliseconds) {
this.vaultConfig.setMaxRetries(maxRetries);
this.vaultConfig.setRetryIntervalMilliseconds(retryIntervalMilliseconds);
return this;
}
Vault withRetries(final int maxRetries, final int retryIntervalMilliseconds);

/**
* Returns the implementing class for Vault's core/logical operations (e.g. read, write).
*
* @return The implementing class for Vault's core/logical operations (e.g. read, write)
*/
public Logical logical() {
return new Logical(vaultConfig);
}
Logical logical();

/**
* Returns the implementing class for operations on Vault's <code>/v1/auth/*</code> REST
* endpoints
*
* @return The implementing class for Vault's auth operations.
*/
public Auth auth() {
return new Auth(vaultConfig);
}
Auth auth();

/**
* Returns the implementing class for operations on Vault's <code>/v1/sys/*</code> REST
* endpoints
*
* @return The implementing class for Vault's auth operations.
*/
Sys sys();

/**
* Returns the implementing class for Vault's PKI secret backend (i.e. <code>/v1/pki/*</code>
* REST endpoints).
*
* @return The implementing class for Vault's PKI secret backend.
*/
public Pki pki() {
return new Pki(vaultConfig);
}
Pki pki();

/**
* <p>Returns the implementing class for Vault's PKI secret backend, using a custom path when
Expand All @@ -203,7 +111,7 @@ public Pki pki() {
* <blockquote>
* <pre>{@code
* final VaultConfig config = new VaultConfig().address(...).token(...).build();
* final Vault vault = new Vault(config);
* final Vault vault = Vault.create(config);
* final PkiResponse response = vault.pki("root-ca").createOrUpdateRole("testRole");
*
* assertEquals(204, response.getRestResponse().getStatus());
Expand All @@ -214,116 +122,37 @@ public Pki pki() {
* <code>/v1/</code> prefix
* @return The implementing class for Vault's PKI secret backend.
*/
public Pki pki(final String mountPath) {
return new Pki(vaultConfig, mountPath);
}
Pki pki(final String mountPath);

public Database database() {
return new Database(vaultConfig);
}
Database database();

public Database database(final String mountPath) {
return new Database(vaultConfig, mountPath);
}
Database database(final String mountPath);

/**
* Returns the implementing class for Vault's lease operations (e.g. revoke, revoke-prefix).
*
* @return The implementing class for Vault's lease operations (e.g. revoke, revoke-prefix).
* @see Sys#leases()
* @deprecated This method is deprecated and in future it will be removed
*/
public Leases leases() {
return new Leases(vaultConfig);
}
Leases leases();

/**
* Returns the implementing class for Vault's debug operations (e.g. raw, health).
*
* @return The implementing class for Vault's debug operations (e.g. raw, health)
*/
public Debug debug() {
return new Debug(vaultConfig);
}
Debug debug();

/**
* Returns the implementing class for Vault's sys mounts operations (i.e.
* <code>/v1/sys/mounts/*</code> REST endpoints).
*
* @return the implementing class for Vault's sys mounts operations
* @see Sys#mounts()
* @deprecated This method is deprecated and in future it will be removed
*/
public Mounts mounts() {
return new Mounts(vaultConfig);
}
Mounts mounts();

/**
* Returns the implementing class for Vault's seal operations (e.g. seal, unseal, sealStatus).
*
* @return The implementing class for Vault's seal operations (e.g. seal, unseal, sealStatus).
* @see Sys#seal()
* @deprecated This method is deprecated and in future it will be removed
*/
public Seal seal() {
return new Seal(vaultConfig);
}
Seal seal();

/**
* Makes a REST call to Vault, to collect information on which secret engine version (if any) is
* used by each available mount point. Possibilities are:
*
* <ul>
* <li>"2" - A mount point running on Vault 0.10 or higher, configured to use the engine 2
* API</li>
* <li>"1" - A mount point running on Vault 0.10 or higher, configured to use the engine 1
* API</li>
* <li>"unknown" - A mount point running on an older version of Vault. Can more or less be
* treated as "1".</li>
* </ul>
* <p>
* IMPORTANT: Whichever authentication mechanism is being used with the
* <code>VaultConfig</code> object, that principal needs permission to access the
* <code>/v1/sys/mounts</code> REST endpoint.
*
* @return A map of mount points (e.g. "/secret") to secret engine version numbers (e.g. "2")
*/
private Map<String, String> collectSecretEngineVersions() {
try {
final RestResponse restResponse = new Rest()//NOPMD
.url(vaultConfig.getAddress() + "/v1/sys/mounts")
.header("X-Vault-Token", vaultConfig.getToken())
.header("X-Vault-Namespace", this.vaultConfig.getNameSpace())
.header("X-Vault-Request", "true")
.connectTimeoutSeconds(vaultConfig.getOpenTimeout())
.readTimeoutSeconds(vaultConfig.getReadTimeout())
.sslVerification(vaultConfig.getSslConfig().isVerify())
.sslContext(vaultConfig.getSslConfig().getSslContext())
.get();
if (restResponse.getStatus() != 200) {
return null;
}
Map<String, String> getSecretEngineVersions();

final String jsonString = new String(restResponse.getBody(), StandardCharsets.UTF_8);
final Map<String, String> data = new HashMap<>();
final JsonObject jsonData = Json.parse(jsonString).asObject().get("data").asObject();
for (JsonObject.Member member : jsonData) {
final String name = member.getName();
String version = "unknown";

final JsonValue options = member.getValue().asObject().get("options");
if (options != null && options.isObject()) {
final JsonValue ver = options.asObject().get("version");
if (ver != null && ver.isString()) {
version = ver.asString();
}
}
data.put(name, version);
}
return data;
} catch (RestException e) {
System.err.print(
String.format("Unable to retrieve the KV Engine secrets, due to exception: %s",
e.getMessage()));
return null;
}
}

public Map<String, String> getSecretEngineVersions() {
return this.collectSecretEngineVersions();
}
}
Loading