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

no.nav.vault.jdbc.hikaricp.HikariCPVaultUtil Maven / Gradle / Ivy

There is a newer version: 1.3.10
Show newest version
package no.nav.vault.jdbc.hikaricp;

import com.bettercloud.vault.Vault;
import com.bettercloud.vault.VaultException;
import com.bettercloud.vault.response.LogicalResponse;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.TimerTask;

public final class HikariCPVaultUtil {

    private static final Logger logger = LoggerFactory.getLogger(HikariCPVaultUtil.class);

    private HikariDataSource ds = null;
    private final HikariConfig hikariConfig;
    private final Vault vault;
    private final String mountPath;
    private final String role;

    private HikariCPVaultUtil(final HikariConfig config, final Vault vault, final String mountPath, final String role) {
        this.hikariConfig = config;
        this.vault = vault;
        this.mountPath = mountPath;
        this.role = role;
    }

    private void setDs(final HikariDataSource ds) {
        this.ds = ds;
    }

    public static HikariDataSource createHikariDataSourceWithVaultIntegration(final HikariConfig config, final String mountPath, final String role) throws VaultError {
        final VaultUtil instance = VaultUtil.getInstance();
        final Vault vault = instance.getClient();

        final HikariCPVaultUtil hikariCPVaultUtil = new HikariCPVaultUtil(config, vault, mountPath, role);

        final class RefreshDbCredentialsTask extends TimerTask {
            @Override
            public void run() {
                try {
                    final RefreshResult refreshResult = hikariCPVaultUtil.refreshCredentialsAndReturnRefreshInterval();
                    instance.getTimer().schedule(new RefreshDbCredentialsTask(), VaultUtil.suggestedRefreshInterval(refreshResult.leaseDuration * 1000));
                } catch (VaultException e) {
                    // We cannot re-throw exceptions (since this task runs in its own thread), so we log them instead.
                    if (e.getHttpStatusCode() == 403) {
                        logger.error("Vault denied permission to fetch database credentials for role \"" + role + "\"", e);
                    } else {
                        logger.error("Could not fetch database credentials for role \"" + role + "\"", e);
                    }

                    // Lets try refreshing again
                    logger.warn("Waiting 5 secs before trying to get new credentials");
                    instance.getTimer().schedule(new RefreshDbCredentialsTask(), 5000);
                }
            }
        }

        // The first time we fetch credentials, we can rethrow an exception if we get one - fail fast!
        try {
            final RefreshResult refreshResult = hikariCPVaultUtil.refreshCredentialsAndReturnRefreshInterval();
            instance.getTimer().schedule(new RefreshDbCredentialsTask(), VaultUtil.suggestedRefreshInterval(refreshResult.leaseDuration * 1000));
        } catch (VaultException e) {
            throw new VaultError("Could not fetch initial database credentials for role \"" + role + "\"", e);
        }

        final HikariDataSource ds = new HikariDataSource(config);
        hikariCPVaultUtil.setDs(ds);

        return ds;
    }

    private RefreshResult refreshCredentialsAndReturnRefreshInterval() throws VaultException {
        final String path = mountPath + "/creds/" + role;
        logger.info("Renewing database credentials for role \"" + role + "\"");
        final LogicalResponse response = vault.logical().read(path);
        final String username = response.getData().get("username");
        final String password = response.getData().get("password");
        logger.info("Got new credentials (username=" + username + ")");

        hikariConfig.setUsername(username);
        hikariConfig.setPassword(password);

        if (ds != null) {
            ds.setUsername(username);
            ds.setPassword(password);
            ds.getHikariConfigMXBean().setUsername(username);
            ds.getHikariConfigMXBean().setPassword(password);
        }

        return new RefreshResult(response.getLeaseId(), response.getLeaseDuration());
    }

    private static final class RefreshResult {
        final String leaseId;
        final long leaseDuration;

        RefreshResult(String leaseId, long leaseDuration) {
            this.leaseId = leaseId;
            this.leaseDuration = leaseDuration;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy