/** * 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 com.google.common.base.Joiner; 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(); conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_USERMEMBERSHIP_KEY, "memberOf"); } @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); } @Test public void testFindGroupDnPositive() throws NamingException { conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_GROUPDNPATTERN, "CN=%s,OU=org1,DC=foo,DC=bar"); String groupDn = "CN=Group1"; NamingEnumeration<SearchResult> result = mockNamingEnumeration(groupDn); when(ctx.search(anyString(), anyString(), any(SearchControls.class))).thenReturn(result); search = new LdapSearch(conf, ctx); String expected = groupDn; String actual = search.findGroupDn("grp1"); assertEquals(expected, actual); } @Test(expected = NamingException.class) public void testFindGroupDNNoResults() throws NamingException { conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_GROUPDNPATTERN, "CN=%s,OU=org1,DC=foo,DC=bar"); NamingEnumeration<SearchResult> result = mockEmptyNamingEnumeration(); when(ctx.search(anyString(), anyString(), any(SearchControls.class))).thenReturn(result); search = new LdapSearch(conf, ctx); search.findGroupDn("anyGroup"); } @Test(expected = NamingException.class) public void testFindGroupDNTooManyResults() throws NamingException { conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_GROUPDNPATTERN, "CN=%s,OU=org1,DC=foo,DC=bar"); NamingEnumeration<SearchResult> result = LdapTestUtils.mockNamingEnumeration("Result1", "Result2", "Result3"); when(ctx.search(anyString(), anyString(), any(SearchControls.class))).thenReturn(result); search = new LdapSearch(conf, ctx); search.findGroupDn("anyGroup"); } @Test public void testFindGroupDNWhenExceptionInSearch() throws NamingException { conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_GROUPDNPATTERN, Joiner.on(":").join( "CN=%s,OU=org1,DC=foo,DC=bar", "CN=%s,OU=org2,DC=foo,DC=bar" ) ); NamingEnumeration<SearchResult> result = LdapTestUtils.mockNamingEnumeration("CN=Group1"); when(ctx.search(anyString(), anyString(), any(SearchControls.class))) .thenReturn(result) .thenThrow(NamingException.class); search = new LdapSearch(conf, ctx); String expected = "CN=Group1"; String actual = search.findGroupDn("grp1"); assertEquals(expected, actual); } @Test public void testIsUserMemberOfGroupWhenUserId() throws NamingException { conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_USERDNPATTERN, "CN=%s,OU=org1,DC=foo,DC=bar"); NamingEnumeration<SearchResult> validResult = LdapTestUtils.mockNamingEnumeration("CN=User1"); NamingEnumeration<SearchResult> emptyResult = LdapTestUtils.mockEmptyNamingEnumeration(); when(ctx.search(anyString(), contains("(uid=usr1)"), any(SearchControls.class))) .thenReturn(validResult); when(ctx.search(anyString(), contains("(uid=usr2)"), any(SearchControls.class))) .thenReturn(emptyResult); search = new LdapSearch(conf, ctx); assertTrue(search.isUserMemberOfGroup("usr1", "grp1")); assertFalse(search.isUserMemberOfGroup("usr2", "grp2")); } @Test public void testIsUserMemberOfGroupWhenUserDn() throws NamingException { conf.setVar(HiveConf.ConfVars.HIVE_SERVER2_PLAIN_LDAP_USERDNPATTERN, "CN=%s,OU=org1,DC=foo,DC=bar"); NamingEnumeration<SearchResult> validResult = LdapTestUtils.mockNamingEnumeration("CN=User1"); NamingEnumeration<SearchResult> emptyResult = LdapTestUtils.mockEmptyNamingEnumeration(); when(ctx.search(anyString(), contains("(uid=User1)"), any(SearchControls.class))) .thenReturn(validResult); when(ctx.search(anyString(), contains("(uid=User2)"), any(SearchControls.class))) .thenReturn(emptyResult); search = new LdapSearch(conf, ctx); assertTrue(search.isUserMemberOfGroup("CN=User1,OU=org1,DC=foo,DC=bar", "grp1")); assertFalse(search.isUserMemberOfGroup("CN=User2,OU=org1,DC=foo,DC=bar", "grp2")); } }