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

com.sap.xs.hdb.options.HanaServiceConnectionHelper Maven / Gradle / Ivy

There is a newer version: 2.13.0
Show newest version
package com.sap.xs.hdb.options;

import static java.lang.String.format;

import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.spec.InvalidKeySpecException;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.sap.xs.env.Credentials;
import com.sap.xs.env.Service;
import com.sap.xs.hdb.utils.DigestUtils;
import com.sap.xs.hdb.utils.KeyStoreInitializer;

public class HanaServiceConnectionHelper {
	private static final String CLIENT_AUTHENTICATION_PRIVATE_KEY = "client_authentication_private_key";
	private static final String CLIENT_AUTHENTICATION_CERTIFICATE = "client_authentication_certificate";

	private static final Logger LOGGER = Logger.getLogger(HanaServiceConnectionHelper.class.getName());

	public static final Path DEFAULT_SECURITY_PATH = Paths.get("META-INF/.sap_java_buildpack/hana_jdbc/security");

	private final Service hanaService;
	private final Credentials hanaCredentials;

	private Path keyStorePath = DEFAULT_SECURITY_PATH;
	private String keyStoreName;
	private char[] keyStorePass;

	public HanaServiceConnectionHelper(Service service) {
		hanaService = ensureHanaService(service);
		hanaCredentials = hanaService.getCredentials();
	}

	private Service ensureHanaService(Service service) {
		if (service == null) {
			throw new IllegalArgumentException("HANA Service cannot be null");
		}
		if (service.getCredentials() == null) {
			throw new IllegalArgumentException(service.getName() + " has no credentials.");
		}
		return service;
	}

	/**
	 * @return {@link ConnectionPropertiesBuilder}
	 */
	public ConnectionPropertiesBuilder getBuilder() {
		ConnectionPropertiesBuilder builder = new ConnectionPropertiesBuilder().withUser(hanaCredentials.getUser())
				.withPassword(hanaCredentials.getPassword());

		if (hanaCredentials.any().containsKey("schema")) {
			builder.withCurrentSchema((String) hanaCredentials.any().get("schema"));
		}

		// These properties are not listed in the official documentation of the hana jdbc driver but are present in the service def
		// builder.withProperty("url", hanaCredentials.getUrl());
		// builder.withProperty("driver", hanaCredentials.getDriver());
		// builder.withProperty("host", hanaCredentials.getHost());
		// builder.withProperty("port", hanaCredentials.getPort());

		if (hanaCredentials.any().containsKey("encrypt")) {
			builder.withEncrypt(ensureBoolean(hanaCredentials, "encrypt"));
		}
		if (hanaCredentials.any().containsKey("validate_certificate")) {
			builder.withValidateCertificate(ensureBoolean(hanaCredentials, "validate_certificate"));
		}
		if (hanaCredentials.any().containsKey("hostname_in_certificate")) {
			builder.withHostNameInCertificate((String) hanaCredentials.any().get("hostname_in_certificate"));
		}

		if (isClientAuthenticationEnabled()) {
			try {
				ensureClientAuthenticationKeyStore();
				builder.withKeyStore(keyStorePath.resolve(keyStoreName).toAbsolutePath().toString())
						.withKeyStorePassword(new String(keyStorePass));
			} catch (ClientAuthenticationInitializationException e) {
				LOGGER.log(Level.SEVERE, e, ()->"Client authentication initialization failed for service " + hanaService.getName());
			}
		}
		return builder;
	}

	// This is the behavior of other such utils (@sap/hdbext for example). May be
	// unnecessary.
	private boolean ensureBoolean(Credentials credentials, String key) {
		final Object value = credentials.any().get(key);
		if (value == null
				|| !(value.toString().equalsIgnoreCase("true") || value.toString().equalsIgnoreCase("false"))) {
			throw new IllegalArgumentException(format("Invalid HANA credentials: \"%s\" must be a boolean", key));
		}
		return Boolean.parseBoolean(value.toString());
	}

	/**
	 * @return true or false if client authentication is enabled
	 */
	public boolean isClientAuthenticationEnabled() {
		return hanaCredentials.any().containsKey(CLIENT_AUTHENTICATION_PRIVATE_KEY)
				&& hanaCredentials.any().containsKey(CLIENT_AUTHENTICATION_CERTIFICATE);
	}

	/**
	 * Ensures the KeyStore used for client authentication is properly set up.
	 * Should not be called unless isClientAuthenticationEnabled() returns true.
	 */
	public void ensureClientAuthenticationKeyStore() {
		initKeyStoreName();
		initKeyStorePassword();
		initKeyStore();
	}

	private void initKeyStoreName() {
		if (keyStoreName == null) {
			keyStoreName = hanaService.getName();
		}
		try {
			// TODO find a better approach for sanitizing the keystore filename
			keyStoreName = DigestUtils.getSha256HexString(keyStoreName);
		} catch (NoSuchAlgorithmException e) {
			throw new ClientAuthenticationInitializationException("Unable to initialize KeyStore filename", e);
		}
	}

	private void initKeyStorePassword() {
		try {
			keyStorePass = DigestUtils.getSha256Hex(getPrivateKey());
		} catch (NoSuchAlgorithmException e) {
			throw new ClientAuthenticationInitializationException("Unable to initialize KeyStore password", e);
		}
	}

	private File getKeyStoreFile() {
		return keyStorePath.resolve(keyStoreName).toFile();
	}

	private void initKeyStore() {
		if (getKeyStoreFile().exists()) {
			LOGGER.info(()->"KeyStore already exists at " + getKeyStoreFile() + " and will be used.");
		} else {
			try {
				new KeyStoreInitializer(hanaService.getName(), getPrivateKey(), getPrivateKeyCertificate())
						.initAndStore(keyStorePass, keyStorePath.resolve(keyStoreName));
			} catch (NoSuchAlgorithmException | KeyStoreException | CertificateException | InvalidKeySpecException
					| IOException e) {
				throw new ClientAuthenticationInitializationException("Unable to initialize KeyStore", e);
			}
		}
	}

	private String getPrivateKey() {
		return (String) hanaCredentials.any().get(CLIENT_AUTHENTICATION_PRIVATE_KEY);
	}

	private String getPrivateKeyCertificate() {
		return (String) hanaCredentials.any().get(CLIENT_AUTHENTICATION_CERTIFICATE);
	}

	/**
	 * @return key store name
	 */
	public String getKeyStoreName() {
		return keyStoreName;
	}

	/**
	 * @param keyStoreName
	 *            key store name to set
	 */
	public void setKeyStoreName(String keyStoreName) {
		this.keyStoreName = keyStoreName;
	}

	/**
	 * @return key store path
	 */
	public Path getKeyStorePath() {
		return keyStorePath;
	}

	/**
	 * @param keyStorePath
	 *            key store path to set
	 */
	public void setKeyStorePath(Path keyStorePath) {
		this.keyStorePath = keyStorePath;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy