35 Commits

Author SHA1 Message Date
aa2619b1b0 Bump version to 0.7.1 2018-03-17 14:01:55 +01:00
88d49bc9f7 Added automatic module name for JPMS compatibility 2018-03-15 19:54:18 +01:00
27e7a2dffc Test against 0.9.5 2018-02-27 18:07:23 +01:00
41c15f285c Test against 0.9.4 2018-02-24 10:58:45 +01:00
5f419e3f9b Updated test dependencies
* JUnit 5.1.0
* Mockito 2.15.0
2018-02-20 19:25:44 +01:00
9362e245ee Minor test changes
Changed Thread.sleep() to TimeUnit.SECONDS.sleep()
2018-02-20 19:25:07 +01:00
b2082925d5 Copy Vault data directory to temp location before each test
To avoid the annoying clean before each test run (because Vault data has
been modified by previous run), the data is now copied from resource
directory to temporary location.
2018-02-04 20:04:47 +01:00
38a7d4952d Minor dependency updates
* httpcore 4.4.9
* httpclient 4.5.5
* jackon 2.9.4
2018-01-29 16:34:28 +01:00
23cea38da6 Test against 0.9.3 2018-01-29 16:30:51 +01:00
bf2da210ba Test against 0.9.2 2018-01-28 10:35:19 +01:00
91fb012acc Update copyright notice to 2018 2018-01-01 17:22:02 +01:00
61e1f3f745 Test against 0.9.1 2017-12-22 16:40:15 +01:00
50cd400ba3 Migrated tests to JUnit5 and removed PowerMock
* Unit tests are using JUnit Jupiter framework
 * Enabled support for legacy rules for now
 * Replaced PowerMock with custom ByteBuddy redefinition for the offline test
2017-11-26 18:17:13 +01:00
470dcb48ba Test framework migrated to Mockito/Powermock 2
This simple migration with the bare minimum of changes necessary solves
compatibility issues with Java 9 build environments.
2017-11-25 18:43:25 +01:00
736f23c19a Tested against 0.9.0 2017-11-16 20:34:52 +01:00
ed2b9d62a3 Preparations for 0.7.0 release 2017-10-03 17:39:03 +02:00
007b523295 Extracted nested try-blocks from response handling into new methods. 2017-10-03 17:29:11 +02:00
061c1e9743 Minor CleanUp
Reworked some JavaDoc comments and optimized imports.
2017-10-03 17:12:03 +02:00
6904ed6817 Added tests for parse exceptions. 2017-09-27 20:23:14 +02:00
1ed5d8d992 Added tests for unexpected 200 responses.
Some methods do expect code 204 (successful without result), those are now covered by tested.
2017-09-27 20:07:12 +02:00
f70fc084be Override toString() on ErrorResponse
Partially reverts last commit that added first error message to exception
without checking for presence of such.
2017-09-25 20:48:36 +02:00
4b14ab3f4b Offline tests with mocked Vault server
Added some tests that do not require an actual Vault server to test constructors and exceptional behavior.
2017-09-25 20:39:13 +02:00
29776f459e Fixed Exception class on invalid response during sealStatus() 2017-09-25 20:35:37 +02:00
4ca8aa56d2 Test constructors and exceptions on sealStatus 2017-09-24 13:23:13 +02:00
32ab9f4bb1 Updated dependencies 2017-09-24 11:39:26 +02:00
e002fc749a Adaptation to Vault 0.8 endpoints for renew and revoke leases (#11)
Breaking backwards compatibility with Vault 0.7 and below.
2017-09-24 11:08:00 +02:00
b9ad2d1551 Test against 0.8.3
Minor test adaptation, because default policy is no longer added to AppRole by default.
2017-09-23 14:46:41 +02:00
35a8c2e0fa Test constants made constant 2017-09-09 20:32:56 +02:00
89f7581d17 Test against 0.8.2 2017-09-08 19:21:41 +02:00
43511dc20b AppRole path generation with pre-formatted String 2017-08-29 09:04:04 +02:00
05b44759c0 Bundled common error messages in static inner class 2017-08-29 08:56:30 +02:00
ba17286ab3 Add Slack notification to Travis 2017-08-29 08:25:31 +02:00
51e505313a Removed deprecated method listAppRoleSecretss (#14)
Has been deprecated because of a typo in the method name in v0.6.2.
As of 0.7 it is now removed. Please use listAppRoleSecrets() instead.
2017-08-29 08:18:10 +02:00
a1784245a3 Refactored (un)seal methods to throw Exception instead of catching it (#12) 2017-08-28 19:24:52 +02:00
df7de5dd73 Health status query and response model implemented (#15) 2017-08-28 17:50:24 +02:00
55 changed files with 1245 additions and 303 deletions

View File

@ -14,12 +14,16 @@ addons:
env:
- PATH=$PATH:.
before_script:
- wget https://releases.hashicorp.com/vault/0.8.1/vault_0.8.1_linux_amd64.zip
- unzip vault_0.8.1_linux_amd64.zip
- rm vault_0.8.1_linux_amd64.zip
- wget https://releases.hashicorp.com/vault/0.9.5/vault_0.9.5_linux_amd64.zip
- wget -q -O - https://releases.hashicorp.com/vault/0.9.5/vault_0.9.5_SHA256SUMS | grep linux_amd64 | sha256sum -c
- unzip vault_0.9.5_linux_amd64.zip
- rm vault_0.9.5_linux_amd64.zip
cache:
directories:
- '$HOME/.m2/repository'
- '$HOME/.sonar/cache'
script:
- mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package sonar:sonar
notifications:
slack:
secure: "YyE5GePOLkCVTtCy8j507BRmQrtrWhtvmUt4kY0Z2/ptf0LzfuDEJQ4ZbCxO5ri5IDJrrvyPAedjft818+bMzdFfxvi1oviIL+LZNhyev8gfeIBF/U2pvSLGKCRX4g4aZ6NKN3Untjdm8lmiVTltOyZ59JizQVwXzAl3LiOpnJugyBqbhOx4EIqBzwW3gaYAofMqY2LczW5W/M+99HJCst8Mb8H06GstCPEHCizAq7VRaUS68PstlxQMV0Q6bsSYMLFbLWmhuXs96WHqOrT+nNsl07ikr3N8c4HafhFutt2Jyc1+8gXO417+eSvVM0iBpHGwTmfGFfCqx/4Pf62DTJuvh8dR4fLgLDiqEeDrBEcRRDOs9cvXVOO22NN1HuBBJY8VRiFcwNAvuVMXCtnC+1RJRAZB2zubsANiFe+ygk/ywj37cVXY+NpqlBwcSph6jPHo2hD6cIl2rTWn1EnZH519Rh38xTSv6MRzAO9kWNVrAlX+UtvYS8Sk7Owrc0tET9Lc4zj6aI5tsA1wYbN3Jk6EbMhsF6K/XF2npt2qg09pxkj8wmxoUoR6/rGuSv55aSxTdLDmH+en4ahEm3uc4h1lYoVCk0yrZoTAas3zS4WpBCKnl+mweuKNxaejyy0Wv6NR9ZCTaS3yFgibNOjvDpxZxTAPdNBL7hn+k4LwgN4="

View File

@ -1,3 +1,15 @@
## 0.7.1 [2018-03-17]
* [improvement] Added automatic module name for JPMS compatibility
* [dependencies] Minor dependency updates
* [test] Tested against Vault 0.9.5
## 0.7.0 [2017-10-03]
* [feature] Retrieval of health status via `getHealth()` (#15)
* [improvement] `seal()`, `unseal()` are now `void` and throw Exception on error (#12)
* [compatibility] Adaptation to Vault 0.8 endpoints for `renew` and `revoke`, **breaking** 0.7 compatibility (#11)
* [deletion] Removed deprecated `listAppRoleSecretss()` (use `listAppRoleSecrets()`) (#14)
* [test] Tested against Vault 0.8.3
## 0.6.2 [2017-08-19]
* [fix] Prevent potential NPE on SecretResponse getter
* [fix] Removed stack traces on PUT request and response deserialization (#13)

View File

@ -31,15 +31,15 @@ Java Vault Connector is a connector library for [Vault](https://www.vaultproject
* Raw secret content or JSON decoding
* SQL secret handling
* Connector Factory with builder pattern
* Tested against Vault 0.8.1
* Tested against Vault 0.9.5
## Maven Artifact
```
```xml
<dependency>
<groupId>de.stklcode.jvault</groupId>
<artifactId>connector</artifactId>
<version>0.6.2</version>
<version>0.7.1</version>
</dependency>
```

86
pom.xml
View File

@ -4,7 +4,7 @@
<groupId>de.stklcode.jvault</groupId>
<artifactId>connector</artifactId>
<version>0.6.2</version>
<version>0.7.1</version>
<packaging>jar</packaging>
@ -29,43 +29,91 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
<version>3.0.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<configuration>
<archive>
<manifestEntries>
<Automatic-Module-Name>de.stklcode.jvault.connector</Automatic-Module-Name>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19.1</version>
<dependencies>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-surefire-provider</artifactId>
<version>1.1.0</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</pluginManagement>
</build>
<dependencies>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.6</version>
<version>4.4.9</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
<version>4.5.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.0</version>
<version>2.9.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
<version>2.9.4</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-migrationsupport</artifactId>
<version>5.1.0</version>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-junit</artifactId>
@ -75,7 +123,25 @@
<dependency>
<groupId>com.github.stefanbirkner</groupId>
<artifactId>system-rules</artifactId>
<version>1.16.1</version>
<version>1.17.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.15.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>2.15.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
<scope>test</scope>
</dependency>
</dependencies>

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2018 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,7 +19,10 @@ package de.stklcode.jvault.connector;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.*;
import de.stklcode.jvault.connector.model.*;
import de.stklcode.jvault.connector.model.AppRole;
import de.stklcode.jvault.connector.model.AppRoleSecret;
import de.stklcode.jvault.connector.model.AuthBackend;
import de.stklcode.jvault.connector.model.Token;
import de.stklcode.jvault.connector.model.response.*;
import de.stklcode.jvault.connector.model.response.embedded.AuthMethod;
import org.apache.http.HttpResponse;
@ -31,11 +34,16 @@ import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import javax.net.ssl.*;
import java.io.*;
import javax.net.ssl.SSLContext;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
@ -49,7 +57,7 @@ public class HTTPVaultConnector implements VaultConnector {
private static final String PATH_SEAL_STATUS = "sys/seal-status";
private static final String PATH_SEAL = "sys/seal";
private static final String PATH_UNSEAL = "sys/unseal";
private static final String PATH_RENEW = "sys/renew";
private static final String PATH_RENEW = "sys/leases/renew";
private static final String PATH_AUTH = "sys/auth";
private static final String PATH_TOKEN = "auth/token";
private static final String PATH_LOOKUP = "/lookup";
@ -58,7 +66,9 @@ public class HTTPVaultConnector implements VaultConnector {
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_APPROLE = "auth/approle/";
private static final String PATH_REVOKE = "sys/revoke/";
private static final String PATH_AUTH_APPROLE_ROLE = "auth/approle/role/%s%s";
private static final String PATH_REVOKE = "sys/leases/revoke/";
private static final String PATH_HEALTH = "sys/health";
private static final String HEADER_VAULT_TOKEN = "X-Vault-Token";
@ -210,32 +220,25 @@ public class HTTPVaultConnector implements VaultConnector {
}
@Override
public final SealResponse sealStatus() {
public final SealResponse sealStatus() throws VaultConnectorException {
try {
String response = requestGet(PATH_SEAL_STATUS, new HashMap<>());
return jsonMapper.readValue(response, SealResponse.class);
} catch (VaultConnectorException | IOException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
} catch (URISyntaxException ignored) {
/* this should never occur and may leak sensible information */
return null;
throw new InvalidRequestException(Error.URI_FORMAT);
}
}
@Override
public final boolean seal() {
try {
public final void seal() throws VaultConnectorException {
requestPut(PATH_SEAL, new HashMap<>());
return true;
} catch (VaultConnectorException e) {
e.printStackTrace();
return false;
}
}
@Override
public final SealResponse unseal(final String key, final Boolean reset) {
public final SealResponse unseal(final String key, final Boolean reset) throws VaultConnectorException {
Map<String, String> param = new HashMap<>();
param.put("key", key);
if (reset != null)
@ -243,9 +246,27 @@ public class HTTPVaultConnector implements VaultConnector {
try {
String response = requestPut(PATH_UNSEAL, param);
return jsonMapper.readValue(response, SealResponse.class);
} catch (VaultConnectorException | IOException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
}
}
@Override
public HealthResponse getHealth() throws VaultConnectorException {
/* Force status code to be 200, so we don't need to modify the request sequence. */
Map<String, String> param = new HashMap<>();
param.put("standbycode", "200"); // Default: 429.
param.put("sealedcode", "200"); // Default: 503.
param.put("uninitcode", "200"); // Default: 501.
try {
String response = requestGet(PATH_HEALTH, param);
/* Parse response */
return jsonMapper.readValue(response, HealthResponse.class);
} catch (IOException e) {
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
} catch (URISyntaxException e) {
/* this should never occur and may leak sensible information */
throw new InvalidRequestException(Error.URI_FORMAT);
}
}
@ -262,10 +283,10 @@ public class HTTPVaultConnector implements VaultConnector {
AuthMethodsResponse amr = jsonMapper.readValue(response, AuthMethodsResponse.class);
return amr.getSupportedMethods().values().stream().map(AuthMethod::getType).collect(Collectors.toList());
} catch (IOException e) {
throw new InvalidResponseException("Unable to parse response", e);
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
} catch (URISyntaxException ignored) {
/* this should never occur and may leak sensible information */
throw new InvalidRequestException("Invalid URI format.");
throw new InvalidRequestException(Error.URI_FORMAT);
}
}
@ -280,7 +301,7 @@ public class HTTPVaultConnector implements VaultConnector {
authorized = true;
return res;
} catch (IOException e) {
throw new InvalidResponseException("Unable to parse response", e);
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
}
}
@ -331,7 +352,7 @@ public class HTTPVaultConnector implements VaultConnector {
this.authorized = true;
return auth;
} catch (IOException e) {
throw new InvalidResponseException("Unable to parse response", e);
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
}
}
@ -347,8 +368,8 @@ public class HTTPVaultConnector implements VaultConnector {
/* Get response */
String response = requestPost(PATH_AUTH_APPID + "map/app-id/" + appID, payload);
/* Response should be code 204 without content */
if (!response.equals(""))
throw new InvalidResponseException("Received response where non was expected.");
if (!response.isEmpty())
throw new InvalidResponseException(Error.UNEXPECTED_RESPONSE);
return true;
}
@ -362,8 +383,8 @@ public class HTTPVaultConnector implements VaultConnector {
/* Get response */
String response = requestPost(PATH_AUTH_APPID + "map/user-id/" + userID, payload);
/* Response should be code 204 without content */
if (!response.equals(""))
throw new InvalidResponseException("Received response where non was expected.");
if (!response.isEmpty())
throw new InvalidResponseException(Error.UNEXPECTED_RESPONSE);
return true;
}
@ -372,10 +393,10 @@ public class HTTPVaultConnector implements VaultConnector {
if (!isAuthorized())
throw new AuthorizationRequiredException();
/* Get response */
String response = requestPost(PATH_AUTH_APPROLE + "role/" + role.getName(), role);
String response = requestPost(String.format(PATH_AUTH_APPROLE_ROLE, role.getName(), ""), role);
/* Response should be code 204 without content */
if (!response.equals(""))
throw new InvalidResponseException("Received response where non was expected.");
if (!response.isEmpty())
throw new InvalidResponseException(Error.UNEXPECTED_RESPONSE);
/* Set custom ID if provided */
return !(role.getId() != null && !role.getId().isEmpty()) || setAppRoleID(role.getName(), role.getId());
@ -387,13 +408,13 @@ public class HTTPVaultConnector implements VaultConnector {
throw new AuthorizationRequiredException();
/* Request HTTP response and parse Secret */
try {
String response = requestGet(PATH_AUTH_APPROLE + "role/" + roleName, new HashMap<>());
String response = requestGet(String.format(PATH_AUTH_APPROLE_ROLE, roleName, ""), new HashMap<>());
return jsonMapper.readValue(response, AppRoleResponse.class);
} catch (IOException e) {
throw new InvalidResponseException("Unable to parse response", e);
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
} catch (URISyntaxException ignored) {
/* this should never occur and may leak sensible information */
throw new InvalidRequestException("Invalid URI format.");
throw new InvalidRequestException(Error.URI_FORMAT);
}
}
@ -403,11 +424,11 @@ public class HTTPVaultConnector implements VaultConnector {
throw new AuthorizationRequiredException();
/* Request HTTP response and expect empty result */
String response = requestDelete(PATH_AUTH_APPROLE + "role/" + roleName);
String response = requestDelete(String.format(PATH_AUTH_APPROLE_ROLE, roleName, ""));
/* Response should be code 204 without content */
if (!response.equals(""))
throw new InvalidResponseException("Received response where non was expected.");
if (!response.isEmpty())
throw new InvalidResponseException(Error.UNEXPECTED_RESPONSE);
return true;
}
@ -418,13 +439,13 @@ public class HTTPVaultConnector implements VaultConnector {
throw new AuthorizationRequiredException();
/* Request HTTP response and parse Secret */
try {
String response = requestGet(PATH_AUTH_APPROLE + "role/" + roleName + "/role-id", new HashMap<>());
String response = requestGet(String.format(PATH_AUTH_APPROLE_ROLE, roleName, "/role-id"), new HashMap<>());
return jsonMapper.readValue(response, RawDataResponse.class).getData().get("role_id").toString();
} catch (IOException e) {
throw new InvalidResponseException("Unable to parse response", e);
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
} catch (URISyntaxException ignored) {
/* this should never occur and may leak sensible information */
throw new InvalidRequestException("Invalid URI format.");
throw new InvalidRequestException(Error.URI_FORMAT);
}
}
@ -435,10 +456,10 @@ public class HTTPVaultConnector implements VaultConnector {
/* Request HTTP response and parse Secret */
Map<String, String> payload = new HashMap<>();
payload.put("role_id", roleID);
String response = requestPost(PATH_AUTH_APPROLE + "role/" + roleName + "/role-id", payload);
String response = requestPost(String.format(PATH_AUTH_APPROLE_ROLE, roleName, "/role-id"), payload);
/* Response should be code 204 without content */
if (!response.equals(""))
throw new InvalidResponseException("Received response where non was expected.");
if (!response.isEmpty())
throw new InvalidResponseException(Error.UNEXPECTED_RESPONSE);
return true;
}
@ -450,15 +471,15 @@ public class HTTPVaultConnector implements VaultConnector {
/* Get response */
String response;
if (secret.getId() != null && !secret.getId().isEmpty())
response = requestPost(PATH_AUTH_APPROLE + "role/" + roleName + "/custom-secret-id", secret);
response = requestPost(String.format(PATH_AUTH_APPROLE_ROLE, roleName, "/custom-secret-id"), secret);
else
response = requestPost(PATH_AUTH_APPROLE + "role/" + roleName + "/secret-id", secret);
response = requestPost(String.format(PATH_AUTH_APPROLE_ROLE, roleName, "/secret-id"), secret);
try {
/* Extract the secret ID from response */
return jsonMapper.readValue(response, AppRoleSecretResponse.class);
} catch (IOException e) {
throw new InvalidResponseException("Unable to parse response.");
throw new InvalidResponseException(Error.PARSE_RESPONSE);
}
}
@ -470,11 +491,11 @@ public class HTTPVaultConnector implements VaultConnector {
/* Request HTTP response and parse Secret */
try {
String response = requestPost(
PATH_AUTH_APPROLE + "role/" + roleName + "/secret-id/lookup",
String.format(PATH_AUTH_APPROLE_ROLE, roleName, "/secret-id/lookup"),
new AppRoleSecret(secretID));
return jsonMapper.readValue(response, AppRoleSecretResponse.class);
} catch (IOException e) {
throw new InvalidResponseException("Unable to parse response", e);
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
}
}
@ -486,12 +507,12 @@ public class HTTPVaultConnector implements VaultConnector {
/* Request HTTP response and expect empty result */
String response = requestPost(
PATH_AUTH_APPROLE + "role/" + roleName + "/secret-id/destroy",
String.format(PATH_AUTH_APPROLE_ROLE, roleName, "/secret-id/destroy"),
new AppRoleSecret(secretID));
/* Response should be code 204 without content */
if (!response.equals(""))
throw new InvalidResponseException("Received response where non was expected.");
if (!response.isEmpty())
throw new InvalidResponseException(Error.UNEXPECTED_RESPONSE);
return true;
}
@ -506,10 +527,10 @@ public class HTTPVaultConnector implements VaultConnector {
SecretListResponse secrets = jsonMapper.readValue(response, SecretListResponse.class);
return secrets.getKeys();
} catch (IOException e) {
throw new InvalidResponseException("Unable to parse response", e);
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
} catch (URISyntaxException ignored) {
/* this should never occur and may leak sensible information */
throw new InvalidRequestException("Invalid URI format.");
throw new InvalidRequestException(Error.URI_FORMAT);
}
}
@ -520,15 +541,15 @@ public class HTTPVaultConnector implements VaultConnector {
try {
String response = requestGet(
PATH_AUTH_APPROLE + "role/" + roleName + "/secret-id?list=true",
String.format(PATH_AUTH_APPROLE_ROLE, roleName, "/secret-id?list=true"),
new HashMap<>());
SecretListResponse secrets = jsonMapper.readValue(response, SecretListResponse.class);
return secrets.getKeys();
} catch (IOException e) {
throw new InvalidResponseException("Unable to parse response", e);
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
} catch (URISyntaxException ignored) {
/* this should never occur and may leak sensible information */
throw new InvalidRequestException("Invalid URI format.");
throw new InvalidRequestException(Error.URI_FORMAT);
}
}
@ -541,10 +562,10 @@ public class HTTPVaultConnector implements VaultConnector {
String response = requestGet(key, new HashMap<>());
return jsonMapper.readValue(response, SecretResponse.class);
} catch (IOException e) {
throw new InvalidResponseException("Unable to parse response", e);
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
} catch (URISyntaxException ignored) {
/* this should never occur and may leak sensible information */
throw new InvalidRequestException("Invalid URI format.");
throw new InvalidRequestException(Error.URI_FORMAT);
}
}
@ -558,10 +579,10 @@ public class HTTPVaultConnector implements VaultConnector {
SecretListResponse secrets = jsonMapper.readValue(response, SecretListResponse.class);
return secrets.getKeys();
} catch (IOException e) {
throw new InvalidResponseException("Unable to parse response", e);
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
} catch (URISyntaxException ignored) {
/* this should never occur and may leak sensible information */
throw new InvalidRequestException("Invalid URI format.");
throw new InvalidRequestException(Error.URI_FORMAT);
}
}
@ -573,8 +594,8 @@ public class HTTPVaultConnector implements VaultConnector {
if (key == null || key.isEmpty())
throw new InvalidRequestException("Secret path must not be empty.");
if (!requestPost(key, data).equals(""))
throw new InvalidResponseException("Received response where none was expected.");
if (!requestPost(key, data).isEmpty())
throw new InvalidResponseException(Error.UNEXPECTED_RESPONSE);
}
@Override
@ -586,8 +607,8 @@ public class HTTPVaultConnector implements VaultConnector {
String response = requestDelete(key);
/* Response should be code 204 without content */
if (!response.equals(""))
throw new InvalidResponseException("Received response where none was expected.");
if (!response.isEmpty())
throw new InvalidResponseException(Error.UNEXPECTED_RESPONSE);
}
@Override
@ -599,8 +620,8 @@ public class HTTPVaultConnector implements VaultConnector {
String response = requestPut(PATH_REVOKE + leaseID, new HashMap<>());
/* Response should be code 204 without content */
if (!response.equals(""))
throw new InvalidResponseException("Received response where none was expected.");
if (!response.isEmpty())
throw new InvalidResponseException(Error.UNEXPECTED_RESPONSE);
}
@Override
@ -618,7 +639,7 @@ public class HTTPVaultConnector implements VaultConnector {
String response = requestPut(PATH_RENEW, payload);
return jsonMapper.readValue(response, SecretResponse.class);
} catch (IOException e) {
throw new InvalidResponseException("Unable to parse response", e);
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
}
}
@ -666,7 +687,7 @@ public class HTTPVaultConnector implements VaultConnector {
try {
return jsonMapper.readValue(response, AuthResponse.class);
} catch (IOException e) {
throw new InvalidResponseException("Unable to parse response", e);
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
}
}
@ -679,10 +700,10 @@ public class HTTPVaultConnector implements VaultConnector {
String response = requestGet(PATH_TOKEN + "/lookup/" + token, new HashMap<>());
return jsonMapper.readValue(response, TokenResponse.class);
} catch (IOException e) {
throw new InvalidResponseException("Unable to parse response", e);
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
} catch (URISyntaxException ignored) {
/* this should never occur and may leak sensible information */
throw new InvalidRequestException("Invalid URI format.");
throw new InvalidRequestException(Error.URI_FORMAT);
}
}
@ -704,7 +725,7 @@ public class HTTPVaultConnector implements VaultConnector {
try {
input = new StringEntity(jsonMapper.writeValueAsString(payload), StandardCharsets.UTF_8);
} catch (JsonProcessingException e) {
throw new InvalidRequestException("Unable to parse response", e);
throw new InvalidRequestException(Error.PARSE_RESPONSE, e);
}
input.setContentEncoding("UTF-8");
input.setContentType("application/json");
@ -810,12 +831,7 @@ public class HTTPVaultConnector implements VaultConnector {
switch (response.getStatusLine().getStatusCode()) {
case 200:
try (BufferedReader br = new BufferedReader(
new InputStreamReader(response.getEntity().getContent()))) {
return br.lines().collect(Collectors.joining("\n"));
} catch (IOException ignored) {
throw new InvalidResponseException("Could not parse response body", 200);
}
return handleResult(response);
case 204:
return "";
case 403:
@ -827,26 +843,15 @@ public class HTTPVaultConnector implements VaultConnector {
return request(base, retries - 1);
} else {
/* Fail on different error code and/or no retries left */
if (response.getEntity() != null) {
try (BufferedReader br = new BufferedReader(
new InputStreamReader(response.getEntity().getContent()))) {
String responseString = br.lines().collect(Collectors.joining("\n"));
ErrorResponse er = jsonMapper.readValue(responseString, ErrorResponse.class);
/* Check for "permission denied" response */
if (!er.getErrors().isEmpty() && er.getErrors().get(0).equals("permission denied"))
throw new PermissionDeniedException();
throw new InvalidResponseException("Invalid response code",
response.getStatusLine().getStatusCode(), er.toString());
} catch (IOException ignored) {
// Exception ignored.
}
}
throw new InvalidResponseException("Invalid response code",
handleError(response);
/* Throw exception withoud details, if response entity is empty. */
throw new InvalidResponseException(Error.RESPONSE_CODE,
response.getStatusLine().getStatusCode());
}
}
} catch (IOException e) {
throw new InvalidResponseException("Unable to read response", e);
throw new InvalidResponseException(Error.READ_RESPONSE, e);
} finally {
if (response != null && response.getEntity() != null)
try {
@ -856,4 +861,60 @@ public class HTTPVaultConnector implements VaultConnector {
}
}
}
/**
* Handle successful result.
*
* @param response The raw HTTP response (assuming status code 200)
* @return Complete response body as String
* @throws InvalidResponseException on reading errors
*/
private String handleResult(final HttpResponse response) throws InvalidResponseException {
try (BufferedReader br = new BufferedReader(
new InputStreamReader(response.getEntity().getContent()))) {
return br.lines().collect(Collectors.joining("\n"));
} catch (IOException ignored) {
throw new InvalidResponseException(Error.READ_RESPONSE, 200);
}
}
/**
* Handle unsuccessful response. Throw detailed exception if possible.
*
* @param response The raw HTTP response (assuming status code 5xx)
* @throws VaultConnectorException Expected exception with details to throw
*/
private void handleError(final HttpResponse response) throws VaultConnectorException {
if (response.getEntity() != null) {
try (BufferedReader br = new BufferedReader(
new InputStreamReader(response.getEntity().getContent()))) {
String responseString = br.lines().collect(Collectors.joining("\n"));
ErrorResponse er = jsonMapper.readValue(responseString, ErrorResponse.class);
/* Check for "permission denied" response */
if (!er.getErrors().isEmpty() && er.getErrors().get(0).equals("permission denied"))
throw new PermissionDeniedException();
throw new InvalidResponseException(Error.RESPONSE_CODE,
response.getStatusLine().getStatusCode(), er.toString());
} catch (IOException ignored) {
// Exception ignored.
}
}
}
/**
* Inner class to bundle common error messages.
*/
private static final class Error {
private static final String READ_RESPONSE = "Unable to read response";
private static final String PARSE_RESPONSE = "Unable to parse response";
private static final String UNEXPECTED_RESPONSE = "Received response where none was expected";
private static final String URI_FORMAT = "Invalid URI format";
private static final String RESPONSE_CODE = "Invalid response code";
/**
* Constructor hidden, this class should not be instantiated.
*/
private Error() {
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2018 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -21,7 +21,10 @@ import de.stklcode.jvault.connector.exception.VaultConnectorException;
import de.stklcode.jvault.connector.model.*;
import de.stklcode.jvault.connector.model.response.*;
import java.util.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Vault Connector interface.
@ -31,6 +34,9 @@ import java.util.*;
* @since 0.1
*/
public interface VaultConnector extends AutoCloseable {
/**
* Default sub-path for Vault secrets.
*/
String PATH_SECRET = "secret";
/**
@ -42,35 +48,47 @@ public interface VaultConnector extends AutoCloseable {
* Retrieve status of vault seal.
*
* @return Seal status
* @throws VaultConnectorException on error
*/
SealResponse sealStatus();
SealResponse sealStatus() throws VaultConnectorException;
/**
* Seal vault.
*
* @return TRUE on success
* @throws VaultConnectorException on error
*/
boolean seal();
void seal() throws VaultConnectorException;
/**
* Unseal vault.
*
* @param key A single master share key
* @param reset Discard previously provided keys (optional)
* @return TRUE on success
* @return Response with seal status
* @throws VaultConnectorException on error
*/
SealResponse unseal(final String key, final Boolean reset);
SealResponse unseal(final String key, final Boolean reset) throws VaultConnectorException;
/**
* Unseal vault.
*
* @param key A single master share key
* @return TRUE on success
* @return Response with seal status
* @throws VaultConnectorException on error
*/
default SealResponse unseal(final String key) {
default SealResponse unseal(final String key) throws VaultConnectorException {
return unseal(key, null);
}
/**
* Query server health information.
*
* @return Health information.
* @throws VaultConnectorException on error
* @since 0.7.0
*/
HealthResponse getHealth() throws VaultConnectorException;
/**
* Get all availale authentication backends.
*
@ -103,7 +121,7 @@ public interface VaultConnector extends AutoCloseable {
*
* @param appID The App ID
* @param userID The User ID
* @return TRUE on success
* @return The {@link AuthResponse}
* @throws VaultConnectorException on error
* @deprecated As of Vault 0.6.1 App-ID is superseded by AppRole. Consider using {@link #authAppRole} instead.
*/
@ -114,7 +132,7 @@ public interface VaultConnector extends AutoCloseable {
* Authorize to Vault using AppRole method without secret ID.
*
* @param roleID The role ID
* @return TRUE on success
* @return The {@link AuthResponse}
* @throws VaultConnectorException on error
* @since 0.4.0
*/
@ -127,7 +145,7 @@ public interface VaultConnector extends AutoCloseable {
*
* @param roleID The role ID
* @param secretID The secret ID
* @return TRUE on success
* @return The {@link AuthResponse}
* @throws VaultConnectorException on error
* @since 0.4.0
*/
@ -139,7 +157,7 @@ public interface VaultConnector extends AutoCloseable {
* @param appID The unique App-ID
* @param policy The policy to associate with
* @param displayName Arbitrary name to display
* @return TRUE on success
* @return {@code true} on success
* @throws VaultConnectorException on error
* @deprecated As of Vault 0.6.1 App-ID is superseded by AppRole. Consider using {@link #createAppRole} instead.
*/
@ -151,7 +169,7 @@ public interface VaultConnector extends AutoCloseable {
* Register a new AppRole role from given metamodel.
*
* @param role The role
* @return TRUE on success
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.4.0
*/
@ -161,7 +179,7 @@ public interface VaultConnector extends AutoCloseable {
* Register new AppRole role with default policy.
*
* @param roleName The role name
* @return TRUE on success
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.4.0
*/
@ -174,7 +192,7 @@ public interface VaultConnector extends AutoCloseable {
*
* @param roleName The role name
* @param policies The policies to associate with
* @return TRUE on success
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.4.0
*/
@ -187,7 +205,7 @@ public interface VaultConnector extends AutoCloseable {
*
* @param roleName The role name
* @param roleID A custom role ID
* @return TRUE on success
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.4.0
*/
@ -201,7 +219,7 @@ public interface VaultConnector extends AutoCloseable {
* @param roleName The role name
* @param policies The policies to associate with
* @param roleID A custom role ID
* @return TRUE on success
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.4.0
*/
@ -214,7 +232,7 @@ public interface VaultConnector extends AutoCloseable {
* Delete AppRole role from Vault.
*
* @param roleName The role anme
* @return TRUE on succevss
* @return {@code true} on succevss
* @throws VaultConnectorException on error
*/
boolean deleteAppRole(final String roleName) throws VaultConnectorException;
@ -244,7 +262,7 @@ public interface VaultConnector extends AutoCloseable {
*
* @param roleName The role name
* @param roleID The role ID
* @return TRUE on success
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.4.0
*/
@ -319,19 +337,6 @@ public interface VaultConnector extends AutoCloseable {
*/
List<String> listAppRoles() throws VaultConnectorException;
/**
* List existing (accessible) secret IDs for AppRole role.
*
* @param roleName The role name
* @return List of roles
* @throws VaultConnectorException on error
* @deprecated Use {@link #listAppRoleSecrets(String)}}. Will be removed in 0.7.0!
*/
@Deprecated
default List<String> listAppRoleSecretss(final String roleName) throws VaultConnectorException {
return listAppRoleSecrets(roleName);
}
/**
* List existing (accessible) secret IDs for AppRole role.
*
@ -346,7 +351,7 @@ public interface VaultConnector extends AutoCloseable {
*
* @param appID The App-ID
* @param userID The User-ID
* @return TRUE on success
* @return {@code true} on success
* @throws VaultConnectorException on error
* @deprecated As of Vault 0.6.1 App-ID is superseded by AppRole.
* Consider using {@link #createAppRoleSecret} instead.
@ -361,7 +366,7 @@ public interface VaultConnector extends AutoCloseable {
* @param policy The policy to associate with
* @param displayName Arbitrary name to display
* @param userID The User-ID
* @return TRUE on success
* @return {@code true} on success
* @throws VaultConnectorException on error
* @deprecated As of Vault 0.6.1 App-ID is superseded by AppRole.
*/
@ -472,8 +477,9 @@ public interface VaultConnector extends AutoCloseable {
* @since 0.5.0
*/
default void writeSecret(final String key, final Map<String, Object> data) throws VaultConnectorException {
if (key == null || key.isEmpty())
if (key == null || key.isEmpty()) {
throw new InvalidRequestException("Secret path must not be empty.");
}
write(PATH_SECRET + "/" + key, data);
}

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2018 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -38,7 +38,7 @@ public final class InvalidResponseException extends VaultConnectorException {
/**
* Constructs a new exception with the specified detail message.
*
* @param message the detail message
* @param message The detail message
*/
public InvalidResponseException(final String message) {
super(message);
@ -49,7 +49,7 @@ public final class InvalidResponseException extends VaultConnectorException {
/**
* Constructs a new exception with the specified cause.
*
* @param cause the cause
* @param cause The cause
*/
public InvalidResponseException(final Throwable cause) {
super(cause);
@ -60,8 +60,8 @@ public final class InvalidResponseException extends VaultConnectorException {
/**
* Constructs a new exception with the specified detail message and cause.
*
* @param message the detail message
* @param cause the cause
* @param message The detail message
* @param cause The cause
*/
public InvalidResponseException(final String message, final Throwable cause) {
super(message, cause);
@ -74,8 +74,8 @@ public final class InvalidResponseException extends VaultConnectorException {
* <p>
* The HTTP status code can be retrieved by {@link #getStatusCode()} later.
*
* @param message the detail message
* @param statusCode status code of the HTTP response
* @param message The detail message
* @param statusCode Status code of the HTTP response
* @since 0.6.2
*/
public InvalidResponseException(final String message, final Integer statusCode) {
@ -89,9 +89,9 @@ public final class InvalidResponseException extends VaultConnectorException {
* <p>
* The HTTP status code can be retrieved by {@link #getStatusCode()} later.
*
* @param message the detail message
* @param statusCode status code of the HTTP response
* @param cause the cause
* @param message The detail message
* @param statusCode Status code of the HTTP response
* @param cause The cause
* @since 0.6.2
*/
public InvalidResponseException(final String message, final Integer statusCode, final Throwable cause) {
@ -103,8 +103,8 @@ public final class InvalidResponseException extends VaultConnectorException {
* <p>
* The HTTP status code can be retrieved by {@link #getStatusCode()} later.
*
* @param message the detail message
* @param statusCode status code of the HTTP response
* @param message The detail message
* @param statusCode Status code of the HTTP response
* @param response HTTP response string
* @since 0.6.2
*/
@ -121,10 +121,10 @@ public final class InvalidResponseException extends VaultConnectorException {
* <p>
* The HTTP status code can be retrieved by {@link #getStatusCode()} later.
*
* @param message the detail message
* @param statusCode status code of the HTTP response
* @param message The detail message
* @param statusCode Status code of the HTTP response
* @param response HTTP response string
* @param cause the cause
* @param cause The cause
* @since 0.6.2
*/
public InvalidResponseException(final String message,
@ -139,7 +139,7 @@ public final class InvalidResponseException extends VaultConnectorException {
/**
* Specify the HTTP status code. Can be retrieved by {@link #getStatusCode()} later.
*
* @param statusCode the status code
* @param statusCode The status code
* @return self
* @deprecated as of 0.6.2, use constructor with status code argument instead
*/
@ -151,7 +151,7 @@ public final class InvalidResponseException extends VaultConnectorException {
/**
* Specify the response string. Can be retrieved by {@link #getResponse()} later.
*
* @param response response text
* @param response Response text
* @return self
* @deprecated use constructor with response argument instead
*/
@ -163,7 +163,7 @@ public final class InvalidResponseException extends VaultConnectorException {
/**
* Retrieve the HTTP status code.
*
* @return the status code or {@code null} if none specified.
* @return The status code or {@code null} if none specified.
*/
public Integer getStatusCode() {
return statusCode;
@ -172,7 +172,7 @@ public final class InvalidResponseException extends VaultConnectorException {
/**
* Retrieve the response text.
*
* @return the response text or {@code null} if none specified.
* @return The response text or {@code null} if none specified.
*/
public String getResponse() {
return response;

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2018 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -201,6 +201,7 @@ public final class HTTPVaultConnectorFactory extends VaultConnectorFactory {
try {
numberOfRetries = Integer.parseInt(System.getenv(ENV_VAULT_MAX_RETRIES));
} catch (NumberFormatException ignored) {
/* Ignore malformed values. */
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2018 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -38,4 +38,13 @@ public final class ErrorResponse implements VaultResponse {
public List<String> getErrors() {
return errors;
}
@Override
public String toString() {
if (errors == null || errors.isEmpty()) {
return "error response";
} else {
return errors.get(0);
}
}
}

View File

@ -0,0 +1,99 @@
/*
* Copyright 2016-2018 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;
/**
* Vault response for health query.
*
* @author Stefan Kalscheuer
* @since 0.7.0
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class HealthResponse implements VaultResponse {
@JsonProperty("cluster_id")
private String clusterID;
@JsonProperty("cluster_name")
private String clusterName;
@JsonProperty("version")
private String version;
@JsonProperty("server_time_utc")
private Long serverTimeUTC;
@JsonProperty("standby")
private Boolean standby;
@JsonProperty("sealed")
private Boolean sealed;
@JsonProperty("initialized")
private Boolean initialized;
/**
* @return The Cluster ID.
*/
public String getClusterID() {
return clusterID;
}
/**
* @return The Cluster name.
*/
public String getClusterName() {
return clusterName;
}
/**
* @return Vault version.
*/
public String getVersion() {
return version;
}
/**
* @return Server time UTC (timestamp).
*/
public Long getServerTimeUTC() {
return serverTimeUTC;
}
/**
* @return Server standby status.
*/
public Boolean isStandby() {
return standby;
}
/**
* @return Server seal status.
*/
public Boolean isSealed() {
return sealed;
}
/**
* @return Server initialization status.
*/
public Boolean isInitialized() {
return initialized;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,483 @@
/*
* Copyright 2016-2018 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;
import de.stklcode.jvault.connector.exception.InvalidRequestException;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.exception.PermissionDeniedException;
import de.stklcode.jvault.connector.exception.VaultConnectorException;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicStatusLine;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.lang.reflect.Field;
import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import static net.bytebuddy.implementation.MethodDelegation.to;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
/**
* JUnit test for HTTP Vault connector.
* This test suite contains tests that do not require connection to an actual Vault instance.
*
* @author Stefan Kalscheuer
* @since 0.7.0
*/
public class HTTPVaultConnectorOfflineTest {
private static final String INVALID_URL = "foo:/\\1nv4l1d_UrL";
private static HttpClientBuilder httpMockBuilder = mock(HttpClientBuilder.class);
private static CloseableHttpClient httpMock = mock(CloseableHttpClient.class);
private CloseableHttpResponse responseMock = mock(CloseableHttpResponse.class);
@BeforeAll
public static void initByteBuddy() {
// Install ByteBuddy Agent.
ByteBuddyAgent.install();
}
/**
* Helper method for redefinition of {@link HttpClientBuilder#create()} from {@link #initHttpMock()}.
*
* @return Mocked HTTP client builder.
*/
public static HttpClientBuilder create() {
return httpMockBuilder;
}
@BeforeEach
public void initHttpMock() {
// Redefine static method to return Mock on HttpClientBuilder creation.
new ByteBuddy().redefine(HttpClientBuilder.class)
.method(named("create"))
.intercept(to(HTTPVaultConnectorOfflineTest.class))
.make()
.load(HttpClientBuilder.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent());
// Ignore SSL context settings.
when(httpMockBuilder.setSSLContext(null)).thenReturn(httpMockBuilder);
// Re-initialize HTTP mock to ensure fresh (empty) results.
httpMock = mock(CloseableHttpClient.class);
// Mock actual client creation.
when(httpMockBuilder.build()).thenReturn(httpMock);
}
/**
* Test exceptions thrown during request.
*/
@Test
public void requestExceptionTest() throws IOException {
HTTPVaultConnector connector = new HTTPVaultConnector("http://127.0.0.1", null, 0, 250);
// Test invalid response code.
final int responseCode = 400;
mockResponse(responseCode, "", ContentType.APPLICATION_JSON);
try {
connector.getHealth();
fail("Querying health status succeeded on invalid instance");
} catch (Exception e) {
assertThat("Unexpected type of exception", e, instanceOf(InvalidResponseException.class));
assertThat("Unexpected exception message", e.getMessage(), is("Invalid response code"));
assertThat("Unexpected status code in exception", ((InvalidResponseException) e).getStatusCode(), is(responseCode));
assertThat("Response message where none was expected", ((InvalidResponseException) e).getResponse(), is(nullValue()));
}
// Simulate permission denied response.
mockResponse(responseCode, "{\"errors\":[\"permission denied\"]}", ContentType.APPLICATION_JSON);
try {
connector.getHealth();
fail("Querying health status succeeded on invalid instance");
} catch (Exception e) {
assertThat("Unexpected type of exception", e, instanceOf(PermissionDeniedException.class));
}
// Test exception thrown during request.
when(httpMock.execute(any())).thenThrow(new IOException("Test Exception"));
try {
connector.getHealth();
fail("Querying health status succeeded on invalid instance");
} catch (Exception e) {
assertThat("Unexpected type of exception", e, instanceOf(InvalidResponseException.class));
assertThat("Unexpected exception message", e.getMessage(), is("Unable to read response"));
assertThat("Unexpected cause", e.getCause(), instanceOf(IOException.class));
}
// Now simulate a failing request that succeeds on second try.
connector = new HTTPVaultConnector("https://127.0.0.1", null, 1, 250);
doReturn(responseMock).doReturn(responseMock).when(httpMock).execute(any());
doReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 500, ""))
.doReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 500, ""))
.doReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 500, ""))
.doReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, ""))
.when(responseMock).getStatusLine();
when(responseMock.getEntity()).thenReturn(new StringEntity("{}", ContentType.APPLICATION_JSON));
try {
connector.getHealth();
} catch (Exception e) {
fail("Request failed unexpectedly: " + e.getMessage());
}
}
/**
* Test constductors of the {@link HTTPVaultConnector} class.
*/
@Test
public void constructorTest() throws NoSuchAlgorithmException {
final String url = "https://vault.example.net/test/";
final String hostname = "vault.example.com";
final Integer port = 1337;
final String prefix = "/custom/prefix/";
final Integer retries = 42;
final String expectedNoTls = "http://" + hostname + "/v1/";
final String expectedCustomPort = "https://" + hostname + ":" + port + "/v1/";
final String expectedCustomPrefix = "https://" + hostname + ":" + port + prefix;
final SSLContext sslContext = SSLContext.getInstance("TLS");
// Most basic constructor expects complete URL.
HTTPVaultConnector connector = new HTTPVaultConnector(url);
assertThat("Unexpected base URL", getPrivate(connector, "baseURL"), is(url));
// Now override TLS usage.
connector = new HTTPVaultConnector(hostname, false);
assertThat("Unexpected base URL with TLS disabled", getPrivate(connector, "baseURL"), is(expectedNoTls));
// Specify custom port.
connector = new HTTPVaultConnector(hostname, true, port);
assertThat("Unexpected base URL with custom port", getPrivate(connector, "baseURL"), is(expectedCustomPort));
// Specify custom prefix.
connector = new HTTPVaultConnector(hostname, true, port, prefix);
assertThat("Unexpected base URL with custom prefix", getPrivate(connector, "baseURL"), is(expectedCustomPrefix));
assertThat("SSL context set, but not specified", getPrivate(connector, "sslContext"), is(nullValue()));
// Provide custom SSL context.
connector = new HTTPVaultConnector(hostname, true, port, prefix, sslContext);
assertThat("Unexpected base URL with custom prefix", getPrivate(connector, "baseURL"), is(expectedCustomPrefix));
assertThat("SSL context not filled correctly", getPrivate(connector, "sslContext"), is(sslContext));
// Specify number of retries.
connector = new HTTPVaultConnector(url, sslContext, retries);
assertThat("Number of retries not set correctly", getPrivate(connector, "retries"), is(retries));
}
/**
* This test is designed to test exceptions caught and thrown by seal-methods if Vault is not reachable.
*/
@Test
public void sealExceptionTest() throws IOException {
HTTPVaultConnector connector = new HTTPVaultConnector(INVALID_URL);
try {
connector.sealStatus();
fail("Querying seal status succeeded on invalid URL");
} catch (Exception e) {
assertThat("Unexpected type of exception", e, instanceOf(InvalidRequestException.class));
assertThat("Unexpected exception message", e.getMessage(), is("Invalid URI format"));
}
connector = new HTTPVaultConnector("https://127.0.0.1", null, 0, 250);
// Simulate NULL response (mock not supplied with data).
try {
connector.sealStatus();
fail("Querying seal status succeeded on invalid instance");
} catch (Exception e) {
assertThat("Unexpected type of exception", e, instanceOf(InvalidResponseException.class));
assertThat("Unexpected exception message", e.getMessage(), is("Response unavailable"));
}
}
/**
* This test is designed to test exceptions caught and thrown by seal-methods if Vault is not reachable.
*/
@Test
public void healthExceptionTest() throws IOException {
HTTPVaultConnector connector = new HTTPVaultConnector(INVALID_URL);
try {
connector.getHealth();
fail("Querying health status succeeded on invalid URL");
} catch (Exception e) {
assertThat("Unexpected type of exception", e, instanceOf(InvalidRequestException.class));
assertThat("Unexpected exception message", e.getMessage(), is("Invalid URI format"));
}
connector = new HTTPVaultConnector("https://127.0.0.1", null, 0, 250);
// Simulate NULL response (mock not supplied with data).
try {
connector.getHealth();
fail("Querying health status succeeded on invalid instance");
} catch (Exception e) {
assertThat("Unexpected type of exception", e, instanceOf(InvalidResponseException.class));
assertThat("Unexpected exception message", e.getMessage(), is("Response unavailable"));
}
}
/**
* Test behavior on unparsable responses.
*/
@Test
public void parseExceptionTest() throws IOException {
HTTPVaultConnector connector = new HTTPVaultConnector("https://127.0.0.1", null, 0, 250);
// Mock authorization.
setPrivate(connector, "authorized", true);
// Mock response.
mockResponse(200, "invalid", ContentType.APPLICATION_JSON);
// Now test the methods.
try {
connector.sealStatus();
fail("sealStatus() succeeded on invalid instance");
} catch (Exception e) {
assertParseError(e);
}
try {
connector.unseal("key");
fail("unseal() succeeded on invalid instance");
} catch (Exception e) {
assertParseError(e);
}
try {
connector.getHealth();
fail("getHealth() succeeded on invalid instance");
} catch (Exception e) {
assertParseError(e);
}
try {
connector.getAuthBackends();
fail("getAuthBackends() succeeded on invalid instance");
} catch (Exception e) {
assertParseError(e);
}
try {
connector.authToken("token");
fail("authToken() succeeded on invalid instance");
} catch (Exception e) {
assertParseError(e);
}
try {
connector.lookupAppRole("roleName");
fail("lookupAppRole() succeeded on invalid instance");
} catch (Exception e) {
assertParseError(e);
}
try {
connector.getAppRoleID("roleName");
fail("getAppRoleID() succeeded on invalid instance");
} catch (Exception e) {
assertParseError(e);
}
try {
connector.createAppRoleSecret("roleName");
fail("createAppRoleSecret() succeeded on invalid instance");
} catch (Exception e) {
assertParseError(e);
}
try {
connector.lookupAppRoleSecret("roleName", "secretID");
fail("lookupAppRoleSecret() succeeded on invalid instance");
} catch (Exception e) {
assertParseError(e);
}
try {
connector.listAppRoles();
fail("listAppRoles() succeeded on invalid instance");
} catch (Exception e) {
assertParseError(e);
}
try {
connector.listAppRoleSecrets("roleName");
fail("listAppRoleSecrets() succeeded on invalid instance");
} catch (Exception e) {
assertParseError(e);
}
try {
connector.read("key");
fail("read() succeeded on invalid instance");
} catch (Exception e) {
assertParseError(e);
}
try {
connector.list("path");
fail("list() succeeded on invalid instance");
} catch (Exception e) {
assertParseError(e);
}
try {
connector.renew("leaseID");
fail("renew() succeeded on invalid instance");
} catch (Exception e) {
assertParseError(e);
}
try {
connector.lookupToken("token");
fail("lookupToken() succeeded on invalid instance");
} catch (Exception e) {
assertParseError(e);
}
}
private void assertParseError(Exception e) {
assertThat("Unexpected type of exception", e, instanceOf(InvalidResponseException.class));
assertThat("Unexpected exception message", e.getMessage(), is("Unable to parse response"));
}
/**
* Test requests that expect an empty response with code 204, but receive a 200 body.
*/
@Test
public void nonEmpty204ResponseTest() throws IOException {
HTTPVaultConnector connector = new HTTPVaultConnector("https://127.0.0.1", null, 0, 250);
// Mock authorization.
setPrivate(connector, "authorized", true);
// Mock response.
mockResponse(200, "{}", ContentType.APPLICATION_JSON);
// Now test the methods expecting a 204.
try {
connector.registerAppId("appID", "policy", "displayName");
fail("registerAppId() with 200 response succeeded");
} catch (VaultConnectorException e) {
assertThat("Unexpected exception type", e, instanceOf(InvalidResponseException.class));
}
try {
connector.registerUserId("appID", "userID");
fail("registerUserId() with 200 response succeeded");
} catch (VaultConnectorException e) {
assertThat("Unexpected exception type", e, instanceOf(InvalidResponseException.class));
}
try {
connector.createAppRole("appID", Collections.singletonList("policy"));
fail("createAppRole() with 200 response succeeded");
} catch (VaultConnectorException e) {
assertThat("Unexpected exception type", e, instanceOf(InvalidResponseException.class));
}
try {
connector.deleteAppRole("roleName");
fail("deleteAppRole() with 200 response succeeded");
} catch (VaultConnectorException e) {
assertThat("Unexpected exception type", e, instanceOf(InvalidResponseException.class));
}
try {
connector.setAppRoleID("roleName", "roleID");
fail("setAppRoleID() with 200 response succeeded");
} catch (VaultConnectorException e) {
assertThat("Unexpected exception type", e, instanceOf(InvalidResponseException.class));
}
try {
connector.destroyAppRoleSecret("roleName", "secretID");
fail("destroyAppRoleSecret() with 200 response succeeded");
} catch (VaultConnectorException e) {
assertThat("Unexpected exception type", e, instanceOf(InvalidResponseException.class));
}
try {
connector.destroyAppRoleSecret("roleName", "secretUD");
fail("destroyAppRoleSecret() with 200 response succeeded");
} catch (VaultConnectorException e) {
assertThat("Unexpected exception type", e, instanceOf(InvalidResponseException.class));
}
try {
connector.delete("key");
fail("delete() with 200 response succeeded");
} catch (VaultConnectorException e) {
assertThat("Unexpected exception type", e, instanceOf(InvalidResponseException.class));
}
try {
connector.revoke("leaseID");
fail("destroyAppRoleSecret() with 200 response succeeded");
} catch (VaultConnectorException e) {
assertThat("Unexpected exception type", e, instanceOf(InvalidResponseException.class));
}
}
private Object getPrivate(Object target, String fieldName) {
try {
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;
} catch (NoSuchFieldException | IllegalAccessException e) {
return null;
}
}
private void setPrivate(Object target, String fieldName, Object value) {
try {
Field field = target.getClass().getDeclaredField(fieldName);
boolean accessible =field.isAccessible();
field.setAccessible(true);
field.set(target, value);
field.setAccessible(accessible);
} catch (NoSuchFieldException | IllegalAccessException e) {
// Should not occur, to be taken care of in test code.
}
}
private void mockResponse(int status, String body, ContentType type) throws IOException {
when(httpMock.execute(any())).thenReturn(responseMock);
when(responseMock.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), status, ""));
when(responseMock.getEntity()).thenReturn(new StringEntity(body, type));
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2018 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,15 +17,16 @@
package de.stklcode.jvault.connector;
import de.stklcode.jvault.connector.exception.*;
import de.stklcode.jvault.connector.factory.HTTPVaultConnectorFactory;
import de.stklcode.jvault.connector.factory.VaultConnectorFactory;
import de.stklcode.jvault.connector.model.*;
import de.stklcode.jvault.connector.model.response.*;
import de.stklcode.jvault.connector.factory.HTTPVaultConnectorFactory;
import de.stklcode.jvault.connector.test.Credentials;
import de.stklcode.jvault.connector.test.VaultConfiguration;
import de.stklcode.jvault.connector.factory.VaultConnectorFactory;
import org.junit.*;
import org.junit.Rule;
import org.junit.jupiter.api.*;
import org.junit.jupiter.migrationsupport.rules.EnableRuleMigrationSupport;
import org.junit.rules.TemporaryFolder;
import org.junit.rules.TestName;
import java.io.*;
import java.lang.reflect.Field;
@ -34,11 +35,17 @@ import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import static org.apache.commons.io.FileUtils.copyDirectory;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.*;
import static org.junit.Assume.*;
import static org.hamcrest.junit.MatcherAssume.assumeThat;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.fail;
import static org.junit.jupiter.api.Assumptions.assumeFalse;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
/**
* JUnit test for HTTP Vault connector.
@ -47,24 +54,26 @@ import static org.junit.Assume.*;
* @author Stefan Kalscheuer
* @since 0.1
*/
@EnableRuleMigrationSupport
public class HTTPVaultConnectorTest {
private static String KEY = "81011a8061e5c028bd0d9503eeba40bd9054b9af0408d080cb24f57405c27a61";
private static String TOKEN_ROOT = "d1bd50e2-587b-6e68-d80b-a9a507625cb7";
private static String USER_VALID = "validUser";
private static String PASS_VALID = "validPass";
private static String APP_ID = "152AEA38-85FB-47A8-9CBD-612D645BFACA";
private static String USER_ID = "5ADF8218-D7FB-4089-9E38-287465DBF37E";
private static String APPROLE_ROLE_NAME = "testrole1"; // role with secret ID
private static String APPROLE_ROLE = "627b6400-90c3-a239-49a9-af65a448ca10";
private static String APPROLE_SECRET = "5e8b0e99-d906-27f5-f043-ccb9bb53b5e8";
private static String APPROLE_SECRET_ACCESSOR = "071e2e9d-742a-fc3c-3fd3-1f4004b0420a";
private static String APPROLE_ROLE2_NAME = "testrole2"; // role with CIDR subnet
private static String APPROLE_ROLE2 = "35b7bf43-9644-588a-e68f-2e8313bb23b7";
private static String SECRET_PATH = "userstore";
private static String SECRET_KEY = "foo";
private static String SECRET_VALUE = "bar";
private static String SECRET_KEY_JSON = "json";
private static String SECRET_KEY_COMPLEX = "complex";
private static final String VAULT_VERISON = "0.9.5"; // the vault version this test is supposed to run against
private static final String KEY = "81011a8061e5c028bd0d9503eeba40bd9054b9af0408d080cb24f57405c27a61";
private static final String TOKEN_ROOT = "d1bd50e2-587b-6e68-d80b-a9a507625cb7";
private static final String USER_VALID = "validUser";
private static final String PASS_VALID = "validPass";
private static final String APP_ID = "152AEA38-85FB-47A8-9CBD-612D645BFACA";
private static final String USER_ID = "5ADF8218-D7FB-4089-9E38-287465DBF37E";
private static final String APPROLE_ROLE_NAME = "testrole1"; // role with secret ID
private static final String APPROLE_ROLE = "627b6400-90c3-a239-49a9-af65a448ca10";
private static final String APPROLE_SECRET = "5e8b0e99-d906-27f5-f043-ccb9bb53b5e8";
private static final String APPROLE_SECRET_ACCESSOR = "071e2e9d-742a-fc3c-3fd3-1f4004b0420a";
private static final String APPROLE_ROLE2_NAME = "testrole2"; // role with CIDR subnet
private static final String APPROLE_ROLE2 = "35b7bf43-9644-588a-e68f-2e8313bb23b7";
private static final String SECRET_PATH = "userstore";
private static final String SECRET_KEY = "foo";
private static final String SECRET_VALUE = "bar";
private static final String SECRET_KEY_JSON = "json";
private static final String SECRET_KEY_COMPLEX = "complex";
private Process vaultProcess;
private VaultConnector connector;
@ -72,22 +81,19 @@ public class HTTPVaultConnectorTest {
@Rule
public TemporaryFolder tmpDir = new TemporaryFolder();
@Rule
public TestName testName = new TestName();
/**
* Initialize Vault instance with generated configuration and provided file backend.
* Requires "vault" binary to be in current user's executable path. Not using MLock, so no extended rights required.
*/
@Before
public void setUp() throws VaultConnectorException {
@BeforeEach
public void setUp(TestInfo testInfo) throws VaultConnectorException, IOException {
/* Determine, if TLS is required */
boolean isTls = testName.getMethodName().equals("tlsConnectionTest");
boolean isTls = testInfo.getTags().contains("tls");
/* Initialize Vault */
VaultConfiguration config = initializeVault(isTls);
try {
Thread.sleep(1000);
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
@ -103,11 +109,11 @@ public class HTTPVaultConnectorTest {
/* Unseal Vault and check result */
SealResponse sealStatus = connector.unseal(KEY);
assumeNotNull(sealStatus);
assumeTrue(sealStatus != null);
assumeFalse(sealStatus.isSealed());
}
@After
@AfterEach
public void tearDown() {
if (vaultProcess != null && vaultProcess.isAlive())
vaultProcess.destroy();
@ -117,22 +123,64 @@ public class HTTPVaultConnectorTest {
* Test sealing and unsealing Vault.
*/
@Test
public void sealTest() {
public void sealTest() throws VaultConnectorException {
SealResponse sealStatus = connector.sealStatus();
assumeFalse(sealStatus.isSealed());
/* Unauthorized sealing should fail */
assertThat("Unauthorized sealing succeeded", connector.seal(), is(false));
try {
connector.seal();
fail("Unauthorized sealing succeeded");
} catch (VaultConnectorException e) {
assertThat("Vault sealed, although sealing failed", sealStatus.isSealed(), is(false));
}
/* Root user should be able to seal */
authRoot();
assumeTrue(connector.isAuthorized());
assertThat("Sealing failed", connector.seal(), is(true));
try {
connector.seal();
sealStatus = connector.sealStatus();
assertThat("Vault not sealed", sealStatus.isSealed(), is(true));
sealStatus = connector.unseal(KEY);
assertThat("Vault not unsealed", sealStatus.isSealed(), is(false));
} catch (VaultConnectorException e) {
fail("Sealing failed");
}
}
/**
* Test health status
*/
@Test
public void healthTest() {
HealthResponse res = null;
try {
res = connector.getHealth();
} catch (VaultConnectorException e) {
fail("Retrieving health status failed: " + e.getMessage());
}
assertThat("Health response should be set", res, is(notNullValue()));
assertThat("Unexpected version", res.getVersion(), is(VAULT_VERISON));
assertThat("Unexpected init status", res.isInitialized(), is(true));
assertThat("Unexpected seal status", res.isSealed(), is(false));
assertThat("Unexpected standby status", res.isStandby(), is(false));
// No seal vault and verify correct status.
authRoot();
try {
connector.seal();
assumeTrue(connector.sealStatus().isSealed());
connector.resetAuth(); // SHould work unauthenticated
} catch (VaultConnectorException e) {
fail("Unexpected exception on sealing: " + e.getMessage());
}
try {
res = connector.getHealth();
} catch (VaultConnectorException e) {
fail("Retrieving health status failed when sealed: " + e.getMessage());
}
assertThat("Unexpected seal status", res.isSealed(), is(true));
}
/**
@ -174,7 +222,7 @@ public class HTTPVaultConnectorTest {
try {
res = connector.authToken(TOKEN_ROOT);
assertNotNull("Login failed with valid token", res);
assertNotNull(res, "Login failed with valid token");
assertThat("Login failed with valid token", connector.isAuthorized(), is(true));
} catch (VaultConnectorException ignored) {
fail("Login failed with valid token");
@ -203,7 +251,7 @@ public class HTTPVaultConnectorTest {
} catch (VaultConnectorException ignored) {
fail("Login failed with valid credentials: Exception thrown");
}
assertNotNull("Login failed with valid credentials: Response not available", res.getAuth());
assertNotNull(res.getAuth(), "Login failed with valid credentials: Response not available");
assertThat("Login failed with valid credentials: Connector not authorized", connector.isAuthorized(), is(true));
}
@ -476,7 +524,8 @@ public class HTTPVaultConnectorTest {
}
try {
AppRoleResponse res = connector.lookupAppRole(roleName);
assertThat("Role lookuo returned wrong policy count", res.getRole().getPolicies(), hasSize(2));
// Note: As of Vault 0.8.3 default policy is not added automatically, so this test should return 1, not 2.
assertThat("Role lookuo returned wrong policy count (before Vault 0.8.3 is should be 2)", res.getRole().getPolicies(), hasSize(1));
assertThat("Role lookuo returned wrong policies", res.getRole().getPolicies(), hasItem("testpolicy"));
} catch (VaultConnectorException e) {
fail("Creation of role by name failed.");
@ -592,6 +641,7 @@ public class HTTPVaultConnectorTest {
* Test reading of secrets.
*/
@Test
@SuppressWarnings("deprecation")
public void readSecretTest() {
authUser();
assumeTrue(connector.isAuthorized());
@ -684,6 +734,7 @@ public class HTTPVaultConnectorTest {
* Test writing secrets.
*/
@Test
@SuppressWarnings("deprecation")
public void writeSecretTest() {
authUser();
assumeTrue(connector.isAuthorized());
@ -855,7 +906,7 @@ public class HTTPVaultConnectorTest {
fail("Overwriting token should fail as of Vault 0.8.0");
} catch (VaultConnectorException e) {
assertThat(e, is(instanceOf(InvalidResponseException.class)));
assertThat(((InvalidResponseException)e).getStatusCode(), is(400));
assertThat(((InvalidResponseException) e).getStatusCode(), is(400));
/* Assert that the exception does not reveal token ID */
assertThat(stackTrace(e), not(stringContainsInOrder(token.getId())));
}
@ -865,6 +916,7 @@ public class HTTPVaultConnectorTest {
* Test TLS connection with custom certificate chain.
*/
@Test
@Tag("tls")
public void tlsConnectionTest() {
TokenResponse res;
try {
@ -875,7 +927,7 @@ public class HTTPVaultConnectorTest {
try {
res = connector.authToken(TOKEN_ROOT);
assertNotNull("Login failed with valid token", res);
assertNotNull(res, "Login failed with valid token");
assertThat("Login failed with valid token", connector.isAuthorized(), is(true));
} catch (VaultConnectorException ignored) {
fail("Login failed with valid token");
@ -910,14 +962,15 @@ public class HTTPVaultConnectorTest {
* @return Vault Configuration
* @throws IllegalStateException on error
*/
private VaultConfiguration initializeVault(boolean tls) throws IllegalStateException {
String dataResource = getClass().getResource("/data_dir").getPath();
private VaultConfiguration initializeVault(boolean tls) throws IllegalStateException, IOException {
File dataDir = tmpDir.newFolder();
copyDirectory(new File(getClass().getResource("/data_dir").getPath()), dataDir);
/* Generate vault local unencrypted configuration */
VaultConfiguration config = new VaultConfiguration()
.withHost("localhost")
.withPort(getFreePort())
.withDataLocation(dataResource)
.withDataLocation(dataDir.toPath())
.disableMlock();
/* Enable TLS with custom certificate and key, if required */
@ -935,8 +988,7 @@ public class HTTPVaultConnectorTest {
bw = new BufferedWriter(new FileWriter(configFile));
bw.write(config.toString());
} catch (IOException e) {
e.printStackTrace();
throw new IllegalStateException("Unable to generate config file.");
throw new IllegalStateException("Unable to generate config file.", e);
} finally {
try {
if (bw != null)
@ -950,8 +1002,7 @@ public class HTTPVaultConnectorTest {
try {
vaultProcess = Runtime.getRuntime().exec("vault server -config " + configFile.toString());
} catch (IOException e) {
e.printStackTrace();
throw new IllegalStateException("Unable to start vault. Make sure vault binary is in your executable path.");
throw new IllegalStateException("Unable to start vault. Make sure vault binary is in your executable path.", e);
}
return config;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2018 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,12 +16,12 @@
package de.stklcode.jvault.connector.exception;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
/**
* Common JUnit test for Exceptions extending {@link VaultConnectorException}.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2018 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,10 +19,10 @@ 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.jupiter.api.Test;
import org.junit.jupiter.migrationsupport.rules.EnableRuleMigrationSupport;
import org.junit.rules.TemporaryFolder;
import java.io.IOException;
@ -30,7 +30,8 @@ import java.lang.reflect.Field;
import java.nio.file.NoSuchFileException;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.fail;
/**
* JUnit test for HTTP Vault connector factory
@ -38,6 +39,7 @@ import static org.junit.Assert.*;
* @author Stefan Kalscheuer
* @since 0.6.0
*/
@EnableRuleMigrationSupport
public class HTTPVaultConnectorFactoryTest {
private static String VAULT_ADDR = "https://localhost:8201";
private static Integer VAULT_MAX_RETRIES = 13;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2018 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,13 +18,14 @@ package de.stklcode.jvault.connector.model;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
/**
* JUnit Test for AppRole Builder.
@ -53,7 +54,7 @@ public class AppRoleBuilderTest {
private static final String JSON_FULL = String.format("{\"role_name\":\"%s\",\"role_id\":\"%s\",\"bind_secret_id\":%s,\"bound_cidr_list\":\"%s\",\"policies\":\"%s\",\"secret_id_num_uses\":%d,\"secret_id_ttl\":%d,\"token_ttl\":%d,\"token_max_ttl\":%d,\"period\":%d}",
NAME, ID, BIND_SECRET_ID, CIDR_1, POLICY, SECRET_ID_NUM_USES, SECRET_ID_TTL, TOKEN_TTL, TOKEN_MAX_TTL, PERIOD);
@BeforeClass
@BeforeAll
public static void init() {
BOUND_CIDR_LIST.add(CIDR_1);
POLICIES.add(POLICY);

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2018 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,11 +16,9 @@
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 org.junit.jupiter.api.Test;
import java.io.IOException;
import java.lang.reflect.Field;
@ -29,12 +27,11 @@ 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.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.junit.MatcherAssume.assumeThat;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.junit.jupiter.api.Assertions.fail;
/**
* JUnit Test for AppRoleSecret model.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2018 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,10 +16,10 @@
package de.stklcode.jvault.connector.model;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
/**
* JUnit Test for AuthBackend model.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2018 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,16 +18,16 @@ package de.stklcode.jvault.connector.model;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
/**
* JUnit Test for Token Builder.
@ -55,7 +55,7 @@ public class TokenBuilderTest {
private static final Boolean RENEWABLE = true;
private static final String JSON_FULL = "{\"id\":\"test-id\",\"display_name\":\"display-name\",\"no_parent\":false,\"no_default_policy\":false,\"ttl\":123,\"num_uses\":4,\"policies\":[\"policy\"],\"meta\":{\"key\":\"value\"},\"renewable\":true}";
@BeforeClass
@BeforeAll
public static void init() {
POLICIES.add(POLICY);
META.put(META_KEY, META_VALUE);

View File

@ -1,17 +1,33 @@
/*
* Copyright 2016-2018 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.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.AppRole;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.junit.jupiter.api.Assertions.fail;
/**
* JUnit Test for {@link AppRoleResponse} model.

View File

@ -1,20 +1,34 @@
/*
* Copyright 2016-2018 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.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.AuthBackend;
import de.stklcode.jvault.connector.model.response.embedded.AuthData;
import de.stklcode.jvault.connector.model.response.embedded.AuthMethod;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.junit.jupiter.api.Assertions.fail;
/**
* JUnit Test for {@link AuthMethodsResponse} model.

View File

@ -1,17 +1,33 @@
/*
* Copyright 2016-2018 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.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.response.embedded.AuthData;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.junit.jupiter.api.Assertions.fail;
/**
* JUnit Test for {@link AuthResponse} model.

View File

@ -0,0 +1,72 @@
/*
* Copyright 2016-2018 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.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.jupiter.api.Assertions.fail;
/**
* JUnit Test for {@link AuthResponse} model.
*
* @author Stefan Kalscheuer
* @since 0.7.0
*/
public class HealthResponseTest {
private static final String CLUSTER_ID = "c9abceea-4f46-4dab-a688-5ce55f89e228";
private static final String CLUSTER_NAME = "vault-cluster-5515c810";
private static final String VERSION = "0.6.2";
private static final Long SERVER_TIME_UTC = 1469555798L;
private static final Boolean STANDBY = false;
private static final Boolean SEALED = false;
private static final Boolean INITIALIZED = true;
private static final String RES_JSON = "{\n" +
" \"cluster_id\": \"" + CLUSTER_ID + "\",\n" +
" \"cluster_name\": \"" + CLUSTER_NAME + "\",\n" +
" \"version\": \"" + VERSION + "\",\n" +
" \"server_time_utc\": " + SERVER_TIME_UTC + ",\n" +
" \"standby\": " + STANDBY + ",\n" +
" \"sealed\": " + SEALED + ",\n" +
" \"initialized\": " + INITIALIZED + "\n" +
"}";
/**
* Test creation from JSON value as returned by Vault (JSON example copied from Vault documentation).
*/
@Test
public void jsonRoundtrip() {
try {
HealthResponse res = new ObjectMapper().readValue(RES_JSON, HealthResponse.class);
assertThat("Parsed response is NULL", res, is(notNullValue()));
assertThat("Incorrect cluster ID", res.getClusterID(), is(CLUSTER_ID));
assertThat("Incorrect cluster name", res.getClusterName(), is(CLUSTER_NAME));
assertThat("Incorrect version", res.getVersion(), is(VERSION));
assertThat("Incorrect server time", res.getServerTimeUTC(), is(SERVER_TIME_UTC));
assertThat("Incorrect standby state", res.isStandby(), is(STANDBY));
assertThat("Incorrect seal state", res.isSealed(), is(SEALED));
assertThat("Incorrect initialization state", res.isInitialized(), is(INITIALIZED));
} catch (IOException e) {
fail("Health deserialization failed: " + e.getMessage());
}
}
}

View File

@ -1,17 +1,33 @@
/*
* Copyright 2016-2018 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.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.junit.jupiter.api.Assertions.fail;
/**
* JUnit Test for {@link SecretResponse} model.

View File

@ -1,17 +1,33 @@
/*
* Copyright 2016-2018 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.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.response.embedded.TokenData;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.junit.jupiter.api.Assertions.fail;
/**
* JUnit Test for {@link TokenResponse} model.

View File

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

View File

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