You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sentry.apache.org by ls...@apache.org on 2015/01/20 18:11:12 UTC

incubator-sentry git commit: SENTRY-614: Add Kerberos authentication and simple authorization support to Sentry webserver (Dapeng Sun via Lenni Kuff)

Repository: incubator-sentry
Updated Branches:
  refs/heads/master 1f8ba5f7b -> 56802bba0


SENTRY-614: Add Kerberos authentication and simple authorization support to Sentry webserver (Dapeng Sun via Lenni Kuff)

Adds Kerberos authentication and simple authorization support to the Sentry webserver.
New config options introduced are:

Authentication:
sentry.service.web.authentication.type - Selects authentication types (NONE/KERBEROS)
sentry.service.web.authentication.kerberos.principal - The principal name
sentry.service.web.authentication.kerberos.keytab - Path to the keytab file

Authorization:
sentry.service.web.authentication.allow.connect.users - Comma-separated list of users
allowed to connect.


Project: http://git-wip-us.apache.org/repos/asf/incubator-sentry/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-sentry/commit/56802bba
Tree: http://git-wip-us.apache.org/repos/asf/incubator-sentry/tree/56802bba
Diff: http://git-wip-us.apache.org/repos/asf/incubator-sentry/diff/56802bba

Branch: refs/heads/master
Commit: 56802bba0e420936a0c39467076b6e41f3139ed8
Parents: 1f8ba5f
Author: Lenni Kuff <ls...@cloudera.com>
Authored: Tue Jan 20 09:02:21 2015 -0800
Committer: Lenni Kuff <ls...@cloudera.com>
Committed: Tue Jan 20 09:06:50 2015 -0800

----------------------------------------------------------------------
 .../db/service/thrift/SentryAuthFilter.java     |  90 +++++++++++++++
 .../db/service/thrift/SentryWebServer.java      |  73 +++++++++++-
 .../sentry/service/thrift/SentryService.java    |   2 +-
 .../sentry/service/thrift/ServiceConstants.java |   9 +-
 .../thrift/TestSentryWebServerWithKerberos.java | 113 +++++++++++++++++++
 .../TestSentryWebServerWithoutSecurity.java     |  44 ++++++++
 .../thrift/SentryServiceIntegrationBase.java    |  24 ++++
 7 files changed, 352 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/56802bba/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryAuthFilter.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryAuthFilter.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryAuthFilter.java
new file mode 100644
index 0000000..0da4903
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryAuthFilter.java
@@ -0,0 +1,90 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sentry.provider.db.service.thrift;
+
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.hadoop.security.authentication.server.AuthenticationFilter;
+import org.apache.hadoop.util.StringUtils;
+import org.apache.sentry.service.thrift.ServiceConstants.ServerConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.Sets;
+
+/**
+ * SentryAuthFilter is a subclass of AuthenticationFilter,
+ * add authorization: Only allowed users could connect the web server.
+ */
+public class SentryAuthFilter extends AuthenticationFilter {
+
+  private static Logger LOG = LoggerFactory.getLogger(SentryAuthFilter.class);
+
+  public static final String ALLOW_WEB_CONNECT_USERS = ServerConfig.SENTRY_WEB_SECURITY_ALLOW_CONNECT_USERS;
+
+  private Set<String> allowUsers;
+
+  @Override
+  protected void doFilter(FilterChain filterChain, HttpServletRequest request,
+      HttpServletResponse response) throws IOException, ServletException {
+    super.doFilter(filterChain, request, response);
+    String userName = request.getRemoteUser();
+    LOG.debug("Authenticating user: " + userName + " from request.");
+    if (!allowUsers.contains(userName)) {
+      response.sendError(HttpServletResponse.SC_FORBIDDEN,
+          userName + " is unauthorized.");
+    }
+  }
+
+  /**
+   * Override <code>getConfiguration<code> to get <code>ALLOW_WEB_CONNECT_USERS<code>.
+   */
+  @Override
+  protected Properties getConfiguration(String configPrefix, FilterConfig filterConfig) throws ServletException {
+    Properties props = new Properties();
+    Enumeration<?> names = filterConfig.getInitParameterNames();
+    while (names.hasMoreElements()) {
+      String name = (String) names.nextElement();
+      if (name.startsWith(configPrefix)) {
+        String value = filterConfig.getInitParameter(name);
+        if (ALLOW_WEB_CONNECT_USERS.equals(name)) {
+          allowUsers = parseConnectUsersFromConf(value);
+        } else {
+          props.put(name.substring(configPrefix.length()), value);
+        }
+      }
+    }
+    return props;
+  }
+
+  private static Set<String> parseConnectUsersFromConf(String value) {
+    if (value != null) {
+      value = value.toLowerCase();
+    }
+    return Sets.newHashSet(StringUtils.getStrings(value));
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/56802bba/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryWebServer.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryWebServer.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryWebServer.java
index 090917c..43f28ea 100644
--- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryWebServer.java
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryWebServer.java
@@ -19,20 +19,37 @@ package org.apache.sentry.provider.db.service.thrift;
  */
 
 import com.codahale.metrics.servlets.AdminServlet;
+import com.google.common.base.Preconditions;
 
+import java.io.IOException;
+import java.util.EnumSet;
 import java.util.EventListener;
+import java.util.HashMap;
+import java.util.Map;
 
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.security.SecurityUtil;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.security.authentication.server.AuthenticationFilter;
+import org.apache.sentry.service.thrift.ServiceConstants.ServerConfig;
+import org.eclipse.jetty.server.DispatcherType;
 import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.servlet.FilterHolder;
 import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.servlet.ServletHolder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.util.List;
 
 public class SentryWebServer {
+
+  private static final Logger LOGGER = LoggerFactory.getLogger(SentryWebServer.class);
+
   Server server;
   int port;
 
-  public SentryWebServer(List<EventListener> listeners, int port) {
+  public SentryWebServer(List<EventListener> listeners, int port, Configuration conf) {
     this.port = port;
     server = new Server(port);
     ServletContextHandler servletContextHandler = new ServletContextHandler();
@@ -43,6 +60,17 @@ public class SentryWebServer {
       servletContextHandler.addEventListener(listener);
     }
 
+    String authMethod = conf.get(ServerConfig.SENTRY_WEB_SECURITY_TYPE);
+    if (!ServerConfig.SENTRY_WEB_SECURITY_TYPE_NONE.equals(authMethod)) {
+      /**
+       * SentryAuthFilter is a subclass of AuthenticationFilter and
+       * AuthenticationFilter tagged as private and unstable interface:
+       * While there are not guarantees that this interface will not change,
+       * it is fairly stable and used by other projects (ie - Oozie)
+       */
+      FilterHolder filterHolder = servletContextHandler.addFilter(SentryAuthFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
+      filterHolder.setInitParameters(loadWebAuthenticationConf(conf));
+    }
     server.setHandler(servletContextHandler);
   }
 
@@ -55,4 +83,47 @@ public class SentryWebServer {
   public boolean isAlive() {
     return server != null && server.isStarted();
   }
+  private static Map<String, String> loadWebAuthenticationConf(Configuration conf) {
+    Map<String,String> prop = new HashMap<String, String>();
+    prop.put(AuthenticationFilter.CONFIG_PREFIX, ServerConfig.SENTRY_WEB_SECURITY_PREFIX);
+    String allowUsers = conf.get(ServerConfig.SENTRY_WEB_SECURITY_ALLOW_CONNECT_USERS);
+    if (allowUsers == null || allowUsers.equals("")) {
+      allowUsers = conf.get(ServerConfig.ALLOW_CONNECT);
+      conf.set(ServerConfig.SENTRY_WEB_SECURITY_ALLOW_CONNECT_USERS, allowUsers);
+    }
+    validateConf(conf);
+    for (Map.Entry<String, String> entry : conf) {
+      String name = entry.getKey();
+      if (name.startsWith(ServerConfig.SENTRY_WEB_SECURITY_PREFIX)) {
+        String value = conf.get(name);
+        prop.put(name, value);
+      }
+    }
+    return prop;
+  }
+
+  private static void validateConf(Configuration conf) {
+    String authHandlerName = conf.get(ServerConfig.SENTRY_WEB_SECURITY_TYPE);
+    Preconditions.checkNotNull(authHandlerName, "Web authHandler should not be null.");
+    String allowUsers = conf.get(ServerConfig.SENTRY_WEB_SECURITY_ALLOW_CONNECT_USERS);
+    Preconditions.checkNotNull(allowUsers, "Allow connect user(s) should not be null.");
+    if (ServerConfig.SENTRY_WEB_SECURITY_TYPE_KERBEROS.equalsIgnoreCase(authHandlerName)) {
+      String principal = conf.get(ServerConfig.SENTRY_WEB_SECURITY_PRINCIPAL);
+      Preconditions.checkNotNull(principal, "Kerberos principal should not be null.");
+      Preconditions.checkArgument(principal.length() != 0, "Kerberos principal is not right.");
+      String keytabFile = conf.get(ServerConfig.SENTRY_WEB_SECURITY_KEYTAB);
+      Preconditions.checkNotNull(keytabFile, "Keytab File should not be null.");
+      Preconditions.checkArgument(keytabFile.length() != 0, "Keytab File is not right.");
+      try {
+        UserGroupInformation.setConfiguration(conf);
+        String hostPrincipal = SecurityUtil.getServerPrincipal(principal, "0.0.0.0");
+        UserGroupInformation.loginUserFromKeytab(hostPrincipal, keytabFile);
+      } catch (IOException ex) {
+        throw new IllegalArgumentException("Can't use Kerberos authentication, principal ["
+          + principal + "] keytab [" + keytabFile + "]", ex);
+      }
+      LOGGER.info("Using Kerberos authentication, principal ["
+          + principal + "] keytab [" + keytabFile + "]");
+    }
+  }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/56802bba/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/SentryService.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/SentryService.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/SentryService.java
index 6d96565..02788aa 100644
--- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/SentryService.java
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/SentryService.java
@@ -230,7 +230,7 @@ public class SentryService implements Callable {
       List<EventListener> listenerList = new ArrayList<EventListener>();
       listenerList.add(new SentryHealthCheckServletContextListener());
       listenerList.add(new SentryMetricsServletContextListener());
-      sentryWebServer = new SentryWebServer(listenerList, webServerPort);
+      sentryWebServer = new SentryWebServer(listenerList, webServerPort, conf);
       sentryWebServer.start();
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/56802bba/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/ServiceConstants.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/ServiceConstants.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/ServiceConstants.java
index 47794bc..ddc5930 100644
--- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/ServiceConstants.java
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/ServiceConstants.java
@@ -155,7 +155,14 @@ public class ServiceConstants {
     public static final String SENTRY_REPORTER_JMX = SentryMetrics.Reporting.JMX.name(); //case insensitive
     public static final String SENTRY_REPORTER_CONSOLE = SentryMetrics.Reporting.CONSOLE.name();//case insensitive
 
-
+    // Web Security
+    public static final String SENTRY_WEB_SECURITY_PREFIX = "sentry.service.web.authentication";
+    public static final String SENTRY_WEB_SECURITY_TYPE = SENTRY_WEB_SECURITY_PREFIX + ".type";
+    public static final String SENTRY_WEB_SECURITY_TYPE_NONE = "NONE";
+    public static final String SENTRY_WEB_SECURITY_TYPE_KERBEROS = "KERBEROS";
+    public static final String SENTRY_WEB_SECURITY_PRINCIPAL = SENTRY_WEB_SECURITY_PREFIX + ".kerberos.principal";
+    public static final String SENTRY_WEB_SECURITY_KEYTAB = SENTRY_WEB_SECURITY_PREFIX + ".kerberos.keytab";
+    public static final String SENTRY_WEB_SECURITY_ALLOW_CONNECT_USERS = SENTRY_WEB_SECURITY_PREFIX + ".allow.connect.users";
   }
   public static class ClientConfig {
     public static final ImmutableMap<String, String> SASL_PROPERTIES = ServiceConstants.SASL_PROPERTIES;

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/56802bba/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/thrift/TestSentryWebServerWithKerberos.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/thrift/TestSentryWebServerWithKerberos.java b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/thrift/TestSentryWebServerWithKerberos.java
new file mode 100644
index 0000000..21c18b7
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/thrift/TestSentryWebServerWithKerberos.java
@@ -0,0 +1,113 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sentry.provider.db.service.thrift;
+
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.security.PrivilegedExceptionAction;
+import java.util.HashSet;
+
+import javax.security.auth.Subject;
+import javax.security.auth.kerberos.KerberosPrincipal;
+import javax.security.auth.login.LoginContext;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.hadoop.security.authentication.client.AuthenticatedURL;
+import org.apache.hadoop.security.authentication.client.KerberosAuthenticator;
+import org.apache.sentry.service.thrift.KerberosConfiguration;
+import org.apache.sentry.service.thrift.SentryServiceIntegrationBase;
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.google.common.collect.Sets;
+
+public class TestSentryWebServerWithKerberos extends SentryServiceIntegrationBase {
+
+  @Override
+  public void beforeSetup() throws Exception {
+    webServerEnabled = true;
+    webSecurity = true;
+  }
+
+  @Test
+  public void testPing() throws Exception {
+    runTestAsSubject(new TestOperation(){
+      @Override
+      public void runTestAsSubject() throws Exception {
+        final URL url = new URL("http://"+ SERVER_HOST + ":" + webServerPort + "/ping");
+        HttpURLConnection conn = new AuthenticatedURL(new KerberosAuthenticator()).
+            openConnection(url, new AuthenticatedURL.Token());
+        Assert.assertEquals(HttpURLConnection.HTTP_OK, conn.getResponseCode());
+        String response = IOUtils.toString(conn.getInputStream());
+        Assert.assertEquals("pong\n", response);
+      }} );
+  }
+
+  @Test
+  public void testPingWithoutSubject() throws Exception {
+    final URL url = new URL("http://"+ SERVER_HOST + ":" + webServerPort + "/ping");
+    try {
+      new AuthenticatedURL(new KerberosAuthenticator()).openConnection(url, new AuthenticatedURL.Token());
+      fail("Here should fail.");
+    } catch (Exception e) {
+      boolean isExpectError = e.getMessage().contains("No valid credentials provided");
+      Assert.assertTrue("Here should fail by 'No valid credentials provided'," +
+          " but the exception is:" + e, isExpectError);
+    }
+  }
+
+  @Test
+  public void testPingUsingHttpURLConnection() throws Exception {
+    final URL url = new URL("http://"+ SERVER_HOST + ":" + webServerPort + "/ping");
+    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+    Assert.assertEquals(HttpURLConnection.HTTP_UNAUTHORIZED, conn.getResponseCode());
+    String errorMessage = IOUtils.toString(conn.getErrorStream());
+    Assert.assertTrue(errorMessage.contains("Authentication required"));
+  }
+
+  @Test
+  public void testPingWithUnauthorizedUser() throws Exception {
+    // create an unauthorized User with Kerberos
+    String userPrinciple = "user/" + SERVER_HOST;
+    String userKerberosName = userPrinciple + "@" + REALM;
+    Subject userSubject = new Subject(false, Sets.newHashSet(
+        new KerberosPrincipal(userKerberosName)), new HashSet<Object>(),new HashSet<Object>());
+    File userKeytab = new File(kdcWorkDir, "user.keytab");
+    kdc.createPrincipal(userKeytab, userPrinciple);
+    LoginContext userLoginContext = new LoginContext("", userSubject, null,
+        KerberosConfiguration.createClientConfig(userKerberosName, userKeytab));
+    userLoginContext.login();
+    Subject.doAs(userLoginContext.getSubject(), new PrivilegedExceptionAction<Void>() {
+      @Override
+      public Void run() throws Exception {
+        final URL url = new URL("http://"+ SERVER_HOST + ":" + webServerPort + "/ping");
+        try {
+          new AuthenticatedURL(new KerberosAuthenticator()).openConnection(url, new AuthenticatedURL.Token());
+          fail("Here should fail.");
+        } catch (Exception e) {
+          String expectedError = "status: 403, message: user is unauthorized.";
+          Assert.assertTrue(e.getMessage().contains(expectedError));
+        }
+        return null;
+      }
+    });
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/56802bba/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/thrift/TestSentryWebServerWithoutSecurity.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/thrift/TestSentryWebServerWithoutSecurity.java b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/thrift/TestSentryWebServerWithoutSecurity.java
new file mode 100644
index 0000000..0bcef1a
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/thrift/TestSentryWebServerWithoutSecurity.java
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sentry.provider.db.service.thrift;
+
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.sentry.service.thrift.SentryServiceIntegrationBase;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestSentryWebServerWithoutSecurity extends SentryServiceIntegrationBase {
+
+  @Override
+  public void beforeSetup() throws Exception {
+    webServerEnabled = true;
+    webSecurity = false;
+  }
+
+  @Test
+  public void testPing() throws Exception {
+    final URL url = new URL("http://"+ SERVER_HOST + ":" + webServerPort + "/ping");
+    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+    Assert.assertEquals(HttpURLConnection.HTTP_OK, conn.getResponseCode());
+    String response = IOUtils.toString(conn.getInputStream());
+    Assert.assertEquals("pong\n", response);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/56802bba/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/SentryServiceIntegrationBase.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/SentryServiceIntegrationBase.java b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/SentryServiceIntegrationBase.java
index 0bdc3a2..ca64ce1 100644
--- a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/SentryServiceIntegrationBase.java
+++ b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/SentryServiceIntegrationBase.java
@@ -62,6 +62,7 @@ public abstract class SentryServiceIntegrationBase extends SentryMiniKdcTestcase
   protected static final String REALM = "EXAMPLE.COM";
   protected static final String SERVER_PRINCIPAL = "sentry/" + SERVER_HOST;
   protected static final String SERVER_KERBEROS_NAME = "sentry/" + SERVER_HOST + "@" + REALM;
+  protected static final String HTTP_PRINCIPAL = "HTTP/" + SERVER_HOST;
   protected static final String CLIENT_PRINCIPAL = "hive/" + SERVER_HOST;
   protected static final String CLIENT_KERBEROS_SHORT_NAME = "hive";
   protected static final String CLIENT_KERBEROS_NAME = CLIENT_KERBEROS_SHORT_NAME
@@ -75,6 +76,7 @@ public abstract class SentryServiceIntegrationBase extends SentryMiniKdcTestcase
   protected File kdcWorkDir;
   protected File dbDir;
   protected File serverKeytab;
+  protected File httpKeytab;
   protected File clientKeytab;
   protected Subject clientSubject;
   protected LoginContext clientLoginContext;
@@ -90,6 +92,10 @@ public abstract class SentryServiceIntegrationBase extends SentryMiniKdcTestcase
 
   private File ZKKeytabFile;
 
+  protected boolean webServerEnabled = false;
+  protected int webServerPort = ServerConfig.SENTRY_WEB_PORT_DEFAULT;
+  protected boolean webSecurity = false;
+
   @Before
   public void setup() throws Exception {
     this.kerberos = true;
@@ -140,6 +146,24 @@ public abstract class SentryServiceIntegrationBase extends SentryMiniKdcTestcase
         conf.set(ServerConfig.SENTRY_HA_ZOOKEEPER_SECURITY, "true");
       }
     }
+    if (webServerEnabled) {
+      conf.set(ServerConfig.SENTRY_WEB_ENABLE, "true");
+      conf.set(ServerConfig.SENTRY_WEB_PORT, String.valueOf(webServerPort));
+      if (webSecurity) {
+        httpKeytab = new File(kdcWorkDir, "http.keytab");
+        kdc.createPrincipal(httpKeytab, HTTP_PRINCIPAL);
+        conf.set(ServerConfig.SENTRY_WEB_SECURITY_TYPE,
+            ServerConfig.SENTRY_WEB_SECURITY_TYPE_KERBEROS);
+        conf.set(ServerConfig.SENTRY_WEB_SECURITY_PRINCIPAL, HTTP_PRINCIPAL);
+        conf.set(ServerConfig.SENTRY_WEB_SECURITY_KEYTAB, httpKeytab.getPath());
+      } else {
+        conf.set(ServerConfig.SENTRY_WEB_SECURITY_TYPE,
+            ServerConfig.SENTRY_WEB_SECURITY_TYPE_NONE);
+      }
+    } else {
+      conf.set(ServerConfig.SENTRY_WEB_ENABLE, "false");
+    }
+
     conf.set(ServerConfig.SENTRY_VERIFY_SCHEM_VERSION, "false");
     conf.set(ServerConfig.ADMIN_GROUPS, ADMIN_GROUP);
     conf.set(ServerConfig.RPC_ADDRESS, SERVER_HOST);