LDAP-291: LdapTemplate correctly throws EmptyResultDataAccessException
This commit is contained in:
@@ -1428,10 +1428,10 @@ public class LdapTemplate implements LdapOperations, InitializingBean {
|
||||
password,
|
||||
getDefaultSearchControls(defaultSearchScope, RETURN_OBJ_FLAG, null),
|
||||
callback,
|
||||
errorCallback);
|
||||
errorCallback).isSuccess();
|
||||
}
|
||||
|
||||
private boolean authenticate(Name base,
|
||||
private AuthenticationStatus authenticate(Name base,
|
||||
String filter,
|
||||
String password,
|
||||
SearchControls searchControls,
|
||||
@@ -1442,7 +1442,7 @@ public class LdapTemplate implements LdapOperations, InitializingBean {
|
||||
if (result.size() == 0) {
|
||||
String msg = "No results found for search, base: '" + base + "'; filter: '" + filter + "'.";
|
||||
LOG.info(msg);
|
||||
return false;
|
||||
return AuthenticationStatus.EMPTYRESULT;
|
||||
} else if (result.size() > 1) {
|
||||
String msg = "base: '" + base + "'; filter: '" + filter + "'.";
|
||||
throw new IncorrectResultSizeDataAccessException(msg, 1, result.size());
|
||||
@@ -1458,12 +1458,12 @@ public class LdapTemplate implements LdapOperations, InitializingBean {
|
||||
return null;
|
||||
}
|
||||
}, ctx);
|
||||
return true;
|
||||
return AuthenticationStatus.SUCCESS;
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.debug("Authentication failed for entry with DN '" + entryIdentification.getAbsoluteName() + "'", e);
|
||||
errorCallback.execute(e);
|
||||
return false;
|
||||
return AuthenticationStatus.UNDEFINED_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1478,7 +1478,7 @@ public class LdapTemplate implements LdapOperations, InitializingBean {
|
||||
CollectingAuthenticationErrorCallback errorCallback =
|
||||
new CollectingAuthenticationErrorCallback();
|
||||
|
||||
boolean succeeded = authenticate(query.base(),
|
||||
AuthenticationStatus authenticationStatus = authenticate(query.base(),
|
||||
query.filter().encode(),
|
||||
password,
|
||||
searchControls,
|
||||
@@ -1493,7 +1493,9 @@ public class LdapTemplate implements LdapOperations, InitializingBean {
|
||||
} else {
|
||||
throw new UncategorizedLdapException(error);
|
||||
}
|
||||
} else if(!succeeded) {
|
||||
} else if(AuthenticationStatus.EMPTYRESULT == authenticationStatus) {
|
||||
throw new EmptyResultDataAccessException(1);
|
||||
} else if(!authenticationStatus.isSuccess()) {
|
||||
throw new AuthenticationException();
|
||||
}
|
||||
|
||||
@@ -1871,4 +1873,38 @@ public class LdapTemplate implements LdapOperations, InitializingBean {
|
||||
|
||||
return result.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* The status of an authentication attempt.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
private enum AuthenticationStatus {
|
||||
/**
|
||||
* Authentication was successful
|
||||
*/
|
||||
SUCCESS(true),
|
||||
/**
|
||||
* The user was not found
|
||||
*/
|
||||
EMPTYRESULT(false),
|
||||
/**
|
||||
* Authentication failed for other reason
|
||||
*/
|
||||
UNDEFINED_FAILURE(false);
|
||||
|
||||
private boolean success;
|
||||
|
||||
AuthenticationStatus(boolean success) {
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the authentication attempt was successful
|
||||
* @return
|
||||
*/
|
||||
public boolean isSuccess() {
|
||||
return success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ import org.springframework.ldap.PartialResultException;
|
||||
import org.springframework.ldap.UncategorizedLdapException;
|
||||
import org.springframework.ldap.filter.EqualsFilter;
|
||||
import org.springframework.ldap.odm.core.ObjectDirectoryMapper;
|
||||
import org.springframework.ldap.query.LdapQuery;
|
||||
import org.springframework.ldap.query.LdapQueryBuilder;
|
||||
import org.springframework.ldap.support.LdapUtils;
|
||||
|
||||
import javax.naming.Binding;
|
||||
@@ -44,6 +46,7 @@ import javax.naming.directory.SearchControls;
|
||||
import javax.naming.directory.SearchResult;
|
||||
import javax.naming.ldap.LdapContext;
|
||||
import javax.naming.ldap.LdapName;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -103,7 +106,10 @@ public class LdapTemplateTest {
|
||||
private AuthenticatedLdapEntryContextCallback entryContextCallbackMock;
|
||||
private ObjectDirectoryMapper odmMock;
|
||||
|
||||
@Before
|
||||
private LdapQuery query;
|
||||
private AuthenticatedLdapEntryContextMapper authContextMapperMock;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
// Setup ContextSource mock
|
||||
@@ -125,6 +131,8 @@ public class LdapTemplateTest {
|
||||
authenticatedContextMock = mock(DirContext.class);
|
||||
entryContextCallbackMock = mock(AuthenticatedLdapEntryContextCallback.class);
|
||||
odmMock = mock(ObjectDirectoryMapper.class);
|
||||
query = LdapQueryBuilder.query().base("ou=spring").filter("ou=user");
|
||||
authContextMapperMock = mock(AuthenticatedLdapEntryContextMapper.class);
|
||||
|
||||
tested = new LdapTemplate(contextSourceMock);
|
||||
tested.setObjectDirectoryMapper(odmMock);
|
||||
@@ -1714,6 +1722,46 @@ public class LdapTemplateTest {
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testAuthenticateQueryPasswordMapperWhenNoUserWasFoundShouldThrowEmptyResult() throws Exception {
|
||||
|
||||
when(contextSourceMock.getReadOnlyContext()).thenReturn(dirContextMock);
|
||||
|
||||
when(dirContextMock.search(
|
||||
any(Name.class),
|
||||
any(String.class),
|
||||
any(SearchControls.class))).thenReturn(namingEnumerationMock);
|
||||
|
||||
when(namingEnumerationMock.hasMore()).thenReturn(false);
|
||||
|
||||
try {
|
||||
tested.authenticate(query, "", authContextMapperMock);
|
||||
fail("Expected Exception");
|
||||
}catch(EmptyResultDataAccessException success) {}
|
||||
verify(dirContextMock).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testAuthenticateQueryPasswordWhenNoUserWasFoundShouldThrowEmptyResult() throws Exception {
|
||||
|
||||
when(contextSourceMock.getReadOnlyContext()).thenReturn(dirContextMock);
|
||||
|
||||
when(dirContextMock.search(
|
||||
any(Name.class),
|
||||
any(String.class),
|
||||
any(SearchControls.class))).thenReturn(namingEnumerationMock);
|
||||
|
||||
when(namingEnumerationMock.hasMore()).thenReturn(false);
|
||||
|
||||
try {
|
||||
tested.authenticate(query, "");
|
||||
fail("Expected Exception");
|
||||
}catch(EmptyResultDataAccessException success) {}
|
||||
verify(dirContextMock).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuthenticateWithFailedAuthenticationShouldFail() throws Exception {
|
||||
when(contextSourceMock.getReadOnlyContext()).thenReturn(dirContextMock);
|
||||
|
||||
Reference in New Issue
Block a user