PHOENIX-4749 Allow impersonation when SPNEGO is disabled

Client impersonation is no longer tied to SPNEGO auth.

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


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

Branch: refs/heads/4.x-cdh5.11
Commit: 58082f8100a82d637c8f5102e16204e51b7cf86d
Parents: f7a27e2
Author: Alex Araujo <alexara...@gmail.com>
Authored: Wed May 23 16:28:48 2018 +0100
Committer: Pedro Boado <pbo...@apache.org>
Committed: Fri May 25 22:58:29 2018 +0100

----------------------------------------------------------------------
 .../phoenix/queryserver/server/QueryServer.java | 90 +++++++++++---------
 .../server/QueryServerConfigurationTest.java    | 72 ++++++++++++++++
 2 files changed, 121 insertions(+), 41 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/58082f81/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 8436086..e3f0f52 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
@@ -54,6 +54,7 @@ import org.apache.phoenix.queryserver.register.Registry;
 import org.apache.phoenix.util.InstanceResolver;
 
 import java.io.File;
+import java.io.IOException;
 import java.lang.management.ManagementFactory;
 import java.lang.management.RuntimeMXBean;
 import java.net.InetAddress;
@@ -197,7 +198,7 @@ public final class QueryServer extends Configured 
implements Tool, Runnable {
               
QueryServicesOptions.DEFAULT_QUERY_SERVER_DISABLE_KERBEROS_LOGIN);
 
       // handle secure cluster credentials
-      if (isKerberos && !disableSpnego && !disableLogin) {
+      if (isKerberos && !disableLogin) {
         hostname = Strings.domainNamePointerToHostName(DNS.getDefaultHost(
             getConf().get(QueryServices.QUERY_SERVER_DNS_INTERFACE_ATTRIB, 
"default"),
             getConf().get(QueryServices.QUERY_SERVER_DNS_NAMESERVER_ATTRIB, 
"default")));
@@ -230,47 +231,9 @@ public final class QueryServer extends Configured 
implements Tool, Runnable {
       final HttpServer.Builder builder = new 
HttpServer.Builder().withPort(port)
           .withHandler(service, getSerialization(getConf()));
 
-      // Enable SPNEGO and Impersonation when using Kerberos
+      // Enable client auth when using Kerberos auth for HBase
       if (isKerberos) {
-        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());
-
-        String keytabPath = 
getConf().get(QueryServices.QUERY_SERVER_KEYTAB_FILENAME_ATTRIB);
-        File keytab = new File(keytabPath);
-        String httpKeytabPath = 
getConf().get(QueryServices.QUERY_SERVER_HTTP_KEYTAB_FILENAME_ATTRIB, null);
-        String httpPrincipal = 
getConf().get(QueryServices.QUERY_SERVER_KERBEROS_HTTP_PRINCIPAL_ATTRIB, null);
-        // Backwards compat for a configuration key change
-        if (httpPrincipal == null) {
-          httpPrincipal = 
getConf().get(QueryServices.QUERY_SERVER_KERBEROS_HTTP_PRINCIPAL_ATTRIB_LEGACY, 
null);
-        }
-        File httpKeytab = null;
-        if (null != httpKeytabPath)
-          httpKeytab = new File(httpKeytabPath);
-
-        String realmsString = 
getConf().get(QueryServices.QUERY_SERVER_KERBEROS_ALLOWED_REALMS, null);
-        String[] additionalAllowedRealms = null;
-        if (null != realmsString) {
-            additionalAllowedRealms = StringUtils.split(realmsString, ',');
-        }
-
-        // Enable SPNEGO and impersonation (through standard Hadoop 
configuration means)
-        if ((null != httpKeytabPath) && (null != httpPrincipal))
-          builder.withSpnego(httpPrincipal, additionalAllowedRealms)
-              .withAutomaticLogin(httpKeytab)
-              .withImpersonation(new PhoenixDoAsCallback(ugi, getConf()));
-        else
-          builder.withSpnego(ugi.getUserName(), additionalAllowedRealms)
-              .withAutomaticLogin(keytab)
-              .withImpersonation(new PhoenixDoAsCallback(ugi, getConf()));
-
-
+        configureClientAuthentication(builder, disableSpnego);
       }
       setRemoteUserExtractorIfNecessary(builder, getConf());
 
@@ -294,6 +257,51 @@ public final class QueryServer extends Configured 
implements Tool, Runnable {
     }
   }
 
+  @VisibleForTesting
+  void configureClientAuthentication(final HttpServer.Builder builder, boolean 
disableSpnego) throws IOException {
+    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());
+
+    // Always enable impersonation for the proxy user (through standard Hadoop 
configuration means)
+    builder.withImpersonation(new PhoenixDoAsCallback(ugi, getConf()));
+
+    // Enable SPNEGO for client authentication unless it's explicitly disabled
+    if (!disableSpnego) {
+      String keytabPath = 
getConf().get(QueryServices.QUERY_SERVER_KEYTAB_FILENAME_ATTRIB);
+      File keytab = new File(keytabPath);
+      String httpKeytabPath =
+          
getConf().get(QueryServices.QUERY_SERVER_HTTP_KEYTAB_FILENAME_ATTRIB, null);
+      String httpPrincipal =
+          
getConf().get(QueryServices.QUERY_SERVER_KERBEROS_HTTP_PRINCIPAL_ATTRIB, null);
+      // Backwards compat for a configuration key change
+      if (httpPrincipal == null) {
+        httpPrincipal =
+            
getConf().get(QueryServices.QUERY_SERVER_KERBEROS_HTTP_PRINCIPAL_ATTRIB_LEGACY, 
null);
+      }
+      File httpKeytab = null;
+      if (null != httpKeytabPath) httpKeytab = new File(httpKeytabPath);
+
+      String realmsString = 
getConf().get(QueryServices.QUERY_SERVER_KERBEROS_ALLOWED_REALMS, null);
+      String[] additionalAllowedRealms = null;
+      if (null != realmsString) {
+        additionalAllowedRealms = StringUtils.split(realmsString, ',');
+      }
+      if ((null != httpKeytabPath) && (null != httpPrincipal)) {
+        builder.withSpnego(httpPrincipal, 
additionalAllowedRealms).withAutomaticLogin(httpKeytab);
+      } else {
+        builder.withSpnego(ugi.getUserName(), additionalAllowedRealms)
+            .withAutomaticLogin(keytab);
+      }
+    }
+  }
+
   public synchronized void stop() {
     server.stop();
   }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/58082f81/phoenix-queryserver/src/test/java/org/apache/phoenix/queryserver/server/QueryServerConfigurationTest.java
----------------------------------------------------------------------
diff --git 
a/phoenix-queryserver/src/test/java/org/apache/phoenix/queryserver/server/QueryServerConfigurationTest.java
 
b/phoenix-queryserver/src/test/java/org/apache/phoenix/queryserver/server/QueryServerConfigurationTest.java
new file mode 100644
index 0000000..f2a1022
--- /dev/null
+++ 
b/phoenix-queryserver/src/test/java/org/apache/phoenix/queryserver/server/QueryServerConfigurationTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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 java.io.File;
+import java.io.IOException;
+
+import org.apache.calcite.avatica.server.DoAsRemoteUserCallback;
+import org.apache.calcite.avatica.server.HttpServer;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HBaseConfiguration;
+import org.apache.phoenix.query.QueryServices;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import static org.mockito.Mockito.*;
+
+public class QueryServerConfigurationTest {
+  private static final Configuration CONF = HBaseConfiguration.create();
+
+  @Rule public TemporaryFolder testFolder = new TemporaryFolder();
+
+  private HttpServer.Builder builder;
+  private QueryServer queryServer;
+
+  @Before
+  public void setup() throws IOException {
+    File keytabFile = testFolder.newFile("test.keytab");
+    CONF.set(QueryServices.QUERY_SERVER_KEYTAB_FILENAME_ATTRIB, 
keytabFile.getAbsolutePath());
+    builder = mock(HttpServer.Builder.class);
+    queryServer = new QueryServer(new String[0], CONF);
+  }
+
+  @Test
+  public void testSpnegoEnabled() throws IOException {
+    // SPENEGO settings will be provided to the builder when enabled
+    doReturn(builder).when(builder).withSpnego(anyString(), 
any(String[].class));
+    configureAndVerifyImpersonation(builder, false);
+    // A keytab file will also be provided for automatic login
+    verify(builder).withAutomaticLogin(any(File.class));
+  }
+
+  @Test
+  public void testSpnegoDisabled() throws IOException {
+    configureAndVerifyImpersonation(builder, true);
+    verify(builder, never()).withSpnego(anyString(), any(String[].class));
+    verify(builder, never()).withAutomaticLogin(any(File.class));
+  }
+
+  private void configureAndVerifyImpersonation(HttpServer.Builder builder, 
boolean disableSpnego)
+      throws IOException {
+    queryServer.configureClientAuthentication(builder, disableSpnego);
+    verify(builder).withImpersonation(any(DoAsRemoteUserCallback.class));
+  }
+}

Reply via email to