I am able to create a DirContext using the credentials provided. So it seems that I am connecting to the ldap server and verifying credentials but later on we do a .search on the context that we get from these credentials. Here it is failing. I have included my spring security configuration in addition to code that shows how I verified the credentials are working and code which seems to be failiing.
spring-security configuration
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<http pattern="/ui/login" security="none"></http>
<http pattern="/styles" security="none"/>
<http use-expressions="true">
<intercept-url pattern="/views/*" access="isAuthenticated()" />
<intercept-url pattern="/database/upload" access="isAuthenticated()" />
<intercept-url pattern="/database/save" access="isAuthenticated()" />
<intercept-url pattern="/database/list" access="isAuthenticated()" />
<intercept-url pattern="/database/delete" access="isAuthenticated()" />
<intercept-url pattern="/project/*" access="isAuthenticated()" />
<intercept-url pattern="/file/*" access="isAuthenticated()" />
<intercept-url pattern="/amazon/*" access="isAuthenticated()" />
<intercept-url pattern="/python/*" access="isAuthenticated()" />
<intercept-url pattern="/r/*" access="isAuthenticated()" />
<intercept-url pattern="/project/*" access="isAuthenticated()" />
<intercept-url pattern="/image/*" access="isAuthenticated()" />
<intercept-url pattern="/shell/*" access="isAuthenticated()" />
<intercept-url pattern="/register" access="hasRole('ROLE_ADMIN')" />
<intercept-url pattern="/user/save" access="hasRole('ROLE_ADMIN')" />
<intercept-url pattern="/user/userAdministrator" access="hasRole('ROLE_ADMIN')" />
<intercept-url pattern="/user/list" access="isAuthenticated()" />
<intercept-url pattern="/user/archive" access="isAuthenticated()" />
<form-login login-page="/login" default-target-url="/views/main"
authentication-failure-url="/loginfailed"/>
<logout logout-success-url="/login" />
</http>
<beans:bean id="ldapAuthProvider"
class="org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider">
<beans:constructor-arg value="simplead.blazingdb.com" />
<beans:constructor-arg value="ldap://simplead.blazingdb.com/" />
</beans:bean>
<authentication-manager alias="authenticationManager" erase-credentials="true">
<authentication-provider ref="ldapAuthProvider">
</authentication-provider>
</authentication-manager>
</beans:beans>
from ActiveDirectoryLdapAuthenticationProvider
@Override
protected DirContextOperations doAuthentication(UsernamePasswordAuthenticationToken auth) {
String username = auth.getName();
String password = (String)auth.getCredentials();
DirContext ctx = bindAsUser(username, password);
try {
return searchForUser(ctx, username);
} catch (NamingException e) {
logger.error("Failed to locate directory entry for authenticated user: " + username, e);
throw badCredentials();
} finally {
LdapUtils.closeContext(ctx);
}
}
This returns just fine so long as I pass in the correct credentials and fails if I send the wrong credentials so I know that we are making it this far.
The issue comes inside of SpringSecurityLdapTemplate
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 NamingEnumeration<SearchResult> resultsEnum = ctx.search(searchBaseDn, filter, params, searchControls);
if (logger.isDebugEnabled()) {
logger.debug("Searching for entry under DN '" + ctxBaseDn
+ "', base = '" + searchBaseDn + "', filter = '" + filter + "'");
}
Set<DirContextOperations> results = new HashSet<DirContextOperations>();
try {
while (resultsEnum.hasMore()) {
SearchResult searchResult = resultsEnum.next();
// Work out the DN of the matched entry
DistinguishedName dn = new DistinguishedName(new CompositeName(searchResult.getName()));
if (base.length() > 0) {
dn.prepend(searchBaseDn);
}
if (logger.isDebugEnabled()) {
logger.debug("Found DN: " + dn);
}
results.add(new DirContextAdapter(searchResult.getAttributes(), dn, ctxBaseDn));
}
} catch (PartialResultException e) {
LdapUtils.closeEnumeration(resultsEnum);
logger.info("Ignoring PartialResultException");
}
if (results.size() == 0) {
throw new IncorrectResultSizeDataAccessException(1, 0);
}
if (results.size() > 1) {
throw new IncorrectResultSizeDataAccessException(1, results.size());
}
return results.iterator().next();
}
Specifically the following line I think is where I am seeing issues. We get a return of size 0 when it is expecting 1 so it throws an error and the whole thing fails.
final NamingEnumeration<SearchResult> resultsEnum = ctx.search(searchBaseDn, filter, params, searchControls);
Whenever he we try to do resultsEnum.hasMore() we catch a PartialResultsException.
I am trying to figure out why this is the case. I am using Amazon Simple directory service (the one that is backed by Samba not the MSFT version). I am very new to LDAP and Active Directory so if my question is poorly formed please let me know what information I need to add.
The issue was pretty straightforward once I used Apache Directory Studio to try and run the ldap queries coming out of Spring Security Active Directory defaults. They assume you have an attribute called userPrincipalName which is a combination of the sAMAccountName and the domain.
In the end I had to set the searchFilter to use sAMAccountName and I had to make my own version of ActiveDirectoryLdapAuthenticationProvider which only looked for users inside of the domain being used but was only comparing sAMAccountName. I only changed searchForUser but since this was a final class I did have to just copy it over. I HATE having to do this but I need to keep moving and these are not configurable options in Spring Security 3.2.9.
package org.springframework.security.ldap.authentication.ad;