From 22a48d4a90f9479f2510d9fe28d79c9ccbffe096 Mon Sep 17 00:00:00 2001 From: Stefan Kalscheuer Date: Fri, 22 Mar 2019 10:03:49 +0100 Subject: [PATCH] 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. --- .../jvault/connector/HTTPVaultConnector.java | 269 +++++------------- .../connector/internal/RequestHelper.java | 82 +++++- 2 files changed, 151 insertions(+), 200 deletions(-) diff --git a/src/main/java/de/stklcode/jvault/connector/HTTPVaultConnector.java b/src/main/java/de/stklcode/jvault/connector/HTTPVaultConnector.java index bcad0b4..fdf81f2 100644 --- a/src/main/java/de/stklcode/jvault/connector/HTTPVaultConnector.java +++ b/src/main/java/de/stklcode/jvault/connector/HTTPVaultConnector.java @@ -16,7 +16,6 @@ package de.stklcode.jvault.connector; -import com.fasterxml.jackson.databind.ObjectMapper; import de.stklcode.jvault.connector.exception.AuthorizationRequiredException; import de.stklcode.jvault.connector.exception.InvalidRequestException; 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.embedded.AuthMethod; -import java.io.IOException; -import java.net.URISyntaxException; import java.security.cert.X509Certificate; import java.util.HashMap; import java.util.List; @@ -69,7 +66,6 @@ public class HTTPVaultConnector implements VaultConnector { public static final String DEFAULT_TLS_VERSION = "TLSv1.2"; - private final ObjectMapper jsonMapper; private final RequestHelper request; private boolean authorized = false; // Authorization status. @@ -219,7 +215,6 @@ public class HTTPVaultConnector implements VaultConnector { final Integer timeout, final String tlsVersion) { this.request = new RequestHelper(baseURL, numberOfRetries, timeout, tlsVersion, trustedCaCert); - this.jsonMapper = new ObjectMapper(); } @Override @@ -231,15 +226,7 @@ public class HTTPVaultConnector implements VaultConnector { @Override public final SealResponse sealStatus() throws VaultConnectorException { - try { - 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); - } + return request.get(PATH_SEAL_STATUS, new HashMap<>(), token, SealResponse.class); } @Override @@ -254,12 +241,8 @@ public class HTTPVaultConnector implements VaultConnector { if (reset != null) { param.put("reset", reset.toString()); } - try { - String response = request.put(PATH_UNSEAL, param, token); - return jsonMapper.readValue(response, SealResponse.class); - } catch (IOException e) { - throw new InvalidResponseException(Error.PARSE_RESPONSE, e); - } + + return request.put(PATH_UNSEAL, param, token, SealResponse.class); } @Override @@ -269,16 +252,8 @@ public class HTTPVaultConnector implements VaultConnector { param.put("standbycode", "200"); // Default: 429. param.put("sealedcode", "200"); // Default: 503. param.put("uninitcode", "200"); // Default: 501. - try { - String response = request.get(PATH_HEALTH, param, token); - /* 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); - } + + return request.get(PATH_HEALTH, param, token, HealthResponse.class); } @Override @@ -288,17 +263,10 @@ public class HTTPVaultConnector implements VaultConnector { @Override public final List getAuthBackends() throws VaultConnectorException { - try { - String response = request.get(PATH_AUTH, new HashMap<>(), token); - /* Parse response */ - AuthMethodsResponse amr = jsonMapper.readValue(response, AuthMethodsResponse.class); - 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); - } + /* Issue request and parse response */ + AuthMethodsResponse amr = request.get(PATH_AUTH, new HashMap<>(), token, AuthMethodsResponse.class); + + return amr.getSupportedMethods().values().stream().map(AuthMethod::getType).collect(Collectors.toList()); } @Override @@ -306,14 +274,10 @@ public class HTTPVaultConnector implements VaultConnector { /* set token */ this.token = token; this.tokenTTL = 0; - try { - String response = request.post(PATH_TOKEN + PATH_LOOKUP, new HashMap<>(), token); - TokenResponse res = jsonMapper.readValue(response, TokenResponse.class); - authorized = true; - return res; - } catch (IOException e) { - throw new InvalidResponseException(Error.PARSE_RESPONSE, e); - } + TokenResponse res = request.post(PATH_TOKEN + PATH_LOOKUP, new HashMap<>(), token, TokenResponse.class); + authorized = true; + + return res; } @Override @@ -353,19 +317,14 @@ public class HTTPVaultConnector implements VaultConnector { */ private AuthResponse queryAuth(final String path, final Map payload) throws VaultConnectorException { - try { - /* Get response */ - String response = request.post(path, payload, token); - /* Parse response */ - AuthResponse auth = jsonMapper.readValue(response, AuthResponse.class); - /* verify response */ - this.token = auth.getAuth().getClientToken(); - this.tokenTTL = System.currentTimeMillis() + auth.getAuth().getLeaseDuration() * 1000L; - this.authorized = true; - return auth; - } catch (IOException e) { - throw new InvalidResponseException(Error.PARSE_RESPONSE, e); - } + /* Issue request and parse response */ + AuthResponse auth = request.post(path, payload, token, AuthResponse.class); + /* verify response */ + this.token = auth.getAuth().getClientToken(); + this.tokenTTL = System.currentTimeMillis() + auth.getAuth().getLeaseDuration() * 1000L; + this.authorized = true; + + return auth; } @Override @@ -418,15 +377,7 @@ public class HTTPVaultConnector implements VaultConnector { public final AppRoleResponse lookupAppRole(final String roleName) throws VaultConnectorException { requireAuth(); /* Request HTTP response and parse Secret */ - try { - 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); - } + return request.get(String.format(PATH_AUTH_APPROLE_ROLE, roleName, ""), new HashMap<>(), token, AppRoleResponse.class); } @Override @@ -447,18 +398,13 @@ public class HTTPVaultConnector implements VaultConnector { @Override public final String getAppRoleID(final String roleName) throws VaultConnectorException { requireAuth(); - /* Request HTTP response and parse Secret */ - try { - String response = request.get(String.format(PATH_AUTH_APPROLE_ROLE, roleName, "/role-id"), - new HashMap<>(), - token); - return jsonMapper.readValue(response, RawDataResponse.class).getData().get("role_id").toString(); - } 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); - } + /* Issue request, parse response and extract Role ID */ + return request.get( + String.format(PATH_AUTH_APPROLE_ROLE, roleName, "/role-id"), + new HashMap<>(), + token, + RawDataResponse.class + ).getData().get("role_id").toString(); } @Override @@ -479,21 +425,20 @@ public class HTTPVaultConnector implements VaultConnector { public final AppRoleSecretResponse createAppRoleSecret(final String roleName, final AppRoleSecret secret) throws VaultConnectorException { 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 { - /* Extract the secret ID from response */ - return jsonMapper.readValue(response, AppRoleSecretResponse.class); - } catch (IOException e) { - throw new InvalidResponseException(Error.PARSE_RESPONSE); + if (secret.getId() != null && !secret.getId().isEmpty()) { + return request.post( + String.format(PATH_AUTH_APPROLE_ROLE, roleName, "/custom-secret-id"), + secret, + 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) throws VaultConnectorException { requireAuth(); - /* Request HTTP response and parse Secret */ - try { - String response = request.post( - String.format(PATH_AUTH_APPROLE_ROLE, roleName, "/secret-id/lookup"), - new AppRoleSecret(secretID), - token); - return jsonMapper.readValue(response, AppRoleSecretResponse.class); - } catch (IOException e) { - throw new InvalidResponseException(Error.PARSE_RESPONSE, e); - } + + /* Issue request and parse secret response */ + return request.post( + String.format(PATH_AUTH_APPROLE_ROLE, roleName, "/secret-id/lookup"), + new AppRoleSecret(secretID), + token, + AppRoleSecretResponse.class + ); } @Override @@ -536,100 +479,58 @@ public class HTTPVaultConnector implements VaultConnector { public final List listAppRoles() throws VaultConnectorException { requireAuth(); - try { - String response = request.get(PATH_AUTH_APPROLE + "role?list=true", new HashMap<>(), token); - 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); - } + SecretListResponse secrets = request.get(PATH_AUTH_APPROLE + "role?list=true", new HashMap<>(), token, SecretListResponse.class); + return secrets.getKeys(); } @Override public final List listAppRoleSecrets(final String roleName) throws VaultConnectorException { requireAuth(); - try { - String response = request.get( - String.format(PATH_AUTH_APPROLE_ROLE, roleName, "/secret-id?list=true"), - new HashMap<>(), - token); - 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); - } + SecretListResponse secrets = request.get( + String.format(PATH_AUTH_APPROLE_ROLE, roleName, "/secret-id?list=true"), + new HashMap<>(), + token, + SecretListResponse.class + ); + + return secrets.getKeys(); } @Override public final SecretResponse read(final String key) throws VaultConnectorException { requireAuth(); - /* Request HTTP response and parse Secret */ - try { - 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); - } + /* Issue request and parse secret response */ + return request.get(key, new HashMap<>(), token, SecretResponse.class); } @Override public final SecretResponse readSecretVersion(final String mount, final String key, final Integer version) throws VaultConnectorException { requireAuth(); /* Request HTTP response and parse secret metadata */ - try { - Map args = new HashMap<>(); - if (version != null) { - 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); + Map args = new HashMap<>(); + if (version != null) { + args.put("version", version.toString()); } + + return request.get(mount + PATH_DATA + key, args, token, SecretResponse.class); } @Override public final MetadataResponse readSecretMetadata(final String mount, final String key) throws VaultConnectorException { requireAuth(); + /* Request HTTP response and parse secret metadata */ - try { - 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); - } + return request.get(mount + PATH_METADATA + key, new HashMap<>(), token, MetadataResponse.class); } @Override public final List list(final String path) throws VaultConnectorException { requireAuth(); - try { - String response = request.get(path + "/?list=true", new HashMap<>(), token); - 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); - } + SecretListResponse secrets = request.get(path + "/?list=true", new HashMap<>(), token, SecretListResponse.class); + + return secrets.getKeys(); } @Override @@ -741,13 +642,8 @@ public class HTTPVaultConnector implements VaultConnector { payload.put("increment", increment.toString()); } - /* Request HTTP response and parse Secret */ - try { - String response = request.put(PATH_RENEW, payload, token); - return jsonMapper.readValue(response, SecretResponse.class); - } catch (IOException e) { - throw new InvalidResponseException(Error.PARSE_RESPONSE, e); - } + /* Issue request and parse secret response */ + return request.put(PATH_RENEW, payload, token, SecretResponse.class); } @Override @@ -791,12 +687,7 @@ public class HTTPVaultConnector implements VaultConnector { throw new InvalidRequestException("Token must be provided."); } - String response = request.post(path, token, this.token); - try { - return jsonMapper.readValue(response, AuthResponse.class); - } catch (IOException e) { - throw new InvalidResponseException(Error.PARSE_RESPONSE, e); - } + return request.post(path, token, this.token, AuthResponse.class); } @Override @@ -804,15 +695,7 @@ public class HTTPVaultConnector implements VaultConnector { requireAuth(); /* Request HTTP response and parse Secret */ - try { - 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); - } + return request.get(PATH_TOKEN + "/lookup/" + token, new HashMap<>(), token, TokenResponse.class); } /** diff --git a/src/main/java/de/stklcode/jvault/connector/internal/RequestHelper.java b/src/main/java/de/stklcode/jvault/connector/internal/RequestHelper.java index a344356..778e933 100644 --- a/src/main/java/de/stklcode/jvault/connector/internal/RequestHelper.java +++ b/src/main/java/de/stklcode/jvault/connector/internal/RequestHelper.java @@ -96,6 +96,27 @@ public final class RequestHelper implements Serializable { 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 post(final String path, final Object payload, final String token, final Class 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. * @@ -129,6 +150,27 @@ public final class RequestHelper implements Serializable { 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 put(final String path, final Map payload, final String token, final Class 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. * @@ -158,17 +200,22 @@ public final class RequestHelper implements Serializable { * @param token Vault token (may be {@code null}). * @return HTTP response * @throws VaultConnectorException on connection error - * @throws URISyntaxException on invalid URI syntax * @since 0.8 Added {@code token} parameter. */ public String get(final String path, final Map payload, final String token) - throws VaultConnectorException, URISyntaxException { - /* Add parameters to URI */ - URIBuilder uriBuilder = new URIBuilder(baseURL + path); - payload.forEach(uriBuilder::addParameter); + throws VaultConnectorException { + HttpGet get; + try { + /* Add parameters to URI */ + URIBuilder uriBuilder = new URIBuilder(baseURL + path); + payload.forEach(uriBuilder::addParameter); - /* Initialize request */ - HttpGet get = new HttpGet(uriBuilder.build()); + /* Initialize request */ + 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 */ if (token != null) { @@ -178,6 +225,27 @@ public final class RequestHelper implements Serializable { 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 get(final String path, final Map payload, final String token, final Class 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. *