Bläddra i källkod

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

Luke Taylor 19 år sedan
förälder
incheckning
bf4fca9126

+ 67 - 0
core/src/test/java/org/acegisecurity/ldap/AbstractLdapServerTestCase.java

@@ -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;
+    }
+}

+ 173 - 0
core/src/test/java/org/acegisecurity/ldap/DefaultInitialDirContextFactoryTests.java

@@ -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) {
+        }
+    }
+}

+ 230 - 0
core/src/test/java/org/acegisecurity/ldap/LdapTestServer.java

@@ -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();
+    }
+
+}

+ 88 - 0
core/src/test/java/org/acegisecurity/ldap/search/FilterBasedLdapUserSearchTests.java

@@ -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());
+    }
+}