diff --git a/ldap/src/main/java/org/springframework/security/ldap/SpringSecurityLdapTemplate.java b/ldap/src/main/java/org/springframework/security/ldap/SpringSecurityLdapTemplate.java index 835c6587da..f6fd2d3a60 100644 --- a/ldap/src/main/java/org/springframework/security/ldap/SpringSecurityLdapTemplate.java +++ b/ldap/src/main/java/org/springframework/security/ldap/SpringSecurityLdapTemplate.java @@ -1,10 +1,11 @@ -/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited +/* + * Copyright 2002-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -12,23 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.springframework.security.ldap; -import java.text.MessageFormat; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -import javax.naming.CompositeName; -import javax.naming.NamingEnumeration; -import javax.naming.NamingException; -import javax.naming.PartialResultException; -import javax.naming.directory.Attributes; -import javax.naming.directory.DirContext; -import javax.naming.directory.SearchControls; -import javax.naming.directory.SearchResult; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.dao.IncorrectResultSizeDataAccessException; @@ -42,6 +28,18 @@ import org.springframework.ldap.core.LdapEncoder; import org.springframework.ldap.core.LdapTemplate; import org.springframework.util.Assert; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.PartialResultException; +import javax.naming.directory.Attributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.SearchControls; +import javax.naming.directory.SearchResult; +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + /** * Extension of Spring LDAP's LdapTemplate class which adds extra functionality required by Spring Security. @@ -55,6 +53,7 @@ public class SpringSecurityLdapTemplate extends LdapTemplate { private static final Log logger = LogFactory.getLog(SpringSecurityLdapTemplate.class); public static final String[] NO_ATTRS = new String[0]; + private static final boolean RETURN_OBJECT = true; //~ Instance fields ================================================================================================ @@ -207,7 +206,7 @@ public class SpringSecurityLdapTemplate extends LdapTemplate { String base, String filter, Object[] params) throws NamingException { final DistinguishedName ctxBaseDn = new DistinguishedName(ctx.getNameInNamespace()); final DistinguishedName searchBaseDn = new DistinguishedName(base); - final NamingEnumeration resultsEnum = ctx.search(searchBaseDn, filter, params, searchControls); + final NamingEnumeration resultsEnum = ctx.search(searchBaseDn, filter, params, buildControls(searchControls)); if (logger.isDebugEnabled()) { logger.debug("Searching for entry under DN '" + ctxBaseDn @@ -218,17 +217,13 @@ public class SpringSecurityLdapTemplate extends LdapTemplate { 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); - } + DirContextAdapter dca = (DirContextAdapter) searchResult.getObject(); + Assert.notNull(dca, "No object returned by search, DirContext is not correctly configured"); if (logger.isDebugEnabled()) { - logger.debug("Found DN: " + dn); + logger.debug("Found DN: " + dca.getDn()); } - results.add(new DirContextAdapter(searchResult.getAttributes(), dn, ctxBaseDn)); + results.add(dca); } } catch (PartialResultException e) { LdapUtils.closeEnumeration(resultsEnum); @@ -246,6 +241,21 @@ public class SpringSecurityLdapTemplate extends LdapTemplate { return results.iterator().next(); } + /** + * We need to make sure the search controls has the return object flag set to true, in order for + * the search to return DirContextAdapter instances. + * @param originalControls + * @return + */ + private static SearchControls buildControls(SearchControls originalControls) { + return new SearchControls(originalControls.getSearchScope(), + originalControls.getCountLimit(), + originalControls.getTimeLimit(), + originalControls.getReturningAttributes(), + RETURN_OBJECT, + originalControls.getDerefLinkFlag()); + } + /** * Sets the search controls which will be used for search operations by the template. * diff --git a/ldap/src/main/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProvider.java b/ldap/src/main/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProvider.java index 0c1c6dc12f..43bb830527 100644 --- a/ldap/src/main/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProvider.java +++ b/ldap/src/main/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProvider.java @@ -15,6 +15,7 @@ package org.springframework.security.ldap.authentication.ad; import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.ldap.core.DirContextOperations; import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.core.support.DefaultDirObjectFactory; import org.springframework.ldap.support.LdapUtils; import org.springframework.security.authentication.AccountExpiredException; import org.springframework.security.authentication.BadCredentialsException; @@ -174,6 +175,7 @@ public final class ActiveDirectoryLdapAuthenticationProvider extends AbstractLda env.put(Context.PROVIDER_URL, bindUrl); env.put(Context.SECURITY_CREDENTIALS, password); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); + env.put(Context.OBJECT_FACTORIES, DefaultDirObjectFactory.class.getName()); try { return contextFactory.createContext(env); diff --git a/ldap/src/test/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProviderTests.java b/ldap/src/test/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProviderTests.java index 99779439fc..d3c3137814 100644 --- a/ldap/src/test/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProviderTests.java +++ b/ldap/src/test/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProviderTests.java @@ -12,16 +12,14 @@ */ package org.springframework.security.ldap.authentication.ad; -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; -import static org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider.ContextFactory; - import org.apache.directory.shared.ldap.util.EmptyEnumeration; import org.hamcrest.BaseMatcher; import org.hamcrest.CoreMatchers; import org.hamcrest.Description; import org.hamcrest.Matcher; -import org.junit.*; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; import org.junit.rules.ExpectedException; import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.ldap.core.DirContextAdapter; @@ -33,8 +31,6 @@ import org.springframework.security.authentication.DisabledException; import org.springframework.security.authentication.LockedException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.userdetails.UsernameNotFoundException; import javax.naming.AuthenticationException; import javax.naming.CommunicationException; @@ -45,7 +41,15 @@ import javax.naming.NamingException; import javax.naming.directory.DirContext; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; -import java.util.*; +import java.util.Hashtable; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider.ContextFactory; /** * @author Luke Taylor @@ -75,7 +79,7 @@ public class ActiveDirectoryLdapAuthenticationProviderTests { when(ctx.getNameInNamespace()).thenReturn(""); DirContextAdapter dca = new DirContextAdapter(); - SearchResult sr = new SearchResult("CN=Joe Jannsen,CN=Users", null, dca.getAttributes()); + SearchResult sr = new SearchResult("CN=Joe Jannsen,CN=Users", dca, dca.getAttributes()); when(ctx.search(any(Name.class), any(String.class), any(Object[].class), any(SearchControls.class))) .thenReturn(new MockNamingEnumeration(sr)) .thenReturn(new MockNamingEnumeration(sr)); @@ -88,8 +92,6 @@ public class ActiveDirectoryLdapAuthenticationProviderTests { dca.addAttributeValue("memberOf","CN=Admin,CN=Users,DC=mydomain,DC=eu"); - sr.setAttributes(dca.getAttributes()); - result = provider.authenticate(joe); assertEquals(1, result.getAuthorities().size()); @@ -102,7 +104,7 @@ public class ActiveDirectoryLdapAuthenticationProviderTests { when(ctx.getNameInNamespace()).thenReturn(""); DirContextAdapter dca = new DirContextAdapter(); - SearchResult sr = new SearchResult("CN=Joe Jannsen,CN=Users", null, dca.getAttributes()); + SearchResult sr = new SearchResult("CN=Joe Jannsen,CN=Users", dca, dca.getAttributes()); when(ctx.search(eq(new DistinguishedName("DC=mydomain,DC=eu")), any(String.class), any(Object[].class), any(SearchControls.class))) .thenReturn(new MockNamingEnumeration(sr)); provider.contextFactory = createContextFactoryReturning(ctx); @@ -149,7 +151,7 @@ public class ActiveDirectoryLdapAuthenticationProviderTests { NamingEnumeration searchResults = mock(NamingEnumeration.class); when(searchResults.hasMore()).thenReturn(true,true,false); SearchResult searchResult = mock(SearchResult.class); - when(searchResult.getName()).thenReturn("ou=1","ou=2"); + when(searchResult.getObject()).thenReturn(new DirContextAdapter("ou=1"),new DirContextAdapter("ou=2")); when(searchResults.next()).thenReturn(searchResult); when(ctx.search(any(Name.class), any(String.class), any(Object[].class), any(SearchControls.class))) .thenReturn(searchResults );