AMBARI-19632. Ldap sync fails when there are special characters in distinguished names (rlevas)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/593234b7 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/593234b7 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/593234b7 Branch: refs/heads/trunk Commit: 593234b76ab8f9ec67f27f504b03285286120358 Parents: 274969b Author: Robert Levas <[email protected]> Authored: Sun Jan 22 10:44:18 2017 -0500 Committer: Robert Levas <[email protected]> Committed: Sun Jan 22 10:44:18 2017 -0500 ---------------------------------------------------------------------- ambari-project/pom.xml | 4 +- .../AmbariLdapBindAuthenticator.java | 9 +- .../security/authorization/AmbariLdapUtils.java | 69 +++++++++-- .../security/ldap/AmbariLdapDataPopulator.java | 105 ++++++++--------- .../server/security/AmbariLdapUtilsTest.java | 118 ++++++++++++++----- .../AmbariLdapBindAuthenticatorTest.java | 23 ++-- .../ldap/AmbariLdapDataPopulatorTest.java | 9 +- 7 files changed, 211 insertions(+), 126 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/593234b7/ambari-project/pom.xml ---------------------------------------------------------------------- diff --git a/ambari-project/pom.xml b/ambari-project/pom.xml index 16ea2af..0eab275 100644 --- a/ambari-project/pom.xml +++ b/ambari-project/pom.xml @@ -146,12 +146,12 @@ <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-ldap</artifactId> - <version>3.1.2.RELEASE</version> + <version>4.0.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.ldap</groupId> <artifactId>spring-ldap-core</artifactId> - <version>1.3.1.RELEASE</version> + <version>2.0.4.RELEASE</version> </dependency> <dependency> <groupId>org.apache.directory.server</groupId> http://git-wip-us.apache.org/repos/asf/ambari/blob/593234b7/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapBindAuthenticator.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapBindAuthenticator.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapBindAuthenticator.java index b4ef889..a6ffa81 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapBindAuthenticator.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapBindAuthenticator.java @@ -20,6 +20,7 @@ package org.apache.ambari.server.security.authorization; import java.util.List; +import javax.naming.Name; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.directory.Attributes; @@ -33,7 +34,6 @@ import org.springframework.ldap.core.AttributesMapper; import org.springframework.ldap.core.ContextSource; import org.springframework.ldap.core.DirContextAdapter; import org.springframework.ldap.core.DirContextOperations; -import org.springframework.ldap.core.DistinguishedName; import org.springframework.ldap.core.LdapTemplate; import org.springframework.ldap.core.support.BaseLdapPathContextSource; import org.springframework.ldap.support.LdapUtils; @@ -234,9 +234,8 @@ public class AmbariLdapBindAuthenticator extends AbstractLdapAuthenticator { } BaseLdapPathContextSource baseLdapPathContextSource = (BaseLdapPathContextSource) contextSource; - DistinguishedName userDistinguishedName = new DistinguishedName(user.getDn()); - DistinguishedName fullDn = new DistinguishedName(userDistinguishedName); - fullDn.prepend(baseLdapPathContextSource.getBaseLdapPath()); + Name userDistinguishedName = user.getDn(); + Name fullDn = AmbariLdapUtils.getFullDn(userDistinguishedName, baseLdapPathContextSource.getBaseLdapName()); LOG.debug("Attempting to bind as {}", fullDn); @@ -252,7 +251,7 @@ public class AmbariLdapBindAuthenticator extends AbstractLdapAuthenticator { // is expected these details will be more complete of querying for them from the bound context. // Some LDAP server implementations will no return all attributes to the bound context due to // the filter being used in the query. - return new DirContextAdapter(user.getAttributes(), userDistinguishedName, baseLdapPathContextSource.getBaseLdapPath()); + return new DirContextAdapter(user.getAttributes(), userDistinguishedName, baseLdapPathContextSource.getBaseLdapName()); } catch (org.springframework.ldap.AuthenticationException e) { String message = String.format("Failed to bind as %s - %s", user.getDn().toString(), e.getMessage()); if (LOG.isTraceEnabled()) { http://git-wip-us.apache.org/repos/asf/ambari/blob/593234b7/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 a64ab3d..2854dbb 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 @@ -1,4 +1,4 @@ -/** +/* * 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 @@ -7,7 +7,7 @@ * "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 + * 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, @@ -20,13 +20,15 @@ package org.apache.ambari.server.security.authorization; import java.util.regex.Pattern; +import javax.naming.Context; +import javax.naming.InvalidNameException; import javax.naming.Name; +import javax.naming.NamingException; 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 org.springframework.ldap.support.LdapUtils; import com.google.common.base.Preconditions; @@ -44,8 +46,9 @@ public class AmbariLdapUtils { /** * Returns true if the given user name contains domain name as well (e.g. username@domain) + * * @param loginName the login name to verify if it contains domain information. - * @return + * @return true if the given user name contains domain name as well; false otherwise */ public static boolean isUserPrincipalNameFormat(String loginName) { return UPN_FORMAT.matcher(loginName).matches(); @@ -54,9 +57,10 @@ 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 + * @param baseDn the base distinguished name + * @return true if the object is out of scope; false otherwise */ public static boolean isLdapObjectOutOfScopeFromBaseDn(DirContextAdapter adapter, String baseDn) { boolean isOutOfScope = true; @@ -64,9 +68,9 @@ public class AmbariLdapUtils { 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)) { + Name fullDn = getFullDn(dn, adapter); + Name base = LdapUtils.newLdapName(baseDn); + if (fullDn.startsWith(base)) { isOutOfScope = false; } } catch (Exception e) { @@ -74,4 +78,49 @@ public class AmbariLdapUtils { } return isOutOfScope; } + + /** + * Ensures the given distinguished name is an absolute value rather than a name relative to the context. + * + * @param dn a distinguished name + * @param context the context containing the base distinguished name + * @return the absolute distinguished name + */ + public static Name getFullDn(String dn, Context context) throws NamingException { + return getFullDn(LdapUtils.newLdapName(dn), context); + } + + /** + * Ensures the given distinguished name is an absolute value rather than a name relative to the context. + * + * @param dn a distinguished name + * @param context the context containing the base distinguished name + * @return the absolute distinguished name + */ + public static Name getFullDn(Name dn, Context context) throws NamingException { + return getFullDn(LdapUtils.newLdapName(dn), LdapUtils.newLdapName(context.getNameInNamespace())); + } + + /** + * Ensures the given distinguished name is an absolute value rather than a name relative to the context. + * + * @param dn a distinguished name + * @param baseDn the base distinguished name + * @return the absolute distinguished name + */ + public static Name getFullDn(Name dn, Name baseDn) { + + if (dn.startsWith(baseDn)) { + return dn; + } else { + try { + //Copy the baseDN so we do not change the one that is passed in... + baseDn = LdapUtils.newLdapName(baseDn); + baseDn.addAll(dn); + } catch (InvalidNameException e) { + LOG.error(e.getMessage()); + } + return baseDn; + } + } } http://git-wip-us.apache.org/repos/asf/ambari/blob/593234b7/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 2dccf11..c134c51 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 @@ -1,4 +1,4 @@ -/** +/* * 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 @@ -54,9 +54,9 @@ import org.springframework.ldap.filter.Filter; import org.springframework.ldap.filter.HardcodedFilter; import org.springframework.ldap.filter.LikeFilter; import org.springframework.ldap.filter.OrFilter; +import org.springframework.ldap.support.LdapUtils; import org.springframework.security.core.userdetails.UsernameNotFoundException; -import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.inject.Inject; @@ -89,13 +89,8 @@ 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 UID_ATTRIBUTE = "uid"; private static final String OBJECT_CLASS_ATTRIBUTE = "objectClass"; private static final int USERS_PAGE_SIZE = 500; @@ -108,8 +103,8 @@ public class AmbariLdapDataPopulator { /** * Construct an AmbariLdapDataPopulator. * - * @param configuration the Ambari configuration - * @param users utility that provides access to Users + * @param configuration the Ambari configuration + * @param users utility that provides access to Users */ @Inject public AmbariLdapDataPopulator(Configuration configuration, Users users) { @@ -220,7 +215,7 @@ public class AmbariLdapDataPopulator { if (internalUsersMap.containsKey(userName)) { final User user = internalUsersMap.get(userName); if (user != null && !user.isLdapUser()) { - if (Configuration.LdapUsernameCollisionHandlingBehavior.SKIP == configuration.getLdapSyncCollisionHandlingBehavior()){ + if (Configuration.LdapUsernameCollisionHandlingBehavior.SKIP == configuration.getLdapSyncCollisionHandlingBehavior()) { LOG.info("User '{}' skipped because it is local user", userName); batchInfo.getUsersSkipped().add(userName); } else { @@ -297,7 +292,7 @@ public class AmbariLdapDataPopulator { if (internalUsersMap.containsKey(userName)) { final User user = internalUsersMap.get(userName); if (user != null && !user.isLdapUser()) { - if (Configuration.LdapUsernameCollisionHandlingBehavior.SKIP == configuration.getLdapSyncCollisionHandlingBehavior()){ + if (Configuration.LdapUsernameCollisionHandlingBehavior.SKIP == configuration.getLdapSyncCollisionHandlingBehavior()) { LOG.info("User '{}' skipped because it is local user", userName); batchInfo.getUsersSkipped().add(userName); } else { @@ -364,11 +359,11 @@ public class AmbariLdapDataPopulator { /** * Check group members of the synced group: add missing ones and remove the ones absent in external LDAP. * - * @param batchInfo batch update object - * @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) + * @param batchInfo batch update object + * @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, @@ -380,7 +375,7 @@ public class AmbariLdapDataPopulator { groupMemberAttributes = new HashSet<String>(); } - for (String memberAttributeValue: group.getMemberAttributes()) { + for (String memberAttributeValue : group.getMemberAttributes()) { LdapUserDto groupMember = getLdapUserByMemberAttr(memberAttributeValue); if (groupMember != null) { externalMembers.add(groupMember.getUserName()); @@ -399,7 +394,7 @@ public class AmbariLdapDataPopulator { } String groupName = group.getGroupName(); final Map<String, User> internalMembers = getInternalMembers(groupName); - for (String externalMember: externalMembers) { + for (String externalMember : externalMembers) { if (internalUsers.containsKey(externalMember)) { final User user = internalUsers.get(externalMember); if (user == null) { @@ -428,7 +423,7 @@ public class AmbariLdapDataPopulator { batchInfo.getMembershipToAdd().add(new LdapUserGroupMemberDto(groupName, externalMember)); } } - for (Entry<String, User> userToBeUnsynced: internalMembers.entrySet()) { + for (Entry<String, User> userToBeUnsynced : internalMembers.entrySet()) { final User user = userToBeUnsynced.getValue(); batchInfo.getMembershipToRemove().add(new LdapUserGroupMemberDto(groupName, user.getUserName())); } @@ -437,8 +432,7 @@ public class AmbariLdapDataPopulator { /** * Get the set of LDAP groups for the given group name. * - * @param groupName the group name - * + * @param groupName the group name * @return the set of LDAP groups for the given name */ protected Set<LdapGroupDto> getLdapGroups(String groupName) { @@ -451,8 +445,7 @@ public class AmbariLdapDataPopulator { /** * Get the set of LDAP users for the given user name. * - * @param username the user name - * + * @param username the user name * @return the set of LDAP users for the given name */ protected Set<LdapUserDto> getLdapUsers(String username) { @@ -464,31 +457,30 @@ public class AmbariLdapDataPopulator { /** * Get the LDAP user member for the given member attribute. * - * @param memberAttributeValue the member attribute value - * + * @param memberAttributeValue the member attribute value * @return the user for the given member attribute; null if not found */ protected LdapUserDto getLdapUserByMemberAttr(String memberAttributeValue) { - Set<LdapUserDto> filteredLdapUsers = new HashSet<LdapUserDto>(); + Set<LdapUserDto> filteredLdapUsers; memberAttributeValue = getUniqueIdByMemberPattern(memberAttributeValue, - ldapServerProperties.getSyncUserMemberReplacePattern()); + ldapServerProperties.getSyncUserMemberReplacePattern()); Filter syncMemberFilter = createCustomMemberFilter(memberAttributeValue, - ldapServerProperties.getSyncUserMemberFilter()); + ldapServerProperties.getSyncUserMemberFilter()); if (memberAttributeValue != null && syncMemberFilter != null) { LOG.trace("Use custom filter '{}' for getting member user with default baseDN ('{}')", - syncMemberFilter.encode(), ldapServerProperties.getBaseDN()); + syncMemberFilter.encode(), ldapServerProperties.getBaseDN()); filteredLdapUsers = getFilteredLdapUsers(ldapServerProperties.getBaseDN(), syncMemberFilter); - } else if (memberAttributeValue!= null && isMemberAttributeBaseDn(memberAttributeValue)) { + } else 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)); + .and(new EqualsFilter(OBJECT_CLASS_ATTRIBUTE, ldapServerProperties.getUserObjectClass())) + .and(new EqualsFilter(ldapServerProperties.getUsernameAttribute(), memberAttributeValue)); filteredLdapUsers = getFilteredLdapUsers(ldapServerProperties.getBaseDN(), filter); } return (filteredLdapUsers.isEmpty()) ? null : filteredLdapUsers.iterator().next(); @@ -497,21 +489,20 @@ public class AmbariLdapDataPopulator { /** * Get the LDAP group member for the given member attribute. * - * @param memberAttributeValue the member attribute value - * + * @param memberAttributeValue the member attribute value * @return the group for the given member attribute; null if not found */ protected LdapGroupDto getLdapGroupByMemberAttr(String memberAttributeValue) { - Set<LdapGroupDto> filteredLdapGroups = new HashSet<LdapGroupDto>(); + Set<LdapGroupDto> filteredLdapGroups; memberAttributeValue = getUniqueIdByMemberPattern(memberAttributeValue, - ldapServerProperties.getSyncGroupMemberReplacePattern()); + ldapServerProperties.getSyncGroupMemberReplacePattern()); Filter syncMemberFilter = createCustomMemberFilter(memberAttributeValue, - ldapServerProperties.getSyncGroupMemberFilter()); + ldapServerProperties.getSyncGroupMemberFilter()); if (memberAttributeValue != null && syncMemberFilter != null) { LOG.trace("Use custom filter '{}' for getting member group with default baseDN ('{}')", - syncMemberFilter.encode(), ldapServerProperties.getBaseDN()); + syncMemberFilter.encode(), ldapServerProperties.getBaseDN()); filteredLdapGroups = getFilteredLdapGroups(ldapServerProperties.getBaseDN(), syncMemberFilter); } else if (memberAttributeValue != null && isMemberAttributeBaseDn(memberAttributeValue)) { LOG.trace("Member can be used as baseDn: {}", memberAttributeValue); @@ -520,8 +511,8 @@ public class AmbariLdapDataPopulator { } else { LOG.trace("Member cannot be used as baseDn: {}", memberAttributeValue); filteredLdapGroups = getFilteredLdapGroups(ldapServerProperties.getBaseDN(), - new EqualsFilter(OBJECT_CLASS_ATTRIBUTE, ldapServerProperties.getGroupObjectClass()), - getMemberFilter(memberAttributeValue)); + new EqualsFilter(OBJECT_CLASS_ATTRIBUTE, ldapServerProperties.getGroupObjectClass()), + getMemberFilter(memberAttributeValue)); } return (filteredLdapGroups.isEmpty()) ? null : filteredLdapGroups.iterator().next(); @@ -570,7 +561,7 @@ public class AmbariLdapDataPopulator { */ protected void cleanUpLdapUsersWithoutGroup() throws AmbariException { final List<User> allUsers = users.getAllUsers(); - for (User user: allUsers) { + for (User user : allUsers) { if (user.isLdapUser() && user.getGroups().isEmpty()) { users.removeUser(user); } @@ -600,7 +591,7 @@ public class AmbariLdapDataPopulator { */ protected boolean isMemberAttributeBaseDn(String memberAttributeValue) { Pattern pattern = Pattern.compile(String.format(IS_MEMBER_DN_REGEXP, - ldapServerProperties.getUsernameAttribute(), ldapServerProperties.getGroupNamingAttr())); + ldapServerProperties.getUsernameAttribute(), ldapServerProperties.getGroupNamingAttr())); return pattern.matcher(memberAttributeValue).find(); } @@ -620,10 +611,10 @@ public class AmbariLdapDataPopulator { String dnAttribute = ldapServerProperties.getDnAttribute(); return new OrFilter().or(new EqualsFilter(dnAttribute, memberAttributeValue)). - or(new EqualsFilter(UID_ATTRIBUTE, memberAttributeValue)); + or(new EqualsFilter(UID_ATTRIBUTE, memberAttributeValue)); } - private Set<LdapGroupDto> getFilteredLdapGroups(String baseDn, Filter...filters) { + private Set<LdapGroupDto> getFilteredLdapGroups(String baseDn, Filter... filters) { AndFilter andFilter = new AndFilter(); for (Filter filter : filters) { andFilter.and(filter); @@ -636,7 +627,7 @@ public class AmbariLdapDataPopulator { final LdapTemplate ldapTemplate = loadLdapTemplate(); LOG.trace("LDAP Group Query - Base DN: '{}' ; Filter: '{}'", baseDn, filter.encode()); ldapTemplate.search(baseDn, filter.encode(), - new LdapGroupContextMapper(groups, ldapServerProperties)); + new LdapGroupContextMapper(groups, ldapServerProperties)); return groups; } @@ -651,7 +642,7 @@ public class AmbariLdapDataPopulator { return getFilteredLdapUsers(ldapServerProperties.getBaseDN(), userObjectFilter); } - private Set<LdapUserDto> getFilteredLdapUsers(String baseDn, Filter...filters) { + private Set<LdapUserDto> getFilteredLdapUsers(String baseDn, Filter... filters) { AndFilter andFilter = new AndFilter(); for (Filter filter : filters) { andFilter.and(filter); @@ -672,15 +663,15 @@ public class AmbariLdapDataPopulator { do { LOG.trace("LDAP User Query - Base DN: '{}' ; Filter: '{}'", baseDn, encodedFilter); List dtos = configuration.getLdapServerProperties().isPaginationEnabled() ? - ldapTemplate.search(baseDn, encodedFilter, searchControls, ldapUserContextMapper, processor) : - ldapTemplate.search(baseDn, encodedFilter, searchControls, ldapUserContextMapper); + ldapTemplate.search(LdapUtils.newLdapName(baseDn), encodedFilter, searchControls, ldapUserContextMapper, processor) : + ldapTemplate.search(LdapUtils.newLdapName(baseDn), encodedFilter, searchControls, ldapUserContextMapper); for (Object dto : dtos) { if (dto != null) { - users.add((LdapUserDto)dto); + users.add((LdapUserDto) dto); } } } while (configuration.getLdapServerProperties().isPaginationEnabled() - && processor.getCookie().getCookie() != null); + && processor.getCookie().getCookie() != null); return users; } @@ -744,7 +735,7 @@ public class AmbariLdapDataPopulator { ldapServerProperties = properties; final LdapContextSource ldapContextSource = createLdapContextSource(); - + // The LdapTemplate by design will close the connection after each call to the LDAP Server // In order to have the interaction work with large/paged results, said connection must be pooled and reused ldapContextSource.setPooled(true); @@ -784,6 +775,7 @@ public class AmbariLdapDataPopulator { /** * PagedResultsDirContextProcessor factory method. + * * @return new processor; */ protected PagedResultsDirContextProcessor createPagingProcessor() { @@ -793,8 +785,7 @@ public class AmbariLdapDataPopulator { /** * LdapTemplate factory method. * - * @param ldapContextSource the LDAP context source - * + * @param ldapContextSource the LDAP context source * @return new LDAP template */ protected LdapTemplate createLdapTemplate(LdapContextSource ldapContextSource) { @@ -829,7 +820,7 @@ public class AmbariLdapDataPopulator { group.setGroupName(groupNameAttribute.toLowerCase()); final String[] uniqueMembers = adapter.getStringAttributes(ldapServerProperties.getGroupMembershipAttr()); if (uniqueMembers != null) { - for (String uniqueMember: uniqueMembers) { + for (String uniqueMember : uniqueMembers) { group.getMemberAttributes().add(uniqueMember.toLowerCase()); } } @@ -849,7 +840,7 @@ public class AmbariLdapDataPopulator { @Override public Object mapFromContext(Object ctx) { - final DirContextAdapter adapter = (DirContextAdapter) ctx; + final DirContextAdapter adapter = (DirContextAdapter) ctx; final String usernameAttribute = adapter.getStringAttribute(ldapServerProperties.getUsernameAttribute()); final String uidAttribute = adapter.getStringAttribute(UID_ATTRIBUTE); @@ -867,7 +858,7 @@ public class AmbariLdapDataPopulator { return user; } else { LOG.warn("Ignoring LDAP user " + adapter.getNameInNamespace() + " as it doesn't have required" + - " attributes uid and " + ldapServerProperties.getUsernameAttribute()); + " attributes uid and " + ldapServerProperties.getUsernameAttribute()); } return null; } http://git-wip-us.apache.org/repos/asf/ambari/blob/593234b7/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 5629913..46f9689 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 @@ -1,4 +1,4 @@ -/** +/* * 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 @@ -7,7 +7,7 @@ * "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 + * 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, @@ -15,33 +15,32 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.apache.ambari.server.security; 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.createStrictMock; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; -import javax.naming.Context; +import javax.naming.Name; import javax.naming.NamingException; + import org.apache.ambari.server.security.authorization.AmbariLdapUtils; +import org.junit.Assert; 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 org.springframework.ldap.support.LdapUtils; -@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"; + private static final String USER_BASE_DN = "ou=hdp,ou=Users,dc=apache,dc=org"; + private static final String USER_RELATIVE_DN = "uid=myuser"; + private static final String USER_DN = USER_RELATIVE_DN + "," + USER_BASE_DN; @Test public void testIsUserPrincipalNameFormat_True() throws Exception { @@ -106,46 +105,101 @@ public class AmbariLdapUtilsTest { @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(); + Name fullDn = LdapUtils.newLdapName(USER_DN); + DirContextAdapter adapter = createNiceMock(DirContextAdapter.class); expect(adapter.getDn()).andReturn(fullDn); - expect(context.getNameInNamespace()).andReturn(USER_DN); + expect(adapter.getNameInNamespace()).andReturn(USER_DN); - replay(adapter, context); - PowerMock.replayAll(); + replay(adapter); // WHEN boolean isOutOfScopeFromBaseDN = AmbariLdapUtils.isLdapObjectOutOfScopeFromBaseDn(adapter, "dc=apache,dc=org"); // THEN assertFalse(isOutOfScopeFromBaseDN); + + verify(adapter); } @Test public void testIsLdapObjectOutOfScopeFromBaseDn_dnOutOfScope() throws NamingException { // GIVEN - DistinguishedName fullDn = new DistinguishedName(USER_DN); - Context context = createNiceMock(Context.class); + Name fullDn = LdapUtils.newLdapName(USER_DN); 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); + expect(adapter.getNameInNamespace()).andReturn(USER_DN); - replay(adapter, context); - PowerMock.replayAll(); + replay(adapter); // WHEN boolean isOutOfScopeFromBaseDN = AmbariLdapUtils.isLdapObjectOutOfScopeFromBaseDn(adapter, "dc=apache,dc=org,ou=custom"); // THEN assertTrue(isOutOfScopeFromBaseDN); + + verify(adapter); + } + + @Test + public void testGetFullDn() throws Exception { + + DirContextAdapter adapterFullDn = createStrictMock(DirContextAdapter.class); + expect(adapterFullDn.getNameInNamespace()).andReturn(USER_DN).anyTimes(); + + DirContextAdapter adapterBaseDn = createStrictMock(DirContextAdapter.class); + expect(adapterBaseDn.getNameInNamespace()).andReturn(USER_BASE_DN).anyTimes(); + + Name absoluteDn = LdapUtils.newLdapName(USER_DN); + Name relativeDn = LdapUtils.newLdapName(USER_RELATIVE_DN); + + replay(adapterFullDn, adapterBaseDn); + + Name fullDn; + + // **************************** + // getFullDn(Name, Context) + fullDn = AmbariLdapUtils.getFullDn(absoluteDn, adapterFullDn); + Assert.assertEquals(absoluteDn, fullDn); + + fullDn = AmbariLdapUtils.getFullDn(absoluteDn, adapterBaseDn); + Assert.assertEquals(absoluteDn, fullDn); + + fullDn = AmbariLdapUtils.getFullDn(relativeDn, adapterBaseDn); + Assert.assertEquals(absoluteDn, fullDn); + // **************************** + + + // **************************** + // getFullDn(String, Context) + fullDn = AmbariLdapUtils.getFullDn(absoluteDn.toString(), adapterFullDn); + Assert.assertEquals(absoluteDn, fullDn); + + fullDn = AmbariLdapUtils.getFullDn(absoluteDn.toString(), adapterBaseDn); + Assert.assertEquals(absoluteDn, fullDn); + + fullDn = AmbariLdapUtils.getFullDn(relativeDn.toString(), adapterBaseDn); + Assert.assertEquals(absoluteDn, fullDn); + // **************************** + + // **************************** + // getFullDn(Name, Name) + Name nameInNamespaceFullDn = LdapUtils.newLdapName(adapterFullDn.getNameInNamespace()); + Name nameInNamespaceBaseDn = LdapUtils.newLdapName(adapterBaseDn.getNameInNamespace()); + + fullDn = AmbariLdapUtils.getFullDn(absoluteDn, nameInNamespaceFullDn); + Assert.assertEquals(absoluteDn, fullDn); + + fullDn = AmbariLdapUtils.getFullDn(absoluteDn, nameInNamespaceBaseDn); + Assert.assertEquals(absoluteDn, fullDn); + + fullDn = AmbariLdapUtils.getFullDn(relativeDn, nameInNamespaceBaseDn); + Assert.assertEquals(absoluteDn, fullDn); + + // Make sure nameInNamespace was not altered + Assert.assertEquals(adapterFullDn.getNameInNamespace(), nameInNamespaceFullDn.toString()); + Assert.assertEquals(adapterBaseDn.getNameInNamespace(), nameInNamespaceBaseDn.toString()); + // **************************** + + verify(adapterFullDn, adapterBaseDn); } } http://git-wip-us.apache.org/repos/asf/ambari/blob/593234b7/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLdapBindAuthenticatorTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLdapBindAuthenticatorTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLdapBindAuthenticatorTest.java index fcaae37..385666a 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLdapBindAuthenticatorTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLdapBindAuthenticatorTest.java @@ -28,8 +28,8 @@ import static org.junit.Assert.fail; import java.util.Properties; import javax.naming.NamingEnumeration; -import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; +import javax.naming.directory.BasicAttributes; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; import javax.naming.ldap.LdapName; @@ -39,8 +39,8 @@ import org.apache.commons.lang.StringUtils; import org.easymock.EasyMockSupport; import org.junit.Test; import org.springframework.ldap.core.DirContextOperations; -import org.springframework.ldap.core.DistinguishedName; import org.springframework.ldap.core.support.LdapContextSource; +import org.springframework.ldap.support.LdapUtils; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.ldap.search.FilterBasedLdapUserSearch; import org.springframework.web.context.request.RequestAttributes; @@ -75,10 +75,10 @@ public class AmbariLdapBindAuthenticatorTest extends EasyMockSupport { String ldapUserRelativeDNString = String.format("uid=%s,ou=people,ou=dev", "ldapUsername"); LdapName ldapUserRelativeDN = new LdapName(ldapUserRelativeDNString); String ldapUserDNString = String.format("%s,%s", ldapUserRelativeDNString, basePathString); - DistinguishedName basePath = new DistinguishedName(basePathString); + LdapName basePath = LdapUtils.newLdapName(basePathString); LdapContextSource ldapCtxSource = createMock(LdapContextSource.class); - expect(ldapCtxSource.getBaseLdapPath()) + expect(ldapCtxSource.getBaseLdapName()) .andReturn(basePath) .atLeastOnce(); expect(ldapCtxSource.getContext(ldapUserDNString, "password")) @@ -117,7 +117,7 @@ public class AmbariLdapBindAuthenticatorTest extends EasyMockSupport { String ldapUserRelativeDNString = String.format("uid=%s,ou=people,ou=dev", ldapUsername); LdapName ldapUserRelativeDN = new LdapName(ldapUserRelativeDNString); String ldapUserDNString = String.format("%s,%s", ldapUserRelativeDNString, basePathString); - DistinguishedName basePath = new DistinguishedName(basePathString); + LdapName basePath = LdapUtils.newLdapName(basePathString); @SuppressWarnings("unchecked") NamingEnumeration<SearchResult> adminGroups = createMock(NamingEnumeration.class); @@ -136,7 +136,7 @@ public class AmbariLdapBindAuthenticatorTest extends EasyMockSupport { LdapContextSource ldapCtxSource = createMock(LdapContextSource.class); - expect(ldapCtxSource.getBaseLdapPath()) + expect(ldapCtxSource.getBaseLdapName()) .andReturn(basePath) .atLeastOnce(); expect(ldapCtxSource.getContext(ldapUserDNString, "password")) @@ -146,16 +146,7 @@ public class AmbariLdapBindAuthenticatorTest extends EasyMockSupport { .andReturn(boundUserContext) .once(); - Attribute uidAttribute = createMock(Attribute.class); - expect(uidAttribute.size()) - .andReturn(1) - .atLeastOnce(); - expect(uidAttribute.get()).andReturn(ldapUsername).atLeastOnce(); - - Attributes searchedAttributes = createMock(Attributes.class); - expect(searchedAttributes.get("uid")) - .andReturn(uidAttribute) - .atLeastOnce(); + Attributes searchedAttributes = new BasicAttributes("uid", ldapUsername); DirContextOperations searchedUserContext = createMock(DirContextOperations.class); expect(searchedUserContext.getDn()) http://git-wip-us.apache.org/repos/asf/ambari/blob/593234b7/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 6143cf8..46fe33a 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 @@ -76,6 +76,7 @@ import org.springframework.ldap.core.DirContextAdapter; import org.springframework.ldap.core.LdapTemplate; import org.springframework.ldap.core.support.LdapContextSource; import org.springframework.ldap.filter.Filter; +import org.springframework.ldap.support.LdapUtils; import com.google.common.collect.Sets; @@ -1656,12 +1657,12 @@ public class AmbariLdapDataPopulatorTest { expect(ldapServerProperties.isPaginationEnabled()).andReturn(true).anyTimes(); expect(ldapServerProperties.getUserObjectClass()).andReturn("objectClass").anyTimes(); expect(ldapServerProperties.getDnAttribute()).andReturn("dn").anyTimes(); - expect(ldapServerProperties.getBaseDN()).andReturn("baseDN").anyTimes(); + expect(ldapServerProperties.getBaseDN()).andReturn("cn=testUser,ou=Ambari,dc=SME,dc=support,dc=com").anyTimes(); expect(ldapServerProperties.getUsernameAttribute()).andReturn("uid").anyTimes(); expect(processor.getCookie()).andReturn(cookie).anyTimes(); expect(cookie.getCookie()).andReturn(null).anyTimes(); - expect(ldapTemplate.search(eq("baseDN"), eq("(&(objectClass=objectClass)(uid=foo))"), anyObject(SearchControls.class), capture(contextMapperCapture), eq(processor))).andReturn(list); + expect(ldapTemplate.search(eq(LdapUtils.newLdapName("cn=testUser,ou=Ambari,dc=SME,dc=support,dc=com")), eq("(&(objectClass=objectClass)(uid=foo))"), anyObject(SearchControls.class), capture(contextMapperCapture), eq(processor))).andReturn(list); replay(ldapTemplate, ldapServerProperties, users, configuration, processor, cookie); @@ -1696,9 +1697,9 @@ public class AmbariLdapDataPopulatorTest { expect(ldapServerProperties.getUserObjectClass()).andReturn("objectClass").anyTimes(); expect(ldapServerProperties.getUsernameAttribute()).andReturn("uid").anyTimes(); expect(ldapServerProperties.getDnAttribute()).andReturn("dn").anyTimes(); - expect(ldapServerProperties.getBaseDN()).andReturn("baseDN").anyTimes(); + expect(ldapServerProperties.getBaseDN()).andReturn("cn=testUser,ou=Ambari,dc=SME,dc=support,dc=com").anyTimes(); - expect(ldapTemplate.search(eq("baseDN"), eq("(&(objectClass=objectClass)(uid=foo))"), anyObject(SearchControls.class), capture(contextMapperCapture))).andReturn(list); + expect(ldapTemplate.search(eq(LdapUtils.newLdapName("cn=testUser,ou=Ambari,dc=SME,dc=support,dc=com") ), eq("(&(objectClass=objectClass)(uid=foo))"), anyObject(SearchControls.class), capture(contextMapperCapture))).andReturn(list); replay(ldapTemplate, ldapServerProperties, users, configuration, processor, cookie);
