You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pinot.apache.org by ja...@apache.org on 2022/06/15 18:54:59 UTC

[pinot] branch master updated: Add username and password support to JDBC driver (#8062)

This is an automated email from the ASF dual-hosted git repository.

jackie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pinot.git


The following commit(s) were added to refs/heads/master by this push:
     new caf8d75582 Add username and password support to JDBC driver (#8062)
caf8d75582 is described below

commit caf8d755820d6bf27bc7daeb74ac35d2e70caa61
Author: Kartik Khare <kh...@gmail.com>
AuthorDate: Thu Jun 16 00:24:53 2022 +0530

    Add username and password support to JDBC driver (#8062)
---
 pinot-clients/pinot-jdbc-client/pom.xml            |  3 +-
 .../java/org/apache/pinot/client/PinotDriver.java  | 33 +++++++++++---
 .../org/apache/pinot/client/utils/DriverUtils.java | 50 ++++++++++++++++++++--
 3 files changed, 77 insertions(+), 9 deletions(-)

diff --git a/pinot-clients/pinot-jdbc-client/pom.xml b/pinot-clients/pinot-jdbc-client/pom.xml
index b61deab966..32cad8cb9f 100644
--- a/pinot-clients/pinot-jdbc-client/pom.xml
+++ b/pinot-clients/pinot-jdbc-client/pom.xml
@@ -67,7 +67,8 @@
     </dependency>
     <dependency>
       <groupId>org.apache.pinot</groupId>
-      <artifactId>pinot-common</artifactId>
+      <artifactId>pinot-core</artifactId>
+      <version>${project.version}</version>
     </dependency>
     <dependency>
       <groupId>org.testng</groupId>
diff --git a/pinot-clients/pinot-jdbc-client/src/main/java/org/apache/pinot/client/PinotDriver.java b/pinot-clients/pinot-jdbc-client/src/main/java/org/apache/pinot/client/PinotDriver.java
index 4055d788d3..a01252b166 100644
--- a/pinot-clients/pinot-jdbc-client/src/main/java/org/apache/pinot/client/PinotDriver.java
+++ b/pinot-clients/pinot-jdbc-client/src/main/java/org/apache/pinot/client/PinotDriver.java
@@ -50,6 +50,7 @@ public class PinotDriver implements Driver {
   public static final String DEFAULT_TENANT = "DefaultTenant";
   public static final String INFO_SCHEME = "scheme";
   public static final String INFO_HEADERS = "headers";
+
   private SSLContext _sslContext = null;
 
   public PinotDriver() { }
@@ -59,6 +60,23 @@ public class PinotDriver implements Driver {
     _sslContext = sslContext;
   }
 
+  /**
+   * Created connection to Pinot Controller from provided properties.
+   * The following properties can be provided -
+   * tenant - Specify the tenant for which this connection is being created. If not provided, DefaultTenant is used.
+   * The connection cannot handle queries for tables which are not present in the specified tenant.
+   * headers.Authorization - base64 token to query pinot. This is required in case Auth is enabled on pinot cluster.
+   * user - Name of the user for which auth is enabled.
+   * password - Password associated with the user for which auth is enabled.
+   * You can also specify username and password in the URL, e.g. jdbc:pinot://localhost:9000?user=Foo&password=Bar
+   * If username and password are specified at multiple places, the precedence takes place in the following order
+   * (header.Authorization property) > (username and password in URL) > (user and password specified in properties)
+   * @param url  jdbc connection url containing pinot controller machine host:port.
+   * example - jdbc:pinot://localhost:9000
+   * @param info properties required for creating connection
+   * @return JDBC connection object to query pinot
+   * @throws SQLException
+   */
   @Override
   public Connection connect(String url, Properties info)
       throws SQLException {
@@ -81,11 +99,9 @@ public class PinotDriver implements Driver {
         }
       }
 
-      Map<String, String> headers =
-          info.entrySet().stream().filter(entry -> entry.getKey().toString().startsWith(INFO_HEADERS + ".")).map(
-                  entry -> Pair
-                      .of(entry.getKey().toString().substring(INFO_HEADERS.length() + 1), entry.getValue().toString()))
-              .collect(Collectors.toMap(Pair::getKey, Pair::getValue));
+      Map<String, String> headers = getHeadersFromProperties(info);
+
+      DriverUtils.handleAuth(url, info, headers);
 
       if (!headers.isEmpty()) {
         factory.setHeaders(headers);
@@ -102,6 +118,13 @@ public class PinotDriver implements Driver {
     }
   }
 
+  private Map<String, String> getHeadersFromProperties(Properties info) {
+    return info.entrySet().stream().filter(entry -> entry.getKey().toString().startsWith(INFO_HEADERS + ".")).map(
+            entry -> Pair.of(entry.getKey().toString().substring(INFO_HEADERS.length() + 1),
+                entry.getValue().toString()))
+        .collect(Collectors.toMap(Pair::getKey, Pair::getValue));
+  }
+
   @Override
   public boolean acceptsURL(String url)
       throws SQLException {
diff --git a/pinot-clients/pinot-jdbc-client/src/main/java/org/apache/pinot/client/utils/DriverUtils.java b/pinot-clients/pinot-jdbc-client/src/main/java/org/apache/pinot/client/utils/DriverUtils.java
index 710a9d1725..3303bc2463 100644
--- a/pinot-clients/pinot-jdbc-client/src/main/java/org/apache/pinot/client/utils/DriverUtils.java
+++ b/pinot-clients/pinot-jdbc-client/src/main/java/org/apache/pinot/client/utils/DriverUtils.java
@@ -20,24 +20,32 @@ package org.apache.pinot.client.utils;
 
 import java.math.BigDecimal;
 import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.sql.SQLException;
 import java.sql.Timestamp;
 import java.sql.Types;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Properties;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import javax.net.ssl.SSLContext;
 import org.apache.commons.configuration.MapConfiguration;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.utils.URLEncodedUtils;
 import org.apache.pinot.common.config.TlsConfig;
 import org.apache.pinot.common.utils.TlsUtils;
+import org.apache.pinot.core.auth.BasicAuthUtils;
 import org.apache.pinot.spi.env.PinotConfiguration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 
 public class DriverUtils {
-  public static final String SCHEME = "jdbc";
+  public static final String SCHEME = "jdbc:";
   public static final String DRIVER = "pinot";
   public static final Logger LOG = LoggerFactory.getLogger(DriverUtils.class);
   private static final String LIMIT_STATEMENT_REGEX = "\\s(limit)\\s";
@@ -45,6 +53,11 @@ public class DriverUtils {
   // SSL Properties
   public static final String PINOT_JDBC_TLS_PREFIX = "pinot.jdbc.tls";
 
+  // Auth Properties
+  public static final String USER_PROPERTY = "user";
+  public static final String PASSWORD_PROPERTY = "password";
+  public static final String AUTH_HEADER = "Authorization";
+
   private DriverUtils() {
   }
 
@@ -55,6 +68,22 @@ public class DriverUtils {
     return TlsUtils.getSslContext();
   }
 
+  public static void handleAuth(String url, Properties info, Map<String, String> headers)
+      throws SQLException {
+    Map<String, String> urlParams = DriverUtils.getURLParams(url);
+    info.putAll(urlParams);
+
+    if (info.contains(USER_PROPERTY) && !headers.containsKey(AUTH_HEADER)) {
+      String username = info.getProperty(USER_PROPERTY);
+      String password = info.getProperty(PASSWORD_PROPERTY, "");
+      if (StringUtils.isAnyEmpty(username, password)) {
+        throw new SQLException("Empty username or password provided.");
+      }
+      String authToken = BasicAuthUtils.toBasicAuthToken(username, password);
+      headers.put(AUTH_HEADER, authToken);
+    }
+  }
+
   public static List<String> getBrokersFromURL(String url) {
     if (url.toLowerCase().startsWith("jdbc:")) {
       url = url.substring(5);
@@ -73,7 +102,7 @@ public class DriverUtils {
     try {
       String broker = brokers.get(0);
       String[] hostPort = broker.split(":");
-      URI uri = new URI(SCHEME + ":" + DRIVER, hostPort[0], hostPort[1]);
+      URI uri = new URI(SCHEME + DRIVER, hostPort[0], hostPort[1]);
       return uri.toString();
     } catch (Exception e) {
       LOG.warn("Broker list is either empty or has incorrect format", e);
@@ -82,7 +111,7 @@ public class DriverUtils {
   }
 
   public static String getControllerFromURL(String url) {
-    if (url.toLowerCase().startsWith("jdbc:")) {
+    if (url.regionMatches(true, 0, SCHEME, 0, SCHEME.length())) {
       url = url.substring(5);
     }
     URI uri = URI.create(url);
@@ -90,6 +119,21 @@ public class DriverUtils {
     return controllerUrl;
   }
 
+  public static Map<String, String> getURLParams(String url) {
+    if (url.regionMatches(true, 0, SCHEME, 0, SCHEME.length())) {
+      url = url.substring(SCHEME.length());
+    }
+    URI uri = URI.create(url);
+    List<NameValuePair> params = URLEncodedUtils.parse(uri, StandardCharsets.UTF_8);
+
+    Map<String, String> paramsMap = new HashMap<>();
+    for (NameValuePair param: params) {
+      paramsMap.put(param.getName(), param.getValue());
+    }
+
+    return paramsMap;
+  }
+
   public static Integer getSQLDataType(String columnDataType) {
     if (columnDataType == null) {
       return Types.VARCHAR;


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@pinot.apache.org
For additional commands, e-mail: commits-help@pinot.apache.org