You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@phoenix.apache.org by el...@apache.org on 2017/07/13 01:51:47 UTC

[5/5] phoenix git commit: PHOENIX-3598 Implement HTTP parameter impersonation for PQS

PHOENIX-3598 Implement HTTP parameter impersonation for PQS

Includes some ITs for PQS by elserj.

Signed-off-by: Josh Elser <el...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/7e020bc2
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/7e020bc2
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/7e020bc2

Branch: refs/heads/4.x-HBase-1.1
Commit: 7e020bc22e42c47b510ef3272f36f673feb6667c
Parents: 0e16432
Author: shiwang <sh...@us.ibm.com>
Authored: Sun Jun 25 23:27:31 2017 -0700
Committer: Josh Elser <el...@apache.org>
Committed: Wed Jul 12 21:50:16 2017 -0400

----------------------------------------------------------------------
 .../phoenix/jdbc/PhoenixDatabaseMetaData.java   |   4 +
 .../org/apache/phoenix/query/QueryServices.java |   3 +
 .../phoenix/query/QueryServicesOptions.java     |   3 +
 phoenix-queryserver/pom.xml                     |  10 ++
 .../src/it/resources/log4j.properties           |   9 +-
 .../phoenix/queryserver/server/QueryServer.java |  75 ++++++++++++-
 .../server/PhoenixRemoteUserExtractorTest.java  | 108 +++++++++++++++++++
 7 files changed, 207 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/7e020bc2/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java
index 0b2d609..24bfde7 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java
@@ -92,6 +92,7 @@ public class PhoenixDatabaseMetaData implements DatabaseMetaData {
     public static final byte[] SYSTEM_CATALOG_SCHEMA_BYTES = QueryConstants.SYSTEM_SCHEMA_NAME_BYTES;
     public static final String SYSTEM_SCHEMA_NAME = QueryConstants.SYSTEM_SCHEMA_NAME;
     public static final byte[] SYSTEM_SCHEMA_NAME_BYTES = QueryConstants.SYSTEM_SCHEMA_NAME_BYTES;
+    public static final TableName SYSTEM_SCHEMA_HBASE_TABLE_NAME = TableName.valueOf(SYSTEM_SCHEMA_NAME);
 
     public static final String SYSTEM_CATALOG_TABLE = "CATALOG";
     public static final byte[] SYSTEM_CATALOG_TABLE_BYTES = Bytes.toBytes(SYSTEM_CATALOG_TABLE);
@@ -106,6 +107,7 @@ public class PhoenixDatabaseMetaData implements DatabaseMetaData {
     public static final byte[] IS_NAMESPACE_MAPPED_BYTES = Bytes.toBytes(IS_NAMESPACE_MAPPED);
     public static final byte[] SYSTEM_STATS_NAME_BYTES = Bytes.toBytes(SYSTEM_STATS_NAME);
     public static final byte[] SYSTEM_STATS_TABLE_BYTES = Bytes.toBytes(SYSTEM_STATS_TABLE);
+    public static final TableName SYSTEM_STATS_HBASE_TABLE_NAME = TableName.valueOf(SYSTEM_STATS_NAME);
     public static final String SYSTEM_CATALOG_ALIAS = "\"SYSTEM.TABLE\"";
 
     public static final byte[] SYSTEM_SEQUENCE_FAMILY_BYTES = QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES;
@@ -116,6 +118,7 @@ public class PhoenixDatabaseMetaData implements DatabaseMetaData {
     public static final String SYSTEM_SEQUENCE = SYSTEM_CATALOG_SCHEMA + ".\"" + SYSTEM_SEQUENCE_TABLE + "\"";
     public static final String SYSTEM_SEQUENCE_NAME = SchemaUtil.getTableName(SYSTEM_SEQUENCE_SCHEMA, SYSTEM_SEQUENCE_TABLE);
     public static final byte[] SYSTEM_SEQUENCE_NAME_BYTES = Bytes.toBytes(SYSTEM_SEQUENCE_NAME);
+    public static final TableName SYSTEM_SEQUENCE_HBASE_TABLE_NAME = TableName.valueOf(SYSTEM_SEQUENCE_NAME);
     
     public static final String TABLE_NAME = "TABLE_NAME";
     public static final byte[] TABLE_NAME_BYTES = Bytes.toBytes(TABLE_NAME);
@@ -215,6 +218,7 @@ public class PhoenixDatabaseMetaData implements DatabaseMetaData {
     public static final String SYSTEM_FUNCTION_TABLE = "FUNCTION";
     public static final String SYSTEM_FUNCTION_NAME = SchemaUtil.getTableName(SYSTEM_CATALOG_SCHEMA, SYSTEM_FUNCTION_TABLE);
     public static final byte[] SYSTEM_FUNCTION_NAME_BYTES = Bytes.toBytes(SYSTEM_FUNCTION_NAME);
+    public static final TableName SYSTEM_FUNCTION_HBASE_TABLE_NAME = TableName.valueOf(SYSTEM_FUNCTION_NAME);
 
     public static final String FUNCTION_NAME = "FUNCTION_NAME";
     public static final byte[] FUNCTION_NAME_BYTES = Bytes.toBytes(FUNCTION_NAME);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/7e020bc2/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java
index 8f6698c..cf52b8e 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java
@@ -225,6 +225,9 @@ public interface QueryServices extends SQLCloseable {
     public static final String QUERY_SERVER_UGI_CACHE_CONCURRENCY = "phoenix.queryserver.ugi.cache.concurrency";
     public static final String QUERY_SERVER_KERBEROS_ALLOWED_REALMS = "phoenix.queryserver.kerberos.allowed.realms";
     public static final String QUERY_SERVER_SPNEGO_AUTH_DISABLED_ATTRIB = "phoenix.queryserver.spnego.auth.disabled";
+    public static final String QUERY_SERVER_WITH_REMOTEUSEREXTRACTOR_ATTRIB = "phoenix.queryserver.withRemoteUserExtractor";
+    public static final String QUERY_SERVER_REMOTEUSEREXTRACTOR_PARAM = "phoenix.queryserver.remoteUserExtractor.param";
+    public static final String QUERY_SERVER_DISABLE_KERBEROS_LOGIN = "phoenix.queryserver.disable.kerberos.login";
 
     public static final String RENEW_LEASE_ENABLED = "phoenix.scanner.lease.renew.enabled";
     public static final String RUN_RENEW_LEASE_FREQUENCY_INTERVAL_MILLISECONDS = "phoenix.scanner.lease.renew.interval";

http://git-wip-us.apache.org/repos/asf/phoenix/blob/7e020bc2/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
index a6225b9..43f7b9c 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
@@ -266,6 +266,9 @@ public class QueryServicesOptions {
     public static final int DEFAULT_QUERY_SERVER_UGI_CACHE_INITIAL_SIZE = 100;
     public static final int DEFAULT_QUERY_SERVER_UGI_CACHE_CONCURRENCY = 10;
     public static final boolean DEFAULT_QUERY_SERVER_SPNEGO_AUTH_DISABLED = false;
+    public static final boolean DEFAULT_QUERY_SERVER_WITH_REMOTEUSEREXTRACTOR = false;
+    public static final String DEFAULT_QUERY_SERVER_REMOTEUSEREXTRACTOR_PARAM = "doAs";
+    public static final boolean DEFAULT_QUERY_SERVER_DISABLE_KERBEROS_LOGIN = false;
 
     public static final boolean DEFAULT_RENEW_LEASE_ENABLED = true;
     public static final int DEFAULT_RUN_RENEW_LEASE_FREQUENCY_INTERVAL_MILLISECONDS =

http://git-wip-us.apache.org/repos/asf/phoenix/blob/7e020bc2/phoenix-queryserver/pom.xml
----------------------------------------------------------------------
diff --git a/phoenix-queryserver/pom.xml b/phoenix-queryserver/pom.xml
index f983948..93625d3 100644
--- a/phoenix-queryserver/pom.xml
+++ b/phoenix-queryserver/pom.xml
@@ -149,6 +149,11 @@
     </dependency>
     <!-- for tests -->
     <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-all</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
       <groupId>org.apache.phoenix</groupId>
       <artifactId>phoenix-core</artifactId>
       <classifier>tests</classifier>
@@ -176,5 +181,10 @@
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.apache.hadoop</groupId>
+      <artifactId>hadoop-minikdc</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 </project>

http://git-wip-us.apache.org/repos/asf/phoenix/blob/7e020bc2/phoenix-queryserver/src/it/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/phoenix-queryserver/src/it/resources/log4j.properties b/phoenix-queryserver/src/it/resources/log4j.properties
index 6b1ce50..f90cf16 100644
--- a/phoenix-queryserver/src/it/resources/log4j.properties
+++ b/phoenix-queryserver/src/it/resources/log4j.properties
@@ -58,6 +58,11 @@ log4j.appender.console.layout.ConversionPattern=%d %-5p [%t] %C{2}(%L): %m%n
 
 #log4j.logger.org.apache.hadoop.fs.FSNamesystem=DEBUG
 
-log4j.logger.org.apache.hadoop=WARN
 log4j.logger.org.apache.zookeeper=ERROR
-log4j.logger.org.apache.hadoop.hbase=DEBUG
+
+# Suppresses junk from minikdc
+log4j.logger.org.mortbay.log=WARN
+log4j.logger.org.apache.directory=WARN
+log4j.logger.net.sf.ehcache=WARN
+# Suppress the "no group for user" spamming
+log4j.logger.org.apache.hadoop.security.UserGroupInformation=ERROR

http://git-wip-us.apache.org/repos/asf/phoenix/blob/7e020bc2/phoenix-queryserver/src/main/java/org/apache/phoenix/queryserver/server/QueryServer.java
----------------------------------------------------------------------
diff --git a/phoenix-queryserver/src/main/java/org/apache/phoenix/queryserver/server/QueryServer.java b/phoenix-queryserver/src/main/java/org/apache/phoenix/queryserver/server/QueryServer.java
index 60d3f86..86aa686 100644
--- a/phoenix-queryserver/src/main/java/org/apache/phoenix/queryserver/server/QueryServer.java
+++ b/phoenix-queryserver/src/main/java/org/apache/phoenix/queryserver/server/QueryServer.java
@@ -28,6 +28,10 @@ import org.apache.calcite.avatica.remote.LocalService;
 import org.apache.calcite.avatica.remote.Service;
 import org.apache.calcite.avatica.server.DoAsRemoteUserCallback;
 import org.apache.calcite.avatica.server.HttpServer;
+import org.apache.calcite.avatica.server.RemoteUserExtractor;
+import org.apache.calcite.avatica.server.RemoteUserExtractionException;
+import org.apache.calcite.avatica.server.HttpRequestRemoteUserExtractor;
+import org.apache.calcite.avatica.server.HttpQueryStringParameterRemoteUserExtractor;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.hadoop.conf.Configuration;
@@ -38,6 +42,7 @@ import org.apache.hadoop.net.DNS;
 import org.apache.hadoop.security.SecurityUtil;
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.security.authorize.ProxyUsers;
+import org.apache.hadoop.security.authorize.AuthorizationException;
 import org.apache.hadoop.util.StringUtils;
 import org.apache.hadoop.util.Tool;
 import org.apache.hadoop.util.ToolRunner;
@@ -58,6 +63,8 @@ import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 
+import javax.servlet.http.HttpServletRequest;
+
 /**
  * A query server for Phoenix over Calcite's Avatica.
  */
@@ -175,10 +182,11 @@ public final class QueryServer extends Configured implements Tool, Runnable {
           QueryServices.QUERY_SERVER_HBASE_SECURITY_CONF_ATTRIB));
       final boolean disableSpnego = getConf().getBoolean(QueryServices.QUERY_SERVER_SPNEGO_AUTH_DISABLED_ATTRIB,
               QueryServicesOptions.DEFAULT_QUERY_SERVER_SPNEGO_AUTH_DISABLED);
-
+      final boolean disableLogin = getConf().getBoolean(QueryServices.QUERY_SERVER_DISABLE_KERBEROS_LOGIN,
+              QueryServicesOptions.DEFAULT_QUERY_SERVER_DISABLE_KERBEROS_LOGIN);
 
       // handle secure cluster credentials
-      if (isKerberos && !disableSpnego) {
+      if (isKerberos && !disableSpnego && !disableLogin) {
         String hostname = Strings.domainNamePointerToHostName(DNS.getDefaultHost(
             getConf().get(QueryServices.QUERY_SERVER_DNS_INTERFACE_ATTRIB, "default"),
             getConf().get(QueryServices.QUERY_SERVER_DNS_NAMESERVER_ATTRIB, "default")));
@@ -210,7 +218,12 @@ public final class QueryServer extends Configured implements Tool, Runnable {
 
       // Enable SPNEGO and Impersonation when using Kerberos
       if (isKerberos) {
-        UserGroupInformation ugi = UserGroupInformation.getLoginUser();
+        UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
+        LOG.debug("Current user is " + ugi);
+        if (!ugi.hasKerberosCredentials()) {
+          ugi = UserGroupInformation.getLoginUser();
+          LOG.debug("Current user does not have Kerberos credentials, using instead " + ugi);
+        }
 
         // Make sure the proxyuser configuration is up to date
         ProxyUsers.refreshSuperUserGroupsConfiguration(getConf());
@@ -228,7 +241,9 @@ public final class QueryServer extends Configured implements Tool, Runnable {
         builder.withSpnego(ugi.getUserName(), additionalAllowedRealms)
             .withAutomaticLogin(keytab)
             .withImpersonation(new PhoenixDoAsCallback(ugi, getConf()));
+
       }
+      setRemoteUserExtractorIfNecessary(builder, getConf());
 
       // Build and start the HttpServer
       server = builder.build();
@@ -243,6 +258,10 @@ public final class QueryServer extends Configured implements Tool, Runnable {
     }
   }
 
+  public synchronized void stop() {
+    server.stop();
+  }
+
   /**
    * Parses the serialization method from the configuration.
    *
@@ -273,6 +292,56 @@ public final class QueryServer extends Configured implements Tool, Runnable {
     }
   }
 
+  // add remoteUserExtractor to builder if enabled
+  @VisibleForTesting
+  public void setRemoteUserExtractorIfNecessary(HttpServer.Builder builder, Configuration conf) {
+    if (conf.getBoolean(QueryServices.QUERY_SERVER_WITH_REMOTEUSEREXTRACTOR_ATTRIB,
+            QueryServicesOptions.DEFAULT_QUERY_SERVER_WITH_REMOTEUSEREXTRACTOR)) {
+      builder.withRemoteUserExtractor(new PhoenixRemoteUserExtractor(conf));
+    }
+  }
+
+  /**
+   * Use the correctly way to extract end user.
+   */
+
+  static class PhoenixRemoteUserExtractor implements RemoteUserExtractor{
+    private final HttpQueryStringParameterRemoteUserExtractor paramRemoteUserExtractor;
+    private final HttpRequestRemoteUserExtractor requestRemoteUserExtractor;
+    private final String userExtractParam;
+
+    public PhoenixRemoteUserExtractor(Configuration conf) {
+      this.requestRemoteUserExtractor = new HttpRequestRemoteUserExtractor();
+      this.userExtractParam = conf.get(QueryServices.QUERY_SERVER_REMOTEUSEREXTRACTOR_PARAM,
+              QueryServicesOptions.DEFAULT_QUERY_SERVER_REMOTEUSEREXTRACTOR_PARAM);
+      this.paramRemoteUserExtractor = new HttpQueryStringParameterRemoteUserExtractor(userExtractParam);
+    }
+
+    @Override
+    public String extract(HttpServletRequest request) throws RemoteUserExtractionException {
+      if (request.getParameter(userExtractParam) != null) {
+        String extractedUser = paramRemoteUserExtractor.extract(request);
+        UserGroupInformation ugi = UserGroupInformation.createRemoteUser(request.getRemoteUser());
+        UserGroupInformation proxyUser = UserGroupInformation.createProxyUser(extractedUser, ugi);
+
+        // Check if this user is allowed to be impersonated.
+        // Will throw AuthorizationException if the impersonation as this user is not allowed
+        try {
+          ProxyUsers.authorize(proxyUser, request.getRemoteAddr());
+          return extractedUser;
+        } catch (AuthorizationException e) {
+          throw new RemoteUserExtractionException(e.getMessage(), e);
+        }
+      } else {
+        if (LOG.isDebugEnabled()) {
+          LOG.debug("The parameter (" + userExtractParam + ") used to extract the remote user doesn't exist in the request.");
+        }
+        return requestRemoteUserExtractor.extract(request);
+      }
+
+    }
+  }
+
   /**
    * Callback to run the Avatica server action as the remote (proxy) user instead of the server.
    */

http://git-wip-us.apache.org/repos/asf/phoenix/blob/7e020bc2/phoenix-queryserver/src/test/java/org/apache/phoenix/queryserver/server/PhoenixRemoteUserExtractorTest.java
----------------------------------------------------------------------
diff --git a/phoenix-queryserver/src/test/java/org/apache/phoenix/queryserver/server/PhoenixRemoteUserExtractorTest.java b/phoenix-queryserver/src/test/java/org/apache/phoenix/queryserver/server/PhoenixRemoteUserExtractorTest.java
new file mode 100644
index 0000000..9351989
--- /dev/null
+++ b/phoenix-queryserver/src/test/java/org/apache/phoenix/queryserver/server/PhoenixRemoteUserExtractorTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.phoenix.queryserver.server;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.apache.calcite.avatica.server.HttpServer;
+import org.apache.calcite.avatica.server.RemoteUserExtractionException;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.security.authorize.AuthorizationException;
+import org.apache.hadoop.security.authorize.ProxyUsers;
+import org.apache.phoenix.queryserver.server.QueryServer.PhoenixRemoteUserExtractor;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * Tests for the RemoteUserExtractor Method Avatica provides for Phoenix to implement.
+ */
+public class PhoenixRemoteUserExtractorTest {
+  private static final Logger LOG = LoggerFactory.getLogger(PhoenixRemoteUserExtractorTest.class);
+
+  @Test
+  public void testWithRemoteUserExtractorSuccess() {
+    HttpServletRequest request = mock(HttpServletRequest.class);
+    when(request.getRemoteUser()).thenReturn("proxyserver");
+    when(request.getParameter("doAs")).thenReturn("enduser");
+    when(request.getRemoteAddr()).thenReturn("localhost:1234");
+
+    Configuration conf = new Configuration(false);
+    conf.set("hadoop.proxyuser.proxyserver.groups", "*");
+    conf.set("hadoop.proxyuser.proxyserver.hosts", "*");
+    conf.set("phoenix.queryserver.withRemoteUserExtractor", "true");
+    ProxyUsers.refreshSuperUserGroupsConfiguration(conf);
+
+    PhoenixRemoteUserExtractor extractor = new PhoenixRemoteUserExtractor(conf);
+    try {
+      assertEquals("enduser", extractor.extract(request));
+    } catch (RemoteUserExtractionException e) {
+      LOG.info(e.getMessage());
+    }
+  }
+
+  @Test
+  public void testNoRemoteUserExtractorParam() {
+    HttpServletRequest request = mock(HttpServletRequest.class);
+    when(request.getRemoteUser()).thenReturn("proxyserver");
+    when(request.getRemoteAddr()).thenReturn("localhost:1234");
+
+    Configuration conf = new Configuration(false);
+    conf.set("hadoop.proxyuser.proxyserver.groups", "*");
+    conf.set("hadoop.proxyuser.proxyserver.hosts", "*");
+    conf.set("phoenix.queryserver.withRemoteUserExtractor", "true");
+    ProxyUsers.refreshSuperUserGroupsConfiguration(conf);
+
+    PhoenixRemoteUserExtractor extractor = new PhoenixRemoteUserExtractor(conf);
+    try {
+      assertEquals("proxyserver", extractor.extract(request));
+    } catch (RemoteUserExtractionException e) {
+      LOG.info(e.getMessage());
+    }
+  }
+
+  @Test
+  public void testDoNotUseRemoteUserExtractor() {
+
+    HttpServer.Builder builder = mock(HttpServer.Builder.class);
+    Configuration conf = new Configuration(false);
+    QueryServer queryServer = new QueryServer();
+    queryServer.setRemoteUserExtractorIfNecessary(builder, conf);
+    verify(builder, never()).withRemoteUserExtractor(any(PhoenixRemoteUserExtractor.class));
+  }
+
+  @Test
+  public void testUseRemoteUserExtractor() {
+
+    HttpServer.Builder builder = mock(HttpServer.Builder.class);
+    Configuration conf = new Configuration(false);
+    conf.set("phoenix.queryserver.withRemoteUserExtractor", "true");
+    QueryServer queryServer = new QueryServer();
+    queryServer.setRemoteUserExtractorIfNecessary(builder, conf);
+    verify(builder).withRemoteUserExtractor(any(PhoenixRemoteUserExtractor.class));
+  }
+
+}