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

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


The following commit(s) were added to refs/heads/master by this push:
     new fc33137  HBASE-24268 REST and Thrift server do not handle the "doAs" 
parameter case insensitively
fc33137 is described below

commit fc3313771ddbd83d68a47a649d4265d9a2941de9
Author: Richard Antal <richard.an...@cloudera.com>
AuthorDate: Wed Nov 18 11:19:42 2020 +0100

    HBASE-24268 REST and Thrift server do not handle the "doAs" parameter case 
insensitively
    
    Closes #1843
    
    Signed-off-by: Josh Elser <els...@apache.org>
    Signed-off-by: Sean Busbey <bus...@apache.org>
---
 .../hbase/http/ProxyUserAuthenticationFilter.java  | 19 ++++++
 .../hadoop/hbase/rest/RESTServletContainer.java    |  5 +-
 .../hadoop/hbase/rest/TestSecureRESTServer.java    | 72 +++++++++++++++++-----
 .../hadoop/hbase/thrift/ThriftHttpServlet.java     |  3 +-
 4 files changed, 80 insertions(+), 19 deletions(-)

diff --git 
a/hbase-http/src/main/java/org/apache/hadoop/hbase/http/ProxyUserAuthenticationFilter.java
 
b/hbase-http/src/main/java/org/apache/hadoop/hbase/http/ProxyUserAuthenticationFilter.java
index 5fb17c9..182a4e1 100644
--- 
a/hbase-http/src/main/java/org/apache/hadoop/hbase/http/ProxyUserAuthenticationFilter.java
+++ 
b/hbase-http/src/main/java/org/apache/hadoop/hbase/http/ProxyUserAuthenticationFilter.java
@@ -150,6 +150,25 @@ public class ProxyUserAuthenticationFilter extends 
AuthenticationFilter {
     return false;
   }
 
+  /**
+   * The purpose of this function is to get the doAs parameter of a http 
request
+   * case insensitively
+   * @param request
+   * @return doAs parameter if exists or null otherwise
+   */
+  public static String getDoasFromHeader(final  HttpServletRequest request) {
+    String doas = null;
+    final Enumeration<String> headers = request.getHeaderNames();
+    while (headers.hasMoreElements()){
+      String header = headers.nextElement();
+      if (header.toLowerCase().equals("doas")){
+        doas = request.getHeader(header);
+        break;
+      }
+    }
+    return doas;
+  }
+
   public static HttpServletRequest toLowerCase(
       final HttpServletRequest request) {
     @SuppressWarnings("unchecked")
diff --git 
a/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/RESTServletContainer.java
 
b/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/RESTServletContainer.java
index 1cae45c..28cf4cb 100644
--- 
a/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/RESTServletContainer.java
+++ 
b/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/RESTServletContainer.java
@@ -22,6 +22,7 @@ import java.io.IOException;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.security.authorize.AuthorizationException;
@@ -30,6 +31,7 @@ import org.apache.yetus.audience.InterfaceAudience;
 
 import org.apache.hbase.thirdparty.org.glassfish.jersey.server.ResourceConfig;
 import 
org.apache.hbase.thirdparty.org.glassfish.jersey.servlet.ServletContainer;
+import static 
org.apache.hadoop.hbase.http.ProxyUserAuthenticationFilter.toLowerCase;
 
 /**
  * REST servlet container. It is used to get the remote request user
@@ -51,7 +53,8 @@ public class RESTServletContainer extends ServletContainer {
   @Override
   public void service(final HttpServletRequest request,
       final HttpServletResponse response) throws ServletException, IOException 
{
-    final String doAsUserFromQuery = request.getParameter("doAs");
+    final HttpServletRequest lowerCaseRequest = toLowerCase(request);
+    final String doAsUserFromQuery = lowerCaseRequest.getParameter("doas");
     RESTServlet servlet = RESTServlet.getInstance();
     if (doAsUserFromQuery != null) {
       Configuration conf = servlet.getConfiguration();
diff --git 
a/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestSecureRESTServer.java
 
b/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestSecureRESTServer.java
index 920cf45..47ef053 100644
--- 
a/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestSecureRESTServer.java
+++ 
b/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestSecureRESTServer.java
@@ -17,6 +17,7 @@
  */
 package org.apache.hadoop.hbase.rest;
 
+import static 
org.apache.hadoop.hbase.rest.RESTServlet.HBASE_REST_SUPPORT_PROXYUSER;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
@@ -24,6 +25,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
 
 import java.io.File;
+import java.io.IOException;
 import java.net.HttpURLConnection;
 import java.net.URL;
 import java.security.Principal;
@@ -115,6 +117,7 @@ public class TestSecureRESTServer {
 
   private static final String HOSTNAME = "localhost";
   private static final String CLIENT_PRINCIPAL = "client";
+  private static final String WHEEL_PRINCIPAL = "wheel";
   // The principal for accepting SPNEGO authn'ed requests (*must* be HTTP/fqdn)
   private static final String SPNEGO_SERVICE_PRINCIPAL = "HTTP/" + HOSTNAME;
   // The principal we use to connect to HBase
@@ -126,6 +129,7 @@ public class TestSecureRESTServer {
   private static RESTServer server;
   private static File restServerKeytab;
   private static File clientKeytab;
+  private static File wheelKeytab;
   private static File serviceKeytab;
 
   @BeforeClass
@@ -148,6 +152,8 @@ public class TestSecureRESTServer {
     restServerKeytab = new File(keytabDir, "spnego.keytab");
     // Keytab for the client
     clientKeytab = new File(keytabDir, CLIENT_PRINCIPAL + ".keytab");
+    // Keytab for wheel
+    wheelKeytab = new File(keytabDir, WHEEL_PRINCIPAL + ".keytab");
 
     /*
      * Update UGI
@@ -159,6 +165,7 @@ public class TestSecureRESTServer {
      */
     KDC = TEST_UTIL.setupMiniKdc(serviceKeytab);
     KDC.createPrincipal(clientKeytab, CLIENT_PRINCIPAL);
+    KDC.createPrincipal(wheelKeytab, WHEEL_PRINCIPAL);
     KDC.createPrincipal(serviceKeytab, SERVICE_PRINCIPAL);
     // REST server's keytab contains keys for both principals REST uses
     KDC.createPrincipal(restServerKeytab, SPNEGO_SERVICE_PRINCIPAL, 
REST_SERVER_PRINCIPAL);
@@ -184,6 +191,8 @@ public class TestSecureRESTServer {
     conf.set("hbase.superuser", "hbase");
     conf.set("hadoop.proxyuser.rest.hosts", "*");
     conf.set("hadoop.proxyuser.rest.users", "*");
+    conf.set("hadoop.proxyuser.wheel.hosts", "*");
+    conf.set("hadoop.proxyuser.wheel.users", "*");
     UserGroupInformation.setConfiguration(conf);
 
     updateKerberosConfiguration(conf, REST_SERVER_PRINCIPAL, 
SPNEGO_SERVICE_PRINCIPAL,
@@ -230,6 +239,7 @@ public class TestSecureRESTServer {
         return null;
       }
     });
+    instertData();
   }
 
   @AfterClass
@@ -299,21 +309,21 @@ public class TestSecureRESTServer {
     // Keytab for both principals above
     conf.set(RESTServer.REST_KEYTAB_FILE, serverKeytab.getAbsolutePath());
     conf.set("hbase.rest.authentication.kerberos.keytab", 
serverKeytab.getAbsolutePath());
+    conf.set(HBASE_REST_SUPPORT_PROXYUSER, "true");
   }
 
-  @Test
-  public void testPositiveAuthorization() throws Exception {
+  private static void instertData() throws IOException, InterruptedException {
     // Create a table, write a row to it, grant read perms to the client
     UserGroupInformation superuser = 
UserGroupInformation.loginUserFromKeytabAndReturnUGI(
-        SERVICE_PRINCIPAL, serviceKeytab.getAbsolutePath());
+      SERVICE_PRINCIPAL, serviceKeytab.getAbsolutePath());
     final TableName table = TableName.valueOf("publicTable");
     superuser.doAs(new PrivilegedExceptionAction<Void>() {
       @Override
       public Void run() throws Exception {
         try (Connection conn = 
ConnectionFactory.createConnection(TEST_UTIL.getConfiguration())) {
           TableDescriptor desc = TableDescriptorBuilder.newBuilder(table)
-              .setColumnFamily(ColumnFamilyDescriptorBuilder.of("f1"))
-              .build();
+            .setColumnFamily(ColumnFamilyDescriptorBuilder.of("f1"))
+            .build();
           conn.getAdmin().createTable(desc);
           try (Table t = conn.getTable(table)) {
             Put p = new Put(Bytes.toBytes("a"));
@@ -331,6 +341,12 @@ public class TestSecureRESTServer {
         return null;
       }
     });
+  }
+
+  public void testProxy(String extraArgs, String PRINCIPAL, File keytab, int 
responseCode) throws Exception{
+    UserGroupInformation superuser = 
UserGroupInformation.loginUserFromKeytabAndReturnUGI(
+      SERVICE_PRINCIPAL, serviceKeytab.getAbsolutePath());
+    final TableName table = TableName.valueOf("publicTable");
 
     // Read that row as the client
     Pair<CloseableHttpClient,HttpClientContext> pair = getClient();
@@ -338,33 +354,55 @@ public class TestSecureRESTServer {
     HttpClientContext context = pair.getSecond();
 
     HttpGet get = new HttpGet(new URL("http://localhost:"+ 
REST_TEST.getServletPort()).toURI()
-        + "/" + table + "/a");
+      + "/" + table + "/a" + extraArgs);
     get.addHeader("Accept", "application/json");
     UserGroupInformation user = 
UserGroupInformation.loginUserFromKeytabAndReturnUGI(
-        CLIENT_PRINCIPAL, clientKeytab.getAbsolutePath());
+      PRINCIPAL, keytab.getAbsolutePath());
     String jsonResponse = user.doAs(new PrivilegedExceptionAction<String>() {
       @Override
       public String run() throws Exception {
         try (CloseableHttpResponse response = client.execute(get, context)) {
           final int statusCode = response.getStatusLine().getStatusCode();
-          assertEquals(response.getStatusLine().toString(), 
HttpURLConnection.HTTP_OK, statusCode);
+          assertEquals(response.getStatusLine().toString(), responseCode, 
statusCode);
           HttpEntity entity = response.getEntity();
           return EntityUtils.toString(entity);
         }
       }
     });
-    ObjectMapper mapper = new JacksonJaxbJsonProvider()
-        .locateMapper(CellSetModel.class, MediaType.APPLICATION_JSON_TYPE);
-    CellSetModel model = mapper.readValue(jsonResponse, CellSetModel.class);
-    assertEquals(1, model.getRows().size());
-    RowModel row = model.getRows().get(0);
-    assertEquals("a", Bytes.toString(row.getKey()));
-    assertEquals(1, row.getCells().size());
-    CellModel cell = row.getCells().get(0);
-    assertEquals("1", Bytes.toString(cell.getValue()));
+    if(responseCode == HttpURLConnection.HTTP_OK) {
+      ObjectMapper mapper = new 
JacksonJaxbJsonProvider().locateMapper(CellSetModel.class, 
MediaType.APPLICATION_JSON_TYPE);
+      CellSetModel model = mapper.readValue(jsonResponse, CellSetModel.class);
+      assertEquals(1, model.getRows().size());
+      RowModel row = model.getRows().get(0);
+      assertEquals("a", Bytes.toString(row.getKey()));
+      assertEquals(1, row.getCells().size());
+      CellModel cell = row.getCells().get(0);
+      assertEquals("1", Bytes.toString(cell.getValue()));
+    }
   }
 
   @Test
+  public void testPositiveAuthorization() throws Exception {
+    testProxy("", CLIENT_PRINCIPAL, clientKeytab, HttpURLConnection.HTTP_OK);
+  }
+
+  @Test
+  public void testDoAs() throws Exception {
+    testProxy("?doAs="+CLIENT_PRINCIPAL, WHEEL_PRINCIPAL, wheelKeytab, 
HttpURLConnection.HTTP_OK);
+  }
+
+  @Test
+  public void testDoas() throws Exception {
+    testProxy("?doas="+CLIENT_PRINCIPAL, WHEEL_PRINCIPAL, wheelKeytab, 
HttpURLConnection.HTTP_OK);
+  }
+
+  @Test
+  public void testWithoutDoAs() throws Exception {
+    testProxy("", WHEEL_PRINCIPAL, wheelKeytab, 
HttpURLConnection.HTTP_FORBIDDEN);
+  }
+
+
+  @Test
   public void testNegativeAuthorization() throws Exception {
     Pair<CloseableHttpClient,HttpClientContext> pair = getClient();
     CloseableHttpClient client = pair.getFirst();
diff --git 
a/hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift/ThriftHttpServlet.java
 
b/hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift/ThriftHttpServlet.java
index f1353c8..c37401d 100644
--- 
a/hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift/ThriftHttpServlet.java
+++ 
b/hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift/ThriftHttpServlet.java
@@ -43,6 +43,7 @@ import org.ietf.jgss.GSSName;
 import org.ietf.jgss.Oid;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import static 
org.apache.hadoop.hbase.http.ProxyUserAuthenticationFilter.getDoasFromHeader;
 
 /**
  * Thrift Http Servlet is used for performing Kerberos authentication if 
security is enabled and
@@ -112,7 +113,7 @@ public class ThriftHttpServlet extends TServlet {
       effectiveUser = serviceUGI.getShortUserName();
     }
 
-    String doAsUserFromQuery = request.getHeader("doAs");
+    String doAsUserFromQuery = getDoasFromHeader(request);
     if (doAsUserFromQuery != null) {
       if (!doAsEnabled) {
         throw new ServletException("Support for proxyuser is not configured");

Reply via email to