30 Commits

Author SHA1 Message Date
3794f4aac6 Preparations for 0.6.0 release 2017-05-12 12:15:35 +02:00
f805a9c751 Test against 0.7.2 2017-05-09 19:32:00 +02:00
767c2cce91 ReadMe and dependency updates 2017-04-30 13:31:54 +02:00
ed703f6e53 Implement AutoCloseable 2017-04-13 19:49:47 +02:00
e767c07a61 #8 Initialization from environment variables 2017-04-13 19:46:01 +02:00
c1f6ee891b #9 Number of retries and custom timeout 2017-04-10 13:22:54 +02:00
5d46e75068 Prevent SecretResponse from raising NPE in get() 2017-04-10 12:30:36 +02:00
7a67080aa0 TlsException cleanup 2017-04-07 15:47:13 +02:00
81c28f10c1 Travis trusty environment 2017-03-19 12:57:10 +01:00
b99edb86ac .gitignore in repo 2017-03-19 12:40:13 +01:00
594b80f62b Typo fix 2017-03-19 12:13:52 +01:00
4bd614a633 Dependencies updated, Release 0.5.0 2017-03-18 14:39:49 +01:00
0f6bccff02 PNG logo 2017-03-18 14:35:00 +01:00
fca6e496d8 SecretResponse.getValue() deprecated 2017-03-18 14:28:48 +01:00
0f5540913a Logo and ReadMe updates 2017-03-18 14:28:48 +01:00
8129017ad0 #7 Convenience methods for SQL credentials 2017-03-18 14:19:40 +01:00
c0ad451134 Test against vault 0.7.0 2017-03-18 13:49:37 +01:00
5f3e285a8a Added "local" attribute for AuthMethod response (0.7.0 compatibility) 2017-03-18 13:48:47 +01:00
107244cb81 Minor fixed and test coverage 2017-02-10 11:16:31 +01:00
e877d377c0 Dependencies updated 2017-02-10 09:58:09 +01:00
a565411b22 UnitTest against 0.6.5 2017-02-10 09:54:23 +01:00
6f13af5c91 More usage examples 2017-01-09 12:48:49 +01:00
ce2de2df81 TokenBuilder with policies as vararg 2017-01-09 12:42:12 +01:00
d7e4e7e5be Writing of complex data 2017-01-09 12:41:49 +01:00
2f312d3937 Read/write/list arbitrary keys 2017-01-08 12:45:07 +01:00
2f5b6d1523 Travis 2017-01-07 21:09:29 +01:00
69874bdceb ReadMe updated 2017-01-07 19:18:37 +01:00
8ab9c23605 Copyright updated 2017-01-07 19:18:37 +01:00
324bff7e58 Additional information in POM 2016-12-30 10:16:12 +01:00
b036b73e11 Typos 2016-12-27 17:49:51 +01:00
49 changed files with 978 additions and 137 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
/target/
/*.iml
/.idea/
/*.project
*~

13
.travis.yml Normal file
View File

@ -0,0 +1,13 @@
branches:
only:
- master
language: java
jdk:
- oraclejdk8
dist: trusty
env:
- PATH=$PATH:.
before_script:
- wget https://releases.hashicorp.com/vault/0.7.2/vault_0.7.2_linux_amd64.zip
- unzip vault_0.7.2_linux_amd64.zip
- rm vault_0.7.2_linux_amd64.zip

View File

@ -1,6 +1,20 @@
## 0.6.0 [2017-05-12]
* [feature] Initialization from environment variables using `fromEnv()` in factory (#8)
* [feature] Automatic authentication with `buildAndAuth()`
* [feature] Custom timeout and number of retries (#9)
* [feature] Connector implements `AutoCloseable`
* [fix] `SecretResponse` does not throw NPE on `get(key)` and `getData()`
* [test] Tested against Vault 0.7.2
## 0.5.0 [2017-03-18]
* [feature] Convenience methods for DB credentials (#7)
* [fix] Minor bugfix in TokenBuilder
* [deprecation] `SecretResponse.getValue()` deprecated
* [test] Tested against Vault 0.7.0
## 0.4.1 [2016-12-24] ## 0.4.1 [2016-12-24]
* [fix] Factory Null-tolerant for trusted certificate (#6) * [fix] Factory Null-tolerant for trusted certificate (#6)
* [test] StackTraces testet for secret leaks * [test] StackTraces tested for secret leaks
* [test] Tested against Vault 0.6.4 * [test] Tested against Vault 0.6.4
## 0.4.0 [2016-11-06] ## 0.4.0 [2016-11-06]

103
README.md
View File

@ -1,12 +1,19 @@
Java Vault Connector # Java Vault Connector
=========
[![Build Status](https://travis-ci.org/stklcode/jvaultconnector.svg?branch=master)](https://travis-ci.org/stklcode/jvaultconnector)
[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://github.com/stklcode/jvaultconnector/blob/master/LICENSE.txt)
[![Maven Central](https://img.shields.io/maven-central/v/de.stklcode.jvault/connector.svg)](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22de.stklcode.jvault%22%20AND%20a%3A%22connector%22)
![Logo](https://raw.githubusercontent.com/stklcode/jvaultconnector/master/assets/logo.png)
Java Vault Connector is a connector library for [Vault](https://www.vaultproject.io) by [Hashicorp](https://www.hashicorp.com) written in Java. The connector allows simple usage of Vault's secret store in own applications. Java Vault Connector is a connector library for [Vault](https://www.vaultproject.io) by [Hashicorp](https://www.hashicorp.com) written in Java. The connector allows simple usage of Vault's secret store in own applications.
**Current available features:** ## Features:
* HTTP(S) backend connector * HTTP(S) backend connector
* Ability to provide or enforce custom CA certificate * Ability to provide or enforce custom CA certificate
* Authorization methods: * Optional initialization from environment variables
* Authorization methods
* Token * Token
* Username/Password * Username/Password
* AppID (register and authenticate) [_deprecated_] * AppID (register and authenticate) [_deprecated_]
@ -21,46 +28,104 @@ Java Vault Connector is a connector library for [Vault](https://www.vaultproject
* Delete secrets * Delete secrets
* Renew/revoke leases * Renew/revoke leases
* Raw secret content or JSON decoding * Raw secret content or JSON decoding
* SQL secret handling
* Connector Factory with builder pattern * Connector Factory with builder pattern
* Tested against Vault 0.6.4 * Tested against Vault 0.7.2
**Usage Example**
## Maven Artifact
```
<dependency>
<groupId>de.stklcode.jvault</groupId>
<artifactId>connector</artifactId>
<version>0.6.0</version>
</dependency>
```
## Usage Examples
### Initialization
```java ```java
// Instanciate using builder pattern style factory // Instantiate using builder pattern style factory (TLS enabled by default)
VaultConnector vault = VaultConnectorFactory.httpFactory() VaultConnector vault = VaultConnectorFactory.httpFactory()
.withHost("127.0.0.1") .withHost("127.0.0.1")
.withPort(8200) .withPort(8200)
.withTLS() .withTLS()
.build(); .build();
//authenticate with token // Instantiate with custom SSL context
VaultConnector vault = VaultConnectorFactory.httpFactory()
.withHost("example.com")
.withPort(8200)
.withTrustedCA(Paths.get("/path/to/CA.pem"))
.build();
// Initialization from environment variables
VaultConnector vault = VaultConnectorFactory.httpFactory()
.fromEnv()
.build();
```
### Authentication
```java
// Authenticate with token
vault.authToken("01234567-89ab-cdef-0123-456789abcdef"); vault.authToken("01234567-89ab-cdef-0123-456789abcdef");
// retrieve secret // Authenticate with username and password
vault.authUserPass("username", "p4ssw0rd");
// Authenticate with AppID (secret - 2nd argument - is optional)
vault.authAppId("01234567-89ab-cdef-0123-456789abcdef", "fedcba98-7654-3210-fedc-ba9876543210");
```
### Secret read & write
```java
// Retrieve secret (prefix "secret/" assumed, use read() to read arbitrary paths)
String secret = vault.readSecret("some/secret/key").getValue(); String secret = vault.readSecret("some/secret/key").getValue();
// Complex secret
Map<String, Object> secretData = vault.readSecret("another/secret/key").getData();
// Write simple secret
vault.writeSecret("new/secret/key", "secret value");
// Write complex data to arbitraty path
Map<String, Object> map = [...]
vault.write("any/path/to/write", map);
// Delete secret
vault.delete("any/path/to/write");
``` ```
**Maven Artifact** ### Token and role creation
```
<dependency> ```java
<groupId>de.stklcode.jvault</groupId> // Create token using TokenBuilder
<artifactId>connector</artifactId> Token token = new TokenBuilder().withId("token id")
<version>0.4.1</version> .withDisplayName("new test token")
</dependency> .withPolicies("pol1", "pol2")
.build();
vault.createToken(token);
// Create AppRole credentials
vault.createAppRole("testrole", policyList);
AppRoleSecretResponse secret = vault.createAppRoleSecret("testrole");
``` ```
**Links** ## Links
[Project Page](http://jvault.stklcode.de) [Project Page](http://jvault.stklcode.de)
[JavaDoc API](http://jvault.stklcode.de/apidocs/) [JavaDoc API](http://jvault.stklcode.de/apidocs/)
**Planned features:** ## Planned features
* Creation and modification of policies * Creation and modification of policies
* Implement more authentication methods * Implement more authentication methods
**License** ## License
The project is licensed under [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0). The project is licensed under [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0).

BIN
assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

12
assets/logo.svg Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128">
<path d="M4,12 l60,104 l60,-104 z" stroke="none" fill="#000000" />
<circle cx="78" cy="24" r="6" stroke="none" fill="#00a9c7" />
<circle cx="78" cy="38" r="6" stroke="none" fill="#00a9c7" />
<circle cx="78" cy="52" r="6" stroke="none" fill="#00a9c7" />
<circle cx="78" cy="66" r="6" stroke="none" fill="#00a9c7" />
<circle cx="72" cy="78" r="6" stroke="none" fill="#00a9c7" />
<circle cx="58" cy="78" r="6" stroke="none" fill="#00a9c7" />
<circle cx="52" cy="66" r="6" stroke="none" fill="#00a9c7" />
</svg>

After

Width:  |  Height:  |  Size: 759 B

35
pom.xml
View File

@ -2,11 +2,23 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<name>jVaultConnector</name>
<groupId>de.stklcode.jvault</groupId> <groupId>de.stklcode.jvault</groupId>
<artifactId>connector</artifactId> <artifactId>connector</artifactId>
<version>0.4.1</version> <version>0.6.0</version>
<packaging>jar</packaging>
<name>jVaultConnector</name>
<description>Connector artifact for Hashicorp's Vault secret management</description>
<url>https://jvault.stklcode.de</url>
<licenses>
<license>
<name>Apache License 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.html</url>
<distribution>repo</distribution>
</license>
</licenses>
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@ -17,7 +29,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.6.0</version> <version>3.6.1</version>
<configuration> <configuration>
<source>1.8</source> <source>1.8</source>
<target>1.8</target> <target>1.8</target>
@ -25,28 +37,27 @@
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
<packaging>jar</packaging>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.apache.httpcomponents</groupId> <groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId> <artifactId>httpcore</artifactId>
<version>4.4.5</version> <version>4.4.6</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.httpcomponents</groupId> <groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId> <artifactId>httpclient</artifactId>
<version>4.5.2</version> <version>4.5.3</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId> <artifactId>jackson-core</artifactId>
<version>2.8.5</version> <version>2.8.8</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId> <artifactId>jackson-databind</artifactId>
<version>2.8.5</version> <version>2.8.8.1</version>
</dependency> </dependency>
<dependency> <dependency>
@ -61,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>

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -23,11 +23,13 @@ import de.stklcode.jvault.connector.model.*;
import de.stklcode.jvault.connector.model.response.*; import de.stklcode.jvault.connector.model.response.*;
import de.stklcode.jvault.connector.model.response.embedded.AuthMethod; import de.stklcode.jvault.connector.model.response.embedded.AuthMethod;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.*; import org.apache.http.client.methods.*;
import org.apache.http.client.utils.URIBuilder; import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity; import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.util.EntityUtils; import org.apache.http.util.EntityUtils;
import javax.net.ssl.*; import javax.net.ssl.*;
@ -58,13 +60,14 @@ public class HTTPVaultConnector implements VaultConnector {
private static final String PATH_AUTH_USERPASS = "auth/userpass/login/"; private static final String PATH_AUTH_USERPASS = "auth/userpass/login/";
private static final String PATH_AUTH_APPID = "auth/app-id/"; private static final String PATH_AUTH_APPID = "auth/app-id/";
private static final String PATH_AUTH_APPROLE = "auth/approle/"; private static final String PATH_AUTH_APPROLE = "auth/approle/";
private static final String PATH_SECRET = "secret";
private static final String PATH_REVOKE = "sys/revoke/"; private static final String PATH_REVOKE = "sys/revoke/";
private final ObjectMapper jsonMapper; private final ObjectMapper jsonMapper;
private final String baseURL; /* Base URL of Vault */ private final String baseURL; /* Base URL of Vault */
private final SSLContext sslContext; /* Custom SSLSocketFactory */ private final SSLContext sslContext; /* Custom SSLSocketFactory */
private final int retries; /* Number of retries on 5xx errors */
private final Integer timeout; /* Timeout in milliseconds */
private boolean authorized = false; /* authorization status */ private boolean authorized = false; /* authorization status */
private String token; /* current token */ private String token; /* current token */
@ -116,11 +119,26 @@ public class HTTPVaultConnector implements VaultConnector {
* @param sslContext Custom SSL Context * @param sslContext Custom SSL Context
*/ */
public HTTPVaultConnector(String hostname, boolean useTLS, Integer port, String prefix, SSLContext sslContext) { public HTTPVaultConnector(String hostname, boolean useTLS, Integer port, String prefix, SSLContext sslContext) {
this(hostname, useTLS, port, prefix, sslContext, 0, null);
}
/**
* Create connector using hostname, schema, port, path and trusted certificate.
*
* @param hostname The hostname
* @param useTLS If TRUE, use HTTPS, otherwise HTTP
* @param port The port
* @param prefix HTTP API prefix (default: /v1/)
* @param sslContext Custom SSL Context
*/
public HTTPVaultConnector(String hostname, boolean useTLS, Integer port, String prefix, SSLContext sslContext, int numberOfRetries, Integer timeout) {
this(((useTLS) ? "https" : "http") + this(((useTLS) ? "https" : "http") +
"://" + hostname + "://" + hostname +
((port != null) ? ":" + port : "") + ((port != null) ? ":" + port : "") +
prefix, prefix,
sslContext); sslContext,
numberOfRetries,
timeout);
} }
/** /**
@ -139,8 +157,32 @@ public class HTTPVaultConnector implements VaultConnector {
* @param sslContext Custom SSL Context * @param sslContext Custom SSL Context
*/ */
public HTTPVaultConnector(String baseURL, SSLContext sslContext) { public HTTPVaultConnector(String baseURL, SSLContext sslContext) {
this(baseURL, sslContext, 0, null);
}
/**
* Create connector using full URL and trusted certificate.
*
* @param baseURL The URL
* @param sslContext Custom SSL Context
* @param numberOfRetries number of retries on 5xx errors
*/
public HTTPVaultConnector(String baseURL, SSLContext sslContext, int numberOfRetries) {
this(baseURL, sslContext, numberOfRetries, null);
}
/**
* Create connector using full URL and trusted certificate.
*
* @param baseURL The URL
* @param sslContext Custom SSL Context
* @param numberOfRetries number of retries on 5xx errors
*/
public HTTPVaultConnector(String baseURL, SSLContext sslContext, int numberOfRetries, Integer timeout) {
this.baseURL = baseURL; this.baseURL = baseURL;
this.sslContext = sslContext; this.sslContext = sslContext;
this.retries = numberOfRetries;
this.timeout = timeout;
this.jsonMapper = new ObjectMapper(); this.jsonMapper = new ObjectMapper();
} }
@ -463,12 +505,12 @@ public class HTTPVaultConnector implements VaultConnector {
} }
@Override @Override
public SecretResponse readSecret(final String key) throws VaultConnectorException { public SecretResponse read(final String key) throws VaultConnectorException {
if (!isAuthorized()) if (!isAuthorized())
throw new AuthorizationRequiredException(); throw new AuthorizationRequiredException();
/* Request HTTP response and parse Secret */ /* Request HTTP response and parse Secret */
try { try {
String response = requestGet(PATH_SECRET + "/" + key, new HashMap<>()); String response = requestGet(key, new HashMap<>());
return jsonMapper.readValue(response, SecretResponse.class); return jsonMapper.readValue(response, SecretResponse.class);
} catch (IOException e) { } catch (IOException e) {
throw new InvalidResponseException("Unable to parse response", e); throw new InvalidResponseException("Unable to parse response", e);
@ -479,12 +521,12 @@ public class HTTPVaultConnector implements VaultConnector {
} }
@Override @Override
public List<String> listSecrets(final String path) throws VaultConnectorException { public List<String> list(final String path) throws VaultConnectorException {
if (!isAuthorized()) if (!isAuthorized())
throw new AuthorizationRequiredException(); throw new AuthorizationRequiredException();
try { try {
String response = requestGet(PATH_SECRET + "/" + path + "/?list=true", new HashMap<>()); String response = requestGet(path + "/?list=true", new HashMap<>());
SecretListResponse secrets = jsonMapper.readValue(response, SecretListResponse.class); SecretListResponse secrets = jsonMapper.readValue(response, SecretListResponse.class);
return secrets.getKeys(); return secrets.getKeys();
} catch (IOException e) { } catch (IOException e) {
@ -495,27 +537,24 @@ public class HTTPVaultConnector implements VaultConnector {
} }
} }
@Override public void write(final String key, final Map<String, Object> data) throws VaultConnectorException {
public void writeSecret(final String key, final String value) throws VaultConnectorException {
if (!isAuthorized()) if (!isAuthorized())
throw new AuthorizationRequiredException(); throw new AuthorizationRequiredException();
if (key == null || key.isEmpty()) if (key == null || key.isEmpty())
throw new InvalidRequestException("Secret path must not be empty."); throw new InvalidRequestException("Secret path must not be empty.");
Map<String, String> param = new HashMap<>(); if (!requestPost(key, data).equals(""))
param.put("value", value);
if (!requestPost(PATH_SECRET + "/" + key, param).equals(""))
throw new InvalidResponseException("Received response where none was expected."); throw new InvalidResponseException("Received response where none was expected.");
} }
@Override @Override
public void deleteSecret(String key) throws VaultConnectorException { public void delete(String key) throws VaultConnectorException {
if (!isAuthorized()) if (!isAuthorized())
throw new AuthorizationRequiredException(); throw new AuthorizationRequiredException();
/* Request HTTP response and expect empty result */ /* Request HTTP response and expect empty result */
String response = requestDelete(PATH_SECRET + "/" + key); String response = requestDelete(key);
/* Response should be code 204 without content */ /* Response should be code 204 without content */
if (!response.equals("")) if (!response.equals(""))
@ -571,6 +610,13 @@ public class HTTPVaultConnector implements VaultConnector {
return createTokenInternal(token, PATH_TOKEN + PATH_CREATE + "/" + role); return createTokenInternal(token, PATH_TOKEN + PATH_CREATE + "/" + role);
} }
@Override
public void close() {
authorized = false;
token = null;
tokenTTL = 0;
}
/** /**
* Create token. * Create token.
* Centralized method to handle different token creation requests. * Centralized method to handle different token creation requests.
@ -638,7 +684,7 @@ public class HTTPVaultConnector implements VaultConnector {
if (token != null) if (token != null)
post.addHeader("X-Vault-Token", token); post.addHeader("X-Vault-Token", token);
return request(post); return request(post, retries);
} }
/** /**
@ -665,7 +711,7 @@ public class HTTPVaultConnector implements VaultConnector {
if (token != null) if (token != null)
put.addHeader("X-Vault-Token", token); put.addHeader("X-Vault-Token", token);
return request(put); return request(put, retries);
} }
/** /**
@ -682,7 +728,7 @@ public class HTTPVaultConnector implements VaultConnector {
if (token != null) if (token != null)
delete.addHeader("X-Vault-Token", token); delete.addHeader("X-Vault-Token", token);
return request(delete); return request(delete, retries);
} }
/** /**
@ -705,22 +751,27 @@ public class HTTPVaultConnector implements VaultConnector {
if (token != null) if (token != null)
get.addHeader("X-Vault-Token", token); get.addHeader("X-Vault-Token", token);
return request(get); return request(get, retries);
} }
/** /**
* Execute prepared HTTP request and return result. * Execute prepared HTTP request and return result.
* *
* @param base Prepares Request * @param base Prepares Request
* @param retries number of retries
* @return HTTP response * @return HTTP response
* @throws VaultConnectorException on connection error * @throws VaultConnectorException on connection error
*/ */
private String request(HttpRequestBase base) throws VaultConnectorException { private String request(HttpRequestBase base, int retries) throws VaultConnectorException {
/* Set JSON Header */ /* Set JSON Header */
base.addHeader("accept", "application/json"); base.addHeader("accept", "application/json");
HttpResponse response = null; HttpResponse response = null;
try (CloseableHttpClient httpClient = HttpClientBuilder.create().setSSLContext(sslContext).build()) { try (CloseableHttpClient httpClient = HttpClientBuilder.create().setSSLContext(sslContext).build()) {
/* Set custom timeout, if defined */
if (this.timeout != null)
base.setConfig(RequestConfig.copy(RequestConfig.DEFAULT).setConnectTimeout(timeout).build());
/* Execute request */
response = httpClient.execute(base); response = httpClient.execute(base);
/* Check if response is valid */ /* Check if response is valid */
if (response == null) if (response == null)
@ -737,20 +788,26 @@ public class HTTPVaultConnector implements VaultConnector {
case 403: case 403:
throw new PermissionDeniedException(); throw new PermissionDeniedException();
default: default:
InvalidResponseException ex = new InvalidResponseException("Invalid response code") if (response.getStatusLine().getStatusCode() >= 500 && response.getStatusLine().getStatusCode() < 600 && retries > 0) {
.withStatusCode(response.getStatusLine().getStatusCode()); /* Retry on 5xx errors */
if (response.getEntity() != null) { return request(base, retries - 1);
try (BufferedReader br = new BufferedReader(new InputStreamReader(response.getEntity().getContent()))) { } else {
String responseString = br.lines().collect(Collectors.joining("\n")); /* Fail on different error code and/or no retries left */
ErrorResponse er = jsonMapper.readValue(responseString, ErrorResponse.class); InvalidResponseException ex = new InvalidResponseException("Invalid response code")
/* Check for "permission denied" response */ .withStatusCode(response.getStatusLine().getStatusCode());
if (er.getErrors().size() > 0 && er.getErrors().get(0).equals("permission denied")) if (response.getEntity() != null) {
throw new PermissionDeniedException(); try (BufferedReader br = new BufferedReader(new InputStreamReader(response.getEntity().getContent()))) {
throw ex.withResponse(er.toString()); String responseString = br.lines().collect(Collectors.joining("\n"));
} catch (IOException ignored) { ErrorResponse er = jsonMapper.readValue(responseString, ErrorResponse.class);
/* Check for "permission denied" response */
if (er.getErrors().size() > 0 && er.getErrors().get(0).equals("permission denied"))
throw new PermissionDeniedException();
throw ex.withResponse(er.toString());
} catch (IOException ignored) {
}
} }
throw ex;
} }
throw ex;
} }
} catch (IOException e) { } catch (IOException e) {
throw new InvalidResponseException("Unable to read response", e); throw new InvalidResponseException("Unable to read response", e);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,13 +16,12 @@
package de.stklcode.jvault.connector; package de.stklcode.jvault.connector;
import de.stklcode.jvault.connector.exception.AuthorizationRequiredException; import de.stklcode.jvault.connector.exception.InvalidRequestException;
import de.stklcode.jvault.connector.exception.VaultConnectorException; import de.stklcode.jvault.connector.exception.VaultConnectorException;
import de.stklcode.jvault.connector.model.*; import de.stklcode.jvault.connector.model.*;
import de.stklcode.jvault.connector.model.response.*; import de.stklcode.jvault.connector.model.response.*;
import java.util.ArrayList; import java.util.*;
import java.util.List;
/** /**
* Vault Connector interface. * Vault Connector interface.
@ -31,7 +30,9 @@ import java.util.List;
* @author Stefan Kalscheuer * @author Stefan Kalscheuer
* @since 0.1 * @since 0.1
*/ */
public interface VaultConnector { public interface VaultConnector extends AutoCloseable {
String PATH_SECRET = "secret";
/** /**
* Reset authorization information. * Reset authorization information.
*/ */
@ -358,39 +359,121 @@ public interface VaultConnector {
boolean isAuthorized(); boolean isAuthorized();
/** /**
* Retrieve secret form Vault. * Retrieve any nodes content from Vault.
*
* @param key Secret identifier
* @return Secret response
* @throws VaultConnectorException on error
* @since 0.5.0
*/
SecretResponse read(final String key) throws VaultConnectorException;
/**
* Retrieve secret from Vault.
* Prefix "secret/" is automatically added to key.
* *
* @param key Secret identifier * @param key Secret identifier
* @return Secret response * @return Secret response
* @throws VaultConnectorException on error * @throws VaultConnectorException on error
*/ */
SecretResponse readSecret(final String key) throws VaultConnectorException; default SecretResponse readSecret(final String key) throws VaultConnectorException {
return read(PATH_SECRET + "/" + key);
}
/**
* List available nodes from Vault.
*
* @param path Root path to search
* @return List of secret keys
* @throws VaultConnectorException on error
* @since 0.5.0
*/
List<String> list(final String path) throws VaultConnectorException;
/** /**
* List available secrets from Vault. * List available secrets from Vault.
* Prefix "secret/" is automatically added to path.
* *
* @param path Root path to search * @param path Root path to search
* @return List of secret keys * @return List of secret keys
* @throws VaultConnectorException on error * @throws VaultConnectorException on error
*/ */
List<String> listSecrets(final String path) throws VaultConnectorException; default List<String> listSecrets(final String path) throws VaultConnectorException {
return list(PATH_SECRET + "/" + path);
}
/**
* Write simple value to Vault.
*
* @param key Secret path
* @param value Secret value
* @throws VaultConnectorException on error
* @since 0.5.0
*/
default void write(final String key, final String value) throws VaultConnectorException {
Map<String, Object> param = new HashMap<>();
param.put("value", value);
write(key, param);
}
/**
* Write value to Vault.
*
* @param key Secret path
* @param data Secret content. Value must be be JSON serializable.
* @throws VaultConnectorException on error
* @since 0.5.0
*/
void write(final String key, final Map<String, Object> data) throws VaultConnectorException;
/** /**
* Write secret to Vault. * Write secret to Vault.
* Prefix "secret/" is automatically added to path.
* *
* @param key Secret path * @param key Secret path
* @param value Secret value * @param value Secret value
* @throws VaultConnectorException on error * @throws VaultConnectorException on error
*/ */
void writeSecret(final String key, final String value) throws VaultConnectorException; default void writeSecret(final String key, final String value) throws VaultConnectorException {
Map<String, Object> param = new HashMap<>();
param.put("value", value);
writeSecret(key, param);
}
/**
* Write secret to Vault.
* Prefix "secret/" is automatically added to path.
*
* @param key Secret path
* @param data Secret content. Value must be be JSON serializable.
* @throws VaultConnectorException on error
* @since 0.5.0
*/
default void writeSecret(final String key, final Map<String, Object> data) throws VaultConnectorException {
if (key == null || key.isEmpty())
throw new InvalidRequestException("Secret path must not be empty.");
write(PATH_SECRET + "/" + key, data);
}
/**
* Delete key from Vault.
*
* @param key Secret path
* @throws VaultConnectorException on error
* @since 0.5.0
*/
void delete(final String key) throws VaultConnectorException;
/** /**
* Delete secret from Vault. * Delete secret from Vault.
* Prefix "secret/" is automatically added to path.
* *
* @param key Secret path * @param key Secret path
* @throws VaultConnectorException on error * @throws VaultConnectorException on error
*/ */
void deleteSecret(final String key) throws VaultConnectorException; default void deleteSecret(final String key) throws VaultConnectorException {
delete(PATH_SECRET + "/" + key);
}
/** /**
* Revoke given lease immediately. * Revoke given lease immediately.
@ -403,7 +486,7 @@ public interface VaultConnector {
/** /**
* Renew lease with given ID. * Renew lease with given ID.
* *
* @param leaseID the lase ID * @param leaseID the lase ID
* @return Renewed lease * @return Renewed lease
* @throws VaultConnectorException on error * @throws VaultConnectorException on error
*/ */
@ -458,4 +541,65 @@ public interface VaultConnector {
* @throws VaultConnectorException on error * @throws VaultConnectorException on error
*/ */
TokenResponse lookupToken(final String token) throws VaultConnectorException; TokenResponse lookupToken(final String token) throws VaultConnectorException;
/**
* Read credentials for MySQL backend at default mount point
*
* @param role the role name
* @return the credentials response
* @throws VaultConnectorException on error
* @since 0.5.0
*/
default CredentialsResponse readMySqlCredentials(final String role) throws VaultConnectorException {
return readDbCredentials(role, "mysql");
}
/**
* Read credentials for PostgreSQL backend at default mount point
*
* @param role the role name
* @return the credentials response
* @throws VaultConnectorException on error
* @since 0.5.0
*/
default CredentialsResponse readPostgreSqlCredentials(final String role) throws VaultConnectorException {
return readDbCredentials(role, "postgresql");
}
/**
* Read credentials for MSSQL backend at default mount point
*
* @param role the role name
* @return the credentials response
* @throws VaultConnectorException on error
* @since 0.5.0
*/
default CredentialsResponse readMsSqlCredentials(final String role) throws VaultConnectorException {
return readDbCredentials(role, "mssql");
}
/**
* Read credentials for MSSQL backend at default mount point
*
* @param role the role name
* @return the credentials response
* @throws VaultConnectorException on error
* @since 0.5.0
*/
default CredentialsResponse readMongoDbCredentials(final String role) throws VaultConnectorException {
return readDbCredentials(role, "mongodb");
}
/**
* Read credentials for SQL backends.
*
* @param role the role name
* @param mount mount point of the SQL backend
* @return the credentials response
* @throws VaultConnectorException on error
* @since 0.5.0
*/
default CredentialsResponse readDbCredentials(final String role, final String mount) throws VaultConnectorException {
return (CredentialsResponse) read(mount + "/creds/" + role);
}
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -23,9 +23,6 @@ package de.stklcode.jvault.connector.exception;
* @since 0.4.0 * @since 0.4.0
*/ */
public class TlsException extends VaultConnectorException { public class TlsException extends VaultConnectorException {
private Integer statusCode;
private String response;
public TlsException() { public TlsException() {
} }
@ -40,12 +37,4 @@ public class TlsException extends VaultConnectorException {
public TlsException(String message, Throwable cause) { public TlsException(String message, Throwable cause) {
super(message, cause); super(message, cause);
} }
public Integer getStatusCode() {
return statusCode;
}
public String getResponse() {
return response;
}
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -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,16 +44,25 @@ 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;
public static final String DEFAULT_PREFIX = "/v1/"; public static final String DEFAULT_PREFIX = "/v1/";
public static final int DEFAULT_NUMBER_OF_RETRIES = 0;
private String host; private String host;
private Integer port; private Integer port;
private boolean tls; private boolean tls;
private String prefix; private String prefix;
private SSLContext sslContext; private SSLContext sslContext;
private int numberOfRetries;
private Integer timeout;
private String token;
/** /**
* Default empty constructor. * Default empty constructor.
@ -59,6 +73,7 @@ public class HTTPVaultConnectorFactory extends VaultConnectorFactory {
port = DEFAULT_PORT; port = DEFAULT_PORT;
tls = DEFAULT_TLS; tls = DEFAULT_TLS;
prefix = DEFAULT_PREFIX; prefix = DEFAULT_PREFIX;
numberOfRetries = DEFAULT_NUMBER_OF_RETRIES;
} }
/** /**
@ -150,9 +165,91 @@ 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.
*
* @param numberOfRetries The number of retries to attempt on 5xx errors (default: 0)
* @return self
* @since 0.6.0
*/
public HTTPVaultConnectorFactory withNumberOfRetries(int numberOfRetries) {
this.numberOfRetries = numberOfRetries;
return this;
}
/**
* Define a custom timeout for the HTTP connection.
*
* @param milliseconds Timeout value in milliseconds.
* @return self
* @since 0.6.0
*/
public HTTPVaultConnectorFactory withTimeout(int milliseconds) {
this.timeout = milliseconds;
return this;
}
@Override @Override
public HTTPVaultConnector build() { public HTTPVaultConnector build() {
return new HTTPVaultConnector(host, tls, port, prefix, sslContext); 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;
} }
/** /**

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -23,13 +23,14 @@ import de.stklcode.jvault.connector.exception.VaultConnectorException;
* Abstract Vault Connector Factory interface. * Abstract Vault Connector Factory interface.
* Provides builder pattern style factory for Vault connectors. * Provides builder pattern style factory for Vault connectors.
* *
* @author Stefan Kalscheuer * @author Stefan Kalscheuer
* @since 0.1 * @since 0.1
*/ */
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() {
return new HTTPVaultConnectorFactory(); return new HTTPVaultConnectorFactory();
@ -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;
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,6 +18,7 @@ package de.stklcode.jvault.connector.model;
import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.annotation.*;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,10 +18,7 @@ package de.stklcode.jvault.connector.model;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.ArrayList; import java.util.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** /**
* A builder for vault tokens. * A builder for vault tokens.
@ -144,6 +141,17 @@ public class TokenBuilder {
return withNoDefaultPolicy(true); return withNoDefaultPolicy(true);
} }
/**
* Add given policies
*
* @param policies the policies
* @return self
* @since 0.5.0
*/
public TokenBuilder withPolicies(final String... policies) {
return withPolicies(Arrays.asList(policies));
}
/** /**
* Add given policies * Add given policies
* *

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -0,0 +1,48 @@
/*
* 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.model.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.response.embedded.TokenData;
import java.io.IOException;
import java.util.Map;
/**
* Vault response from credentials lookup. Simple wrapper for data objects containing username and password fields.
*
* @author Stefan Kalscheuer
* @since 0.5.0
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class CredentialsResponse extends SecretResponse {
public String getUsername() {
if (get("username") != null)
return get("username").toString();
return null;
}
public String getPassword() {
if (get("username") != null)
return get("username").toString();
return null;
}
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -21,6 +21,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.InvalidResponseException; import de.stklcode.jvault.connector.exception.InvalidResponseException;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** /**
@ -45,6 +46,8 @@ public class SecretResponse extends VaultDataResponse {
* @since 0.4.0 * @since 0.4.0
*/ */
public Map<String, Object> getData() { public Map<String, Object> getData() {
if (data == null)
return new HashMap<>();
return data; return data;
} }
@ -56,7 +59,9 @@ public class SecretResponse extends VaultDataResponse {
* @since 0.4.0 * @since 0.4.0
*/ */
public Object get(String key) { public Object get(String key) {
return data.get(key); if (data == null)
return null;
return getData().get(key);
} }
/** /**
@ -64,11 +69,13 @@ public class SecretResponse extends VaultDataResponse {
* Method for backwards compatibility in case of simple secrets. * Method for backwards compatibility in case of simple secrets.
* *
* @return the value * @return the value
* @deprecated Deprecated artifact, will be removed at latest at v1.0.0
*/ */
@Deprecated
public String getValue() { public String getValue() {
if (data.get("value") == null) if (get("value") == null)
return null; return null;
return data.get("value").toString(); return get("value").toString();
} }
/** /**
@ -79,7 +86,9 @@ public class SecretResponse extends VaultDataResponse {
* @return Parsed object * @return Parsed object
* @throws InvalidResponseException on parsing error * @throws InvalidResponseException on parsing error
* @since 0.3 * @since 0.3
* @deprecated Deprecated artifact, will be removed at latest at v1.0.0
*/ */
@Deprecated
public <T> T getValue(Class<T> type) throws InvalidResponseException { public <T> T getValue(Class<T> type) throws InvalidResponseException {
return get("value", type); return get("value", type);
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,6 +16,7 @@
package de.stklcode.jvault.connector.model.response.embedded; package de.stklcode.jvault.connector.model.response.embedded;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSetter; import com.fasterxml.jackson.annotation.JsonSetter;
import de.stklcode.jvault.connector.model.AuthBackend; import de.stklcode.jvault.connector.model.AuthBackend;
@ -28,6 +29,7 @@ import java.util.Map;
* @author Stefan Kalscheuer * @author Stefan Kalscheuer
* @since 0.1 * @since 0.1
*/ */
@JsonIgnoreProperties(ignoreUnknown = true)
public class AuthMethod { public class AuthMethod {
private AuthBackend type; private AuthBackend type;
private String rawType; private String rawType;
@ -38,6 +40,9 @@ public class AuthMethod {
@JsonProperty("config") @JsonProperty("config")
private Map<String, String> config; private Map<String, String> config;
@JsonProperty("local")
private boolean local;
@JsonSetter("type") @JsonSetter("type")
public void setType(String type) { public void setType(String type) {
this.rawType = type; this.rawType = type;
@ -59,4 +64,8 @@ public class AuthMethod {
public Map<String, String> getConfig() { public Map<String, String> getConfig() {
return config; return config;
} }
public boolean isLocal() {
return local;
}
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -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;
}
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -0,0 +1,215 @@
/*
* 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.model;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.hamcrest.Matchers.emptyString;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.junit.MatcherAssume.assumeThat;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
/**
* JUnit Test for AppRoleSecret model.
*
* @author Stefan Kalscheuer
* @since 0.5.0
*/
public class AppRoleSecretTest {
private static final String TEST_ID = "abc123";
private static final Map<String, Object> TEST_META = new HashMap<>();
private static final List<String> TEST_CIDR = Arrays.asList("203.0.113.0/24", "198.51.100.0/24");
static {
TEST_META.put("foo", "bar");
TEST_META.put("number", 1337);
}
/**
* Test constructors.
*/
@Test
public void constructorTest() {
/* Empty constructor */
AppRoleSecret secret = new AppRoleSecret();
assertThat(secret.getId(), is(nullValue()));
assertThat(secret.getAccessor(), is(nullValue()));
assertThat(secret.getMetadata(), is(nullValue()));
assertThat(secret.getCidrList(), is(nullValue()));
assertThat(secret.getCidrListString(), is(emptyString()));
assertThat(secret.getCreationTime(), is(nullValue()));
assertThat(secret.getExpirationTime(), is(nullValue()));
assertThat(secret.getLastUpdatedTime(), is(nullValue()));
assertThat(secret.getNumUses(), is(nullValue()));
assertThat(secret.getTtl(), is(nullValue()));
/* Constructor with ID */
secret = new AppRoleSecret(TEST_ID);
assertThat(secret.getId(), is(TEST_ID));
assertThat(secret.getAccessor(), is(nullValue()));
assertThat(secret.getMetadata(), is(nullValue()));
assertThat(secret.getCidrList(), is(nullValue()));
assertThat(secret.getCidrListString(), is(emptyString()));
assertThat(secret.getCreationTime(), is(nullValue()));
assertThat(secret.getExpirationTime(), is(nullValue()));
assertThat(secret.getLastUpdatedTime(), is(nullValue()));
assertThat(secret.getNumUses(), is(nullValue()));
assertThat(secret.getTtl(), is(nullValue()));
/* Constructor with Metadata and CIDR bindings */
secret = new AppRoleSecret(TEST_ID, TEST_META, TEST_CIDR);
assertThat(secret.getId(), is(TEST_ID));
assertThat(secret.getAccessor(), is(nullValue()));
assertThat(secret.getMetadata(), is(TEST_META));
assertThat(secret.getCidrList(), is(TEST_CIDR));
assertThat(secret.getCidrListString(), is(String.join(",", TEST_CIDR)));
assertThat(secret.getCreationTime(), is(nullValue()));
assertThat(secret.getExpirationTime(), is(nullValue()));
assertThat(secret.getLastUpdatedTime(), is(nullValue()));
assertThat(secret.getNumUses(), is(nullValue()));
assertThat(secret.getTtl(), is(nullValue()));
}
/**
* Test setter.
*/
@Test
public void setterTest() {
AppRoleSecret secret = new AppRoleSecret(TEST_ID);
assertThat(secret.getCidrList(), is(nullValue()));
assertThat(secret.getCidrListString(), is(emptyString()));
secret.setCidrList(TEST_CIDR);
assertThat(secret.getCidrList(), is(TEST_CIDR));
assertThat(secret.getCidrListString(), is(String.join(",", TEST_CIDR)));
secret.setCidrList(null);
assertThat(secret.getCidrList(), is(nullValue()));
assertThat(secret.getCidrListString(), is(emptyString()));
}
/**
* Test JSON (de)serialization.
*/
@Test
public void jsonTest() throws NoSuchFieldException, IllegalAccessException {
ObjectMapper mapper = new ObjectMapper();
/* A simple roundtrip first. All set fields should be present afterwards. */
AppRoleSecret secret = new AppRoleSecret(TEST_ID, TEST_META, TEST_CIDR);
String secretJson = "";
try {
secretJson = mapper.writeValueAsString(secret);
} catch (JsonProcessingException e) {
e.printStackTrace();
fail("Serialization failed");
}
/* CIDR list is comma-separated when used as input, but List otherwise, hence convert string to list */
secretJson = commaSeparatedToList(secretJson);
AppRoleSecret secret2;
try {
secret2 = mapper.readValue(secretJson, AppRoleSecret.class);
assertThat(secret.getId(), is(secret2.getId()));
assertThat(secret.getMetadata(), is(secret2.getMetadata()));
assertThat(secret.getCidrList(), is(secret2.getCidrList()));
} catch (IOException e) {
e.printStackTrace();
fail("Deserialization failed");
}
/* Test fields, that should not be written to JSON */
setPrivateField(secret, "accessor", "TEST_ACCESSOR");
assumeThat(secret.getAccessor(), is("TEST_ACCESSOR"));
setPrivateField(secret, "creationTime", "TEST_CREATION");
assumeThat(secret.getCreationTime(), is("TEST_CREATION"));
setPrivateField(secret, "expirationTime", "TEST_EXPIRATION");
assumeThat(secret.getExpirationTime(), is("TEST_EXPIRATION"));
setPrivateField(secret, "lastUpdatedTime", "TEST_UPDATETIME");
assumeThat(secret.getLastUpdatedTime(), is("TEST_UPDATETIME"));
setPrivateField(secret, "numUses", 678);
assumeThat(secret.getNumUses(), is(678));
setPrivateField(secret, "ttl", 12345);
assumeThat(secret.getTtl(), is(12345));
try {
secretJson = mapper.writeValueAsString(secret);
} catch (JsonProcessingException e) {
e.printStackTrace();
fail("Serialization failed");
}
try {
secret2 = mapper.readValue(commaSeparatedToList(secretJson), AppRoleSecret.class);
assertThat(secret.getId(), is(secret2.getId()));
assertThat(secret.getMetadata(), is(secret2.getMetadata()));
assertThat(secret.getCidrList(), is(secret2.getCidrList()));
assertThat(secret2.getAccessor(), is(nullValue()));
assertThat(secret2.getCreationTime(), is(nullValue()));
assertThat(secret2.getExpirationTime(), is(nullValue()));
assertThat(secret2.getLastUpdatedTime(), is(nullValue()));
assertThat(secret2.getNumUses(), is(nullValue()));
assertThat(secret2.getTtl(), is(nullValue()));
} catch (IOException e) {
e.printStackTrace();
fail("Deserialization failed");
}
/* Those fields should be deserialized from JSON though */
secretJson = "{\"secret_id\":\"abc123\",\"metadata\":{\"number\":1337,\"foo\":\"bar\"}," +
"\"cidr_list\":[\"203.0.113.0/24\",\"198.51.100.0/24\"],\"secret_id_accessor\":\"TEST_ACCESSOR\"," +
"\"creation_time\":\"TEST_CREATION\",\"expiration_time\":\"TEST_EXPIRATION\"," +
"\"last_updated_time\":\"TEST_LASTUPDATE\",\"secret_id_num_uses\":678,\"secret_id_ttl\":12345}";
try {
secret2 = mapper.readValue(secretJson, AppRoleSecret.class);
assertThat(secret2.getAccessor(), is("TEST_ACCESSOR"));
assertThat(secret2.getCreationTime(), is("TEST_CREATION"));
assertThat(secret2.getExpirationTime(), is("TEST_EXPIRATION"));
assertThat(secret2.getLastUpdatedTime(), is("TEST_LASTUPDATE"));
assertThat(secret2.getNumUses(), is(678));
assertThat(secret2.getTtl(), is(12345));
} catch (IOException e) {
e.printStackTrace();
fail("Deserialization failed");
}
}
private static void setPrivateField(Object object, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {
Field field = object.getClass().getDeclaredField(fieldName);
boolean accessible = field.isAccessible();
field.setAccessible(true);
field.set(object, value);
field.setAccessible(accessible);
}
private static String commaSeparatedToList(String json) {
return json.replaceAll("\"cidr_list\":\"([^\"]*)\"", "\"cidr_list\":\\[$1\\]")
.replaceAll("(\\d+\\.\\d+\\.\\d+\\.\\d+/\\d+)", "\"$1\"");
}
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -45,7 +45,8 @@ public class TokenBuilderTest {
private static final Integer NUM_USES = 4; private static final Integer NUM_USES = 4;
private static final List<String> POLICIES = new ArrayList<>(); private static final List<String> POLICIES = new ArrayList<>();
private static final String POLICY = "policy"; private static final String POLICY = "policy";
private static final String POLICY_2 = "policy"; private static final String POLICY_2 = "policy2";
private static final String POLICY_3 = "policy3";
private static final Map<String, String> META = new HashMap<>(); private static final Map<String, String> META = new HashMap<>();
private static final String META_KEY = "key"; private static final String META_KEY = "key";
private static final String META_VALUE = "value"; private static final String META_VALUE = "value";
@ -138,11 +139,11 @@ public class TokenBuilderTest {
assertThat(token.getPolicies(), hasSize(1)); assertThat(token.getPolicies(), hasSize(1));
assertThat(token.getPolicies(), contains(POLICY_2)); assertThat(token.getPolicies(), contains(POLICY_2));
token = new TokenBuilder() token = new TokenBuilder()
.withPolicies(POLICIES) .withPolicies(POLICY, POLICY_2)
.withPolicy(POLICY_2) .withPolicy(POLICY_3)
.build(); .build();
assertThat(token.getPolicies(), hasSize(2)); assertThat(token.getPolicies(), hasSize(3));
assertThat(token.getPolicies(), contains(POLICY, POLICY_2)); assertThat(token.getPolicies(), contains(POLICY, POLICY_2, POLICY_3));
/* Add single metadata */ /* Add single metadata */
token = new TokenBuilder().withMeta(META_KEY_2, META_VALUE_2).build(); token = new TokenBuilder().withMeta(META_KEY_2, META_VALUE_2).build();

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Stefan Kalscheuer * Copyright 2016-2017 Stefan Kalscheuer
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.