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

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


The following commit(s) were added to refs/heads/master by this push:
     new 0e4d16b  HIVE-21009: Adding ability for user to set bind user (David 
McGinnis reviewed by Prasanth Jayachandran)
0e4d16b is described below

commit 0e4d16b462bf9abd7ec58e60936e24ee4302736c
Author: David McGinnis <656337+davidov...@users.noreply.github.com>
AuthorDate: Wed Feb 6 14:52:16 2019 -0800

    HIVE-21009: Adding ability for user to set bind user (David McGinnis 
reviewed by Prasanth Jayachandran)
---
 .../java/org/apache/hadoop/hive/conf/HiveConf.java |  10 ++
 service/pom.xml                                    |  11 ++
 .../auth/LdapAuthenticationProviderImpl.java       |  32 +++++-
 .../auth/TestLdapAuthenticationProviderImpl.java   | 113 +++++++++++++++++++++
 4 files changed, 164 insertions(+), 2 deletions(-)

diff --git a/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java 
b/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java
index a3b03ca..2156ff1 100644
--- a/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java
+++ b/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java
@@ -3499,6 +3499,16 @@ public class HiveConf extends Configuration {
         "For example: 
(&(objectClass=group)(objectClass=top)(instanceType=4)(cn=Domain*)) \n" +
         "(&(objectClass=person)(|(sAMAccountName=admin)(|(memberOf=CN=Domain 
Admins,CN=Users,DC=domain,DC=com)" +
         "(memberOf=CN=Administrators,CN=Builtin,DC=domain,DC=com))))"),
+    
HIVE_SERVER2_PLAIN_LDAP_BIND_USER("hive.server2.authentication.ldap.binddn", 
null,
+        "The user with which to bind to the LDAP server, and search for the 
full domain name " +
+        "of the user being authenticated.\n" +
+        "This should be the full domain name of the user, and should have 
search access across all " +
+        "users in the LDAP tree.\n" +
+        "If not specified, then the user being authenticated will be used as 
the bind user.\n" +
+        "For example: CN=bindUser,CN=Users,DC=subdomain,DC=domain,DC=com"),
+    
HIVE_SERVER2_PLAIN_LDAP_BIND_PASSWORD("hive.server2.authentication.ldap.bindpw",
 null,
+        "The password for the bind user, to be used to search for the full 
name of the user being authenticated.\n" +
+        "If the username is specified, this parameter must also be 
specified."),
     
HIVE_SERVER2_CUSTOM_AUTHENTICATION_CLASS("hive.server2.custom.authentication.class",
 null,
         "Custom authentication class. Used when property\n" +
         "'hive.server2.authentication' is set to 'CUSTOM'. Provided class\n" +
diff --git a/service/pom.xml b/service/pom.xml
index eca6f3b..30b7398 100644
--- a/service/pom.xml
+++ b/service/pom.xml
@@ -36,6 +36,17 @@
     <!-- intra-project -->
     <dependency>
       <groupId>org.apache.hive</groupId>
+      <artifactId>hive-common</artifactId>
+      <version>${project.version}</version>
+      <exclusions>
+        <exclusion>
+          <groupId>org.eclipse.jetty.aggregate</groupId>
+          <artifactId>jetty-all</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.hive</groupId>
       <artifactId>hive-exec</artifactId>
       <version>${project.version}</version>
     </dependency>
diff --git 
a/service/src/java/org/apache/hive/service/auth/LdapAuthenticationProviderImpl.java
 
b/service/src/java/org/apache/hive/service/auth/LdapAuthenticationProviderImpl.java
index 73bbb6b..0120513 100644
--- 
a/service/src/java/org/apache/hive/service/auth/LdapAuthenticationProviderImpl.java
+++ 
b/service/src/java/org/apache/hive/service/auth/LdapAuthenticationProviderImpl.java
@@ -18,9 +18,10 @@
 package org.apache.hive.service.auth;
 
 import javax.security.sasl.AuthenticationException;
-
+import javax.naming.NamingException;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableList;
+import java.io.IOException;
 import java.util.Iterator;
 import java.util.List;
 import org.apache.commons.lang.StringUtils;
@@ -68,9 +69,36 @@ public class LdapAuthenticationProviderImpl implements 
PasswdAuthenticationProvi
   @Override
   public void Authenticate(String user, String password) throws 
AuthenticationException {
     DirSearch search = null;
+    String bindUser = 
this.conf.getVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_BIND_USER);
+    String bindPassword = null;
+    try {
+      char[] rawPassword = 
this.conf.getPassword(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_BIND_PASSWORD.toString());
+      if (rawPassword != null) {
+        bindPassword = new String(rawPassword);
+      }
+    } catch (IOException e) {
+      bindPassword = null;
+    }
+    boolean usedBind = bindUser != null && bindPassword != null;
+    if (!usedBind) {
+      // If no bind user or bind password was specified,
+      // we assume the user we are authenticating has the ability to search
+      // the LDAP tree, so we use it as the "binding" account.
+      // This is the way it worked before bind users were allowed in the LDAP 
authenticator,
+      // so we keep existing systems working.
+      bindUser = user;
+      bindPassword = password;
+    }
     try {
-      search = createDirSearch(user, password);
+      search = createDirSearch(bindUser, bindPassword);
       applyFilter(search, user);
+      if (usedBind) {
+        // If we used the bind user, then we need to authenticate again,
+        // this time using the full user name we got during the bind process.
+        createDirSearch(search.findUserDn(user), password);
+      }
+    } catch (NamingException e) {
+      throw new AuthenticationException("Unable to find the user in the LDAP 
tree. " + e.getMessage());
     } finally {
       ServiceUtils.cleanup(LOG, search);
     }
diff --git 
a/service/src/test/org/apache/hive/service/auth/TestLdapAuthenticationProviderImpl.java
 
b/service/src/test/org/apache/hive/service/auth/TestLdapAuthenticationProviderImpl.java
index 43453fa..0396b74 100644
--- 
a/service/src/test/org/apache/hive/service/auth/TestLdapAuthenticationProviderImpl.java
+++ 
b/service/src/test/org/apache/hive/service/auth/TestLdapAuthenticationProviderImpl.java
@@ -22,6 +22,7 @@ import java.util.Arrays;
 import javax.naming.NamingException;
 import javax.security.sasl.AuthenticationException;
 import org.apache.hadoop.hive.conf.HiveConf;
+import org.apache.hadoop.security.alias.CredentialProviderFactory;
 import org.apache.hive.service.auth.ldap.DirSearch;
 import org.apache.hive.service.auth.ldap.DirSearchFactory;
 import org.apache.hive.service.auth.ldap.LdapSearchFactory;
@@ -324,6 +325,118 @@ public class TestLdapAuthenticationProviderImpl {
     verify(search, atLeastOnce()).close();
   }
 
+  @Test
+  public void testAuthenticateWithBindInCredentialFilePasses() throws 
AuthenticationException, NamingException {
+    String bindUser = "cn=BindUser,ou=Users,ou=branch1,dc=mycorp,dc=com";
+    String bindPass = "testPassword";
+    String authFullUser = "cn=user1,ou=Users,ou=branch1,dc=mycorp,dc=com";
+    String authUser = "user1";
+    String authPass = "Blah";
+    String tmpDir = System.getProperty("build.dir");
+    String credentialsPath = "jceks://file" + tmpDir + 
"/test-classes/creds/test.jceks";
+    conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_BIND_USER, bindUser);
+    conf.set(CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH, 
credentialsPath);
+
+    System.out.println(tmpDir);
+
+    when(search.findUserDn(eq(authUser))).thenReturn(authFullUser);
+
+    auth = new LdapAuthenticationProviderImpl(conf, factory);
+    auth.Authenticate(authUser, authPass);
+
+    verify(factory, times(1)).getInstance(isA(HiveConf.class), eq(bindUser), 
eq(bindPass));
+    verify(factory, times(1)).getInstance(isA(HiveConf.class), 
eq(authFullUser), eq(authPass));
+    verify(search, times(1)).findUserDn(eq(authUser));
+  }
+
+  @Test
+  public void testAuthenticateWithBindInMissingCredentialFilePasses() throws 
AuthenticationException, NamingException {
+    String bindUser = "cn=BindUser,ou=Users,ou=branch1,dc=mycorp,dc=com";
+    String authUser = "user1";
+    String authPass = "Blah";
+    String tmpDir = System.getProperty("build.dir");
+    String credentialsPath = "jceks://file" + tmpDir + 
"/test-classes/creds/nonExistent.jceks";
+    conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_BIND_USER, bindUser);
+    conf.set(CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH, 
credentialsPath);
+
+    auth = new LdapAuthenticationProviderImpl(conf, factory);
+    auth.Authenticate(authUser, authPass);
+
+    verify(factory, times(1)).getInstance(isA(HiveConf.class), eq(authUser), 
eq(authPass));
+  }
+
+  @Test
+  public void testAuthenticateWithBindUserPasses() throws 
AuthenticationException, NamingException {
+    String bindUser = "cn=BindUser,ou=Users,ou=branch1,dc=mycorp,dc=com";
+    String bindPass = "Blah";
+    String authFullUser = "cn=user1,ou=Users,ou=branch1,dc=mycorp,dc=com";
+    String authUser = "user1";
+    String authPass = "Blah2";
+    conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_BIND_USER, bindUser);
+    conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_BIND_PASSWORD, 
bindPass);
+
+    when(search.findUserDn(eq(authUser))).thenReturn(authFullUser);
+
+    auth = new LdapAuthenticationProviderImpl(conf, factory);
+    auth.Authenticate(authUser, authPass);
+
+    verify(factory, times(1)).getInstance(isA(HiveConf.class), eq(bindUser), 
eq(bindPass));
+    verify(factory, times(1)).getInstance(isA(HiveConf.class), 
eq(authFullUser), eq(authPass));
+    verify(search, times(1)).findUserDn(eq(authUser));
+  }
+
+  @Test
+  public void testAuthenticateWithBindUserFailsOnAuthentication() throws 
AuthenticationException, NamingException {
+    String bindUser = "cn=BindUser,ou=Users,ou=branch1,dc=mycorp,dc=com";
+    String bindPass = "Blah";
+    String authFullUser = "cn=user1,ou=Users,ou=branch1,dc=mycorp,dc=com";
+    String authUser = "user1";
+    String authPass = "Blah2";
+    conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_BIND_USER, bindUser);
+    conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_BIND_PASSWORD, 
bindPass);
+
+    thrown.expect(AuthenticationException.class);
+    when(factory.getInstance(any(HiveConf.class), eq(authFullUser), 
eq(authPass))).
+      thenThrow(AuthenticationException.class);
+    when(search.findUserDn(eq(authUser))).thenReturn(authFullUser);
+
+    auth = new LdapAuthenticationProviderImpl(conf, factory);
+    auth.Authenticate(authUser, authPass);
+  }
+
+  @Test
+  public void testAuthenticateWithBindUserFailsOnGettingDn() throws 
AuthenticationException, NamingException {
+    String bindUser = "cn=BindUser,ou=Users,ou=branch1,dc=mycorp,dc=com";
+    String bindPass = "Blah";
+    String authFullUser = "cn=user1,ou=Users,ou=branch1,dc=mycorp,dc=com";
+    String authUser = "user1";
+    String authPass = "Blah2";
+    conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_BIND_USER, bindUser);
+    conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_BIND_PASSWORD, 
bindPass);
+
+    thrown.expect(AuthenticationException.class);
+    when(search.findUserDn(eq(authUser))).thenThrow(NamingException.class);
+
+    auth = new LdapAuthenticationProviderImpl(conf, factory);
+    auth.Authenticate(authUser, authPass);
+  }
+
+  @Test
+  public void testAuthenticateWithBindUserFailsOnBinding() throws 
AuthenticationException {
+    String bindUser = "cn=BindUser,ou=Users,ou=branch1,dc=mycorp,dc=com";
+    String bindPass = "Blah";
+    String authUser = "user1";
+    String authPass = "Blah2";
+    conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_BIND_USER, bindUser);
+    conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_BIND_PASSWORD, 
bindPass);
+
+    thrown.expect(AuthenticationException.class);
+    when(factory.getInstance(any(HiveConf.class), eq(bindUser), 
eq(bindPass))).thenThrow(AuthenticationException.class);
+
+    auth = new LdapAuthenticationProviderImpl(conf, factory);
+    auth.Authenticate(authUser, authPass);
+  }
+
   private void expectAuthenticationExceptionForInvalidPassword() {
     thrown.expect(AuthenticationException.class);
     thrown.expectMessage("a null or blank password has been provided");

Reply via email to