Repository: hive Updated Branches: refs/heads/master 39f1e82ad -> fdd8fabdc
HIVE-18447: JDBC: Provide a way for JDBC users to pass cookie info via connection string (Vaibhav Gumashta reviewed by Thejas Nair) Project: http://git-wip-us.apache.org/repos/asf/hive/repo Commit: http://git-wip-us.apache.org/repos/asf/hive/commit/fdd8fabd Tree: http://git-wip-us.apache.org/repos/asf/hive/tree/fdd8fabd Diff: http://git-wip-us.apache.org/repos/asf/hive/diff/fdd8fabd Branch: refs/heads/master Commit: fdd8fabdcc5f8eb6ce749f55ec4637a0b96b4423 Parents: 39f1e82 Author: Vaibhav Gumashta <vgumas...@hortonworks.com> Authored: Fri Feb 2 10:22:18 2018 -0800 Committer: Vaibhav Gumashta <vgumas...@hortonworks.com> Committed: Fri Feb 2 10:22:18 2018 -0800 ---------------------------------------------------------------------- .../TestThriftHttpCLIServiceFeatures.java | 70 +++++++++++++++----- .../org/apache/hive/jdbc/HiveConnection.java | 23 ++++--- .../hive/jdbc/HttpBasicAuthInterceptor.java | 13 ++-- .../jdbc/HttpKerberosRequestInterceptor.java | 8 +-- .../hive/jdbc/HttpRequestInterceptorBase.java | 20 +++++- .../hive/jdbc/HttpTokenAuthInterceptor.java | 6 +- jdbc/src/java/org/apache/hive/jdbc/Utils.java | 8 ++- 7 files changed, 105 insertions(+), 43 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/hive/blob/fdd8fabd/itests/hive-unit/src/test/java/org/apache/hive/service/cli/thrift/TestThriftHttpCLIServiceFeatures.java ---------------------------------------------------------------------- diff --git a/itests/hive-unit/src/test/java/org/apache/hive/service/cli/thrift/TestThriftHttpCLIServiceFeatures.java b/itests/hive-unit/src/test/java/org/apache/hive/service/cli/thrift/TestThriftHttpCLIServiceFeatures.java index 93b10fb..9012867 100644 --- a/itests/hive-unit/src/test/java/org/apache/hive/service/cli/thrift/TestThriftHttpCLIServiceFeatures.java +++ b/itests/hive-unit/src/test/java/org/apache/hive/service/cli/thrift/TestThriftHttpCLIServiceFeatures.java @@ -90,18 +90,19 @@ public class TestThriftHttpCLIServiceFeatures { */ public class HttpBasicAuthInterceptorWithLogging extends HttpBasicAuthInterceptor { - ArrayList<String> requestHeaders; + ArrayList<String> requestHeaders; + String cookieHeader; - public HttpBasicAuthInterceptorWithLogging(String username, - String password, CookieStore cookieStore, String cn, boolean isSSL, - Map<String, String> additionalHeaders) { - super(username, password, cookieStore, cn, isSSL, additionalHeaders); + public HttpBasicAuthInterceptorWithLogging(String username, String password, + CookieStore cookieStore, String cn, boolean isSSL, Map<String, String> additionalHeaders, + Map<String, String> customCookies) { + super(username, password, cookieStore, cn, isSSL, additionalHeaders, customCookies); requestHeaders = new ArrayList<String>(); } @Override public void process(HttpRequest httpRequest, HttpContext httpContext) - throws HttpException, IOException { + throws HttpException, IOException { super.process(httpRequest, httpContext); String currHeaders = ""; @@ -110,11 +111,21 @@ public class TestThriftHttpCLIServiceFeatures { currHeaders += h.getName() + ":" + h.getValue() + " "; } requestHeaders.add(currHeaders); + + Header[] headers = httpRequest.getHeaders("Cookie"); + cookieHeader = ""; + for (Header h : headers) { + cookieHeader = cookieHeader + h.getName() + ":" + h.getValue(); + } } - public ArrayList<String> getRequestHeaders() { + public ArrayList<String> getRequestHeaders() { return requestHeaders; } + + public String getCookieHeader() { + return cookieHeader; + } } @@ -130,7 +141,7 @@ public class TestThriftHttpCLIServiceFeatures { assertNotNull(ThriftCLIServiceTest.hiveServer2); assertNotNull(ThriftCLIServiceTest.hiveConf); HiveConf hiveConf = ThriftCLIServiceTest.hiveConf; - + hiveConf.setBoolVar(ConfVars.HIVE_SERVER2_ENABLE_DOAS, false); hiveConf.setVar(ConfVars.HIVE_SERVER2_THRIFT_BIND_HOST, ThriftCLIServiceTest.host); hiveConf.setIntVar(ConfVars.HIVE_SERVER2_THRIFT_HTTP_PORT, ThriftCLIServiceTest.port); @@ -219,7 +230,7 @@ public class TestThriftHttpCLIServiceFeatures { String httpUrl = getHttpUrl(); httpClient.addRequestInterceptor( new HttpBasicAuthInterceptor(ThriftCLIServiceTest.USERNAME, ThriftCLIServiceTest.PASSWORD, - null, null, false, null)); + null, null, false, null, null)); return new THttpClient(httpUrl, httpClient); } @@ -243,7 +254,7 @@ public class TestThriftHttpCLIServiceFeatures { additionalHeaders.put("key2", "value2"); HttpBasicAuthInterceptorWithLogging authInt = new HttpBasicAuthInterceptorWithLogging(ThriftCLIServiceTest.USERNAME, ThriftCLIServiceTest.PASSWORD, null, null, - false, additionalHeaders); + false, additionalHeaders, null); hClient.addRequestInterceptor(authInt); transport = new THttpClient(httpUrl, hClient); TCLIService.Client httpClient = getClient(transport); @@ -260,6 +271,35 @@ public class TestThriftHttpCLIServiceFeatures { } /** + * Test additional http headers passed to request interceptor. + * @throws Exception + */ + @Test + public void testCustomCookies() throws Exception { + TTransport transport; + DefaultHttpClient hClient = new DefaultHttpClient(); + String httpUrl = getHttpUrl(); + Map<String, String> additionalHeaders = new HashMap<String, String>(); + Map<String, String> cookieHeaders = new HashMap<String, String>(); + cookieHeaders.put("key1", "value1"); + cookieHeaders.put("key2", "value2"); + HttpBasicAuthInterceptorWithLogging authInt = + new HttpBasicAuthInterceptorWithLogging(ThriftCLIServiceTest.USERNAME, ThriftCLIServiceTest.PASSWORD, null, null, + false, additionalHeaders, cookieHeaders); + hClient.addRequestInterceptor(authInt); + transport = new THttpClient(httpUrl, hClient); + TCLIService.Client httpClient = getClient(transport); + + // Create a new open session request object + TOpenSessionReq openReq = new TOpenSessionReq(); + httpClient.OpenSession(openReq).getSessionHandle(); + String cookieHeader = authInt.getCookieHeader(); + assertTrue(cookieHeader.contains("key1=value1")); + assertTrue(cookieHeader.contains("key2=value2")); + } + + + /** * This factory creates a mocked HiveAuthorizer class. * Use the mocked class to capture the argument passed to it in the test case. */ @@ -299,9 +339,9 @@ public class TestThriftHttpCLIServiceFeatures { // interceptor for adding username, pwd HttpBasicAuthInterceptor authInt = new HttpBasicAuthInterceptor(ThriftCLIServiceTest.USERNAME, ThriftCLIServiceTest.PASSWORD, null, null, - false, null); + false, null, null); hClient.addRequestInterceptor(authInt); - + transport = new THttpClient(httpUrl, hClient); TCLIService.Client httpClient = getClient(transport); @@ -327,9 +367,9 @@ public class TestThriftHttpCLIServiceFeatures { List<String> auditIPAddresses = new ArrayList<String>(context.getForwardedAddresses()); Collections.sort(auditIPAddresses); Collections.sort(headerIPs); - + Assert.assertEquals("Checking forwarded IP Address" , headerIPs, auditIPAddresses); } - - + + } http://git-wip-us.apache.org/repos/asf/hive/blob/fdd8fabd/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java ---------------------------------------------------------------------- diff --git a/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java b/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java index cb2f09c..c3f9c63 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java +++ b/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java @@ -378,15 +378,19 @@ public class HiveConnection implements java.sql.Connection { // Request interceptor for any request pre-processing logic HttpRequestInterceptor requestInterceptor; Map<String, String> additionalHttpHeaders = new HashMap<String, String>(); + Map<String, String> customCookies = new HashMap<String, String>(); // Retrieve the additional HttpHeaders for (Map.Entry<String, String> entry : sessConfMap.entrySet()) { String key = entry.getKey(); - if (key.startsWith(JdbcConnectionParams.HTTP_HEADER_PREFIX)) { additionalHttpHeaders.put(key.substring(JdbcConnectionParams.HTTP_HEADER_PREFIX.length()), entry.getValue()); } + if (key.startsWith(JdbcConnectionParams.HTTP_COOKIE_PREFIX)) { + customCookies.put(key.substring(JdbcConnectionParams.HTTP_COOKIE_PREFIX.length()), + entry.getValue()); + } } // Configure http client for kerberos/password based authentication if (isKerberosAuthMode()) { @@ -396,25 +400,22 @@ public class HiveConnection implements java.sql.Connection { * for sending to the server before every request. * In https mode, the entire information is encrypted */ - requestInterceptor = - new HttpKerberosRequestInterceptor(sessConfMap.get(JdbcConnectionParams.AUTH_PRINCIPAL), - host, getServerHttpUrl(useSsl), assumeSubject, cookieStore, cookieName, useSsl, - additionalHttpHeaders); + requestInterceptor = new HttpKerberosRequestInterceptor( + sessConfMap.get(JdbcConnectionParams.AUTH_PRINCIPAL), host, getServerHttpUrl(useSsl), + assumeSubject, cookieStore, cookieName, useSsl, additionalHttpHeaders, customCookies); } else { // Check for delegation token, if present add it in the header String tokenStr = getClientDelegationToken(sessConfMap); if (tokenStr != null) { - requestInterceptor = - new HttpTokenAuthInterceptor(tokenStr, cookieStore, cookieName, useSsl, - additionalHttpHeaders); + requestInterceptor = new HttpTokenAuthInterceptor(tokenStr, cookieStore, cookieName, useSsl, + additionalHttpHeaders, customCookies); } else { /** * Add an interceptor to pass username/password in the header. * In https mode, the entire information is encrypted */ - requestInterceptor = - new HttpBasicAuthInterceptor(getUserName(), getPassword(), cookieStore, cookieName, - useSsl, additionalHttpHeaders); + requestInterceptor = new HttpBasicAuthInterceptor(getUserName(), getPassword(), cookieStore, + cookieName, useSsl, additionalHttpHeaders, customCookies); } } // Configure http client for cookie based authentication http://git-wip-us.apache.org/repos/asf/hive/blob/fdd8fabd/jdbc/src/java/org/apache/hive/jdbc/HttpBasicAuthInterceptor.java ---------------------------------------------------------------------- diff --git a/jdbc/src/java/org/apache/hive/jdbc/HttpBasicAuthInterceptor.java b/jdbc/src/java/org/apache/hive/jdbc/HttpBasicAuthInterceptor.java index 5d2ddb5..1887e07 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/HttpBasicAuthInterceptor.java +++ b/jdbc/src/java/org/apache/hive/jdbc/HttpBasicAuthInterceptor.java @@ -29,8 +29,8 @@ import org.apache.http.impl.auth.BasicScheme; import org.apache.http.protocol.HttpContext; /** - * The class is instantiated with the username and password, it is then - * used to add header with these credentials to HTTP requests + * The class is instantiated with the username and password, it is then used to add header with + * these credentials to HTTP requests * */ public class HttpBasicAuthInterceptor extends HttpRequestInterceptorBase { @@ -38,17 +38,18 @@ public class HttpBasicAuthInterceptor extends HttpRequestInterceptorBase { AuthSchemeBase authScheme; public HttpBasicAuthInterceptor(String username, String password, CookieStore cookieStore, - String cn, boolean isSSL, Map<String, String> additionalHeaders) { - super(cookieStore, cn, isSSL, additionalHeaders); + String cn, boolean isSSL, Map<String, String> additionalHeaders, + Map<String, String> customCookies) { + super(cookieStore, cn, isSSL, additionalHeaders, customCookies); this.authScheme = new BasicScheme(); - if (username != null){ + if (username != null) { this.credentials = new UsernamePasswordCredentials(username, password); } } @Override protected void addHttpAuthHeader(HttpRequest httpRequest, HttpContext httpContext) - throws Exception { + throws Exception { Header basicAuthHeader = authScheme.authenticate(credentials, httpRequest, httpContext); httpRequest.addHeader(basicAuthHeader); } http://git-wip-us.apache.org/repos/asf/hive/blob/fdd8fabd/jdbc/src/java/org/apache/hive/jdbc/HttpKerberosRequestInterceptor.java ---------------------------------------------------------------------- diff --git a/jdbc/src/java/org/apache/hive/jdbc/HttpKerberosRequestInterceptor.java b/jdbc/src/java/org/apache/hive/jdbc/HttpKerberosRequestInterceptor.java index 37862be..28d42d7 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/HttpKerberosRequestInterceptor.java +++ b/jdbc/src/java/org/apache/hive/jdbc/HttpKerberosRequestInterceptor.java @@ -42,10 +42,10 @@ public class HttpKerberosRequestInterceptor extends HttpRequestInterceptorBase { // A fair reentrant lock private static ReentrantLock kerberosLock = new ReentrantLock(true); - public HttpKerberosRequestInterceptor(String principal, String host, - String serverHttpUrl, boolean assumeSubject, CookieStore cs, String cn, - boolean isSSL, Map<String, String> additionalHeaders) { - super(cs, cn, isSSL, additionalHeaders); + public HttpKerberosRequestInterceptor(String principal, String host, String serverHttpUrl, + boolean assumeSubject, CookieStore cs, String cn, boolean isSSL, + Map<String, String> additionalHeaders, Map<String, String> customCookies) { + super(cs, cn, isSSL, additionalHeaders, customCookies); this.principal = principal; this.host = host; this.serverHttpUrl = serverHttpUrl; http://git-wip-us.apache.org/repos/asf/hive/blob/fdd8fabd/jdbc/src/java/org/apache/hive/jdbc/HttpRequestInterceptorBase.java ---------------------------------------------------------------------- diff --git a/jdbc/src/java/org/apache/hive/jdbc/HttpRequestInterceptorBase.java b/jdbc/src/java/org/apache/hive/jdbc/HttpRequestInterceptorBase.java index cf1a11e..1ef0ab1 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/HttpRequestInterceptorBase.java +++ b/jdbc/src/java/org/apache/hive/jdbc/HttpRequestInterceptorBase.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.util.Map; import java.util.concurrent.locks.ReentrantLock; +import org.apache.http.Header; import org.apache.http.HttpException; import org.apache.http.HttpRequest; import org.apache.http.HttpRequestInterceptor; @@ -35,18 +36,20 @@ public abstract class HttpRequestInterceptorBase implements HttpRequestIntercept String cookieName; boolean isSSL; Map<String, String> additionalHeaders; + Map<String, String> customCookies; // Abstract function to add HttpAuth Header protected abstract void addHttpAuthHeader(HttpRequest httpRequest, HttpContext httpContext) throws Exception; public HttpRequestInterceptorBase(CookieStore cs, String cn, boolean isSSL, - Map<String, String> additionalHeaders) { + Map<String, String> additionalHeaders, Map<String, String> customCookies) { this.cookieStore = cs; this.isCookieEnabled = (cs != null); this.cookieName = cn; this.isSSL = isSSL; this.additionalHeaders = additionalHeaders; + this.customCookies = customCookies; } @Override @@ -82,6 +85,21 @@ public abstract class HttpRequestInterceptorBase implements HttpRequestIntercept httpRequest.addHeader(entry.getKey(), entry.getValue()); } } + // Add custom cookies if passed to the jdbc driver + if (customCookies != null) { + String cookieHeaderKeyValues = ""; + Header cookieHeaderServer = httpRequest.getFirstHeader("Cookie"); + if ((cookieHeaderServer != null) && (cookieHeaderServer.getValue() != null)) { + cookieHeaderKeyValues = cookieHeaderServer.getValue(); + } + for (Map.Entry<String, String> entry : customCookies.entrySet()) { + cookieHeaderKeyValues += ";" + entry.getKey() + "=" + entry.getValue(); + } + if (cookieHeaderKeyValues.startsWith(";")) { + cookieHeaderKeyValues = cookieHeaderKeyValues.substring(1); + } + httpRequest.addHeader("Cookie", cookieHeaderKeyValues); + } } catch (Exception e) { throw new HttpException(e.getMessage(), e); } http://git-wip-us.apache.org/repos/asf/hive/blob/fdd8fabd/jdbc/src/java/org/apache/hive/jdbc/HttpTokenAuthInterceptor.java ---------------------------------------------------------------------- diff --git a/jdbc/src/java/org/apache/hive/jdbc/HttpTokenAuthInterceptor.java b/jdbc/src/java/org/apache/hive/jdbc/HttpTokenAuthInterceptor.java index 59a91dd..fbfa7f6 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/HttpTokenAuthInterceptor.java +++ b/jdbc/src/java/org/apache/hive/jdbc/HttpTokenAuthInterceptor.java @@ -32,10 +32,10 @@ import org.apache.http.protocol.HttpContext; public class HttpTokenAuthInterceptor extends HttpRequestInterceptorBase { private String tokenStr; private static final String HIVE_DELEGATION_TOKEN_HEADER = "X-Hive-Delegation-Token"; - + public HttpTokenAuthInterceptor(String tokenStr, CookieStore cookieStore, String cn, - boolean isSSL, Map<String, String> additionalHeaders) { - super(cookieStore, cn, isSSL, additionalHeaders); + boolean isSSL, Map<String, String> additionalHeaders, Map<String, String> customCookies) { + super(cookieStore, cn, isSSL, additionalHeaders, customCookies); this.tokenStr = tokenStr; } http://git-wip-us.apache.org/repos/asf/hive/blob/fdd8fabd/jdbc/src/java/org/apache/hive/jdbc/Utils.java ---------------------------------------------------------------------- diff --git a/jdbc/src/java/org/apache/hive/jdbc/Utils.java b/jdbc/src/java/org/apache/hive/jdbc/Utils.java index f7f3854..9a12977 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/Utils.java +++ b/jdbc/src/java/org/apache/hive/jdbc/Utils.java @@ -124,9 +124,11 @@ public class Utils { static final String FETCH_SIZE = "fetchSize"; static final String INIT_FILE = "initFile"; static final String WM_POOL = "wmPool"; + // Cookie prefix + static final String HTTP_COOKIE_PREFIX = "http.cookie."; // We support ways to specify application name modeled after some existing DBs, since - // there's no standard approach. + // there's no standard approach. // MSSQL: applicationName https://docs.microsoft.com/en-us/sql/connect/jdbc/building-the-connection-url // Postgres 9~: ApplicationName https://jdbc.postgresql.org/documentation/91/connect.html // Note: various ODBC names used include "Application Name", "APP", etc. Add those? @@ -164,7 +166,7 @@ public class Utils { public JdbcConnectionParams() { } - + public JdbcConnectionParams(JdbcConnectionParams params) { this.host = params.host; this.port = params.port; @@ -404,7 +406,7 @@ public class Utils { connParams.getHiveVars().put(varMatcher.group(1), varMatcher.group(2)); } } - + // Apply configs supplied in the JDBC connection properties object for (Map.Entry<Object, Object> kv : info.entrySet()) { if ((kv.getKey() instanceof String)) {