Repository: drill
Updated Branches:
  refs/heads/master e25c58f7b -> b4ffa4012


http://git-wip-us.apache.org/repos/asf/drill/blob/adee4614/exec/java-exec/src/test/java/org/apache/drill/exec/server/rest/spnego/TestDrillSpnegoAuthenticator.java
----------------------------------------------------------------------
diff --git 
a/exec/java-exec/src/test/java/org/apache/drill/exec/server/rest/spnego/TestDrillSpnegoAuthenticator.java
 
b/exec/java-exec/src/test/java/org/apache/drill/exec/server/rest/spnego/TestDrillSpnegoAuthenticator.java
new file mode 100644
index 0000000..2b7da56
--- /dev/null
+++ 
b/exec/java-exec/src/test/java/org/apache/drill/exec/server/rest/spnego/TestDrillSpnegoAuthenticator.java
@@ -0,0 +1,286 @@
+/*
+ * 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.drill.exec.server.rest.spnego;
+
+import com.google.common.collect.Lists;
+import com.typesafe.config.ConfigValueFactory;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.drill.categories.SecurityTest;
+import org.apache.drill.common.config.DrillConfig;
+import org.apache.drill.exec.ExecConstants;
+import org.apache.drill.exec.rpc.security.KerberosHelper;
+import org.apache.drill.exec.server.DrillbitContext;
+import org.apache.drill.exec.server.options.SystemOptionManager;
+import org.apache.drill.exec.server.rest.WebServerConstants;
+import org.apache.drill.exec.server.rest.auth.DrillSpnegoAuthenticator;
+import org.apache.drill.exec.server.rest.auth.DrillSpnegoLoginService;
+import org.apache.drill.test.BaseDirTestWatcher;
+import org.apache.hadoop.security.authentication.util.KerberosName;
+import org.apache.hadoop.security.authentication.util.KerberosUtil;
+import org.apache.kerby.kerberos.kerb.client.JaasKrbUtil;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.security.Authenticator;
+import org.eclipse.jetty.security.DefaultIdentityService;
+import org.eclipse.jetty.security.UserAuthentication;
+import org.eclipse.jetty.security.authentication.SessionAuthentication;
+import org.eclipse.jetty.server.Authentication;
+import org.ietf.jgss.GSSContext;
+import org.ietf.jgss.GSSManager;
+import org.ietf.jgss.GSSName;
+import org.ietf.jgss.Oid;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.mockito.Mockito;
+import sun.security.jgss.GSSUtil;
+
+import javax.security.auth.Subject;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.lang.reflect.Field;
+import java.security.PrivilegedExceptionAction;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Test for validating {@link DrillSpnegoAuthenticator}
+ */
+@Ignore("See DRILL-5387")
+@Category(SecurityTest.class)
+public class TestDrillSpnegoAuthenticator {
+
+  private static KerberosHelper spnegoHelper;
+
+  private static final String primaryName = "HTTP";
+
+  private static DrillSpnegoAuthenticator spnegoAuthenticator;
+
+  private static final BaseDirTestWatcher dirTestWatcher = new 
BaseDirTestWatcher();
+
+  @BeforeClass
+  public static void setupTest() throws Exception {
+    spnegoHelper = new 
KerberosHelper(TestSpnegoAuthentication.class.getSimpleName(), primaryName);
+    spnegoHelper.setupKdc(dirTestWatcher.getTmpDir());
+
+
+    sun.security.krb5.Config.refresh();
+
+    // (2) Reset the default realm.
+    final Field defaultRealm = 
KerberosName.class.getDeclaredField("defaultRealm");
+    defaultRealm.setAccessible(true);
+    defaultRealm.set(null, KerberosUtil.getDefaultRealm());
+
+    // Create a DrillbitContext with service principal and keytab for 
DrillSpnegoLoginService
+    final DrillConfig newConfig = new DrillConfig(DrillConfig.create()
+        .withValue(ExecConstants.HTTP_AUTHENTICATION_MECHANISMS,
+            ConfigValueFactory.fromIterable(Lists.newArrayList("spnego")))
+        .withValue(ExecConstants.HTTP_SPNEGO_PRINCIPAL,
+            ConfigValueFactory.fromAnyRef(spnegoHelper.SERVER_PRINCIPAL))
+        .withValue(ExecConstants.HTTP_SPNEGO_KEYTAB,
+            
ConfigValueFactory.fromAnyRef(spnegoHelper.serverKeytab.toString())),
+        false);
+
+    // Create mock objects for optionManager and AuthConfiguration
+    final SystemOptionManager optionManager = 
Mockito.mock(SystemOptionManager.class);
+    Mockito.when(optionManager.getOption(ExecConstants.ADMIN_USERS_VALIDATOR))
+        .thenReturn(ExecConstants.ADMIN_USERS_VALIDATOR.DEFAULT_ADMIN_USERS);
+    
Mockito.when(optionManager.getOption(ExecConstants.ADMIN_USER_GROUPS_VALIDATOR))
+        
.thenReturn(ExecConstants.ADMIN_USER_GROUPS_VALIDATOR.DEFAULT_ADMIN_USER_GROUPS);
+
+    final DrillbitContext drillbitContext = 
Mockito.mock(DrillbitContext.class);
+    Mockito.when(drillbitContext.getConfig()).thenReturn(newConfig);
+    Mockito.when(drillbitContext.getOptionManager()).thenReturn(optionManager);
+
+    Authenticator.AuthConfiguration authConfiguration = 
Mockito.mock(Authenticator.AuthConfiguration.class);
+
+    spnegoAuthenticator = new DrillSpnegoAuthenticator("SPNEGO");
+    DrillSpnegoLoginService spnegoLoginService = new 
DrillSpnegoLoginService(drillbitContext);
+
+    
Mockito.when(authConfiguration.getLoginService()).thenReturn(spnegoLoginService);
+    Mockito.when(authConfiguration.getIdentityService()).thenReturn(new 
DefaultIdentityService());
+    
Mockito.when(authConfiguration.isSessionRenewedOnAuthentication()).thenReturn(true);
+
+    // Set the login service and identity service inside SpnegoAuthenticator
+    spnegoAuthenticator.setConfiguration(authConfiguration);
+  }
+
+  @AfterClass
+  public static void cleanTest() throws Exception {
+    spnegoHelper.stopKdc();
+  }
+
+  /**
+   * Test to verify response when request is sent for {@link 
WebServerConstants#SPENGO_LOGIN_RESOURCE_PATH} from
+   * unauthenticated session. Expectation is client will receive response with 
Negotiate header.
+   * @throws Exception
+   */
+  @Test
+  public void testNewSessionReqForSpnegoLogin() throws Exception {
+    final HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
+    final HttpServletResponse response = 
Mockito.mock(HttpServletResponse.class);
+    final HttpSession session = Mockito.mock(HttpSession.class);
+
+    Mockito.when(request.getSession(true)).thenReturn(session);
+    
Mockito.when(request.getRequestURI()).thenReturn(WebServerConstants.SPENGO_LOGIN_RESOURCE_PATH);
+
+    final Authentication authentication = 
spnegoAuthenticator.validateRequest(request, response, false);
+
+    assertEquals(authentication, Authentication.SEND_CONTINUE);
+    verify(response).sendError(401);
+    verify(response).setHeader(HttpHeader.WWW_AUTHENTICATE.asString(), 
HttpHeader.NEGOTIATE.asString());
+  }
+
+  /**
+   * Test to verify response when request is sent for {@link 
WebServerConstants#SPENGO_LOGIN_RESOURCE_PATH} from
+   * authenticated session. Expectation is server will find the authenticated 
UserIdentity.
+   * @throws Exception
+   */
+  @Test
+  public void testAuthClientRequestForSpnegoLoginResource() throws Exception {
+
+    final HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
+    final HttpServletResponse response = 
Mockito.mock(HttpServletResponse.class);
+    final HttpSession session = Mockito.mock(HttpSession.class);
+    final Authentication authentication = 
Mockito.mock(UserAuthentication.class);
+
+    Mockito.when(request.getSession(true)).thenReturn(session);
+    
Mockito.when(request.getRequestURI()).thenReturn(WebServerConstants.SPENGO_LOGIN_RESOURCE_PATH);
+    
Mockito.when(session.getAttribute(SessionAuthentication.__J_AUTHENTICATED)).thenReturn(authentication);
+
+    final UserAuthentication returnedAuthentication = (UserAuthentication) 
spnegoAuthenticator.validateRequest
+        (request, response, false);
+    assertEquals(authentication, returnedAuthentication);
+    verify(response, never()).sendError(401);
+    verify(response, 
never()).setHeader(HttpHeader.WWW_AUTHENTICATE.asString(), 
HttpHeader.NEGOTIATE.asString());
+  }
+
+  /**
+   * Test to verify response when request is sent for any other resource other 
than
+   * {@link WebServerConstants#SPENGO_LOGIN_RESOURCE_PATH} from authenticated 
session. Expectation is server will
+   * find the authenticated UserIdentity and will not perform the 
authentication again for new resource.
+   * @throws Exception
+   */
+  @Test
+  public void testAuthClientRequestForOtherPage() throws Exception {
+
+    final HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
+    final HttpServletResponse response = 
Mockito.mock(HttpServletResponse.class);
+    final HttpSession session = Mockito.mock(HttpSession.class);
+    final Authentication authentication = 
Mockito.mock(UserAuthentication.class);
+
+    Mockito.when(request.getSession(true)).thenReturn(session);
+    
Mockito.when(request.getRequestURI()).thenReturn(WebServerConstants.WEBSERVER_ROOT_PATH);
+    
Mockito.when(session.getAttribute(SessionAuthentication.__J_AUTHENTICATED)).thenReturn(authentication);
+
+    final UserAuthentication returnedAuthentication = (UserAuthentication) 
spnegoAuthenticator.validateRequest
+        (request, response, false);
+    assertEquals(authentication, returnedAuthentication);
+    verify(response, never()).sendError(401);
+    verify(response, 
never()).setHeader(HttpHeader.WWW_AUTHENTICATE.asString(), 
HttpHeader.NEGOTIATE.asString());
+  }
+
+  /**
+   * Test to verify that when request is sent for {@link 
WebServerConstants#LOGOUT_RESOURCE_PATH} then the UserIdentity
+   * will be removed from the session and returned authentication will be null 
from
+   * {@link DrillSpnegoAuthenticator#validateRequest(ServletRequest, 
ServletResponse, boolean)}
+   * @throws Exception
+   */
+  @Test
+  public void testAuthClientRequestForLogOut() throws Exception {
+    final HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
+    final HttpServletResponse response = 
Mockito.mock(HttpServletResponse.class);
+    final HttpSession session = Mockito.mock(HttpSession.class);
+    final Authentication authentication = 
Mockito.mock(UserAuthentication.class);
+
+    Mockito.when(request.getSession(true)).thenReturn(session);
+    
Mockito.when(request.getRequestURI()).thenReturn(WebServerConstants.LOGOUT_RESOURCE_PATH);
+    
Mockito.when(session.getAttribute(SessionAuthentication.__J_AUTHENTICATED)).thenReturn(authentication);
+
+    final UserAuthentication returnedAuthentication = (UserAuthentication) 
spnegoAuthenticator.validateRequest
+        (request, response, false);
+    assertNull(returnedAuthentication);
+    verify(session).removeAttribute(SessionAuthentication.__J_AUTHENTICATED);
+    verify(response, never()).sendError(401);
+    verify(response, 
never()).setHeader(HttpHeader.WWW_AUTHENTICATE.asString(), 
HttpHeader.NEGOTIATE.asString());
+  }
+
+  /**
+   * Test to verify authentication fails when client sends invalid SPNEGO 
token for the
+   * {@link WebServerConstants#SPENGO_LOGIN_RESOURCE_PATH} resource.
+   * @throws Exception
+   */
+  @Test
+  public void testSpnegoLoginInvalidToken() throws Exception {
+
+    final HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
+    final HttpServletResponse response = 
Mockito.mock(HttpServletResponse.class);
+    final HttpSession session = Mockito.mock(HttpSession.class);
+
+    // Create client subject using it's principal and keytab
+    final Subject clientSubject = 
JaasKrbUtil.loginUsingKeytab(spnegoHelper.CLIENT_PRINCIPAL,
+        spnegoHelper.clientKeytab.getAbsoluteFile());
+
+    // Generate a SPNEGO token for the peer SERVER_PRINCIPAL from this 
CLIENT_PRINCIPAL
+    final String token = Subject.doAs(clientSubject, new 
PrivilegedExceptionAction<String>() {
+      @Override
+      public String run() throws Exception {
+
+        final GSSManager gssManager = GSSManager.getInstance();
+        GSSContext gssContext = null;
+        try {
+          final Oid oid = GSSUtil.GSS_SPNEGO_MECH_OID;
+          final GSSName serviceName = 
gssManager.createName(spnegoHelper.SERVER_PRINCIPAL, GSSName.NT_USER_NAME, oid);
+
+          gssContext = gssManager.createContext(serviceName, oid, null, 
GSSContext.DEFAULT_LIFETIME);
+          gssContext.requestCredDeleg(true);
+          gssContext.requestMutualAuth(true);
+
+          byte[] outToken = new byte[0];
+          outToken = gssContext.initSecContext(outToken, 0, outToken.length);
+          return Base64.encodeBase64String(outToken);
+
+        } finally {
+          if (gssContext != null) {
+            gssContext.dispose();
+          }
+        }
+      }
+    });
+
+    Mockito.when(request.getSession(true)).thenReturn(session);
+
+    final String httpReqAuthHeader = String.format("%s:%s", 
HttpHeader.NEGOTIATE.asString(), String.format
+        ("%s%s","1234", token));
+    
Mockito.when(request.getHeader(HttpHeader.AUTHORIZATION.asString())).thenReturn(httpReqAuthHeader);
+    
Mockito.when(request.getRequestURI()).thenReturn(WebServerConstants.SPENGO_LOGIN_RESOURCE_PATH);
+
+    assertEquals(spnegoAuthenticator.validateRequest(request, response, 
false), Authentication.UNAUTHENTICATED);
+
+    verify(session, 
never()).setAttribute(SessionAuthentication.__J_AUTHENTICATED, null);
+    verify(response, never()).sendError(401);
+    verify(response, 
never()).setHeader(HttpHeader.WWW_AUTHENTICATE.asString(), 
HttpHeader.NEGOTIATE.asString());
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/drill/blob/adee4614/exec/java-exec/src/test/java/org/apache/drill/exec/server/rest/spnego/TestSpnegoAuthentication.java
----------------------------------------------------------------------
diff --git 
a/exec/java-exec/src/test/java/org/apache/drill/exec/server/rest/spnego/TestSpnegoAuthentication.java
 
b/exec/java-exec/src/test/java/org/apache/drill/exec/server/rest/spnego/TestSpnegoAuthentication.java
new file mode 100644
index 0000000..51171cd
--- /dev/null
+++ 
b/exec/java-exec/src/test/java/org/apache/drill/exec/server/rest/spnego/TestSpnegoAuthentication.java
@@ -0,0 +1,326 @@
+/*
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.drill.exec.server.rest.spnego;
+
+
+import com.google.common.collect.Lists;
+import com.typesafe.config.ConfigValueFactory;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.drill.categories.SecurityTest;
+import org.apache.drill.common.config.DrillConfig;
+import org.apache.drill.common.scanner.ClassPathScanner;
+import org.apache.drill.common.scanner.persistence.ScanResult;
+import org.apache.drill.exec.ExecConstants;
+import org.apache.drill.exec.exception.DrillbitStartupException;
+import org.apache.drill.exec.rpc.security.AuthenticatorProviderImpl;
+import org.apache.drill.exec.rpc.security.KerberosHelper;
+import org.apache.drill.exec.rpc.security.plain.PlainFactory;
+import org.apache.drill.exec.server.DrillbitContext;
+import org.apache.drill.exec.server.options.SystemOptionManager;
+import org.apache.drill.exec.server.rest.auth.DrillHttpSecurityHandlerProvider;
+import org.apache.drill.exec.server.rest.auth.DrillSpnegoLoginService;
+import org.apache.drill.test.BaseDirTestWatcher;
+import org.apache.hadoop.security.authentication.util.KerberosName;
+import org.apache.hadoop.security.authentication.util.KerberosUtil;
+import org.apache.kerby.kerberos.kerb.client.JaasKrbUtil;
+import org.eclipse.jetty.server.UserIdentity;
+import org.ietf.jgss.GSSContext;
+import org.ietf.jgss.GSSManager;
+import org.ietf.jgss.GSSName;
+import org.ietf.jgss.Oid;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.mockito.Mockito;
+import sun.security.jgss.GSSUtil;
+
+import javax.security.auth.Subject;
+import java.lang.reflect.Field;
+import java.security.PrivilegedExceptionAction;
+
+import static junit.framework.TestCase.fail;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test for validating {@link DrillSpnegoLoginService}
+ */
+@Ignore("See DRILL-5387")
+@Category(SecurityTest.class)
+public class TestSpnegoAuthentication {
+
+  private static KerberosHelper spnegoHelper;
+
+  private static final String primaryName = "HTTP";
+
+  private static final BaseDirTestWatcher dirTestWatcher = new 
BaseDirTestWatcher();
+
+  @BeforeClass
+  public static void setupTest() throws Exception {
+    spnegoHelper = new 
KerberosHelper(TestSpnegoAuthentication.class.getSimpleName(), primaryName);
+    spnegoHelper.setupKdc(dirTestWatcher.getTmpDir());
+
+
+    sun.security.krb5.Config.refresh();
+
+    // (2) Reset the default realm.
+    final Field defaultRealm = 
KerberosName.class.getDeclaredField("defaultRealm");
+    defaultRealm.setAccessible(true);
+    defaultRealm.set(null, KerberosUtil.getDefaultRealm());
+  }
+
+  /**
+   * Both SPNEGO and FORM mechanism is enabled for WebServer in configuration. 
Test to see if the respective security
+   * handlers are created successfully or not.
+   * @throws Exception
+   */
+  @Test
+  public void testSPNEGOAndFORMEnabled() throws Exception {
+
+    final DrillConfig newConfig = new DrillConfig(DrillConfig.create()
+        .withValue(ExecConstants.USER_AUTHENTICATION_ENABLED,
+            ConfigValueFactory.fromAnyRef(true))
+        .withValue(ExecConstants.HTTP_AUTHENTICATION_MECHANISMS,
+            ConfigValueFactory.fromIterable(Lists.newArrayList("form", 
"spnego")))
+        .withValue(ExecConstants.HTTP_SPNEGO_PRINCIPAL,
+            ConfigValueFactory.fromAnyRef(spnegoHelper.SERVER_PRINCIPAL))
+        .withValue(ExecConstants.HTTP_SPNEGO_KEYTAB,
+            
ConfigValueFactory.fromAnyRef(spnegoHelper.serverKeytab.toString())),
+        false);
+
+    final ScanResult scanResult = ClassPathScanner.fromPrescan(newConfig);
+    final AuthenticatorProviderImpl authenticatorProvider = 
Mockito.mock(AuthenticatorProviderImpl.class);
+    
Mockito.when(authenticatorProvider.containsFactory(PlainFactory.SIMPLE_NAME)).thenReturn(true);
+
+    final DrillbitContext context = Mockito.mock(DrillbitContext.class);
+    Mockito.when(context.getClasspathScan()).thenReturn(scanResult);
+    Mockito.when(context.getConfig()).thenReturn(newConfig);
+    Mockito.when(context.getAuthProvider()).thenReturn(authenticatorProvider);
+
+    final DrillHttpSecurityHandlerProvider securityProvider = new 
DrillHttpSecurityHandlerProvider(newConfig, context);
+    assertTrue(securityProvider.isFormEnabled());
+    assertTrue(securityProvider.isSpnegoEnabled());
+  }
+
+  /**
+   * Validate if FORM security handler is created successfully when only form 
is configured as auth mechanism
+   * @throws Exception
+   */
+  @Test
+  public void testOnlyFORMEnabled() throws Exception {
+
+    final DrillConfig newConfig = new DrillConfig(DrillConfig.create()
+        .withValue(ExecConstants.HTTP_AUTHENTICATION_MECHANISMS,
+            ConfigValueFactory.fromIterable(Lists.newArrayList("form")))
+        .withValue(ExecConstants.USER_AUTHENTICATION_ENABLED,
+            ConfigValueFactory.fromAnyRef(true))
+        .withValue(ExecConstants.HTTP_SPNEGO_PRINCIPAL,
+            ConfigValueFactory.fromAnyRef(spnegoHelper.SERVER_PRINCIPAL))
+        .withValue(ExecConstants.HTTP_SPNEGO_KEYTAB,
+            
ConfigValueFactory.fromAnyRef(spnegoHelper.serverKeytab.toString())),
+        false);
+
+    final ScanResult scanResult = ClassPathScanner.fromPrescan(newConfig);
+    final AuthenticatorProviderImpl authenticatorProvider = 
Mockito.mock(AuthenticatorProviderImpl.class);
+    
Mockito.when(authenticatorProvider.containsFactory(PlainFactory.SIMPLE_NAME)).thenReturn(true);
+
+    final DrillbitContext context = Mockito.mock(DrillbitContext.class);
+    Mockito.when(context.getClasspathScan()).thenReturn(scanResult);
+    Mockito.when(context.getConfig()).thenReturn(newConfig);
+    Mockito.when(context.getAuthProvider()).thenReturn(authenticatorProvider);
+
+    final DrillHttpSecurityHandlerProvider securityProvider = new 
DrillHttpSecurityHandlerProvider(newConfig, context);
+    assertTrue(securityProvider.isFormEnabled());
+    assertTrue(!securityProvider.isSpnegoEnabled());
+  }
+
+  /**
+   * Validate failure in creating FORM security handler when PAM authenticator 
is absent. PAM authenticator is provided
+   * via {@link PlainFactory#getAuthenticator()}
+   * @throws Exception
+   */
+  @Test
+  public void testFORMEnabledWithPlainDisabled() throws Exception {
+    try {
+      final DrillConfig newConfig = new DrillConfig(DrillConfig.create()
+          .withValue(ExecConstants.USER_AUTHENTICATION_ENABLED,
+              ConfigValueFactory.fromAnyRef(true))
+          .withValue(ExecConstants.HTTP_AUTHENTICATION_MECHANISMS,
+              ConfigValueFactory.fromIterable(Lists.newArrayList("form")))
+          .withValue(ExecConstants.HTTP_SPNEGO_PRINCIPAL,
+              ConfigValueFactory.fromAnyRef(spnegoHelper.SERVER_PRINCIPAL))
+          .withValue(ExecConstants.HTTP_SPNEGO_KEYTAB,
+              
ConfigValueFactory.fromAnyRef(spnegoHelper.serverKeytab.toString())),
+          false);
+
+      final ScanResult scanResult = ClassPathScanner.fromPrescan(newConfig);
+      final AuthenticatorProviderImpl authenticatorProvider = 
Mockito.mock(AuthenticatorProviderImpl.class);
+      
Mockito.when(authenticatorProvider.containsFactory(PlainFactory.SIMPLE_NAME)).thenReturn(false);
+
+      final DrillbitContext context = Mockito.mock(DrillbitContext.class);
+      Mockito.when(context.getClasspathScan()).thenReturn(scanResult);
+      Mockito.when(context.getConfig()).thenReturn(newConfig);
+      
Mockito.when(context.getAuthProvider()).thenReturn(authenticatorProvider);
+
+      final DrillHttpSecurityHandlerProvider securityProvider =
+          new DrillHttpSecurityHandlerProvider(newConfig, context);
+      fail();
+    } catch(Exception ex) {
+      assertTrue(ex instanceof DrillbitStartupException);
+    }
+  }
+
+  /**
+   * Validate only SPNEGO security handler is configured properly when enabled 
via configuration
+   * @throws Exception
+   */
+  @Test
+  public void testOnlySPNEGOEnabled() throws Exception {
+
+    final DrillConfig newConfig = new DrillConfig(DrillConfig.create()
+        .withValue(ExecConstants.HTTP_AUTHENTICATION_MECHANISMS,
+            ConfigValueFactory.fromIterable(Lists.newArrayList("spnego")))
+        .withValue(ExecConstants.USER_AUTHENTICATION_ENABLED,
+            ConfigValueFactory.fromAnyRef(true))
+        .withValue(ExecConstants.HTTP_SPNEGO_PRINCIPAL,
+            ConfigValueFactory.fromAnyRef(spnegoHelper.SERVER_PRINCIPAL))
+        .withValue(ExecConstants.HTTP_SPNEGO_KEYTAB,
+            
ConfigValueFactory.fromAnyRef(spnegoHelper.serverKeytab.toString())),
+        false);
+
+    final ScanResult scanResult = ClassPathScanner.fromPrescan(newConfig);
+    final AuthenticatorProviderImpl authenticatorProvider = 
Mockito.mock(AuthenticatorProviderImpl.class);
+    
Mockito.when(authenticatorProvider.containsFactory(PlainFactory.SIMPLE_NAME)).thenReturn(false);
+
+    final DrillbitContext context = Mockito.mock(DrillbitContext.class);
+    Mockito.when(context.getClasspathScan()).thenReturn(scanResult);
+    Mockito.when(context.getConfig()).thenReturn(newConfig);
+    Mockito.when(context.getAuthProvider()).thenReturn(authenticatorProvider);
+
+    final DrillHttpSecurityHandlerProvider securityProvider = new 
DrillHttpSecurityHandlerProvider(newConfig, context);
+
+    assertTrue(!securityProvider.isFormEnabled());
+    assertTrue(securityProvider.isSpnegoEnabled());
+  }
+
+  /**
+   * Validate when none of the security mechanism is specified in the
+   * {@link ExecConstants#HTTP_AUTHENTICATION_MECHANISMS}, FORM security 
handler is still configured correctly when
+   * authentication is enabled along with PAM authenticator module.
+   * @throws Exception
+   */
+  @Test
+  public void testConfigBackwardCompatibility() throws Exception {
+
+    final DrillConfig newConfig = new DrillConfig(DrillConfig.create()
+        .withValue(ExecConstants.USER_AUTHENTICATION_ENABLED,
+            ConfigValueFactory.fromAnyRef(true)),
+        false);
+
+    final ScanResult scanResult = ClassPathScanner.fromPrescan(newConfig);
+    final AuthenticatorProviderImpl authenticatorProvider = 
Mockito.mock(AuthenticatorProviderImpl.class);
+    
Mockito.when(authenticatorProvider.containsFactory(PlainFactory.SIMPLE_NAME)).thenReturn(true);
+
+    final DrillbitContext context = Mockito.mock(DrillbitContext.class);
+    Mockito.when(context.getClasspathScan()).thenReturn(scanResult);
+    Mockito.when(context.getConfig()).thenReturn(newConfig);
+    Mockito.when(context.getAuthProvider()).thenReturn(authenticatorProvider);
+
+    final DrillHttpSecurityHandlerProvider securityProvider = new 
DrillHttpSecurityHandlerProvider(newConfig, context);
+
+    assertTrue(securityProvider.isFormEnabled());
+    assertTrue(!securityProvider.isSpnegoEnabled());
+  }
+
+  /**
+   * Validate successful {@link DrillSpnegoLoginService#login(String, Object)} 
when provided with client token for a
+   * configured service principal.
+   * @throws Exception
+   */
+  @Test
+  public void testDrillSpnegoLoginService() throws Exception {
+
+    // Create client subject using it's principal and keytab
+    final Subject clientSubject = 
JaasKrbUtil.loginUsingKeytab(spnegoHelper.CLIENT_PRINCIPAL,
+            spnegoHelper.clientKeytab.getAbsoluteFile());
+
+    // Generate a SPNEGO token for the peer SERVER_PRINCIPAL from this 
CLIENT_PRINCIPAL
+    final String token = Subject.doAs(clientSubject, new 
PrivilegedExceptionAction<String>() {
+      @Override
+      public String run() throws Exception {
+
+        final GSSManager gssManager = GSSManager.getInstance();
+        GSSContext gssContext = null;
+        try {
+          final Oid oid = GSSUtil.GSS_SPNEGO_MECH_OID;
+          final GSSName serviceName = 
gssManager.createName(spnegoHelper.SERVER_PRINCIPAL, GSSName.NT_USER_NAME, oid);
+
+          gssContext = gssManager.createContext(serviceName, oid, null, 
GSSContext.DEFAULT_LIFETIME);
+          gssContext.requestCredDeleg(true);
+          gssContext.requestMutualAuth(true);
+
+          byte[] outToken = new byte[0];
+          outToken = gssContext.initSecContext(outToken, 0, outToken.length);
+          return Base64.encodeBase64String(outToken);
+
+        } finally {
+          if (gssContext != null) {
+            gssContext.dispose();
+          }
+        }
+      }
+    });
+
+    // Create a DrillbitContext with service principal and keytab for 
DrillSpnegoLoginService
+    final DrillConfig newConfig = new DrillConfig(DrillConfig.create()
+        .withValue(ExecConstants.HTTP_AUTHENTICATION_MECHANISMS,
+            ConfigValueFactory.fromIterable(Lists.newArrayList("spnego")))
+        .withValue(ExecConstants.HTTP_SPNEGO_PRINCIPAL,
+            ConfigValueFactory.fromAnyRef(spnegoHelper.SERVER_PRINCIPAL))
+        .withValue(ExecConstants.HTTP_SPNEGO_KEYTAB,
+            
ConfigValueFactory.fromAnyRef(spnegoHelper.serverKeytab.toString())),
+        false);
+
+
+    final SystemOptionManager optionManager = 
Mockito.mock(SystemOptionManager.class);
+    Mockito.when(optionManager.getOption(ExecConstants.ADMIN_USERS_VALIDATOR))
+        .thenReturn(ExecConstants.ADMIN_USERS_VALIDATOR.DEFAULT_ADMIN_USERS);
+    
Mockito.when(optionManager.getOption(ExecConstants.ADMIN_USER_GROUPS_VALIDATOR))
+        
.thenReturn(ExecConstants.ADMIN_USER_GROUPS_VALIDATOR.DEFAULT_ADMIN_USER_GROUPS);
+
+    final DrillbitContext drillbitContext = 
Mockito.mock(DrillbitContext.class);
+    Mockito.when(drillbitContext.getConfig()).thenReturn(newConfig);
+    Mockito.when(drillbitContext.getOptionManager()).thenReturn(optionManager);
+
+    final DrillSpnegoLoginService loginService = new 
DrillSpnegoLoginService(drillbitContext);
+
+    // Authenticate the client using its SPNEGO token
+    final UserIdentity user = loginService.login(null, token);
+
+    // Validate the UserIdentity of authenticated client
+    assertTrue(user != null);
+    
assertTrue(user.getUserPrincipal().getName().equals(spnegoHelper.CLIENT_PRINCIPAL));
+    assertTrue(user.isUserInRole("authenticated", null));
+  }
+
+  @AfterClass
+  public static void cleanTest() throws Exception {
+    spnegoHelper.stopKdc();
+  }
+}

http://git-wip-us.apache.org/repos/asf/drill/blob/adee4614/exec/java-exec/src/test/java/org/apache/drill/exec/server/rest/spnego/TestSpnegoConfig.java
----------------------------------------------------------------------
diff --git 
a/exec/java-exec/src/test/java/org/apache/drill/exec/server/rest/spnego/TestSpnegoConfig.java
 
b/exec/java-exec/src/test/java/org/apache/drill/exec/server/rest/spnego/TestSpnegoConfig.java
new file mode 100644
index 0000000..7803b9a
--- /dev/null
+++ 
b/exec/java-exec/src/test/java/org/apache/drill/exec/server/rest/spnego/TestSpnegoConfig.java
@@ -0,0 +1,167 @@
+/*
+ * 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.drill.exec.server.rest.spnego;
+
+import com.google.common.collect.Lists;
+import com.typesafe.config.ConfigValueFactory;
+import org.apache.drill.categories.SecurityTest;
+import org.apache.drill.common.config.DrillConfig;
+import org.apache.drill.common.exceptions.DrillException;
+import org.apache.drill.exec.ExecConstants;
+import org.apache.drill.exec.rpc.security.KerberosHelper;
+import 
org.apache.drill.exec.rpc.user.security.testing.UserAuthenticatorTestImpl;
+import org.apache.drill.exec.server.rest.auth.SpnegoConfig;
+import org.apache.drill.test.BaseDirTestWatcher;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.security.authentication.util.KerberosName;
+import org.apache.hadoop.security.authentication.util.KerberosUtil;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import java.lang.reflect.Field;
+
+import static junit.framework.TestCase.fail;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test for validating {@link SpnegoConfig}
+ */
+@Ignore("See DRILL-5387")
+@Category(SecurityTest.class)
+public class TestSpnegoConfig {
+  //private static final org.slf4j.Logger logger = 
org.slf4j.LoggerFactory.getLogger(TestSpnegoConfig.class);
+
+  private static KerberosHelper spnegoHelper;
+
+  private static final String primaryName = "HTTP";
+
+  private static final BaseDirTestWatcher dirTestWatcher = new 
BaseDirTestWatcher();
+
+  @BeforeClass
+  public static void setupTest() throws Exception {
+    spnegoHelper = new 
KerberosHelper(TestSpnegoAuthentication.class.getSimpleName(), primaryName);
+    spnegoHelper.setupKdc(dirTestWatcher.getTmpDir());
+
+
+    sun.security.krb5.Config.refresh();
+
+    // (2) Reset the default realm.
+    final Field defaultRealm = 
KerberosName.class.getDeclaredField("defaultRealm");
+    defaultRealm.setAccessible(true);
+    defaultRealm.set(null, KerberosUtil.getDefaultRealm());
+  }
+
+  @AfterClass
+  public static void cleanTest() throws Exception {
+    spnegoHelper.stopKdc();
+  }
+
+  /**
+   * Test invalid {@link SpnegoConfig} with missing keytab and principal
+   * @throws Exception
+   */
+  @Test
+  public void testInvalidSpnegoConfig() throws Exception {
+    // Invalid configuration for SPNEGO
+    try {
+      final DrillConfig newConfig = new DrillConfig(DrillConfig.create()
+          .withValue(ExecConstants.USER_AUTHENTICATION_ENABLED,
+              ConfigValueFactory.fromAnyRef(true))
+          .withValue(ExecConstants.AUTHENTICATION_MECHANISMS,
+              ConfigValueFactory.fromIterable(Lists.newArrayList("plain")))
+          .withValue(ExecConstants.USER_AUTHENTICATOR_IMPL,
+              ConfigValueFactory.fromAnyRef(UserAuthenticatorTestImpl.TYPE)),
+          false);
+
+      final SpnegoConfig spnegoConfig = new SpnegoConfig(newConfig);
+      spnegoConfig.validateSpnegoConfig();
+      fail();
+    } catch (Exception ex) {
+      assertTrue(ex instanceof DrillException);
+    }
+  }
+
+  /**
+   * Invalid configuration with keytab only and missing principal
+   * @throws Exception
+   */
+  @Test
+  public void testSpnegoConfigOnlyKeytab() throws Exception {
+    try {
+      final DrillConfig newConfig = new 
DrillConfig(DrillConfig.create().withValue(ExecConstants.USER_AUTHENTICATION_ENABLED,
 
ConfigValueFactory.fromAnyRef(true)).withValue(ExecConstants.AUTHENTICATION_MECHANISMS,
 
ConfigValueFactory.fromIterable(Lists.newArrayList("plain"))).withValue(ExecConstants.HTTP_SPNEGO_KEYTAB,
 
ConfigValueFactory.fromAnyRef(spnegoHelper.serverKeytab.toString())).withValue(ExecConstants.USER_AUTHENTICATOR_IMPL,
 ConfigValueFactory.fromAnyRef(UserAuthenticatorTestImpl.TYPE)), false);
+
+      final SpnegoConfig spnegoConfig = new SpnegoConfig(newConfig);
+      spnegoConfig.validateSpnegoConfig();
+      fail();
+    } catch (Exception ex) {
+      assertTrue(ex instanceof DrillException);
+    }
+  }
+
+  /**
+   * Invalid configuration with principal only and missing keytab
+   * @throws Exception
+   */
+  @Test
+  public void testSpnegoConfigOnlyPrincipal() throws Exception {
+    try {
+      final DrillConfig newConfig = new 
DrillConfig(DrillConfig.create().withValue(ExecConstants.USER_AUTHENTICATION_ENABLED,
 
ConfigValueFactory.fromAnyRef(true)).withValue(ExecConstants.AUTHENTICATION_MECHANISMS,
 
ConfigValueFactory.fromIterable(Lists.newArrayList("plain"))).withValue(ExecConstants.HTTP_SPNEGO_PRINCIPAL,
 
ConfigValueFactory.fromAnyRef(spnegoHelper.SERVER_PRINCIPAL)).withValue(ExecConstants.USER_AUTHENTICATOR_IMPL,
 ConfigValueFactory.fromAnyRef(UserAuthenticatorTestImpl.TYPE)), false);
+
+      final SpnegoConfig spnegoConfig = new SpnegoConfig(newConfig);
+      spnegoConfig.validateSpnegoConfig();
+      fail();
+    } catch (Exception ex) {
+      assertTrue(ex instanceof DrillException);
+    }
+  }
+
+  /**
+   * Valid Configuration with both keytab & principal
+   * @throws Exception
+   */
+  @Test
+  public void testValidSpnegoConfig() throws Exception {
+
+    try {
+      final DrillConfig newConfig = new DrillConfig(DrillConfig.create()
+          .withValue(ExecConstants.USER_AUTHENTICATION_ENABLED,
+              ConfigValueFactory.fromAnyRef(true))
+          .withValue(ExecConstants.AUTHENTICATION_MECHANISMS,
+              ConfigValueFactory.fromIterable(Lists.newArrayList("plain")))
+          .withValue(ExecConstants.HTTP_SPNEGO_PRINCIPAL,
+              ConfigValueFactory.fromAnyRef(spnegoHelper.SERVER_PRINCIPAL))
+          .withValue(ExecConstants.HTTP_SPNEGO_KEYTAB,
+              
ConfigValueFactory.fromAnyRef(spnegoHelper.serverKeytab.toString()))
+          .withValue(ExecConstants.USER_AUTHENTICATOR_IMPL,
+              ConfigValueFactory.fromAnyRef(UserAuthenticatorTestImpl.TYPE)),
+          false);
+
+      final SpnegoConfig spnegoConfig = new SpnegoConfig(newConfig);
+      spnegoConfig.validateSpnegoConfig();
+      UserGroupInformation ugi = spnegoConfig.getLoggedInUgi();
+      assertEquals(primaryName, ugi.getShortUserName());
+      assertEquals(spnegoHelper.SERVER_PRINCIPAL, ugi.getUserName());
+    } catch (Exception ex) {
+      fail();
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/drill/blob/adee4614/exec/rpc/src/main/java/org/apache/drill/exec/rpc/BasicServer.java
----------------------------------------------------------------------
diff --git a/exec/rpc/src/main/java/org/apache/drill/exec/rpc/BasicServer.java 
b/exec/rpc/src/main/java/org/apache/drill/exec/rpc/BasicServer.java
index 67fc89a..888a49b 100644
--- a/exec/rpc/src/main/java/org/apache/drill/exec/rpc/BasicServer.java
+++ b/exec/rpc/src/main/java/org/apache/drill/exec/rpc/BasicServer.java
@@ -202,6 +202,7 @@ public abstract class BasicServer<T extends EnumLite, SC 
extends ServerConnectio
         if (e instanceof BindException && allowPortHunting) {
           continue;
         }
+
         final UserException bindException =
             UserException
               .resourceError( e )

Reply via email to