All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.bettercloud.vault.api.Logical Maven / Gradle / Ivy

There is a newer version: 5.1.0
Show newest version
package com.bettercloud.vault.api;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.bettercloud.vault.VaultConfig;
import com.bettercloud.vault.VaultException;
import com.bettercloud.vault.json.Json;
import com.bettercloud.vault.json.JsonArray;
import com.bettercloud.vault.json.JsonObject;
import com.bettercloud.vault.json.JsonValue;
import com.bettercloud.vault.response.LogicalResponse;
import com.bettercloud.vault.rest.Rest;
import com.bettercloud.vault.rest.RestException;
import com.bettercloud.vault.rest.RestResponse;

/**
 * 

The implementing class for Vault's core/logical operations (e.g. read, write).

* *

This class is not intended to be constructed directly. Rather, it is meant to used by way of Vault * in a DSL-style builder pattern. See the Javadoc comments of each public method for usage examples.

*/ public class Logical { private final VaultConfig config; public Logical(final VaultConfig config) { this.config = config; } /** *

Basic read operation to retrieve a secret. A single secret key can map to multiple name-value pairs, * which can be retrieved from the response object. E.g.:

* *
*
{@code
     * final LogicalResponse response = vault.logical().read("secret/hello");
     *
     * final String value = response.getData().get("value");
     * final String otherValue = response.getData().get("other_value");
     * }
*
* * @param path The Vault key value from which to read (e.g. secret/hello) * @return * @throws VaultException If any errors occurs with the REST request (e.g. non-200 status code, invalid JSON payload, etc), and the maximum number of retries is exceeded. */ public LogicalResponse read(final String path) throws VaultException { int retryCount = 0; while (true) { try { // Make an HTTP request to Vault final RestResponse restResponse = new Rest()//NOPMD .url(config.getAddress() + "/v1/" + path) .header("X-Vault-Token", config.getToken()) .connectTimeoutSeconds(config.getOpenTimeout()) .readTimeoutSeconds(config.getReadTimeout()) .sslPemUTF8(config.getSslPemUTF8()) .sslVerification(config.isSslVerify() != null ? config.isSslVerify() : null) .get(); // Validate response if (restResponse.getStatus() != 200) { throw new VaultException("Vault responded with HTTP status code: " + restResponse.getStatus()); } final Map data = parseResponseData(restResponse); return new LogicalResponse(restResponse, retryCount, data); } catch (RuntimeException | VaultException | RestException e) { // If there are retries to perform, then pause for the configured interval and then execute the loop again... if (retryCount < config.getMaxRetries()) { retryCount++; try { final int retryIntervalMilliseconds = config.getRetryIntervalMilliseconds(); Thread.sleep(retryIntervalMilliseconds); } catch (InterruptedException e1) { e1.printStackTrace(); } } else { // ... otherwise, give up. throw new VaultException(e); } } } } /** *

Basic operation to store secrets. Multiple name value pairs can be stored under the same secret key. * E.g.:

* *
*
{@code
     * final Map nameValuePairs = new HashMap();
     * nameValuePairs.put("value", "foo");
     * nameValuePairs.put("other_value", "bar");
     *
     * final LogicalResponse response = vault.logical().write("secret/hello", nameValuePairs);
     * }
*
* * @param path The Vault key value to which to write (e.g. secret/hello) * @param nameValuePairs Secret name and value pairs to store under this Vault key * @throws VaultException */ public LogicalResponse write(final String path, final Map nameValuePairs) throws VaultException { int retryCount = 0; while (true) { try { JsonObject requestJson = Json.object(); for (final Map.Entry pair : nameValuePairs.entrySet()) { requestJson = requestJson.add(pair.getKey(), pair.getValue()); } final RestResponse restResponse = new Rest()//NOPMD .url(config.getAddress() + "/v1/" + path) .body(requestJson.toString().getBytes("UTF-8")) .header("X-Vault-Token", config.getToken()) .connectTimeoutSeconds(config.getOpenTimeout()) .readTimeoutSeconds(config.getReadTimeout()) .sslPemUTF8(config.getSslPemUTF8()) .sslVerification(config.isSslVerify() != null ? config.isSslVerify() : null) .post(); // HTTP Status should be either 200 (with content - e.g. PKI write) or 204 (no content) final int restStatus = restResponse.getStatus(); if (restStatus == 204) { return new LogicalResponse(restResponse, retryCount); } else if (restStatus == 200) { final Map data = parseResponseData(restResponse); return new LogicalResponse(restResponse, retryCount, data); } else { throw new VaultException("Expecting HTTP status 204 or 200, but instead receiving " + restStatus); } } catch (Exception e) { // If there are retries to perform, then pause for the configured interval and then execute the loop again... if (retryCount < config.getMaxRetries()) { retryCount++; try { final int retryIntervalMilliseconds = config.getRetryIntervalMilliseconds(); Thread.sleep(retryIntervalMilliseconds); } catch (InterruptedException e1) { e1.printStackTrace(); } } else { // ... otherwise, give up. throw new VaultException(e); } } } } /** /** *

Retrieve a list of keys corresponding to key/value pairs at a given Vault path.

* *

Key values ending with a trailing-slash characters are sub-paths. Running a subsequent list() * call, using the original path appended with this key, will retrieve all secret keys stored at that sub-path.

* *

This method returns only the secret keys, not values. To retrieve the actual stored value for a key, * use read() with the key appended onto the original base path.

* * @param path The Vault key value at which to look for secrets (e.g. secret) * @return * @throws VaultException */ public List list(final String path) throws VaultException { final String fullPath = path == null ? "list=true" : path + "?list=true"; final LogicalResponse response = read(fullPath); final List returnValues = new ArrayList<>(); if (response.getData() != null && response.getData().get("keys") != null) { final JsonArray keys = Json.parse(response.getData().get("keys")).asArray(); for (int index = 0; index < keys.size(); index++) { returnValues.add(keys.get(index).asString()); } } return returnValues; } /** *

Deletes the key/value pair located at the provided path.

* *

If the path represents a sub-path, then all of its contents must be deleted prior to deleting the empty * sub-path itself.

* * @param path The Vault key value to delete (e.g. secret/hello). * @return * @throws VaultException */ public LogicalResponse delete(final String path) throws VaultException { int retryCount = 0; while (true) { try { // Make an HTTP request to Vault final RestResponse restResponse = new Rest()//NOPMD .url(config.getAddress() + "/v1/" + path) .header("X-Vault-Token", config.getToken()) .connectTimeoutSeconds(config.getOpenTimeout()) .readTimeoutSeconds(config.getReadTimeout()) .sslPemUTF8(config.getSslPemUTF8()) .sslVerification(config.isSslVerify() != null ? config.isSslVerify() : null) .delete(); // Validate response if (restResponse.getStatus() != 204) { throw new VaultException("Vault responded with HTTP status code: " + restResponse.getStatus()); } return new LogicalResponse(restResponse, retryCount); } catch (RuntimeException | VaultException | RestException e) { // If there are retries to perform, then pause for the configured interval and then execute the loop again... if (retryCount < config.getMaxRetries()) { retryCount++; try { final int retryIntervalMilliseconds = config.getRetryIntervalMilliseconds(); Thread.sleep(retryIntervalMilliseconds); } catch (InterruptedException e1) { e1.printStackTrace(); } } else { // ... otherwise, give up. throw new VaultException(e); } } } } /** * This logic will move into the LogicalResponse constructor. * * @param restResponse * @return * @throws VaultException */ @Deprecated private Map parseResponseData(final RestResponse restResponse) throws VaultException { final String mimeType = restResponse.getMimeType() == null ? "null" : restResponse.getMimeType(); if (!mimeType.equals("application/json")) { throw new VaultException("Vault responded with MIME type: " + mimeType); } String jsonString; try { jsonString = new String(restResponse.getBody(), "UTF-8");//NOPMD } catch (UnsupportedEncodingException e) { throw new VaultException(e); } // Parse JSON final Map data = new HashMap();//NOPMD for (final JsonObject.Member member : Json.parse(jsonString).asObject().get("data").asObject()) { final JsonValue jsonValue = member.getValue(); if (jsonValue == null || jsonValue.isNull()) { continue; } else if (jsonValue.isString()) { data.put(member.getName(), jsonValue.asString()); } else { data.put(member.getName(), jsonValue.toString()); } } return data; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy