17 Commits

Author SHA1 Message Date
23419e94f1 Preparations for 0.6.2 release 2017-08-19 12:27:20 +02:00
5b34cfcc27 Refactored extraction of auth backends
Changed iteration over keys ot iteration over entries.
Implemented unit test as part of this process.
2017-08-19 12:16:57 +02:00
b1c78b50d2 Added unit tests for response classes with embedded functionality
AppRoleResponse, AuthResponse, SecretResponse, TokenResponse
2017-08-19 11:28:04 +02:00
238ac8bd10 Do not print stacktrace in response models (#13) 2017-08-18 21:09:40 +02:00
c8a2e0784f Do not print stacktrace on PUT request (#13) 2017-08-18 20:58:57 +02:00
23f98f190b Fix typo in method name listAppRoleSecrets (#14) 2017-08-18 20:45:47 +02:00
745ab7a24c More connector unit tests
Listing of AppRole roles and secrets, seal-unseal roundtrip, closing
2017-08-18 20:27:06 +02:00
71f68f088f Unit tests for custom exceptions
... for the sake of coverage.
2017-08-18 18:28:36 +02:00
52a1abfb87 Fields of InvalidResposneException made final
Deprecated pseudo-builder methods withStatusCode() and withResponse() in
favor of additional constructor arguments to enforce fixed state.
2017-08-18 17:47:55 +02:00
4bf9df5f73 Quality Gate badge in ReadMe 2017-08-18 17:07:22 +02:00
8ae4dccdd6 Throw more speaking Exception when parsing body failed
Removed the fall-through behavior in code 200 exception.
2017-08-18 17:01:30 +02:00
7ac550230f Prevent potential NPE on SecretResponse getter 2017-08-18 16:58:33 +02:00
c1d519826c Add SonarCloud to Travis 2017-08-18 13:57:10 +02:00
a727ed960b Test against Vault 0.8.1 2017-08-16 19:41:38 +02:00
c685ec82a0 Test against Vault 0.8.0 2017-08-11 16:16:05 +02:00
934628f382 Test against Vault 0.8.0-rc1 2017-08-04 08:46:33 +02:00
ce90f9fba2 Readme 0.6.1 2017-08-04 08:37:03 +02:00
22 changed files with 1044 additions and 67 deletions

View File

@ -5,9 +5,21 @@ language: java
jdk:
- oraclejdk8
dist: trusty
install: true
addons:
sonarcloud:
organization: "stklcode-github"
token:
secure: "sM9OfX5jW764pn9cb2LSXArnXucKMws+eGeg5NnZxHRcGYt4hpBKLSregBSsBNzUoWVj0zNzPCpnh+UQvgxQzUerOqwEdjTBpy3SNPaxSn7UpoSg+Wz3aUmL9ugmx01b51/wMG4UCHEwTZt2tpgTPVtw8K6uSO78e0dSICCBHDnRcdQwOjMEQHIJJ/qHVRwuy/MzLCAP3W1JPZlsphZg9QsFyhB4hW97dE90joZezfocQIv2xI/r6k+BLz0pY6MxYCul0RiDumaiaej0CPvEJI/uSu//BAQjUdHw+mQgnKUYIbrn2ONOviwNfwdr94JyoZEN2B6zASUmNLjPf4AbIojDeyS+CrpQpm17EVm/Qk/Ds+Xra4PPPIcsZhiWzV0KoDUz9xLfXuRJ526VT5tDPiaeI7oETf0+8l+JIS1b399FyqHi7smzjpvC6GuKflQrbuHK4MuKzDh7WTHiqokGG4SS0wOQIaaHB3dfdwwQzPh6IM24e8CETxh3DjMeqUTU4DWmv5po55jZ934TtxVQvVN78bTG9O0zS9u+JmRY04OZ+OaXuFam6MfMUFQi0EPZzdGul/oWSibGUu3bNfVEBp60CnJwYNM/dKG6U7pJthLHvSwiQFOdKzHZ+l1jZJ4gPaXaIGqpwqVGr28ntqA/El1rytPixr2driE6bYMt5jw="
env:
- PATH=$PATH:.
before_script:
- wget https://releases.hashicorp.com/vault/0.7.3/vault_0.7.3_linux_amd64.zip
- unzip vault_0.7.3_linux_amd64.zip
- rm vault_0.7.3_linux_amd64.zip
- 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
cache:
directories:
- '$HOME/.m2/repository'
- '$HOME/.sonar/cache'
script:
- mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package sonar:sonar

View File

@ -1,3 +1,10 @@
## 0.6.2 [2017-08-19]
* [fix] Prevent potential NPE on SecretResponse getter
* [fix] Removed stack traces on PUT request and response deserialization (#13)
* [improvement] Fields of InvalidResposneException made final
* [deprecation] `listAppRoleSecretss()` in favor of `listAppRoleSecrets()` (#14)
* [test] Tested against Vault 0.8.1, increased coverage
## 0.6.1 [2017-08-02]
* [fix] `TokenModel.getPassword()` returned username instead of password
* [fix] `TokenModel.getUsername()` and `getPassword()` could produce NPE in multithreaded environments

View File

@ -1,6 +1,7 @@
# Java Vault Connector
[![Build Status](https://travis-ci.org/stklcode/jvaultconnector.svg?branch=master)](https://travis-ci.org/stklcode/jvaultconnector)
[![Quality Gate](https://sonarcloud.io/api/badges/gate?key=de.stklcode.jvault%3Aconnector)](https://sonarcloud.io/dashboard?id=de.stklcode.jvault%3Aconnector)
[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://github.com/stklcode/jvaultconnector/blob/master/LICENSE.txt)
[![Maven Central](https://img.shields.io/maven-central/v/de.stklcode.jvault/connector.svg)](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22de.stklcode.jvault%22%20AND%20a%3A%22connector%22)
@ -30,7 +31,7 @@ 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.7.3
* Tested against Vault 0.8.1
## Maven Artifact
@ -38,7 +39,7 @@ Java Vault Connector is a connector library for [Vault](https://www.vaultproject
<dependency>
<groupId>de.stklcode.jvault</groupId>
<artifactId>connector</artifactId>
<version>0.6.0</version>
<version>0.6.2</version>
</dependency>
```

View File

@ -4,7 +4,7 @@
<groupId>de.stklcode.jvault</groupId>
<artifactId>connector</artifactId>
<version>0.6.1</version>
<version>0.6.2</version>
<packaging>jar</packaging>

View File

@ -60,6 +60,8 @@ public class HTTPVaultConnector implements VaultConnector {
private static final String PATH_AUTH_APPROLE = "auth/approle/";
private static final String PATH_REVOKE = "sys/revoke/";
private static final String HEADER_VAULT_TOKEN = "X-Vault-Token";
private final ObjectMapper jsonMapper;
private final String baseURL; /* Base URL of Vault */
@ -512,7 +514,7 @@ public class HTTPVaultConnector implements VaultConnector {
}
@Override
public final List<String> listAppRoleSecretss(final String roleName) throws VaultConnectorException {
public final List<String> listAppRoleSecrets(final String roleName) throws VaultConnectorException {
if (!isAuthorized())
throw new AuthorizationRequiredException();
@ -709,7 +711,7 @@ public class HTTPVaultConnector implements VaultConnector {
post.setEntity(input);
/* Set X-Vault-Token header */
if (token != null)
post.addHeader("X-Vault-Token", token);
post.addHeader(HEADER_VAULT_TOKEN, token);
return request(post, retries);
}
@ -730,13 +732,13 @@ public class HTTPVaultConnector implements VaultConnector {
try {
entity = new StringEntity(jsonMapper.writeValueAsString(payload));
} catch (UnsupportedEncodingException | JsonProcessingException e) {
e.printStackTrace();
throw new InvalidRequestException("Payload serialization failed", e);
}
/* Parse parameters */
put.setEntity(entity);
/* Set X-Vault-Token header */
if (token != null)
put.addHeader("X-Vault-Token", token);
put.addHeader(HEADER_VAULT_TOKEN, token);
return request(put, retries);
}
@ -753,7 +755,7 @@ public class HTTPVaultConnector implements VaultConnector {
HttpDelete delete = new HttpDelete(baseURL + path);
/* Set X-Vault-Token header */
if (token != null)
delete.addHeader("X-Vault-Token", token);
delete.addHeader(HEADER_VAULT_TOKEN, token);
return request(delete, retries);
}
@ -778,7 +780,7 @@ public class HTTPVaultConnector implements VaultConnector {
/* Set X-Vault-Token header */
if (token != null)
get.addHeader("X-Vault-Token", token);
get.addHeader(HEADER_VAULT_TOKEN, token);
return request(get, retries);
}
@ -812,6 +814,7 @@ public class HTTPVaultConnector implements VaultConnector {
new InputStreamReader(response.getEntity().getContent()))) {
return br.lines().collect(Collectors.joining("\n"));
} catch (IOException ignored) {
throw new InvalidResponseException("Could not parse response body", 200);
}
case 204:
return "";
@ -824,21 +827,22 @@ public class HTTPVaultConnector implements VaultConnector {
return request(base, retries - 1);
} else {
/* Fail on different error code and/or no retries left */
InvalidResponseException ex = new InvalidResponseException("Invalid response code")
.withStatusCode(response.getStatusLine().getStatusCode());
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().size() > 0 && er.getErrors().get(0).equals("permission denied"))
if (!er.getErrors().isEmpty() && er.getErrors().get(0).equals("permission denied"))
throw new PermissionDeniedException();
throw ex.withResponse(er.toString());
throw new InvalidResponseException("Invalid response code",
response.getStatusLine().getStatusCode(), er.toString());
} catch (IOException ignored) {
// Exception ignored.
}
}
throw ex;
throw new InvalidResponseException("Invalid response code",
response.getStatusLine().getStatusCode());
}
}
} catch (IOException e) {
@ -848,6 +852,7 @@ public class HTTPVaultConnector implements VaultConnector {
try {
EntityUtils.consume(response.getEntity());
} catch (IOException ignored) {
// Exception ignored.
}
}
}

View File

@ -325,8 +325,21 @@ public interface VaultConnector extends AutoCloseable {
* @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!
*/
List<String> listAppRoleSecretss(final String roleName) throws VaultConnectorException;
@Deprecated
default List<String> listAppRoleSecretss(final String roleName) throws VaultConnectorException {
return listAppRoleSecrets(roleName);
}
/**
* List existing (accessible) secret IDs for AppRole role.
*
* @param roleName The role name
* @return List of roles
* @throws VaultConnectorException on error
*/
List<String> listAppRoleSecrets(final String roleName) throws VaultConnectorException;
/**
* Register User-ID with App-ID.

View File

@ -24,13 +24,15 @@ package de.stklcode.jvault.connector.exception;
* @since 0.1
*/
public final class InvalidResponseException extends VaultConnectorException {
private Integer statusCode;
private String response;
private final Integer statusCode;
private final String response;
/**
* Constructs a new empty exception.
*/
public InvalidResponseException() {
this.statusCode = null;
this.response = null;
}
/**
@ -40,6 +42,8 @@ public final class InvalidResponseException extends VaultConnectorException {
*/
public InvalidResponseException(final String message) {
super(message);
this.statusCode = null;
this.response = null;
}
/**
@ -49,6 +53,8 @@ public final class InvalidResponseException extends VaultConnectorException {
*/
public InvalidResponseException(final Throwable cause) {
super(cause);
this.statusCode = null;
this.response = null;
}
/**
@ -59,6 +65,75 @@ public final class InvalidResponseException extends VaultConnectorException {
*/
public InvalidResponseException(final String message, final Throwable cause) {
super(message, cause);
this.statusCode = null;
this.response = null;
}
/**
* Constructs a new exception with the specified detail message and status code.
* <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
* @since 0.6.2
*/
public InvalidResponseException(final String message, final Integer statusCode) {
super(message);
this.statusCode = statusCode;
this.response = null;
}
/**
* Constructs a new exception with the specified detail message, cause and status code.
* <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
* @since 0.6.2
*/
public InvalidResponseException(final String message, final Integer statusCode, final Throwable cause) {
this(message, statusCode, null, cause);
}
/**
* Constructs a new exception with the specified detail message, cause and status code.
* <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 response HTTP response string
* @since 0.6.2
*/
public InvalidResponseException(final String message,
final Integer statusCode,
final String response) {
super(message);
this.statusCode = statusCode;
this.response = response;
}
/**
* Constructs a new exception with the specified detail message, cause and status code.
* <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 response HTTP response string
* @param cause the cause
* @since 0.6.2
*/
public InvalidResponseException(final String message,
final Integer statusCode,
final String response,
final Throwable cause) {
super(message, cause);
this.statusCode = statusCode;
this.response = response;
}
/**
@ -66,10 +141,11 @@ public final class InvalidResponseException extends VaultConnectorException {
*
* @param statusCode the status code
* @return self
* @deprecated as of 0.6.2, use constructor with status code argument instead
*/
@Deprecated
public InvalidResponseException withStatusCode(final Integer statusCode) {
this.statusCode = statusCode;
return this;
return new InvalidResponseException(getMessage(), statusCode, getResponse(), getCause());
}
/**
@ -77,10 +153,11 @@ public final class InvalidResponseException extends VaultConnectorException {
*
* @param response response text
* @return self
* @deprecated use constructor with response argument instead
*/
@Deprecated
public InvalidResponseException withResponse(final String response) {
this.response = response;
return this;
return new InvalidResponseException(getMessage(), getStatusCode(), response, getCause());
}
/**

View File

@ -27,6 +27,7 @@ public enum AuthBackend {
APPID("app-id"),
APPROLE("approle"),
USERPASS("userpass"),
GITHUB("github"), // Not supported yet.
UNKNOWN("");
private final String type;

View File

@ -46,8 +46,7 @@ public final class AppRoleResponse extends VaultDataResponse {
});
this.role = mapper.readValue(mapper.writeValueAsString(filteredData), AppRole.class);
} catch (IOException e) {
e.printStackTrace();
throw new InvalidResponseException();
throw new InvalidResponseException("Failed deserializing response", e);
}
}

View File

@ -46,8 +46,7 @@ public final class AppRoleSecretResponse extends VaultDataResponse {
});
this.secret = mapper.readValue(mapper.writeValueAsString(filteredData), AppRoleSecret.class);
} catch (IOException e) {
e.printStackTrace();
throw new InvalidResponseException();
throw new InvalidResponseException("Failed deserializing response", e);
}
}

View File

@ -45,11 +45,10 @@ public final class AuthMethodsResponse extends VaultDataResponse {
@Override
public void setData(final Map<String, Object> data) throws InvalidResponseException {
ObjectMapper mapper = new ObjectMapper();
for (String path : data.keySet()) {
for (Map.Entry<String, Object> entry : data.entrySet()) {
try {
this.supportedMethods.put(
path, mapper.readValue(mapper.writeValueAsString(data.get(path)),
AuthMethod.class));
this.supportedMethods.put(entry.getKey(),
mapper.readValue(mapper.writeValueAsString(entry.getValue()), AuthMethod.class));
} catch (IOException e) {
throw new InvalidResponseException();
}

View File

@ -49,8 +49,7 @@ public final class AuthResponse extends VaultDataResponse {
try {
this.auth = mapper.readValue(mapper.writeValueAsString(auth), AuthData.class);
} catch (IOException e) {
e.printStackTrace();
throw new InvalidResponseException();
throw new InvalidResponseException("Failed deserializing response", e);
}
}

View File

@ -55,7 +55,7 @@ public class SecretResponse extends VaultDataResponse {
* Get a single value for given key.
*
* @param key the key
* @return the value or NULL if absent
* @return the value or {@code null} if absent
* @since 0.4.0
*/
public final Object get(final String key) {
@ -73,9 +73,10 @@ public class SecretResponse extends VaultDataResponse {
*/
@Deprecated
public final String getValue() {
if (get("value") == null)
Object value = get("value");
if (value == null)
return null;
return get("value").toString();
return value.toString();
}
/**
@ -99,13 +100,16 @@ public class SecretResponse extends VaultDataResponse {
* @param key the key
* @param type Class to parse response
* @param <T> Class to parse response
* @return Parsed object
* @return Parsed object or {@code null} if absent
* @throws InvalidResponseException on parsing error
* @since 0.4.0
*/
public final <T> T get(final String key, final Class<T> type) throws InvalidResponseException {
try {
return new ObjectMapper().readValue(get(key).toString(), type);
Object rawValue = get(key);
if (rawValue == null)
return null;
return new ObjectMapper().readValue(rawValue.toString(), type);
} catch (IOException e) {
throw new InvalidResponseException("Unable to parse response payload: " + e.getMessage());
}

View File

@ -50,8 +50,7 @@ public final class TokenResponse extends VaultDataResponse {
try {
this.data = mapper.readValue(mapper.writeValueAsString(data), TokenData.class);
} catch (IOException e) {
e.printStackTrace();
throw new InvalidResponseException();
throw new InvalidResponseException("Failed deserializing response", e);
}
}

View File

@ -16,21 +16,19 @@
package de.stklcode.jvault.connector;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.exception.*;
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.exception.InvalidRequestException;
import de.stklcode.jvault.connector.exception.PermissionDeniedException;
import de.stklcode.jvault.connector.exception.VaultConnectorException;
import de.stklcode.jvault.connector.factory.VaultConnectorFactory;
import org.junit.*;
import org.junit.rules.TemporaryFolder;
import org.junit.rules.TestName;
import java.io.*;
import java.lang.reflect.Field;
import java.net.ServerSocket;
import java.nio.file.Paths;
import java.util.Arrays;
@ -59,7 +57,9 @@ public class HTTPVaultConnectorTest {
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_ROLE2 = "35b7bf43-9644-588a-e68f-2e8313bb23b7"; // role with CIDR subnet
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";
@ -113,6 +113,28 @@ public class HTTPVaultConnectorTest {
vaultProcess.destroy();
}
/**
* Test sealing and unsealing Vault.
*/
@Test
public void sealTest() {
SealResponse sealStatus = connector.sealStatus();
assumeFalse(sealStatus.isSealed());
/* Unauthorized sealing should fail */
assertThat("Unauthorized sealing succeeded", connector.seal(), is(false));
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));
sealStatus = connector.sealStatus();
assertThat("Vault not sealed", sealStatus.isSealed(), is(true));
sealStatus = connector.unseal(KEY);
assertThat("Vault not unsealed", sealStatus.isSealed(), is(false));
}
/**
* Test listing of authentication backends
*/
@ -191,6 +213,23 @@ public class HTTPVaultConnectorTest {
@Test
@SuppressWarnings("deprecation")
public void authAppIdTest() {
/* Try unauthorized access first. */
assumeFalse(connector.isAuthorized());
try {
connector.registerAppId("", "", "");
fail("Expected exception not thrown");
} catch (Exception e) {
assertThat("Unexpected exception class", e, is(instanceOf(AuthorizationRequiredException.class)));
}
try {
connector.registerUserId("", "");
fail("Expected exception not thrown");
} catch (Exception e) {
assertThat("Unexpected exception class", e, is(instanceOf(AuthorizationRequiredException.class)));
}
/* Authorize. */
authRoot();
assumeTrue(connector.isAuthorized());
@ -285,6 +324,65 @@ public class HTTPVaultConnectorTest {
*/
@Test
public void createAppRoleTest() {
/* Try unauthorized access first. */
assumeFalse(connector.isAuthorized());
try {
connector.createAppRole(new AppRole());
fail("Expected exception not thrown");
} catch (Exception e) {
assertThat("Unexpected exception class", e, is(instanceOf(AuthorizationRequiredException.class)));
}
try {
connector.lookupAppRole("");
fail("Expected exception not thrown");
} catch (Exception e) {
assertThat("Unexpected exception class", e, is(instanceOf(AuthorizationRequiredException.class)));
}
try {
connector.deleteAppRole("");
fail("Expected exception not thrown");
} catch (Exception e) {
assertThat("Unexpected exception class", e, is(instanceOf(AuthorizationRequiredException.class)));
}
try {
connector.getAppRoleID("");
fail("Expected exception not thrown");
} catch (Exception e) {
assertThat("Unexpected exception class", e, is(instanceOf(AuthorizationRequiredException.class)));
}
try {
connector.setAppRoleID("", "");
fail("Expected exception not thrown");
} catch (Exception e) {
assertThat("Unexpected exception class", e, is(instanceOf(AuthorizationRequiredException.class)));
}
try {
connector.createAppRoleSecret("", "");
fail("Expected exception not thrown");
} catch (Exception e) {
assertThat("Unexpected exception class", e, is(instanceOf(AuthorizationRequiredException.class)));
}
try {
connector.lookupAppRoleSecret("", "");
fail("Expected exception not thrown");
} catch (Exception e) {
assertThat("Unexpected exception class", e, is(instanceOf(AuthorizationRequiredException.class)));
}
try {
connector.destroyAppRoleSecret("", "");
fail("Expected exception not thrown");
} catch (Exception e) {
assertThat("Unexpected exception class", e, is(instanceOf(AuthorizationRequiredException.class)));
}
/* Authorize. */
authRoot();
assumeTrue(connector.isAuthorized());
@ -310,15 +408,16 @@ public class HTTPVaultConnectorTest {
}
/* Lookup role ID */
String roleID = "";
try {
String res = connector.getAppRoleID(roleName);
assertThat("Role ID lookup returned empty ID.", res, is(not(emptyString())));
roleID = connector.getAppRoleID(roleName);
assertThat("Role ID lookup returned empty ID.", roleID, is(not(emptyString())));
} catch (VaultConnectorException e) {
fail("Role ID lookup failed.");
}
/* Set custom role ID */
String roleID = "custom-role-id";
roleID = "custom-role-id";
try {
connector.setAppRoleID(roleName, roleID);
} catch (VaultConnectorException e) {
@ -444,6 +543,51 @@ public class HTTPVaultConnectorTest {
}
}
/**
* Test listing of AppRole roles and secrets.
*/
@Test
public void listAppRoleTest() {
/* Try unauthorized access first. */
assumeFalse(connector.isAuthorized());
try {
connector.listAppRoles();
fail("Expected exception not thrown");
} catch (Exception e) {
assertThat("Unexpected exception class", e, is(instanceOf(AuthorizationRequiredException.class)));
}
try {
connector.listAppRoleSecrets("");
fail("Expected exception not thrown");
} catch (Exception e) {
assertThat("Unexpected exception class", e, is(instanceOf(AuthorizationRequiredException.class)));
}
/* Authorize. */
authRoot();
assumeTrue(connector.isAuthorized());
/* Verify pre-existing rules */
try {
List<String> res = connector.listAppRoles();
assertThat("Unexpected number of AppRoles", res, hasSize(2));
assertThat("Pre-configured roles not listed", res, containsInAnyOrder(APPROLE_ROLE_NAME, APPROLE_ROLE2_NAME));
} catch (VaultConnectorException e) {
fail("Role listing failed.");
}
/* Check secret IDs */
try {
List<String> res = connector.listAppRoleSecrets(APPROLE_ROLE_NAME);
assertThat("Unexpected number of AppRole secrets", res, hasSize(1));
assertThat("Pre-configured AppRole secret not listed", res, contains(APPROLE_SECRET_ACCESSOR));
} catch (VaultConnectorException e) {
fail("AppRole secret listing failed.");
}
}
/**
* Test reading of secrets.
*/
@ -697,24 +841,24 @@ public class HTTPVaultConnectorTest {
}
/* Overwrite token should fail as of Vault 0.8.0 */
// token = new TokenBuilder()
// .withId("test-id2")
// .withDisplayName("test name 3")
// .withPolicies(Arrays.asList("pol1", "pol2"))
// .withDefaultPolicy()
// .withMeta("test", "success")
// .withMeta("key", "value")
// .withTtl(1234)
// .build();
// try {
// connector.createToken(token);
// 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));
// /* Assert that the exception does not reveal token ID */
// assertThat(stackTrace(e), not(stringContainsInOrder(token.getId())));
// }
token = new TokenBuilder()
.withId("test-id2")
.withDisplayName("test name 3")
.withPolicies(Arrays.asList("pol1", "pol2"))
.withDefaultPolicy()
.withMeta("test", "success")
.withMeta("key", "value")
.withTtl(1234)
.build();
try {
connector.createToken(token);
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));
/* Assert that the exception does not reveal token ID */
assertThat(stackTrace(e), not(stringContainsInOrder(token.getId())));
}
}
/**
@ -738,6 +882,27 @@ public class HTTPVaultConnectorTest {
}
}
/**
* Test closing the connector.
*/
@Test
public void closeTest() {
authUser();
assumeTrue(connector.isAuthorized());
try {
connector.close();
assertThat("Not unauthorized after close().", connector.isAuthorized(), is(false));
/* Verify that (private) token has indeed been removed */
Field tokenField = HTTPVaultConnector.class.getDeclaredField("token");
tokenField.setAccessible(true);
assertThat("Token not removed after close().", tokenField.get(connector), is(nullValue()));
} catch (Exception e) {
fail("Closing the connector failed: " + e.getMessage());
}
}
/**
* Initialize Vault with resource datastore and generated configuration.
*

View File

@ -0,0 +1,160 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.stklcode.jvault.connector.exception;
import org.junit.Test;
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}.
*
* @author Stefan Kalscheuer
* @since 0.6.2
*/
public class VaultConnectorExceptionTest {
private static final String MSG = "This is a test exception!";
private static final Throwable CAUSE = new Exception("Test-Cause");
private static final Integer STATUS_CODE = 1337;
private static final String RESPONSE = "Dummy response";
@Test
public void authorizationRequiredExceptionTest() {
assertEmptyConstructor(new AuthorizationRequiredException());
}
@Test
public void connectionExceptionTest() {
assertEmptyConstructor(new ConnectionException());
assertMsgConstructor(new ConnectionException(MSG));
assertCauseConstructor(new ConnectionException(CAUSE));
assertMsgCauseConstructor(new ConnectionException(MSG, CAUSE));
}
@Test
public void invalidRequestExceptionTest() {
assertEmptyConstructor(new InvalidRequestException());
assertMsgConstructor(new InvalidRequestException(MSG));
assertCauseConstructor(new InvalidRequestException(CAUSE));
assertMsgCauseConstructor(new InvalidRequestException(MSG, CAUSE));
}
@Test
public void invalidResponseExceptionTest() {
assertEmptyConstructor(new InvalidResponseException());
assertMsgConstructor(new InvalidResponseException(MSG));
assertCauseConstructor(new InvalidResponseException(CAUSE));
assertMsgCauseConstructor(new InvalidResponseException(MSG, CAUSE));
// Constructor with message and status code.
InvalidResponseException e = new InvalidResponseException(MSG, STATUS_CODE);
assertThat(e.getMessage(), is(MSG));
assertThat(e.getCause(), is(nullValue()));
assertThat(e.getStatusCode(), is(STATUS_CODE));
assertThat(e.getResponse(), is(nullValue()));
// Constructor with message, status code and cause.
e = new InvalidResponseException(MSG, STATUS_CODE, CAUSE);
assertThat(e.getMessage(), is(MSG));
assertThat(e.getCause(), is(CAUSE));
assertThat(e.getStatusCode(), is(STATUS_CODE));
assertThat(e.getResponse(), is(nullValue()));
// Constructor with message, status code and response.
e = new InvalidResponseException(MSG, STATUS_CODE, RESPONSE);
assertThat(e.getMessage(), is(MSG));
assertThat(e.getCause(), is(nullValue()));
assertThat(e.getStatusCode(), is(STATUS_CODE));
assertThat(e.getResponse(), is(RESPONSE));
// Constructor with message, status code, response and cause.
e = new InvalidResponseException(MSG, STATUS_CODE, RESPONSE, CAUSE);
assertThat(e.getMessage(), is(MSG));
assertThat(e.getCause(), is(CAUSE));
assertThat(e.getStatusCode(), is(STATUS_CODE));
assertThat(e.getResponse(), is(RESPONSE));
}
@Test
public void permissionDeniedExceptionTest() {
// Default message overwritten.
PermissionDeniedException e = new PermissionDeniedException();
assertThat(e, is(instanceOf(VaultConnectorException.class)));
assertThat(e, is(instanceOf(Exception.class)));
assertThat(e, is(instanceOf(Throwable.class)));
assertThat(e.getMessage(), is("Permission denied"));
assertThat(e.getCause(), is(nullValue()));
assertMsgConstructor(new PermissionDeniedException(MSG));
assertCauseConstructor(new PermissionDeniedException(CAUSE));
assertMsgCauseConstructor(new PermissionDeniedException(MSG, CAUSE));
}
@Test
public void tlsExceptionTest() {
assertEmptyConstructor(new TlsException());
assertMsgConstructor(new TlsException(MSG));
assertCauseConstructor(new TlsException(CAUSE));
assertMsgCauseConstructor(new TlsException(MSG, CAUSE));
}
/**
* Assertions for empty constructor.
*
* @param e the exception
*/
private void assertEmptyConstructor(VaultConnectorException e) {
assertThat(e, is(instanceOf(VaultConnectorException.class)));
assertThat(e, is(instanceOf(Exception.class)));
assertThat(e, is(instanceOf(Throwable.class)));
assertThat(e.getMessage(), is(nullValue()));
assertThat(e.getCause(), is(nullValue()));
}
/**
* Assertions for constructor with message.
*
* @param e the exception
*/
private void assertMsgConstructor(VaultConnectorException e) {
assertThat(e.getMessage(), is(MSG));
assertThat(e.getCause(), is(nullValue()));
}
/**
* Assertions for constructor with cause.
*
* @param e the exception
*/
private void assertCauseConstructor(VaultConnectorException e) {
assertThat(e.getMessage(), is(CAUSE.toString()));
assertThat(e.getCause(), is(CAUSE));
}
/**
* Assertions for constructor with message and cause.
*
* @param e the exception
*/
private void assertMsgCauseConstructor(VaultConnectorException e) {
assertThat(e.getMessage(), is(MSG));
assertThat(e.getCause(), is(CAUSE));
}
}

View File

@ -37,6 +37,7 @@ public class AuthBackendTest {
assertThat(AuthBackend.forType("token"), is(AuthBackend.TOKEN));
assertThat(AuthBackend.forType("app-id"), is(AuthBackend.APPID));
assertThat(AuthBackend.forType("userpass"), is(AuthBackend.USERPASS));
assertThat(AuthBackend.forType("github"), is(AuthBackend.GITHUB));
assertThat(AuthBackend.forType(""), is(AuthBackend.UNKNOWN));
assertThat(AuthBackend.forType("foobar"), is(AuthBackend.UNKNOWN));
}

View File

@ -0,0 +1,101 @@
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 java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
/**
* JUnit Test for {@link AppRoleResponse} model.
*
* @author Stefan Kalscheuer
* @since 0.6.2
*/
public class AppRoleResponseTest {
private static final Integer ROLE_TOKEN_TTL = 1200;
private static final Integer ROLE_TOKEN_MAX_TTL = 1800;
private static final Integer ROLE_SECRET_TTL = 600;
private static final Integer ROLE_SECRET_NUM_USES = 40;
private static final String ROLE_POLICY = "default";
private static final Integer ROLE_PERIOD = 0;
private static final Boolean ROLE_BIND_SECRET = true;
private static final String RES_JSON = "{\n" +
" \"auth\": null,\n" +
" \"warnings\": null,\n" +
" \"wrap_info\": null,\n" +
" \"data\": {\n" +
" \"token_ttl\": " + ROLE_TOKEN_TTL + ",\n" +
" \"token_max_ttl\": " + ROLE_TOKEN_MAX_TTL + ",\n" +
" \"secret_id_ttl\": " + ROLE_SECRET_TTL + ",\n" +
" \"secret_id_num_uses\": " + ROLE_SECRET_NUM_USES + ",\n" +
" \"policies\": [\n" +
" \"" + ROLE_POLICY + "\"\n" +
" ],\n" +
" \"period\": " + ROLE_PERIOD + ",\n" +
" \"bind_secret_id\": " + ROLE_BIND_SECRET + ",\n" +
" \"bound_cidr_list\": \"\"\n" +
" },\n" +
" \"lease_duration\": 0,\n" +
" \"renewable\": false,\n" +
" \"lease_id\": \"\"\n" +
"}";
private static final Map<String, Object> INVALID_DATA = new HashMap<>();
static {
INVALID_DATA.put("policies", "fancy-policy");
}
/**
* Test getter, setter and get-methods for response data.
*/
@Test
public void getDataRoundtrip() {
// Create empty Object.
AppRoleResponse res = new AppRoleResponse();
assertThat("Initial data should be empty", res.getRole(), is(nullValue()));
// Parsing invalid auth data map should fail.
try {
res.setData(INVALID_DATA);
fail("Parsing invalid data succeeded");
} catch (Exception e) {
assertThat(e, is(instanceOf(InvalidResponseException.class)));
}
}
/**
* Test creation from JSON value as returned by Vault (JSON example copied from Vault documentation).
*/
@Test
public void jsonRoundtrip() {
try {
AppRoleResponse res = new ObjectMapper().readValue(RES_JSON, AppRoleResponse.class);
assertThat("Parsed response is NULL", res, is(notNullValue()));
// Extract role data.
AppRole role = res.getRole();
assertThat("Role data is NULL", role, is(notNullValue()));
assertThat("Incorrect token TTL", role.getTokenTtl(), is(ROLE_TOKEN_TTL));
assertThat("Incorrect token max TTL", role.getTokenMaxTtl(), is(ROLE_TOKEN_MAX_TTL));
assertThat("Incorrect secret ID TTL", role.getSecretIdTtl(), is(ROLE_SECRET_TTL));
assertThat("Incorrect secret ID umber of uses", role.getSecretIdNumUses(), is(ROLE_SECRET_NUM_USES));
assertThat("Incorrect number of policies", role.getPolicies(), hasSize(1));
assertThat("Incorrect role policies", role.getPolicies(), contains(ROLE_POLICY));
assertThat("Incorrect role period", role.getPeriod(), is(ROLE_PERIOD));
assertThat("Incorrect role bind secret ID flag", role.getBindSecretId(), is(ROLE_BIND_SECRET));
assertThat("Incorrect biund CIDR list", role.getBoundCidrList(), is(nullValue()));
assertThat("Incorrect biund CIDR list string", role.getBoundCidrListString(), is(emptyString()));
} catch (IOException e) {
fail("AuthResponse deserialization failed: " + e.getMessage());
}
}
}

View File

@ -0,0 +1,114 @@
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 java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
/**
* JUnit Test for {@link AuthMethodsResponse} model.
*
* @author Stefan Kalscheuer
* @since 0.6.2
*/
public class AuthMethodsResponseTest {
private static final String GH_PATH = "github/";
private static final String GH_TYPE = "github";
private static final String GH_DESCR = "GitHub auth";
private static final String TK_PATH = "token/";
private static final String TK_TYPE = "token";
private static final String TK_DESCR = "token based credentials";
private static final Integer TK_LEASE_TTL = 0;
private static final Integer TK_MAX_LEASE_TTL = 0;
private static final String RES_JSON = "{\n" +
" \"data\": {" +
" \"" + GH_PATH + "\": {\n" +
" \"type\": \"" + GH_TYPE + "\",\n" +
" \"description\": \"" + GH_DESCR + "\"\n" +
" },\n" +
" \"" + TK_PATH + "\": {\n" +
" \"config\": {\n" +
" \"default_lease_ttl\": " + TK_LEASE_TTL + ",\n" +
" \"max_lease_ttl\": " + TK_MAX_LEASE_TTL + "\n" +
" },\n" +
" \"description\": \"" + TK_DESCR + "\",\n" +
" \"type\": \"" + TK_TYPE + "\"\n" +
" }\n" +
" }\n" +
"}";
private static final Map<String, Object> INVALID_DATA = new HashMap<>();
static {
INVALID_DATA.put("dummy/", new Dummy());
}
/**
* Test getter, setter and get-methods for response data.
*/
@Test
public void getDataRoundtrip() {
// Create empty Object.
AuthMethodsResponse res = new AuthMethodsResponse();
assertThat("Initial method map should be empty", res.getSupportedMethods(), is(anEmptyMap()));
// Parsing invalid data map should fail.
try {
res.setData(INVALID_DATA);
fail("Parsing invalid data succeeded");
} catch (Exception e) {
assertThat(e, is(instanceOf(InvalidResponseException.class)));
}
}
/**
* Test creation from JSON value as returned by Vault (JSON example copied from Vault documentation).
*/
@Test
public void jsonRoundtrip() {
try {
AuthMethodsResponse res = new ObjectMapper().readValue(RES_JSON, AuthMethodsResponse.class);
assertThat("Parsed response is NULL", res, is(notNullValue()));
// Extract auth data.
Map<String, AuthMethod> supported = res.getSupportedMethods();
assertThat("Auth data is NULL", supported, is(notNullValue()));
assertThat("Incorrect number of supported methods", supported.entrySet(), hasSize(2));
assertThat("Incorrect method paths", supported.keySet(), containsInAnyOrder(GH_PATH, TK_PATH));
// Verify first method.
AuthMethod method = supported.get(GH_PATH);
assertThat("Incorrect raw type for GitHub", method.getRawType(), is(GH_TYPE));
assertThat("Incorrect parsed type for GitHub", method.getType(), is(AuthBackend.GITHUB));
assertThat("Incorrect description for GitHub", method.getDescription(), is(GH_DESCR));
assertThat("Unexpected config for GitHub", method.getConfig(), is(nullValue()));
// Verify first method.
method = supported.get(TK_PATH);
assertThat("Incorrect raw type for Token", method.getRawType(), is(TK_TYPE));
assertThat("Incorrect parsed type for Token", method.getType(), is(AuthBackend.TOKEN));
assertThat("Incorrect description for Token", method.getDescription(), is(TK_DESCR));
assertThat("Missing config for Token", method.getConfig(), is(notNullValue()));
assertThat("Unexpected config size for Token", method.getConfig().keySet(), hasSize(2));
assertThat("Incorrect lease TTL config", method.getConfig().get("default_lease_ttl"), is(TK_LEASE_TTL.toString()));
assertThat("Incorrect max lease TTL config", method.getConfig().get("max_lease_ttl"), is(TK_MAX_LEASE_TTL.toString()));
} catch (IOException e) {
fail("AuthResponse deserialization failed: " + e.getMessage());
}
}
private static class Dummy {
}
}

View File

@ -0,0 +1,97 @@
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 java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
/**
* JUnit Test for {@link AuthResponse} model.
*
* @author Stefan Kalscheuer
* @since 0.6.2
*/
public class AuthResponseTest {
private static final String AUTH_ACCESSOR = "2c84f488-2133-4ced-87b0-570f93a76830";
private static final String AUTH_CLIENT_TOKEN = "ABCD";
private static final String AUTH_POLICY_1 = "web";
private static final String AUTH_POLICY_2 = "stage";
private static final String AUTH_META_KEY = "user";
private static final String AUTH_META_VALUE = "armon";
private static final Integer AUTH_LEASE_DURATION = 3600;
private static final Boolean AUTH_RENEWABLE = true;
private static final String RES_JSON = "{\n" +
" \"auth\": {\n" +
" \"accessor\": \"" + AUTH_ACCESSOR + "\",\n" +
" \"client_token\": \"" + AUTH_CLIENT_TOKEN + "\",\n" +
" \"policies\": [\n" +
" \"" + AUTH_POLICY_1 + "\", \n" +
" \"" + AUTH_POLICY_2 + "\"\n" +
" ],\n" +
" \"metadata\": {\n" +
" \"" + AUTH_META_KEY + "\": \"" + AUTH_META_VALUE + "\"\n" +
" },\n" +
" \"lease_duration\": " + AUTH_LEASE_DURATION + ",\n" +
" \"renewable\": " + AUTH_RENEWABLE + "\n" +
" }\n" +
"}";
private static final Map<String, Object> INVALID_AUTH_DATA = new HashMap<>();
static {
INVALID_AUTH_DATA.put("policies", "fancy-policy");
}
/**
* Test getter, setter and get-methods for response data.
*/
@Test
public void getDataRoundtrip() {
// Create empty Object.
AuthResponse res = new AuthResponse();
assertThat("Initial data should be empty", res.getData(), is(nullValue()));
// Parsing invalid auth data map should fail.
try {
res.setAuth(INVALID_AUTH_DATA);
fail("Parsing invalid auth data succeeded");
} catch (Exception e) {
assertThat(e, is(instanceOf(InvalidResponseException.class)));
}
// Data method should be agnostic.
res.setData(INVALID_AUTH_DATA);
assertThat("Data not passed through", res.getData(), is(INVALID_AUTH_DATA));
}
/**
* Test creation from JSON value as returned by Vault (JSON example copied from Vault documentation).
*/
@Test
public void jsonRoundtrip() {
try {
AuthResponse res = new ObjectMapper().readValue(RES_JSON, AuthResponse.class);
assertThat("Parsed response is NULL", res, is(notNullValue()));
// Extract auth data.
AuthData data = res.getAuth();
assertThat("Auth data is NULL", data, is(notNullValue()));
assertThat("Incorrect auth accessor", data.getAccessor(), is(AUTH_ACCESSOR));
assertThat("Incorrect auth client token", data.getClientToken(), is(AUTH_CLIENT_TOKEN));
assertThat("Incorrect auth lease duration", data.getLeaseDuration(), is(AUTH_LEASE_DURATION));
assertThat("Incorrect auth renewable flag", data.isRenewable(), is(AUTH_RENEWABLE));
assertThat("Incorrect number of policies", data.getPolicies(), hasSize(2));
assertThat("Incorrect auth policies", data.getPolicies(), containsInAnyOrder(AUTH_POLICY_1, AUTH_POLICY_2));
} catch (IOException e) {
fail("AuthResponse deserialization failed: " + e.getMessage());
}
}
}

View File

@ -0,0 +1,117 @@
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 java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
/**
* JUnit Test for {@link SecretResponse} model.
*
* @author Stefan Kalscheuer
* @since 0.6.2
*/
public class SecretResponseTest {
private static final Map<String, Object> DATA = new HashMap<>();
private static final String KEY_UNKNOWN = "unknown";
private static final String KEY_STRING = "test1";
private static final String VAL_STRING = "testvalue";
private static final String KEY_INTEGER = "test2";
private static final Integer VAL_INTEGER = 42;
private static final String KEY_LIST = "list";
private static final String VAL_LIST = "[\"first\",\"second\"]";
private static final String SECRET_REQUEST_ID = "68315073-6658-e3ff-2da7-67939fb91bbd";
private static final String SECRET_LEASE_ID = "";
private static final Integer SECRET_LEASE_DURATION = 2764800;
private static final boolean SECRET_RENEWABLE = false;
private static final String SECRET_DATA_K1 = "excited";
private static final String SECRET_DATA_V1 = "yes";
private static final String SECRET_DATA_K2 = "value";
private static final String SECRET_DATA_V2 = "world";
private static final List<String> SECRET_WARNINGS = null;
private static final String SECRET_JSON = "{\n" +
" \"request_id\": \"" + SECRET_REQUEST_ID + "\",\n" +
" \"lease_id\": \"" + SECRET_LEASE_ID + "\",\n" +
" \"lease_duration\": " + SECRET_LEASE_DURATION + ",\n" +
" \"renewable\": " + SECRET_RENEWABLE + ",\n" +
" \"data\": {\n" +
" \"" + SECRET_DATA_K1 + "\": \"" + SECRET_DATA_V1 + "\",\n" +
" \"" + SECRET_DATA_K2 + "\": \"" + SECRET_DATA_V2 + "\"\n" +
" },\n" +
" \"warnings\": " + SECRET_WARNINGS + "\n" +
"}";
static {
DATA.put(KEY_STRING, VAL_STRING);
DATA.put(KEY_INTEGER, VAL_INTEGER);
DATA.put(KEY_LIST, VAL_LIST);
}
/**
* Test getter, setter and get-methods for response data.
*
* @throws InvalidResponseException Should not occur
*/
@Test
@SuppressWarnings("unchecked")
public void getDataRoundtrip() throws InvalidResponseException {
// Create empty Object.
SecretResponse res = new SecretResponse();
assertThat("Initial data should be Map", res.getData(), is(instanceOf(Map.class)));
assertThat("Initial data should be empty", res.getData().entrySet(), empty());
assertThat("Getter should return NULL on empty data map", res.get(KEY_STRING), is(nullValue()));
// Fill data map.
res.setData(DATA);
assertThat("Data setter/getter not transparent", res.getData(), is(DATA));
assertThat("Data size modified", res.getData().keySet(), hasSize(DATA.size()));
assertThat("Data keys not passed correctly", res.getData().keySet(), containsInAnyOrder(KEY_STRING, KEY_INTEGER, KEY_LIST));
assertThat("Data values not passed correctly", res.get(KEY_STRING), is(VAL_STRING));
assertThat("Data values not passed correctly", res.get(KEY_INTEGER), is(VAL_INTEGER));
assertThat("Non-Null returned on unknown key", res.get(KEY_UNKNOWN), is(nullValue()));
// Try explicit JSON conversion.
final List list = res.get(KEY_LIST, List.class);
assertThat("JSON parsing of list failed", list, is(notNullValue()));
assertThat("JSON parsing of list returned incorrect size", list.size(), is(2));
assertThat("JSON parsing of list returned incorrect elements", (List<Object>)list, contains("first", "second"));
assertThat("Non-Null returned on unknown key", res.get(KEY_UNKNOWN, Object.class), is(nullValue()));
// Requesting invalid class should result in Exception.
try {
res.get(KEY_LIST, Double.class);
fail("JSON parsing to incorrect type succeeded.");
} catch (Exception e) {
assertThat(e, is(instanceOf(InvalidResponseException.class)));
}
}
/**
* Test creation from JSON value as returned by Vault (JSON example copied from Vault documentation).
*/
@Test
public void jsonRoundtrip() {
try {
SecretResponse res = new ObjectMapper().readValue(SECRET_JSON, SecretResponse.class);
assertThat("Parsed response is NULL", res, is(notNullValue()));
assertThat("Incorrect lease ID", res.getLeaseId(), is(SECRET_LEASE_ID));
assertThat("Incorrect lease duration", res.getLeaseDuration(), is(SECRET_LEASE_DURATION));
assertThat("Incorrect renewable status", res.isRenewable(), is(SECRET_RENEWABLE));
assertThat("Incorrect warnings", res.getWarnings(), is(SECRET_WARNINGS));
assertThat("Response does not contain correct data", res.get(SECRET_DATA_K1), is(SECRET_DATA_V1));
assertThat("Response does not contain correct data", res.get(SECRET_DATA_K2), is(SECRET_DATA_V2));
} catch (IOException e) {
fail("SecretResponse deserialization failed: " + e.getMessage());
}
}
}

View File

@ -0,0 +1,107 @@
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 java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
/**
* JUnit Test for {@link TokenResponse} model.
*
* @author Stefan Kalscheuer
* @since 0.6.2
*/
public class TokenResponseTest {
private static final Integer TOKEN_CREATION_TIME = 1457533232;
private static final Integer TOKEN_TTL = 2764800;
private static final String TOKEN_DISPLAY_NAME = "token";
private static final Integer TOKEN_NUM_USES = 0;
private static final Boolean TOKEN_ORPHAN = false;
private static final String TOKEN_PATH = "auth/token/create";
private static final String TOKEN_POLICY_1 = "default";
private static final String TOKEN_POLICY_2 = "web";
private static final Boolean RES_RENEWABLE = false;
private static final Integer RES_TTL = 2591976;
private static final Integer RES_LEASE_DURATION = 0;
private static final String RES_JSON = "{\n" +
" \"lease_id\": \"\",\n" +
" \"renewable\": " + RES_RENEWABLE + ",\n" +
" \"lease_duration\": " + RES_LEASE_DURATION + ",\n" +
" \"data\": {\n" +
" \"creation_time\": " + TOKEN_CREATION_TIME + ",\n" +
" \"creation_ttl\": " + TOKEN_TTL + ",\n" +
" \"display_name\": \"" + TOKEN_DISPLAY_NAME + "\",\n" +
" \"meta\": null,\n" +
" \"num_uses\": " + TOKEN_NUM_USES + ",\n" +
" \"orphan\": " + TOKEN_ORPHAN + ",\n" +
" \"path\": \"" + TOKEN_PATH + "\",\n" +
" \"policies\": [\n" +
" \"" + TOKEN_POLICY_1 + "\", \n" +
" \"" + TOKEN_POLICY_2 + "\"\n" +
" ],\n" +
" \"ttl\": " + RES_TTL + "\n" +
" },\n" +
" \"warnings\": null,\n" +
" \"auth\": null\n" +
"}";
private static final Map<String, Object> INVALID_TOKEN_DATA = new HashMap<>();
static {
INVALID_TOKEN_DATA.put("num_uses", "fourtytwo");
}
/**
* Test getter, setter and get-methods for response data.
*/
@Test
public void getDataRoundtrip() {
// Create empty Object.
TokenResponse res = new TokenResponse();
assertThat("Initial data should be empty", res.getData(), is(nullValue()));
// Parsing invalid data map should fail.
try {
res.setData(INVALID_TOKEN_DATA);
fail("Parsing invalid token data succeeded");
} catch (Exception e) {
assertThat(e, is(instanceOf(InvalidResponseException.class)));
}
}
/**
* Test creation from JSON value as returned by Vault (JSON example copied from Vault documentation).
*/
@Test
public void jsonRoundtrip() {
try {
TokenResponse res = new ObjectMapper().readValue(RES_JSON, TokenResponse.class);
assertThat("Parsed response is NULL", res, is(notNullValue()));
assertThat("Incorrect lease duration", res.getLeaseDuration(), is(RES_LEASE_DURATION));
assertThat("Incorrect renewable status", res.isRenewable(), is(RES_RENEWABLE));
// Extract token data.
TokenData data = res.getData();
assertThat("Token data is NULL", data, is(notNullValue()));
assertThat("Incorrect token creation time", data.getCreationTime(), is(TOKEN_CREATION_TIME));
assertThat("Incorrect token creation TTL", data.getCreationTtl(), is(TOKEN_TTL));
assertThat("Incorrect token display name", data.getName(), is(TOKEN_DISPLAY_NAME));
assertThat("Incorrect token number of uses", data.getNumUses(), is(TOKEN_NUM_USES));
assertThat("Incorrect token orphan flag", data.isOrphan(), is(TOKEN_ORPHAN));
assertThat("Incorrect token path", data.getPath(), is(TOKEN_PATH));
assertThat("Incorrect response renewable flag", res.isRenewable(), is(RES_RENEWABLE));
assertThat("Incorrect response TTL", data.getTtl(), is(RES_TTL));
assertThat("Incorrect response lease duration", res.getLeaseDuration(), is(RES_LEASE_DURATION));
} catch (IOException e) {
fail("TokenResponse deserialization failed: " + e.getMessage());
}
}
}