diff --git a/ldap/src/integration-test/java/org/springframework/security/ldap/authentication/BindAuthenticatorTests.java b/ldap/src/integration-test/java/org/springframework/security/ldap/authentication/BindAuthenticatorTests.java index 7d859f2fca..95e73bd217 100644 --- a/ldap/src/integration-test/java/org/springframework/security/ldap/authentication/BindAuthenticatorTests.java +++ b/ldap/src/integration-test/java/org/springframework/security/ldap/authentication/BindAuthenticatorTests.java @@ -159,17 +159,19 @@ public class BindAuthenticatorTests { BindAuthenticator authenticator = new BindAuthenticator(contextSource); authenticator.setUserDnPatterns(new String[] { "uid={0},ou=people" }); LdapContext dirContext = mock(LdapContext.class); - given(dirContext.getAttributes(any(Name.class), any())).willThrow(new javax.naming.AuthenticationException("exception")); - Name fullDn = LdapUtils.newLdapName("uid=bob,ou=people").addAll(0, contextSource.getBaseLdapPath()); + given(dirContext.getAttributes(any(Name.class), any())) + .willThrow(new javax.naming.AuthenticationException("exception")); + Name fullDn = LdapUtils.prepend(LdapUtils.newLdapName("uid=bob,ou=people"), contextSource.getBaseLdapName()); given(contextSource.getContext(fullDn.toString(), (String) this.bob.getCredentials())).willReturn(dirContext); authenticator.setAlsoHandleJavaxNamingBindExceptions(true); assertThatExceptionOfType(BadCredentialsException.class).isThrownBy(authenticateBob(authenticator)); authenticator.setAlsoHandleJavaxNamingBindExceptions(false); assertThatExceptionOfType(AuthenticationException.class).isThrownBy(authenticateBob(authenticator)) - .withCauseInstanceOf(javax.naming.AuthenticationException.class); + .withCauseInstanceOf(javax.naming.AuthenticationException.class); } private ThrowingCallable authenticateBob(BindAuthenticator authenticator) { return () -> authenticator.authenticate(this.bob); } + } diff --git a/ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/LdapUserDetailsManagerTests.java b/ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/LdapUserDetailsManagerTests.java index 53ec07d335..78d999232a 100644 --- a/ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/LdapUserDetailsManagerTests.java +++ b/ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/LdapUserDetailsManagerTests.java @@ -185,7 +185,7 @@ public class LdapUserDetailsManagerTests { assertThatExceptionOfType(UsernameNotFoundException.class).isThrownBy(() -> this.mgr.loadUserByUsername("don")); // Check that no authorities are left - assertThat(this.mgr.getUserAuthorities(this.mgr.usernameMapper.buildDn("don"), "don")).hasSize(0); + assertThat(this.mgr.getUserAuthorities(this.mgr.usernameMapper.buildLdapName("don"), "don")).hasSize(0); } @Test diff --git a/ldap/src/main/java/org/springframework/security/ldap/DefaultLdapUsernameToDnMapper.java b/ldap/src/main/java/org/springframework/security/ldap/DefaultLdapUsernameToDnMapper.java index e4c0ac0e08..cd96068adb 100644 --- a/ldap/src/main/java/org/springframework/security/ldap/DefaultLdapUsernameToDnMapper.java +++ b/ldap/src/main/java/org/springframework/security/ldap/DefaultLdapUsernameToDnMapper.java @@ -16,7 +16,10 @@ package org.springframework.security.ldap; +import javax.naming.ldap.LdapName; + import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.support.LdapNameBuilder; /** * This implementation appends a name component to the userDnBase context using @@ -43,12 +46,19 @@ public class DefaultLdapUsernameToDnMapper implements LdapUsernameToDnMapper { /** * Assembles the Distinguished Name that should be used the given username. + * @deprecated Use {@link #buildLdapName(String)} instead */ @Override + @Deprecated public DistinguishedName buildDn(String username) { DistinguishedName dn = new DistinguishedName(this.userDnBase); dn.add(this.usernameAttribute, username); return dn; } + @Override + public LdapName buildLdapName(String username) { + return LdapNameBuilder.newInstance(this.userDnBase).add(this.usernameAttribute, username).build(); + } + } diff --git a/ldap/src/main/java/org/springframework/security/ldap/DefaultSpringSecurityContextSource.java b/ldap/src/main/java/org/springframework/security/ldap/DefaultSpringSecurityContextSource.java index 0471113f6c..ab0de580ae 100644 --- a/ldap/src/main/java/org/springframework/security/ldap/DefaultSpringSecurityContextSource.java +++ b/ldap/src/main/java/org/springframework/security/ldap/DefaultSpringSecurityContextSource.java @@ -88,7 +88,7 @@ public class DefaultSpringSecurityContextSource extends LdapContextSource { public void setupEnvironment(Hashtable env, String dn, String password) { super.setupEnvironment(env, dn, password); // Remove the pooling flag unless authenticating as the 'manager' user. - if (!DefaultSpringSecurityContextSource.this.userDn.equals(dn) + if (!DefaultSpringSecurityContextSource.this.getUserDn().equals(dn) && env.containsKey(SUN_LDAP_POOLING_FLAG)) { DefaultSpringSecurityContextSource.this.logger.trace("Removing pooling flag for user " + dn); env.remove(SUN_LDAP_POOLING_FLAG); diff --git a/ldap/src/main/java/org/springframework/security/ldap/LdapUsernameToDnMapper.java b/ldap/src/main/java/org/springframework/security/ldap/LdapUsernameToDnMapper.java index 43d38b8177..63b197755b 100644 --- a/ldap/src/main/java/org/springframework/security/ldap/LdapUsernameToDnMapper.java +++ b/ldap/src/main/java/org/springframework/security/ldap/LdapUsernameToDnMapper.java @@ -16,6 +16,8 @@ package org.springframework.security.ldap; +import javax.naming.ldap.LdapName; + import org.springframework.ldap.core.DistinguishedName; /** @@ -25,6 +27,14 @@ import org.springframework.ldap.core.DistinguishedName; */ public interface LdapUsernameToDnMapper { + /** + * @deprecated Use {@link #buildLdapName(String)} instead + */ + @Deprecated DistinguishedName buildDn(String username); + default LdapName buildLdapName(String username) { + return org.springframework.ldap.support.LdapUtils.newLdapName(buildDn(username)); + } + } diff --git a/ldap/src/main/java/org/springframework/security/ldap/LdapUtils.java b/ldap/src/main/java/org/springframework/security/ldap/LdapUtils.java index 5909eb8f7a..a3b2522bfb 100644 --- a/ldap/src/main/java/org/springframework/security/ldap/LdapUtils.java +++ b/ldap/src/main/java/org/springframework/security/ldap/LdapUtils.java @@ -22,12 +22,14 @@ import java.net.URISyntaxException; import javax.naming.Context; import javax.naming.NamingEnumeration; import javax.naming.NamingException; +import javax.naming.ldap.LdapName; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.ldap.core.DirContextAdapter; import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.support.LdapNameBuilder; import org.springframework.security.crypto.codec.Utf8; import org.springframework.util.Assert; @@ -84,13 +86,15 @@ public final class LdapUtils { if (baseDn.length() == 0) { return fullDn; } - DistinguishedName base = new DistinguishedName(baseDn); - DistinguishedName full = new DistinguishedName(fullDn); + LdapName base = LdapNameBuilder.newInstance(baseDn).build(); + LdapName full = LdapNameBuilder.newInstance(fullDn).build(); if (base.equals(full)) { return ""; } Assert.isTrue(full.startsWith(base), "Full DN does not start with base DN"); - full.removeFirst(base); + for (int i = 0; i < base.size(); i++) { + full.remove(0); + } return full.toString(); } @@ -98,6 +102,7 @@ public final class LdapUtils { * Gets the full dn of a name by prepending the name of the context it is relative to. * If the name already contains the base name, it is returned unaltered. */ + @Deprecated public static DistinguishedName getFullDn(DistinguishedName dn, Context baseCtx) throws NamingException { DistinguishedName baseDn = new DistinguishedName(baseCtx.getNameInNamespace()); if (dn.contains(baseDn)) { @@ -107,6 +112,15 @@ public final class LdapUtils { return baseDn; } + public static LdapName getFullDn(LdapName dn, Context baseCtx) throws NamingException { + LdapName baseDn = LdapNameBuilder.newInstance(baseCtx.getNameInNamespace()).build(); + if (dn.startsWith(baseDn)) { + return dn; + } + baseDn.addAll(dn); + return baseDn; + } + public static String convertPasswordToString(Object passObj) { Assert.notNull(passObj, "Password object to convert must not be null"); if (passObj instanceof byte[]) { diff --git a/ldap/src/main/java/org/springframework/security/ldap/SpringSecurityLdapTemplate.java b/ldap/src/main/java/org/springframework/security/ldap/SpringSecurityLdapTemplate.java index 42d4067b26..0c3056e563 100644 --- a/ldap/src/main/java/org/springframework/security/ldap/SpringSecurityLdapTemplate.java +++ b/ldap/src/main/java/org/springframework/security/ldap/SpringSecurityLdapTemplate.java @@ -33,6 +33,7 @@ import javax.naming.directory.Attributes; import javax.naming.directory.DirContext; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; +import javax.naming.ldap.LdapName; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -44,8 +45,8 @@ import org.springframework.ldap.core.ContextMapper; 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.support.LdapNameBuilder; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -113,8 +114,8 @@ public class SpringSecurityLdapTemplate extends LdapTemplate { public DirContextOperations retrieveEntry(final String dn, final String[] attributesToRetrieve) { return (DirContextOperations) executeReadOnly((ContextExecutor) (ctx) -> { Attributes attrs = ctx.getAttributes(dn, attributesToRetrieve); - return new DirContextAdapter(attrs, new DistinguishedName(dn), - new DistinguishedName(ctx.getNameInNamespace())); + return new DirContextAdapter(attrs, LdapNameBuilder.newInstance(dn).build(), + LdapNameBuilder.newInstance(ctx.getNameInNamespace()).build()); }); } @@ -266,8 +267,8 @@ public class SpringSecurityLdapTemplate extends LdapTemplate { */ public static DirContextOperations searchForSingleEntryInternal(DirContext ctx, SearchControls searchControls, String base, String filter, Object[] params) throws NamingException { - final DistinguishedName ctxBaseDn = new DistinguishedName(ctx.getNameInNamespace()); - final DistinguishedName searchBaseDn = new DistinguishedName(base); + final LdapName ctxBaseDn = LdapNameBuilder.newInstance(ctx.getNameInNamespace()).build(); + final LdapName searchBaseDn = LdapNameBuilder.newInstance(base).build(); final NamingEnumeration resultsEnum = ctx.search(searchBaseDn, filter, params, buildControls(searchControls)); logger.trace(LogMessage.format("Searching for entry under DN '%s', base = '%s', filter = '%s'", ctxBaseDn, diff --git a/ldap/src/main/java/org/springframework/security/ldap/authentication/BindAuthenticator.java b/ldap/src/main/java/org/springframework/security/ldap/authentication/BindAuthenticator.java index d37dc0748f..1847f932c2 100644 --- a/ldap/src/main/java/org/springframework/security/ldap/authentication/BindAuthenticator.java +++ b/ldap/src/main/java/org/springframework/security/ldap/authentication/BindAuthenticator.java @@ -16,6 +16,7 @@ package org.springframework.security.ldap.authentication; +import javax.naming.Name; import javax.naming.directory.Attributes; import javax.naming.directory.DirContext; @@ -26,7 +27,6 @@ import org.springframework.core.log.LogMessage; import org.springframework.ldap.NamingException; import org.springframework.ldap.core.DirContextAdapter; import org.springframework.ldap.core.DirContextOperations; -import org.springframework.ldap.core.DistinguishedName; import org.springframework.ldap.core.support.BaseLdapPathContextSource; import org.springframework.ldap.support.LdapUtils; import org.springframework.security.authentication.BadCredentialsException; @@ -104,9 +104,8 @@ public class BindAuthenticator extends AbstractLdapAuthenticator { private DirContextOperations bindWithDn(String userDnStr, String username, String password, Attributes attrs) { BaseLdapPathContextSource ctxSource = (BaseLdapPathContextSource) getContextSource(); - DistinguishedName userDn = new DistinguishedName(userDnStr); - DistinguishedName fullDn = new DistinguishedName(userDn); - fullDn.prepend(ctxSource.getBaseLdapPath()); + Name userDn = LdapUtils.newLdapName(userDnStr); + Name fullDn = LdapUtils.prepend(userDn, ctxSource.getBaseLdapName()); logger.trace(LogMessage.format("Attempting to bind as %s", fullDn)); DirContext ctx = null; try { @@ -116,7 +115,7 @@ public class BindAuthenticator extends AbstractLdapAuthenticator { if (attrs == null || attrs.size() == 0) { attrs = ctx.getAttributes(userDn, getUserAttributes()); } - DirContextAdapter result = new DirContextAdapter(attrs, userDn, ctxSource.getBaseLdapPath()); + DirContextAdapter result = new DirContextAdapter(attrs, userDn, ctxSource.getBaseLdapName()); if (ppolicy != null) { result.setAttributeValue(ppolicy.getID(), ppolicy); } @@ -161,17 +160,19 @@ public class BindAuthenticator extends AbstractLdapAuthenticator { } /** - * Set whether javax-based bind exceptions should also be delegated to {@code #handleBindException} - * (only Spring-based bind exceptions are handled by default) + * Set whether javax-based bind exceptions should also be delegated to + * {@code #handleBindException} (only Spring-based bind exceptions are handled by + * default) * - *

For passivity reasons, defaults to {@code false}, though may change to {@code true} + *

+ * For passivity reasons, defaults to {@code false}, though may change to {@code true} * in future releases. - * - * @param alsoHandleJavaxNamingBindExceptions - whether to delegate javax-based bind exceptions to - * #handleBindException + * @param alsoHandleJavaxNamingBindExceptions - whether to delegate javax-based bind + * exceptions to #handleBindException * @since 6.4 */ public void setAlsoHandleJavaxNamingBindExceptions(boolean alsoHandleJavaxNamingBindExceptions) { this.alsoHandleJavaxNamingBindExceptions = alsoHandleJavaxNamingBindExceptions; } + } diff --git a/ldap/src/main/java/org/springframework/security/ldap/authentication/ad/DefaultActiveDirectoryAuthoritiesPopulator.java b/ldap/src/main/java/org/springframework/security/ldap/authentication/ad/DefaultActiveDirectoryAuthoritiesPopulator.java index 1f51b4de10..1ba3549150 100644 --- a/ldap/src/main/java/org/springframework/security/ldap/authentication/ad/DefaultActiveDirectoryAuthoritiesPopulator.java +++ b/ldap/src/main/java/org/springframework/security/ldap/authentication/ad/DefaultActiveDirectoryAuthoritiesPopulator.java @@ -21,11 +21,13 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; +import javax.naming.ldap.LdapName; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.ldap.core.DirContextOperations; -import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.support.LdapNameBuilder; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.authority.SimpleGrantedAuthority; @@ -59,7 +61,9 @@ public final class DefaultActiveDirectoryAuthoritiesPopulator implements LdapAut List authorities = new ArrayList<>(groups.length); for (String group : groups) { - authorities.add(new SimpleGrantedAuthority(new DistinguishedName(group).removeLast().getValue())); + LdapName name = LdapNameBuilder.newInstance(group).build(); + String authority = name.getRdn(name.size() - 1).getValue().toString(); + authorities.add(new SimpleGrantedAuthority(authority)); } return authorities; diff --git a/ldap/src/main/java/org/springframework/security/ldap/ppolicy/PasswordPolicyAwareContextSource.java b/ldap/src/main/java/org/springframework/security/ldap/ppolicy/PasswordPolicyAwareContextSource.java index 9b5d299881..9c4ee19ecf 100755 --- a/ldap/src/main/java/org/springframework/security/ldap/ppolicy/PasswordPolicyAwareContextSource.java +++ b/ldap/src/main/java/org/springframework/security/ldap/ppolicy/PasswordPolicyAwareContextSource.java @@ -47,12 +47,12 @@ public class PasswordPolicyAwareContextSource extends DefaultSpringSecurityConte @Override public DirContext getContext(String principal, String credentials) throws PasswordPolicyException { - if (principal.equals(this.userDn)) { + if (principal.equals(getUserDn())) { return super.getContext(principal, credentials); } - this.logger.trace(LogMessage.format("Binding as %s, prior to reconnect as user %s", this.userDn, principal)); + this.logger.trace(LogMessage.format("Binding as %s, prior to reconnect as user %s", getUserDn(), principal)); // First bind as manager user before rebinding as the specific principal. - LdapContext ctx = (LdapContext) super.getContext(this.userDn, this.password); + LdapContext ctx = (LdapContext) super.getContext(getUserDn(), getPassword()); Control[] rctls = { new PasswordPolicyControl(false) }; try { ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, principal); diff --git a/ldap/src/main/java/org/springframework/security/ldap/userdetails/LdapUserDetailsManager.java b/ldap/src/main/java/org/springframework/security/ldap/userdetails/LdapUserDetailsManager.java index cb3d4c3ff0..3ace03ebe8 100644 --- a/ldap/src/main/java/org/springframework/security/ldap/userdetails/LdapUserDetailsManager.java +++ b/ldap/src/main/java/org/springframework/security/ldap/userdetails/LdapUserDetailsManager.java @@ -36,6 +36,7 @@ import javax.naming.directory.SearchControls; import javax.naming.ldap.ExtendedRequest; import javax.naming.ldap.ExtendedResponse; import javax.naming.ldap.LdapContext; +import javax.naming.ldap.LdapName; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -49,6 +50,7 @@ import org.springframework.ldap.core.DirContextAdapter; import org.springframework.ldap.core.DistinguishedName; import org.springframework.ldap.core.LdapTemplate; import org.springframework.ldap.core.SearchExecutor; +import org.springframework.ldap.support.LdapNameBuilder; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; @@ -93,7 +95,7 @@ public class LdapUserDetailsManager implements UserDetailsManager { LdapUsernameToDnMapper usernameMapper = new DefaultLdapUsernameToDnMapper("cn=users", "uid"); /** The DN under which groups are stored */ - private DistinguishedName groupSearchBase = new DistinguishedName("cn=groups"); + private LdapName groupSearchBase = LdapNameBuilder.newInstance("cn=groups").build(); /** Password attribute name */ private String passwordAttributeName = "userPassword"; @@ -137,14 +139,14 @@ public class LdapUserDetailsManager implements UserDetailsManager { @Override public UserDetails loadUserByUsername(String username) { - DistinguishedName dn = this.usernameMapper.buildDn(username); + LdapName dn = this.usernameMapper.buildLdapName(username); List authorities = getUserAuthorities(dn, username); this.logger.debug(LogMessage.format("Loading user '%s' with DN '%s'", username, dn)); DirContextAdapter userCtx = loadUserAsContext(dn, username); return this.userDetailsMapper.mapUserFromContext(userCtx, username, authorities); } - private DirContextAdapter loadUserAsContext(final DistinguishedName dn, final String username) { + private DirContextAdapter loadUserAsContext(final LdapName dn, final String username) { return (DirContextAdapter) this.template.executeReadOnly((ContextExecutor) (ctx) -> { try { Attributes attrs = ctx.getAttributes(dn, this.attributesToRetrieve); @@ -188,7 +190,7 @@ public class LdapUserDetailsManager implements UserDetailsManager { "No authentication object found in security context. Can't change current user's password!"); String username = authentication.getName(); this.logger.debug(LogMessage.format("Changing password for user '%s'", username)); - DistinguishedName userDn = this.usernameMapper.buildDn(username); + LdapName userDn = this.usernameMapper.buildLdapName(username); if (this.usePasswordModifyExtensionOperation) { changePasswordUsingExtensionOperation(userDn, oldPassword, newPassword); } @@ -204,13 +206,13 @@ public class LdapUserDetailsManager implements UserDetailsManager { * @return the granted authorities returned by the group search */ @SuppressWarnings("unchecked") - List getUserAuthorities(final DistinguishedName dn, final String username) { + List getUserAuthorities(final LdapName dn, final String username) { SearchExecutor se = (ctx) -> { - DistinguishedName fullDn = LdapUtils.getFullDn(dn, ctx); + LdapName fullDn = LdapUtils.getFullDn(dn, ctx); SearchControls ctrls = new SearchControls(); ctrls.setReturningAttributes(new String[] { this.groupRoleAttributeName }); - return ctx.search(this.groupSearchBase, this.groupSearchFilter, new String[] { fullDn.toUrl(), username }, - ctrls); + return ctx.search(this.groupSearchBase, this.groupSearchFilter, + new String[] { fullDn.toString(), username }, ctrls); }; AttributesMapperCallbackHandler roleCollector = new AttributesMapperCallbackHandler(this.roleMapper); this.template.search(se, roleCollector); @@ -221,7 +223,7 @@ public class LdapUserDetailsManager implements UserDetailsManager { public void createUser(UserDetails user) { DirContextAdapter ctx = new DirContextAdapter(); copyToContext(user, ctx); - DistinguishedName dn = this.usernameMapper.buildDn(user.getUsername()); + LdapName dn = this.usernameMapper.buildLdapName(user.getUsername()); this.logger.debug(LogMessage.format("Creating new user '%s' with DN '%s'", user.getUsername(), dn)); this.template.bind(dn, ctx, null); // Check for any existing authorities which might be set for this @@ -235,7 +237,7 @@ public class LdapUserDetailsManager implements UserDetailsManager { @Override public void updateUser(UserDetails user) { - DistinguishedName dn = this.usernameMapper.buildDn(user.getUsername()); + LdapName dn = this.usernameMapper.buildLdapName(user.getUsername()); this.logger.debug(LogMessage.format("Updating new user '%s' with DN '%s'", user.getUsername(), dn)); List authorities = getUserAuthorities(dn, user.getUsername()); DirContextAdapter ctx = loadUserAsContext(dn, user.getUsername()); @@ -260,14 +262,14 @@ public class LdapUserDetailsManager implements UserDetailsManager { @Override public void deleteUser(String username) { - DistinguishedName dn = this.usernameMapper.buildDn(username); + LdapName dn = this.usernameMapper.buildLdapName(username); removeAuthorities(dn, getUserAuthorities(dn, username)); this.template.unbind(dn); } @Override public boolean userExists(String username) { - DistinguishedName dn = this.usernameMapper.buildDn(username); + LdapName dn = this.usernameMapper.buildLdapName(username); try { Object obj = this.template.lookup(dn); if (obj instanceof Context) { @@ -285,33 +287,48 @@ public class LdapUserDetailsManager implements UserDetailsManager { * @param group the name of the group * @return the DN of the corresponding group, including the groupSearchBase */ + @Deprecated protected DistinguishedName buildGroupDn(String group) { DistinguishedName dn = new DistinguishedName(this.groupSearchBase); dn.add(this.groupRoleAttributeName, group.toLowerCase()); return dn; } + protected LdapName buildGroupName(String group) { + return LdapNameBuilder.newInstance(buildGroupDn(group)).build(); + } + protected void copyToContext(UserDetails user, DirContextAdapter ctx) { this.userDetailsMapper.mapUserToContext(user, ctx); } + @Deprecated protected void addAuthorities(DistinguishedName userDn, Collection authorities) { - modifyAuthorities(userDn, authorities, DirContext.ADD_ATTRIBUTE); + modifyAuthorities(LdapNameBuilder.newInstance(userDn).build(), authorities, DirContext.ADD_ATTRIBUTE); } + protected void addAuthorities(LdapName userDn, Collection authorities) { + addAuthorities(new DistinguishedName(userDn), authorities); + } + + @Deprecated protected void removeAuthorities(DistinguishedName userDn, Collection authorities) { - modifyAuthorities(userDn, authorities, DirContext.REMOVE_ATTRIBUTE); + modifyAuthorities(LdapNameBuilder.newInstance(userDn).build(), authorities, DirContext.REMOVE_ATTRIBUTE); } - private void modifyAuthorities(final DistinguishedName userDn, - final Collection authorities, final int modType) { + protected void removeAuthorities(LdapName userDn, Collection authorities) { + removeAuthorities(new DistinguishedName(userDn), authorities); + } + + private void modifyAuthorities(final LdapName userDn, final Collection authorities, + final int modType) { this.template.executeReadWrite((ContextExecutor) (ctx) -> { for (GrantedAuthority authority : authorities) { String group = convertAuthorityToGroup(authority); - DistinguishedName fullDn = LdapUtils.getFullDn(userDn, ctx); + LdapName fullDn = LdapUtils.getFullDn(userDn, ctx); ModificationItem addGroup = new ModificationItem(modType, - new BasicAttribute(this.groupMemberAttributeName, fullDn.toUrl())); - ctx.modifyAttributes(buildGroupDn(group), new ModificationItem[] { addGroup }); + new BasicAttribute(this.groupMemberAttributeName, fullDn.toString())); + ctx.modifyAttributes(buildGroupName(group), new ModificationItem[] { addGroup }); } return null; }); @@ -334,7 +351,7 @@ public class LdapUserDetailsManager implements UserDetailsManager { } public void setGroupSearchBase(String groupSearchBase) { - this.groupSearchBase = new DistinguishedName(groupSearchBase); + this.groupSearchBase = LdapNameBuilder.newInstance(groupSearchBase).build(); } public void setGroupRoleAttributeName(String groupRoleAttributeName) { @@ -413,8 +430,7 @@ public class LdapUserDetailsManager implements UserDetailsManager { this.rolePrefix = rolePrefix; } - private void changePasswordUsingAttributeModification(DistinguishedName userDn, String oldPassword, - String newPassword) { + private void changePasswordUsingAttributeModification(LdapName userDn, String oldPassword, String newPassword) { ModificationItem[] passwordChange = new ModificationItem[] { new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute(this.passwordAttributeName, newPassword)) }; if (oldPassword == null) { @@ -438,11 +454,10 @@ public class LdapUserDetailsManager implements UserDetailsManager { }); } - private void changePasswordUsingExtensionOperation(DistinguishedName userDn, String oldPassword, - String newPassword) { + private void changePasswordUsingExtensionOperation(LdapName userDn, String oldPassword, String newPassword) { this.template.executeReadWrite((dirCtx) -> { LdapContext ctx = (LdapContext) dirCtx; - String userIdentity = LdapUtils.getFullDn(userDn, ctx).encode(); + String userIdentity = LdapUtils.getFullDn(userDn, ctx).toString(); PasswordModifyRequest request = new PasswordModifyRequest(userIdentity, oldPassword, newPassword); try { return ctx.extendedOperation(request); diff --git a/ldap/src/test/java/org/springframework/security/ldap/SpringSecurityLdapTemplateTests.java b/ldap/src/test/java/org/springframework/security/ldap/SpringSecurityLdapTemplateTests.java index 51d43be1e8..cfe4fc3c32 100644 --- a/ldap/src/test/java/org/springframework/security/ldap/SpringSecurityLdapTemplateTests.java +++ b/ldap/src/test/java/org/springframework/security/ldap/SpringSecurityLdapTemplateTests.java @@ -16,6 +16,7 @@ package org.springframework.security.ldap; +import javax.naming.Name; import javax.naming.NamingEnumeration; import javax.naming.directory.DirContext; import javax.naming.directory.SearchControls; @@ -29,7 +30,6 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.ldap.core.DirContextAdapter; -import org.springframework.ldap.core.DistinguishedName; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -60,7 +60,8 @@ public class SpringSecurityLdapTemplateTests { String searchResultName = "ldap://example.com/dc=springframework,dc=org"; Object[] params = new Object[] {}; DirContextAdapter searchResultObject = mock(DirContextAdapter.class); - given(this.ctx.search(any(DistinguishedName.class), eq(filter), eq(params), this.searchControls.capture())) + given(this.ctx.getNameInNamespace()).willReturn("dc=springframework,dc=org"); + given(this.ctx.search(any(Name.class), eq(filter), eq(params), this.searchControls.capture())) .willReturn(this.resultsEnum); given(this.resultsEnum.hasMore()).willReturn(true, false); given(this.resultsEnum.next()).willReturn(this.searchResult); diff --git a/ldap/src/test/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProviderTests.java b/ldap/src/test/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProviderTests.java index 92205144f6..4668d37174 100644 --- a/ldap/src/test/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProviderTests.java +++ b/ldap/src/test/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProviderTests.java @@ -36,7 +36,7 @@ import org.mockito.ArgumentCaptor; import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.ldap.core.DirContextAdapter; -import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.support.LdapNameBuilder; import org.springframework.security.authentication.AccountExpiredException; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.CredentialsExpiredException; @@ -118,8 +118,7 @@ public class ActiveDirectoryLdapAuthenticationProviderTests { customProvider.contextFactory = createContextFactoryReturning(ctx); Authentication result = customProvider.authenticate(this.joe); assertThat(result.isAuthenticated()).isTrue(); - verify(ctx).search(any(DistinguishedName.class), eq(defaultSearchFilter), any(Object[].class), - any(SearchControls.class)); + verify(ctx).search(any(Name.class), eq(defaultSearchFilter), any(Object[].class), any(SearchControls.class)); } // SEC-2897,SEC-2224 @@ -158,8 +157,8 @@ public class ActiveDirectoryLdapAuthenticationProviderTests { given(ctx.getNameInNamespace()).willReturn(""); DirContextAdapter dca = new DirContextAdapter(); SearchResult sr = new SearchResult("CN=Joe Jannsen,CN=Users", dca, dca.getAttributes()); - given(ctx.search(eq(new DistinguishedName("DC=mydomain,DC=eu")), any(String.class), any(Object[].class), - any(SearchControls.class))) + given(ctx.search(eq(LdapNameBuilder.newInstance("DC=mydomain,DC=eu").build()), any(String.class), + any(Object[].class), any(SearchControls.class))) .willReturn(new MockNamingEnumeration(sr)); this.provider.contextFactory = createContextFactoryReturning(ctx); assertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> this.provider.authenticate(this.joe)); @@ -363,7 +362,7 @@ public class ActiveDirectoryLdapAuthenticationProviderTests { DirContextAdapter dca = new DirContextAdapter(); SearchResult sr = new SearchResult("CN=Joe Jannsen,CN=Users", dca, dca.getAttributes()); @SuppressWarnings("deprecation") - DistinguishedName searchBaseDn = new DistinguishedName(rootDn); + Name searchBaseDn = LdapNameBuilder.newInstance(rootDn).build(); given(ctx.search(eq(searchBaseDn), any(String.class), any(Object[].class), any(SearchControls.class))) .willReturn(new MockNamingEnumeration(sr)) .willReturn(new MockNamingEnumeration(sr));