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");