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

io.quarkus.vault.runtime.client.VertxVaultClient Maven / Gradle / Ivy

There is a newer version: 3.0.0.Beta1
Show newest version
package io.quarkus.vault.runtime.client;

import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
import static io.quarkus.vault.runtime.client.MutinyVertxClientFactory.createHttpClient;
import static java.util.Collections.emptyMap;

import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import javax.annotation.PreDestroy;
import javax.inject.Singleton;

import org.jboss.logging.Logger;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import io.quarkus.runtime.TlsConfig;
import io.quarkus.vault.VaultException;
import io.quarkus.vault.runtime.VaultConfigHolder;
import io.quarkus.vault.runtime.config.VaultBootstrapConfig;
import io.smallrye.mutiny.Uni;
import io.vertx.core.http.HttpMethod;
import io.vertx.mutiny.core.Vertx;
import io.vertx.mutiny.core.buffer.Buffer;
import io.vertx.mutiny.ext.web.client.HttpRequest;
import io.vertx.mutiny.ext.web.client.HttpResponse;
import io.vertx.mutiny.ext.web.client.WebClient;

@Singleton
public class VertxVaultClient implements VaultClient {

    private static final Logger log = Logger.getLogger(VertxVaultClient.class);

    private static final List ROOT_NAMESPACE_API = Arrays.asList("sys/init", "sys/license", "sys/leader", "sys/health",
            "sys/metrics", "sys/config/state", "sys/host-info", "sys/key-status", "sys/storage", "sys/storage/raft");

    private final Vertx vertx;
    private URL baseUrl;
    private final TlsConfig tlsConfig;
    private final VaultConfigHolder vaultConfigHolder;
    private WebClient webClient;

    ObjectMapper mapper = new ObjectMapper();

    public VertxVaultClient(VaultConfigHolder vaultConfigHolder, TlsConfig tlsConfig) {
        this.vaultConfigHolder = vaultConfigHolder;
        this.tlsConfig = tlsConfig;
        this.mapper.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);
        this.mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        this.vertx = Vertx.vertx();
    }

    public void init() {
        VaultBootstrapConfig config = vaultConfigHolder.getVaultBootstrapConfig();
        this.webClient = createHttpClient(vertx, config, tlsConfig);
        this.baseUrl = config.url.orElseThrow(() -> new VaultException("no vault url provided"));
    }

    @PreDestroy
    @Override
    public void close() {
        try {
            if (webClient != null) {
                webClient.close();
            }
        } finally {
            vertx.close();
        }
    }

    // ---

    public  T put(String path, String token, Object body, int expectedCode) {
        HttpRequest request = builder(path, token).method(HttpMethod.PUT);
        return exec(request, body, null, expectedCode);
    }

    public  T list(String path, String token, Class resultClass) {
        HttpRequest request = builder(path, token).rawMethod("LIST");
        return exec(request, resultClass);
    }

    public  T delete(String path, String token, int expectedCode) {
        HttpRequest request = builder(path, token).method(HttpMethod.DELETE);
        return exec(request, expectedCode);
    }

    public  T post(String path, String token, Object body, Class resultClass, int expectedCode) {
        HttpRequest request = builder(path, token).method(HttpMethod.POST);
        return exec(request, body, resultClass, expectedCode);
    }

    public  T post(String path, String token, Object body, Class resultClass) {
        return post(path, token, emptyMap(), body, resultClass);
    }

    public  T post(String path, String token, Map headers, Object body, Class resultClass) {
        HttpRequest request = builder(path, token).method(HttpMethod.POST);
        headers.forEach(request::putHeader);
        return exec(request, body, resultClass);
    }

    public  T post(String path, String token, Object body, int expectedCode) {
        HttpRequest request = builder(path, token).method(HttpMethod.POST);
        return exec(request, body, null, expectedCode);
    }

    public  T put(String path, String token, Object body, Class resultClass) {
        HttpRequest request = builder(path, token).method(HttpMethod.PUT);
        return exec(request, body, resultClass);
    }

    public  T put(String path, Object body, Class resultClass) {
        HttpRequest request = builder(path).method(HttpMethod.PUT);
        return exec(request, body, resultClass);
    }

    public  T get(String path, String token, Class resultClass) {
        HttpRequest request = builder(path, token).method(HttpMethod.GET);
        return exec(request, resultClass);
    }

    public  T get(String path, Map queryParams, Class resultClass) {
        final HttpRequest request = builder(path, queryParams).method(HttpMethod.GET);
        return exec(request, resultClass);
    }

    public int head(String path) {
        final HttpRequest request = builder(path).method(HttpMethod.HEAD);
        return exec(request);
    }

    public int head(String path, Map queryParams) {
        final HttpRequest request = builder(path, queryParams).method(HttpMethod.HEAD);
        return exec(request);
    }

    private  T exec(HttpRequest request, Class resultClass) {
        return exec(request, null, resultClass, 200);
    }

    private  T exec(HttpRequest request, int expectedCode) {
        return exec(request, null, null, expectedCode);
    }

    private  T exec(HttpRequest request, Object body, Class resultClass) {
        return exec(request, body, resultClass, 200);
    }

    private  T exec(HttpRequest request, Object body, Class resultClass, int expectedCode) {
        try {
            Uni> uni = body == null ? request.send()
                    : request.sendBuffer(Buffer.buffer(requestBody(body)));
            HttpResponse response = uni.await().atMost(getRequestTimeout());

            if (response.statusCode() != expectedCode) {
                throwVaultException(response);
            }
            Buffer responseBuffer = response.body();
            if (responseBuffer != null) {
                return resultClass == null ? null : mapper.readValue(responseBuffer.toString(), resultClass);
            } else {
                return null;
            }
        } catch (JsonProcessingException e) {
            throw new VaultException(e);
        }
    }

    private Duration getRequestTimeout() {
        return vaultConfigHolder.getVaultBootstrapConfig().readTimeout;
    }

    private int exec(HttpRequest request) {
        return request.send().await().atMost(getRequestTimeout()).statusCode();
    }

    private void throwVaultException(HttpResponse response) {
        String body = null;
        try {
            body = response.body().toString();
        } catch (Exception e) {
            // ignore
        }
        throw new VaultClientException(response.statusCode(), body);
    }

    private HttpRequest builder(String path, String token) {
        HttpRequest request = builder(path);
        if (token != null) {
            request.putHeader(X_VAULT_TOKEN, token);
        }
        Optional namespace = vaultConfigHolder.getVaultBootstrapConfig().enterprise.namespace;
        if (namespace.isPresent() && !isRootNamespaceAPI(path)) {
            request.putHeader(X_VAULT_NAMESPACE, namespace.get());
        }
        return request;
    }

    private boolean isRootNamespaceAPI(String path) {
        return ROOT_NAMESPACE_API.stream().anyMatch(path::startsWith);
    }

    private HttpRequest builder(String path) {
        return webClient.getAbs(getUrl(path).toString());
    }

    private HttpRequest builder(String path, Map queryParams) {
        HttpRequest request = builder(path);
        if (queryParams != null) {
            queryParams.forEach(request::addQueryParam);
        }
        return request;
    }

    private String requestBody(Object body) {
        try {
            return mapper.writeValueAsString(body);
        } catch (JsonProcessingException e) {
            throw new VaultException(e);
        }
    }

    private URL getUrl(String path) {
        try {
            return new URL(baseUrl, API_VERSION + "/" + path);
        } catch (MalformedURLException e) {
            throw new VaultException(e);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy