SEC-1124: Refactored LDAP code into separate module

This commit is contained in:
Luke Taylor
2009-03-19 06:30:32 +00:00
parent 69b86fd045
commit 4aae5ec42e
71 changed files with 117 additions and 235 deletions

View File

@@ -0,0 +1,112 @@
package org.springframework.security;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.junit.After;
import org.junit.Test;
import org.springframework.security.Authentication;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.SecurityConfigurationException;
import org.springframework.security.providers.ProviderManager;
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
import org.springframework.security.providers.ldap.LdapAuthenticationProvider;
import org.springframework.security.userdetails.ldap.InetOrgPersonContextMapper;
import org.springframework.security.userdetails.ldap.LdapUserDetailsImpl;
import org.springframework.security.util.FieldUtils;
import org.springframework.security.util.InMemoryXmlApplicationContext;
/**
* @author Luke Taylor
* @version $Id$
*/
public class LdapProviderBeanDefinitionParserTests {
InMemoryXmlApplicationContext appCtx;
@After
public void closeAppContext() {
if (appCtx != null) {
appCtx.close();
appCtx = null;
}
}
@Test
public void simpleProviderAuthenticatesCorrectly() {
setContext("<ldap-server /> <ldap-authentication-provider group-search-filter='member={0}' />");
LdapAuthenticationProvider provider = getProvider();
Authentication auth = provider.authenticate(new UsernamePasswordAuthenticationToken("ben", "benspassword"));
LdapUserDetailsImpl ben = (LdapUserDetailsImpl) auth.getPrincipal();
assertEquals(3, ben.getAuthorities().size());
}
@Test(expected = SecurityConfigurationException.class)
public void missingServerEltCausesConfigException() {
setContext("<ldap-authentication-provider />");
}
@Test
public void supportsPasswordComparisonAuthentication() {
setContext("<ldap-server /> " +
"<ldap-authentication-provider user-dn-pattern='uid={0},ou=people'>" +
" <password-compare />" +
"</ldap-authentication-provider>");
LdapAuthenticationProvider provider = getProvider();
provider.authenticate(new UsernamePasswordAuthenticationToken("ben", "benspassword"));
}
@Test
public void supportsPasswordComparisonAuthenticationWithHashAttribute() {
setContext("<ldap-server /> " +
"<ldap-authentication-provider user-dn-pattern='uid={0},ou=people'>" +
" <password-compare password-attribute='uid' hash='plaintext'/>" +
"</ldap-authentication-provider>");
LdapAuthenticationProvider provider = getProvider();
provider.authenticate(new UsernamePasswordAuthenticationToken("ben", "ben"));
}
@Test
public void supportsPasswordComparisonAuthenticationWithPasswordEncoder() {
setContext("<ldap-server /> " +
"<ldap-authentication-provider user-dn-pattern='uid={0},ou=people'>" +
" <password-compare password-attribute='uid'>" +
" <password-encoder hash='plaintext'/>" +
" </password-compare>" +
"</ldap-authentication-provider>");
LdapAuthenticationProvider provider = getProvider();
provider.authenticate(new UsernamePasswordAuthenticationToken("ben", "ben"));
}
@Test
public void detectsNonStandardServerId() {
setContext("<ldap-server id='myServer'/> " +
"<ldap-authentication-provider />");
}
@Test
public void inetOrgContextMapperIsSupported() throws Exception {
setContext(
"<ldap-server id='someServer' url='ldap://127.0.0.1:343/dc=springframework,dc=org'/>" +
"<ldap-authentication-provider user-details-class='inetOrgPerson'/>");
LdapAuthenticationProvider provider = getProvider();
assertTrue(FieldUtils.getFieldValue(provider, "userDetailsContextMapper") instanceof InetOrgPersonContextMapper);
}
private void setContext(String context) {
appCtx = new InMemoryXmlApplicationContext(context);
}
private LdapAuthenticationProvider getProvider() {
ProviderManager authManager = (ProviderManager) appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER);
assertEquals(1, authManager.getProviders().size());
LdapAuthenticationProvider provider = (LdapAuthenticationProvider) authManager.getProviders().get(0);
return provider;
}
}

View File

@@ -0,0 +1,64 @@
package org.springframework.security;
import org.junit.After;
import org.junit.Test;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.security.config.BeanIds;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.util.InMemoryXmlApplicationContext;
/**
* @author Luke Taylor
* @version $Id$
*/
public class LdapServerBeanDefinitionParserTests {
InMemoryXmlApplicationContext appCtx;
@After
public void closeAppContext() {
if (appCtx != null) {
appCtx.close();
appCtx = null;
}
}
@Test
public void embeddedServerCreationContainsExpectedContextSourceAndData() {
appCtx = new InMemoryXmlApplicationContext("<ldap-server />");
DefaultSpringSecurityContextSource contextSource = (DefaultSpringSecurityContextSource) appCtx.getBean(BeanIds.CONTEXT_SOURCE);
// Check data is loaded
LdapTemplate template = new LdapTemplate(contextSource);
template.lookup("uid=ben,ou=people");
}
@Test
public void useOfUrlAttributeCreatesCorrectContextSource() {
// Create second "server" with a url pointing at embedded one
appCtx = new InMemoryXmlApplicationContext("<ldap-server port='33388'/>" +
"<ldap-server id='blah' url='ldap://127.0.0.1:33388/dc=springframework,dc=org' />");
// Check the default context source is still there.
appCtx.getBean(BeanIds.CONTEXT_SOURCE);
DefaultSpringSecurityContextSource contextSource = (DefaultSpringSecurityContextSource) appCtx.getBean("blah");
// Check data is loaded as before
LdapTemplate template = new LdapTemplate(contextSource);
template.lookup("uid=ben,ou=people");
}
@Test
public void loadingSpecificLdifFileIsSuccessful() {
appCtx = new InMemoryXmlApplicationContext(
"<ldap-server ldif='classpath*:test-server2.xldif' root='dc=monkeymachine,dc=co,dc=uk' />");
DefaultSpringSecurityContextSource contextSource = (DefaultSpringSecurityContextSource) appCtx.getBean(BeanIds.CONTEXT_SOURCE);
LdapTemplate template = new LdapTemplate(contextSource);
template.lookup("uid=pg,ou=gorillas");
}
}

View File

@@ -0,0 +1,129 @@
package org.springframework.security;
import java.util.Set;
import org.springframework.security.GrantedAuthorityImpl;
import org.springframework.security.util.AuthorityUtils;
import org.springframework.security.util.InMemoryXmlApplicationContext;
import org.springframework.security.userdetails.UserDetailsService;
import org.springframework.security.userdetails.UserDetails;
import org.springframework.security.userdetails.ldap.InetOrgPerson;
import org.springframework.security.userdetails.ldap.Person;
import org.junit.Test;
import org.junit.After;
import static org.junit.Assert.*;
/**
* @author Luke Taylor
* @version $Id$
*/
public class LdapUserServiceBeanDefinitionParserTests {
private InMemoryXmlApplicationContext appCtx;
@After
public void closeAppContext() {
if (appCtx != null) {
appCtx.close();
appCtx = null;
}
}
@Test
public void minimalConfigurationIsParsedOk() throws Exception {
setContext("<ldap-user-service user-search-filter='(uid={0})' /><ldap-server url='ldap://127.0.0.1:343/dc=springframework,dc=org' />");
}
@Test
public void userServiceReturnsExpectedData() throws Exception {
setContext("<ldap-user-service id='ldapUDS' user-search-filter='(uid={0})' group-search-filter='member={0}' /><ldap-server />");
UserDetailsService uds = (UserDetailsService) appCtx.getBean("ldapUDS");
UserDetails ben = uds.loadUserByUsername("ben");
Set<String> authorities = AuthorityUtils.authorityListToSet(ben.getAuthorities());
assertEquals(3, authorities.size());
assertTrue(authorities.contains("ROLE_DEVELOPERS"));
}
@Test
public void differentUserSearchBaseWorksAsExpected() throws Exception {
setContext("<ldap-user-service id='ldapUDS' " +
" user-search-base='ou=otherpeople' " +
" user-search-filter='(cn={0})' " +
" group-search-filter='member={0}' /><ldap-server />");
UserDetailsService uds = (UserDetailsService) appCtx.getBean("ldapUDS");
UserDetails joe = uds.loadUserByUsername("Joe Smeth");
assertEquals("Joe Smeth", joe.getUsername());
}
@Test
public void rolePrefixIsSupported() throws Exception {
setContext(
"<ldap-user-service id='ldapUDS' " +
" user-search-filter='(uid={0})' " +
" group-search-filter='member={0}' role-prefix='PREFIX_'/>" +
"<ldap-user-service id='ldapUDSNoPrefix' " +
" user-search-filter='(uid={0})' " +
" group-search-filter='member={0}' role-prefix='none'/><ldap-server />");
UserDetailsService uds = (UserDetailsService) appCtx.getBean("ldapUDS");
UserDetails ben = uds.loadUserByUsername("ben");
assertTrue(AuthorityUtils.authorityListToSet(ben.getAuthorities()).contains("PREFIX_DEVELOPERS"));
uds = (UserDetailsService) appCtx.getBean("ldapUDSNoPrefix");
ben = uds.loadUserByUsername("ben");
assertTrue(AuthorityUtils.authorityListToSet(ben.getAuthorities()).contains("DEVELOPERS"));
}
@Test
public void differentGroupRoleAttributeWorksAsExpected() throws Exception {
setContext("<ldap-user-service id='ldapUDS' user-search-filter='(uid={0})' group-role-attribute='ou' group-search-filter='member={0}' /><ldap-server />");
UserDetailsService uds = (UserDetailsService) appCtx.getBean("ldapUDS");
UserDetails ben = uds.loadUserByUsername("ben");
Set<String> authorities = AuthorityUtils.authorityListToSet(ben.getAuthorities());
assertEquals(3, authorities.size());
assertTrue(authorities.contains(new GrantedAuthorityImpl("ROLE_DEVELOPER")));
}
@Test
public void isSupportedByAuthenticationProviderElement() {
setContext(
"<ldap-server url='ldap://127.0.0.1:343/dc=springframework,dc=org'/>" +
"<authentication-provider>" +
" <ldap-user-service user-search-filter='(uid={0})' />" +
"</authentication-provider>");
}
@Test
public void personContextMapperIsSupported() {
setContext(
"<ldap-server />" +
"<ldap-user-service id='ldapUDS' user-search-filter='(uid={0})' user-details-class='person'/>");
UserDetailsService uds = (UserDetailsService) appCtx.getBean("ldapUDS");
UserDetails ben = uds.loadUserByUsername("ben");
assertTrue(ben instanceof Person);
}
@Test
public void inetOrgContextMapperIsSupported() {
setContext(
"<ldap-server id='someServer'/>" +
"<ldap-user-service id='ldapUDS' user-search-filter='(uid={0})' user-details-class='inetOrgPerson'/>");
UserDetailsService uds = (UserDetailsService) appCtx.getBean("ldapUDS");
UserDetails ben = uds.loadUserByUsername("ben");
assertTrue(ben instanceof InetOrgPerson);
}
private void setContext(String context) {
appCtx = new InMemoryXmlApplicationContext(context);
}
}

View File

@@ -0,0 +1,135 @@
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* 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
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.ldap;
import javax.naming.Binding;
import javax.naming.ContextNotEmptyException;
import javax.naming.Name;
import javax.naming.NameNotFoundException;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import org.apache.directory.server.core.DirectoryService;
import org.apache.directory.server.protocol.shared.store.LdifFileLoader;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.springframework.core.io.ClassPathResource;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
import org.springframework.security.config.BeanIds;
import org.springframework.security.util.InMemoryXmlApplicationContext;
/**
* Based on class borrowed from Spring Ldap project.
*
* @author Luke Taylor
* @version $Id$
*/
public abstract class AbstractLdapIntegrationTests {
private static InMemoryXmlApplicationContext appContext;
protected AbstractLdapIntegrationTests() {
}
@BeforeClass
public static void loadContext() throws NamingException {
shutdownRunningServers();
appContext = new InMemoryXmlApplicationContext("<ldap-server port='53389' ldif='classpath:test-server.ldif'/>");
}
@AfterClass
public static void closeContext() throws Exception {
if(appContext != null) {
appContext.close();
}
shutdownRunningServers();
}
private static void shutdownRunningServers() throws NamingException {
DirectoryService ds = DirectoryService.getInstance();
if (ds.isStarted()) {
System.out.println("WARNING: Discovered running DirectoryService with configuration: " + ds.getConfiguration().getStartupConfiguration().toString());
System.out.println("Shutting it down...");
ds.shutdown();
}
}
@Before
public void onSetUp() throws Exception {
}
@After
public final void reloadServerDataIfDirty() throws Exception {
ClassPathResource ldifs = new ClassPathResource("test-server.ldif");
if (!ldifs.getFile().exists()) {
throw new IllegalStateException("Ldif file not found: " + ldifs.getFile().getAbsolutePath());
}
DirContext ctx = getContextSource().getReadWriteContext();
// First of all, make sure the database is empty.
Name startingPoint = new DistinguishedName("dc=springframework,dc=org");
try {
clearSubContexts(ctx, startingPoint);
LdifFileLoader loader = new LdifFileLoader(ctx, ldifs.getFile().getAbsolutePath());
loader.execute();
} finally {
ctx.close();
}
}
public BaseLdapPathContextSource getContextSource() {
return (BaseLdapPathContextSource)appContext.getBean(BeanIds.CONTEXT_SOURCE);
}
private void clearSubContexts(DirContext ctx, Name name) throws NamingException {
NamingEnumeration<Binding> enumeration = null;
try {
enumeration = ctx.listBindings(name);
while (enumeration.hasMore()) {
Binding element = (Binding) enumeration.next();
DistinguishedName childName = new DistinguishedName(element.getName());
childName.prepend((DistinguishedName) name);
try {
ctx.destroySubcontext(childName);
} catch (ContextNotEmptyException e) {
clearSubContexts(ctx, childName);
ctx.destroySubcontext(childName);
}
}
} catch(NameNotFoundException ignored) {
}
catch (NamingException e) {
e.printStackTrace();
} finally {
try {
enumeration.close();
} catch (Exception ignored) {
}
}
}
}

View File

@@ -0,0 +1,60 @@
package org.springframework.security.ldap;
import static org.junit.Assert.*;
import java.util.Hashtable;
import org.junit.Test;
import org.springframework.ldap.core.support.AbstractContextSource;
/**
* @author Luke Taylor
* @version $Id$
*/
public class DefaultSpringSecurityContextSourceTests {
@Test
public void instantiationSucceedsWithExpectedProperties() {
DefaultSpringSecurityContextSource ctxSrc =
new DefaultSpringSecurityContextSource("ldap://blah:789/dc=springframework,dc=org");
assertFalse(ctxSrc.isAnonymousReadOnly());
assertTrue(ctxSrc.isPooled());
}
@Test
public void supportsSpacesInUrl() {
new DefaultSpringSecurityContextSource("ldap://myhost:10389/dc=spring%20framework,dc=org");
}
@Test
public void poolingFlagIsSetWhenAuthenticationDnMatchesManagerUserDn() throws Exception {
EnvExposingDefaultSpringSecurityContextSource ctxSrc =
new EnvExposingDefaultSpringSecurityContextSource("ldap://blah:789/dc=springframework,dc=org");
ctxSrc.setUserDn("manager");
ctxSrc.setPassword("password");
ctxSrc.afterPropertiesSet();
assertTrue(ctxSrc.getAuthenticatedEnvForTest("manager", "password").containsKey(AbstractContextSource.SUN_LDAP_POOLING_FLAG));
}
@Test
public void poolingFlagIsNotSetWhenAuthenticationDnIsNotManagerUserDn() throws Exception {
EnvExposingDefaultSpringSecurityContextSource ctxSrc =
new EnvExposingDefaultSpringSecurityContextSource("ldap://blah:789/dc=springframework,dc=org");
ctxSrc.setUserDn("manager");
ctxSrc.setPassword("password");
ctxSrc.afterPropertiesSet();
assertFalse(ctxSrc.getAuthenticatedEnvForTest("user", "password").containsKey(AbstractContextSource.SUN_LDAP_POOLING_FLAG));
}
static class EnvExposingDefaultSpringSecurityContextSource extends DefaultSpringSecurityContextSource {
public EnvExposingDefaultSpringSecurityContextSource(String providerUrl) {
super(providerUrl);
}
@SuppressWarnings("unchecked")
Hashtable getAuthenticatedEnvForTest(String userDn, String password) {
return getAuthenticatedEnv(userDn, password);
}
}
}

View File

@@ -0,0 +1,105 @@
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* 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
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.ldap;
import static org.junit.Assert.assertEquals;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.integration.junit4.JMock;
import org.jmock.integration.junit4.JUnit4Mockery;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Tests {@link LdapUtils}
*
* @author Luke Taylor
* @version $Id$
*/
@RunWith(JMock.class)
public class LdapUtilsTests {
Mockery context = new JUnit4Mockery();
//~ Methods ========================================================================================================
@Test
public void testCloseContextSwallowsNamingException() throws Exception {
final DirContext dirCtx = context.mock(DirContext.class);
context.checking(new Expectations() {{
oneOf(dirCtx).close(); will(throwException(new NamingException()));
}});
LdapUtils.closeContext(dirCtx);
}
@Test
public void testGetRelativeNameReturnsEmptyStringForDnEqualToBaseName() throws Exception {
final DirContext mockCtx = context.mock(DirContext.class);
context.checking(new Expectations() {{
atLeast(1).of(mockCtx).getNameInNamespace(); will(returnValue("dc=springframework,dc=org"));
}});
assertEquals("", LdapUtils.getRelativeName("dc=springframework,dc=org", mockCtx));
}
@Test
public void testGetRelativeNameReturnsFullDnWithEmptyBaseName() throws Exception {
final DirContext mockCtx = context.mock(DirContext.class);
context.checking(new Expectations() {{
atLeast(1).of(mockCtx).getNameInNamespace(); will(returnValue(""));
}});
assertEquals("cn=jane,dc=springframework,dc=org",
LdapUtils.getRelativeName("cn=jane,dc=springframework,dc=org", mockCtx));
}
@Test
public void testGetRelativeNameWorksWithArbitrarySpaces() throws Exception {
final DirContext mockCtx = context.mock(DirContext.class);
context.checking(new Expectations() {{
atLeast(1).of(mockCtx).getNameInNamespace(); will(returnValue("dc=springsecurity,dc = org"));
}});
assertEquals("cn=jane smith",
LdapUtils.getRelativeName("cn=jane smith, dc = springsecurity , dc=org", mockCtx));
}
@Test
public void testRootDnsAreParsedFromUrlsCorrectly() {
assertEquals("", LdapUtils.parseRootDnFromUrl("ldap://monkeymachine"));
assertEquals("", LdapUtils.parseRootDnFromUrl("ldap://monkeymachine:11389"));
assertEquals("", LdapUtils.parseRootDnFromUrl("ldap://monkeymachine/"));
assertEquals("", LdapUtils.parseRootDnFromUrl("ldap://monkeymachine.co.uk/"));
assertEquals("dc=springframework,dc=org",
LdapUtils.parseRootDnFromUrl("ldaps://monkeymachine.co.uk/dc=springframework,dc=org"));
assertEquals("dc=springframework,dc=org", LdapUtils.parseRootDnFromUrl("ldap:///dc=springframework,dc=org"));
assertEquals("dc=springframework,dc=org",
LdapUtils.parseRootDnFromUrl("ldap://monkeymachine/dc=springframework,dc=org"));
assertEquals("dc=springframework,dc=org/ou=blah",
LdapUtils.parseRootDnFromUrl("ldap://monkeymachine.co.uk/dc=springframework,dc=org/ou=blah"));
assertEquals("dc=springframework,dc=org/ou=blah",
LdapUtils.parseRootDnFromUrl("ldap://monkeymachine.co.uk:389/dc=springframework,dc=org/ou=blah"));
}
}

View File

@@ -0,0 +1,70 @@
package org.springframework.security.ldap;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.providers.TestingAuthenticationToken;
import org.springframework.security.providers.anonymous.AnonymousAuthenticationToken;
import org.springframework.security.userdetails.ldap.LdapUserDetailsImpl;
import org.springframework.security.util.AuthorityUtils;
import org.springframework.ldap.core.AuthenticationSource;
import org.springframework.ldap.core.DistinguishedName;
import org.junit.After;
import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.Test;
/**
* @author Luke Taylor
* @version $Id$
*/
public class SpringSecurityAuthenticationSourceTests {
@Before
@After
public void clearContext() {
SecurityContextHolder.clearContext();
}
@Test
public void principalAndCredentialsAreEmptyWithNoAuthentication() {
AuthenticationSource source = new SpringSecurityAuthenticationSource();
assertEquals("", source.getPrincipal());
assertEquals("", source.getCredentials());
}
@Test
public void principalIsEmptyForAnonymousUser() {
AuthenticationSource source = new SpringSecurityAuthenticationSource();
SecurityContextHolder.getContext().setAuthentication(
new AnonymousAuthenticationToken("key", "anonUser", AuthorityUtils.createAuthorityList("ignored")));
assertEquals("", source.getPrincipal());
}
@Test(expected=IllegalArgumentException.class)
public void getPrincipalRejectsNonLdapUserDetailsObject() {
AuthenticationSource source = new SpringSecurityAuthenticationSource();
SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken(new Object(), "password"));
source.getPrincipal();
}
@Test
public void expectedCredentialsAreReturned() {
AuthenticationSource source = new SpringSecurityAuthenticationSource();
SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken(new Object(), "password"));
assertEquals("password", source.getCredentials());
}
@Test
public void expectedPrincipalIsReturned() {
LdapUserDetailsImpl.Essence user = new LdapUserDetailsImpl.Essence();
user.setUsername("joe");
user.setDn(new DistinguishedName("uid=joe,ou=users"));
AuthenticationSource source = new SpringSecurityAuthenticationSource();
SecurityContextHolder.getContext().setAuthentication(
new TestingAuthenticationToken(user.createUserDetails(), null));
assertEquals("uid=joe,ou=users", source.getPrincipal());
}
}

View File

@@ -0,0 +1,151 @@
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* 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
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.ldap;
import static org.junit.Assert.*;
import java.util.Set;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import org.junit.Test;
import org.springframework.ldap.UncategorizedLdapException;
import org.springframework.ldap.core.ContextExecutor;
/**
* @author Luke Taylor
* @version $Id$
*/
public class SpringSecurityLdapTemplateTests extends AbstractLdapIntegrationTests {
//~ Instance fields ================================================================================================
private SpringSecurityLdapTemplate template;
//~ Methods ========================================================================================================
public void onSetUp() throws Exception {
super.onSetUp();
template = new SpringSecurityLdapTemplate(getContextSource());
}
@Test
public void compareOfCorrectValueSucceeds() {
assertTrue(template.compare("uid=bob,ou=people", "uid", "bob"));
}
@Test
public void compareOfCorrectByteValueSucceeds() {
assertTrue(template.compare("uid=bob,ou=people", "userPassword", LdapUtils.getUtf8Bytes("bobspassword")));
}
@Test
public void compareOfWrongByteValueFails() {
assertFalse(template.compare("uid=bob,ou=people", "userPassword", LdapUtils.getUtf8Bytes("wrongvalue")));
}
@Test
public void compareOfWrongValueFails() {
assertFalse(template.compare("uid=bob,ou=people", "uid", "wrongvalue"));
}
// @Test
// public void testNameExistsForInValidNameFails() {
// assertFalse(template.nameExists("ou=doesntexist,dc=springframework,dc=org"));
// }
//
// @Test
// public void testNameExistsForValidNameSucceeds() {
// assertTrue(template.nameExists("ou=groups,dc=springframework,dc=org"));
// }
@Test
public void namingExceptionIsTranslatedCorrectly() {
try {
template.executeReadOnly(new ContextExecutor() {
public Object executeWithContext(DirContext dirContext) throws NamingException {
throw new NamingException();
}
});
fail("Expected UncategorizedLdapException on NamingException");
} catch (UncategorizedLdapException expected) {}
}
@Test
public void roleSearchReturnsCorrectNumberOfRoles() {
String param = "uid=ben,ou=people,dc=springframework,dc=org";
Set<String> values = template.searchForSingleAttributeValues("ou=groups", "(member={0})", new String[] {param}, "ou");
assertEquals("Expected 3 results from search", 3, values.size());
assertTrue(values.contains("developer"));
assertTrue(values.contains("manager"));
assertTrue(values.contains("submanager"));
}
@Test
public void testRoleSearchForMissingAttributeFailsGracefully() {
String param = "uid=ben,ou=people,dc=springframework,dc=org";
Set<String> values = template.searchForSingleAttributeValues("ou=groups", "(member={0})", new String[] {param}, "mail");
assertEquals(0, values.size());
}
@Test
public void roleSearchWithEscapedCharacterSucceeds() throws Exception {
String param = "cn=mouse\\, jerry,ou=people,dc=springframework,dc=org";
Set<String> values = template.searchForSingleAttributeValues("ou=groups", "(member={0})", new String[] {param}, "cn");
assertEquals(1, values.size());
}
@Test
public void nonSpringLdapSearchCodeTestMethod() throws Exception {
java.util.Hashtable<String, String> env = new java.util.Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:53389");
env.put(Context.SECURITY_PRINCIPAL, "");
env.put(Context.SECURITY_CREDENTIALS, "");
DirContext ctx = new javax.naming.directory.InitialDirContext(env);
SearchControls controls = new SearchControls();
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
controls.setReturningObjFlag(true);
controls.setReturningAttributes(null);
String param = "cn=mouse\\, jerry,ou=people,dc=springframework,dc=org";
javax.naming.NamingEnumeration<SearchResult> results =
ctx.search("ou=groups,dc=springframework,dc=org",
"(member={0})", new String[] {param},
controls);
assertTrue("Expected a result", results.hasMore());
}
@Test
public void searchForSingleEntryWithEscapedCharsInDnSucceeds() {
String param = "mouse, jerry";
template.searchForSingleEntry("ou=people", "(cn={0})", new String[] {param});
}
}

View File

@@ -0,0 +1,148 @@
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* 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
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.ldap.populator;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.ldap.AbstractLdapIntegrationTests;
import org.springframework.security.ldap.populator.DefaultLdapAuthoritiesPopulator;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DistinguishedName;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static org.junit.Assert.*;
import org.junit.Test;
/**
*
* @author Luke Taylor
* @version $Id$
*/
public class DefaultLdapAuthoritiesPopulatorTests extends AbstractLdapIntegrationTests {
private DefaultLdapAuthoritiesPopulator populator;
//~ Methods ========================================================================================================
public void onSetUp() throws Exception {
super.onSetUp();
populator = new DefaultLdapAuthoritiesPopulator(getContextSource(), "ou=groups");
}
@Test
public void testDefaultRoleIsAssignedWhenSet() {
populator.setDefaultRole("ROLE_USER");
DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName("cn=notfound"));
List<GrantedAuthority> authorities = populator.getGrantedAuthorities(ctx, "notfound");
assertEquals(1, authorities.size());
assertEquals("ROLE_USER", authorities.get(0).getAuthority());
}
@Test
public void testGroupSearchReturnsExpectedRoles() {
populator.setRolePrefix("ROLE_");
populator.setGroupRoleAttribute("ou");
populator.setSearchSubtree(true);
populator.setSearchSubtree(false);
populator.setConvertToUpperCase(true);
populator.setGroupSearchFilter("(member={0})");
DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName("uid=ben,ou=people,dc=springframework,dc=org"));
List<GrantedAuthority> authorities = populator.getGrantedAuthorities(ctx, "ben");
assertEquals("Should have 2 roles", 2, authorities.size());
Set<String> roles = new HashSet<String>();
roles.add(authorities.get(0).toString());
roles.add(authorities.get(1).toString());
assertTrue(roles.contains("ROLE_DEVELOPER"));
assertTrue(roles.contains("ROLE_MANAGER"));
}
@Test
public void testUseOfUsernameParameterReturnsExpectedRoles() {
populator.setGroupRoleAttribute("ou");
populator.setConvertToUpperCase(true);
populator.setGroupSearchFilter("(ou={1})");
DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName("uid=ben,ou=people,dc=springframework,dc=org"));
List<GrantedAuthority> authorities = populator.getGrantedAuthorities(ctx, "manager");
assertEquals("Should have 1 role", 1, authorities.size());
assertEquals("ROLE_MANAGER", authorities.get(0).getAuthority());
}
@Test
public void testSubGroupRolesAreNotFoundByDefault() {
populator.setGroupRoleAttribute("ou");
populator.setConvertToUpperCase(true);
DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName("uid=ben,ou=people,dc=springframework,dc=org"));
List<GrantedAuthority> authorities = populator.getGrantedAuthorities(ctx, "manager");
assertEquals("Should have 2 roles", 2, authorities.size());
Set<String> roles = new HashSet<String>(2);
roles.add(authorities.get(0).getAuthority());
roles.add(authorities.get(1).getAuthority());
assertTrue(roles.contains("ROLE_MANAGER"));
assertTrue(roles.contains("ROLE_DEVELOPER"));
}
@Test
public void testSubGroupRolesAreFoundWhenSubtreeSearchIsEnabled() {
populator.setGroupRoleAttribute("ou");
populator.setConvertToUpperCase(true);
populator.setSearchSubtree(true);
DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName("uid=ben,ou=people,dc=springframework,dc=org"));
List<GrantedAuthority> authorities = populator.getGrantedAuthorities(ctx, "manager");
assertEquals("Should have 3 roles", 3, authorities.size());
Set<String> roles = new HashSet<String>(3);
roles.add(authorities.get(0).getAuthority());
roles.add(authorities.get(1).getAuthority());
roles.add(authorities.get(2).getAuthority());
assertTrue(roles.contains("ROLE_MANAGER"));
assertTrue(roles.contains("ROLE_DEVELOPER"));
assertTrue(roles.contains("ROLE_SUBMANAGER"));
}
@Test
public void testUserDnWithEscapedCharacterParameterReturnsExpectedRoles() {
populator.setGroupRoleAttribute("ou");
populator.setConvertToUpperCase(true);
populator.setGroupSearchFilter("(member={0})");
DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName("cn=mouse\\, jerry,ou=people,dc=springframework,dc=org"));
List<GrantedAuthority> authorities = populator.getGrantedAuthorities(ctx, "notused");
assertEquals("Should have 1 role", 1, authorities.size());
assertEquals("ROLE_MANAGER", authorities.get(0).getAuthority());
}
}

View File

@@ -0,0 +1,34 @@
package org.springframework.security.ldap.populator;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.*;
import java.util.List;
import org.junit.Test;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.userdetails.UserDetails;
import org.springframework.security.userdetails.UserDetailsService;
import org.springframework.security.util.AuthorityUtils;
/**
* @author Luke Taylor
* @version $Id$
*/
public class UserDetailsServiceLdapAuthoritiesPopulatorTests {
@Test
public void delegationToUserDetailsServiceReturnsCorrectRoles() throws Exception {
UserDetailsService uds = mock(UserDetailsService.class);
UserDetails user = mock(UserDetails.class);
when(uds.loadUserByUsername("joe")).thenReturn(user);
when(user.getAuthorities()).thenReturn(AuthorityUtils.createAuthorityList("ROLE_USER"));
UserDetailsServiceLdapAuthoritiesPopulator populator = new UserDetailsServiceLdapAuthoritiesPopulator(uds);
List<GrantedAuthority> auths = populator.getGrantedAuthorities(new DirContextAdapter(), "joe");
assertEquals(1, auths.size());
assertEquals("ROLE_USER", auths.get(0).getAuthority());
}
}

View File

@@ -0,0 +1,114 @@
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* 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
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.ldap.search;
import org.springframework.security.ldap.AbstractLdapIntegrationTests;
import org.springframework.security.userdetails.UsernameNotFoundException;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
/**
* Tests for FilterBasedLdapUserSearch.
*
* @author Luke Taylor
* @version $Id$
*/
public class FilterBasedLdapUserSearchTests extends AbstractLdapIntegrationTests {
//~ Instance fields ================================================================================================
private BaseLdapPathContextSource dirCtxFactory;
//~ Methods ========================================================================================================
public void onSetUp() throws Exception {
super.onSetUp();
dirCtxFactory = getContextSource();
}
@Test
public void basicSearchSucceeds() {
FilterBasedLdapUserSearch locator = new FilterBasedLdapUserSearch("ou=people", "(uid={0})", dirCtxFactory);
locator.setSearchSubtree(false);
locator.setSearchTimeLimit(0);
locator.setDerefLinkFlag(false);
DirContextOperations bob = locator.searchForUser("bob");
assertEquals("bob", bob.getStringAttribute("uid"));
assertEquals(new DistinguishedName("uid=bob,ou=people"), bob.getDn());
}
@Test
public void searchForNameWithCommaSucceeds() {
FilterBasedLdapUserSearch locator = new FilterBasedLdapUserSearch("ou=people", "(uid={0})", dirCtxFactory);
locator.setSearchSubtree(false);
DirContextOperations jerry = locator.searchForUser("jerry");
assertEquals("jerry", jerry.getStringAttribute("uid"));
assertEquals(new DistinguishedName("cn=mouse\\, jerry,ou=people"), jerry.getDn());
}
// Try some funny business with filters.
@Test
public void extraFilterPartToExcludeBob() throws Exception {
FilterBasedLdapUserSearch locator = new FilterBasedLdapUserSearch("ou=people",
"(&(cn=*)(!(|(uid={0})(uid=rod)(uid=jerry))))", dirCtxFactory);
// Search for bob, get back ben...
DirContextOperations ben = locator.searchForUser("bob");
assertEquals("Ben Alex", ben.getStringAttribute("cn"));
// assertEquals("uid=ben,ou=people,"+ROOT_DN, ben.getDn());
}
@Test(expected=IncorrectResultSizeDataAccessException.class)
public void searchFailsOnMultipleMatches() {
FilterBasedLdapUserSearch locator = new FilterBasedLdapUserSearch("ou=people", "(cn=*)", dirCtxFactory);
locator.searchForUser("Ignored");
}
@Test(expected=UsernameNotFoundException.class)
public void searchForInvalidUserFails() {
FilterBasedLdapUserSearch locator = new FilterBasedLdapUserSearch("ou=people", "(uid={0})", dirCtxFactory);
locator.searchForUser("Joe");
}
@Test
public void subTreeSearchSucceeds() {
// Don't set the searchBase, so search from the root.
FilterBasedLdapUserSearch locator = new FilterBasedLdapUserSearch("", "(cn={0})", dirCtxFactory);
locator.setSearchSubtree(true);
DirContextOperations ben = locator.searchForUser("Ben Alex");
assertEquals("ben", ben.getStringAttribute("uid"));
assertEquals(new DistinguishedName("uid=ben,ou=people"), ben.getDn());
}
@Test
public void searchWithDifferentSearchBaseIsSuccessful() throws Exception {
FilterBasedLdapUserSearch locator = new FilterBasedLdapUserSearch("ou=otherpeople", "(cn={0})", dirCtxFactory);
DirContextOperations joe = locator.searchForUser("Joe Smeth");
assertEquals("Joe Smeth", joe.getStringAttribute("cn"));
}
}

View File

@@ -0,0 +1,211 @@
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* 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
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.providers.ldap;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.List;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.integration.junit4.JUnit4Mockery;
import org.junit.Test;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.security.Authentication;
import org.springframework.security.BadCredentialsException;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.ldap.LdapAuthoritiesPopulator;
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
import org.springframework.security.userdetails.UserDetails;
import org.springframework.security.userdetails.UsernameNotFoundException;
import org.springframework.security.userdetails.ldap.LdapUserDetailsMapper;
import org.springframework.security.util.AuthorityUtils;
/**
* Tests {@link LdapAuthenticationProvider}.
*
* @author Luke Taylor
* @version $Id$
*/
public class LdapAuthenticationProviderTests {
Mockery jmock = new JUnit4Mockery();
//~ Methods ========================================================================================================
@Test
public void testSupportsUsernamePasswordAuthenticationToken() {
LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(new MockAuthenticator(),
new MockAuthoritiesPopulator());
assertTrue(ldapProvider.supports(UsernamePasswordAuthenticationToken.class));
}
@Test
public void testDefaultMapperIsSet() {
LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(new MockAuthenticator(),
new MockAuthoritiesPopulator());
assertTrue(ldapProvider.getUserDetailsContextMapper() instanceof LdapUserDetailsMapper);
}
@Test
public void testEmptyOrNullUserNameThrowsException() {
LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(new MockAuthenticator(),
new MockAuthoritiesPopulator());
try {
ldapProvider.authenticate(new UsernamePasswordAuthenticationToken(null, "password"));
fail("Expected BadCredentialsException for empty username");
} catch (BadCredentialsException expected) {}
try {
ldapProvider.authenticate(new UsernamePasswordAuthenticationToken("", "bobspassword"));
fail("Expected BadCredentialsException for null username");
} catch (BadCredentialsException expected) {}
}
@Test(expected=BadCredentialsException.class)
public void emptyPasswordIsRejected() {
LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(new MockAuthenticator());
ldapProvider.authenticate(new UsernamePasswordAuthenticationToken("jen", ""));
}
@Test
public void usernameNotFoundExceptionIsHiddenByDefault() {
final LdapAuthenticator authenticator = jmock.mock(LdapAuthenticator.class);
final UsernamePasswordAuthenticationToken joe = new UsernamePasswordAuthenticationToken("joe", "password");
jmock.checking(new Expectations() {{
oneOf(authenticator).authenticate(joe); will(throwException(new UsernameNotFoundException("nobody")));
}});
LdapAuthenticationProvider provider = new LdapAuthenticationProvider(authenticator);
try {
provider.authenticate(joe);
fail();
} catch (BadCredentialsException expected) {
if (expected instanceof UsernameNotFoundException) {
fail("Exception should have been hidden");
}
}
}
@Test(expected=UsernameNotFoundException.class)
public void usernameNotFoundExceptionIsNotHiddenIfConfigured() {
final LdapAuthenticator authenticator = jmock.mock(LdapAuthenticator.class);
final UsernamePasswordAuthenticationToken joe = new UsernamePasswordAuthenticationToken("joe", "password");
jmock.checking(new Expectations() {{
oneOf(authenticator).authenticate(joe); will(throwException(new UsernameNotFoundException("nobody")));
}});
LdapAuthenticationProvider provider = new LdapAuthenticationProvider(authenticator);
provider.setHideUserNotFoundExceptions(false);
provider.authenticate(joe);
}
@Test
public void normalUsage() {
MockAuthoritiesPopulator populator = new MockAuthoritiesPopulator();
LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(new MockAuthenticator(), populator);
LdapUserDetailsMapper userMapper = new LdapUserDetailsMapper();
userMapper.setRoleAttributes(new String[] {"ou"});
ldapProvider.setUserDetailsContextMapper(userMapper);
assertNotNull(ldapProvider.getAuthoritiesPopulator());
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken("ben", "benspassword");
Authentication authResult = ldapProvider.authenticate(authRequest);
assertEquals("benspassword", authResult.getCredentials());
UserDetails user = (UserDetails) authResult.getPrincipal();
assertEquals(2, user.getAuthorities().size());
assertEquals("{SHA}nFCebWjxfaLbHHG1Qk5UU4trbvQ=", user.getPassword());
assertEquals("ben", user.getUsername());
assertEquals("ben", populator.getRequestedUsername());
ArrayList<String> authorities = new ArrayList<String>();
authorities.add(user.getAuthorities().get(0).getAuthority());
authorities.add(user.getAuthorities().get(1).getAuthority());
assertTrue(authorities.contains("ROLE_FROM_ENTRY"));
assertTrue(authorities.contains("ROLE_FROM_POPULATOR"));
}
@Test
public void passwordIsSetFromUserDataIfUseAuthenticationRequestCredentialsIsFalse() {
LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(new MockAuthenticator(),
new MockAuthoritiesPopulator());
ldapProvider.setUseAuthenticationRequestCredentials(false);
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken("ben", "benspassword");
Authentication authResult = ldapProvider.authenticate(authRequest);
assertEquals("{SHA}nFCebWjxfaLbHHG1Qk5UU4trbvQ=", authResult.getCredentials());
}
@Test
public void useWithNullAuthoritiesPopulatorReturnsCorrectRole() {
LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(new MockAuthenticator());
LdapUserDetailsMapper userMapper = new LdapUserDetailsMapper();
userMapper.setRoleAttributes(new String[] {"ou"});
ldapProvider.setUserDetailsContextMapper(userMapper);
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken("ben", "benspassword");
UserDetails user = (UserDetails) ldapProvider.authenticate(authRequest).getPrincipal();
assertEquals(1, user.getAuthorities().size());
assertEquals("ROLE_FROM_ENTRY", user.getAuthorities().get(0).getAuthority());
}
//~ Inner Classes ==================================================================================================
class MockAuthenticator implements LdapAuthenticator {
public DirContextOperations authenticate(Authentication authentication) {
DirContextAdapter ctx = new DirContextAdapter();
ctx.setAttributeValue("ou", "FROM_ENTRY");
String username = authentication.getName();
String password = (String) authentication.getCredentials();
if (username.equals("ben") && password.equals("benspassword")) {
ctx.setDn(new DistinguishedName("cn=ben,ou=people,dc=springframework,dc=org"));
ctx.setAttributeValue("userPassword","{SHA}nFCebWjxfaLbHHG1Qk5UU4trbvQ=");
return ctx;
} else if (username.equals("jen") && password.equals("")) {
ctx.setDn(new DistinguishedName("cn=jen,ou=people,dc=springframework,dc=org"));
return ctx;
}
throw new BadCredentialsException("Authentication failed.");
}
}
class MockAuthoritiesPopulator implements LdapAuthoritiesPopulator {
String username;
public List<GrantedAuthority> getGrantedAuthorities(DirContextOperations userCtx, String username) {
this.username = username;
return AuthorityUtils.createAuthorityList("ROLE_FROM_POPULATOR");
}
String getRequestedUsername() {
return username;
}
}
}

View File

@@ -0,0 +1,97 @@
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* 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
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.providers.ldap.authenticator;
import org.springframework.security.Authentication;
import org.springframework.security.BadCredentialsException;
import org.springframework.security.SpringSecurityMessageSource;
import org.springframework.security.ldap.AbstractLdapIntegrationTests;
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.DistinguishedName;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import org.junit.Test;
/**
* Tests for {@link BindAuthenticator}.
*
* @author Luke Taylor
* @version $Id$
*/
public class BindAuthenticatorTests extends AbstractLdapIntegrationTests {
//~ Instance fields ================================================================================================
private BindAuthenticator authenticator;
private Authentication bob;
// private Authentication ben;
//~ Methods ========================================================================================================
public void onSetUp() {
authenticator = new BindAuthenticator(getContextSource());
authenticator.setMessageSource(new SpringSecurityMessageSource());
bob = new UsernamePasswordAuthenticationToken("bob", "bobspassword");
// ben = new UsernamePasswordAuthenticationToken("ben", "benspassword");
}
@Test
public void testAuthenticationWithCorrectPasswordSucceeds() {
authenticator.setUserDnPatterns(new String[] {"uid={0},ou=people"});
DirContextOperations user = authenticator.authenticate(bob);
assertEquals("bob", user.getStringAttribute("uid"));
}
@Test
public void testAuthenticationWithInvalidUserNameFails() {
authenticator.setUserDnPatterns(new String[] {"uid={0},ou=people"});
try {
authenticator.authenticate(new UsernamePasswordAuthenticationToken("nonexistentsuser", "password"));
fail("Shouldn't be able to bind with invalid username");
} catch (BadCredentialsException expected) {}
}
@Test
public void testAuthenticationWithUserSearch() throws Exception {
DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName("uid=bob,ou=people"));
authenticator.setUserSearch(new MockUserSearch(ctx));
authenticator.afterPropertiesSet();
authenticator.authenticate(bob);
}
@Test
public void testAuthenticationWithWrongPasswordFails() {
authenticator.setUserDnPatterns(new String[] {"uid={0},ou=people"});
try {
authenticator.authenticate(new UsernamePasswordAuthenticationToken("bob", "wrongpassword"));
fail("Shouldn't be able to bind with wrong password");
} catch (BadCredentialsException expected) {}
}
@Test
public void testUserDnPatternReturnsCorrectDn() {
authenticator.setUserDnPatterns(new String[] {"cn={0},ou=people"});
assertEquals("cn=Joe,ou=people", authenticator.getUserDns("Joe").get(0));
}
}

View File

@@ -0,0 +1,116 @@
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* 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
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.providers.ldap.authenticator;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import org.springframework.security.providers.encoding.LdapShaPasswordEncoder;
/**
* Tests {@link LdapShaPasswordEncoder}.
*
* @author Luke Taylor
* @version $Id$
*/
public class LdapShaPasswordEncoderTests {
//~ Instance fields ================================================================================================
LdapShaPasswordEncoder sha;
//~ Methods ========================================================================================================
@Before
public void setUp() throws Exception {
sha = new LdapShaPasswordEncoder();
}
@Test
public void invalidPasswordFails() {
assertFalse(sha.isPasswordValid("{SHA}ddSFGmjXYPbZC+NXR2kCzBRjqiE=", "wrongpassword", null));
}
@Test
public void invalidSaltedPasswordFails() {
assertFalse(sha.isPasswordValid("{SSHA}25ro4PKC8jhQZ26jVsozhX/xaP0suHgX", "wrongpassword", null));
assertFalse(sha.isPasswordValid("{SSHA}PQy2j+6n5ytA+YlAKkM8Fh4p6u2JxfVd", "wrongpassword", null));
}
@Test(expected=IllegalArgumentException.class)
public void nonByteArraySaltThrowsException() {
sha.encodePassword("password", "AStringNotAByteArray");
}
/**
* Test values generated by 'slappasswd -h {SHA} -s boabspasswurd'
*/
@Test
public void validPasswordSucceeds() {
sha.setForceLowerCasePrefix(false);
assertTrue(sha.isPasswordValid("{SHA}ddSFGmjXYPbZC+NXR2kCzBRjqiE=", "boabspasswurd", null));
assertTrue(sha.isPasswordValid("{sha}ddSFGmjXYPbZC+NXR2kCzBRjqiE=", "boabspasswurd", null));
sha.setForceLowerCasePrefix(true);
assertTrue(sha.isPasswordValid("{SHA}ddSFGmjXYPbZC+NXR2kCzBRjqiE=", "boabspasswurd", null));
assertTrue(sha.isPasswordValid("{sha}ddSFGmjXYPbZC+NXR2kCzBRjqiE=", "boabspasswurd", null));
}
/**
* Test values generated by 'slappasswd -s boabspasswurd'
*/
@Test
public void validSaltedPasswordSucceeds() {
sha.setForceLowerCasePrefix(false);
assertTrue(sha.isPasswordValid("{SSHA}25ro4PKC8jhQZ26jVsozhX/xaP0suHgX", "boabspasswurd", null));
assertTrue(sha.isPasswordValid("{ssha}PQy2j+6n5ytA+YlAKkM8Fh4p6u2JxfVd", "boabspasswurd", null));
sha.setForceLowerCasePrefix(true);
assertTrue(sha.isPasswordValid("{SSHA}25ro4PKC8jhQZ26jVsozhX/xaP0suHgX", "boabspasswurd", null));
assertTrue(sha.isPasswordValid("{ssha}PQy2j+6n5ytA+YlAKkM8Fh4p6u2JxfVd", "boabspasswurd", null));
}
@Test
// SEC-1031
public void fullLengthOfHashIsUsedInComparison() throws Exception {
// Change the first hash character from '2' to '3'
assertFalse(sha.isPasswordValid("{SSHA}35ro4PKC8jhQZ26jVsozhX/xaP0suHgX", "boabspasswurd", null));
// Change the last hash character from 'X' to 'Y'
assertFalse(sha.isPasswordValid("{SSHA}25ro4PKC8jhQZ26jVsozhX/xaP0suHgY", "boabspasswurd", null));
}
@Test
public void correctPrefixCaseIsUsed() {
sha.setForceLowerCasePrefix(false);
assertEquals("{SHA}ddSFGmjXYPbZC+NXR2kCzBRjqiE=", sha.encodePassword("boabspasswurd", null));
assertTrue(sha.encodePassword("somepassword", "salt".getBytes()).startsWith("{SSHA}"));
sha.setForceLowerCasePrefix(true);
assertEquals("{sha}ddSFGmjXYPbZC+NXR2kCzBRjqiE=", sha.encodePassword("boabspasswurd", null));
assertTrue(sha.encodePassword("somepassword", "salt".getBytes()).startsWith("{ssha}"));
}
@Test(expected=IllegalArgumentException.class)
public void invalidPrefixIsRejected() {
sha.isPasswordValid("{MD9}xxxxxxxxxx" , "somepassword", null);
}
@Test(expected=IllegalArgumentException.class)
public void malformedPrefixIsRejected() {
// No right brace
sha.isPasswordValid("{SSHA25ro4PKC8jhQZ26jVsozhX/xaP0suHgX" , "somepassword", null);
}
}

View File

@@ -0,0 +1,48 @@
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* 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
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.providers.ldap.authenticator;
import org.springframework.security.ldap.LdapUserSearch;
import org.springframework.ldap.core.DirContextOperations;
/**
*
*
* @author Luke Taylor
* @version $Id$
*/
public class MockUserSearch implements LdapUserSearch {
//~ Instance fields ================================================================================================
DirContextOperations user;
//~ Constructors ===================================================================================================
public MockUserSearch() {
}
public MockUserSearch(DirContextOperations user) {
this.user = user;
}
//~ Methods ========================================================================================================
public DirContextOperations searchForUser(String username) {
return user;
}
}

View File

@@ -0,0 +1,76 @@
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* 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
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.providers.ldap.authenticator;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.SearchControls;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.integration.junit4.JUnit4Mockery;
import org.junit.Test;
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
/**
*
* @author Luke Taylor
* @version $Id$
*/
public class PasswordComparisonAuthenticatorMockTests {
Mockery jmock = new JUnit4Mockery();
//~ Methods ========================================================================================================
@Test
public void ldapCompareOperationIsUsedWhenPasswordIsNotRetrieved() throws Exception {
final DirContext dirCtx = jmock.mock(DirContext.class);
final BaseLdapPathContextSource source = jmock.mock(BaseLdapPathContextSource.class);
final BasicAttributes attrs = new BasicAttributes();
attrs.put(new BasicAttribute("uid", "bob"));
PasswordComparisonAuthenticator authenticator = new PasswordComparisonAuthenticator(source);
authenticator.setUserDnPatterns(new String[] {"cn={0},ou=people"});
// Get the mock to return an empty attribute set
jmock.checking(new Expectations() {{
allowing(source).getReadOnlyContext(); will(returnValue(dirCtx));
oneOf(dirCtx).getAttributes(with(equal("cn=Bob,ou=people")), with(aNull(String[].class))); will(returnValue(attrs));
oneOf(dirCtx).getNameInNamespace(); will(returnValue("dc=springframework,dc=org"));
}});
// Setup a single return value (i.e. success)
final Attributes searchResults = new BasicAttributes("", null);
jmock.checking(new Expectations() {{
oneOf(dirCtx).search(with(equal("cn=Bob,ou=people")),
with(equal("(userPassword={0})")),
with(aNonNull(Object[].class)),
with(aNonNull(SearchControls.class)));
will(returnValue(searchResults.getAll()));
atLeast(1).of(dirCtx).close();
}});
authenticator.authenticate(new UsernamePasswordAuthenticationToken("Bob","bobspassword"));
jmock.assertIsSatisfied();
}
}

View File

@@ -0,0 +1,144 @@
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* 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
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.providers.ldap.authenticator;
import org.springframework.security.BadCredentialsException;
import org.springframework.security.Authentication;
import org.springframework.security.ldap.AbstractLdapIntegrationTests;
import org.springframework.security.providers.encoding.LdapShaPasswordEncoder;
import org.springframework.security.providers.encoding.PlaintextPasswordEncoder;
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
import org.springframework.security.userdetails.UsernameNotFoundException;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DistinguishedName;
import static org.junit.Assert.*;
import org.junit.Test;
/**
* Tests for {@link PasswordComparisonAuthenticator}.
*
* @author Luke Taylor
* @version $Id$
*/
public class PasswordComparisonAuthenticatorTests extends AbstractLdapIntegrationTests {
//~ Instance fields ================================================================================================
private PasswordComparisonAuthenticator authenticator;
private Authentication bob;
private Authentication ben;
//~ Methods ========================================================================================================
public void onSetUp() throws Exception {
super.onSetUp();
authenticator = new PasswordComparisonAuthenticator(getContextSource());
authenticator.setPasswordEncoder(new PlaintextPasswordEncoder());
authenticator.setUserDnPatterns(new String[] {"uid={0},ou=people"});
bob = new UsernamePasswordAuthenticationToken("bob", "bobspassword");
ben = new UsernamePasswordAuthenticationToken("ben", "benspassword");
}
@Test
public void testAllAttributesAreRetrievedByDefault() {
DirContextAdapter user = (DirContextAdapter) authenticator.authenticate(bob);
//System.out.println(user.getAttributes().toString());
assertEquals("User should have 5 attributes", 5, user.getAttributes().size());
}
@Test
public void testFailedSearchGivesUserNotFoundException() throws Exception {
authenticator = new PasswordComparisonAuthenticator(getContextSource());
assertTrue("User DN matches shouldn't be available", authenticator.getUserDns("Bob").isEmpty());
authenticator.setUserSearch(new MockUserSearch(null));
authenticator.afterPropertiesSet();
try {
authenticator.authenticate(new UsernamePasswordAuthenticationToken("Joe", "pass"));
fail("Expected exception on failed user search");
} catch (UsernameNotFoundException expected) {}
}
@Test(expected = BadCredentialsException.class)
public void testLdapPasswordCompareFailsWithWrongPassword() {
// Don't retrieve the password
authenticator.setUserAttributes(new String[] {"uid", "cn", "sn"});
authenticator.authenticate(new UsernamePasswordAuthenticationToken("bob", "wrongpass"));
}
@Test
public void testMultipleDnPatternsWorkOk() {
authenticator.setUserDnPatterns(new String[] {"uid={0},ou=nonexistent", "uid={0},ou=people"});
authenticator.authenticate(bob);
}
@Test
public void testOnlySpecifiedAttributesAreRetrieved() throws Exception {
authenticator.setUserAttributes(new String[] {"uid", "userPassword"});
DirContextAdapter user = (DirContextAdapter) authenticator.authenticate(bob);
assertEquals("Should have retrieved 2 attribute (uid, userPassword)", 2, user.getAttributes().size());
}
@Test
public void testLdapCompareSucceedsWithCorrectPassword() {
// Don't retrieve the password
authenticator.setUserAttributes(new String[] {"uid"});
authenticator.authenticate(bob);
}
@Test
public void testLdapCompareSucceedsWithShaEncodedPassword() {
// Don't retrieve the password
authenticator.setUserAttributes(new String[] {"uid"});
authenticator.setPasswordEncoder(new LdapShaPasswordEncoder());
authenticator.authenticate(ben);
}
@Test(expected = IllegalArgumentException.class)
public void testPasswordEncoderCantBeNull() {
authenticator.setPasswordEncoder(null);
}
@Test
public void testUseOfDifferentPasswordAttributeSucceeds() {
authenticator.setPasswordAttributeName("uid");
authenticator.authenticate(new UsernamePasswordAuthenticationToken("bob", "bob"));
}
@Test
public void testLdapCompareWithDifferentPasswordAttributeSucceeds() {
authenticator.setUserAttributes(new String[] {"uid"});
authenticator.setPasswordAttributeName("cn");
authenticator.authenticate(new UsernamePasswordAuthenticationToken("ben", "Ben Alex"));
}
@Test
public void testWithUserSearch() {
authenticator = new PasswordComparisonAuthenticator(getContextSource());
authenticator.setPasswordEncoder(new PlaintextPasswordEncoder());
assertTrue("User DN matches shouldn't be available", authenticator.getUserDns("Bob").isEmpty());
DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName("uid=Bob,ou=people"));
ctx.setAttributeValue("userPassword", "bobspassword");
authenticator.setUserSearch(new MockUserSearch(ctx));
authenticator.authenticate(new UsernamePasswordAuthenticationToken("shouldntbeused", "bobspassword"));
}
}

View File

@@ -0,0 +1,113 @@
package org.springframework.security.userdetails.ldap;
import junit.framework.TestCase;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DistinguishedName;
/**
* @author Luke Taylor
* @version $Id$
*/
public class InetOrgPersonTests extends TestCase {
public void testUsernameIsMappedFromContextUidIfNotSet() {
InetOrgPerson.Essence essence = new InetOrgPerson.Essence(createUserContext());
InetOrgPerson p = (InetOrgPerson) essence.createUserDetails();
assertEquals("ghengis", p.getUsername());
}
public void testUsernameIsDifferentFromContextUidIfSet() {
InetOrgPerson.Essence essence = new InetOrgPerson.Essence(createUserContext());
essence.setUsername("joe");
InetOrgPerson p = (InetOrgPerson) essence.createUserDetails();
assertEquals("joe", p.getUsername());
assertEquals("ghengis", p.getUid());
}
public void testAttributesMapCorrectlyFromContext() {
InetOrgPerson.Essence essence = new InetOrgPerson.Essence(createUserContext());
InetOrgPerson p = (InetOrgPerson) essence.createUserDetails();
assertEquals("HORS1", p.getCarLicense());
assertEquals("ghengis@mongolia", p.getMail());
assertEquals("Khan", p.getSn());
assertEquals("Ghengis Khan", p.getCn()[0]);
assertEquals("00001", p.getEmployeeNumber());
assertEquals("+442075436521", p.getTelephoneNumber());
assertEquals("Steppes", p.getHomePostalAddress());
assertEquals("+467575436521", p.getHomePhone());
assertEquals("Hordes", p.getO());
assertEquals("Horde1", p.getOu());
assertEquals("On the Move", p.getPostalAddress());
assertEquals("Changes Frequently", p.getPostalCode());
assertEquals("Yurt 1", p.getRoomNumber());
assertEquals("Westward Avenue", p.getStreet());
assertEquals("Scary", p.getDescription());
assertEquals("Ghengis McCann", p.getDisplayName());
assertEquals("G", p.getInitials());
}
public void testPasswordIsSetFromContextUserPassword() {
InetOrgPerson.Essence essence = new InetOrgPerson.Essence(createUserContext());
InetOrgPerson p = (InetOrgPerson) essence.createUserDetails();
assertEquals("pillage", p.getPassword());
}
public void testMappingBackToContextMatchesOriginalData() {
DirContextAdapter ctx1 = createUserContext();
DirContextAdapter ctx2 = new DirContextAdapter();
ctx1.setAttributeValues("objectclass", new String[] {"top", "person", "organizationalPerson", "inetOrgPerson"});
ctx2.setDn(new DistinguishedName("ignored=ignored"));
InetOrgPerson p = (InetOrgPerson) (new InetOrgPerson.Essence(ctx1)).createUserDetails();
p.populateContext(ctx2);
assertEquals(ctx1, ctx2);
}
public void testCopyMatchesOriginalData() {
DirContextAdapter ctx1 = createUserContext();
DirContextAdapter ctx2 = new DirContextAdapter();
ctx2.setDn(new DistinguishedName("ignored=ignored"));
ctx1.setAttributeValues("objectclass", new String[] {"top", "person", "organizationalPerson", "inetOrgPerson"});
InetOrgPerson p = (InetOrgPerson) (new InetOrgPerson.Essence(ctx1)).createUserDetails();
InetOrgPerson p2 = (InetOrgPerson) new InetOrgPerson.Essence(p).createUserDetails();
p2.populateContext(ctx2);
assertEquals(ctx1, ctx2);
}
private DirContextAdapter createUserContext() {
DirContextAdapter ctx = new DirContextAdapter();
ctx.setDn(new DistinguishedName("ignored=ignored"));
ctx.setAttributeValue("uid", "ghengis");
ctx.setAttributeValue("userPassword", "pillage");
ctx.setAttributeValue("carLicense", "HORS1");
ctx.setAttributeValue("cn", "Ghengis Khan");
ctx.setAttributeValue("description", "Scary");
ctx.setAttributeValue("destinationIndicator", "West");
ctx.setAttributeValue("displayName", "Ghengis McCann");
ctx.setAttributeValue("homePhone", "+467575436521");
ctx.setAttributeValue("initials", "G");
ctx.setAttributeValue("employeeNumber", "00001");
ctx.setAttributeValue("homePostalAddress", "Steppes");
ctx.setAttributeValue("mail", "ghengis@mongolia");
ctx.setAttributeValue("mobile", "always");
ctx.setAttributeValue("o", "Hordes");
ctx.setAttributeValue("ou", "Horde1");
ctx.setAttributeValue("postalAddress", "On the Move");
ctx.setAttributeValue("postalCode", "Changes Frequently");
ctx.setAttributeValue("roomNumber", "Yurt 1");
ctx.setAttributeValue("roomNumber", "Yurt 1");
ctx.setAttributeValue("sn", "Khan");
ctx.setAttributeValue("street", "Westward Avenue");
ctx.setAttributeValue("telephoneNumber", "+442075436521");
return ctx;
}
}

View File

@@ -0,0 +1,210 @@
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* 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
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.userdetails.ldap;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.List;
import org.junit.After;
import org.junit.Test;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.security.BadCredentialsException;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.ldap.AbstractLdapIntegrationTests;
import org.springframework.security.ldap.DefaultLdapUsernameToDnMapper;
import org.springframework.security.ldap.SpringSecurityLdapTemplate;
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
import org.springframework.security.userdetails.UsernameNotFoundException;
import org.springframework.security.util.AuthorityUtils;
/**
* @author Luke Taylor
* @version $Id$
*/
public class LdapUserDetailsManagerTests extends AbstractLdapIntegrationTests {
private static final List<GrantedAuthority> TEST_AUTHORITIES = AuthorityUtils.createAuthorityList("ROLE_CLOWNS","ROLE_ACROBATS");
private LdapUserDetailsManager mgr;
private SpringSecurityLdapTemplate template;
public void onSetUp() throws Exception {
super.onSetUp();
mgr = new LdapUserDetailsManager(getContextSource());
template = new SpringSecurityLdapTemplate(getContextSource());
DirContextAdapter ctx = new DirContextAdapter();
ctx.setAttributeValue("objectclass", "organizationalUnit");
ctx.setAttributeValue("ou", "test people");
template.bind("ou=test people", ctx, null);
ctx.setAttributeValue("ou", "testgroups");
template.bind("ou=testgroups", ctx, null);
DirContextAdapter group = new DirContextAdapter();
group.setAttributeValue("objectclass", "groupOfNames");
group.setAttributeValue("cn", "clowns");
group.setAttributeValue("member", "cn=nobody,ou=test people,dc=springframework,dc=org");
template.bind("cn=clowns,ou=testgroups", group, null);
group.setAttributeValue("cn", "acrobats");
template.bind("cn=acrobats,ou=testgroups", group, null);
mgr.setUsernameMapper(new DefaultLdapUsernameToDnMapper("ou=test people","uid"));
mgr.setGroupSearchBase("ou=testgroups");
mgr.setGroupRoleAttributeName("cn");
mgr.setGroupMemberAttributeName("member");
mgr.setUserDetailsMapper(new PersonContextMapper());
}
@After
public void onTearDown() throws Exception {
// Iterator people = template.list("ou=testpeople").iterator();
// DirContext rootCtx = new DirContextAdapter(new DistinguishedName(getInitialCtxFactory().getRootDn()));
//
// while(people.hasNext()) {
// template.unbind((String) people.next() + ",ou=testpeople");
// }
template.unbind("ou=test people",true);
template.unbind("ou=testgroups",true);
SecurityContextHolder.clearContext();
}
@Test
public void testLoadUserByUsernameReturnsCorrectData() {
mgr.setUsernameMapper(new DefaultLdapUsernameToDnMapper("ou=people","uid"));
mgr.setGroupSearchBase("ou=groups");
LdapUserDetails bob = (LdapUserDetails) mgr.loadUserByUsername("bob");
assertEquals("bob", bob.getUsername());
assertEquals("uid=bob,ou=people,dc=springframework,dc=org", bob.getDn());
assertEquals("bobspassword", bob.getPassword());
assertEquals(1, bob.getAuthorities().size());
}
@Test(expected = UsernameNotFoundException.class)
public void testLoadingInvalidUsernameThrowsUsernameNotFoundException() {
mgr.loadUserByUsername("jim");
}
@Test
public void testUserExistsReturnsTrueForValidUser() {
mgr.setUsernameMapper(new DefaultLdapUsernameToDnMapper("ou=people","uid"));
assertTrue(mgr.userExists("bob"));
}
@Test
public void testUserExistsReturnsFalseForInValidUser() {
assertFalse(mgr.userExists("jim"));
}
@Test
public void testCreateNewUserSucceeds() {
InetOrgPerson.Essence p = new InetOrgPerson.Essence();
p.setCarLicense("XXX");
p.setCn(new String[] {"Joe Smeth"});
p.setDepartmentNumber("5679");
p.setDescription("Some description");
p.setDn("whocares");
p.setEmployeeNumber("E781");
p.setInitials("J");
p.setMail("joe@smeth.com");
p.setMobile("+44776542911");
p.setOu("Joes Unit");
p.setO("Organization");
p.setRoomNumber("500X");
p.setSn("Smeth");
p.setUid("joe");
p.setAuthorities(TEST_AUTHORITIES);
mgr.createUser(p.createUserDetails());
}
@Test
public void testDeleteUserSucceeds() {
InetOrgPerson.Essence p = new InetOrgPerson.Essence();
p.setDn("whocares");
p.setCn(new String[] {"Don Smeth"});
p.setSn("Smeth");
p.setUid("don");
p.setAuthorities(TEST_AUTHORITIES);
mgr.createUser(p.createUserDetails());
mgr.setUserDetailsMapper(new InetOrgPersonContextMapper());
InetOrgPerson don = (InetOrgPerson) mgr.loadUserByUsername("don");
assertEquals(2, don.getAuthorities().size());
mgr.deleteUser("don");
try {
mgr.loadUserByUsername("don");
fail("Expected UsernameNotFoundException after deleting user");
} catch(UsernameNotFoundException expected) {
// expected
}
// Check that no authorities are left
assertEquals(0, mgr.getUserAuthorities(mgr.usernameMapper.buildDn("don"), "don").size());
}
@Test
public void testPasswordChangeWithCorrectOldPasswordSucceeds() {
InetOrgPerson.Essence p = new InetOrgPerson.Essence();
p.setDn("whocares");
p.setCn(new String[] {"John Yossarian"});
p.setSn("Yossarian");
p.setUid("johnyossarian");
p.setPassword("yossarianspassword");
p.setAuthorities(TEST_AUTHORITIES);
mgr.createUser(p.createUserDetails());
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken("johnyossarian", "yossarianspassword", TEST_AUTHORITIES));
mgr.changePassword("yossarianspassword", "yossariansnewpassword");
assertTrue(template.compare("uid=johnyossarian,ou=test people",
"userPassword", "yossariansnewpassword"));
}
@Test(expected = BadCredentialsException.class)
public void testPasswordChangeWithWrongOldPasswordFails() {
InetOrgPerson.Essence p = new InetOrgPerson.Essence();
p.setDn("whocares");
p.setCn(new String[] {"John Yossarian"});
p.setSn("Yossarian");
p.setUid("johnyossarian");
p.setPassword("yossarianspassword");
p.setAuthorities(TEST_AUTHORITIES);
mgr.createUser(p.createUserDetails());
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken("johnyossarian", "yossarianspassword", TEST_AUTHORITIES));
mgr.changePassword("wrongpassword", "yossariansnewpassword");
}
}

View File

@@ -0,0 +1,86 @@
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* 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
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.userdetails.ldap;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import junit.framework.TestCase;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.security.util.AuthorityUtils;
/**
* Tests {@link LdapUserDetailsMapper}.
*
* @author Luke Taylor
* @version $Id$
*/
public class LdapUserDetailsMapperTests extends TestCase {
public void testMultipleRoleAttributeValuesAreMappedToAuthorities() throws Exception {
LdapUserDetailsMapper mapper = new LdapUserDetailsMapper();
mapper.setConvertToUpperCase(false);
mapper.setRolePrefix("");
mapper.setRoleAttributes(new String[] {"userRole"});
DirContextAdapter ctx = new DirContextAdapter();
ctx.setAttributeValues("userRole", new String[] {"X", "Y", "Z"});
ctx.setAttributeValue("uid", "ani");
LdapUserDetailsImpl user = (LdapUserDetailsImpl) mapper.mapUserFromContext(ctx, "ani", AuthorityUtils.NO_AUTHORITIES);
assertEquals(3, user.getAuthorities().size());
}
/**
* SEC-303. Non-retrieved role attribute causes NullPointerException
*/
public void testNonRetrievedRoleAttributeIsIgnored() throws Exception {
LdapUserDetailsMapper mapper = new LdapUserDetailsMapper();
mapper.setRoleAttributes(new String[] {"userRole", "nonRetrievedAttribute"});
BasicAttributes attrs = new BasicAttributes();
attrs.put(new BasicAttribute("userRole", "x"));
DirContextAdapter ctx = new DirContextAdapter(attrs, new DistinguishedName("cn=someName"));
ctx.setAttributeValue("uid", "ani");
LdapUserDetailsImpl user = (LdapUserDetailsImpl) mapper.mapUserFromContext(ctx, "ani", AuthorityUtils.NO_AUTHORITIES);
assertEquals(1, user.getAuthorities().size());
assertEquals("ROLE_X", user.getAuthorities().get(0).getAuthority());
}
public void testPasswordAttributeIsMappedCorrectly() throws Exception {
LdapUserDetailsMapper mapper = new LdapUserDetailsMapper();
mapper.setPasswordAttributeName("myappsPassword");
BasicAttributes attrs = new BasicAttributes();
attrs.put(new BasicAttribute("myappsPassword", "mypassword".getBytes()));
DirContextAdapter ctx = new DirContextAdapter(attrs, new DistinguishedName("cn=someName"));
ctx.setAttributeValue("uid", "ani");
LdapUserDetails user = (LdapUserDetailsImpl) mapper.mapUserFromContext(ctx, "ani", AuthorityUtils.NO_AUTHORITIES);
assertEquals("mypassword", user.getPassword());
}
}

View File

@@ -0,0 +1,57 @@
package org.springframework.security.userdetails.ldap;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.List;
import java.util.Set;
import org.junit.Test;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.ldap.LdapAuthoritiesPopulator;
import org.springframework.security.providers.ldap.authenticator.MockUserSearch;
import org.springframework.security.userdetails.UserDetails;
import org.springframework.security.util.AuthorityUtils;
/**
* Tests for {@link LdapUserDetailsService}
*
* @author Luke Taylor
* @version $Id$
*/
public class LdapUserDetailsServiceTests {
@Test(expected = IllegalArgumentException.class)
public void rejectsNullSearchObject() {
new LdapUserDetailsService(null, new MockAuthoritiesPopulator());
}
@Test(expected = IllegalArgumentException.class)
public void rejectsNullAuthoritiesPopulator() {
new LdapUserDetailsService(new MockUserSearch(), null);
}
@Test
public void correctAuthoritiesAreReturned() {
DirContextAdapter userData = new DirContextAdapter(new DistinguishedName("uid=joe"));
LdapUserDetailsService service =
new LdapUserDetailsService(new MockUserSearch(userData), new MockAuthoritiesPopulator());
service.setUserDetailsMapper(new LdapUserDetailsMapper());
UserDetails user = service.loadUserByUsername("doesntmatterwegetjoeanyway");
Set<String> authorities = AuthorityUtils.authorityListToSet(user.getAuthorities());
assertEquals(1, authorities.size());
assertTrue(authorities.contains("ROLE_FROM_POPULATOR"));
}
class MockAuthoritiesPopulator implements LdapAuthoritiesPopulator {
public List<GrantedAuthority> getGrantedAuthorities(DirContextOperations userCtx, String username) {
return AuthorityUtils.createAuthorityList("ROLE_FROM_POPULATOR");
}
}
}