Initial LDAP provider checkin.
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
package org.acegisecurity.providers.ldap;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public abstract class AbstractLdapServerTestCase extends TestCase {
|
||||
protected static final String ROOT_DN = "dc=acegisecurity,dc=org";
|
||||
protected static final String PROVIDER_URL = "ldap://monkeymachine:389/"+ROOT_DN;
|
||||
//protected static final String PROVIDER_URL = "ldap://localhost:10389/" + ROOT_DN;
|
||||
protected static final String MANAGER_USER = "cn=manager," + ROOT_DN;
|
||||
protected static final String MANAGER_PASSWORD = "acegisecurity";
|
||||
|
||||
|
||||
// protected static final LdapTestServer server = new LdapTestServer();
|
||||
|
||||
protected AbstractLdapServerTestCase() {
|
||||
}
|
||||
|
||||
protected AbstractLdapServerTestCase(String string) {
|
||||
super(string);
|
||||
}
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
package org.acegisecurity.providers.ldap;
|
||||
|
||||
import javax.naming.Context;
|
||||
import javax.naming.directory.DirContext;
|
||||
import java.util.Hashtable;
|
||||
|
||||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
import org.acegisecurity.BadCredentialsException;
|
||||
|
||||
/**
|
||||
* Tests {@link InitialDirContextFactory}.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public class InitialDirContextFactoryTests extends AbstractLdapServerTestCase {
|
||||
|
||||
public void testNonLdapUrlIsRejected() throws Exception {
|
||||
DefaultInitialDirContextFactory idf = new DefaultInitialDirContextFactory();
|
||||
|
||||
idf.setUrl("http://acegisecurity.org/dc=acegisecurity,dc=org");
|
||||
|
||||
try {
|
||||
idf.afterPropertiesSet();
|
||||
fail("Expected exception for non 'ldap://' URL");
|
||||
} catch(IllegalArgumentException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testConnectionFailure() throws Exception {
|
||||
DefaultInitialDirContextFactory idf = new DefaultInitialDirContextFactory();
|
||||
// Use the wrong port
|
||||
idf.setUrl("ldap://localhost:60389");
|
||||
Hashtable env = new Hashtable();
|
||||
env.put("com.sun.jndi.ldap.connect.timeout", "200");
|
||||
idf.setExtraEnvVars(env);
|
||||
idf.afterPropertiesSet();
|
||||
try {
|
||||
idf.newInitialDirContext();
|
||||
fail("Connection succeeded unexpectedly");
|
||||
} catch(DataAccessResourceFailureException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testAnonymousBindSucceeds() throws Exception {
|
||||
DefaultInitialDirContextFactory idf = new DefaultInitialDirContextFactory();
|
||||
idf.setUrl(PROVIDER_URL);
|
||||
idf.afterPropertiesSet();
|
||||
DirContext ctx = idf.newInitialDirContext();
|
||||
// Connection pooling should be set by default for anon users.
|
||||
assertEquals("true",ctx.getEnvironment().get("com.sun.jndi.ldap.connect.pool"));
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
public void testBindAsManagerSucceeds() throws Exception {
|
||||
DefaultInitialDirContextFactory idf = new DefaultInitialDirContextFactory();
|
||||
idf.setUrl(PROVIDER_URL);
|
||||
idf.setManagerPassword(MANAGER_PASSWORD);
|
||||
idf.setManagerDn(MANAGER_USER);
|
||||
idf.afterPropertiesSet();
|
||||
DirContext ctx = idf.newInitialDirContext();
|
||||
assertEquals("true",ctx.getEnvironment().get("com.sun.jndi.ldap.connect.pool"));
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
public void testInvalidPasswordCausesBadCredentialsException() throws Exception {
|
||||
DefaultInitialDirContextFactory idf = new DefaultInitialDirContextFactory();
|
||||
idf.setUrl(PROVIDER_URL);
|
||||
idf.setManagerDn(MANAGER_USER);
|
||||
idf.setManagerPassword("wrongpassword");
|
||||
idf.afterPropertiesSet();
|
||||
try {
|
||||
DirContext ctx = idf.newInitialDirContext();
|
||||
fail("Authentication with wrong credentials should fail.");
|
||||
} catch(BadCredentialsException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testConnectionAsSpecificUserSucceeds() throws Exception {
|
||||
DefaultInitialDirContextFactory idf = new DefaultInitialDirContextFactory();
|
||||
idf.setUrl(PROVIDER_URL);
|
||||
idf.afterPropertiesSet();
|
||||
DirContext ctx = idf.newInitialDirContext("uid=Bob,ou=people,dc=acegisecurity,dc=org",
|
||||
"bobspassword");
|
||||
// We don't want pooling for specific users.
|
||||
assertNull(ctx.getEnvironment().get("com.sun.jndi.ldap.connect.pool"));
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
public void testEnvironment() {
|
||||
DefaultInitialDirContextFactory idf = new DefaultInitialDirContextFactory();
|
||||
idf.setUrl("ldap://acegisecurity.org/");
|
||||
|
||||
// check basic env
|
||||
Hashtable env = idf.getEnvironment();
|
||||
assertEquals("com.sun.jndi.ldap.LdapCtxFactory", env.get(Context.INITIAL_CONTEXT_FACTORY));
|
||||
assertEquals("ldap://acegisecurity.org/", env.get(Context.PROVIDER_URL));
|
||||
assertEquals("simple",env.get(Context.SECURITY_AUTHENTICATION));
|
||||
assertNull(env.get(Context.SECURITY_PRINCIPAL));
|
||||
assertNull(env.get(Context.SECURITY_CREDENTIALS));
|
||||
|
||||
// Ctx factory.
|
||||
idf.setInitialContextFactory("org.acegisecurity.NonExistentCtxFactory");
|
||||
env = idf.getEnvironment();
|
||||
assertEquals("org.acegisecurity.NonExistentCtxFactory", env.get(Context.INITIAL_CONTEXT_FACTORY));
|
||||
|
||||
// Auth type
|
||||
idf.setAuthenticationType("myauthtype");
|
||||
env = idf.getEnvironment();
|
||||
assertEquals("myauthtype", env.get(Context.SECURITY_AUTHENTICATION));
|
||||
|
||||
// Check extra vars
|
||||
Hashtable extraVars = new Hashtable();
|
||||
extraVars.put("extravar", "extravarvalue");
|
||||
idf.setExtraEnvVars(extraVars);
|
||||
env = idf.getEnvironment();
|
||||
assertEquals("extravarvalue", env.get("extravar"));
|
||||
}
|
||||
|
||||
public void testBaseDnIsParsedFromCorrectlyFromUrl() throws Exception {
|
||||
DefaultInitialDirContextFactory idf = new DefaultInitialDirContextFactory();
|
||||
|
||||
idf.setUrl("ldap://acegisecurity.org/dc=acegisecurity,dc=org");
|
||||
idf.afterPropertiesSet();
|
||||
assertEquals("dc=acegisecurity,dc=org", idf.getRootDn());
|
||||
|
||||
// Check with an empty root
|
||||
idf = new DefaultInitialDirContextFactory();
|
||||
idf.setUrl("ldap://acegisecurity.org/");
|
||||
idf.afterPropertiesSet();
|
||||
assertEquals("", idf.getRootDn());
|
||||
|
||||
// Empty root without trailing slash
|
||||
idf = new DefaultInitialDirContextFactory();
|
||||
idf.setUrl("ldap://acegisecurity.org");
|
||||
idf.afterPropertiesSet();
|
||||
assertEquals("", idf.getRootDn());
|
||||
}
|
||||
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
package org.acegisecurity.providers.ldap;
|
||||
|
||||
import javax.naming.directory.Attributes;
|
||||
import javax.naming.directory.BasicAttributes;
|
||||
|
||||
import org.acegisecurity.GrantedAuthority;
|
||||
import org.acegisecurity.GrantedAuthorityImpl;
|
||||
import org.acegisecurity.BadCredentialsException;
|
||||
import org.acegisecurity.Authentication;
|
||||
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
|
||||
import org.acegisecurity.providers.ldap.authenticator.FilterBasedLdapUserSearch;
|
||||
import org.acegisecurity.providers.ldap.authenticator.BindAuthenticator;
|
||||
import org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator;
|
||||
import org.acegisecurity.userdetails.UserDetails;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public class LdapAuthenticationProviderTests extends AbstractLdapServerTestCase {
|
||||
DefaultInitialDirContextFactory dirCtxFactory;
|
||||
|
||||
|
||||
public LdapAuthenticationProviderTests(String string) {
|
||||
super(string);
|
||||
}
|
||||
|
||||
public LdapAuthenticationProviderTests() {
|
||||
super();
|
||||
}
|
||||
|
||||
public void testNormalUsage() throws Exception {
|
||||
LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider();
|
||||
|
||||
ldapProvider.setAuthenticator(new MockAuthenticator());
|
||||
ldapProvider.setLdapAuthoritiesPopulator(new MockAuthoritiesPopulator());
|
||||
ldapProvider.afterPropertiesSet();
|
||||
|
||||
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("bob","bobspassword");
|
||||
UserDetails user = ldapProvider.retrieveUser("bob", token);
|
||||
assertEquals(1, user.getAuthorities().length);
|
||||
assertTrue(user.getAuthorities()[0].equals("ROLE_USER"));
|
||||
ldapProvider.additionalAuthenticationChecks(user, token);
|
||||
|
||||
}
|
||||
|
||||
public void testIntegration() throws Exception {
|
||||
LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider();
|
||||
|
||||
// Connection information
|
||||
DefaultInitialDirContextFactory dirCtxFactory = new DefaultInitialDirContextFactory();
|
||||
dirCtxFactory.setUrl(PROVIDER_URL);
|
||||
dirCtxFactory.setManagerDn(MANAGER_USER);
|
||||
dirCtxFactory.setManagerPassword(MANAGER_PASSWORD);
|
||||
dirCtxFactory.afterPropertiesSet();
|
||||
BindAuthenticator authenticator = new BindAuthenticator();
|
||||
//PasswordComparisonAuthenticator authenticator = new PasswordComparisonAuthenticator();
|
||||
authenticator.setInitialDirContextFactory(dirCtxFactory);
|
||||
//authenticator.setUserDnPattern("cn={0},ou=people");
|
||||
|
||||
FilterBasedLdapUserSearch userSearch = new FilterBasedLdapUserSearch();
|
||||
userSearch.setSearchBase("ou=people");
|
||||
userSearch.setSearchFilter("(cn={0})");
|
||||
userSearch.setInitialDirContextFactory(dirCtxFactory);
|
||||
userSearch.afterPropertiesSet();
|
||||
|
||||
authenticator.setUserSearch(userSearch);
|
||||
|
||||
authenticator.afterPropertiesSet();
|
||||
|
||||
DefaultLdapAuthoritiesPopulator populator;
|
||||
populator = new DefaultLdapAuthoritiesPopulator();
|
||||
populator.setRolePrefix("ROLE_");
|
||||
populator.setInitialDirContextFactory(dirCtxFactory);
|
||||
populator.setGroupSearchBase("ou=groups");
|
||||
populator.afterPropertiesSet();
|
||||
|
||||
ldapProvider.setLdapAuthoritiesPopulator(populator);
|
||||
ldapProvider.setAuthenticator(authenticator);
|
||||
Authentication auth = ldapProvider.authenticate(new UsernamePasswordAuthenticationToken("Ben Alex","benspassword"));
|
||||
assertEquals(2, auth.getAuthorities().length);
|
||||
}
|
||||
|
||||
class MockAuthoritiesPopulator implements LdapAuthoritiesPopulator {
|
||||
|
||||
public GrantedAuthority[] getGrantedAuthorities(String userDn, String dn, Attributes userAttributes) {
|
||||
return new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_USER") };
|
||||
}
|
||||
}
|
||||
|
||||
class MockAuthenticator implements LdapAuthenticator {
|
||||
Attributes userAttributes = new BasicAttributes("cn","bob");
|
||||
|
||||
public LdapUserDetails authenticate(String username, String password) {
|
||||
if(username.equals("bob") && password.equals("bobspassword")) {
|
||||
|
||||
return new LdapUserDetails("cn=bob,ou=people,dc=acegisecurity,dc=org", userAttributes);
|
||||
}
|
||||
throw new BadCredentialsException("Authentication of Bob failed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package org.acegisecurity.providers.ldap;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.apache.ldap.server.configuration.MutableServerStartupConfiguration;
|
||||
import org.apache.ldap.server.jndi.ServerContextFactory;
|
||||
|
||||
import javax.naming.Context;
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.NameAlreadyBoundException;
|
||||
import javax.naming.directory.InitialDirContext;
|
||||
import javax.naming.directory.Attributes;
|
||||
import javax.naming.directory.BasicAttributes;
|
||||
import javax.naming.directory.Attribute;
|
||||
import javax.naming.directory.BasicAttribute;
|
||||
import javax.naming.directory.DirContext;
|
||||
import java.io.IOException;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public class LdapTestServer {
|
||||
|
||||
//~ Instance fields ========================================================
|
||||
|
||||
private DirContext serverContext;
|
||||
|
||||
//~ Constructors ================================================================
|
||||
|
||||
public LdapTestServer() {
|
||||
startLdapServer();
|
||||
createManagerUser();
|
||||
}
|
||||
|
||||
//~ Methods ================================================================
|
||||
|
||||
private void startLdapServer() {
|
||||
ApplicationContext factory = new ClassPathXmlApplicationContext( "org/acegisecurity/providers/ldap/apacheds-context.xml");
|
||||
MutableServerStartupConfiguration cfg = ( MutableServerStartupConfiguration ) factory.getBean( "configuration" );
|
||||
ClassPathResource ldifDir = new ClassPathResource("org/acegisecurity/providers/ldap/ldif");
|
||||
|
||||
try {
|
||||
cfg.setLdifDirectory(ldifDir.getFile());
|
||||
} catch (IOException e) {
|
||||
System.err.println("Failed to set LDIF directory for server");
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
Properties env = ( Properties ) factory.getBean( "environment" );
|
||||
|
||||
env.setProperty( Context.PROVIDER_URL, "dc=acegisecurity,dc=org" );
|
||||
env.setProperty( Context.INITIAL_CONTEXT_FACTORY, ServerContextFactory.class.getName() );
|
||||
env.putAll( cfg.toJndiEnvironment() );
|
||||
|
||||
try {
|
||||
serverContext = new InitialDirContext( env );
|
||||
} catch (NamingException e) {
|
||||
System.err.println("Failed to start Apache DS");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void createManagerUser() {
|
||||
Attributes user = new BasicAttributes( "cn", "manager" , true );
|
||||
user.put( "userPassword", "acegisecurity" );
|
||||
Attribute objectClass = new BasicAttribute("objectClass");
|
||||
user.put( objectClass );
|
||||
objectClass.add( "top" );
|
||||
objectClass.add( "person" );
|
||||
objectClass.add( "organizationalPerson" );
|
||||
objectClass.add( "inetOrgPerson" );
|
||||
user.put( "sn", "Manager" );
|
||||
user.put( "cn", "manager" );
|
||||
try {
|
||||
serverContext.createSubcontext("cn=manager", user );
|
||||
} catch(NameAlreadyBoundException ignore) {
|
||||
System.out.println("Manager user already exists.");
|
||||
} catch (NamingException ne) {
|
||||
System.err.println("Failed to create manager user.");
|
||||
ne.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public DirContext getServerContext() {
|
||||
return serverContext;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
new LdapTestServer();
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
package org.acegisecurity.providers.ldap.authenticator;
|
||||
|
||||
import org.acegisecurity.providers.ldap.DefaultInitialDirContextFactory;
|
||||
import org.acegisecurity.providers.ldap.LdapUserDetails;
|
||||
import org.acegisecurity.providers.ldap.AbstractLdapServerTestCase;
|
||||
import org.acegisecurity.BadCredentialsException;
|
||||
|
||||
/**
|
||||
* Tests {@link BindAuthenticator}.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public class BindAuthenticatorTests extends AbstractLdapServerTestCase {
|
||||
|
||||
private DefaultInitialDirContextFactory dirCtxFactory;
|
||||
private BindAuthenticator authenticator;
|
||||
|
||||
public void setUp() throws Exception {
|
||||
// Connection information
|
||||
dirCtxFactory = new DefaultInitialDirContextFactory();
|
||||
dirCtxFactory.setUrl(PROVIDER_URL);
|
||||
dirCtxFactory.afterPropertiesSet();
|
||||
authenticator = new BindAuthenticator();
|
||||
authenticator.setInitialDirContextFactory(dirCtxFactory);
|
||||
}
|
||||
|
||||
public void testUserDnPatternReturnsCorrectDn() throws Exception {
|
||||
authenticator.setUserDnPattern("cn={0},ou=people");
|
||||
assertEquals("cn=Joe,ou=people,"+ ROOT_DN, authenticator.getUserDn("Joe"));
|
||||
}
|
||||
|
||||
public void testAuthenticationWithCorrectPasswordSucceeds() throws Exception {
|
||||
authenticator.setUserDnPattern("uid={0},ou=people");
|
||||
LdapUserDetails user = authenticator.authenticate("bob","bobspassword");
|
||||
}
|
||||
|
||||
public void testAuthenticationWithWrongPasswordFails() {
|
||||
BindAuthenticator authenticator = new BindAuthenticator();
|
||||
|
||||
authenticator.setInitialDirContextFactory(dirCtxFactory);
|
||||
authenticator.setUserDnPattern("uid={0},ou=people");
|
||||
|
||||
try {
|
||||
authenticator.authenticate("bob","wrongpassword");
|
||||
fail("Shouldn't be able to bind with wrong password");
|
||||
} catch(BadCredentialsException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testAuthenticationWithUserSearch() throws Exception {
|
||||
LdapUserDetails user = new LdapUserDetails("uid=bob,ou=people," + ROOT_DN, null);
|
||||
authenticator.setUserSearch(new MockUserSearch(user));
|
||||
authenticator.afterPropertiesSet();
|
||||
authenticator.authenticate("bob","bobspassword");
|
||||
}
|
||||
|
||||
|
||||
// Apache DS falls apart with unknown DNs.
|
||||
//
|
||||
// public void testAuthenticationWithInvalidUserNameFails() {
|
||||
// BindAuthenticator authenticator = new BindAuthenticator();
|
||||
//
|
||||
// authenticator.setInitialDirContextFactory(dirCtxFactory);
|
||||
// authenticator.setUserDnPattern("cn={0},ou=people");
|
||||
// try {
|
||||
// authenticator.authenticate("Baz","bobspassword");
|
||||
// fail("Shouldn't be able to bind with invalid username");
|
||||
// } catch(BadCredentialsException expected) {
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
package org.acegisecurity.providers.ldap.authenticator;
|
||||
|
||||
import org.acegisecurity.providers.ldap.AbstractLdapServerTestCase;
|
||||
import org.acegisecurity.providers.ldap.DefaultInitialDirContextFactory;
|
||||
import org.acegisecurity.providers.ldap.LdapUserDetails;
|
||||
import org.acegisecurity.userdetails.UsernameNotFoundException;
|
||||
import org.acegisecurity.BadCredentialsException;
|
||||
|
||||
/**
|
||||
* Tests for FilterBasedLdapUserSearch.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public class FilterBasedLdapUserSearchTests extends AbstractLdapServerTestCase {
|
||||
private DefaultInitialDirContextFactory dirCtxFactory;
|
||||
private FilterBasedLdapUserSearch locator;
|
||||
|
||||
public void setUp() throws Exception {
|
||||
dirCtxFactory = new DefaultInitialDirContextFactory();
|
||||
dirCtxFactory.setUrl(PROVIDER_URL);
|
||||
dirCtxFactory.setManagerDn(MANAGER_USER);
|
||||
dirCtxFactory.setManagerPassword(MANAGER_PASSWORD);
|
||||
dirCtxFactory.afterPropertiesSet();
|
||||
locator = new FilterBasedLdapUserSearch();
|
||||
locator.setSearchSubtree(false);
|
||||
locator.setSearchTimeLimit(0);
|
||||
locator.setInitialDirContextFactory(dirCtxFactory);
|
||||
}
|
||||
|
||||
public FilterBasedLdapUserSearchTests(String string) {
|
||||
super(string);
|
||||
}
|
||||
|
||||
public FilterBasedLdapUserSearchTests() {
|
||||
super();
|
||||
}
|
||||
|
||||
public void testBasicSearch() throws Exception {
|
||||
locator.setSearchBase("ou=people");
|
||||
locator.setSearchFilter("(uid={0})");
|
||||
locator.afterPropertiesSet();
|
||||
LdapUserDetails bob = locator.searchForUser("Bob");
|
||||
assertEquals("uid=bob,ou=people,"+ROOT_DN, bob.getDn());
|
||||
}
|
||||
|
||||
public void testSubTreeSearchSucceeds() throws Exception {
|
||||
// Don't set the searchBase, so search from the root.
|
||||
locator.setSearchFilter("(uid={0})");
|
||||
locator.setSearchSubtree(true);
|
||||
locator.afterPropertiesSet();
|
||||
LdapUserDetails bob = locator.searchForUser("Bob");
|
||||
assertEquals("uid=bob,ou=people,"+ROOT_DN, bob.getDn());
|
||||
}
|
||||
|
||||
public void testSearchForInvalidUserFails() {
|
||||
locator.setSearchBase("ou=people");
|
||||
locator.setSearchFilter("(uid={0})");
|
||||
|
||||
try {
|
||||
locator.searchForUser("Joe");
|
||||
fail("Expected UsernameNotFoundException for non-existent user.");
|
||||
} catch (UsernameNotFoundException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testFailsOnMultipleMatches() {
|
||||
locator.setSearchBase("ou=people");
|
||||
locator.setSearchFilter("(cn=*)");
|
||||
|
||||
try {
|
||||
locator.searchForUser("Ignored");
|
||||
fail("Expected exception for multiple search matches.");
|
||||
} catch (BadCredentialsException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
/** Try some funny business with filters. */
|
||||
public void testExtraFilterPartToExcludeBob() {
|
||||
locator.setSearchBase("ou=people");
|
||||
locator.setSearchFilter("(&(cn=*)(!(uid={0})))");
|
||||
|
||||
// Search for bob, get back ben...
|
||||
LdapUserDetails ben = locator.searchForUser("bob");
|
||||
assertEquals("cn=Ben Alex,ou=people,"+ROOT_DN, ben.getDn());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.acegisecurity.providers.ldap.authenticator;
|
||||
|
||||
import org.acegisecurity.providers.ldap.LdapUserDetails;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public class MockUserSearch implements LdapUserSearch {
|
||||
LdapUserDetails user;
|
||||
|
||||
public MockUserSearch(LdapUserDetails user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public LdapUserDetails searchForUser(String username) {
|
||||
return user;
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package org.acegisecurity.providers.ldap.authenticator;
|
||||
|
||||
import org.jmock.Mock;
|
||||
import org.jmock.MockObjectTestCase;
|
||||
import org.acegisecurity.providers.ldap.InitialDirContextFactory;
|
||||
|
||||
import javax.naming.directory.DirContext;
|
||||
import javax.naming.directory.BasicAttributes;
|
||||
import javax.naming.directory.Attributes;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public class PasswordComparisonAuthenticatorMockTests extends MockObjectTestCase {
|
||||
|
||||
public void testLdapCompareIsUsedWhenPasswordIsNotRetrieved() throws Exception {
|
||||
Mock mockCtx = new Mock(DirContext.class);
|
||||
|
||||
PasswordComparisonAuthenticator authenticator = new PasswordComparisonAuthenticator();
|
||||
authenticator.setUserDnPattern("cn={0},ou=people");
|
||||
authenticator.setInitialDirContextFactory(
|
||||
new MockInitialDirContextFactory((DirContext)mockCtx.proxy(),
|
||||
"dc=acegisecurity,dc=org"));
|
||||
// Get the mock to return an empty attribute set
|
||||
mockCtx.expects(atLeastOnce()).method("getNameInNamespace").will(returnValue("dc=acegisecurity,dc=org"));
|
||||
mockCtx.expects(once()).method("getAttributes").with(eq("cn=Bob,ou=people"), NULL).will(returnValue(new BasicAttributes()));
|
||||
// Setup a single return value (i.e. success)
|
||||
Attributes searchResults = new BasicAttributes("", null);
|
||||
mockCtx.expects(once()).method("search").with(eq("cn=Bob,ou=people"),
|
||||
eq("(userPassword={0})"), NOT_NULL, NOT_NULL).will(returnValue(searchResults.getAll()));
|
||||
mockCtx.expects(once()).method("close");
|
||||
authenticator.authenticate("Bob", "bobspassword");
|
||||
}
|
||||
|
||||
class MockInitialDirContextFactory implements InitialDirContextFactory {
|
||||
DirContext ctx;
|
||||
String baseDn;
|
||||
|
||||
public MockInitialDirContextFactory(DirContext ctx, String baseDn) {
|
||||
this.baseDn = baseDn;
|
||||
this.ctx = ctx;
|
||||
}
|
||||
|
||||
public DirContext newInitialDirContext() {
|
||||
return ctx;
|
||||
}
|
||||
|
||||
public DirContext newInitialDirContext(String username, String password) {
|
||||
return ctx;
|
||||
}
|
||||
|
||||
public String getRootDn() {
|
||||
return baseDn;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
package org.acegisecurity.providers.ldap.authenticator;
|
||||
|
||||
import org.acegisecurity.providers.ldap.DefaultInitialDirContextFactory;
|
||||
import org.acegisecurity.providers.ldap.LdapUserDetails;
|
||||
import org.acegisecurity.providers.ldap.AbstractLdapServerTestCase;
|
||||
import org.acegisecurity.providers.encoding.PlaintextPasswordEncoder;
|
||||
import org.acegisecurity.BadCredentialsException;
|
||||
import org.acegisecurity.userdetails.UsernameNotFoundException;
|
||||
|
||||
import javax.naming.directory.BasicAttributes;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public class PasswordComparisonAuthenticatorTests extends AbstractLdapServerTestCase {
|
||||
private DefaultInitialDirContextFactory dirCtxFactory;
|
||||
private PasswordComparisonAuthenticator authenticator;
|
||||
|
||||
public void setUp() throws Exception {
|
||||
// Connection information
|
||||
dirCtxFactory = new DefaultInitialDirContextFactory();
|
||||
dirCtxFactory.setUrl(PROVIDER_URL);
|
||||
dirCtxFactory.setManagerDn(MANAGER_USER);
|
||||
dirCtxFactory.setManagerPassword(MANAGER_PASSWORD);
|
||||
dirCtxFactory.afterPropertiesSet();
|
||||
authenticator = new PasswordComparisonAuthenticator();
|
||||
authenticator.setInitialDirContextFactory(dirCtxFactory);
|
||||
authenticator.setUserDnPattern("uid={0},ou=people");
|
||||
}
|
||||
|
||||
public void tearDown() {
|
||||
// com.sun.jndi.ldap.LdapPoolManager.showStats(System.out);
|
||||
}
|
||||
|
||||
public void testLdapCompareSucceedsWithCorrectPassword() {
|
||||
// Don't retrieve the password
|
||||
authenticator.setUserAttributes(new String[] {"cn", "sn"});
|
||||
// Bob has a plaintext password.
|
||||
authenticator.setPasswordEncoder(new PlaintextPasswordEncoder());
|
||||
authenticator.authenticate("Bob", "bobspassword");
|
||||
}
|
||||
|
||||
public void testLdapCompareSucceedsWithShaEncodedPassword() {
|
||||
authenticator = new PasswordComparisonAuthenticator();
|
||||
authenticator.setInitialDirContextFactory(dirCtxFactory);
|
||||
authenticator.setUserDnPattern("cn={0},ou=people");
|
||||
// Don't retrieve the password
|
||||
authenticator.setUserAttributes(new String[] {"cn", "sn"});
|
||||
authenticator.authenticate("Ben Alex", "benspassword");
|
||||
}
|
||||
|
||||
public void testPasswordEncoderCantBeNull() {
|
||||
try {
|
||||
authenticator.setPasswordEncoder(null);
|
||||
fail("Password encoder can't be null");
|
||||
} catch(IllegalArgumentException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testLdapPasswordCompareFailsWithWrongPassword() {
|
||||
// Don't retrieve the password
|
||||
authenticator.setUserAttributes(new String[] {"cn", "sn"});
|
||||
|
||||
try {
|
||||
authenticator.authenticate("Bob", "wrongpassword");
|
||||
fail("Authentication should fail with wrong password.");
|
||||
} catch(BadCredentialsException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testLocalPasswordComparisonSucceedsWithCorrectPassword() {
|
||||
authenticator.authenticate("Bob", "bobspassword");
|
||||
}
|
||||
|
||||
public void testLocalCompareSucceedsWithShaEncodedPassword() {
|
||||
authenticator = new PasswordComparisonAuthenticator();
|
||||
authenticator.setInitialDirContextFactory(dirCtxFactory);
|
||||
authenticator.setUserDnPattern("cn={0},ou=people");
|
||||
authenticator.authenticate("Ben Alex", "benspassword");
|
||||
}
|
||||
|
||||
public void testLocalPasswordComparisonFailsWithWrongPassword() {
|
||||
try {
|
||||
authenticator.authenticate("Bob", "wrongpassword");
|
||||
fail("Authentication should fail with wrong password.");
|
||||
} catch(BadCredentialsException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testAllAttributesAreRetrivedByDefault() {
|
||||
LdapUserDetails user = authenticator.authenticate("Bob", "bobspassword");
|
||||
System.out.println(user.getAttributes().toString());
|
||||
assertEquals("User should have 5 attributes", 5, user.getAttributes().size());
|
||||
|
||||
}
|
||||
|
||||
public void testOnlySpecifiedAttributesAreRetrieved() throws Exception {
|
||||
authenticator.setUserAttributes(new String[] {"cn", "sn"});
|
||||
authenticator.setPasswordEncoder(new PlaintextPasswordEncoder());
|
||||
LdapUserDetails user = authenticator.authenticate("Bob", "bobspassword");
|
||||
assertEquals("Should have retrieved 2 attributes (cn, sn)",2, user.getAttributes().size());
|
||||
assertEquals("Bob Hamilton", user.getAttributes().get("cn").get());
|
||||
assertEquals("Hamilton", user.getAttributes().get("sn").get());
|
||||
}
|
||||
|
||||
public void testUseOfDifferentPasswordAttribute() {
|
||||
authenticator.setPasswordAttributeName("sn");
|
||||
authenticator.authenticate("Bob", "Hamilton");
|
||||
}
|
||||
|
||||
public void testWithUserSearch() {
|
||||
LdapUserDetails user = new LdapUserDetails("uid=Bob,ou=people" + ROOT_DN,
|
||||
new BasicAttributes("userPassword","bobspassword"));
|
||||
authenticator.setUserDnPattern(null);
|
||||
assertNull(authenticator.getUserDnPattern());
|
||||
assertNull(authenticator.getUserDn("Bob"));
|
||||
authenticator.setUserSearch(new MockUserSearch(user));
|
||||
authenticator.authenticate("ShouldntBeUsed","bobspassword");
|
||||
}
|
||||
|
||||
public void testFailedSearchGivesUserNotFoundException() throws Exception {
|
||||
authenticator.setUserDnPattern(null);
|
||||
authenticator.setUserSearch(new MockUserSearch(null));
|
||||
authenticator.afterPropertiesSet();
|
||||
|
||||
try {
|
||||
authenticator.authenticate("Joe","password");
|
||||
fail("Expected exception on failed user search");
|
||||
} catch (UsernameNotFoundException expected) {
|
||||
}
|
||||
}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
package org.acegisecurity.providers.ldap.populator;
|
||||
|
||||
import javax.naming.directory.Attributes;
|
||||
import javax.naming.directory.BasicAttributes;
|
||||
import javax.naming.directory.BasicAttribute;
|
||||
|
||||
import org.acegisecurity.GrantedAuthority;
|
||||
import org.acegisecurity.providers.ldap.AbstractLdapServerTestCase;
|
||||
import org.acegisecurity.providers.ldap.DefaultInitialDirContextFactory;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public class DefaultLdapAuthoritiesPopulatorTests extends AbstractLdapServerTestCase {
|
||||
private DefaultInitialDirContextFactory dirCtxFactory;
|
||||
private DefaultLdapAuthoritiesPopulator populator;
|
||||
|
||||
public void setUp() {
|
||||
dirCtxFactory = new DefaultInitialDirContextFactory();
|
||||
dirCtxFactory.setUrl(PROVIDER_URL);
|
||||
dirCtxFactory.setManagerDn(MANAGER_USER);
|
||||
dirCtxFactory.setManagerPassword(MANAGER_PASSWORD);
|
||||
|
||||
populator = new DefaultLdapAuthoritiesPopulator();
|
||||
populator.setRolePrefix("ROLE_");
|
||||
}
|
||||
|
||||
public void testCtxFactoryMustBeSetIfSearchBaseIsSet() throws Exception {
|
||||
populator.setGroupSearchBase("");
|
||||
|
||||
try {
|
||||
populator.afterPropertiesSet();
|
||||
fail("expected exception.");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testUserAttributeMappingToRoles() {
|
||||
populator.setUserRoleAttributes(new String[] {"userRole", "otherUserRole"});
|
||||
populator.getUserRoleAttributes();
|
||||
|
||||
Attributes userAttrs = new BasicAttributes();
|
||||
BasicAttribute attr = new BasicAttribute("userRole", "role1");
|
||||
attr.add("role2");
|
||||
userAttrs.put(attr);
|
||||
attr = new BasicAttribute("otherUserRole", "role3");
|
||||
attr.add("role2"); // duplicate
|
||||
userAttrs.put(attr);
|
||||
|
||||
GrantedAuthority[] authorities = populator.getGrantedAuthorities("Ignored", "Ignored", userAttrs);
|
||||
assertEquals("User should have three roles", 3, authorities.length);
|
||||
}
|
||||
|
||||
public void testGroupSearch() throws Exception {
|
||||
populator.setInitialDirContextFactory(dirCtxFactory);
|
||||
populator.setGroupSearchBase("ou=groups");
|
||||
populator.setGroupRoleAttribute("ou");
|
||||
populator.setSearchSubtree(true);
|
||||
populator.setSearchSubtree(false);
|
||||
populator.setConvertToUpperCase(true);
|
||||
populator.setGroupSearchFilter("member={0}");
|
||||
populator.afterPropertiesSet();
|
||||
|
||||
GrantedAuthority[] authorities = populator.getGrantedAuthorities("Ben", "cn=Ben Alex,ou=people,"+ROOT_DN, new BasicAttributes());
|
||||
assertEquals("Should have 2 roles", 2, authorities.length);
|
||||
Set roles = new HashSet();
|
||||
roles.add(authorities[0].toString());
|
||||
roles.add(authorities[1].toString());
|
||||
assertTrue(roles.contains("ROLE_DEVELOPER"));
|
||||
assertTrue(roles.contains("ROLE_MANAGER"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
|
||||
"http://www.springframework.org/dtd/spring-beans.dtd">
|
||||
|
||||
<beans>
|
||||
<!-- JNDI environment variable -->
|
||||
<bean id="environment" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
|
||||
<property name="properties">
|
||||
<props>
|
||||
<!--prop key="asn.1.berlib.provider">org.apache.ldap.common.berlib.asn1.SnickersProvider</prop -->
|
||||
<!--prop key="asn.1.berlib.provider">org.apache.asn1new.ldap.TwixProvider</prop-->
|
||||
<prop key="java.naming.security.authentication">simple</prop>
|
||||
<prop key="java.naming.security.principal">uid=admin,ou=system</prop>
|
||||
<prop key="java.naming.security.credentials">secret</prop>
|
||||
<prop key="java.naming.ldap.attributes.binary">
|
||||
photo personalSignature audio jpegPhoto javaSerializedData userPassword
|
||||
userCertificate cACertificate authorityRevocationList certificateRevocationList
|
||||
crossCertificatePair x500UniqueIdentifier krb5Key
|
||||
</prop>
|
||||
</props>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<!-- StartupConfiguration to start ApacheDS -->
|
||||
<bean id="configuration" class="org.apache.ldap.server.configuration.MutableServerStartupConfiguration">
|
||||
<property name="workingDirectory"><value>${java.io.tmpdir}/apache_ds</value></property>
|
||||
<property name="allowAnonymousAccess"><value>true</value></property>
|
||||
<property name="accessControlEnabled"><value>false</value></property>
|
||||
<property name="ldapPort"><value>10389</value></property>
|
||||
<property name="contextPartitionConfigurations">
|
||||
<set>
|
||||
<ref bean="acegiPartitionConfiguration"/>
|
||||
</set>
|
||||
</property>
|
||||
|
||||
<!-- Bootstrap schemas -->
|
||||
<!-- <property name="bootstrapSchemas">
|
||||
<set>
|
||||
<bean class="org.apache.ldap.server.schema.bootstrap.AutofsSchema"/>
|
||||
<bean class="org.apache.ldap.server.schema.bootstrap.CorbaSchema"/>
|
||||
<bean class="org.apache.ldap.server.schema.bootstrap.CoreSchema"/>
|
||||
|
||||
|
||||
</set>
|
||||
</property>
|
||||
-->
|
||||
<!-- Interceptor configurations -->
|
||||
<!--property name="interceptorConfigurations">
|
||||
<list>
|
||||
<bean class="org.apache.ldap.server.configuration.MutableInterceptorConfiguration">
|
||||
<property name="name"><value>normalizationService</value></property>
|
||||
<property name="interceptor">
|
||||
<bean class="org.apache.ldap.server.normalization.NormalizationService" />
|
||||
</property>
|
||||
</bean>
|
||||
<bean class="org.apache.ldap.server.configuration.MutableInterceptorConfiguration">
|
||||
<property name="name"><value>authenticationService</value></property>
|
||||
<property name="interceptor">
|
||||
<bean class="org.apache.ldap.server.authn.AuthenticationService" />
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
|
||||
</list>
|
||||
</property>
|
||||
-->
|
||||
</bean>
|
||||
|
||||
<!-- Additional ContextPartitionConfiguration -->
|
||||
<bean id="acegiPartitionConfiguration" class="org.apache.ldap.server.configuration.MutableDirectoryPartitionConfiguration">
|
||||
<property name="name"><value>acegisecurity</value></property>
|
||||
<property name="suffix"><value>dc=acegisecurity,dc=org</value></property>
|
||||
<property name="indexedAttributes">
|
||||
<set>
|
||||
<value>objectClass</value>
|
||||
<value>ou</value>
|
||||
<value>uid</value>
|
||||
</set>
|
||||
</property>
|
||||
<property name="contextEntry">
|
||||
<value>
|
||||
objectClass: top
|
||||
objectClass: domain
|
||||
objectClass: extensibleObject
|
||||
dc: apache
|
||||
</value>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<!-- Custom editors required to launch ApacheDS -->
|
||||
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
|
||||
<property name="customEditors">
|
||||
<map>
|
||||
<entry key="javax.naming.directory.Attributes">
|
||||
<bean class="org.apache.ldap.server.configuration.AttributesPropertyEditor"/>
|
||||
</entry>
|
||||
</map>
|
||||
</property>
|
||||
</bean>
|
||||
</beans>
|
||||
@@ -0,0 +1,54 @@
|
||||
version: 1
|
||||
dn: dc=acegisecurity,dc=org
|
||||
objectClass: dcObject
|
||||
objectClass: organization
|
||||
dc: acegisecurity
|
||||
description: Acegi Security (Test LDAP DIT)
|
||||
o: Monkey Machine Ltd.
|
||||
|
||||
dn: ou=people,dc=acegisecurity,dc=org
|
||||
objectClass: organizationalUnit
|
||||
description: All people in organisation
|
||||
ou: people
|
||||
|
||||
dn: cn=Ben Alex,ou=people,dc=acegisecurity,dc=org
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: organizationalPerson
|
||||
objectClass: person
|
||||
objectClass: top
|
||||
cn: Ben Alex
|
||||
sn: Alex
|
||||
uid: Ben
|
||||
userPassword:: e3NoYX1uRkNlYldqeGZhTGJISEcxUWs1VVU0dHJidlE9
|
||||
|
||||
dn: uid=bob,ou=people,dc=acegisecurity,dc=org
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: organizationalPerson
|
||||
objectClass: person
|
||||
objectClass: top
|
||||
cn: Bob Hamilton
|
||||
sn: Hamilton
|
||||
uid: bob
|
||||
userPassword:: Ym9ic3Bhc3N3b3Jk
|
||||
|
||||
dn: ou=groups,dc=acegisecurity,dc=org
|
||||
objectClass: top
|
||||
objectClass: organizationalUnit
|
||||
ou: groups
|
||||
|
||||
dn: cn=developers,ou=groups,dc=acegisecurity,dc=org
|
||||
objectClass: groupOfNames
|
||||
objectClass: top
|
||||
cn: developers
|
||||
description: Acegi Security Developers
|
||||
member: uid=bob,ou=people,dc=acegisecurity,dc=org
|
||||
member: cn=Ben Alex,ou=people,dc=acegisecurity,dc=org
|
||||
o: Acegi Security System for Spring
|
||||
ou: developer
|
||||
|
||||
dn: cn=managers,ou=groups,dc=acegisecurity,dc=org
|
||||
objectClass: groupOfNames
|
||||
objectClass: top
|
||||
cn: managers
|
||||
member: cn=Ben Alex,ou=people,dc=acegisecurity,dc=org
|
||||
ou: manager
|
||||
Reference in New Issue
Block a user