/* * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ package org.opensearch.jdbc.config; import org.opensearch.jdbc.auth.AuthenticationType; import org.opensearch.jdbc.logging.LogLevel; import org.opensearch.jdbc.internal.util.AwsHostNameUtil; import org.opensearch.jdbc.internal.util.UrlParser; import com.amazonaws.auth.AWSCredentialsProvider; import java.io.PrintWriter; import java.net.URISyntaxException; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.Properties; public class ConnectionConfig { private String url; private String host; private int port; private int fetchSize; private String path; private boolean useSSL; private int loginTimeout; private String logOutput; private PrintWriter logWriter; private String user; private String password; private boolean requestCompression; private AuthenticationType authenticationType; private AWSCredentialsProvider awsCredentialsProvider; private String region; private LogLevel logLevel; private String keyStoreLocation; private String keyStorePassword; private String keyStoreType; private String trustStoreLocation; private String trustStorePassword; private String trustStoreType; private boolean trustSelfSigned; private boolean hostnameVerification; private String tunnelHost; private ConnectionConfig(Builder builder) { this.url = builder.getUrl(); this.host = builder.getHostProperty().getValue(); this.port = builder.getPortProperty().getValue(); this.fetchSize = builder.getFetchSizeProperty().getValue(); this.path = builder.getPathProperty().getValue(); this.useSSL = builder.getUseSSLProperty().getValue(); this.logOutput = builder.getLogOutputProperty().getValue(); this.logLevel = builder.getLogLevelConnectionProperty().getValue(); this.logWriter = builder.getLogWriter(); this.loginTimeout = builder.getLoginTimeoutProperty().getValue(); this.user = builder.getUserProperty().getValue(); this.password = builder.getPasswordProperty().getValue(); this.requestCompression = builder.getRequestCompressionProperty().getValue(); this.authenticationType = builder.getAuthConnectionProperty().getValue(); this.awsCredentialsProvider = builder.getAwsCredentialProvider().getValue(); this.region = builder.getRegionConnectionProperty().getValue(); this.keyStoreLocation = builder.getKeyStoreLocationConnectionProperty().getValue(); this.keyStorePassword = builder.getKeyStorePasswordConnectionProperty().getValue(); this.keyStoreType = builder.getKeyStoreTypeConnectionProperty().getValue(); this.trustStoreLocation = builder.getTrustStoreLocationConnectionProperty().getValue(); this.trustStorePassword = builder.getTrustStorePasswordConnectionProperty().getValue(); this.trustStoreType = builder.getTrustStoreTypeConnectionProperty().getValue(); this.trustSelfSigned = builder.getTrustSelfSignedConnectionProperty().getValue(); this.hostnameVerification = builder.getHostnameVerificationConnectionProperty().getValue(); this.tunnelHost = builder.getTunnelHostConnectionProperty().getValue(); } public static Builder builder() { return new Builder(); } public String getUrl() { return url; } public String getHost() { return host; } public int getPort() { return port; } public int getFetchSize() { return fetchSize; } public String getPath() { return path; } public boolean isUseSSL() { return useSSL; } public boolean requestCompression() { return requestCompression; } public int getLoginTimeout() { return loginTimeout; } public String getLogOutput() { return logOutput; } public LogLevel getLogLevel() { return logLevel; } public String getUser() { return user; } public String getPassword() { return password; } public AuthenticationType getAuthenticationType() { return authenticationType; } public AWSCredentialsProvider getAwsCredentialsProvider() { return awsCredentialsProvider; } public String getRegion() { return region; } public PrintWriter getLogWriter() { return logWriter; } public String getKeyStoreLocation() { return keyStoreLocation; } public String getKeyStorePassword() { return keyStorePassword; } public String getKeyStoreType() { return keyStoreType; } public String getTrustStoreLocation() { return trustStoreLocation; } public String getTrustStorePassword() { return trustStorePassword; } public String getTrustStoreType() { return trustStoreType; } public boolean trustSelfSigned() { return trustSelfSigned; } public boolean hostnameVerification() { return hostnameVerification; } public String tunnelHost() { return tunnelHost; } @Override public String toString() { return "ConnectionConfig{" + "url='" + url + '\'' + ", host='" + host + '\'' + ", port=" + port + ", fetchSize=" + fetchSize + ", path='" + path + '\'' + ", useSSL=" + useSSL + ", loginTimeout=" + loginTimeout + ", logOutput='" + logOutput + '\'' + ", logWriter=" + logWriter + ", user='" + user + '\'' + ", password='" + mask(password) + '\'' + ", requestCompression=" + requestCompression + ", authenticationType=" + authenticationType + ", awsCredentialsProvider=" + awsCredentialsProvider + ", region='" + region + '\'' + ", logLevel=" + logLevel + ", keyStoreLocation='" + keyStoreLocation + '\'' + ", keyStorePassword='" + mask(keyStorePassword) + '\'' + ", keyStoreType='" + keyStoreType + '\'' + ", trustStoreLocation='" + trustStoreLocation + '\'' + ", trustStorePassword='" + mask(trustStorePassword) + '\'' + ", trustStoreType='" + trustStoreType + '\'' + ", trustSelfSigned='" + trustSelfSigned + '\'' + ", hostnameVerification='" + hostnameVerification + '\'' + ", tunnelHost='" + tunnelHost + '\'' + '}'; } private String mask(String string) { return string == null || string.length() == 0 ? "" : ""; } public static class Builder { private HostConnectionProperty hostProperty = new HostConnectionProperty(); private PortConnectionProperty portProperty = new PortConnectionProperty(); private FetchSizeProperty fetchSizeProperty = new FetchSizeProperty(); private LoginTimeoutConnectionProperty loginTimeoutProperty = new LoginTimeoutConnectionProperty(); private UseSSLConnectionProperty useSSLProperty = new UseSSLConnectionProperty(); private PathConnectionProperty pathProperty = new PathConnectionProperty(); private LogOutputConnectionProperty logOutputProperty = new LogOutputConnectionProperty(); private UserConnectionProperty userProperty = new UserConnectionProperty(); private PasswordConnectionProperty passwordProperty = new PasswordConnectionProperty(); private RequestCompressionConnectionProperty requestCompressionProperty = new RequestCompressionConnectionProperty(); private AuthConnectionProperty authConnectionProperty = new AuthConnectionProperty(); private RegionConnectionProperty regionConnectionProperty = new RegionConnectionProperty(); private LogLevelConnectionProperty logLevelConnectionProperty = new LogLevelConnectionProperty(); private KeyStoreLocationConnectionProperty keyStoreLocationConnectionProperty = new KeyStoreLocationConnectionProperty(); private KeyStorePasswordConnectionProperty keyStorePasswordConnectionProperty = new KeyStorePasswordConnectionProperty(); private KeyStoreTypeConnectionProperty keyStoreTypeConnectionProperty = new KeyStoreTypeConnectionProperty(); private TrustStoreLocationConnectionProperty trustStoreLocationConnectionProperty = new TrustStoreLocationConnectionProperty(); private TrustStorePasswordConnectionProperty trustStorePasswordConnectionProperty = new TrustStorePasswordConnectionProperty(); private TrustStoreTypeConnectionProperty trustStoreTypeConnectionProperty = new TrustStoreTypeConnectionProperty(); private TrustSelfSignedConnectionProperty trustSelfSignedConnectionProperty = new TrustSelfSignedConnectionProperty(); private AwsCredentialsProviderProperty awsCredentialsProviderProperty = new AwsCredentialsProviderProperty(); private HostnameVerificationConnectionProperty hostnameVerificationConnectionProperty = new HostnameVerificationConnectionProperty(); private TunnelHostConnectionProperty tunnelHostConnectionProperty = new TunnelHostConnectionProperty(); ConnectionProperty[] connectionProperties = new ConnectionProperty[]{ hostProperty, portProperty, fetchSizeProperty, loginTimeoutProperty, useSSLProperty, pathProperty, logOutputProperty, logLevelConnectionProperty, userProperty, passwordProperty, requestCompressionProperty, authConnectionProperty, awsCredentialsProviderProperty, regionConnectionProperty, keyStoreLocationConnectionProperty, keyStorePasswordConnectionProperty, keyStoreTypeConnectionProperty, trustStoreLocationConnectionProperty, trustStorePasswordConnectionProperty, trustStoreTypeConnectionProperty, trustSelfSignedConnectionProperty, hostnameVerificationConnectionProperty, tunnelHostConnectionProperty }; private String url = null; private PrintWriter logWriter = null; private Map propertyMap; private Map overrideMap; private Properties urlProperties; private Properties properties; public ConnectionProperty[] getConnectionProperties() { return connectionProperties; } public HostConnectionProperty getHostProperty() { return hostProperty; } public PortConnectionProperty getPortProperty() { return portProperty; } public FetchSizeProperty getFetchSizeProperty() { return fetchSizeProperty; } public LoginTimeoutConnectionProperty getLoginTimeoutProperty() { return loginTimeoutProperty; } public UseSSLConnectionProperty getUseSSLProperty() { return useSSLProperty; } public PathConnectionProperty getPathProperty() { return pathProperty; } public LogOutputConnectionProperty getLogOutputProperty() { return logOutputProperty; } public UserConnectionProperty getUserProperty() { return userProperty; } public PasswordConnectionProperty getPasswordProperty() { return passwordProperty; } public RequestCompressionConnectionProperty getRequestCompressionProperty() { return requestCompressionProperty; } public AuthConnectionProperty getAuthConnectionProperty() { return authConnectionProperty; } public AwsCredentialsProviderProperty getAwsCredentialProvider() { return awsCredentialsProviderProperty; } public RegionConnectionProperty getRegionConnectionProperty() { return regionConnectionProperty; } public LogLevelConnectionProperty getLogLevelConnectionProperty() { return logLevelConnectionProperty; } public PrintWriter getLogWriter() { return logWriter; } public KeyStoreLocationConnectionProperty getKeyStoreLocationConnectionProperty() { return keyStoreLocationConnectionProperty; } public KeyStorePasswordConnectionProperty getKeyStorePasswordConnectionProperty() { return keyStorePasswordConnectionProperty; } public KeyStoreTypeConnectionProperty getKeyStoreTypeConnectionProperty() { return keyStoreTypeConnectionProperty; } public TrustStoreLocationConnectionProperty getTrustStoreLocationConnectionProperty() { return trustStoreLocationConnectionProperty; } public TrustStorePasswordConnectionProperty getTrustStorePasswordConnectionProperty() { return trustStorePasswordConnectionProperty; } public TrustStoreTypeConnectionProperty getTrustStoreTypeConnectionProperty() { return trustStoreTypeConnectionProperty; } public TrustSelfSignedConnectionProperty getTrustSelfSignedConnectionProperty() { return trustSelfSignedConnectionProperty; } public HostnameVerificationConnectionProperty getHostnameVerificationConnectionProperty() { return hostnameVerificationConnectionProperty; } public TunnelHostConnectionProperty getTunnelHostConnectionProperty() { return tunnelHostConnectionProperty; } public Builder setLogWriter(PrintWriter printWriter) { this.logWriter = printWriter; return this; } public String getUrl() { return url; } public Builder setUrl(String url) { this.url = url; return this; } public Builder setPropertyMap(Map map) { if (map != null) { if (propertyMap == null) { propertyMap = new HashMap<>(); } for (Map.Entry pair : map.entrySet()) { propertyMap.put(pair.getKey().toLowerCase(), pair.getValue()); } } return this; } /** * Accumulates property values to override. Successive calls * are applied in the order they are made i.e. a property value * supplied in the most recent invocation overrides any value * supplied in a prior invocations. * * @param map map containing property key-value pairs * * @return */ public Builder overrideProperties(Map map) { if (map != null) { if (overrideMap == null) { overrideMap = new HashMap<>(); } for (Map.Entry pair : map.entrySet()) { overrideMap.put(pair.getKey().toLowerCase(), pair.getValue()); } } return this; } public Builder setProperties(Properties properties) { if (properties != null) { this.properties = new Properties(); Enumeration enumeration = properties.propertyNames(); while (enumeration.hasMoreElements()) { String key = (String) enumeration.nextElement(); this.properties.setProperty(key.toLowerCase(), properties.getProperty(key)); } } return this; } public ConnectionConfig build() throws ConnectionPropertyException { if (url != null) { try { urlProperties = UrlParser.parseProperties(url); } catch (URISyntaxException e) { throw new ConnectionPropertyException("Invalid connection URL", e); } } for (ConnectionProperty connectionProperty : connectionProperties) { setRawValue(connectionProperty); } validateConfig(); return new ConnectionConfig(this); } // public DriverPropertyInfo[] buildDriverPropertyInfo() throws ConnectionPropertyException { // // Return connection properties that need more // try { // build(); // } catch (ConnectionPropertyException cpe) { // // } // validateConfig(); // // return new ConnectionConfig(this); // } private void setRawValue(ConnectionProperty connectionProperty) throws ConnectionPropertyException { Object value = getPropertyValueToSet(connectionProperty.getKey()); connectionProperty.setRawValue(value); } /** * Validate the overall configuration to be applied. * * @throws ConnectionPropertyException if the configuration attempted * fails validation checks */ private void validateConfig() throws ConnectionPropertyException { AuthenticationType authenticationType = authConnectionProperty.getValue(); if (authenticationType == AuthenticationType.NONE) { // Use Basic auth if it a username is provided but an // explicit auth type is not set if (userProperty.getValue() != null) { authConnectionProperty.setRawValue(AuthenticationType.BASIC.name()); } } else if (authenticationType == AuthenticationType.BASIC && userProperty.getValue() == null) { throw new ConnectionPropertyException(authConnectionProperty.getKey(), "Basic authentication requires a valid username but none was provided."); } else if (authenticationType == AuthenticationType.AWS_SIGV4 && regionConnectionProperty.getValue() == null) { // aws sdk auto-detection does not work for AWS ES endpoints String region = AwsHostNameUtil.parseRegion(hostProperty.getValue()); if (region == null) { throw new ConnectionPropertyException(authConnectionProperty.getKey(), String.format("AWS Signature V4 authentication requires a region to be used, but " + "a valid value could not be determined from the specified hostname. " + "Provide an explicit region value (e.g. us-east-1) " + "via the \"%s\" connection property.", regionConnectionProperty.getKey())); } else { regionConnectionProperty.setRawValue(region); } } if (portProperty.getRawValue() == null && useSSLProperty.getValue() == true) { // port is not explicitly specified, but SSL is enabled // change the default port to use to 443 portProperty.setRawValue(443); } if (fetchSizeProperty.getValue() < 0) { throw new ConnectionPropertyException(fetchSizeProperty.getKey(), "Cursor fetch size value should be greater or equal to zero"); } } /** * Computes the effective value for a connection property * as per the necessary precedence order. *

* Properties specified via overrideProperties have the * highest precedence, followed by properties specified via * setPropertyMap, followed by properties specified via * setProperties and finally property values specified via * the connection URL. * * @param key name of the property * * @return effective value */ private Object getPropertyValueToSet(String key) { key = key.toLowerCase(); if (overrideMap != null && overrideMap.containsKey(key)) { return overrideMap.get(key); } if (propertyMap != null && propertyMap.containsKey(key)) { return propertyMap.get(key); } if (properties != null) { Object value = properties.getProperty(key); if (value != null) return value; } if (urlProperties != null) { Object value = urlProperties.getProperty(key); if (value != null) return value; } return null; } } }