http://git-wip-us.apache.org/repos/asf/hive/blob/990927e3/service/src/java/org/apache/hive/service/auth/ldap/UserSearchFilterFactory.java ---------------------------------------------------------------------- diff --git a/service/src/java/org/apache/hive/service/auth/ldap/UserSearchFilterFactory.java b/service/src/java/org/apache/hive/service/auth/ldap/UserSearchFilterFactory.java new file mode 100644 index 0000000..3218875 --- /dev/null +++ b/service/src/java/org/apache/hive/service/auth/ldap/UserSearchFilterFactory.java @@ -0,0 +1,65 @@ +/** + * 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.hive.service.auth.ldap; + +import java.util.Collection; +import javax.naming.NamingException; +import javax.security.sasl.AuthenticationException; +import org.apache.hadoop.hive.conf.HiveConf; + +/** + * A factory for a {@link Filter} that check whether provided user could be found in the directory. + * <br> + * The produced filter object filters out all users that are not found in the directory. + */ +public final class UserSearchFilterFactory implements FilterFactory { + + /** + * {@inheritDoc} + */ + @Override + public Filter getInstance(HiveConf conf) { + Collection<String> groupFilter = conf.getStringCollection( + HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_GROUPFILTER.varname); + Collection<String> userFilter = conf.getStringCollection( + HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_USERFILTER.varname); + + if (groupFilter.isEmpty() && userFilter.isEmpty()) { + return null; + } + + return new UserSearchFilter(); + } + + private static final class UserSearchFilter implements Filter { + @Override + public void apply(DirSearch client, String user) throws AuthenticationException { + try { + String userDn = client.findUserDn(user); + + // This should not be null because we were allowed to bind with this username + // safe check in case we were able to bind anonymously. + if (userDn == null) { + throw new AuthenticationException("Authentication failed: User search failed"); + } + } catch (NamingException e) { + throw new AuthenticationException("LDAP Authentication failed for user", e); + } + } + } +}
http://git-wip-us.apache.org/repos/asf/hive/blob/990927e3/service/src/test/org/apache/hive/service/auth/TestLdapAtnProviderWithMiniDS.java ---------------------------------------------------------------------- diff --git a/service/src/test/org/apache/hive/service/auth/TestLdapAtnProviderWithMiniDS.java b/service/src/test/org/apache/hive/service/auth/TestLdapAtnProviderWithMiniDS.java index 089a059..23a048a 100644 --- a/service/src/test/org/apache/hive/service/auth/TestLdapAtnProviderWithMiniDS.java +++ b/service/src/test/org/apache/hive/service/auth/TestLdapAtnProviderWithMiniDS.java @@ -225,7 +225,6 @@ public class TestLdapAtnProviderWithMiniDS extends AbstractLdapTestUnit { hiveConf = new HiveConf(); ldapProvider = new LdapAuthenticationProviderImpl(hiveConf); - ldapProvider.init(hiveConf); } @AfterClass @@ -259,7 +258,7 @@ public class TestLdapAtnProviderWithMiniDS extends AbstractLdapTestUnit { } } - ldapProvider.init(hiveConf); + ldapProvider = new LdapAuthenticationProviderImpl(hiveConf); } @Test http://git-wip-us.apache.org/repos/asf/hive/blob/990927e3/service/src/test/org/apache/hive/service/auth/TestLdapAuthenticationProviderImpl.java ---------------------------------------------------------------------- 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 f276906..4fad755 100644 --- a/service/src/test/org/apache/hive/service/auth/TestLdapAuthenticationProviderImpl.java +++ b/service/src/test/org/apache/hive/service/auth/TestLdapAuthenticationProviderImpl.java @@ -15,51 +15,260 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.hive.service.auth; +import java.io.IOException; +import java.util.Arrays; +import javax.naming.NamingException; import javax.security.sasl.AuthenticationException; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileOutputStream; - -import junit.framework.TestCase; import org.apache.hadoop.hive.conf.HiveConf; +import org.apache.hive.service.auth.ldap.DirSearch; +import org.apache.hive.service.auth.ldap.DirSearchFactory; +import org.apache.hive.service.auth.ldap.LdapSearchFactory; +import org.junit.Test; +import org.junit.Before; +import org.junit.Rule; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +public class TestLdapAuthenticationProviderImpl { + + @Rule + public ExpectedException thrown = ExpectedException.none(); -public class TestLdapAuthenticationProviderImpl extends TestCase { + public HiveConf conf; + public LdapAuthenticationProviderImpl auth; - private static HiveConf hiveConf; - private static byte[] hiveConfBackup; + @Mock + public DirSearchFactory factory; - @Override - public void setUp() throws Exception { - hiveConf = new HiveConf(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - hiveConf.writeXml(baos); - baos.close(); - hiveConfBackup = baos.toByteArray(); - hiveConf.set("hive.server2.authentication.ldap.url", "localhost"); - FileOutputStream fos = new FileOutputStream(new File(hiveConf.getHiveSiteLocation().toURI())); - hiveConf.writeXml(fos); - fos.close(); + @Mock + public DirSearch search; + + @Before + public void setup() throws AuthenticationException { + conf = new HiveConf(); + conf.set("hive.root.logger", "DEBUG,console"); + conf.set("hive.server2.authentication.ldap.url", "localhost"); + when(factory.getInstance(any(HiveConf.class), anyString(), anyString())).thenReturn(search); } - public void testLdapEmptyPassword() { - LdapAuthenticationProviderImpl ldapImpl = new LdapAuthenticationProviderImpl(hiveConf); - try { - ldapImpl.Authenticate("user", ""); - assertFalse(true); - } catch (AuthenticationException e) { - assertTrue(e.getMessage(), e.getMessage().contains("a null or blank password has been provided")); - } + @Test + public void authenticateGivenBlankPassword() throws Exception { + auth = new LdapAuthenticationProviderImpl(conf, new LdapSearchFactory()); + expectAuthenticationExceptionForInvalidPassword(); + auth.Authenticate("user", ""); + } + + @Test + public void authenticateGivenStringWithNullCharacterForPassword() throws Exception { + auth = new LdapAuthenticationProviderImpl(conf, new LdapSearchFactory()); + expectAuthenticationExceptionForInvalidPassword(); + auth.Authenticate("user", "\0"); + } + + @Test + public void authenticateGivenNullForPassword() throws Exception { + auth = new LdapAuthenticationProviderImpl(conf, new LdapSearchFactory()); + expectAuthenticationExceptionForInvalidPassword(); + auth.Authenticate("user", null); + } + + @Test + public void testAuthenticateNoUserOrGroupFilter() throws NamingException, AuthenticationException, IOException { + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_USERDNPATTERN, + "cn=%s,ou=Users,dc=mycorp,dc=com:cn=%s,ou=PowerUsers,dc=mycorp,dc=com"); + + DirSearchFactory factory = mock(DirSearchFactory.class); + + when(search.findUserDn("user1")).thenReturn("cn=user1,ou=PowerUsers,dc=mycorp,dc=com"); + + when(factory.getInstance(conf, "cn=user1,ou=PowerUsers,dc=mycorp,dc=com", "Blah")).thenReturn(search); + when(factory.getInstance(conf, "cn=user1,ou=Users,dc=mycorp,dc=com", "Blah")).thenThrow(AuthenticationException.class); + + auth = new LdapAuthenticationProviderImpl(conf, factory); + auth.Authenticate("user1", "Blah"); + + verify(factory, times(2)).getInstance(isA(HiveConf.class), anyString(), eq("Blah")); + verify(search, atLeastOnce()).close(); } - @Override - public void tearDown() throws Exception { - if(hiveConf != null && hiveConfBackup != null) { - FileOutputStream fos = new FileOutputStream(new File(hiveConf.getHiveSiteLocation().toURI())); - fos.write(hiveConfBackup); - fos.close(); + @Test + public void testAuthenticateWhenUserFilterPasses() throws NamingException, AuthenticationException, IOException { + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_USERFILTER, + "user1,user2"); + + when(search.findUserDn("user1")).thenReturn("cn=user1,ou=PowerUsers,dc=mycorp,dc=com"); + when(search.findUserDn("user2")).thenReturn("cn=user2,ou=PowerUsers,dc=mycorp,dc=com"); + + authenticateUserAndCheckSearchIsClosed("user1"); + authenticateUserAndCheckSearchIsClosed("user2"); + } + + @Test + public void testAuthenticateWhenLoginWithDomainAndUserFilterPasses() throws NamingException, AuthenticationException, IOException { + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_USERFILTER, + "user1"); + + when(search.findUserDn("user1")).thenReturn("cn=user1,ou=PowerUsers,dc=mycorp,dc=com"); + + authenticateUserAndCheckSearchIsClosed("[email protected]"); + } + + @Test + public void testAuthenticateWhenLoginWithDnAndUserFilterPasses() throws NamingException, AuthenticationException, IOException { + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_USERFILTER, + "user1"); + + when(search.findUserDn("cn=user1,ou=PowerUsers,dc=mycorp,dc=com")).thenReturn("cn=user1,ou=PowerUsers,dc=mycorp,dc=com"); + + authenticateUserAndCheckSearchIsClosed("cn=user1,ou=PowerUsers,dc=mycorp,dc=com"); + } + + @Test + public void testAuthenticateWhenUserSearchFails() throws NamingException, AuthenticationException, IOException { + thrown.expect(AuthenticationException.class); + + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_USERFILTER, "user1,user2"); + + when(search.findUserDn("user1")).thenReturn(null); + + authenticateUserAndCheckSearchIsClosed("user1"); + } + + @Test + public void testAuthenticateWhenUserFilterFails() throws NamingException, AuthenticationException, IOException { + thrown.expect(AuthenticationException.class); + + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_USERFILTER, "user1,user2"); + + when(search.findUserDn("user3")).thenReturn("cn=user3,ou=PowerUsers,dc=mycorp,dc=com"); + + authenticateUserAndCheckSearchIsClosed("user3"); + } + + @Test + public void testAuthenticateWhenGroupFilterPasses() throws NamingException, AuthenticationException, IOException { + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_GROUPFILTER, "group1,group2"); + + when(search.findUserDn("user1")).thenReturn("cn=user1,ou=PowerUsers,dc=mycorp,dc=com"); + when(search.findUserDn("user2")).thenReturn("cn=user2,ou=PowerUsers,dc=mycorp,dc=com"); + + when(search.findGroupsForUser("cn=user1,ou=PowerUsers,dc=mycorp,dc=com")) + .thenReturn(Arrays.asList( + "cn=testGroup,ou=Groups,dc=mycorp,dc=com", + "cn=group1,ou=Groups,dc=mycorp,dc=com")); + when(search.findGroupsForUser("cn=user2,ou=PowerUsers,dc=mycorp,dc=com")) + .thenReturn(Arrays.asList( + "cn=testGroup,ou=Groups,dc=mycorp,dc=com", + "cn=group2,ou=Groups,dc=mycorp,dc=com")); + + authenticateUserAndCheckSearchIsClosed("user1"); + authenticateUserAndCheckSearchIsClosed("user2"); + } + + @Test + public void testAuthenticateWhenUserAndGroupFiltersPass() throws NamingException, AuthenticationException, IOException { + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_GROUPFILTER, "group1,group2"); + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_USERFILTER, "user1,user2"); + + when(search.findUserDn("user1")).thenReturn("cn=user1,ou=PowerUsers,dc=mycorp,dc=com"); + when(search.findUserDn("user2")).thenReturn("cn=user2,ou=PowerUsers,dc=mycorp,dc=com"); + + when(search.findGroupsForUser("cn=user1,ou=PowerUsers,dc=mycorp,dc=com")) + .thenReturn(Arrays.asList( + "cn=testGroup,ou=Groups,dc=mycorp,dc=com", + "cn=group1,ou=Groups,dc=mycorp,dc=com")); + when(search.findGroupsForUser("cn=user2,ou=PowerUsers,dc=mycorp,dc=com")) + .thenReturn(Arrays.asList( + "cn=testGroup,ou=Groups,dc=mycorp,dc=com", + "cn=group2,ou=Groups,dc=mycorp,dc=com")); + + authenticateUserAndCheckSearchIsClosed("user1"); + authenticateUserAndCheckSearchIsClosed("user2"); + } + + @Test + public void testAuthenticateWhenUserFilterPassesAndGroupFilterFails() + throws NamingException, AuthenticationException, IOException { + thrown.expect(AuthenticationException.class); + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_GROUPFILTER, "group1,group2"); + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_USERFILTER, "user1,user2"); + + when(search.findUserDn("user1")).thenReturn("cn=user1,ou=PowerUsers,dc=mycorp,dc=com"); + + when(search.findGroupsForUser("cn=user1,ou=PowerUsers,dc=mycorp,dc=com")) + .thenReturn(Arrays.asList( + "cn=testGroup,ou=Groups,dc=mycorp,dc=com", + "cn=OtherGroup,ou=Groups,dc=mycorp,dc=com")); + + authenticateUserAndCheckSearchIsClosed("user1"); + } + + @Test + public void testAuthenticateWhenUserFilterFailsAndGroupFilterPasses() + throws NamingException, AuthenticationException, IOException { + thrown.expect(AuthenticationException.class); + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_GROUPFILTER, "group3"); + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_USERFILTER, "user1,user2"); + + when(search.findUserDn("user3")).thenReturn("cn=user3,ou=PowerUsers,dc=mycorp,dc=com"); + + when(search.findGroupsForUser("cn=user3,ou=PowerUsers,dc=mycorp,dc=com")) + .thenReturn(Arrays.asList( + "cn=testGroup,ou=Groups,dc=mycorp,dc=com", + "cn=group3,ou=Groups,dc=mycorp,dc=com")); + + authenticateUserAndCheckSearchIsClosed("user3"); + } + + @Test + public void testAuthenticateWhenCustomQueryFilterPasses() throws NamingException, AuthenticationException, IOException { + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_BASEDN, "dc=mycorp,dc=com"); + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_CUSTOMLDAPQUERY, + "(&(objectClass=person)(|(memberOf=CN=Domain Admins,CN=Users,DC=apache,DC=org)(memberOf=CN=Administrators,CN=Builtin,DC=apache,DC=org)))"); + + when(search.executeCustomQuery(anyString())).thenReturn(Arrays.asList( + "cn=user1,ou=PowerUsers,dc=mycorp,dc=com", + "cn=user2,ou=PowerUsers,dc=mycorp,dc=com")); + + authenticateUserAndCheckSearchIsClosed("user1"); + } + + @Test + public void testAuthenticateWhenCustomQueryFilterFailsAndUserFilterPasses() throws NamingException, AuthenticationException, IOException { + thrown.expect(AuthenticationException.class); + + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_BASEDN, "dc=mycorp,dc=com"); + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_CUSTOMLDAPQUERY, + "(&(objectClass=person)(|(memberOf=CN=Domain Admins,CN=Users,DC=apache,DC=org)(memberOf=CN=Administrators,CN=Builtin,DC=apache,DC=org)))"); + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_USERFILTER, "user3"); + + when(search.findUserDn("user3")).thenReturn("cn=user3,ou=PowerUsers,dc=mycorp,dc=com"); + when(search.executeCustomQuery(anyString())).thenReturn(Arrays.asList( + "cn=user1,ou=PowerUsers,dc=mycorp,dc=com", + "cn=user2,ou=PowerUsers,dc=mycorp,dc=com")); + + authenticateUserAndCheckSearchIsClosed("user3"); + } + + private void expectAuthenticationExceptionForInvalidPassword() { + thrown.expect(AuthenticationException.class); + thrown.expectMessage("a null or blank password has been provided"); + } + + private void authenticateUserAndCheckSearchIsClosed(String user) throws IOException { + auth = new LdapAuthenticationProviderImpl(conf, factory); + try { + auth.Authenticate(user, "password doesn't matter"); + } finally { + verify(search, atLeastOnce()).close(); } } } http://git-wip-us.apache.org/repos/asf/hive/blob/990927e3/service/src/test/org/apache/hive/service/auth/ldap/Credentials.java ---------------------------------------------------------------------- diff --git a/service/src/test/org/apache/hive/service/auth/ldap/Credentials.java b/service/src/test/org/apache/hive/service/auth/ldap/Credentials.java new file mode 100644 index 0000000..ce22b8e --- /dev/null +++ b/service/src/test/org/apache/hive/service/auth/ldap/Credentials.java @@ -0,0 +1,41 @@ +/** + * 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.hive.service.auth.ldap; + +public final class Credentials { + + private final String user; + private final String password; + + private Credentials(String user, String password) { + this.user = user; + this.password = password; + } + + public static Credentials of(String user, String password) { + return new Credentials(user, password); + } + + public String getUser() { + return user; + } + + public String getPassword() { + return password; + } +} http://git-wip-us.apache.org/repos/asf/hive/blob/990927e3/service/src/test/org/apache/hive/service/auth/ldap/LdapTestUtils.java ---------------------------------------------------------------------- diff --git a/service/src/test/org/apache/hive/service/auth/ldap/LdapTestUtils.java b/service/src/test/org/apache/hive/service/auth/ldap/LdapTestUtils.java new file mode 100644 index 0000000..d4e034f --- /dev/null +++ b/service/src/test/org/apache/hive/service/auth/ldap/LdapTestUtils.java @@ -0,0 +1,126 @@ +/** + * 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.hive.service.auth.ldap; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.directory.BasicAttribute; +import javax.naming.directory.BasicAttributes; +import javax.naming.directory.SearchResult; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import org.mockito.stubbing.OngoingStubbing; + +public final class LdapTestUtils { + + private LdapTestUtils() { + } + + public static NamingEnumeration<SearchResult> mockEmptyNamingEnumeration() throws NamingException { + return mockNamingEnumeration(new SearchResult[0]); + } + + public static NamingEnumeration<SearchResult> mockNamingEnumeration(String... dns) throws NamingException { + return mockNamingEnumeration(mockSearchResults(dns).toArray(new SearchResult[0])); + } + + public static NamingEnumeration<SearchResult> mockNamingEnumeration(SearchResult... searchResults) throws NamingException { + NamingEnumeration<SearchResult> ne = + (NamingEnumeration<SearchResult>) mock(NamingEnumeration.class); + mockHasMoreMethod(ne, searchResults.length); + if (searchResults.length > 0) { + List<SearchResult> mockedResults = Arrays.asList(searchResults); + mockNextMethod(ne, mockedResults); + } + return ne; + } + + public static void mockHasMoreMethod(NamingEnumeration<SearchResult> ne, int length) throws NamingException { + OngoingStubbing<Boolean> hasMoreStub = when(ne.hasMore()); + for (int i = 0; i < length; i++) { + hasMoreStub = hasMoreStub.thenReturn(true); + } + hasMoreStub.thenReturn(false); + } + + public static void mockNextMethod(NamingEnumeration<SearchResult> ne, List<SearchResult> searchResults) throws NamingException { + OngoingStubbing<SearchResult> nextStub = when(ne.next()); + for (SearchResult searchResult : searchResults) { + nextStub = nextStub.thenReturn(searchResult); + } + } + + public static List<SearchResult> mockSearchResults(String[] dns) { + List<SearchResult> list = new ArrayList<>(); + for (String dn : dns) { + list.add(mockSearchResult(dn, null)); + } + return list; + } + + public static SearchResult mockSearchResult(String dn, Attributes attributes) { + SearchResult searchResult = mock(SearchResult.class); + when(searchResult.getNameInNamespace()).thenReturn(dn); + when(searchResult.getAttributes()).thenReturn(attributes); + return searchResult; + } + + public static Attributes mockEmptyAttributes() throws NamingException { + return mockAttributes(); + } + + public static Attributes mockAttributes(String name, String value) throws NamingException { + return mockAttributes(new NameValues(name, value)); + } + + public static Attributes mockAttributes(String name1, String value1, String name2, String value2) throws NamingException { + if (name1.equals(name2)) { + return mockAttributes(new NameValues(name1, value1, value2)); + } else { + return mockAttributes(new NameValues(name1, value1), new NameValues(name2, value2)); + } + } + + private static Attributes mockAttributes(NameValues... namedValues) throws NamingException { + Attributes attributes = new BasicAttributes(); + for (NameValues namedValue : namedValues) { + Attribute attr = new BasicAttribute(namedValue.name); + for (String value : namedValue.values) { + attr.add(value); + } + attributes.put(attr); + } + return attributes; + } + + private static final class NameValues { + final String name; + final String[] values; + + public NameValues(String name, String... values) { + this.name = name; + this.values = values; + } + } +} http://git-wip-us.apache.org/repos/asf/hive/blob/990927e3/service/src/test/org/apache/hive/service/auth/ldap/TestChainFilter.java ---------------------------------------------------------------------- diff --git a/service/src/test/org/apache/hive/service/auth/ldap/TestChainFilter.java b/service/src/test/org/apache/hive/service/auth/ldap/TestChainFilter.java new file mode 100644 index 0000000..9caa233 --- /dev/null +++ b/service/src/test/org/apache/hive/service/auth/ldap/TestChainFilter.java @@ -0,0 +1,103 @@ +/** + * 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.hive.service.auth.ldap; + +import java.io.IOException; +import javax.naming.NamingException; +import javax.security.sasl.AuthenticationException; +import org.apache.hadoop.hive.conf.HiveConf; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.runners.MockitoJUnitRunner; + +import org.junit.Before; +import org.mockito.Mock; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +public class TestChainFilter { + + private FilterFactory factory; + private HiveConf conf; + + @Mock + public Filter filter1; + + @Mock + public Filter filter2; + + @Mock + public Filter filter3; + + @Mock + public FilterFactory factory1; + + @Mock + public FilterFactory factory2; + + @Mock + public FilterFactory factory3; + + @Mock + private DirSearch search; + + @Before + public void setup() { + conf = new HiveConf(); + factory = new ChainFilterFactory(factory1, factory2, factory3); + } + + @Test + public void testFactoryAllNull() { + assertNull(factory.getInstance(conf)); + } + + @Test + public void testFactoryAllEmpty() { + FilterFactory emptyFactory = new ChainFilterFactory(); + assertNull(emptyFactory.getInstance(conf)); + } + + @Test + public void testFactory() throws AuthenticationException { + when(factory1.getInstance(any(HiveConf.class))).thenReturn(filter1); + when(factory2.getInstance(any(HiveConf.class))).thenReturn(filter2); + when(factory3.getInstance(any(HiveConf.class))).thenReturn(filter3); + + Filter filter = factory.getInstance(conf); + + filter.apply(search, "User"); + verify(filter1, times(1)).apply(search, "User"); + verify(filter2, times(1)).apply(search, "User"); + verify(filter3, times(1)).apply(search, "User"); + } + + @Test(expected = AuthenticationException.class) + public void testApplyNegative() throws AuthenticationException, NamingException, IOException { + doThrow(AuthenticationException.class).when(filter3).apply((DirSearch) anyObject(), anyString()); + + when(factory1.getInstance(any(HiveConf.class))).thenReturn(filter1); + when(factory3.getInstance(any(HiveConf.class))).thenReturn(filter3); + + Filter filter = factory.getInstance(conf); + + filter.apply(search, "User"); + } +} http://git-wip-us.apache.org/repos/asf/hive/blob/990927e3/service/src/test/org/apache/hive/service/auth/ldap/TestCustomQueryFilter.java ---------------------------------------------------------------------- diff --git a/service/src/test/org/apache/hive/service/auth/ldap/TestCustomQueryFilter.java b/service/src/test/org/apache/hive/service/auth/ldap/TestCustomQueryFilter.java new file mode 100644 index 0000000..fd4b898 --- /dev/null +++ b/service/src/test/org/apache/hive/service/auth/ldap/TestCustomQueryFilter.java @@ -0,0 +1,85 @@ +/** + * 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.hive.service.auth.ldap; + +import java.io.IOException; +import java.util.Arrays; +import javax.naming.NamingException; +import javax.security.sasl.AuthenticationException; +import org.apache.hadoop.hive.conf.HiveConf; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.runners.MockitoJUnitRunner; + +import org.junit.Before; +import org.mockito.Mock; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +public class TestCustomQueryFilter { + + private static final String USER2_DN = "uid=user2,ou=People,dc=example,dc=com"; + private static final String USER1_DN = "uid=user1,ou=People,dc=example,dc=com"; + private static final String CUSTOM_QUERY = "(&(objectClass=person)(|(uid=user1)(uid=user2)))"; + + private FilterFactory factory; + private HiveConf conf; + + @Mock + private DirSearch search; + + @Before + public void setup() { + conf = new HiveConf(); + conf.set("hive.root.logger", "DEBUG,console"); + factory = new CustomQueryFilterFactory(); + } + + @Test + public void testFactory() { + conf.unset(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_CUSTOMLDAPQUERY.varname); + assertNull(factory.getInstance(conf)); + + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_CUSTOMLDAPQUERY, CUSTOM_QUERY); + assertNotNull(factory.getInstance(conf)); + } + + @Test + public void testApplyPositive() throws AuthenticationException, NamingException, IOException { + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_CUSTOMLDAPQUERY, CUSTOM_QUERY); + + when(search.executeCustomQuery(eq(CUSTOM_QUERY))).thenReturn(Arrays.asList(USER1_DN, USER2_DN)); + + Filter filter = factory.getInstance(conf); + filter.apply(search, "user1"); + filter.apply(search, "user2"); + } + + + @Test(expected = AuthenticationException.class) + public void testApplyNegative() throws AuthenticationException, NamingException, IOException { + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_CUSTOMLDAPQUERY, CUSTOM_QUERY); + + when(search.executeCustomQuery(eq(CUSTOM_QUERY))).thenReturn(Arrays.asList(USER1_DN, USER2_DN)); + + Filter filter = factory.getInstance(conf); + filter.apply(search, "user3"); + } +} http://git-wip-us.apache.org/repos/asf/hive/blob/990927e3/service/src/test/org/apache/hive/service/auth/ldap/TestGroupFilter.java ---------------------------------------------------------------------- diff --git a/service/src/test/org/apache/hive/service/auth/ldap/TestGroupFilter.java b/service/src/test/org/apache/hive/service/auth/ldap/TestGroupFilter.java new file mode 100644 index 0000000..0cc2ead --- /dev/null +++ b/service/src/test/org/apache/hive/service/auth/ldap/TestGroupFilter.java @@ -0,0 +1,101 @@ +/** + * 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.hive.service.auth.ldap; + +import java.io.IOException; +import java.util.Arrays; +import javax.naming.NamingException; +import javax.security.sasl.AuthenticationException; +import org.apache.hadoop.hive.conf.HiveConf; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.runners.MockitoJUnitRunner; + +import org.junit.Before; +import org.mockito.Mock; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +public class TestGroupFilter { + + private FilterFactory factory; + private HiveConf conf; + + @Mock + private DirSearch search; + + @Before + public void setup() { + conf = new HiveConf(); + conf.set("hive.root.logger", "DEBUG,console"); + factory = new GroupFilterFactory(); + } + + @Test + public void testFactory() { + conf.unset(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_GROUPFILTER.varname); + assertNull(factory.getInstance(conf)); + + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_GROUPFILTER, "G1"); + assertNotNull(factory.getInstance(conf)); + } + + @Test + public void testApplyPositive() throws AuthenticationException, NamingException, IOException { + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_GROUPFILTER, "HiveUsers"); + + when(search.findUserDn(eq("user1"))) + .thenReturn("cn=user1,ou=People,dc=example,dc=com"); + when(search.findUserDn(eq("cn=user2,dc=example,dc=com"))) + .thenReturn("cn=user2,ou=People,dc=example,dc=com"); + when(search.findUserDn(eq("[email protected]"))) + .thenReturn("cn=user3,ou=People,dc=example,dc=com"); + + when(search.findGroupsForUser(eq("cn=user1,ou=People,dc=example,dc=com"))) + .thenReturn(Arrays.asList( + "cn=SuperUsers,ou=Groups,dc=example,dc=com", + "cn=Office1,ou=Groups,dc=example,dc=com", + "cn=HiveUsers,ou=Groups,dc=example,dc=com", + "cn=G1,ou=Groups,dc=example,dc=com")); + when(search.findGroupsForUser(eq("cn=user2,ou=People,dc=example,dc=com"))) + .thenReturn(Arrays.asList( + "cn=HiveUsers,ou=Groups,dc=example,dc=com")); + when(search.findGroupsForUser(eq("cn=user3,ou=People,dc=example,dc=com"))) + .thenReturn(Arrays.asList( + "cn=HiveUsers,ou=Groups,dc=example,dc=com", + "cn=G1,ou=Groups,dc=example,dc=com", + "cn=G2,ou=Groups,dc=example,dc=com")); + + Filter filter = factory.getInstance(conf); + filter.apply(search, "user1"); + filter.apply(search, "cn=user2,dc=example,dc=com"); + filter.apply(search, "[email protected]"); + } + + @Test(expected = AuthenticationException.class) + public void testApplyNegative() throws AuthenticationException, NamingException, IOException { + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_GROUPFILTER, "HiveUsers"); + + when(search.findGroupsForUser(eq("user1"))).thenReturn(Arrays.asList("SuperUsers", "Office1", "G1", "G2")); + + Filter filter = factory.getInstance(conf); + filter.apply(search, "user1"); + } +} http://git-wip-us.apache.org/repos/asf/hive/blob/990927e3/service/src/test/org/apache/hive/service/auth/ldap/TestLdapSearch.java ---------------------------------------------------------------------- diff --git a/service/src/test/org/apache/hive/service/auth/ldap/TestLdapSearch.java b/service/src/test/org/apache/hive/service/auth/ldap/TestLdapSearch.java new file mode 100644 index 0000000..499b624 --- /dev/null +++ b/service/src/test/org/apache/hive/service/auth/ldap/TestLdapSearch.java @@ -0,0 +1,209 @@ +/** + * 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.hive.service.auth.ldap; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.DirContext; +import javax.naming.directory.SearchControls; +import javax.naming.directory.SearchResult; +import org.apache.hadoop.hive.conf.HiveConf; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; +import static org.apache.hive.service.auth.ldap.LdapTestUtils.*; + +@RunWith(MockitoJUnitRunner.class) +public class TestLdapSearch { + + @Mock + private DirContext ctx; + + private HiveConf conf; + private LdapSearch search; + + @Before + public void setup() { + conf = new HiveConf(); + } + + @Test + public void testClose() throws NamingException { + search = new LdapSearch(conf, ctx); + search.close(); + verify(ctx, atLeastOnce()).close(); + } + + @Test + public void testFindUserDnWhenUserDnPositive() throws NamingException { + NamingEnumeration<SearchResult> searchResult = mockNamingEnumeration("CN=User1,OU=org1,DC=foo,DC=bar"); + when(ctx.search(anyString(), anyString(), any(SearchControls.class))) + .thenReturn(searchResult) + .thenThrow(NamingException.class); + search = new LdapSearch(conf, ctx); + String expected = "CN=User1,OU=org1,DC=foo,DC=bar"; + String actual = search.findUserDn("CN=User1,OU=org1"); + assertEquals(expected, actual); + } + + @Test + public void testFindUserDnWhenUserDnNegativeDuplicates() throws NamingException { + NamingEnumeration<SearchResult> searchResult = mockNamingEnumeration( + "CN=User1,OU=org1,DC=foo,DC=bar", + "CN=User1,OU=org2,DC=foo,DC=bar"); + when(ctx.search(anyString(), anyString(), any(SearchControls.class))).thenReturn(searchResult); + search = new LdapSearch(conf, ctx); + assertNull(search.findUserDn("CN=User1,DC=foo,DC=bar")); + } + + @Test + public void testFindUserDnWhenUserDnNegativeNone() throws NamingException { + NamingEnumeration<SearchResult> searchResult = mockEmptyNamingEnumeration(); + when(ctx.search(anyString(), anyString(), any(SearchControls.class))).thenReturn(searchResult); + search = new LdapSearch(conf, ctx); + assertNull(search.findUserDn("CN=User1,DC=foo,DC=bar")); + } + + @Test + public void testFindUserDnWhenUserPatternFoundBySecondPattern() throws NamingException { + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_USERDNPATTERN, + "CN=%s,OU=org1,DC=foo,DC=bar:CN=%s,OU=org2,DC=foo,DC=bar"); + NamingEnumeration<SearchResult> emptyResult = mockEmptyNamingEnumeration(); + NamingEnumeration<SearchResult> validResult = mockNamingEnumeration("CN=User1,OU=org2,DC=foo,DC=bar"); + when(ctx.search(anyString(), anyString(), any(SearchControls.class))) + .thenReturn(emptyResult) + .thenReturn(validResult); + search = new LdapSearch(conf, ctx); + String expected = "CN=User1,OU=org2,DC=foo,DC=bar"; + String actual = search.findUserDn("User1"); + assertEquals(expected, actual); + verify(ctx).search(eq("OU=org1,DC=foo,DC=bar"), contains("CN=User1"), any(SearchControls.class)); + verify(ctx).search(eq("OU=org2,DC=foo,DC=bar"), contains("CN=User1"), any(SearchControls.class)); + } + + @Test + public void testFindUserDnWhenUserPatternFoundByFirstPattern() throws NamingException { + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_USERDNPATTERN, + "CN=%s,OU=org1,DC=foo,DC=bar:CN=%s,OU=org2,DC=foo,DC=bar"); + NamingEnumeration<SearchResult> emptyResult = mockEmptyNamingEnumeration(); + NamingEnumeration<SearchResult> validResult = mockNamingEnumeration("CN=User1,OU=org2,DC=foo,DC=bar"); + when(ctx.search(anyString(), anyString(), any(SearchControls.class))) + .thenReturn(validResult) + .thenReturn(emptyResult); + search = new LdapSearch(conf, ctx); + String expected = "CN=User1,OU=org2,DC=foo,DC=bar"; + String actual = search.findUserDn("User1"); + assertEquals(expected, actual); + verify(ctx).search(eq("OU=org1,DC=foo,DC=bar"), contains("CN=User1"), any(SearchControls.class)); + } + + @Test + public void testFindUserDnWhenUserPatternFoundByUniqueIdentifier() throws NamingException { + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_USERDNPATTERN, + "CN=%s,OU=org1,DC=foo,DC=bar"); + NamingEnumeration<SearchResult> validResult = mockNamingEnumeration("CN=User1,OU=org1,DC=foo,DC=bar"); + when(ctx.search(anyString(), anyString(), any(SearchControls.class))) + .thenReturn(null) + .thenReturn(validResult); + search = new LdapSearch(conf, ctx); + String expected = "CN=User1,OU=org1,DC=foo,DC=bar"; + String actual = search.findUserDn("User1"); + assertEquals(expected, actual); + verify(ctx).search(eq("OU=org1,DC=foo,DC=bar"), contains("CN=User1"), any(SearchControls.class)); + verify(ctx).search(eq("OU=org1,DC=foo,DC=bar"), contains("uid=User1"), any(SearchControls.class)); + } + + @Test + public void testFindUserDnWhenUserPatternFoundByUniqueIdentifierNegativeNone() throws NamingException { + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_USERDNPATTERN, + "CN=%s,OU=org1,DC=foo,DC=bar"); + when(ctx.search(anyString(), anyString(), any(SearchControls.class))) + .thenReturn(null) + .thenReturn(null); + search = new LdapSearch(conf, ctx); + assertNull(search.findUserDn("User1")); + } + + @Test + public void testFindUserDnWhenUserPatternFoundByUniqueIdentifierNegativeMany() throws NamingException { + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_USERDNPATTERN, + "CN=%s,OU=org1,DC=foo,DC=bar"); + NamingEnumeration<SearchResult> manyResult = mockNamingEnumeration( + "CN=User1,OU=org1,DC=foo,DC=bar", + "CN=User12,OU=org1,DC=foo,DC=bar"); + when(ctx.search(anyString(), anyString(), any(SearchControls.class))) + .thenReturn(null) + .thenReturn(manyResult); + search = new LdapSearch(conf, ctx); + assertNull(search.findUserDn("User1")); + } + + @Test + public void testFindGroupsForUser() throws NamingException { + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_GROUPDNPATTERN, + "CN=%s,OU=org1,DC=foo,DC=bar"); + + NamingEnumeration<SearchResult> groupsResult = mockNamingEnumeration("CN=Group1,OU=org1,DC=foo,DC=bar"); + when(ctx.search(eq("OU=org1,DC=foo,DC=bar"), contains("User1"), any(SearchControls.class))) + .thenReturn(groupsResult); + + search = new LdapSearch(conf, ctx); + + List<String> expected = Arrays.asList("CN=Group1,OU=org1,DC=foo,DC=bar"); + List<String> actual = search.findGroupsForUser("CN=User1,OU=org1,DC=foo,DC=bar"); + assertEquals(expected, actual); + } + + @Test + public void testExecuteCustomQuery() throws NamingException { + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_BASEDN, "dc=example,dc=com"); + + NamingEnumeration<SearchResult> customQueryResult = mockNamingEnumeration( + mockSearchResult( + "uid=group1,ou=Groups,dc=example,dc=com", + mockAttributes("member", "uid=user1,ou=People,dc=example,dc=com")), + mockSearchResult( + "uid=group2,ou=Groups,dc=example,dc=com", + mockAttributes("member", "uid=user2,ou=People,dc=example,dc=com")) + ); + + when(ctx.search(eq("dc=example,dc=com"), anyString(), any(SearchControls.class))) + .thenReturn(customQueryResult); + + search = new LdapSearch(conf, ctx); + + List<String> expected = Arrays.asList( + "uid=group1,ou=Groups,dc=example,dc=com", + "uid=user1,ou=People,dc=example,dc=com", + "uid=group2,ou=Groups,dc=example,dc=com", + "uid=user2,ou=People,dc=example,dc=com"); + List<String> actual = search.executeCustomQuery("(&(objectClass=groupOfNames)(|(cn=group1)(cn=group2)))"); + Collections.sort(expected); + Collections.sort(actual); + assertEquals(expected, actual); + } +} http://git-wip-us.apache.org/repos/asf/hive/blob/990927e3/service/src/test/org/apache/hive/service/auth/ldap/TestLdapUtils.java ---------------------------------------------------------------------- diff --git a/service/src/test/org/apache/hive/service/auth/ldap/TestLdapUtils.java b/service/src/test/org/apache/hive/service/auth/ldap/TestLdapUtils.java new file mode 100644 index 0000000..661aff4 --- /dev/null +++ b/service/src/test/org/apache/hive/service/auth/ldap/TestLdapUtils.java @@ -0,0 +1,103 @@ +/** + * 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.hive.service.auth.ldap; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.apache.hadoop.hive.conf.HiveConf; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class TestLdapUtils { + + @Test + public void testCreateCandidatePrincipalsForUserDn() { + HiveConf conf = new HiveConf(); + String userDn = "cn=user1,ou=CORP,dc=mycompany,dc=com"; + List<String> expected = Arrays.asList(userDn); + List<String> actual = LdapUtils.createCandidatePrincipals(conf, userDn); + assertEquals(expected, actual); + } + + @Test + public void testCreateCandidatePrincipalsForUserWithDomain() { + HiveConf conf = new HiveConf(); + String userWithDomain = "[email protected]"; + List<String> expected = Arrays.asList(userWithDomain); + List<String> actual = LdapUtils.createCandidatePrincipals(conf, userWithDomain); + assertEquals(expected, actual); + } + + @Test + public void testCreateCandidatePrincipalsLdapDomain() { + HiveConf conf = new HiveConf(); + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_DOMAIN, "mycompany.com"); + List<String> expected = Arrays.asList("[email protected]"); + List<String> actual = LdapUtils.createCandidatePrincipals(conf, "user1"); + assertEquals(expected, actual); + } + + @Test + public void testCreateCandidatePrincipalsUserPatternsDefaultBaseDn() { + HiveConf conf = new HiveConf(); + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_GUIDKEY, "sAMAccountName"); + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_BASEDN, "dc=mycompany,dc=com"); + List<String> expected = Arrays.asList("sAMAccountName=user1,dc=mycompany,dc=com"); + List<String> actual = LdapUtils.createCandidatePrincipals(conf, "user1"); + assertEquals(expected, actual); + } + + @Test + public void testCreateCandidatePrincipals() { + HiveConf conf = new HiveConf(); + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_BASEDN, "dc=mycompany,dc=com"); + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_USERDNPATTERN, + "cn=%s,ou=CORP1,dc=mycompany,dc=com:cn=%s,ou=CORP2,dc=mycompany,dc=com"); + List<String> expected = Arrays.asList( + "cn=user1,ou=CORP1,dc=mycompany,dc=com", + "cn=user1,ou=CORP2,dc=mycompany,dc=com"); + List<String> actual = LdapUtils.createCandidatePrincipals(conf, "user1"); + Collections.sort(expected); + Collections.sort(actual); + assertEquals(expected, actual); + } + + @Test + public void testExtractFirstRdn() { + String dn = "cn=user1,ou=CORP1,dc=mycompany,dc=com"; + String expected = "cn=user1"; + String actual = LdapUtils.extractFirstRdn(dn); + assertEquals(expected, actual); + } + + @Test + public void testExtractBaseDn() { + String dn = "cn=user1,ou=CORP1,dc=mycompany,dc=com"; + String expected = "ou=CORP1,dc=mycompany,dc=com"; + String actual = LdapUtils.extractBaseDn(dn); + assertEquals(expected, actual); + } + + @Test + public void testExtractBaseDnNegative() { + String dn = "cn=user1"; + assertNull(LdapUtils.extractBaseDn(dn)); + } +} http://git-wip-us.apache.org/repos/asf/hive/blob/990927e3/service/src/test/org/apache/hive/service/auth/ldap/TestQuery.java ---------------------------------------------------------------------- diff --git a/service/src/test/org/apache/hive/service/auth/ldap/TestQuery.java b/service/src/test/org/apache/hive/service/auth/ldap/TestQuery.java new file mode 100644 index 0000000..1f4bb1a --- /dev/null +++ b/service/src/test/org/apache/hive/service/auth/ldap/TestQuery.java @@ -0,0 +1,59 @@ +/** + * 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.hive.service.auth.ldap; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class TestQuery { + + @Test + public void testQueryBuilderFilter() { + Query q = Query.builder() + .filter("test <uid_attr>=<value> query") + .map("uid_attr", "uid") + .map("value", "Hello!") + .build(); + assertEquals("test uid=Hello! query", q.getFilter()); + assertEquals(0, q.getControls().getCountLimit()); + } + + @Test + public void testQueryBuilderLimit() { + Query q = Query.builder() + .filter("<key1>,<key2>") + .map("key1", "value1") + .map("key2", "value2") + .limit(8) + .build(); + assertEquals("value1,value2", q.getFilter()); + assertEquals(8, q.getControls().getCountLimit()); + } + + @Test + public void testQueryBuilderReturningAttributes() { + Query q = Query.builder() + .filter("(query)") + .returnAttribute("attr1") + .returnAttribute("attr2") + .build(); + assertEquals("(query)", q.getFilter()); + assertArrayEquals(new String[] {"attr1", "attr2"}, q.getControls().getReturningAttributes()); + } +} http://git-wip-us.apache.org/repos/asf/hive/blob/990927e3/service/src/test/org/apache/hive/service/auth/ldap/TestQueryFactory.java ---------------------------------------------------------------------- diff --git a/service/src/test/org/apache/hive/service/auth/ldap/TestQueryFactory.java b/service/src/test/org/apache/hive/service/auth/ldap/TestQueryFactory.java new file mode 100644 index 0000000..3054e33 --- /dev/null +++ b/service/src/test/org/apache/hive/service/auth/ldap/TestQueryFactory.java @@ -0,0 +1,79 @@ +/** + * 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.hive.service.auth.ldap; + +import org.apache.hadoop.hive.conf.HiveConf; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class TestQueryFactory { + + private QueryFactory queries; + private HiveConf conf; + + @Before + public void setup() { + conf = new HiveConf(); + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_GUIDKEY, "guid"); + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_GROUPCLASS_KEY, "superGroups"); + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_GROUPMEMBERSHIP_KEY, "member"); + queries = new QueryFactory(conf); + } + + @Test + public void testFindGroupDnById() { + Query q = queries.findGroupDnById("unique_group_id"); + String expected = "(&(objectClass=superGroups)(guid=unique_group_id))"; + String actual = q.getFilter(); + assertEquals(expected, actual); + } + + @Test + public void testFindUserDnByRdn() { + Query q = queries.findUserDnByRdn("cn=User1"); + String expected = "(&(|(objectClass=person)(objectClass=user)(objectClass=inetOrgPerson))(cn=User1))"; + String actual = q.getFilter(); + assertEquals(expected, actual); + } + + @Test + public void testFindDnByPattern() { + Query q = queries.findDnByPattern("cn=User1"); + String expected = "(cn=User1)"; + String actual = q.getFilter(); + assertEquals(expected, actual); + } + + @Test + public void testFindUserDnByName() { + Query q = queries.findUserDnByName("unique_user_id"); + String expected = "(&(|(objectClass=person)(objectClass=user)(objectClass=inetOrgPerson))(|(uid=unique_user_id)(sAMAccountName=unique_user_id)))"; + String actual = q.getFilter(); + assertEquals(expected, actual); + } + + @Test + public void testFindGroupsForUser() { + Query q = queries.findGroupsForUser("user_name", "user_Dn"); + String expected = "(&(objectClass=superGroups)(|(member=user_Dn)(member=user_name)))"; + String actual = q.getFilter(); + assertEquals(expected, actual); + } +} http://git-wip-us.apache.org/repos/asf/hive/blob/990927e3/service/src/test/org/apache/hive/service/auth/ldap/TestSearchResultHandler.java ---------------------------------------------------------------------- diff --git a/service/src/test/org/apache/hive/service/auth/ldap/TestSearchResultHandler.java b/service/src/test/org/apache/hive/service/auth/ldap/TestSearchResultHandler.java new file mode 100644 index 0000000..2615680 --- /dev/null +++ b/service/src/test/org/apache/hive/service/auth/ldap/TestSearchResultHandler.java @@ -0,0 +1,222 @@ +/** + * 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.hive.service.auth.ldap; + +import java.util.AbstractCollection; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.SearchResult; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.runners.MockitoJUnitRunner; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; +import static org.apache.hive.service.auth.ldap.LdapTestUtils.*; + +@RunWith(MockitoJUnitRunner.class) +public class TestSearchResultHandler { + + SearchResultHandler handler; + + @Test + public void testHandle() throws NamingException { + MockResultCollection resultCollection = MockResultCollection.create() + .addSearchResultWithDns("1") + .addSearchResultWithDns("2", "3"); + handler = new SearchResultHandler(resultCollection); + List<String> expected = Arrays.asList("1", "2"); + final List<String> actual = new ArrayList<>(); + handler.handle(new SearchResultHandler.RecordProcessor() { + @Override + public boolean process(SearchResult record) throws NamingException { + actual.add(record.getNameInNamespace()); + return actual.size() < 2; + } + }); + assertEquals(expected, actual); + assertAllNamingEnumerationsClosed(resultCollection); + } + + @Test + public void testGetAllLdapNamesNoRecords() throws NamingException { + MockResultCollection resultCollection = MockResultCollection.create() + .addEmptySearchResult(); + handler = new SearchResultHandler(resultCollection); + List<String> actual = handler.getAllLdapNames(); + assertEquals("Resultset size", 0, actual.size()); + assertAllNamingEnumerationsClosed(resultCollection); + } + + @Test + public void testGetAllLdapNamesWithExceptionInNamingEnumerationClose() throws NamingException { + MockResultCollection resultCollection = MockResultCollection.create() + .addSearchResultWithDns("1") + .addSearchResultWithDns("2"); + doThrow(NamingException.class).when(resultCollection.iterator().next()).close(); + handler = new SearchResultHandler(resultCollection); + List<String> actual = handler.getAllLdapNames(); + assertEquals("Resultset size", 2, actual.size()); + assertAllNamingEnumerationsClosed(resultCollection); + } + + @Test + public void testGetAllLdapNames() throws NamingException { + String objectDn1 = "cn=a1,dc=b,dc=c"; + String objectDn2 = "cn=a2,dc=b,dc=c"; + String objectDn3 = "cn=a3,dc=b,dc=c"; + MockResultCollection resultCollection = MockResultCollection.create() + .addSearchResultWithDns(objectDn1) + .addSearchResultWithDns(objectDn2, objectDn3); + handler = new SearchResultHandler(resultCollection); + List<String> expected = Arrays.asList(objectDn1, objectDn2, objectDn3); + Collections.sort(expected); + List<String> actual = handler.getAllLdapNames(); + Collections.sort(actual); + assertEquals(expected, actual); + assertAllNamingEnumerationsClosed(resultCollection); + } + + @Test + public void testGetAllLdapNamesAndAttributes() throws NamingException { + SearchResult searchResult1 = mockSearchResult("cn=a1,dc=b,dc=c", + mockAttributes("attr1", "attr1value1")); + SearchResult searchResult2 = mockSearchResult("cn=a2,dc=b,dc=c", + mockAttributes("attr1", "attr1value2", "attr2", "attr2value1")); + SearchResult searchResult3 = mockSearchResult("cn=a3,dc=b,dc=c", + mockAttributes("attr1", "attr1value3", "attr1", "attr1value4")); + SearchResult searchResult4 = mockSearchResult("cn=a4,dc=b,dc=c", + mockEmptyAttributes()); + + MockResultCollection resultCollection = MockResultCollection.create() + .addSearchResults(searchResult1) + .addSearchResults(searchResult2, searchResult3) + .addSearchResults(searchResult4); + + handler = new SearchResultHandler(resultCollection); + List<String> expected = Arrays.asList( + "cn=a1,dc=b,dc=c", "attr1value1", + "cn=a2,dc=b,dc=c", "attr1value2", "attr2value1", + "cn=a3,dc=b,dc=c", "attr1value3", "attr1value4", + "cn=a4,dc=b,dc=c"); + Collections.sort(expected); + List<String> actual = handler.getAllLdapNamesAndAttributes(); + Collections.sort(actual); + assertEquals(expected, actual); + assertAllNamingEnumerationsClosed(resultCollection); + } + + @Test + public void testHasSingleResultNoRecords() throws NamingException { + MockResultCollection resultCollection = MockResultCollection.create() + .addEmptySearchResult(); + handler = new SearchResultHandler(resultCollection); + assertFalse(handler.hasSingleResult()); + assertAllNamingEnumerationsClosed(resultCollection); + } + + @Test + public void testHasSingleResult() throws NamingException { + MockResultCollection resultCollection = MockResultCollection.create() + .addSearchResultWithDns("1"); + handler = new SearchResultHandler(resultCollection); + assertTrue(handler.hasSingleResult()); + assertAllNamingEnumerationsClosed(resultCollection); + } + + @Test + public void testHasSingleResultManyRecords() throws NamingException { + MockResultCollection resultCollection = MockResultCollection.create() + .addSearchResultWithDns("1") + .addSearchResultWithDns("2"); + handler = new SearchResultHandler(resultCollection); + assertFalse(handler.hasSingleResult()); + assertAllNamingEnumerationsClosed(resultCollection); + } + + @Test(expected = NamingException.class) + public void testGetSingleLdapNameNoRecords() throws NamingException { + MockResultCollection resultCollection = MockResultCollection.create() + .addEmptySearchResult(); + handler = new SearchResultHandler(resultCollection); + try { + handler.getSingleLdapName(); + } finally { + assertAllNamingEnumerationsClosed(resultCollection); + } + } + + @Test + public void testGetSingleLdapName() throws NamingException { + String objectDn = "cn=a,dc=b,dc=c"; + MockResultCollection resultCollection = MockResultCollection.create() + .addEmptySearchResult() + .addSearchResultWithDns(objectDn); + + handler = new SearchResultHandler(resultCollection); + String expected = objectDn; + String actual = handler.getSingleLdapName(); + assertEquals(expected, actual); + assertAllNamingEnumerationsClosed(resultCollection); + } + + private void assertAllNamingEnumerationsClosed(MockResultCollection resultCollection) throws NamingException { + for (NamingEnumeration<SearchResult> namingEnumeration : resultCollection) { + verify(namingEnumeration, atLeastOnce()).close(); + } + } + + private static final class MockResultCollection extends AbstractCollection<NamingEnumeration<SearchResult>> { + + List<NamingEnumeration<SearchResult>> results = new ArrayList<>(); + + static MockResultCollection create() { + return new MockResultCollection(); + } + + MockResultCollection addSearchResultWithDns(String... dns) throws NamingException { + results.add(mockNamingEnumeration(dns)); + return this; + } + + MockResultCollection addSearchResults(SearchResult... dns) throws NamingException { + results.add(mockNamingEnumeration(dns)); + return this; + } + + MockResultCollection addEmptySearchResult() throws NamingException { + addSearchResults(); + return this; + } + + @Override + public Iterator<NamingEnumeration<SearchResult>> iterator() { + return results.iterator(); + } + + @Override + public int size() { + return results.size(); + } + } +} http://git-wip-us.apache.org/repos/asf/hive/blob/990927e3/service/src/test/org/apache/hive/service/auth/ldap/TestUserFilter.java ---------------------------------------------------------------------- diff --git a/service/src/test/org/apache/hive/service/auth/ldap/TestUserFilter.java b/service/src/test/org/apache/hive/service/auth/ldap/TestUserFilter.java new file mode 100644 index 0000000..f941c9c --- /dev/null +++ b/service/src/test/org/apache/hive/service/auth/ldap/TestUserFilter.java @@ -0,0 +1,75 @@ +/** + * 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.hive.service.auth.ldap; + +import java.io.IOException; +import javax.naming.NamingException; +import javax.security.sasl.AuthenticationException; +import org.apache.hadoop.hive.conf.HiveConf; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.runners.MockitoJUnitRunner; + +import org.junit.Before; +import org.mockito.Mock; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +public class TestUserFilter { + + private FilterFactory factory; + private HiveConf conf; + + @Mock + private DirSearch search; + + @Before + public void setup() { + conf = new HiveConf(); + factory = new UserFilterFactory(); + } + + @Test + public void testFactory() { + conf.unset(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_USERFILTER.varname); + assertNull(factory.getInstance(conf)); + + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_USERFILTER, "User1"); + assertNotNull(factory.getInstance(conf)); + } + + @Test + public void testApplyPositive() throws AuthenticationException, NamingException, IOException { + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_USERFILTER, "User1,User2,uSeR3"); + + Filter filter = factory.getInstance(conf); + filter.apply(search, "User1"); + filter.apply(search, "uid=user2,ou=People,dc=example,dc=com"); + filter.apply(search, "[email protected]"); + } + + @Test(expected = AuthenticationException.class) + public void testApplyNegative() throws AuthenticationException, NamingException, IOException { + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_USERFILTER, "User1,User2"); + + Filter filter = factory.getInstance(conf); + filter.apply(search, "User3"); + } +} http://git-wip-us.apache.org/repos/asf/hive/blob/990927e3/service/src/test/org/apache/hive/service/auth/ldap/TestUserSearchFilter.java ---------------------------------------------------------------------- diff --git a/service/src/test/org/apache/hive/service/auth/ldap/TestUserSearchFilter.java b/service/src/test/org/apache/hive/service/auth/ldap/TestUserSearchFilter.java new file mode 100644 index 0000000..0f2b509 --- /dev/null +++ b/service/src/test/org/apache/hive/service/auth/ldap/TestUserSearchFilter.java @@ -0,0 +1,94 @@ +/** + * 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.hive.service.auth.ldap; + +import java.io.IOException; +import javax.naming.NamingException; +import javax.security.sasl.AuthenticationException; +import org.apache.hadoop.hive.conf.HiveConf; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +public class TestUserSearchFilter { + + private FilterFactory factory; + private HiveConf conf; + + @Mock + private DirSearch search; + + @Before + public void setup() { + conf = new HiveConf(); + factory = new UserSearchFilterFactory(); + } + + @Test + public void testFactoryWhenNoGroupOrUserFilters() { + assertNull(factory.getInstance(conf)); + } + + @Test + public void testFactoryWhenGroupFilter() { + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_GROUPFILTER, "Grp1,Grp2"); + assertNotNull(factory.getInstance(conf)); + } + + @Test + public void testFactoryWhenUserFilter() { + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_USERFILTER, "User1,User2"); + assertNotNull(factory.getInstance(conf)); + } + + @Test + public void testApplyPositive() throws AuthenticationException, NamingException, IOException { + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_USERFILTER, "User1"); + Filter filter = factory.getInstance(conf); + + when(search.findUserDn(anyString())).thenReturn("cn=User1,ou=People,dc=example,dc=com"); + + filter.apply(search, "User1"); + } + + @Test(expected = AuthenticationException.class) + public void testApplyWhenNamingException() throws AuthenticationException, NamingException, IOException { + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_USERFILTER, "User1"); + Filter filter = factory.getInstance(conf); + + when(search.findUserDn(anyString())).thenThrow(NamingException.class); + + filter.apply(search, "User3"); + } + + @Test(expected = AuthenticationException.class) + public void testApplyWhenNotFound() throws AuthenticationException, NamingException, IOException { + conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_USERFILTER, "User1"); + Filter filter = factory.getInstance(conf); + + when(search.findUserDn(anyString())).thenReturn(null); + + filter.apply(search, "User3"); + } +}
