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

com.sap.hana.datalake.files.HdlfsConnectionConfigurator Maven / Gradle / Ivy

Go to download

An implementation of org.apache.hadoop.fs.FileSystem targeting SAP HANA Data Lake Files.

There is a newer version: 3.0.27
Show newest version
package com.sap.hana.datalake.files;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.security.authentication.client.ConnectionConfigurator;
import org.apache.hadoop.security.ssl.FileBasedKeyStoresFactory;
import org.apache.hadoop.security.ssl.KeyStoresFactory;
import org.apache.hadoop.security.ssl.SSLFactory;
import org.apache.hadoop.util.ReflectionUtils;
import org.apache.hadoop.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.security.GeneralSecurityException;
import java.util.Base64;

public class HdlfsConnectionConfigurator implements ConnectionConfigurator {
  private static final Logger LOG = LoggerFactory.getLogger(HdlfsConnectionConfigurator.class);

  private final ConnectionConfigurator sslConfigurator;
  private final Configuration config;

  private int readTimeout = HdlfsConstants.FS_HDLFS_READ_TIMEOUT_DEFAULT;
  private int connectTimeout = HdlfsConstants.FS_HDLFS_CONNECTION_TIMEOUT_DEFAULT;

  private SSLSocketFactory socketFactory;
  private HostnameVerifier hostnameVerifier;
  private KeyStoresFactory keystoresFactory;
  private String fileContainer;
  private boolean impersonationEnabled;
  private String impersonationUserEncoded;
  private String connectionId;
  private boolean isSSLEnabled;
  private String[] enabledProtocols = null;

  public HdlfsConnectionConfigurator(final Configuration conf, final ConnectionConfigurator sslConfigurator) {
    this.config = conf;
    this.sslConfigurator = sslConfigurator;
  }

  public HdlfsConnectionConfigurator(final Configuration conf, final Integer connectTimeout, final Integer readTimeout) throws IOException {
    this(conf, null);

    this.connectTimeout = connectTimeout;
    this.readTimeout = readTimeout;

    try {
      this.parseConfig();
      this.setupSSLFactory();
      this.initSSLFactory();
    } catch (final Exception ex) {
      throw new IOException(ex);
    }
  }

  public String getFileContainer() {
    return this.fileContainer;
  }

  public boolean isImpersonationEnabled() {
    return this.impersonationEnabled;
  }

  public String getImpersonationUserEncoded() {
    return this.impersonationUserEncoded;
  }

  public String getConnectionId() {
    return this.connectionId;
  }

  public boolean isSSLEnabled() {
    return this.isSSLEnabled;
  }

  /**
   * parse the file system config to check for specified properties.
   */
  private void parseConfig() {
    // parse the filecontainer if present along with impersonation flag
    this.fileContainer = this.config.get(HdlfsConstants.FS_HDLFS_FILECONTAINER_KEY);
    this.impersonationEnabled = this.config.getBoolean(HdlfsConstants.FS_HDLFS_IMPERSONATION_ENABLED_KEY, false);

    if (this.impersonationEnabled) {
      final String impersonationUser = this.config.get(HdlfsConstants.FS_HDLFS_IMPERSONATION_USER_KEY, "");

      if (!impersonationUser.isEmpty()) {
        this.impersonationUserEncoded = Base64.getEncoder().encodeToString(impersonationUser.getBytes(HdlfsConstants.DEFAULT_CHARSET));
      }
    }

    // parse the connection id property is present
    this.connectionId = this.config.get(HdlfsConstants.FS_HDLFS_CONNECTION_ID);
    this.isSSLEnabled = this.config.getBoolean(HdlfsConstants.FS_HDLFS_SSL_ENABLED_KEY, true);
  }

  @Override
  public HttpURLConnection configure(final HttpURLConnection conn) {
    this.configureSSLConn(conn);

    /*
    the getters are introduced to work with unit tests' mocking based on the current setup.
    ToDo: Remove the usage of getters and fix the unit tests
     */
    if (this.getFileContainer() != null && !this.getFileContainer().isEmpty()) {
      LOG.debug("File container specified, setting header [{}]", HdlfsConstants.FS_HDLFS_CONTAINER_HEADER);
      conn.setRequestProperty(HdlfsConstants.FS_HDLFS_CONTAINER_HEADER, this.getFileContainer());
    }

    final boolean impersonationEnabled = this.isImpersonationEnabled();
    final String encodedUser = this.getImpersonationUserEncoded();

    if (impersonationEnabled && encodedUser != null && !encodedUser.isEmpty()) {
      LOG.debug("Impersonation enabled, setting headers [{}] and [{}]", HdlfsConstants.TRUSTED_USER_HEADER, HdlfsConstants.TRUSTED_USER_ENCODING_HEADER);

      conn.setRequestProperty(HdlfsConstants.TRUSTED_USER_HEADER, encodedUser);
      conn.setRequestProperty(HdlfsConstants.TRUSTED_USER_ENCODING_HEADER, HdlfsConstants.TRUSTED_USER_ENCODING);
    }

    if (this.getConnectionId() != null && !this.getConnectionId().isEmpty()) {
      LOG.debug("Connection-Id specified, setting header [{}]", HdlfsConstants.FS_HDLFS_CONNECTION_ID_HEADER);
      conn.setRequestProperty(HdlfsConstants.FS_HDLFS_CONNECTION_ID_HEADER, this.getConnectionId());
    }

    return conn;
  }

  protected void configureSSLConn(final HttpURLConnection conn) {
    if (conn instanceof HttpsURLConnection) {
      final HttpsURLConnection secureConn = (HttpsURLConnection) conn;
      secureConn.setSSLSocketFactory(this.socketFactory);
      secureConn.setHostnameVerifier(this.hostnameVerifier);
    }

    conn.setConnectTimeout(this.connectTimeout);
    conn.setReadTimeout(this.readTimeout);
  }

  protected KeyStoresFactory getKeyStoresFactory(final Configuration sslConf) {
    final Class klass = this.config.getClass(HdlfsConstants.FS_HDLFS_KEYSTORES_FACTORY_CLASS_KEY, FileBasedKeyStoresFactory.class, KeyStoresFactory.class);
    return ReflectionUtils.newInstance(klass, sslConf);
  }

  private void setupSSLFactory() throws IOException {
    final Configuration sslConf = this.readSSLConfiguration();

    this.keystoresFactory = this.getKeyStoresFactory(sslConf);
    this.enabledProtocols = this.config.getStrings(SSLFactory.SSL_ENABLED_PROTOCOLS_KEY, SSLFactory.SSL_ENABLED_PROTOCOLS_DEFAULT);
  }

  protected Configuration readSSLConfiguration() throws IOException {
    final Configuration sslConf = new Configuration(this.config);
    final String keyStoreFactoryClassConfig = this.config.get(HdlfsConstants.FS_HDLFS_KEYSTORES_FACTORY_CLASS_KEY);
    final boolean isCustomKeystoresFactory = keyStoreFactoryClassConfig != null && !keyStoreFactoryClassConfig.isEmpty();

    // we do not require to parse keystore/truststore config in case 1) a custom keystores factory is used  OR
    // 2) ssl is disabled
    if (isCustomKeystoresFactory || !this.isSSLEnabled()) {
      return sslConf;
    }

    sslConf.setBoolean(SSLFactory.SSL_REQUIRE_CLIENT_CERT_KEY, true);

    // Default file based, so we need to load our configurations
    final String keystoreLocation = this.config.get(HdlfsConstants.FS_HDLFS_SSL_KEYSTORE_LOCATION_KEY);
    final String keystorePassword = this.config.get(HdlfsConstants.FS_HDLFS_SSL_KEYSTORE_PASSWORD_KEY, HdlfsConstants.FS_HDLFS_SSL_KEYSTORE_PASSWORD_DEFAULT);
    final String keystoreKeyPassword = this.config.get(HdlfsConstants.FS_HDLFS_SSL_KEYSTORE_KEYPASSWORD_KEY, keystorePassword);
    final String keystoreType = this.config.get(HdlfsConstants.FS_HDLFS_SSL_KEYSTORE_TYPE_KEY, HdlfsConstants.FS_HDLFS_SSL_KEYSTORE_TYPE_DEFAULT);
    final String truststoreLocation = this.config.get(HdlfsConstants.FS_HDLFS_SSL_TRUSTSTORE_LOCATION_KEY, HdlfsConstants.FS_HDLFS_SSL_TRUSTSTORE_LOCATION_DEFAULT);
    final String truststorePassword = this.config.get(HdlfsConstants.FS_HDLFS_SSL_TRUSTSTORE_PASSWORD_KEY, HdlfsConstants.FS_HDLFS_SSL_TRUSTSTORE_PASSWORD_DEFAULT);
    final String truststoreType = this.config.get(HdlfsConstants.FS_HDLFS_SSL_TRUSTSTORE_TYPE_KEY, HdlfsConstants.FS_HDLFS_SSL_TRUSTSTORE_TYPE_DEFAULT);

    if (keystoreLocation == null || keystoreLocation.isEmpty()) {
      throw new IOException("Keystore location was not provided.");
    }

    sslConf.set(FileBasedKeyStoresFactory.resolvePropertyName(SSLFactory.Mode.CLIENT,
        FileBasedKeyStoresFactory.SSL_KEYSTORE_LOCATION_TPL_KEY), keystoreLocation);
    sslConf.set(FileBasedKeyStoresFactory.resolvePropertyName(SSLFactory.Mode.CLIENT,
        FileBasedKeyStoresFactory.SSL_KEYSTORE_PASSWORD_TPL_KEY), keystorePassword);
    sslConf.set(FileBasedKeyStoresFactory.resolvePropertyName(SSLFactory.Mode.CLIENT,
        FileBasedKeyStoresFactory.SSL_KEYSTORE_KEYPASSWORD_TPL_KEY), keystoreKeyPassword);
    sslConf.set(FileBasedKeyStoresFactory.resolvePropertyName(SSLFactory.Mode.CLIENT,
        FileBasedKeyStoresFactory.SSL_KEYSTORE_TYPE_TPL_KEY), keystoreType);
    sslConf.set(FileBasedKeyStoresFactory.resolvePropertyName(SSLFactory.Mode.CLIENT,
        FileBasedKeyStoresFactory.SSL_TRUSTSTORE_LOCATION_TPL_KEY), truststoreLocation);
    sslConf.set(FileBasedKeyStoresFactory.resolvePropertyName(SSLFactory.Mode.CLIENT,
        FileBasedKeyStoresFactory.SSL_TRUSTSTORE_PASSWORD_TPL_KEY), truststorePassword);
    sslConf.set(FileBasedKeyStoresFactory.resolvePropertyName(SSLFactory.Mode.CLIENT,
        FileBasedKeyStoresFactory.SSL_TRUSTSTORE_TYPE_TPL_KEY), truststoreType);

    return sslConf;
  }

  private void initSSLFactory() throws GeneralSecurityException, IOException {
    this.keystoresFactory.init(SSLFactory.Mode.CLIENT);

    final SSLContext context = SSLContext.getInstance("TLS");
    context.init(this.keystoresFactory.getKeyManagers(), this.keystoresFactory.getTrustManagers(), null);
    context.getDefaultSSLParameters().setProtocols(this.enabledProtocols);

    this.socketFactory = context.getSocketFactory();
    this.hostnameVerifier = SSLFactory.getHostnameVerifier(StringUtils.toUpperCase(
            this.config.get(SSLFactory.SSL_HOSTNAME_VERIFIER_KEY, "DEFAULT").trim()));
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy