LDAP-291: LdapTemplate correctly throws EmptyResultDataAccessException

This commit is contained in:
Rob Winch
2014-02-11 13:43:40 -06:00
parent 3345854deb
commit 9e2fbbc273
2 changed files with 92 additions and 8 deletions

View File

@@ -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;
}
}
}

View File

@@ -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);