mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-06-27 06:12:27 +00:00
Update Deprecated Usage
Issue gh-3834
This commit is contained in:
parent
ef5c1a72db
commit
55895f3b08
@ -159,8 +159,9 @@ public class BindAuthenticatorTests {
|
|||||||
BindAuthenticator authenticator = new BindAuthenticator(contextSource);
|
BindAuthenticator authenticator = new BindAuthenticator(contextSource);
|
||||||
authenticator.setUserDnPatterns(new String[] { "uid={0},ou=people" });
|
authenticator.setUserDnPatterns(new String[] { "uid={0},ou=people" });
|
||||||
LdapContext dirContext = mock(LdapContext.class);
|
LdapContext dirContext = mock(LdapContext.class);
|
||||||
given(dirContext.getAttributes(any(Name.class), any())).willThrow(new javax.naming.AuthenticationException("exception"));
|
given(dirContext.getAttributes(any(Name.class), any()))
|
||||||
Name fullDn = LdapUtils.newLdapName("uid=bob,ou=people").addAll(0, contextSource.getBaseLdapPath());
|
.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);
|
given(contextSource.getContext(fullDn.toString(), (String) this.bob.getCredentials())).willReturn(dirContext);
|
||||||
authenticator.setAlsoHandleJavaxNamingBindExceptions(true);
|
authenticator.setAlsoHandleJavaxNamingBindExceptions(true);
|
||||||
assertThatExceptionOfType(BadCredentialsException.class).isThrownBy(authenticateBob(authenticator));
|
assertThatExceptionOfType(BadCredentialsException.class).isThrownBy(authenticateBob(authenticator));
|
||||||
@ -172,4 +173,5 @@ public class BindAuthenticatorTests {
|
|||||||
private ThrowingCallable authenticateBob(BindAuthenticator authenticator) {
|
private ThrowingCallable authenticateBob(BindAuthenticator authenticator) {
|
||||||
return () -> authenticator.authenticate(this.bob);
|
return () -> authenticator.authenticate(this.bob);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -185,7 +185,7 @@ public class LdapUserDetailsManagerTests {
|
|||||||
assertThatExceptionOfType(UsernameNotFoundException.class).isThrownBy(() -> this.mgr.loadUserByUsername("don"));
|
assertThatExceptionOfType(UsernameNotFoundException.class).isThrownBy(() -> this.mgr.loadUserByUsername("don"));
|
||||||
|
|
||||||
// Check that no authorities are left
|
// 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
|
@Test
|
||||||
|
@ -16,7 +16,10 @@
|
|||||||
|
|
||||||
package org.springframework.security.ldap;
|
package org.springframework.security.ldap;
|
||||||
|
|
||||||
|
import javax.naming.ldap.LdapName;
|
||||||
|
|
||||||
import org.springframework.ldap.core.DistinguishedName;
|
import org.springframework.ldap.core.DistinguishedName;
|
||||||
|
import org.springframework.ldap.support.LdapNameBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This implementation appends a name component to the <tt>userDnBase</tt> context using
|
* This implementation appends a name component to the <tt>userDnBase</tt> context using
|
||||||
@ -43,12 +46,19 @@ public class DefaultLdapUsernameToDnMapper implements LdapUsernameToDnMapper {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Assembles the Distinguished Name that should be used the given username.
|
* Assembles the Distinguished Name that should be used the given username.
|
||||||
|
* @deprecated Use {@link #buildLdapName(String)} instead
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@Deprecated
|
||||||
public DistinguishedName buildDn(String username) {
|
public DistinguishedName buildDn(String username) {
|
||||||
DistinguishedName dn = new DistinguishedName(this.userDnBase);
|
DistinguishedName dn = new DistinguishedName(this.userDnBase);
|
||||||
dn.add(this.usernameAttribute, username);
|
dn.add(this.usernameAttribute, username);
|
||||||
return dn;
|
return dn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LdapName buildLdapName(String username) {
|
||||||
|
return LdapNameBuilder.newInstance(this.userDnBase).add(this.usernameAttribute, username).build();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@ public class DefaultSpringSecurityContextSource extends LdapContextSource {
|
|||||||
public void setupEnvironment(Hashtable env, String dn, String password) {
|
public void setupEnvironment(Hashtable env, String dn, String password) {
|
||||||
super.setupEnvironment(env, dn, password);
|
super.setupEnvironment(env, dn, password);
|
||||||
// Remove the pooling flag unless authenticating as the 'manager' user.
|
// 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)) {
|
&& env.containsKey(SUN_LDAP_POOLING_FLAG)) {
|
||||||
DefaultSpringSecurityContextSource.this.logger.trace("Removing pooling flag for user " + dn);
|
DefaultSpringSecurityContextSource.this.logger.trace("Removing pooling flag for user " + dn);
|
||||||
env.remove(SUN_LDAP_POOLING_FLAG);
|
env.remove(SUN_LDAP_POOLING_FLAG);
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package org.springframework.security.ldap;
|
package org.springframework.security.ldap;
|
||||||
|
|
||||||
|
import javax.naming.ldap.LdapName;
|
||||||
|
|
||||||
import org.springframework.ldap.core.DistinguishedName;
|
import org.springframework.ldap.core.DistinguishedName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,6 +27,14 @@ import org.springframework.ldap.core.DistinguishedName;
|
|||||||
*/
|
*/
|
||||||
public interface LdapUsernameToDnMapper {
|
public interface LdapUsernameToDnMapper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use {@link #buildLdapName(String)} instead
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
DistinguishedName buildDn(String username);
|
DistinguishedName buildDn(String username);
|
||||||
|
|
||||||
|
default LdapName buildLdapName(String username) {
|
||||||
|
return org.springframework.ldap.support.LdapUtils.newLdapName(buildDn(username));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,12 +22,14 @@ import java.net.URISyntaxException;
|
|||||||
import javax.naming.Context;
|
import javax.naming.Context;
|
||||||
import javax.naming.NamingEnumeration;
|
import javax.naming.NamingEnumeration;
|
||||||
import javax.naming.NamingException;
|
import javax.naming.NamingException;
|
||||||
|
import javax.naming.ldap.LdapName;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
import org.springframework.ldap.core.DirContextAdapter;
|
import org.springframework.ldap.core.DirContextAdapter;
|
||||||
import org.springframework.ldap.core.DistinguishedName;
|
import org.springframework.ldap.core.DistinguishedName;
|
||||||
|
import org.springframework.ldap.support.LdapNameBuilder;
|
||||||
import org.springframework.security.crypto.codec.Utf8;
|
import org.springframework.security.crypto.codec.Utf8;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
@ -84,13 +86,15 @@ public final class LdapUtils {
|
|||||||
if (baseDn.length() == 0) {
|
if (baseDn.length() == 0) {
|
||||||
return fullDn;
|
return fullDn;
|
||||||
}
|
}
|
||||||
DistinguishedName base = new DistinguishedName(baseDn);
|
LdapName base = LdapNameBuilder.newInstance(baseDn).build();
|
||||||
DistinguishedName full = new DistinguishedName(fullDn);
|
LdapName full = LdapNameBuilder.newInstance(fullDn).build();
|
||||||
if (base.equals(full)) {
|
if (base.equals(full)) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
Assert.isTrue(full.startsWith(base), "Full DN does not start with base DN");
|
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();
|
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.
|
* 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.
|
* If the name already contains the base name, it is returned unaltered.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static DistinguishedName getFullDn(DistinguishedName dn, Context baseCtx) throws NamingException {
|
public static DistinguishedName getFullDn(DistinguishedName dn, Context baseCtx) throws NamingException {
|
||||||
DistinguishedName baseDn = new DistinguishedName(baseCtx.getNameInNamespace());
|
DistinguishedName baseDn = new DistinguishedName(baseCtx.getNameInNamespace());
|
||||||
if (dn.contains(baseDn)) {
|
if (dn.contains(baseDn)) {
|
||||||
@ -107,6 +112,15 @@ public final class LdapUtils {
|
|||||||
return baseDn;
|
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) {
|
public static String convertPasswordToString(Object passObj) {
|
||||||
Assert.notNull(passObj, "Password object to convert must not be null");
|
Assert.notNull(passObj, "Password object to convert must not be null");
|
||||||
if (passObj instanceof byte[]) {
|
if (passObj instanceof byte[]) {
|
||||||
|
@ -33,6 +33,7 @@ import javax.naming.directory.Attributes;
|
|||||||
import javax.naming.directory.DirContext;
|
import javax.naming.directory.DirContext;
|
||||||
import javax.naming.directory.SearchControls;
|
import javax.naming.directory.SearchControls;
|
||||||
import javax.naming.directory.SearchResult;
|
import javax.naming.directory.SearchResult;
|
||||||
|
import javax.naming.ldap.LdapName;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
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.ContextSource;
|
||||||
import org.springframework.ldap.core.DirContextAdapter;
|
import org.springframework.ldap.core.DirContextAdapter;
|
||||||
import org.springframework.ldap.core.DirContextOperations;
|
import org.springframework.ldap.core.DirContextOperations;
|
||||||
import org.springframework.ldap.core.DistinguishedName;
|
|
||||||
import org.springframework.ldap.core.LdapTemplate;
|
import org.springframework.ldap.core.LdapTemplate;
|
||||||
|
import org.springframework.ldap.support.LdapNameBuilder;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
|
|
||||||
@ -113,8 +114,8 @@ public class SpringSecurityLdapTemplate extends LdapTemplate {
|
|||||||
public DirContextOperations retrieveEntry(final String dn, final String[] attributesToRetrieve) {
|
public DirContextOperations retrieveEntry(final String dn, final String[] attributesToRetrieve) {
|
||||||
return (DirContextOperations) executeReadOnly((ContextExecutor) (ctx) -> {
|
return (DirContextOperations) executeReadOnly((ContextExecutor) (ctx) -> {
|
||||||
Attributes attrs = ctx.getAttributes(dn, attributesToRetrieve);
|
Attributes attrs = ctx.getAttributes(dn, attributesToRetrieve);
|
||||||
return new DirContextAdapter(attrs, new DistinguishedName(dn),
|
return new DirContextAdapter(attrs, LdapNameBuilder.newInstance(dn).build(),
|
||||||
new DistinguishedName(ctx.getNameInNamespace()));
|
LdapNameBuilder.newInstance(ctx.getNameInNamespace()).build());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,8 +267,8 @@ public class SpringSecurityLdapTemplate extends LdapTemplate {
|
|||||||
*/
|
*/
|
||||||
public static DirContextOperations searchForSingleEntryInternal(DirContext ctx, SearchControls searchControls,
|
public static DirContextOperations searchForSingleEntryInternal(DirContext ctx, SearchControls searchControls,
|
||||||
String base, String filter, Object[] params) throws NamingException {
|
String base, String filter, Object[] params) throws NamingException {
|
||||||
final DistinguishedName ctxBaseDn = new DistinguishedName(ctx.getNameInNamespace());
|
final LdapName ctxBaseDn = LdapNameBuilder.newInstance(ctx.getNameInNamespace()).build();
|
||||||
final DistinguishedName searchBaseDn = new DistinguishedName(base);
|
final LdapName searchBaseDn = LdapNameBuilder.newInstance(base).build();
|
||||||
final NamingEnumeration<SearchResult> resultsEnum = ctx.search(searchBaseDn, filter, params,
|
final NamingEnumeration<SearchResult> resultsEnum = ctx.search(searchBaseDn, filter, params,
|
||||||
buildControls(searchControls));
|
buildControls(searchControls));
|
||||||
logger.trace(LogMessage.format("Searching for entry under DN '%s', base = '%s', filter = '%s'", ctxBaseDn,
|
logger.trace(LogMessage.format("Searching for entry under DN '%s', base = '%s', filter = '%s'", ctxBaseDn,
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.springframework.security.ldap.authentication;
|
package org.springframework.security.ldap.authentication;
|
||||||
|
|
||||||
|
import javax.naming.Name;
|
||||||
import javax.naming.directory.Attributes;
|
import javax.naming.directory.Attributes;
|
||||||
import javax.naming.directory.DirContext;
|
import javax.naming.directory.DirContext;
|
||||||
|
|
||||||
@ -26,7 +27,6 @@ import org.springframework.core.log.LogMessage;
|
|||||||
import org.springframework.ldap.NamingException;
|
import org.springframework.ldap.NamingException;
|
||||||
import org.springframework.ldap.core.DirContextAdapter;
|
import org.springframework.ldap.core.DirContextAdapter;
|
||||||
import org.springframework.ldap.core.DirContextOperations;
|
import org.springframework.ldap.core.DirContextOperations;
|
||||||
import org.springframework.ldap.core.DistinguishedName;
|
|
||||||
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
|
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
|
||||||
import org.springframework.ldap.support.LdapUtils;
|
import org.springframework.ldap.support.LdapUtils;
|
||||||
import org.springframework.security.authentication.BadCredentialsException;
|
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) {
|
private DirContextOperations bindWithDn(String userDnStr, String username, String password, Attributes attrs) {
|
||||||
BaseLdapPathContextSource ctxSource = (BaseLdapPathContextSource) getContextSource();
|
BaseLdapPathContextSource ctxSource = (BaseLdapPathContextSource) getContextSource();
|
||||||
DistinguishedName userDn = new DistinguishedName(userDnStr);
|
Name userDn = LdapUtils.newLdapName(userDnStr);
|
||||||
DistinguishedName fullDn = new DistinguishedName(userDn);
|
Name fullDn = LdapUtils.prepend(userDn, ctxSource.getBaseLdapName());
|
||||||
fullDn.prepend(ctxSource.getBaseLdapPath());
|
|
||||||
logger.trace(LogMessage.format("Attempting to bind as %s", fullDn));
|
logger.trace(LogMessage.format("Attempting to bind as %s", fullDn));
|
||||||
DirContext ctx = null;
|
DirContext ctx = null;
|
||||||
try {
|
try {
|
||||||
@ -116,7 +115,7 @@ public class BindAuthenticator extends AbstractLdapAuthenticator {
|
|||||||
if (attrs == null || attrs.size() == 0) {
|
if (attrs == null || attrs.size() == 0) {
|
||||||
attrs = ctx.getAttributes(userDn, getUserAttributes());
|
attrs = ctx.getAttributes(userDn, getUserAttributes());
|
||||||
}
|
}
|
||||||
DirContextAdapter result = new DirContextAdapter(attrs, userDn, ctxSource.getBaseLdapPath());
|
DirContextAdapter result = new DirContextAdapter(attrs, userDn, ctxSource.getBaseLdapName());
|
||||||
if (ppolicy != null) {
|
if (ppolicy != null) {
|
||||||
result.setAttributeValue(ppolicy.getID(), ppolicy);
|
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}
|
* Set whether javax-based bind exceptions should also be delegated to
|
||||||
* (only Spring-based bind exceptions are handled by default)
|
* {@code #handleBindException} (only Spring-based bind exceptions are handled by
|
||||||
|
* default)
|
||||||
*
|
*
|
||||||
* <p>For passivity reasons, defaults to {@code false}, though may change to {@code true}
|
* <p>
|
||||||
|
* For passivity reasons, defaults to {@code false}, though may change to {@code true}
|
||||||
* in future releases.
|
* in future releases.
|
||||||
*
|
* @param alsoHandleJavaxNamingBindExceptions - whether to delegate javax-based bind
|
||||||
* @param alsoHandleJavaxNamingBindExceptions - whether to delegate javax-based bind exceptions to
|
* exceptions to #handleBindException
|
||||||
* #handleBindException
|
|
||||||
* @since 6.4
|
* @since 6.4
|
||||||
*/
|
*/
|
||||||
public void setAlsoHandleJavaxNamingBindExceptions(boolean alsoHandleJavaxNamingBindExceptions) {
|
public void setAlsoHandleJavaxNamingBindExceptions(boolean alsoHandleJavaxNamingBindExceptions) {
|
||||||
this.alsoHandleJavaxNamingBindExceptions = alsoHandleJavaxNamingBindExceptions;
|
this.alsoHandleJavaxNamingBindExceptions = alsoHandleJavaxNamingBindExceptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -21,11 +21,13 @@ import java.util.Arrays;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.naming.ldap.LdapName;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
import org.springframework.ldap.core.DirContextOperations;
|
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.GrantedAuthority;
|
||||||
import org.springframework.security.core.authority.AuthorityUtils;
|
import org.springframework.security.core.authority.AuthorityUtils;
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
@ -59,7 +61,9 @@ public final class DefaultActiveDirectoryAuthoritiesPopulator implements LdapAut
|
|||||||
List<GrantedAuthority> authorities = new ArrayList<>(groups.length);
|
List<GrantedAuthority> authorities = new ArrayList<>(groups.length);
|
||||||
|
|
||||||
for (String group : groups) {
|
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;
|
return authorities;
|
||||||
|
@ -47,12 +47,12 @@ public class PasswordPolicyAwareContextSource extends DefaultSpringSecurityConte
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DirContext getContext(String principal, String credentials) throws PasswordPolicyException {
|
public DirContext getContext(String principal, String credentials) throws PasswordPolicyException {
|
||||||
if (principal.equals(this.userDn)) {
|
if (principal.equals(getUserDn())) {
|
||||||
return super.getContext(principal, credentials);
|
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.
|
// 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) };
|
Control[] rctls = { new PasswordPolicyControl(false) };
|
||||||
try {
|
try {
|
||||||
ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, principal);
|
ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, principal);
|
||||||
|
@ -36,6 +36,7 @@ import javax.naming.directory.SearchControls;
|
|||||||
import javax.naming.ldap.ExtendedRequest;
|
import javax.naming.ldap.ExtendedRequest;
|
||||||
import javax.naming.ldap.ExtendedResponse;
|
import javax.naming.ldap.ExtendedResponse;
|
||||||
import javax.naming.ldap.LdapContext;
|
import javax.naming.ldap.LdapContext;
|
||||||
|
import javax.naming.ldap.LdapName;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
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.DistinguishedName;
|
||||||
import org.springframework.ldap.core.LdapTemplate;
|
import org.springframework.ldap.core.LdapTemplate;
|
||||||
import org.springframework.ldap.core.SearchExecutor;
|
import org.springframework.ldap.core.SearchExecutor;
|
||||||
|
import org.springframework.ldap.support.LdapNameBuilder;
|
||||||
import org.springframework.security.authentication.BadCredentialsException;
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
@ -93,7 +95,7 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
|||||||
LdapUsernameToDnMapper usernameMapper = new DefaultLdapUsernameToDnMapper("cn=users", "uid");
|
LdapUsernameToDnMapper usernameMapper = new DefaultLdapUsernameToDnMapper("cn=users", "uid");
|
||||||
|
|
||||||
/** The DN under which groups are stored */
|
/** 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 */
|
/** Password attribute name */
|
||||||
private String passwordAttributeName = "userPassword";
|
private String passwordAttributeName = "userPassword";
|
||||||
@ -137,14 +139,14 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserDetails loadUserByUsername(String username) {
|
public UserDetails loadUserByUsername(String username) {
|
||||||
DistinguishedName dn = this.usernameMapper.buildDn(username);
|
LdapName dn = this.usernameMapper.buildLdapName(username);
|
||||||
List<GrantedAuthority> authorities = getUserAuthorities(dn, username);
|
List<GrantedAuthority> authorities = getUserAuthorities(dn, username);
|
||||||
this.logger.debug(LogMessage.format("Loading user '%s' with DN '%s'", username, dn));
|
this.logger.debug(LogMessage.format("Loading user '%s' with DN '%s'", username, dn));
|
||||||
DirContextAdapter userCtx = loadUserAsContext(dn, username);
|
DirContextAdapter userCtx = loadUserAsContext(dn, username);
|
||||||
return this.userDetailsMapper.mapUserFromContext(userCtx, username, authorities);
|
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) -> {
|
return (DirContextAdapter) this.template.executeReadOnly((ContextExecutor) (ctx) -> {
|
||||||
try {
|
try {
|
||||||
Attributes attrs = ctx.getAttributes(dn, this.attributesToRetrieve);
|
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!");
|
"No authentication object found in security context. Can't change current user's password!");
|
||||||
String username = authentication.getName();
|
String username = authentication.getName();
|
||||||
this.logger.debug(LogMessage.format("Changing password for user '%s'", username));
|
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) {
|
if (this.usePasswordModifyExtensionOperation) {
|
||||||
changePasswordUsingExtensionOperation(userDn, oldPassword, newPassword);
|
changePasswordUsingExtensionOperation(userDn, oldPassword, newPassword);
|
||||||
}
|
}
|
||||||
@ -204,13 +206,13 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
|||||||
* @return the granted authorities returned by the group search
|
* @return the granted authorities returned by the group search
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
List<GrantedAuthority> getUserAuthorities(final DistinguishedName dn, final String username) {
|
List<GrantedAuthority> getUserAuthorities(final LdapName dn, final String username) {
|
||||||
SearchExecutor se = (ctx) -> {
|
SearchExecutor se = (ctx) -> {
|
||||||
DistinguishedName fullDn = LdapUtils.getFullDn(dn, ctx);
|
LdapName fullDn = LdapUtils.getFullDn(dn, ctx);
|
||||||
SearchControls ctrls = new SearchControls();
|
SearchControls ctrls = new SearchControls();
|
||||||
ctrls.setReturningAttributes(new String[] { this.groupRoleAttributeName });
|
ctrls.setReturningAttributes(new String[] { this.groupRoleAttributeName });
|
||||||
return ctx.search(this.groupSearchBase, this.groupSearchFilter, new String[] { fullDn.toUrl(), username },
|
return ctx.search(this.groupSearchBase, this.groupSearchFilter,
|
||||||
ctrls);
|
new String[] { fullDn.toString(), username }, ctrls);
|
||||||
};
|
};
|
||||||
AttributesMapperCallbackHandler roleCollector = new AttributesMapperCallbackHandler(this.roleMapper);
|
AttributesMapperCallbackHandler roleCollector = new AttributesMapperCallbackHandler(this.roleMapper);
|
||||||
this.template.search(se, roleCollector);
|
this.template.search(se, roleCollector);
|
||||||
@ -221,7 +223,7 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
|||||||
public void createUser(UserDetails user) {
|
public void createUser(UserDetails user) {
|
||||||
DirContextAdapter ctx = new DirContextAdapter();
|
DirContextAdapter ctx = new DirContextAdapter();
|
||||||
copyToContext(user, ctx);
|
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.logger.debug(LogMessage.format("Creating new user '%s' with DN '%s'", user.getUsername(), dn));
|
||||||
this.template.bind(dn, ctx, null);
|
this.template.bind(dn, ctx, null);
|
||||||
// Check for any existing authorities which might be set for this
|
// Check for any existing authorities which might be set for this
|
||||||
@ -235,7 +237,7 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateUser(UserDetails user) {
|
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));
|
this.logger.debug(LogMessage.format("Updating new user '%s' with DN '%s'", user.getUsername(), dn));
|
||||||
List<GrantedAuthority> authorities = getUserAuthorities(dn, user.getUsername());
|
List<GrantedAuthority> authorities = getUserAuthorities(dn, user.getUsername());
|
||||||
DirContextAdapter ctx = loadUserAsContext(dn, user.getUsername());
|
DirContextAdapter ctx = loadUserAsContext(dn, user.getUsername());
|
||||||
@ -260,14 +262,14 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteUser(String username) {
|
public void deleteUser(String username) {
|
||||||
DistinguishedName dn = this.usernameMapper.buildDn(username);
|
LdapName dn = this.usernameMapper.buildLdapName(username);
|
||||||
removeAuthorities(dn, getUserAuthorities(dn, username));
|
removeAuthorities(dn, getUserAuthorities(dn, username));
|
||||||
this.template.unbind(dn);
|
this.template.unbind(dn);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean userExists(String username) {
|
public boolean userExists(String username) {
|
||||||
DistinguishedName dn = this.usernameMapper.buildDn(username);
|
LdapName dn = this.usernameMapper.buildLdapName(username);
|
||||||
try {
|
try {
|
||||||
Object obj = this.template.lookup(dn);
|
Object obj = this.template.lookup(dn);
|
||||||
if (obj instanceof Context) {
|
if (obj instanceof Context) {
|
||||||
@ -285,33 +287,48 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
|||||||
* @param group the name of the group
|
* @param group the name of the group
|
||||||
* @return the DN of the corresponding group, including the groupSearchBase
|
* @return the DN of the corresponding group, including the groupSearchBase
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
protected DistinguishedName buildGroupDn(String group) {
|
protected DistinguishedName buildGroupDn(String group) {
|
||||||
DistinguishedName dn = new DistinguishedName(this.groupSearchBase);
|
DistinguishedName dn = new DistinguishedName(this.groupSearchBase);
|
||||||
dn.add(this.groupRoleAttributeName, group.toLowerCase());
|
dn.add(this.groupRoleAttributeName, group.toLowerCase());
|
||||||
return dn;
|
return dn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected LdapName buildGroupName(String group) {
|
||||||
|
return LdapNameBuilder.newInstance(buildGroupDn(group)).build();
|
||||||
|
}
|
||||||
|
|
||||||
protected void copyToContext(UserDetails user, DirContextAdapter ctx) {
|
protected void copyToContext(UserDetails user, DirContextAdapter ctx) {
|
||||||
this.userDetailsMapper.mapUserToContext(user, ctx);
|
this.userDetailsMapper.mapUserToContext(user, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
protected void addAuthorities(DistinguishedName userDn, Collection<? extends GrantedAuthority> authorities) {
|
protected void addAuthorities(DistinguishedName userDn, Collection<? extends GrantedAuthority> authorities) {
|
||||||
modifyAuthorities(userDn, authorities, DirContext.ADD_ATTRIBUTE);
|
modifyAuthorities(LdapNameBuilder.newInstance(userDn).build(), authorities, DirContext.ADD_ATTRIBUTE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void addAuthorities(LdapName userDn, Collection<? extends GrantedAuthority> authorities) {
|
||||||
|
addAuthorities(new DistinguishedName(userDn), authorities);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
protected void removeAuthorities(DistinguishedName userDn, Collection<? extends GrantedAuthority> authorities) {
|
protected void removeAuthorities(DistinguishedName userDn, Collection<? extends GrantedAuthority> authorities) {
|
||||||
modifyAuthorities(userDn, authorities, DirContext.REMOVE_ATTRIBUTE);
|
modifyAuthorities(LdapNameBuilder.newInstance(userDn).build(), authorities, DirContext.REMOVE_ATTRIBUTE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void modifyAuthorities(final DistinguishedName userDn,
|
protected void removeAuthorities(LdapName userDn, Collection<? extends GrantedAuthority> authorities) {
|
||||||
final Collection<? extends GrantedAuthority> authorities, final int modType) {
|
removeAuthorities(new DistinguishedName(userDn), authorities);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void modifyAuthorities(final LdapName userDn, final Collection<? extends GrantedAuthority> authorities,
|
||||||
|
final int modType) {
|
||||||
this.template.executeReadWrite((ContextExecutor) (ctx) -> {
|
this.template.executeReadWrite((ContextExecutor) (ctx) -> {
|
||||||
for (GrantedAuthority authority : authorities) {
|
for (GrantedAuthority authority : authorities) {
|
||||||
String group = convertAuthorityToGroup(authority);
|
String group = convertAuthorityToGroup(authority);
|
||||||
DistinguishedName fullDn = LdapUtils.getFullDn(userDn, ctx);
|
LdapName fullDn = LdapUtils.getFullDn(userDn, ctx);
|
||||||
ModificationItem addGroup = new ModificationItem(modType,
|
ModificationItem addGroup = new ModificationItem(modType,
|
||||||
new BasicAttribute(this.groupMemberAttributeName, fullDn.toUrl()));
|
new BasicAttribute(this.groupMemberAttributeName, fullDn.toString()));
|
||||||
ctx.modifyAttributes(buildGroupDn(group), new ModificationItem[] { addGroup });
|
ctx.modifyAttributes(buildGroupName(group), new ModificationItem[] { addGroup });
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
@ -334,7 +351,7 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setGroupSearchBase(String groupSearchBase) {
|
public void setGroupSearchBase(String groupSearchBase) {
|
||||||
this.groupSearchBase = new DistinguishedName(groupSearchBase);
|
this.groupSearchBase = LdapNameBuilder.newInstance(groupSearchBase).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setGroupRoleAttributeName(String groupRoleAttributeName) {
|
public void setGroupRoleAttributeName(String groupRoleAttributeName) {
|
||||||
@ -413,8 +430,7 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
|||||||
this.rolePrefix = rolePrefix;
|
this.rolePrefix = rolePrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void changePasswordUsingAttributeModification(DistinguishedName userDn, String oldPassword,
|
private void changePasswordUsingAttributeModification(LdapName userDn, String oldPassword, String newPassword) {
|
||||||
String newPassword) {
|
|
||||||
ModificationItem[] passwordChange = new ModificationItem[] { new ModificationItem(DirContext.REPLACE_ATTRIBUTE,
|
ModificationItem[] passwordChange = new ModificationItem[] { new ModificationItem(DirContext.REPLACE_ATTRIBUTE,
|
||||||
new BasicAttribute(this.passwordAttributeName, newPassword)) };
|
new BasicAttribute(this.passwordAttributeName, newPassword)) };
|
||||||
if (oldPassword == null) {
|
if (oldPassword == null) {
|
||||||
@ -438,11 +454,10 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void changePasswordUsingExtensionOperation(DistinguishedName userDn, String oldPassword,
|
private void changePasswordUsingExtensionOperation(LdapName userDn, String oldPassword, String newPassword) {
|
||||||
String newPassword) {
|
|
||||||
this.template.executeReadWrite((dirCtx) -> {
|
this.template.executeReadWrite((dirCtx) -> {
|
||||||
LdapContext ctx = (LdapContext) 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);
|
PasswordModifyRequest request = new PasswordModifyRequest(userIdentity, oldPassword, newPassword);
|
||||||
try {
|
try {
|
||||||
return ctx.extendedOperation(request);
|
return ctx.extendedOperation(request);
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.springframework.security.ldap;
|
package org.springframework.security.ldap;
|
||||||
|
|
||||||
|
import javax.naming.Name;
|
||||||
import javax.naming.NamingEnumeration;
|
import javax.naming.NamingEnumeration;
|
||||||
import javax.naming.directory.DirContext;
|
import javax.naming.directory.DirContext;
|
||||||
import javax.naming.directory.SearchControls;
|
import javax.naming.directory.SearchControls;
|
||||||
@ -29,7 +30,6 @@ import org.mockito.Mock;
|
|||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
import org.springframework.ldap.core.DirContextAdapter;
|
import org.springframework.ldap.core.DirContextAdapter;
|
||||||
import org.springframework.ldap.core.DistinguishedName;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
@ -60,7 +60,8 @@ public class SpringSecurityLdapTemplateTests {
|
|||||||
String searchResultName = "ldap://example.com/dc=springframework,dc=org";
|
String searchResultName = "ldap://example.com/dc=springframework,dc=org";
|
||||||
Object[] params = new Object[] {};
|
Object[] params = new Object[] {};
|
||||||
DirContextAdapter searchResultObject = mock(DirContextAdapter.class);
|
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);
|
.willReturn(this.resultsEnum);
|
||||||
given(this.resultsEnum.hasMore()).willReturn(true, false);
|
given(this.resultsEnum.hasMore()).willReturn(true, false);
|
||||||
given(this.resultsEnum.next()).willReturn(this.searchResult);
|
given(this.resultsEnum.next()).willReturn(this.searchResult);
|
||||||
|
@ -36,7 +36,7 @@ import org.mockito.ArgumentCaptor;
|
|||||||
|
|
||||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||||
import org.springframework.ldap.core.DirContextAdapter;
|
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.AccountExpiredException;
|
||||||
import org.springframework.security.authentication.BadCredentialsException;
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
import org.springframework.security.authentication.CredentialsExpiredException;
|
import org.springframework.security.authentication.CredentialsExpiredException;
|
||||||
@ -118,8 +118,7 @@ public class ActiveDirectoryLdapAuthenticationProviderTests {
|
|||||||
customProvider.contextFactory = createContextFactoryReturning(ctx);
|
customProvider.contextFactory = createContextFactoryReturning(ctx);
|
||||||
Authentication result = customProvider.authenticate(this.joe);
|
Authentication result = customProvider.authenticate(this.joe);
|
||||||
assertThat(result.isAuthenticated()).isTrue();
|
assertThat(result.isAuthenticated()).isTrue();
|
||||||
verify(ctx).search(any(DistinguishedName.class), eq(defaultSearchFilter), any(Object[].class),
|
verify(ctx).search(any(Name.class), eq(defaultSearchFilter), any(Object[].class), any(SearchControls.class));
|
||||||
any(SearchControls.class));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SEC-2897,SEC-2224
|
// SEC-2897,SEC-2224
|
||||||
@ -158,8 +157,8 @@ public class ActiveDirectoryLdapAuthenticationProviderTests {
|
|||||||
given(ctx.getNameInNamespace()).willReturn("");
|
given(ctx.getNameInNamespace()).willReturn("");
|
||||||
DirContextAdapter dca = new DirContextAdapter();
|
DirContextAdapter dca = new DirContextAdapter();
|
||||||
SearchResult sr = new SearchResult("CN=Joe Jannsen,CN=Users", dca, dca.getAttributes());
|
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),
|
given(ctx.search(eq(LdapNameBuilder.newInstance("DC=mydomain,DC=eu").build()), any(String.class),
|
||||||
any(SearchControls.class)))
|
any(Object[].class), any(SearchControls.class)))
|
||||||
.willReturn(new MockNamingEnumeration(sr));
|
.willReturn(new MockNamingEnumeration(sr));
|
||||||
this.provider.contextFactory = createContextFactoryReturning(ctx);
|
this.provider.contextFactory = createContextFactoryReturning(ctx);
|
||||||
assertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> this.provider.authenticate(this.joe));
|
assertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> this.provider.authenticate(this.joe));
|
||||||
@ -363,7 +362,7 @@ public class ActiveDirectoryLdapAuthenticationProviderTests {
|
|||||||
DirContextAdapter dca = new DirContextAdapter();
|
DirContextAdapter dca = new DirContextAdapter();
|
||||||
SearchResult sr = new SearchResult("CN=Joe Jannsen,CN=Users", dca, dca.getAttributes());
|
SearchResult sr = new SearchResult("CN=Joe Jannsen,CN=Users", dca, dca.getAttributes());
|
||||||
@SuppressWarnings("deprecation")
|
@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)))
|
given(ctx.search(eq(searchBaseDn), any(String.class), any(Object[].class), any(SearchControls.class)))
|
||||||
.willReturn(new MockNamingEnumeration(sr))
|
.willReturn(new MockNamingEnumeration(sr))
|
||||||
.willReturn(new MockNamingEnumeration(sr));
|
.willReturn(new MockNamingEnumeration(sr));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user