Repository: ambari Updated Branches: refs/heads/trunk 6f61de093 -> a6f0fbfb4
AMBARI-15383. Cleanup Ldap Sync Process (oleewere) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/a6f0fbfb Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/a6f0fbfb Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/a6f0fbfb Branch: refs/heads/trunk Commit: a6f0fbfb48e27e0199ade5eb4d9cfdfb7d5807c5 Parents: 6f61de0 Author: oleewere <[email protected]> Authored: Thu Mar 31 19:10:58 2016 +0200 Committer: oleewere <[email protected]> Committed: Thu Mar 31 19:10:58 2016 +0200 ---------------------------------------------------------------------- .../security/authorization/AmbariLdapUtils.java | 32 ++++ .../server/security/authorization/Users.java | 7 +- .../security/ldap/AmbariLdapDataPopulator.java | 73 ++++++--- .../server/security/AmbariLdapUtilsTest.java | 64 ++++++++ .../ldap/AmbariLdapDataPopulatorTest.java | 156 +++++++++++++++++-- 5 files changed, 300 insertions(+), 32 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/a6f0fbfb/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapUtils.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapUtils.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapUtils.java index ffebd45..6d20de3 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapUtils.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapUtils.java @@ -18,6 +18,14 @@ package org.apache.ambari.server.security.authorization; +import com.google.common.base.Preconditions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.ldap.core.DirContextAdapter; +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.security.ldap.LdapUtils; + +import javax.naming.Name; import java.util.regex.Pattern; /** @@ -25,6 +33,8 @@ import java.util.regex.Pattern; */ public class AmbariLdapUtils { + private static final Logger LOG = LoggerFactory.getLogger(AmbariLdapUtils.class); + /** * Regexp to verify if user login name beside user contains domain information as well (User principal name format). */ @@ -40,4 +50,26 @@ public class AmbariLdapUtils { } + /** + * Determine that the full DN of an LDAP object is in/out of the base DN scope. + * @param adapter used for get the full dn from the ldap query response + * @param baseDn + * @return + */ + public static boolean isLdapObjectOutOfScopeFromBaseDn(DirContextAdapter adapter, String baseDn) { + boolean isOutOfScope = true; + try { + Name dn = adapter.getDn(); + Preconditions.checkArgument(dn != null, "DN cannot be null in LDAP response object"); + + DistinguishedName full = LdapUtils.getFullDn((DistinguishedName) dn, adapter); + DistinguishedName base = new DistinguishedName(baseDn); + if (full.startsWith(base)) { + isOutOfScope = false; + } + } catch (Exception e) { + LOG.error(e.getMessage()); + } + return isOutOfScope; + } } http://git-wip-us.apache.org/repos/asf/ambari/blob/a6f0fbfb/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java index c2bc22f..d80edf3 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java @@ -360,7 +360,12 @@ public class Users { } else { final Set<User> users = new HashSet<User>(); for (MemberEntity memberEntity: groupEntity.getMemberEntities()) { - users.add(new User(memberEntity.getUser())); + if (memberEntity.getUser() != null) { + users.add(new User(memberEntity.getUser())); + } else { + LOG.error("Wrong state, not found user for member '{}' (group: '{}')", + memberEntity.getMemberId(), memberEntity.getGroup().getGroupName()); + } } return users; } http://git-wip-us.apache.org/repos/asf/ambari/blob/a6f0fbfb/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/AmbariLdapDataPopulator.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/AmbariLdapDataPopulator.java b/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/AmbariLdapDataPopulator.java index 75df9cc..9a66456 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/AmbariLdapDataPopulator.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/AmbariLdapDataPopulator.java @@ -25,19 +25,22 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.regex.Pattern; import javax.naming.NamingException; import javax.naming.directory.Attributes; import javax.naming.directory.SearchControls; +import com.google.common.collect.Lists; import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.configuration.Configuration; +import org.apache.ambari.server.security.authorization.AmbariLdapUtils; import org.apache.ambari.server.security.authorization.Group; import org.apache.ambari.server.security.authorization.LdapServerProperties; import org.apache.ambari.server.security.authorization.User; import org.apache.ambari.server.security.authorization.Users; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.google.common.collect.Sets; import org.springframework.ldap.control.PagedResultsDirContextProcessor; @@ -62,7 +65,7 @@ public class AmbariLdapDataPopulator { /** * Log. */ - private static final Log LOG = LogFactory.getLog(AmbariLdapDataPopulator.class); + private static final Logger LOG = LoggerFactory.getLogger(AmbariLdapDataPopulator.class); /** * Ambari configuration. @@ -84,11 +87,19 @@ public class AmbariLdapDataPopulator { */ private LdapTemplate ldapTemplate; + /** + * List for organizationUnits from the base DN + */ + private List<String> baseOrganizationUnits = Lists.newArrayList(); + // Constants private static final String UID_ATTRIBUTE = "uid"; private static final String OBJECT_CLASS_ATTRIBUTE = "objectClass"; private static final int USERS_PAGE_SIZE = 500; + // REGEXP to check member attribute starts with "cn=" or "uid=" - case insensitive + private static final String IS_MEMBER_DN_REGEXP = "^(?i)(%s|%s)=.*$"; + /** * Construct an AmbariLdapDataPopulator. * @@ -169,7 +180,7 @@ public class AmbariLdapDataPopulator { * @throws AmbariException if synchronization failed for any reason */ public LdapBatchDto synchronizeAllLdapGroups(LdapBatchDto batchInfo) throws AmbariException { - + LOG.trace("Synchronize All LDAP groups..."); Set<LdapGroupDto> externalLdapGroupInfo = getExternalLdapGroupInfo(); final Map<String, Group> internalGroupsMap = getInternalGroups(); @@ -178,7 +189,7 @@ public class AmbariLdapDataPopulator { for (LdapGroupDto groupDto : externalLdapGroupInfo) { String groupName = groupDto.getGroupName(); addLdapGroup(batchInfo, internalGroupsMap, groupName); - refreshGroupMembers(batchInfo, groupDto, internalUsersMap, internalGroupsMap, null); + refreshGroupMembers(batchInfo, groupDto, internalUsersMap, internalGroupsMap, null, false); } for (Entry<String, Group> internalGroup : internalGroupsMap.entrySet()) { if (internalGroup.getValue().isLdapGroup()) { @@ -195,7 +206,7 @@ public class AmbariLdapDataPopulator { * @throws AmbariException if synchronization failed for any reason */ public LdapBatchDto synchronizeAllLdapUsers(LdapBatchDto batchInfo) throws AmbariException { - + LOG.trace("Synchronize All LDAP users..."); Set<LdapUserDto> externalLdapUserInfo = getExternalLdapUserInfo(); Map<String, User> internalUsersMap = getInternalUsers(); @@ -205,6 +216,7 @@ public class AmbariLdapDataPopulator { final User user = internalUsersMap.get(userName); if (user != null && !user.isLdapUser()) { batchInfo.getUsersToBecomeLdap().add(userName); + LOG.trace("Convert user '{}' to LDAP user.", userName); } internalUsersMap.remove(userName); } else { @@ -227,7 +239,7 @@ public class AmbariLdapDataPopulator { * @throws AmbariException if synchronization failed for any reason */ public LdapBatchDto synchronizeLdapGroups(Set<String> groups, LdapBatchDto batchInfo) throws AmbariException { - + LOG.trace("Synchronize LDAP groups..."); final Set<LdapGroupDto> specifiedGroups = new HashSet<LdapGroupDto>(); for (String group : groups) { Set<LdapGroupDto> groupDtos = getLdapGroups(group); @@ -244,7 +256,7 @@ public class AmbariLdapDataPopulator { for (LdapGroupDto groupDto : specifiedGroups) { String groupName = groupDto.getGroupName(); addLdapGroup(batchInfo, internalGroupsMap, groupName); - refreshGroupMembers(batchInfo, groupDto, internalUsersMap, internalGroupsMap, null); + refreshGroupMembers(batchInfo, groupDto, internalUsersMap, internalGroupsMap, null, true); } return batchInfo; @@ -257,7 +269,7 @@ public class AmbariLdapDataPopulator { * @throws AmbariException if synchronization failed for any reason */ public LdapBatchDto synchronizeLdapUsers(Set<String> users, LdapBatchDto batchInfo) throws AmbariException { - + LOG.trace("Synchronize LDAP users..."); final Set<LdapUserDto> specifiedUsers = new HashSet<LdapUserDto>(); for (String user : users) { @@ -292,6 +304,7 @@ public class AmbariLdapDataPopulator { * @throws AmbariException if synchronization failed for any reason */ public LdapBatchDto synchronizeExistingLdapGroups(LdapBatchDto batchInfo) throws AmbariException { + LOG.trace("Synchronize Existing LDAP groups..."); final Map<String, Group> internalGroupsMap = getInternalGroups(); final Map<String, User> internalUsersMap = getInternalUsers(); @@ -304,7 +317,7 @@ public class AmbariLdapDataPopulator { batchInfo.getGroupsToBeRemoved().add(group.getGroupName()); } else { LdapGroupDto groupDto = groupDtos.iterator().next(); - refreshGroupMembers(batchInfo, groupDto, internalUsersMap, internalGroupsMap, null); + refreshGroupMembers(batchInfo, groupDto, internalUsersMap, internalGroupsMap, null, true); } } } @@ -318,6 +331,7 @@ public class AmbariLdapDataPopulator { * @throws AmbariException if synchronization failed for any reason */ public LdapBatchDto synchronizeExistingLdapUsers(LdapBatchDto batchInfo) throws AmbariException { + LOG.trace("Synchronize Existing LDAP users..."); final Map<String, User> internalUsersMap = getInternalUsers(); for (User user : internalUsersMap.values()) { @@ -339,10 +353,11 @@ public class AmbariLdapDataPopulator { * @param group ldap group * @param internalUsers map of internal users * @param groupMemberAttributes set of group member attributes that have already been refreshed + * @param recursive if disabled, it won't refresh members recursively (its not needed in case of all groups are processed) * @throws AmbariException if group refresh failed */ protected void refreshGroupMembers(LdapBatchDto batchInfo, LdapGroupDto group, Map<String, User> internalUsers, - Map<String, Group> internalGroupsMap, Set<String> groupMemberAttributes) + Map<String, Group> internalGroupsMap, Set<String> groupMemberAttributes, boolean recursive) throws AmbariException { Set<String> externalMembers = new HashSet<String>(); @@ -356,13 +371,13 @@ public class AmbariLdapDataPopulator { externalMembers.add(groupMember.getUserName()); } else { // if we haven't already processed this group - if (!groupMemberAttributes.contains(memberAttributeValue)) { + if (recursive && !groupMemberAttributes.contains(memberAttributeValue)) { // if the member is another group then add all of its members LdapGroupDto subGroup = getLdapGroupByMemberAttr(memberAttributeValue); if (subGroup != null) { groupMemberAttributes.add(memberAttributeValue); addLdapGroup(batchInfo, internalGroupsMap, subGroup.getGroupName()); - refreshGroupMembers(batchInfo, subGroup, internalUsers, internalGroupsMap, groupMemberAttributes); + refreshGroupMembers(batchInfo, subGroup, internalUsers, internalGroupsMap, groupMemberAttributes, true); } } } @@ -434,9 +449,11 @@ public class AmbariLdapDataPopulator { protected LdapUserDto getLdapUserByMemberAttr(String memberAttributeValue) { Set<LdapUserDto> filteredLdapUsers = new HashSet<LdapUserDto>(); if (memberAttributeValue!= null && isMemberAttributeBaseDn(memberAttributeValue)) { + LOG.trace("Member can be used as baseDn: {}", memberAttributeValue); Filter filter = new EqualsFilter(OBJECT_CLASS_ATTRIBUTE, ldapServerProperties.getUserObjectClass()); filteredLdapUsers = getFilteredLdapUsers(memberAttributeValue, filter); } else { + LOG.trace("Member cannot be used as baseDn: {}", memberAttributeValue); Filter filter = new AndFilter() .and(new EqualsFilter(OBJECT_CLASS_ATTRIBUTE, ldapServerProperties.getUserObjectClass())) .and(new EqualsFilter(ldapServerProperties.getUsernameAttribute(), memberAttributeValue)); @@ -455,9 +472,11 @@ public class AmbariLdapDataPopulator { protected LdapGroupDto getLdapGroupByMemberAttr(String memberAttributeValue) { Set<LdapGroupDto> filteredLdapGroups = new HashSet<LdapGroupDto>(); if (memberAttributeValue != null && isMemberAttributeBaseDn(memberAttributeValue)) { + LOG.trace("Member can be used as baseDn: {}", memberAttributeValue); Filter filter = new EqualsFilter(OBJECT_CLASS_ATTRIBUTE, ldapServerProperties.getGroupObjectClass()); filteredLdapGroups = getFilteredLdapGroups(memberAttributeValue, filter); } else { + LOG.trace("Member cannot be used as baseDn: {}", memberAttributeValue); filteredLdapGroups = getFilteredLdapGroups(ldapServerProperties.getBaseDN(), new EqualsFilter(OBJECT_CLASS_ATTRIBUTE, ldapServerProperties.getGroupObjectClass()), getMemberFilter(memberAttributeValue)); @@ -482,11 +501,12 @@ public class AmbariLdapDataPopulator { // Utility methods - private void addLdapGroup(LdapBatchDto batchInfo, Map<String, Group> internalGroupsMap, String groupName) { + protected void addLdapGroup(LdapBatchDto batchInfo, Map<String, Group> internalGroupsMap, String groupName) { if (internalGroupsMap.containsKey(groupName)) { final Group group = internalGroupsMap.get(groupName); if (!group.isLdapGroup()) { batchInfo.getGroupsToBecomeLdap().add(groupName); + LOG.trace("Convert group '{}' to LDAP group.", groupName); } internalGroupsMap.remove(groupName); batchInfo.getGroupsProcessedInternal().add(groupName); @@ -500,9 +520,10 @@ public class AmbariLdapDataPopulator { /** * Determines that the member attribute can be used as a 'dn' */ - private boolean isMemberAttributeBaseDn(String memberAttributeValue) { - return memberAttributeValue.startsWith(ldapServerProperties.getUsernameAttribute() + "=") - || memberAttributeValue.startsWith(ldapServerProperties.getGroupNamingAttr() + "="); + protected boolean isMemberAttributeBaseDn(String memberAttributeValue) { + Pattern pattern = Pattern.compile(String.format(IS_MEMBER_DN_REGEXP, + ldapServerProperties.getUsernameAttribute(), ldapServerProperties.getGroupNamingAttr())); + return pattern.matcher(memberAttributeValue).find(); } /** @@ -535,7 +556,9 @@ public class AmbariLdapDataPopulator { private Set<LdapGroupDto> getFilteredLdapGroups(String baseDn, Filter filter) { final Set<LdapGroupDto> groups = new HashSet<LdapGroupDto>(); final LdapTemplate ldapTemplate = loadLdapTemplate(); - ldapTemplate.search(baseDn, filter.encode(), new LdapGroupContextMapper(groups, ldapServerProperties)); + LOG.trace("LDAP Group Query - Base DN: '{}' ; Filter: '{}'", baseDn, filter.encode()); + ldapTemplate.search(baseDn, filter.encode(), + new LdapGroupContextMapper(groups, ldapServerProperties)); return groups; } @@ -572,6 +595,7 @@ public class AmbariLdapDataPopulator { List dtos = configuration.getLdapServerProperties().isPaginationEnabled() ? ldapTemplate.search(baseDn, encodedFilter, searchControls, ldapUserContextMapper, processor) : ldapTemplate.search(baseDn, encodedFilter, searchControls, ldapUserContextMapper); + LOG.trace("LDAP User Query - Base DN: '{}' ; Filter: '{}'", baseDn, encodedFilter); for (Object dto : dtos) { if (dto != null) { users.add((LdapUserDto)dto); @@ -604,6 +628,7 @@ public class AmbariLdapDataPopulator { protected Map<String, User> getInternalUsers() { final List<User> internalUsers = users.getAllUsers(); final Map<String, User> internalUsersMap = new HashMap<String, User>(); + LOG.trace("Get all users from Ambari Server."); for (User user : internalUsers) { internalUsersMap.put(user.getUserName(), user); } @@ -716,6 +741,11 @@ public class AmbariLdapDataPopulator { public Object mapFromContext(Object ctx) { final DirContextAdapter adapter = (DirContextAdapter) ctx; final String groupNameAttribute = adapter.getStringAttribute(ldapServerProperties.getGroupNamingAttr()); + boolean outOfScope = AmbariLdapUtils.isLdapObjectOutOfScopeFromBaseDn(adapter, ldapServerProperties.getBaseDN()); + if (outOfScope) { + LOG.warn("Group '{}' is out of scope of the base DN. It will be skipped.", groupNameAttribute); + return null; + } if (groupNameAttribute != null) { final LdapGroupDto group = new LdapGroupDto(); group.setGroupName(groupNameAttribute.toLowerCase()); @@ -744,6 +774,13 @@ public class AmbariLdapDataPopulator { final DirContextAdapter adapter = (DirContextAdapter) ctx; final String usernameAttribute = adapter.getStringAttribute(ldapServerProperties.getUsernameAttribute()); final String uidAttribute = adapter.getStringAttribute(UID_ATTRIBUTE); + + boolean outOfScope = AmbariLdapUtils.isLdapObjectOutOfScopeFromBaseDn(adapter, ldapServerProperties.getBaseDN()); + if (outOfScope) { + LOG.warn("User '{}' is out of scope of the base DN. It will be skipped.", usernameAttribute); + return null; + } + if (usernameAttribute != null || uidAttribute != null) { final LdapUserDto user = new LdapUserDto(); user.setUserName(usernameAttribute != null ? usernameAttribute.toLowerCase() : null); http://git-wip-us.apache.org/repos/asf/ambari/blob/a6f0fbfb/ambari-server/src/test/java/org/apache/ambari/server/security/AmbariLdapUtilsTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/AmbariLdapUtilsTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/AmbariLdapUtilsTest.java index b2778ae..5bcdf48 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/security/AmbariLdapUtilsTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/security/AmbariLdapUtilsTest.java @@ -19,12 +19,30 @@ package org.apache.ambari.server.security; import org.apache.ambari.server.security.authorization.AmbariLdapUtils; import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.api.easymock.PowerMock; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.springframework.ldap.core.DirContextAdapter; +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.security.ldap.LdapUtils; + +import javax.naming.Context; +import javax.naming.NamingException; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; +import static org.easymock.EasyMock.anyObject; +import static org.easymock.EasyMock.createNiceMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +@RunWith(PowerMockRunner.class) +@PrepareForTest(LdapUtils.class) public class AmbariLdapUtilsTest { + private static final String USER_DN = "uid=myuser,ou=hdp,ou=Users,dc=apache,dc=org"; + @Test public void testIsUserPrincipalNameFormat_True() throws Exception { // Given @@ -84,4 +102,50 @@ public class AmbariLdapUtilsTest { // Then assertFalse(isUserPrincipalNameFormat); } + + @Test + public void testIsLdapObjectOutOfScopeFromBaseDn() throws NamingException { + // GIVEN + DistinguishedName fullDn = new DistinguishedName(USER_DN); + Context context = createNiceMock(Context.class); + DirContextAdapter adapter = createNiceMock(DirContextAdapter.class); + + PowerMock.mockStatic(LdapUtils.class); + expect(LdapUtils.getFullDn(anyObject(DistinguishedName.class), anyObject(Context.class))) + .andReturn(fullDn).anyTimes(); + + expect(adapter.getDn()).andReturn(fullDn); + expect(context.getNameInNamespace()).andReturn(USER_DN); + + replay(adapter, context); + PowerMock.replayAll(); + + // WHEN + boolean isOutOfScopeFromBaseDN = AmbariLdapUtils.isLdapObjectOutOfScopeFromBaseDn(adapter, "dc=apache,dc=org"); + // THEN + assertFalse(isOutOfScopeFromBaseDN); + } + + @Test + public void testIsLdapObjectOutOfScopeFromBaseDn_dnOutOfScope() throws NamingException { + // GIVEN + DistinguishedName fullDn = new DistinguishedName(USER_DN); + Context context = createNiceMock(Context.class); + DirContextAdapter adapter = createNiceMock(DirContextAdapter.class); + + PowerMock.mockStatic(LdapUtils.class); + expect(LdapUtils.getFullDn(anyObject(DistinguishedName.class), anyObject(Context.class))) + .andReturn(fullDn).anyTimes(); + + expect(adapter.getDn()).andReturn(fullDn); + expect(context.getNameInNamespace()).andReturn(USER_DN); + + replay(adapter, context); + PowerMock.replayAll(); + + // WHEN + boolean isOutOfScopeFromBaseDN = AmbariLdapUtils.isLdapObjectOutOfScopeFromBaseDn(adapter, "dc=apache,dc=org,ou=custom"); + // THEN + assertTrue(isOutOfScopeFromBaseDN); + } } http://git-wip-us.apache.org/repos/asf/ambari/blob/a6f0fbfb/ambari-server/src/test/java/org/apache/ambari/server/security/ldap/AmbariLdapDataPopulatorTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/ldap/AmbariLdapDataPopulatorTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/ldap/AmbariLdapDataPopulatorTest.java index ffff3ea..eef91c1 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/security/ldap/AmbariLdapDataPopulatorTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/security/ldap/AmbariLdapDataPopulatorTest.java @@ -36,6 +36,7 @@ import org.apache.ambari.server.orm.entities.MemberEntity; import org.apache.ambari.server.orm.entities.PrincipalEntity; import org.apache.ambari.server.orm.entities.PrivilegeEntity; import org.apache.ambari.server.orm.entities.UserEntity; +import org.apache.ambari.server.security.authorization.AmbariLdapUtils; import org.apache.ambari.server.security.authorization.Group; import org.apache.ambari.server.security.authorization.LdapServerProperties; import org.apache.ambari.server.security.authorization.User; @@ -44,6 +45,10 @@ import org.easymock.Capture; import org.easymock.EasyMock; import org.easymock.IAnswer; import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.api.easymock.PowerMock; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; import org.springframework.ldap.control.PagedResultsCookie; import org.springframework.ldap.control.PagedResultsDirContextProcessor; import org.springframework.ldap.core.AttributesMapper; @@ -56,8 +61,11 @@ import javax.naming.directory.SearchControls; import static junit.framework.Assert.*; import static org.easymock.EasyMock.*; +import static org.easymock.EasyMock.anyBoolean; import static org.easymock.EasyMock.createNiceMock; +@RunWith(PowerMockRunner.class) +@PrepareForTest(AmbariLdapUtils.class) public class AmbariLdapDataPopulatorTest { public static class AmbariLdapDataPopulatorTestInstance extends TestAmbariLdapDataPopulator { public AmbariLdapDataPopulatorTestInstance(Configuration configuration, Users users) { @@ -250,7 +258,7 @@ public class AmbariLdapDataPopulatorTest { expect(populator.getLdapGroups("group2")).andReturn(Collections.EMPTY_SET); LdapGroupDto externalGroup1 = createNiceMock(LdapGroupDto.class); LdapBatchDto batchInfo = new LdapBatchDto(); - populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup1), anyObject(Map.class), anyObject(Map.class), anyObject(Set.class)); + populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup1), anyObject(Map.class), anyObject(Map.class), anyObject(Set.class), anyBoolean()); expectLastCall(); expect(populator.getLdapGroups("group4")).andReturn(Collections.singleton(externalGroup1)); expect(populator.getLdapGroups("group5")).andReturn(Collections.EMPTY_SET); @@ -373,12 +381,15 @@ public class AmbariLdapDataPopulatorTest { LdapBatchDto batchInfo = new LdapBatchDto(); Set<LdapGroupDto> externalGroups = createSet(externalGroup3, externalGroup4); for (LdapGroupDto externalGroup : externalGroups) { - populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup), anyObject(Map.class), anyObject(Map.class), anyObject(Set.class)); + populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup), + anyObject(Map.class), anyObject(Map.class), anyObject(Set.class), anyBoolean()); expectLastCall(); } - populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup1), anyObject(Map.class), anyObject(Map.class), anyObject(Set.class)); + populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup1), + anyObject(Map.class), anyObject(Map.class), anyObject(Set.class), anyBoolean()); expectLastCall(); - populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup2), anyObject(Map.class), anyObject(Map.class), anyObject(Set.class)); + populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup2), anyObject(Map.class), + anyObject(Map.class), anyObject(Set.class), anyBoolean()); expectLastCall(); expect(populator.getLdapGroups("x*")).andReturn(externalGroups); expect(populator.getLdapGroups("group1")).andReturn(Collections.singleton(externalGroup1)); @@ -456,10 +467,12 @@ public class AmbariLdapDataPopulatorTest { LdapBatchDto batchInfo = new LdapBatchDto(); Set<LdapGroupDto> externalGroups = createSet(externalGroup3, externalGroup4); for (LdapGroupDto externalGroup : externalGroups) { - populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup), anyObject(Map.class), anyObject(Map.class), anyObject(Set.class)); + populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup), anyObject(Map.class), anyObject(Map.class), + anyObject(Set.class), anyBoolean()); expectLastCall(); } - populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup2), anyObject(Map.class), anyObject(Map.class), anyObject(Set.class)); + populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup2), anyObject(Map.class), + anyObject(Map.class), anyObject(Set.class), anyBoolean()); expectLastCall(); expect(populator.getLdapGroups("x*")).andReturn(externalGroups); expect(populator.getLdapGroups("group2")).andReturn(Collections.singleton(externalGroup2)); @@ -530,7 +543,8 @@ public class AmbariLdapDataPopulatorTest { LdapBatchDto batchInfo = new LdapBatchDto(); Set<LdapGroupDto> externalGroups = createSet(externalGroup1, externalGroup2, externalGroup3, externalGroup4); for (LdapGroupDto externalGroup : externalGroups) { - populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup), anyObject(Map.class), anyObject(Map.class), anyObject(Set.class)); + populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup), anyObject(Map.class), + anyObject(Map.class), anyObject(Set.class), anyBoolean()); expectLastCall(); } expect(populator.getLdapGroups("group*")).andReturn(externalGroups); @@ -660,7 +674,8 @@ public class AmbariLdapDataPopulatorTest { LdapBatchDto batchInfo = new LdapBatchDto(); Set<LdapGroupDto> externalGroups = createSet(externalGroup1, externalGroup2, externalGroup3, externalGroup4); for (LdapGroupDto externalGroup : externalGroups) { - populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup), anyObject(Map.class), anyObject(Map.class), anyObject(Set.class)); + populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup), anyObject(Map.class), + anyObject(Map.class), anyObject(Set.class), anyBoolean()); expectLastCall(); } @@ -721,7 +736,8 @@ public class AmbariLdapDataPopulatorTest { LdapBatchDto batchInfo = new LdapBatchDto(); Set<LdapGroupDto> externalGroups = createSet(externalGroup1, externalGroup2); for (LdapGroupDto externalGroup : externalGroups) { - populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup), anyObject(Map.class), anyObject(Map.class), anyObject(Set.class)); + populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup), anyObject(Map.class), + anyObject(Map.class), anyObject(Set.class), anyBoolean()); expectLastCall(); } expect(populator.getExternalLdapGroupInfo()).andReturn(externalGroups); @@ -785,7 +801,8 @@ public class AmbariLdapDataPopulatorTest { LdapBatchDto batchInfo = new LdapBatchDto(); Set<LdapGroupDto> externalGroups = createSet(externalGroup1); for (LdapGroupDto externalGroup : externalGroups) { - populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup), anyObject(Map.class), anyObject(Map.class), anyObject(Set.class)); + populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup), anyObject(Map.class), + anyObject(Map.class), anyObject(Set.class), anyBoolean()); expectLastCall(); } expect(populator.getExternalLdapGroupInfo()).andReturn(externalGroups); @@ -848,7 +865,8 @@ public class AmbariLdapDataPopulatorTest { LdapBatchDto batchInfo = new LdapBatchDto(); Set<LdapGroupDto> externalGroups = createSet(externalGroup1, externalGroup2); for (LdapGroupDto externalGroup : externalGroups) { - populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup), anyObject(Map.class), anyObject(Map.class), anyObject(Set.class)); + populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup), anyObject(Map.class), + anyObject(Map.class), anyObject(Set.class), anyBoolean()); expectLastCall(); } expect(populator.getExternalLdapGroupInfo()).andReturn(externalGroups); @@ -1461,7 +1479,7 @@ public class AmbariLdapDataPopulatorTest { Map<String, Group> internalGroups = new HashMap<String, Group>(); internalGroups.put("group2", group2); - populator.refreshGroupMembers(batchInfo, externalGroup, internalUsers, internalGroups, null); + populator.refreshGroupMembers(batchInfo, externalGroup, internalUsers, internalGroups, null, true); Set<String> groupMembersToAdd = new HashSet<String>(); for (LdapUserGroupMemberDto ldapUserGroupMemberDto : batchInfo.getMembershipToAdd()) { @@ -1624,12 +1642,18 @@ public class AmbariLdapDataPopulatorTest { public void testLdapUserContextMapper_uidIsNull() throws Exception { LdapServerProperties ldapServerProperties = createNiceMock(LdapServerProperties.class); expect(ldapServerProperties.getUsernameAttribute()).andReturn("cn").once(); + expect(ldapServerProperties.getBaseDN()).andReturn("dc=SME,dc=support,dc=com").anyTimes(); DirContextAdapter adapter = createNiceMock(DirContextAdapter.class); expect(adapter.getStringAttribute("cn")).andReturn("testUser"); expect(adapter.getStringAttribute("uid")).andReturn(null); expect(adapter.getNameInNamespace()).andReturn("cn=testUser,ou=Ambari,dc=SME,dc=support,dc=com"); - replay(ldapServerProperties, adapter); + PowerMock.mockStatic(AmbariLdapUtils.class); + expect(AmbariLdapUtils.isLdapObjectOutOfScopeFromBaseDn(adapter, "dc=SME,dc=support,dc=com")) + .andReturn(false).anyTimes(); + + replay(adapter, ldapServerProperties); + PowerMock.replayAll(); AmbariLdapDataPopulator.LdapUserContextMapper ldapUserContextMapper = new AmbariLdapDataPopulator.LdapUserContextMapper(ldapServerProperties); LdapUserDto userDto = (LdapUserDto) ldapUserContextMapper.mapFromContext(adapter); @@ -1659,12 +1683,18 @@ public class AmbariLdapDataPopulatorTest { public void testLdapUserContextMapper() throws Exception { LdapServerProperties ldapServerProperties = createNiceMock(LdapServerProperties.class); expect(ldapServerProperties.getUsernameAttribute()).andReturn("cn").once(); + expect(ldapServerProperties.getBaseDN()).andReturn("dc=SME,dc=support,dc=com").anyTimes(); DirContextAdapter adapter = createNiceMock(DirContextAdapter.class); expect(adapter.getStringAttribute("cn")).andReturn("testUser"); expect(adapter.getStringAttribute("uid")).andReturn("UID1"); expect(adapter.getNameInNamespace()).andReturn("cn=testUser,ou=Ambari,dc=SME,dc=support,dc=com"); + PowerMock.mockStatic(AmbariLdapUtils.class); + expect(AmbariLdapUtils.isLdapObjectOutOfScopeFromBaseDn(adapter, "dc=SME,dc=support,dc=com")) + .andReturn(false).anyTimes(); + replay(ldapServerProperties, adapter); + PowerMock.replayAll(); AmbariLdapDataPopulator.LdapUserContextMapper ldapUserContextMapper = new AmbariLdapDataPopulator.LdapUserContextMapper(ldapServerProperties); LdapUserDto userDto = (LdapUserDto) ldapUserContextMapper.mapFromContext(adapter); @@ -1675,6 +1705,106 @@ public class AmbariLdapDataPopulatorTest { assertEquals("cn=testuser,ou=ambari,dc=sme,dc=support,dc=com", userDto.getDn()); } + @Test + public void testIsMemberAttributeBaseDn() { + // GIVEN + Configuration configuration = createNiceMock(Configuration.class); + Users users = createNiceMock(Users.class); + LdapServerProperties ldapServerProperties = createNiceMock(LdapServerProperties.class); + + expect(configuration.getLdapServerProperties()).andReturn(ldapServerProperties).anyTimes(); + expect(ldapServerProperties.getUsernameAttribute()).andReturn("UID"); + expect(ldapServerProperties.getGroupNamingAttr()).andReturn("CN"); + + replay(configuration, users, ldapServerProperties); + + // WHEN + AmbariLdapDataPopulatorTestInstance populator = new AmbariLdapDataPopulatorTestInstance(configuration, users); + boolean result = populator.isMemberAttributeBaseDn("CN=mygroupname,OU=myOrganizationUnit,DC=apache,DC=org"); + // THEN + assertTrue(result); + } + + @Test + public void testIsMemberAttributeBaseDn_withUidMatch() { + // GIVEN + Configuration configuration = createNiceMock(Configuration.class); + Users users = createNiceMock(Users.class); + LdapServerProperties ldapServerProperties = createNiceMock(LdapServerProperties.class); + + expect(configuration.getLdapServerProperties()).andReturn(ldapServerProperties).anyTimes(); + expect(ldapServerProperties.getUsernameAttribute()).andReturn("UID"); + expect(ldapServerProperties.getGroupNamingAttr()).andReturn("CN"); + + replay(configuration, users, ldapServerProperties); + + // WHEN + AmbariLdapDataPopulatorTestInstance populator = new AmbariLdapDataPopulatorTestInstance(configuration, users); + boolean result = populator.isMemberAttributeBaseDn("uid=myuid,OU=myOrganizationUnit,DC=apache,DC=org"); + // THEN + assertTrue(result); + } + + @Test + public void testIsMemberAttributeBaseDn_withLowerAndUpperCaseValue() { + // GIVEN + Configuration configuration = createNiceMock(Configuration.class); + Users users = createNiceMock(Users.class); + LdapServerProperties ldapServerProperties = createNiceMock(LdapServerProperties.class); + + expect(configuration.getLdapServerProperties()).andReturn(ldapServerProperties).anyTimes(); + expect(ldapServerProperties.getUsernameAttribute()).andReturn("uid"); + expect(ldapServerProperties.getGroupNamingAttr()).andReturn("CN"); + + replay(configuration, users, ldapServerProperties); + + // WHEN + AmbariLdapDataPopulatorTestInstance populator = new AmbariLdapDataPopulatorTestInstance(configuration, users); + boolean result = populator.isMemberAttributeBaseDn("cn=mygroupname,OU=myOrganizationUnit,DC=apache,DC=org"); + // THEN + assertTrue(result); + } + + @Test + public void testIsMemberAttributeBaseDn_withWrongAttribute() { + // GIVEN + Configuration configuration = createNiceMock(Configuration.class); + Users users = createNiceMock(Users.class); + LdapServerProperties ldapServerProperties = createNiceMock(LdapServerProperties.class); + + expect(configuration.getLdapServerProperties()).andReturn(ldapServerProperties).anyTimes(); + expect(ldapServerProperties.getUsernameAttribute()).andReturn("uid"); + expect(ldapServerProperties.getGroupNamingAttr()).andReturn("CN"); + + replay(configuration, users, ldapServerProperties); + + // WHEN + AmbariLdapDataPopulatorTestInstance populator = new AmbariLdapDataPopulatorTestInstance(configuration, users); + boolean result = populator.isMemberAttributeBaseDn("cnn=mygroupname,OU=myOrganizationUnit,DC=apache,DC=org"); + // THEN + assertFalse(result); + } + + @Test + public void testIsMemberAttributeBaseDn_withEmptyValues() { + // GIVEN + Configuration configuration = createNiceMock(Configuration.class); + Users users = createNiceMock(Users.class); + LdapServerProperties ldapServerProperties = createNiceMock(LdapServerProperties.class); + + expect(configuration.getLdapServerProperties()).andReturn(ldapServerProperties).anyTimes(); + expect(ldapServerProperties.getUsernameAttribute()).andReturn(""); + expect(ldapServerProperties.getGroupNamingAttr()).andReturn(null); + + replay(configuration, users, ldapServerProperties); + + // WHEN + AmbariLdapDataPopulatorTestInstance populator = new AmbariLdapDataPopulatorTestInstance(configuration, users); + boolean result = populator.isMemberAttributeBaseDn("cnn=mygroupname,OU=myOrganizationUnit,DC=apache,DC=org"); + // THEN + assertFalse(result); + } + private static int userIdCounter = 1; private User createUser(String name, boolean ldapUser, GroupEntity group) {
