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

com.sap.cds.feature.postgresql.PostgreSqlDataSourceDescriptor Maven / Gradle / Ivy

There is a newer version: 3.6.0
Show newest version
/**************************************************************************
 * (C) 2019-2024 SAP SE or an SAP affiliate company. All rights reserved. *
 **************************************************************************/
package com.sap.cds.feature.postgresql;

import static java.nio.charset.StandardCharsets.UTF_8;

import java.util.Base64;
import java.util.Base64.Encoder;
import java.util.Map;

import com.sap.cds.services.datasource.DataSourceDescriptor;
import com.sap.cds.services.utils.StringUtils;
import com.sap.cloud.environment.servicebinding.api.ServiceBinding;

import org.postgresql.Driver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Converts a PostgreSQL {@link ServiceBinding} into a {@link DataSourceDescriptor}
 */
public class PostgreSqlDataSourceDescriptor implements DataSourceDescriptor {

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

	private final String name;
	private final String url;
	private final String username;
	private final String password;

	public PostgreSqlDataSourceDescriptor(ServiceBinding binding) {
		this.name = binding.getName().get(); // NOSONAR

		Map credentials = binding.getCredentials();

		String jdbcUrl = createJdbcUrl(credentials);

		this.url = enableSsl(jdbcUrl, credentials);
		this.username = getUser(credentials);
		this.password = (String) credentials.get("password");
	}

	@Override
	public String getName() {
		return name;
	}

	@Override
	public String getDriverClassName() {
		return Driver.class.getName();
	}

	@Override
	public String getUrl() {
		return url;
	}

	@Override
	public String getUsername() {
		return username;
	}

	@Override
	public String getPassword() {
		return password;
	}

	private String enableSsl(String jdbcUrl, Map credentials) {

		String sslRootCert = getCaCert(credentials);

		// enable SSL mode and host verification, if root/ca certificate is available
		if (!StringUtils.isEmpty(sslRootCert)) {
			Encoder encoder = Base64.getUrlEncoder();

			/*
			 * Use existence of 'sslkey' field as an indicator if the hyperscaler only supports 'verify-ca' mode.
			 * There is no hard relation between these properties, but it is a common pattern on current hyperscalers.
			 * Further details can be found in the PostgreSQL documentation:
			 * https://jdbc.postgresql.org/documentation/ssl/#configuring-the-client
			 */
			String sslVerificationMode = credentials.containsKey("sslkey") ? "verify-ca" : "verify-full";

			jdbcUrl += "?sslmode=%s&sslfactory=%s&sslrootcertbase64=%s".formatted(sslVerificationMode,
					PostgreSqlSSLFactory.class.getName(), encoder.encodeToString(sslRootCert.getBytes(UTF_8)));
			log.debug("Enabled SSL certificate and hostname verification for PostgreSQL binding '{}'", this.name);

			String sslClientCert = getClientCert(credentials);
			String sslPrivateKey = getPrivateKey(credentials);

			// enable mTLS if private key and client cert are provided
			if (!StringUtils.isEmpty(sslClientCert) && !StringUtils.isEmpty(sslPrivateKey)) {
				jdbcUrl += "&sslclientcertbase64=%s&sslprivatekeybase64=%s".formatted(
						encoder.encodeToString(sslClientCert.getBytes(UTF_8)),
						encoder.encodeToString(sslPrivateKey.getBytes(UTF_8)));
				log.debug("Enabled mTLS for PostgreSQL binding '{}'", this.name);
			}
		} else {
			log.debug("PostgreSQL binding '{}' is missing the CA certificate.", this.name);
		}

		return jdbcUrl;
	}

	private static String createJdbcUrl(Map credentials) {
		String host = (String) credentials.get("hostname");
		if (StringUtils.isEmpty(host)) {
			host = (String) credentials.get("host");
		}

		Object port = credentials.get("port");
		if (port != null) {
			port = port.toString();
		} else {
			port = "5432";
		}

		String dbName = (String) credentials.get("dbname");
		if (StringUtils.isEmpty(dbName)) {
			dbName = (String) credentials.get("database");
		}

		return "jdbc:postgresql://%s:%s/%s".formatted(host, port, dbName);
	}

	private static String getCaCert(Map credentials) {
		return (String) credentials.get("sslrootcert");
		}

	private static String getClientCert(Map credentials) {
		return (String) credentials.get("sslcert");
	}

	private static String getPrivateKey(Map credentials) {
		return (String) credentials.get("sslkey");
	}

	private static String getUser(Map credentials) {
		String user = (String) credentials.get("username");
		if (StringUtils.isEmpty(user)) {
			user = (String) credentials.get("user");
		}
		return user;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy