Move non security-specific LDAP classes to org.acegisecurity.ldap package

This commit is contained in:
Luke Taylor 2006-04-16 14:26:46 +00:00
parent 7c69668589
commit bf4fca9126
4 changed files with 558 additions and 0 deletions

View File

@ -0,0 +1,67 @@
/* Copyright 2004, 2005 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.acegisecurity.ldap;
import junit.framework.TestCase;
import java.util.Hashtable;
import org.apache.ldap.server.jndi.CoreContextFactory;
/**
* @author Luke Taylor
* @version $Id$
*/
public abstract class AbstractLdapServerTestCase extends TestCase {
private static final String ROOT_DN = "dc=acegisecurity,dc=org";
protected static final String MANAGER_USER = "cn=manager," + ROOT_DN;
protected static final String MANAGER_PASSWORD = "acegisecurity";
// External server config
// private static final String PROVIDER_URL = "ldap://monkeymachine:389/"+ROOT_DN;
// private static final String CONTEXT_FACTORY = "com.sun.jndi.ldap.LdapCtxFactory";
// private static final Hashtable EXTRA_ENV = new Hashtable();
// Embedded (non-networked) server config
private static final LdapTestServer SERVER = new LdapTestServer();
private static final String PROVIDER_URL = ROOT_DN;
private static final String CONTEXT_FACTORY = CoreContextFactory.class.getName();
private static final Hashtable EXTRA_ENV = SERVER.getConfiguration().toJndiEnvironment();
protected AbstractLdapServerTestCase() {
}
protected AbstractLdapServerTestCase(String string) {
super(string);
}
private DefaultInitialDirContextFactory idf;
public final void setUp() {
idf = new DefaultInitialDirContextFactory(PROVIDER_URL);
idf.setInitialContextFactory(CONTEXT_FACTORY);
idf.setExtraEnvVars(EXTRA_ENV);
onSetUp();
}
protected void onSetUp() {}
protected DefaultInitialDirContextFactory getInitialCtxFactory() {
return idf;
}
}

View File

@ -0,0 +1,173 @@
package org.acegisecurity.ldap;
import javax.naming.Context;
import javax.naming.directory.DirContext;
import java.util.Hashtable;
import org.acegisecurity.BadCredentialsException;
/**
* Tests {@link org.acegisecurity.ldap.DefaultInitialDirContextFactory}.
*
* @author Luke Taylor
* @version $Id$
*/
public class DefaultInitialDirContextFactoryTests extends AbstractLdapServerTestCase {
DefaultInitialDirContextFactory idf;
public void onSetUp() {
idf = getInitialCtxFactory();
}
// public void testNonLdapUrlIsRejected() throws Exception {
// DefaultInitialDirContextFactory idf = new DefaultInitialDirContextFactory();
//
// idf.setUrl("http://acegisecurity.org/dc=acegisecurity,dc=org");
// idf.setInitialContextFactory(CoreContextFactory.class.getName());
//
// try {
// idf.afterPropertiesSet();
// fail("Expected exception for non 'ldap://' URL");
// } catch(IllegalArgumentException expected) {
// }
// }
public void testServiceLocationUrlIsSupported() {
idf = new DefaultInitialDirContextFactory("ldap:///dc=acegisecurity,dc=org");
assertEquals("dc=acegisecurity,dc=org", idf.getRootDn());
}
public void testSecureLdapUrlIsSupported() {
idf = new DefaultInitialDirContextFactory("ldaps://localhost/dc=acegisecurity,dc=org");
assertEquals("dc=acegisecurity,dc=org", idf.getRootDn());
}
public void testConnectionFailure() throws Exception {
// Use the wrong port
idf = new DefaultInitialDirContextFactory("ldap://localhost:60389");
idf.setInitialContextFactory("com.sun.jndi.ldap.LdapCtxFactory");
Hashtable env = new Hashtable();
env.put("com.sun.jndi.ldap.connect.timeout", "200");
idf.setExtraEnvVars(env);
try {
idf.newInitialDirContext();
fail("Connection succeeded unexpectedly");
} catch(LdapDataAccessException expected) {
}
}
public void testAnonymousBindSucceeds() throws Exception {
DirContext ctx = idf.newInitialDirContext();
// Connection pooling should be set by default for anon users.
// Can't rely on this property being there with embedded server
// assertEquals("true",ctx.getEnvironment().get("com.sun.jndi.ldap.connect.pool"));
ctx.close();
}
public void testBindAsManagerSucceeds() throws Exception {
idf.setManagerPassword(MANAGER_PASSWORD);
idf.setManagerDn(MANAGER_USER);
DirContext ctx = idf.newInitialDirContext();
// Can't rely on this property being there with embedded server
// assertEquals("true",ctx.getEnvironment().get("com.sun.jndi.ldap.connect.pool"));
ctx.close();
}
public void testBindAsManagerFailsIfNoPasswordSet() throws Exception {
idf.setManagerDn(MANAGER_USER);
DirContext ctx = null;
try {
ctx = idf.newInitialDirContext();
fail("Binding with no manager password should fail.");
// Can't rely on this property being there with embedded server
// assertEquals("true",ctx.getEnvironment().get("com.sun.jndi.ldap.connect.pool"));
} catch(BadCredentialsException expected) {
}
LdapUtils.closeContext(ctx);
}
public void testInvalidPasswordCausesBadCredentialsException() throws Exception {
idf.setManagerDn(MANAGER_USER);
idf.setManagerPassword("wrongpassword");
DirContext ctx = null;
try {
ctx = idf.newInitialDirContext();
fail("Binding with wrong credentials should fail.");
} catch(BadCredentialsException expected) {
}
LdapUtils.closeContext(ctx);
}
public void testConnectionAsSpecificUserSucceeds() throws Exception {
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"));
// com.sun.jndi.ldap.LdapPoolManager.showStats(System.out);
ctx.close();
}
public void testEnvironment() {
idf = new DefaultInitialDirContextFactory("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 {
idf = new DefaultInitialDirContextFactory("ldap://acegisecurity.org/dc=acegisecurity,dc=org");
assertEquals("dc=acegisecurity,dc=org", idf.getRootDn());
// Check with an empty root
idf = new DefaultInitialDirContextFactory("ldap://acegisecurity.org/");
assertEquals("", idf.getRootDn());
// Empty root without trailing slash
idf = new DefaultInitialDirContextFactory("ldap://acegisecurity.org");
assertEquals("", idf.getRootDn());
}
public void testMultipleProviderUrlsAreAccepted() {
idf = new DefaultInitialDirContextFactory("ldaps://acegisecurity.org/dc=acegisecurity,dc=org " +
"ldap://monkeymachine.co.uk/dc=acegisecurity,dc=org");
}
public void testMultipleProviderUrlsWithDifferentRootsAreRejected() {
try {
idf = new DefaultInitialDirContextFactory("ldap://acegisecurity.org/dc=acegisecurity,dc=org " +
"ldap://monkeymachine.co.uk/dc=someotherplace,dc=org");
fail("Different root DNs should cause an exception");
} catch (IllegalArgumentException expected) {
}
}
}

View File

@ -0,0 +1,230 @@
/* Copyright 2004, 2005 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.acegisecurity.ldap;
import org.apache.ldap.server.configuration.MutableDirectoryPartitionConfiguration;
import org.apache.ldap.server.configuration.MutableStartupConfiguration;
import org.apache.ldap.server.configuration.Configuration;
import org.apache.ldap.server.jndi.CoreContextFactory;
import org.acegisecurity.ldap.LdapUtils;
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.util.Properties;
import java.util.Set;
import java.util.HashSet;
import java.io.File;
/**
* An embedded LDAP test server, complete with test data for running the
* unit tests against.
*
* @author Luke Taylor
* @version $Id$
*/
public class LdapTestServer {
//~ Instance fields ========================================================
private DirContext serverContext;
private MutableStartupConfiguration cfg;
// Move the working dir to the temp directory
private File workingDir = new File( System.getProperty("java.io.tmpdir")
+ File.separator + "apacheds-work" );
//~ Constructors ================================================================
/**
* Starts up and configures ApacheDS.
*/
public LdapTestServer() {
startLdapServer();
createManagerUser();
initTestData();
}
//~ Methods ================================================================
private void startLdapServer() {
cfg = new MutableStartupConfiguration();
((MutableStartupConfiguration)cfg).setWorkingDirectory(workingDir);
System.out.println("Working directory is " + workingDir.getAbsolutePath());
initConfiguration();
Properties env = new Properties();
env.setProperty( Context.PROVIDER_URL, "dc=acegisecurity,dc=org" );
env.setProperty( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.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 initTestData() {
createOu("people");
createOu("groups");
createUser("bob","Bob Hamilton", "bobspassword");
createUser("ben","Ben Alex", "{SHA}nFCebWjxfaLbHHG1Qk5UU4trbvQ=");
String[] developers = new String[]
{"uid=ben,ou=people,dc=acegisecurity,dc=org", "uid=bob,ou=people,dc=acegisecurity,dc=org"};
createGroup("developers","developer",developers);
createGroup("managers","manager", new String[] { developers[0]});
}
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 void createUser( String uid, String cn, String password ) {
Attributes user = new BasicAttributes("uid", uid);
user.put( "cn", cn);
user.put( "userPassword", LdapUtils.getUtf8Bytes(password) );
Attribute objectClass = new BasicAttribute( "objectClass" );
user.put( objectClass );
objectClass.add( "top" );
objectClass.add( "person" );
objectClass.add( "organizationalPerson" );
objectClass.add( "inetOrgPerson" );
user.put( "sn", uid );
try {
serverContext.createSubcontext( "uid="+uid+",ou=people", user );
} catch(NameAlreadyBoundException ignore) {
// System.out.println(" user " + uid + " already exists.");
} catch (NamingException ne) {
System.err.println("Failed to create user.");
ne.printStackTrace();
}
}
public void createOu(String name) {
Attributes ou = new BasicAttributes( "ou", name );
Attribute objectClass = new BasicAttribute( "objectClass" );
objectClass.add("top");
objectClass.add("organizationalUnit");
ou.put(objectClass);
try {
serverContext.createSubcontext( "ou="+name, ou);
} catch(NameAlreadyBoundException ignore) {
// System.out.println(" ou " + name + " already exists.");
} catch (NamingException ne) {
System.err.println("Failed to create ou.");
ne.printStackTrace();
}
}
public void createGroup( String cn, String ou, String[] memberDns ) {
Attributes group = new BasicAttributes("cn", cn);
Attribute members = new BasicAttribute("member");
Attribute orgUnit = new BasicAttribute("ou", ou);
for(int i=0; i < memberDns.length; i++) {
members.add(memberDns[i]);
}
Attribute objectClass = new BasicAttribute( "objectClass" );
objectClass.add( "top" );
objectClass.add( "groupOfNames" );
group.put(objectClass);
group.put(members);
group.put(orgUnit);
try {
serverContext.createSubcontext( "cn="+cn+",ou=groups", group );
} catch(NameAlreadyBoundException ignore) {
// System.out.println(" group " + cn + " already exists.");
} catch (NamingException ne) {
System.err.println("Failed to create group.");
ne.printStackTrace();
}
}
private void initConfiguration() {
// Create the partition for the acegi tests
MutableDirectoryPartitionConfiguration acegiDit = new MutableDirectoryPartitionConfiguration();
acegiDit.setName("acegisecurity");
acegiDit.setSuffix("dc=acegisecurity,dc=org");
BasicAttributes attributes = new BasicAttributes();
BasicAttribute objectClass = new BasicAttribute("objectClass");
objectClass.add("top");
objectClass.add("domain");
objectClass.add("extensibleObject");
attributes.put(objectClass);
acegiDit.setContextEntry(attributes);
Set indexedAttrs = new HashSet();
indexedAttrs.add("objectClass");
indexedAttrs.add("uid");
indexedAttrs.add("cn");
indexedAttrs.add("ou");
indexedAttrs.add("member");
acegiDit.setIndexedAttributes(indexedAttrs);
Set partitions = new HashSet();
partitions.add(acegiDit);
cfg.setContextPartitionConfigurations(partitions);
}
public Configuration getConfiguration() {
return cfg;
}
public static void main(String[] args) {
LdapTestServer server = new LdapTestServer();
}
}

View File

@ -0,0 +1,88 @@
package org.acegisecurity.ldap.search;
import org.acegisecurity.ldap.AbstractLdapServerTestCase;
import org.acegisecurity.ldap.DefaultInitialDirContextFactory;
import org.acegisecurity.ldap.LdapUserInfo;
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;
public void onSetUp() {
dirCtxFactory = getInitialCtxFactory();
dirCtxFactory.setManagerDn(MANAGER_USER);
dirCtxFactory.setManagerPassword(MANAGER_PASSWORD);
}
public FilterBasedLdapUserSearchTests(String string) {
super(string);
}
public FilterBasedLdapUserSearchTests() {
super();
}
public void testBasicSearch() throws Exception {
FilterBasedLdapUserSearch locator =
new FilterBasedLdapUserSearch("ou=people", "(uid={0})", dirCtxFactory);
LdapUserInfo bob = locator.searchForUser("bob");
locator.setSearchSubtree(false);
locator.setSearchTimeLimit(0);
// name is wrong with embedded apacheDS
// assertEquals("uid=bob,ou=people,"+ROOT_DN, bob.getDn());
}
public void testSubTreeSearchSucceeds() throws Exception {
// Don't set the searchBase, so search from the root.
FilterBasedLdapUserSearch locator =
new FilterBasedLdapUserSearch("", "(cn={0})", dirCtxFactory);
locator.setSearchSubtree(true);
LdapUserInfo ben = locator.searchForUser("Ben Alex");
// assertEquals("uid=ben,ou=people,"+ROOT_DN, bob.getDn());
}
public void testSearchForInvalidUserFails() {
FilterBasedLdapUserSearch locator =
new FilterBasedLdapUserSearch("ou=people", "(uid={0})", dirCtxFactory);
try {
locator.searchForUser("Joe");
fail("Expected UsernameNotFoundException for non-existent user.");
} catch (UsernameNotFoundException expected) {
}
}
public void testFailsOnMultipleMatches() {
FilterBasedLdapUserSearch locator =
new FilterBasedLdapUserSearch("ou=people", "(cn=*)", dirCtxFactory);
try {
locator.searchForUser("Ignored");
fail("Expected exception for multiple search matches.");
} catch (BadCredentialsException expected) {
}
}
// Try some funny business with filters.
public void testExtraFilterPartToExcludeBob() throws Exception {
FilterBasedLdapUserSearch locator =
new FilterBasedLdapUserSearch("ou=people",
"(&(cn=*)(!(|(uid={0})(uid=marissa))))",
dirCtxFactory);
// Search for bob, get back ben...
LdapUserInfo ben = locator.searchForUser("bob");
String cn = (String)ben.getAttributes().get("cn").get();
assertEquals("Ben Alex", cn);
// assertEquals("uid=ben,ou=people,"+ROOT_DN, ben.getDn());
}
}