Move parsing of JSON response into RequestHelper

The pattern is always the same, issue request, parse String response
into target class and catch exceptions. Bundle these blocks in the
helper class for GET, POST and PUT methods to reduce overhead in the
actual connector.
This commit is contained in:
Stefan Kalscheuer 2019-03-22 10:03:49 +01:00
parent 7020d3701c
commit 22a48d4a90
2 changed files with 151 additions and 200 deletions

View File

@ -16,7 +16,6 @@
package de.stklcode.jvault.connector; package de.stklcode.jvault.connector;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.AuthorizationRequiredException; import de.stklcode.jvault.connector.exception.AuthorizationRequiredException;
import de.stklcode.jvault.connector.exception.InvalidRequestException; import de.stklcode.jvault.connector.exception.InvalidRequestException;
import de.stklcode.jvault.connector.exception.InvalidResponseException; import de.stklcode.jvault.connector.exception.InvalidResponseException;
@ -30,8 +29,6 @@ import de.stklcode.jvault.connector.model.Token;
import de.stklcode.jvault.connector.model.response.*; import de.stklcode.jvault.connector.model.response.*;
import de.stklcode.jvault.connector.model.response.embedded.AuthMethod; import de.stklcode.jvault.connector.model.response.embedded.AuthMethod;
import java.io.IOException;
import java.net.URISyntaxException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -69,7 +66,6 @@ public class HTTPVaultConnector implements VaultConnector {
public static final String DEFAULT_TLS_VERSION = "TLSv1.2"; public static final String DEFAULT_TLS_VERSION = "TLSv1.2";
private final ObjectMapper jsonMapper;
private final RequestHelper request; private final RequestHelper request;
private boolean authorized = false; // Authorization status. private boolean authorized = false; // Authorization status.
@ -219,7 +215,6 @@ public class HTTPVaultConnector implements VaultConnector {
final Integer timeout, final Integer timeout,
final String tlsVersion) { final String tlsVersion) {
this.request = new RequestHelper(baseURL, numberOfRetries, timeout, tlsVersion, trustedCaCert); this.request = new RequestHelper(baseURL, numberOfRetries, timeout, tlsVersion, trustedCaCert);
this.jsonMapper = new ObjectMapper();
} }
@Override @Override
@ -231,15 +226,7 @@ public class HTTPVaultConnector implements VaultConnector {
@Override @Override
public final SealResponse sealStatus() throws VaultConnectorException { public final SealResponse sealStatus() throws VaultConnectorException {
try { return request.get(PATH_SEAL_STATUS, new HashMap<>(), token, SealResponse.class);
String response = request.get(PATH_SEAL_STATUS, new HashMap<>(), token);
return jsonMapper.readValue(response, SealResponse.class);
} catch (IOException e) {
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
} catch (URISyntaxException ignored) {
/* this should never occur and may leak sensible information */
throw new InvalidRequestException(Error.URI_FORMAT);
}
} }
@Override @Override
@ -254,12 +241,8 @@ public class HTTPVaultConnector implements VaultConnector {
if (reset != null) { if (reset != null) {
param.put("reset", reset.toString()); param.put("reset", reset.toString());
} }
try {
String response = request.put(PATH_UNSEAL, param, token); return request.put(PATH_UNSEAL, param, token, SealResponse.class);
return jsonMapper.readValue(response, SealResponse.class);
} catch (IOException e) {
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
}
} }
@Override @Override
@ -269,16 +252,8 @@ public class HTTPVaultConnector implements VaultConnector {
param.put("standbycode", "200"); // Default: 429. param.put("standbycode", "200"); // Default: 429.
param.put("sealedcode", "200"); // Default: 503. param.put("sealedcode", "200"); // Default: 503.
param.put("uninitcode", "200"); // Default: 501. param.put("uninitcode", "200"); // Default: 501.
try {
String response = request.get(PATH_HEALTH, param, token); return request.get(PATH_HEALTH, param, token, HealthResponse.class);
/* 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);
}
} }
@Override @Override
@ -288,17 +263,10 @@ public class HTTPVaultConnector implements VaultConnector {
@Override @Override
public final List<AuthBackend> getAuthBackends() throws VaultConnectorException { public final List<AuthBackend> getAuthBackends() throws VaultConnectorException {
try { /* Issue request and parse response */
String response = request.get(PATH_AUTH, new HashMap<>(), token); AuthMethodsResponse amr = request.get(PATH_AUTH, new HashMap<>(), token, AuthMethodsResponse.class);
/* Parse response */
AuthMethodsResponse amr = jsonMapper.readValue(response, AuthMethodsResponse.class); return amr.getSupportedMethods().values().stream().map(AuthMethod::getType).collect(Collectors.toList());
return amr.getSupportedMethods().values().stream().map(AuthMethod::getType).collect(Collectors.toList());
} catch (IOException e) {
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
} catch (URISyntaxException ignored) {
/* this should never occur and may leak sensible information */
throw new InvalidRequestException(Error.URI_FORMAT);
}
} }
@Override @Override
@ -306,14 +274,10 @@ public class HTTPVaultConnector implements VaultConnector {
/* set token */ /* set token */
this.token = token; this.token = token;
this.tokenTTL = 0; this.tokenTTL = 0;
try { TokenResponse res = request.post(PATH_TOKEN + PATH_LOOKUP, new HashMap<>(), token, TokenResponse.class);
String response = request.post(PATH_TOKEN + PATH_LOOKUP, new HashMap<>(), token); authorized = true;
TokenResponse res = jsonMapper.readValue(response, TokenResponse.class);
authorized = true; return res;
return res;
} catch (IOException e) {
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
}
} }
@Override @Override
@ -353,19 +317,14 @@ public class HTTPVaultConnector implements VaultConnector {
*/ */
private AuthResponse queryAuth(final String path, final Map<String, String> payload) private AuthResponse queryAuth(final String path, final Map<String, String> payload)
throws VaultConnectorException { throws VaultConnectorException {
try { /* Issue request and parse response */
/* Get response */ AuthResponse auth = request.post(path, payload, token, AuthResponse.class);
String response = request.post(path, payload, token); /* verify response */
/* Parse response */ this.token = auth.getAuth().getClientToken();
AuthResponse auth = jsonMapper.readValue(response, AuthResponse.class); this.tokenTTL = System.currentTimeMillis() + auth.getAuth().getLeaseDuration() * 1000L;
/* verify response */ this.authorized = true;
this.token = auth.getAuth().getClientToken();
this.tokenTTL = System.currentTimeMillis() + auth.getAuth().getLeaseDuration() * 1000L; return auth;
this.authorized = true;
return auth;
} catch (IOException e) {
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
}
} }
@Override @Override
@ -418,15 +377,7 @@ public class HTTPVaultConnector implements VaultConnector {
public final AppRoleResponse lookupAppRole(final String roleName) throws VaultConnectorException { public final AppRoleResponse lookupAppRole(final String roleName) throws VaultConnectorException {
requireAuth(); requireAuth();
/* Request HTTP response and parse Secret */ /* Request HTTP response and parse Secret */
try { return request.get(String.format(PATH_AUTH_APPROLE_ROLE, roleName, ""), new HashMap<>(), token, AppRoleResponse.class);
String response = request.get(String.format(PATH_AUTH_APPROLE_ROLE, roleName, ""), new HashMap<>(), token);
return jsonMapper.readValue(response, AppRoleResponse.class);
} catch (IOException e) {
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
} catch (URISyntaxException ignored) {
/* this should never occur and may leak sensible information */
throw new InvalidRequestException(Error.URI_FORMAT);
}
} }
@Override @Override
@ -447,18 +398,13 @@ public class HTTPVaultConnector implements VaultConnector {
@Override @Override
public final String getAppRoleID(final String roleName) throws VaultConnectorException { public final String getAppRoleID(final String roleName) throws VaultConnectorException {
requireAuth(); requireAuth();
/* Request HTTP response and parse Secret */ /* Issue request, parse response and extract Role ID */
try { return request.get(
String response = request.get(String.format(PATH_AUTH_APPROLE_ROLE, roleName, "/role-id"), String.format(PATH_AUTH_APPROLE_ROLE, roleName, "/role-id"),
new HashMap<>(), new HashMap<>(),
token); token,
return jsonMapper.readValue(response, RawDataResponse.class).getData().get("role_id").toString(); RawDataResponse.class
} catch (IOException e) { ).getData().get("role_id").toString();
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
} catch (URISyntaxException ignored) {
/* this should never occur and may leak sensible information */
throw new InvalidRequestException(Error.URI_FORMAT);
}
} }
@Override @Override
@ -479,21 +425,20 @@ public class HTTPVaultConnector implements VaultConnector {
public final AppRoleSecretResponse createAppRoleSecret(final String roleName, final AppRoleSecret secret) public final AppRoleSecretResponse createAppRoleSecret(final String roleName, final AppRoleSecret secret)
throws VaultConnectorException { throws VaultConnectorException {
requireAuth(); requireAuth();
/* Get response */
String response;
if (secret.getId() != null && !secret.getId().isEmpty()) {
response = request.post(String.format(PATH_AUTH_APPROLE_ROLE, roleName, "/custom-secret-id"),
secret,
token);
} else {
response = request.post(String.format(PATH_AUTH_APPROLE_ROLE, roleName, "/secret-id"), secret, token);
}
try { if (secret.getId() != null && !secret.getId().isEmpty()) {
/* Extract the secret ID from response */ return request.post(
return jsonMapper.readValue(response, AppRoleSecretResponse.class); String.format(PATH_AUTH_APPROLE_ROLE, roleName, "/custom-secret-id"),
} catch (IOException e) { secret,
throw new InvalidResponseException(Error.PARSE_RESPONSE); token,
AppRoleSecretResponse.class
);
} else {
return request.post(
String.format(PATH_AUTH_APPROLE_ROLE, roleName, "/secret-id"),
secret, token,
AppRoleSecretResponse.class
);
} }
} }
@ -501,16 +446,14 @@ public class HTTPVaultConnector implements VaultConnector {
public final AppRoleSecretResponse lookupAppRoleSecret(final String roleName, final String secretID) public final AppRoleSecretResponse lookupAppRoleSecret(final String roleName, final String secretID)
throws VaultConnectorException { throws VaultConnectorException {
requireAuth(); requireAuth();
/* Request HTTP response and parse Secret */
try { /* Issue request and parse secret response */
String response = request.post( return request.post(
String.format(PATH_AUTH_APPROLE_ROLE, roleName, "/secret-id/lookup"), String.format(PATH_AUTH_APPROLE_ROLE, roleName, "/secret-id/lookup"),
new AppRoleSecret(secretID), new AppRoleSecret(secretID),
token); token,
return jsonMapper.readValue(response, AppRoleSecretResponse.class); AppRoleSecretResponse.class
} catch (IOException e) { );
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
}
} }
@Override @Override
@ -536,100 +479,58 @@ public class HTTPVaultConnector implements VaultConnector {
public final List<String> listAppRoles() throws VaultConnectorException { public final List<String> listAppRoles() throws VaultConnectorException {
requireAuth(); requireAuth();
try { SecretListResponse secrets = request.get(PATH_AUTH_APPROLE + "role?list=true", new HashMap<>(), token, SecretListResponse.class);
String response = request.get(PATH_AUTH_APPROLE + "role?list=true", new HashMap<>(), token); return secrets.getKeys();
SecretListResponse secrets = jsonMapper.readValue(response, SecretListResponse.class);
return secrets.getKeys();
} catch (IOException e) {
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
} catch (URISyntaxException ignored) {
/* this should never occur and may leak sensible information */
throw new InvalidRequestException(Error.URI_FORMAT);
}
} }
@Override @Override
public final List<String> listAppRoleSecrets(final String roleName) throws VaultConnectorException { public final List<String> listAppRoleSecrets(final String roleName) throws VaultConnectorException {
requireAuth(); requireAuth();
try { SecretListResponse secrets = request.get(
String response = request.get( String.format(PATH_AUTH_APPROLE_ROLE, roleName, "/secret-id?list=true"),
String.format(PATH_AUTH_APPROLE_ROLE, roleName, "/secret-id?list=true"), new HashMap<>(),
new HashMap<>(), token,
token); SecretListResponse.class
SecretListResponse secrets = jsonMapper.readValue(response, SecretListResponse.class); );
return secrets.getKeys();
} catch (IOException e) { return secrets.getKeys();
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
} catch (URISyntaxException ignored) {
/* this should never occur and may leak sensible information */
throw new InvalidRequestException(Error.URI_FORMAT);
}
} }
@Override @Override
public final SecretResponse read(final String key) throws VaultConnectorException { public final SecretResponse read(final String key) throws VaultConnectorException {
requireAuth(); requireAuth();
/* Request HTTP response and parse Secret */ /* Issue request and parse secret response */
try { return request.get(key, new HashMap<>(), token, SecretResponse.class);
String response = request.get(key, new HashMap<>(), token);
return jsonMapper.readValue(response, SecretResponse.class);
} catch (IOException e) {
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
} catch (URISyntaxException ignored) {
/* this should never occur and may leak sensible information */
throw new InvalidRequestException(Error.URI_FORMAT);
}
} }
@Override @Override
public final SecretResponse readSecretVersion(final String mount, final String key, final Integer version) throws VaultConnectorException { public final SecretResponse readSecretVersion(final String mount, final String key, final Integer version) throws VaultConnectorException {
requireAuth(); requireAuth();
/* Request HTTP response and parse secret metadata */ /* Request HTTP response and parse secret metadata */
try { Map<String, String> args = new HashMap<>();
Map<String, String> args = new HashMap<>(); if (version != null) {
if (version != null) { args.put("version", version.toString());
args.put("version", version.toString());
}
String response = request.get(mount + PATH_DATA + key, args, token);
return jsonMapper.readValue(response, SecretResponse.class);
} catch (IOException e) {
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
} catch (URISyntaxException ignored) {
/* this should never occur and may leak sensible information */
throw new InvalidRequestException(Error.URI_FORMAT);
} }
return request.get(mount + PATH_DATA + key, args, token, SecretResponse.class);
} }
@Override @Override
public final MetadataResponse readSecretMetadata(final String mount, final String key) throws VaultConnectorException { public final MetadataResponse readSecretMetadata(final String mount, final String key) throws VaultConnectorException {
requireAuth(); requireAuth();
/* Request HTTP response and parse secret metadata */ /* Request HTTP response and parse secret metadata */
try { return request.get(mount + PATH_METADATA + key, new HashMap<>(), token, MetadataResponse.class);
String response = request.get(mount + PATH_METADATA + key, new HashMap<>(), token);
return jsonMapper.readValue(response, MetadataResponse.class);
} catch (IOException e) {
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
} catch (URISyntaxException ignored) {
/* this should never occur and may leak sensible information */
throw new InvalidRequestException(Error.URI_FORMAT);
}
} }
@Override @Override
public final List<String> list(final String path) throws VaultConnectorException { public final List<String> list(final String path) throws VaultConnectorException {
requireAuth(); requireAuth();
try { SecretListResponse secrets = request.get(path + "/?list=true", new HashMap<>(), token, SecretListResponse.class);
String response = request.get(path + "/?list=true", new HashMap<>(), token);
SecretListResponse secrets = jsonMapper.readValue(response, SecretListResponse.class); return secrets.getKeys();
return secrets.getKeys();
} catch (IOException e) {
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
} catch (URISyntaxException ignored) {
/* this should never occur and may leak sensible information */
throw new InvalidRequestException(Error.URI_FORMAT);
}
} }
@Override @Override
@ -741,13 +642,8 @@ public class HTTPVaultConnector implements VaultConnector {
payload.put("increment", increment.toString()); payload.put("increment", increment.toString());
} }
/* Request HTTP response and parse Secret */ /* Issue request and parse secret response */
try { return request.put(PATH_RENEW, payload, token, SecretResponse.class);
String response = request.put(PATH_RENEW, payload, token);
return jsonMapper.readValue(response, SecretResponse.class);
} catch (IOException e) {
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
}
} }
@Override @Override
@ -791,12 +687,7 @@ public class HTTPVaultConnector implements VaultConnector {
throw new InvalidRequestException("Token must be provided."); throw new InvalidRequestException("Token must be provided.");
} }
String response = request.post(path, token, this.token); return request.post(path, token, this.token, AuthResponse.class);
try {
return jsonMapper.readValue(response, AuthResponse.class);
} catch (IOException e) {
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
}
} }
@Override @Override
@ -804,15 +695,7 @@ public class HTTPVaultConnector implements VaultConnector {
requireAuth(); requireAuth();
/* Request HTTP response and parse Secret */ /* Request HTTP response and parse Secret */
try { return request.get(PATH_TOKEN + "/lookup/" + token, new HashMap<>(), token, TokenResponse.class);
String response = request.get(PATH_TOKEN + "/lookup/" + token, new HashMap<>(), token);
return jsonMapper.readValue(response, TokenResponse.class);
} catch (IOException e) {
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
} catch (URISyntaxException ignored) {
/* this should never occur and may leak sensible information */
throw new InvalidRequestException(Error.URI_FORMAT);
}
} }
/** /**

View File

@ -96,6 +96,27 @@ public final class RequestHelper implements Serializable {
return request(post, retries); return request(post, retries);
} }
/**
* Execute HTTP request using POST method and parse JSON result.
*
* @param path URL path (relative to base).
* @param payload Map of payload values (will be converted to JSON).
* @param token Vault token (may be {@code null}).
* @param target Target class.
* @return HTTP response
* @throws VaultConnectorException on connection error
* @since 0.8
*/
public <T> T post(final String path, final Object payload, final String token, final Class<T> target)
throws VaultConnectorException {
try {
String response = post(path, payload, token);
return jsonMapper.readValue(response, target);
} catch (IOException e) {
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
}
}
/** /**
* Execute HTTP request using PUT method. * Execute HTTP request using PUT method.
* *
@ -129,6 +150,27 @@ public final class RequestHelper implements Serializable {
return request(put, retries); return request(put, retries);
} }
/**
* Execute HTTP request using PUT method and parse JSON result.
*
* @param path URL path (relative to base).
* @param payload Map of payload values (will be converted to JSON).
* @param token Vault token (may be {@code null}).
* @param target Target class.
* @return HTTP response
* @throws VaultConnectorException on connection error
* @since 0.8
*/
public <T> T put(final String path, final Map<String, String> payload, final String token, final Class<T> target)
throws VaultConnectorException {
try {
String response = put(path, payload, token);
return jsonMapper.readValue(response, target);
} catch (IOException e) {
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
}
}
/** /**
* Execute HTTP request using DELETE method. * Execute HTTP request using DELETE method.
* *
@ -158,17 +200,22 @@ public final class RequestHelper implements Serializable {
* @param token Vault token (may be {@code null}). * @param token Vault token (may be {@code null}).
* @return HTTP response * @return HTTP response
* @throws VaultConnectorException on connection error * @throws VaultConnectorException on connection error
* @throws URISyntaxException on invalid URI syntax
* @since 0.8 Added {@code token} parameter. * @since 0.8 Added {@code token} parameter.
*/ */
public String get(final String path, final Map<String, String> payload, final String token) public String get(final String path, final Map<String, String> payload, final String token)
throws VaultConnectorException, URISyntaxException { throws VaultConnectorException {
/* Add parameters to URI */ HttpGet get;
URIBuilder uriBuilder = new URIBuilder(baseURL + path); try {
payload.forEach(uriBuilder::addParameter); /* Add parameters to URI */
URIBuilder uriBuilder = new URIBuilder(baseURL + path);
payload.forEach(uriBuilder::addParameter);
/* Initialize request */ /* Initialize request */
HttpGet get = new HttpGet(uriBuilder.build()); get = new HttpGet(uriBuilder.build());
} catch (URISyntaxException e) {
/* this should never occur and may leak sensible information */
throw new InvalidRequestException(Error.URI_FORMAT);
}
/* Set X-Vault-Token header */ /* Set X-Vault-Token header */
if (token != null) { if (token != null) {
@ -178,6 +225,27 @@ public final class RequestHelper implements Serializable {
return request(get, retries); return request(get, retries);
} }
/**
* Execute HTTP request using GET method and parse JSON result to target class.
*
* @param path URL path (relative to base).
* @param payload Map of payload values (will be converted to JSON).
* @param token Vault token (may be {@code null}).
* @param target Target class.
* @return HTTP response
* @throws VaultConnectorException on connection error
* @since 0.8
*/
public <T> T get(final String path, final Map<String, String> payload, final String token, final Class<T> target)
throws VaultConnectorException {
try {
String response = get(path, payload, token);
return jsonMapper.readValue(response, target);
} catch (IOException e) {
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
}
}
/** /**
* Execute prepared HTTP request and return result. * Execute prepared HTTP request and return result.
* *