You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hive.apache.org by pr...@apache.org on 2018/05/04 18:12:38 UTC
hive git commit: HIVE-19415: Support CORS for all HS2 web endpoints
(Prasanth Jayachandran reviewed by Sergey Shelukhin)
Repository: hive
Updated Branches:
refs/heads/branch-3 5e5cd02a3 -> d1950a06a
HIVE-19415: Support CORS for all HS2 web endpoints (Prasanth Jayachandran reviewed by Sergey Shelukhin)
Project: http://git-wip-us.apache.org/repos/asf/hive/repo
Commit: http://git-wip-us.apache.org/repos/asf/hive/commit/d1950a06
Tree: http://git-wip-us.apache.org/repos/asf/hive/tree/d1950a06
Diff: http://git-wip-us.apache.org/repos/asf/hive/diff/d1950a06
Branch: refs/heads/branch-3
Commit: d1950a06a4fd488bed56cf168f46295156c11c5b
Parents: 5e5cd02
Author: Prasanth Jayachandran <pr...@apache.org>
Authored: Fri May 4 11:10:19 2018 -0700
Committer: Prasanth Jayachandran <pr...@apache.org>
Committed: Fri May 4 11:11:58 2018 -0700
----------------------------------------------------------------------
.../org/apache/hadoop/hive/conf/HiveConf.java | 10 ++++
.../java/org/apache/hive/http/HttpServer.java | 46 +++++++++++++++
.../apache/hive/jdbc/TestActivePassiveHA.java | 62 +++++++++++++++-----
.../apache/hive/service/server/HiveServer2.java | 18 ++++++
4 files changed, 122 insertions(+), 14 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/hive/blob/d1950a06/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java
----------------------------------------------------------------------
diff --git a/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java b/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java
index 572e993..9a0d8a9 100644
--- a/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java
+++ b/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java
@@ -3022,6 +3022,16 @@ public class HiveConf extends Configuration {
"The maximum number of past queries to show in HiverSever2 WebUI."),
HIVE_SERVER2_WEBUI_USE_PAM("hive.server2.webui.use.pam", false,
"If true, the HiveServer2 WebUI will be secured with PAM."),
+ HIVE_SERVER2_WEBUI_ENABLE_CORS("hive.server2.webui.enable.cors", false,
+ "Whether to enable cross origin requests (CORS)\n"),
+ HIVE_SERVER2_WEBUI_CORS_ALLOWED_ORIGINS("hive.server2.webui.cors.allowed.origins", "*",
+ "Comma separated list of origins that are allowed when CORS is enabled.\n"),
+ HIVE_SERVER2_WEBUI_CORS_ALLOWED_METHODS("hive.server2.webui.cors.allowed.methods", "GET,POST,DELETE,HEAD",
+ "Comma separated list of http methods that are allowed when CORS is enabled.\n"),
+ HIVE_SERVER2_WEBUI_CORS_ALLOWED_HEADERS("hive.server2.webui.cors.allowed.headers",
+ "X-Requested-With,Content-Type,Accept,Origin",
+ "Comma separated list of http headers that are allowed when CORS is enabled.\n"),
+
// Tez session settings
HIVE_SERVER2_ACTIVE_PASSIVE_HA_ENABLE("hive.server2.active.passive.ha.enable", false,
http://git-wip-us.apache.org/repos/asf/hive/blob/d1950a06/common/src/java/org/apache/hive/http/HttpServer.java
----------------------------------------------------------------------
diff --git a/common/src/java/org/apache/hive/http/HttpServer.java b/common/src/java/org/apache/hive/http/HttpServer.java
index 93b11e3..3cb7a33 100644
--- a/common/src/java/org/apache/hive/http/HttpServer.java
+++ b/common/src/java/org/apache/hive/http/HttpServer.java
@@ -46,6 +46,7 @@ import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.security.authentication.server.AuthenticationFilter;
import org.apache.hadoop.security.authorize.AccessControlList;
import org.apache.hadoop.hive.common.classification.InterfaceAudience;
+import org.apache.hadoop.security.http.CrossOriginFilter;
import org.apache.hive.http.security.PamAuthenticator;
import org.apache.hive.http.security.PamConstraint;
import org.apache.hive.http.security.PamConstraintMapping;
@@ -125,6 +126,10 @@ public class HttpServer {
private boolean useSPNEGO;
private boolean useSSL;
private boolean usePAM;
+ private boolean enableCORS;
+ private String allowedOrigins;
+ private String allowedMethods;
+ private String allowedHeaders;
private PamAuthenticator pamAuthenticator;
private String contextRootRewriteTarget = "/index.html";
private final List<Pair<String, Class<? extends HttpServlet>>> servlets =
@@ -199,6 +204,26 @@ public class HttpServer {
return this;
}
+ public Builder setEnableCORS(boolean enableCORS) {
+ this.enableCORS = enableCORS;
+ return this;
+ }
+
+ public Builder setAllowedOrigins(String allowedOrigins) {
+ this.allowedOrigins = allowedOrigins;
+ return this;
+ }
+
+ public Builder setAllowedMethods(String allowedMethods) {
+ this.allowedMethods = allowedMethods;
+ return this;
+ }
+
+ public Builder setAllowedHeaders(String allowedHeaders) {
+ this.allowedHeaders = allowedHeaders;
+ return this;
+ }
+
public Builder setSPNEGOPrincipal(String principal) {
this.spnegoPrincipal = principal;
return this;
@@ -401,6 +426,23 @@ public class HttpServer {
}
/**
+ * Setup cross-origin requests (CORS) filter.
+ * @param b - builder
+ */
+ private void setupCORSFilter(Builder b) {
+ FilterHolder holder = new FilterHolder();
+ holder.setClassName(CrossOriginFilter.class.getName());
+ Map<String, String> params = new HashMap<>();
+ params.put(CrossOriginFilter.ALLOWED_ORIGINS, b.allowedOrigins);
+ params.put(CrossOriginFilter.ALLOWED_METHODS, b.allowedMethods);
+ params.put(CrossOriginFilter.ALLOWED_HEADERS, b.allowedHeaders);
+ holder.setInitParameters(params);
+
+ ServletHandler handler = webAppContext.getServletHandler();
+ handler.addFilterWithMapping(holder, "/*", FilterMapping.ALL);
+ }
+
+ /**
* Create a channel connector for "http/https" requests
*/
Connector createChannelConnector(int queueSize, Builder b) {
@@ -474,6 +516,10 @@ public class HttpServer {
setupSpnegoFilter(b);
}
+ if (b.enableCORS) {
+ setupCORSFilter(b);
+ }
+
initializeWebServer(b, threadPool.getMaxThreads());
}
http://git-wip-us.apache.org/repos/asf/hive/blob/d1950a06/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestActivePassiveHA.java
----------------------------------------------------------------------
diff --git a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestActivePassiveHA.java b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestActivePassiveHA.java
index fb846b4..c55271f 100644
--- a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestActivePassiveHA.java
+++ b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestActivePassiveHA.java
@@ -19,7 +19,9 @@
package org.apache.hive.jdbc;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import java.sql.Connection;
import java.sql.DriverManager;
@@ -38,6 +40,7 @@ import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethodBase;
import org.apache.commons.httpclient.methods.DeleteMethod;
import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.methods.OptionsMethod;
import org.apache.curator.test.TestingServer;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
@@ -51,6 +54,7 @@ import org.apache.hive.service.server.HS2ActivePassiveHARegistryClient;
import org.apache.hive.service.server.HiveServer2Instance;
import org.apache.hive.service.server.TestHS2HttpServerPam;
import org.apache.hive.service.servlet.HS2Peers;
+import org.apache.http.HttpException;
import org.apache.http.HttpHeaders;
import org.codehaus.jackson.map.ObjectMapper;
import org.junit.After;
@@ -317,6 +321,8 @@ public class TestActivePassiveHA {
@Test(timeout = 60000)
public void testManualFailover() throws Exception {
+ hiveConf1.setBoolVar(ConfVars.HIVE_SERVER2_WEBUI_ENABLE_CORS, true);
+ hiveConf2.setBoolVar(ConfVars.HIVE_SERVER2_WEBUI_ENABLE_CORS, true);
setPamConfs(hiveConf1);
setPamConfs(hiveConf2);
PamAuthenticator pamAuthenticator1 = new TestHS2HttpServerPam.TestPamAuthenticator(hiveConf1);
@@ -337,41 +343,43 @@ public class TestActivePassiveHA {
// when we start miniHS2_1 will be leader (sequential start)
assertEquals(true, miniHS2_1.getIsLeaderTestFuture().get());
assertEquals(true, miniHS2_1.isLeader());
- assertEquals("true", sendGet(url1, true));
+ assertEquals("true", sendGet(url1, true, true));
// trigger failover on miniHS2_1
- String resp = sendDelete(url1, true);
+ String resp = sendDelete(url1, true, true);
assertTrue(resp.contains("Failover successful!"));
// make sure miniHS2_1 is not leader
assertEquals(true, miniHS2_1.getNotLeaderTestFuture().get());
assertEquals(false, miniHS2_1.isLeader());
- assertEquals("false", sendGet(url1, true));
+ assertEquals("false", sendGet(url1, true, true));
// make sure miniHS2_2 is the new leader
assertEquals(true, miniHS2_2.getIsLeaderTestFuture().get());
assertEquals(true, miniHS2_2.isLeader());
- assertEquals("true", sendGet(url2, true));
+ assertEquals("true", sendGet(url2, true, true));
// send failover request again to miniHS2_1 and get a failure
- resp = sendDelete(url1, true);
+ resp = sendDelete(url1, true, true);
assertTrue(resp.contains("Cannot failover an instance that is not a leader"));
assertEquals(true, miniHS2_1.getNotLeaderTestFuture().get());
assertEquals(false, miniHS2_1.isLeader());
// send failover request to miniHS2_2 and make sure miniHS2_1 takes over (returning back to leader, test listeners)
- resp = sendDelete(url2, true);
+ resp = sendDelete(url2, true, true);
assertTrue(resp.contains("Failover successful!"));
assertEquals(true, miniHS2_1.getIsLeaderTestFuture().get());
assertEquals(true, miniHS2_1.isLeader());
- assertEquals("true", sendGet(url1, true));
+ assertEquals("true", sendGet(url1, true, true));
assertEquals(true, miniHS2_2.getNotLeaderTestFuture().get());
- assertEquals("false", sendGet(url2, true));
+ assertEquals("false", sendGet(url2, true, true));
assertEquals(false, miniHS2_2.isLeader());
} finally {
// revert configs to not affect other tests
unsetPamConfs(hiveConf1);
unsetPamConfs(hiveConf2);
+ hiveConf1.unset(ConfVars.HIVE_SERVER2_WEBUI_ENABLE_CORS.varname);
+ hiveConf2.unset(ConfVars.HIVE_SERVER2_WEBUI_ENABLE_CORS.varname);
}
}
@@ -533,20 +541,40 @@ public class TestActivePassiveHA {
}
private String sendGet(String url, boolean enableAuth) throws Exception {
- return sendAuthMethod(new GetMethod(url), enableAuth);
+ return sendAuthMethod(new GetMethod(url), enableAuth, false);
+ }
+
+ private String sendGet(String url, boolean enableAuth, boolean enableCORS) throws Exception {
+ return sendAuthMethod(new GetMethod(url), enableAuth, enableCORS);
}
private String sendDelete(String url, boolean enableAuth) throws Exception {
- return sendAuthMethod(new DeleteMethod(url), enableAuth);
+ return sendAuthMethod(new DeleteMethod(url), enableAuth, false);
+ }
+
+ private String sendDelete(String url, boolean enableAuth, boolean enableCORS) throws Exception {
+ return sendAuthMethod(new DeleteMethod(url), enableAuth, enableCORS);
}
- private String sendAuthMethod(HttpMethodBase method, boolean enableAuth) throws Exception {
+ private String sendAuthMethod(HttpMethodBase method, boolean enableAuth, boolean enableCORS) throws Exception {
HttpClient client = new HttpClient();
try {
if (enableAuth) {
- String userPass = ADMIN_USER + ":" + ADMIN_PASSWORD;
- method.addRequestHeader(HttpHeaders.AUTHORIZATION,
- "Basic " + new String(Base64.getEncoder().encode(userPass.getBytes())));
+ setupAuthHeaders(method);
+ }
+ // CORS check
+ if (enableCORS) {
+ String origin = "http://example.com";
+ OptionsMethod optionsMethod = new OptionsMethod(method.getURI().toString());
+ optionsMethod.addRequestHeader("Origin", origin);
+ setupAuthHeaders(optionsMethod);
+ int statusCode = client.executeMethod(optionsMethod);
+ if (statusCode == 200) {
+ assertNotNull(optionsMethod.getResponseHeader("Access-Control-Allow-Origin"));
+ assertEquals(origin, optionsMethod.getResponseHeader("Access-Control-Allow-Origin").getValue());
+ } else {
+ fail("CORS returned: " + statusCode + " Error: " + optionsMethod.getStatusLine().getReasonPhrase());
+ }
}
int statusCode = client.executeMethod(method);
if (statusCode == 200) {
@@ -559,6 +587,12 @@ public class TestActivePassiveHA {
}
}
+ private void setupAuthHeaders(final HttpMethodBase method) {
+ String userPass = ADMIN_USER + ":" + ADMIN_PASSWORD;
+ method.addRequestHeader(HttpHeaders.AUTHORIZATION,
+ "Basic " + new String(Base64.getEncoder().encode(userPass.getBytes())));
+ }
+
private Map<String, String> getConfOverlay(final String instanceId) {
Map<String, String> confOverlay = new HashMap<>();
confOverlay.put("hive.server2.zookeeper.publish.configs", "true");
http://git-wip-us.apache.org/repos/asf/hive/blob/d1950a06/service/src/java/org/apache/hive/service/server/HiveServer2.java
----------------------------------------------------------------------
diff --git a/service/src/java/org/apache/hive/service/server/HiveServer2.java b/service/src/java/org/apache/hive/service/server/HiveServer2.java
index e373628..8584250 100644
--- a/service/src/java/org/apache/hive/service/server/HiveServer2.java
+++ b/service/src/java/org/apache/hive/service/server/HiveServer2.java
@@ -342,6 +342,24 @@ public class HiveServer2 extends CompositeService {
builder.setSPNEGOKeytab(spnegoKeytab);
builder.setUseSPNEGO(true);
}
+ if (hiveConf.getBoolVar(ConfVars.HIVE_SERVER2_WEBUI_ENABLE_CORS)) {
+ builder.setEnableCORS(true);
+ String allowedOrigins = hiveConf.getVar(ConfVars.HIVE_SERVER2_WEBUI_CORS_ALLOWED_ORIGINS);
+ String allowedMethods = hiveConf.getVar(ConfVars.HIVE_SERVER2_WEBUI_CORS_ALLOWED_METHODS);
+ String allowedHeaders = hiveConf.getVar(ConfVars.HIVE_SERVER2_WEBUI_CORS_ALLOWED_HEADERS);
+ if (Strings.isBlank(allowedOrigins) || Strings.isBlank(allowedMethods) || Strings.isBlank(allowedHeaders)) {
+ throw new IllegalArgumentException("CORS enabled. But " +
+ ConfVars.HIVE_SERVER2_WEBUI_CORS_ALLOWED_ORIGINS.varname + "/" +
+ ConfVars.HIVE_SERVER2_WEBUI_CORS_ALLOWED_METHODS.varname + "/" +
+ ConfVars.HIVE_SERVER2_WEBUI_CORS_ALLOWED_HEADERS.varname + "/" +
+ " is not configured");
+ }
+ builder.setAllowedOrigins(allowedOrigins);
+ builder.setAllowedMethods(allowedMethods);
+ builder.setAllowedHeaders(allowedHeaders);
+ LOG.info("CORS enabled - allowed-origins: {} allowed-methods: {} allowed-headers: {}", allowedOrigins,
+ allowedMethods, allowedHeaders);
+ }
if (hiveConf.getBoolVar(ConfVars.HIVE_SERVER2_WEBUI_USE_PAM)) {
if (hiveConf.getBoolVar(ConfVars.HIVE_SERVER2_WEBUI_USE_SSL)) {
String hiveServer2PamServices = hiveConf.getVar(ConfVars.HIVE_SERVER2_PAM_SERVICES);