Skip to content

Commit ef5c1a7

Browse files
committed
Support Handling Javax-based Bind Exceptions
Closes gh-3834
1 parent fd1246a commit ef5c1a7

File tree

2 files changed

+61
-8
lines changed

2 files changed

+61
-8
lines changed

ldap/src/integration-test/java/org/springframework/security/ldap/authentication/BindAuthenticatorTests.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,19 @@
1616

1717
package org.springframework.security.ldap.authentication;
1818

19+
import javax.naming.Name;
20+
import javax.naming.ldap.LdapContext;
21+
22+
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
1923
import org.junit.jupiter.api.BeforeEach;
2024
import org.junit.jupiter.api.Test;
2125
import org.junit.jupiter.api.extension.ExtendWith;
2226

2327
import org.springframework.beans.factory.annotation.Autowired;
28+
import org.springframework.ldap.AuthenticationException;
2429
import org.springframework.ldap.core.DirContextOperations;
30+
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
31+
import org.springframework.ldap.support.LdapUtils;
2532
import org.springframework.security.authentication.BadCredentialsException;
2633
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
2734
import org.springframework.security.core.Authentication;
@@ -34,6 +41,10 @@
3441

3542
import static org.assertj.core.api.Assertions.assertThat;
3643
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
44+
import static org.mockito.ArgumentMatchers.any;
45+
import static org.mockito.BDDMockito.given;
46+
import static org.mockito.Mockito.mock;
47+
import static org.mockito.Mockito.spy;
3748

3849
/**
3950
* Tests for {@link BindAuthenticator}.
@@ -142,4 +153,23 @@ public void testUserDnPatternReturnsCorrectDn() {
142153
assertThat(this.authenticator.getUserDns("Joe").get(0)).isEqualTo("cn=Joe,ou=people");
143154
}
144155

156+
@Test
157+
public void setAlsoHandleJavaxNamingBindExceptionsWhenTrueThenHandles() throws Exception {
158+
BaseLdapPathContextSource contextSource = spy(this.contextSource);
159+
BindAuthenticator authenticator = new BindAuthenticator(contextSource);
160+
authenticator.setUserDnPatterns(new String[] { "uid={0},ou=people" });
161+
LdapContext dirContext = mock(LdapContext.class);
162+
given(dirContext.getAttributes(any(Name.class), any())).willThrow(new javax.naming.AuthenticationException("exception"));
163+
Name fullDn = LdapUtils.newLdapName("uid=bob,ou=people").addAll(0, contextSource.getBaseLdapPath());
164+
given(contextSource.getContext(fullDn.toString(), (String) this.bob.getCredentials())).willReturn(dirContext);
165+
authenticator.setAlsoHandleJavaxNamingBindExceptions(true);
166+
assertThatExceptionOfType(BadCredentialsException.class).isThrownBy(authenticateBob(authenticator));
167+
authenticator.setAlsoHandleJavaxNamingBindExceptions(false);
168+
assertThatExceptionOfType(AuthenticationException.class).isThrownBy(authenticateBob(authenticator))
169+
.withCauseInstanceOf(javax.naming.AuthenticationException.class);
170+
}
171+
172+
private ThrowingCallable authenticateBob(BindAuthenticator authenticator) {
173+
return () -> authenticator.authenticate(this.bob);
174+
}
145175
}

ldap/src/main/java/org/springframework/security/ldap/authentication/BindAuthenticator.java

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ public class BindAuthenticator extends AbstractLdapAuthenticator {
4747

4848
private static final Log logger = LogFactory.getLog(BindAuthenticator.class);
4949

50+
private boolean alsoHandleJavaxNamingBindExceptions = false;
51+
5052
/**
5153
* Create an initialized instance using the {@link BaseLdapPathContextSource}
5254
* provided.
@@ -125,23 +127,30 @@ private DirContextOperations bindWithDn(String userDnStr, String username, Strin
125127
// This will be thrown if an invalid user name is used and the method may
126128
// be called multiple times to try different names, so we trap the exception
127129
// unless a subclass wishes to implement more specialized behaviour.
128-
if ((ex instanceof org.springframework.ldap.AuthenticationException)
129-
|| (ex instanceof org.springframework.ldap.OperationNotSupportedException)) {
130-
handleBindException(userDnStr, username, ex);
131-
}
132-
else {
133-
throw ex;
134-
}
130+
handleIfBindException(userDnStr, username, ex);
135131
}
136132
catch (javax.naming.NamingException ex) {
137-
throw LdapUtils.convertLdapException(ex);
133+
if (!this.alsoHandleJavaxNamingBindExceptions) {
134+
throw LdapUtils.convertLdapException(ex);
135+
}
136+
handleIfBindException(userDnStr, username, LdapUtils.convertLdapException(ex));
138137
}
139138
finally {
140139
LdapUtils.closeContext(ctx);
141140
}
142141
return null;
143142
}
144143

144+
private void handleIfBindException(String dn, String username, org.springframework.ldap.NamingException naming) {
145+
if ((naming instanceof org.springframework.ldap.AuthenticationException)
146+
|| (naming instanceof org.springframework.ldap.OperationNotSupportedException)) {
147+
handleBindException(dn, username, naming);
148+
}
149+
else {
150+
throw naming;
151+
}
152+
}
153+
145154
/**
146155
* Allows subclasses to inspect the exception thrown by an attempt to bind with a
147156
* particular DN. The default implementation just reports the failure to the debug
@@ -151,4 +160,18 @@ protected void handleBindException(String userDn, String username, Throwable cau
151160
logger.trace(LogMessage.format("Failed to bind as %s", userDn), cause);
152161
}
153162

163+
/**
164+
* Set whether javax-based bind exceptions should also be delegated to {@code #handleBindException}
165+
* (only Spring-based bind exceptions are handled by default)
166+
*
167+
* <p>For passivity reasons, defaults to {@code false}, though may change to {@code true}
168+
* in future releases.
169+
*
170+
* @param alsoHandleJavaxNamingBindExceptions - whether to delegate javax-based bind exceptions to
171+
* #handleBindException
172+
* @since 6.4
173+
*/
174+
public void setAlsoHandleJavaxNamingBindExceptions(boolean alsoHandleJavaxNamingBindExceptions) {
175+
this.alsoHandleJavaxNamingBindExceptions = alsoHandleJavaxNamingBindExceptions;
176+
}
154177
}

0 commit comments

Comments
 (0)