#8 Initialization from environment variables
This commit is contained in:
parent
c1f6ee891b
commit
e767c07a61
@ -1,4 +1,6 @@
|
|||||||
## 0.6.0 [work in progress]
|
## 0.6.0 [work in progress]
|
||||||
|
* [feature] Initialization from environment variables using `fromEnv()` in factory (#8)
|
||||||
|
* [feature] Automatic authentication with `buildAndAuth()`
|
||||||
* [feature] Custom timeout and number of retries (#9)
|
* [feature] Custom timeout and number of retries (#9)
|
||||||
* [fix] `SecretResponse` does not throw NPE on `get(key)` and `getData()`
|
* [fix] `SecretResponse` does not throw NPE on `get(key)` and `getData()`
|
||||||
|
|
||||||
|
6
pom.xml
6
pom.xml
@ -72,5 +72,11 @@
|
|||||||
<version>2.0.0.0</version>
|
<version>2.0.0.0</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.stefanbirkner</groupId>
|
||||||
|
<artifactId>system-rules</artifactId>
|
||||||
|
<version>1.16.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
package de.stklcode.jvault.connector.factory;
|
package de.stklcode.jvault.connector.factory;
|
||||||
|
|
||||||
import de.stklcode.jvault.connector.HTTPVaultConnector;
|
import de.stklcode.jvault.connector.HTTPVaultConnector;
|
||||||
|
import de.stklcode.jvault.connector.VaultConnector;
|
||||||
|
import de.stklcode.jvault.connector.exception.ConnectionException;
|
||||||
import de.stklcode.jvault.connector.exception.TlsException;
|
import de.stklcode.jvault.connector.exception.TlsException;
|
||||||
import de.stklcode.jvault.connector.exception.VaultConnectorException;
|
import de.stklcode.jvault.connector.exception.VaultConnectorException;
|
||||||
|
|
||||||
@ -25,8 +27,11 @@ import javax.net.ssl.TrustManager;
|
|||||||
import javax.net.ssl.TrustManagerFactory;
|
import javax.net.ssl.TrustManagerFactory;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.security.*;
|
import java.security.*;
|
||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
import java.security.cert.CertificateFactory;
|
import java.security.cert.CertificateFactory;
|
||||||
@ -39,6 +44,11 @@ import java.security.cert.X509Certificate;
|
|||||||
* @since 0.1
|
* @since 0.1
|
||||||
*/
|
*/
|
||||||
public class HTTPVaultConnectorFactory extends VaultConnectorFactory {
|
public class HTTPVaultConnectorFactory extends VaultConnectorFactory {
|
||||||
|
private static final String ENV_VAULT_ADDR = "VAULT_ADDR";
|
||||||
|
private static final String ENV_VAULT_CACERT = "VAULT_CACERT";
|
||||||
|
private static final String ENV_VAULT_TOKEN = "VAULT_TOKEN";
|
||||||
|
private static final String ENV_VAULT_MAX_RETRIES = "VAULT_MAX_RETRIES";
|
||||||
|
|
||||||
public static final String DEFAULT_HOST = "127.0.0.1";
|
public static final String DEFAULT_HOST = "127.0.0.1";
|
||||||
public static final Integer DEFAULT_PORT = 8200;
|
public static final Integer DEFAULT_PORT = 8200;
|
||||||
public static final boolean DEFAULT_TLS = true;
|
public static final boolean DEFAULT_TLS = true;
|
||||||
@ -52,6 +62,7 @@ public class HTTPVaultConnectorFactory extends VaultConnectorFactory {
|
|||||||
private SSLContext sslContext;
|
private SSLContext sslContext;
|
||||||
private int numberOfRetries;
|
private int numberOfRetries;
|
||||||
private Integer timeout;
|
private Integer timeout;
|
||||||
|
private String token;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default empty constructor.
|
* Default empty constructor.
|
||||||
@ -154,6 +165,55 @@ public class HTTPVaultConnectorFactory extends VaultConnectorFactory {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set token for automatic authentication, using {@link #buildAndAuth()}.
|
||||||
|
*
|
||||||
|
* @param token Vault token
|
||||||
|
* @return self
|
||||||
|
* @since 0.6.0
|
||||||
|
*/
|
||||||
|
public HTTPVaultConnectorFactory withToken(String token) throws VaultConnectorException {
|
||||||
|
this.token = token;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build connector based on the {@code }VAULT_ADDR} and {@code VAULT_CACERT} (optional) environment variables.
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
* @since 0.6.0
|
||||||
|
*/
|
||||||
|
public HTTPVaultConnectorFactory fromEnv() throws VaultConnectorException {
|
||||||
|
/* Parse URL from environment variable */
|
||||||
|
if (System.getenv(ENV_VAULT_ADDR) != null && !System.getenv(ENV_VAULT_ADDR).trim().isEmpty()) {
|
||||||
|
try {
|
||||||
|
URL url = new URL(System.getenv(ENV_VAULT_ADDR));
|
||||||
|
this.host = url.getHost();
|
||||||
|
this.port = url.getPort();
|
||||||
|
this.tls = url.getProtocol().equals("https");
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
throw new ConnectionException("URL provided in environment variable malformed", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read number of retries */
|
||||||
|
if (System.getenv(ENV_VAULT_MAX_RETRIES) != null) {
|
||||||
|
try {
|
||||||
|
numberOfRetries = Integer.parseInt(System.getenv(ENV_VAULT_MAX_RETRIES));
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read token */
|
||||||
|
token = System.getenv(ENV_VAULT_TOKEN);
|
||||||
|
|
||||||
|
/* Parse certificate, if set */
|
||||||
|
if (System.getenv(ENV_VAULT_CACERT) != null && !System.getenv(ENV_VAULT_CACERT).trim().isEmpty()) {
|
||||||
|
return withTrustedCA(Paths.get(System.getenv(ENV_VAULT_CACERT)));
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define the number of retries to attempt on 5xx errors.
|
* Define the number of retries to attempt on 5xx errors.
|
||||||
*
|
*
|
||||||
@ -183,6 +243,15 @@ public class HTTPVaultConnectorFactory extends VaultConnectorFactory {
|
|||||||
return new HTTPVaultConnector(host, tls, port, prefix, sslContext, numberOfRetries, timeout);
|
return new HTTPVaultConnector(host, tls, port, prefix, sslContext, numberOfRetries, timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HTTPVaultConnector buildAndAuth() throws VaultConnectorException {
|
||||||
|
if (token == null)
|
||||||
|
throw new ConnectionException("No vault token provided, unable to authenticate.");
|
||||||
|
HTTPVaultConnector con = new HTTPVaultConnector(host, tls, port, prefix, sslContext, numberOfRetries, timeout);
|
||||||
|
con.authToken(token);
|
||||||
|
return con;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create SSL Context trusting only provided certificate.
|
* Create SSL Context trusting only provided certificate.
|
||||||
*
|
*
|
||||||
|
@ -29,6 +29,7 @@ import de.stklcode.jvault.connector.exception.VaultConnectorException;
|
|||||||
public abstract class VaultConnectorFactory {
|
public abstract class VaultConnectorFactory {
|
||||||
/**
|
/**
|
||||||
* Get Factory implementation for HTTP Vault Connector
|
* Get Factory implementation for HTTP Vault Connector
|
||||||
|
*
|
||||||
* @return HTTP Connector Factory
|
* @return HTTP Connector Factory
|
||||||
*/
|
*/
|
||||||
public static HTTPVaultConnectorFactory httpFactory() {
|
public static HTTPVaultConnectorFactory httpFactory() {
|
||||||
@ -37,7 +38,16 @@ public abstract class VaultConnectorFactory {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Build command, produces connector after initialization.
|
* Build command, produces connector after initialization.
|
||||||
|
*
|
||||||
* @return Vault Connector instance.
|
* @return Vault Connector instance.
|
||||||
*/
|
*/
|
||||||
public abstract VaultConnector build();
|
public abstract VaultConnector build();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build connector and authenticate with token set in factory or from environment.
|
||||||
|
*
|
||||||
|
* @return Authenticated Vault connector instance.
|
||||||
|
* @since 0.6.0
|
||||||
|
*/
|
||||||
|
public abstract VaultConnector buildAndAuth() throws VaultConnectorException;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016-2017 Stefan Kalscheuer
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.stklcode.jvault.connector.factory;
|
||||||
|
|
||||||
|
import de.stklcode.jvault.connector.HTTPVaultConnector;
|
||||||
|
import de.stklcode.jvault.connector.exception.TlsException;
|
||||||
|
import de.stklcode.jvault.connector.exception.VaultConnectorException;
|
||||||
|
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.contrib.java.lang.system.EnvironmentVariables;
|
||||||
|
import org.junit.rules.TemporaryFolder;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.nio.file.NoSuchFileException;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.*;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JUnit test for HTTP Vault connector factory
|
||||||
|
*
|
||||||
|
* @author Stefan Kalscheuer
|
||||||
|
* @since 0.6.0
|
||||||
|
*/
|
||||||
|
public class HTTPVaultConnectorFactoryTest {
|
||||||
|
private static String VAULT_ADDR = "https://localhost:8201";
|
||||||
|
private static Integer VAULT_MAX_RETRIES = 13;
|
||||||
|
private static String VAULT_TOKEN = "00001111-2222-3333-4444-555566667777";
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public TemporaryFolder tmpDir = new TemporaryFolder();
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final EnvironmentVariables environment = new EnvironmentVariables();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test building from environment variables
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testFromEnv() throws NoSuchFieldException, IllegalAccessException, IOException {
|
||||||
|
/* Provide address only should be enough */
|
||||||
|
setenv(VAULT_ADDR, null, null, null);
|
||||||
|
|
||||||
|
HTTPVaultConnectorFactory factory = null;
|
||||||
|
HTTPVaultConnector connector;
|
||||||
|
try {
|
||||||
|
factory = VaultConnectorFactory.httpFactory().fromEnv();
|
||||||
|
} catch (VaultConnectorException e) {
|
||||||
|
fail("Factory creation from minimal environment failed");
|
||||||
|
}
|
||||||
|
connector = factory.build();
|
||||||
|
|
||||||
|
assertThat("URL nor set correctly", getPrivate(connector, "baseURL"), is(equalTo(VAULT_ADDR + "/v1/")));
|
||||||
|
assertThat("SSL context set when no cert provided", getPrivate(connector, "sslContext"), is(nullValue()));
|
||||||
|
assertThat("Non-default number of retries, when none set", getPrivate(connector, "retries"), is(0));
|
||||||
|
|
||||||
|
/* Provide address and number of retries */
|
||||||
|
setenv(VAULT_ADDR, null, VAULT_MAX_RETRIES.toString(), null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
factory = VaultConnectorFactory.httpFactory().fromEnv();
|
||||||
|
} catch (VaultConnectorException e) {
|
||||||
|
fail("Factory creation from environment failed");
|
||||||
|
}
|
||||||
|
connector = factory.build();
|
||||||
|
|
||||||
|
assertThat("URL nor set correctly", getPrivate(connector, "baseURL"), is(equalTo(VAULT_ADDR + "/v1/")));
|
||||||
|
assertThat("SSL context set when no cert provided", getPrivate(connector, "sslContext"), is(nullValue()));
|
||||||
|
assertThat("Number of retries not set correctly", getPrivate(connector, "retries"), is(VAULT_MAX_RETRIES));
|
||||||
|
|
||||||
|
/* Provide CA certificate */
|
||||||
|
String VAULT_CACERT = tmpDir.newFolder().toString() + "/doesnotexist";
|
||||||
|
setenv(VAULT_ADDR, VAULT_CACERT, VAULT_MAX_RETRIES.toString(), null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
VaultConnectorFactory.httpFactory().fromEnv();
|
||||||
|
fail("Creation with unknown cert path failed.");
|
||||||
|
} catch (VaultConnectorException e) {
|
||||||
|
assertThat(e, is(instanceOf(TlsException.class)));
|
||||||
|
assertThat(e.getCause(), is(instanceOf(NoSuchFileException.class)));
|
||||||
|
assertThat(((NoSuchFileException)e.getCause()).getFile(), is(VAULT_CACERT));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Automatic authentication */
|
||||||
|
setenv(VAULT_ADDR, null, VAULT_MAX_RETRIES.toString(), VAULT_TOKEN);
|
||||||
|
|
||||||
|
try {
|
||||||
|
factory = VaultConnectorFactory.httpFactory().fromEnv();
|
||||||
|
} catch (VaultConnectorException e) {
|
||||||
|
fail("Factory creation from minimal environment failed");
|
||||||
|
}
|
||||||
|
assertThat("Token nor set correctly", getPrivate(factory, "token"), is(equalTo(VAULT_TOKEN)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setenv(String vault_addr, String vault_cacert, String vault_max_retries, String vault_token) {
|
||||||
|
environment.set("VAULT_ADDR", vault_addr);
|
||||||
|
environment.set("VAULT_CACERT", vault_cacert);
|
||||||
|
environment.set("VAULT_MAX_RETRIES", vault_max_retries);
|
||||||
|
environment.set("VAULT_TOKEN", vault_token);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getPrivate(Object target, String fieldName) throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
Field field = target.getClass().getDeclaredField(fieldName);
|
||||||
|
if (field.isAccessible())
|
||||||
|
return field.get(target);
|
||||||
|
field.setAccessible(true);
|
||||||
|
Object value = field.get(target);
|
||||||
|
field.setAccessible(false);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user