浏览代码

SEC-2017: Convert IncorrectResultsSizeException.size() == 0 to BadCredentialsException in ActiveDirectoryAuthenticationProvider

Rob Winch 13 年之前
父节点
当前提交
e6593151fc

+ 14 - 1
ldap/src/main/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProvider.java

@@ -12,6 +12,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.support.LdapUtils;
@@ -24,6 +25,7 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.authority.AuthorityUtils;
 import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
 import org.springframework.security.ldap.SpringSecurityLdapTemplate;
 import org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider;
 import org.springframework.util.Assert;
@@ -266,6 +268,7 @@ public final class ActiveDirectoryLdapAuthenticationProvider extends AbstractLda
         return (BadCredentialsException) badCredentials().initCause(cause);
     }
 
+    @SuppressWarnings("deprecation")
     private DirContextOperations searchForUser(DirContext ctx, String username) throws NamingException {
         SearchControls searchCtls = new SearchControls();
         searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
@@ -276,8 +279,18 @@ public final class ActiveDirectoryLdapAuthenticationProvider extends AbstractLda
 
         String searchRoot = rootDn != null ? rootDn : searchRootFromPrincipal(bindPrincipal);
 
-        return SpringSecurityLdapTemplate.searchForSingleEntryInternal(ctx, searchCtls, searchRoot, searchFilter,
+        try {
+            return SpringSecurityLdapTemplate.searchForSingleEntryInternal(ctx, searchCtls, searchRoot, searchFilter,
                 new Object[]{bindPrincipal});
+        } catch (IncorrectResultSizeDataAccessException incorrectResults) {
+            if (incorrectResults.getActualSize() == 0) {
+                UsernameNotFoundException userNameNotFoundException = new UsernameNotFoundException("User " + username + " not found in directory.", username);
+                userNameNotFoundException.initCause(incorrectResults);
+                throw badCredentials(userNameNotFoundException);
+            }
+            // Search should never return multiple results if properly configured, so just rethrow
+            throw incorrectResults;
+        }
     }
 
     private String searchRootFromPrincipal(String bindPrincipal) {

+ 34 - 0
ldap/src/test/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProviderTests.java

@@ -16,12 +16,14 @@ 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.rules.ExpectedException;
+import org.springframework.dao.IncorrectResultSizeDataAccessException;
 import org.springframework.ldap.core.DirContextAdapter;
 import org.springframework.ldap.core.DistinguishedName;
 import org.springframework.security.authentication.AccountExpiredException;
@@ -32,6 +34,7 @@ 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;
@@ -125,6 +128,37 @@ public class ActiveDirectoryLdapAuthenticationProviderTests {
         provider.authenticate(joe);
     }
 
+    // SEC-2017
+    @Test(expected = BadCredentialsException.class)
+    public void noUserSearchCausesUsernameNotFound() throws Exception {
+        DirContext ctx = mock(DirContext.class);
+        when(ctx.getNameInNamespace()).thenReturn("");
+        when(ctx.search(any(Name.class), any(String.class), any(Object[].class), any(SearchControls.class)))
+                .thenReturn(new EmptyEnumeration<SearchResult>());
+
+        provider.contextFactory = createContextFactoryReturning(ctx);
+
+        provider.authenticate(joe);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test(expected = IncorrectResultSizeDataAccessException.class)
+    public void duplicateUserSearchCausesError() throws Exception {
+        DirContext ctx = mock(DirContext.class);
+        when(ctx.getNameInNamespace()).thenReturn("");
+        NamingEnumeration<SearchResult> 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(searchResults.next()).thenReturn(searchResult);
+        when(ctx.search(any(Name.class), any(String.class), any(Object[].class), any(SearchControls.class)))
+                .thenReturn(searchResults );
+
+        provider.contextFactory = createContextFactoryReturning(ctx);
+
+        provider.authenticate(joe);
+    }
+
     static final String msg = "[LDAP: error code 49 - 80858585: LdapErr: DSID-DECAFF0, comment: AcceptSecurityContext error, data ";
 
     @Test(expected = BadCredentialsException.class)