瀏覽代碼

SEC-1075: Update the embedded LDAP server to use Apache DS 1.5. Updated to use the new 1.5.5 release for the embedded server.

Luke Taylor 16 年之前
父節點
當前提交
245fc96137

+ 16 - 2
config/pom.xml

@@ -59,6 +59,19 @@
             <groupId>org.springframework</groupId>
             <artifactId>spring-web</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.apache.directory.server</groupId>
+            <artifactId>apacheds-core</artifactId>
+            <version>1.5.5</version>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.directory.server</groupId>
+            <artifactId>apacheds-server-jndi</artifactId>
+            <version>1.5.5</version>
+            <optional>true</optional>
+        </dependency>
+<!--
         <dependency>
             <groupId>org.apache.directory.server</groupId>
             <artifactId>apacheds-core</artifactId>
@@ -69,14 +82,15 @@
             <groupId>org.apache.directory.server</groupId>
             <artifactId>apacheds-server-jndi</artifactId>
             <version>1.0.2</version>
-            <optional>true</optional>            
+            <optional>true</optional>
         </dependency>
         <dependency>
             <groupId>org.apache.mina</groupId>
             <artifactId>mina-core</artifactId>
             <version>1.0.5</version>
-            <optional>true</optional>            
+            <optional>true</optional>
         </dependency>
+-->
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-log4j12</artifactId>

+ 8 - 45
config/src/main/java/org/springframework/security/config/ldap/LdapServerBeanDefinitionParser.java

@@ -1,23 +1,16 @@
 package org.springframework.security.config.ldap;
 
-import javax.naming.directory.Attribute;
-import javax.naming.directory.Attributes;
-import javax.naming.directory.BasicAttribute;
-import javax.naming.directory.BasicAttributes;
-
-import org.springframework.beans.factory.xml.BeanDefinitionParser;
-import org.springframework.beans.factory.xml.ParserContext;
-import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.springframework.beans.factory.config.BeanDefinition;
-import org.springframework.beans.factory.support.RootBeanDefinition;
 import org.springframework.beans.factory.support.BeanDefinitionBuilder;
-import org.springframework.beans.factory.support.ManagedSet;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
+import org.springframework.beans.factory.xml.BeanDefinitionParser;
+import org.springframework.beans.factory.xml.ParserContext;
 import org.springframework.security.config.BeanIds;
 import org.springframework.util.StringUtils;
-
 import org.w3c.dom.Element;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
 
 /**
  * @author Luke Taylor
@@ -97,25 +90,8 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
      *
      * @see ApacheDSContainer
      */
-    @SuppressWarnings("unchecked")
     private RootBeanDefinition createEmbeddedServer(Element element, ParserContext parserContext) {
         Object source = parserContext.extractSource(element);
-        BeanDefinitionBuilder configuration =
-            BeanDefinitionBuilder.rootBeanDefinition("org.apache.directory.server.configuration.MutableServerStartupConfiguration");
-        BeanDefinitionBuilder partition =
-            BeanDefinitionBuilder.rootBeanDefinition("org.apache.directory.server.core.partition.impl.btree.MutableBTreePartitionConfiguration");
-        configuration.getRawBeanDefinition().setSource(source);
-        partition.getRawBeanDefinition().setSource(source);
-
-        Attributes rootAttributes = new BasicAttributes("dc", "springsecurity");
-        Attribute a = new BasicAttribute("objectClass");
-        a.add("top");
-        a.add("domain");
-        a.add("extensibleObject");
-        rootAttributes.put(a);
-
-        partition.addPropertyValue("name", "springsecurity");
-        partition.addPropertyValue("contextEntry", rootAttributes);
 
         String suffix = element.getAttribute(ATT_ROOT_SUFFIX);
 
@@ -123,25 +99,12 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
             suffix = OPT_DEFAULT_ROOT_SUFFIX;
         }
 
-        partition.addPropertyValue("suffix", suffix);
-
-        ManagedSet partitions = new ManagedSet(1);
-        partitions.add(partition.getBeanDefinition());
-
         String port = element.getAttribute(ATT_PORT);
 
         if (!StringUtils.hasText(port)) {
             port = OPT_DEFAULT_PORT;
         }
 
-        configuration.addPropertyValue("ldapPort", port);
-
-        // We shut down the server ourself when the app context is closed so we don't need
-        // the extra shutdown hook from apache DS itself.
-        configuration.addPropertyValue("shutdownHookEnabled", Boolean.FALSE);
-        configuration.addPropertyValue("exitVmOnShutdown", Boolean.FALSE);
-        configuration.addPropertyValue("contextPartitionConfigurations", partitions);
-
         String url = "ldap://127.0.0.1:" + port + "/" + suffix;
 
         BeanDefinitionBuilder contextSource = BeanDefinitionBuilder.rootBeanDefinition(CONTEXT_SOURCE_CLASS);
@@ -151,8 +114,7 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
 
         RootBeanDefinition apacheContainer = new RootBeanDefinition("org.springframework.security.ldap.server.ApacheDSContainer", null, null);
         apacheContainer.setSource(source);
-        apacheContainer.getConstructorArgumentValues().addGenericArgumentValue(configuration.getBeanDefinition());
-        apacheContainer.getConstructorArgumentValues().addGenericArgumentValue(contextSource.getBeanDefinition());
+        apacheContainer.getConstructorArgumentValues().addGenericArgumentValue(suffix);
 
         String ldifs = element.getAttribute(ATT_LDIF_FILE);
         if (!StringUtils.hasText(ldifs)) {
@@ -160,6 +122,7 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
         }
 
         apacheContainer.getConstructorArgumentValues().addGenericArgumentValue(ldifs);
+        apacheContainer.getPropertyValues().addPropertyValue("port", port);
 
         logger.info("Embedded LDAP server bean created for URL: " + url);
 

+ 13 - 3
ldap/pom.xml

@@ -28,21 +28,30 @@
           <artifactId>ldapsdk</artifactId>
           <version>4.1</version>
           <optional>true</optional>
-        </dependency>        
+        </dependency>
+<!--
+        <dependency>
+            <groupId>org.apache.directory.server</groupId>
+            <artifactId>apacheds-all</artifactId>
+            <version>1.5.5</version>
+            <optional>true</optional>
+        </dependency>
+-->
         <dependency>
             <groupId>org.apache.directory.server</groupId>
             <artifactId>apacheds-core</artifactId>
-            <version>1.0.2</version>
+            <version>1.5.5</version>
             <scope>compile</scope>
             <optional>true</optional>
         </dependency>
         <dependency>
             <groupId>org.apache.directory.server</groupId>
             <artifactId>apacheds-server-jndi</artifactId>
-            <version>1.0.2</version>
+            <version>1.5.5</version>
             <scope>compile</scope>
             <optional>true</optional>
         </dependency>
+<!--
         <dependency>
             <groupId>org.apache.mina</groupId>
             <artifactId>mina-core</artifactId>
@@ -50,6 +59,7 @@
             <scope>compile</scope>
             <optional>true</optional>
         </dependency>
+         -->
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-log4j12</artifactId>

+ 109 - 96
ldap/src/main/java/org/springframework/security/ldap/server/ApacheDSContainer.java

@@ -1,31 +1,26 @@
 package org.springframework.security.ldap.server;
 
-import org.springframework.beans.factory.InitializingBean;
-import org.springframework.beans.factory.DisposableBean;
+import java.io.File;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.directory.server.core.DefaultDirectoryService;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
+import org.apache.directory.server.ldap.LdapServer;
+import org.apache.directory.server.protocol.shared.store.LdifFileLoader;
+import org.apache.directory.server.protocol.shared.transport.TcpTransport;
+import org.apache.directory.shared.ldap.exception.LdapNameNotFoundException;
+import org.apache.directory.shared.ldap.name.LdapDN;
 import org.springframework.beans.BeansException;
-import org.springframework.context.ApplicationContextAware;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.InitializingBean;
 import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
 import org.springframework.context.Lifecycle;
 import org.springframework.core.io.Resource;
 import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
-import org.springframework.ldap.core.ContextSource;
 import org.springframework.util.Assert;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.directory.server.configuration.MutableServerStartupConfiguration;
-import org.apache.directory.server.jndi.ServerContextFactory;
-import org.apache.directory.server.protocol.shared.store.LdifFileLoader;
-import org.apache.directory.server.core.configuration.ShutdownConfiguration;
-import org.apache.directory.server.core.DirectoryService;
-
-import javax.naming.Context;
-import javax.naming.InitialContext;
-import javax.naming.NamingException;
-import javax.naming.directory.DirContext;
-import javax.naming.directory.InitialDirContext;
-import java.util.Properties;
-import java.io.File;
-import java.io.IOException;
 
 /**
  * Provides lifecycle services for the embedded apacheDS server defined by the supplied configuration.
@@ -49,18 +44,30 @@ import java.io.IOException;
 public class ApacheDSContainer implements InitializingBean, DisposableBean, Lifecycle, ApplicationContextAware {
     private Log logger = LogFactory.getLog(getClass());
 
-    private MutableServerStartupConfiguration configuration;
+    DefaultDirectoryService service;
+    LdapServer server;
+
     private ApplicationContext ctxt;
     private File workingDir;
 
-    private ContextSource contextSource;
     private boolean running;
     private String ldifResources;
+    private JdbmPartition partition;
+    private String root;
+    private int port = 53389;
 
-    public ApacheDSContainer(MutableServerStartupConfiguration config, ContextSource contextSource, String ldifs) {
-        this.configuration = config;
-        this.contextSource = contextSource;
+    public ApacheDSContainer(String root, String ldifs) throws Exception {
         this.ldifResources = ldifs;
+        service = new DefaultDirectoryService();
+        partition =  new JdbmPartition();
+        partition.setId("rootPartition");
+        partition.setSuffix(root);
+        this.root = root;
+        service.addPartition(partition);
+        service.setExitVmOnShutdown(false);
+        service.setShutdownHookEnabled(false);
+        service.getChangeLog().setEnabled(false);
+        service.setDenormalizeOpAttrsEnabled(true);
     }
 
     public void afterPropertiesSet() throws Exception {
@@ -73,6 +80,10 @@ public class ApacheDSContainer implements InitializingBean, DisposableBean, Life
 
             setWorkingDirectory(new File(apacheWorkDir));
         }
+
+        server = new LdapServer();
+        server.setDirectoryService(service);
+        server.setTransports(new TcpTransport(port));
         start();
     }
 
@@ -84,20 +95,6 @@ public class ApacheDSContainer implements InitializingBean, DisposableBean, Life
         ctxt = applicationContext;
     }
 
-    private boolean deleteDir(File dir) {
-        if (dir.isDirectory()) {
-            String[] children = dir.list();
-            for (int i=0; i < children.length; i++) {
-                boolean success = deleteDir(new File(dir, children[i]));
-                if (!success) {
-                    return false;
-                }
-            }
-        }
-
-        return dir.delete();
-    }
-
     public void setWorkingDirectory(File workingDir) {
         Assert.notNull(workingDir);
 
@@ -112,37 +109,54 @@ public class ApacheDSContainer implements InitializingBean, DisposableBean, Life
 
         this.workingDir = workingDir;
 
-        configuration.setWorkingDirectory(workingDir);
+        service.setWorkingDirectory(workingDir);
+    }
+
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+    public DefaultDirectoryService getService() {
+        return service;
     }
 
-    @SuppressWarnings("unchecked")
     public void start() {
         if (isRunning()) {
             return;
         }
 
-        DirectoryService ds = DirectoryService.getInstance(configuration.getInstanceId());
-
-        if (ds.isStarted()) {
-            throw new IllegalStateException("A DirectoryService with Id '" + configuration.getInstanceId() + "' is already running.");
+        if (service.isStarted()) {
+            throw new IllegalStateException("DirectoryService is already running.");
         }
 
-        logger.info("Starting directory server with Id '" + configuration.getInstanceId() + "'");
-        Properties env = new Properties();
-
-        env.setProperty(Context.INITIAL_CONTEXT_FACTORY, ServerContextFactory.class.getName());
-        env.setProperty(Context.SECURITY_AUTHENTICATION, "simple");
-        env.setProperty(Context.SECURITY_PRINCIPAL, "uid=admin,ou=system");
-        env.setProperty(Context.SECURITY_CREDENTIALS, "secret");
-        env.putAll(configuration.toJndiEnvironment());
-
+        logger.info("Starting directory server...");
         try {
-            new InitialDirContext(env);
-        } catch (NamingException e) {
-            logger.error("Failed to start directory service", e);
+            service.startup();
+            server.start();
+        } catch (Exception e) {
+            logger.error("Server startup failed ", e);
             return;
         }
 
+        try {
+            service.getAdminSession().lookup(partition.getSuffixDn());
+        }
+        catch (LdapNameNotFoundException e) {
+            try {
+                LdapDN dn = new LdapDN(root);
+                Assert.isTrue(root.startsWith("dc="));
+                String dc = root.substring(3,root.indexOf(','));
+                ServerEntry entry = service.newEntry(dn);
+                entry.add("objectClass", "top", "domain", "extensibleObject");
+                entry.add("dc",dc);
+                service.getAdminSession().add( entry );
+            } catch (Exception e1) {
+                logger.error("Failed to create dc entry", e1);
+            }
+        } catch (Exception e) {
+            logger.error("Lookup failed", e);
+        }
+
         running = true;
 
         try {
@@ -152,7 +166,29 @@ public class ApacheDSContainer implements InitializingBean, DisposableBean, Life
         }
     }
 
-    private void importLdifs() throws IOException, NamingException {
+    public void stop() {
+        if (!isRunning()) {
+            return;
+        }
+
+        logger.info("Shutting down directory server ...");
+        try {
+            server.stop();
+            service.shutdown();
+        } catch (Exception e) {
+            logger.error("Shutdown failed", e);
+            return;
+        }
+
+        running = false;
+
+        if (workingDir.exists()) {
+            logger.info("Deleting working directory " + workingDir.getAbsolutePath());
+            deleteDir(workingDir);
+        }
+    }
+
+    private void importLdifs() throws Exception {
         // Import any ldif files
         Resource[] ldifs;
 
@@ -166,51 +202,28 @@ public class ApacheDSContainer implements InitializingBean, DisposableBean, Life
         // Note that we can't just import using the ServerContext returned
         // from starting Apace DS, apparently because of the long-running issue DIRSERVER-169.
         // We need a standard context.
-        DirContext dirContext = contextSource.getReadWriteContext();
+        //DirContext dirContext = contextSource.getReadWriteContext();
 
         if(ldifs != null && ldifs.length > 0) {
-            try {
-                String ldifFile = ldifs[0].getFile().getAbsolutePath();
-                logger.info("Loading LDIF file: " + ldifFile);
-                LdifFileLoader loader = new LdifFileLoader(dirContext, ldifFile);
-                loader.execute();
-            } finally {
-                dirContext.close();
-            }
+            String ldifFile = ldifs[0].getFile().getAbsolutePath();
+            logger.info("Loading LDIF file: " + ldifFile);
+            LdifFileLoader loader = new LdifFileLoader(service.getAdminSession(), ldifFile);
+            loader.execute();
         }
-
     }
 
-    @SuppressWarnings("unchecked")
-    public void stop() {
-        if (!isRunning()) {
-            return;
-        }
-
-        Properties env = new Properties();
-        env.setProperty(Context.INITIAL_CONTEXT_FACTORY, ServerContextFactory.class.getName());
-        env.setProperty(Context.SECURITY_AUTHENTICATION, "simple");
-        env.setProperty(Context.SECURITY_PRINCIPAL, "uid=admin,ou=system");
-        env.setProperty(Context.SECURITY_CREDENTIALS, "secret");
-
-        ShutdownConfiguration shutdown = new ShutdownConfiguration(configuration.getInstanceId());
-        env.putAll(shutdown.toJndiEnvironment());
-
-        logger.info("Shutting down directory server with Id '" + configuration.getInstanceId() + "'");
-
-        try {
-            new InitialContext(env);
-        } catch (NamingException e) {
-            logger.error("Failed to shutdown directory server", e);
-            return;
+    private boolean deleteDir(File dir) {
+        if (dir.isDirectory()) {
+            String[] children = dir.list();
+            for (int i=0; i < children.length; i++) {
+                boolean success = deleteDir(new File(dir, children[i]));
+                if (!success) {
+                    return false;
+                }
+            }
         }
 
-        running = false;
-
-        if (workingDir.exists()) {
-            logger.info("Deleting working directory " + workingDir.getAbsolutePath());
-            deleteDir(workingDir);
-        }
+        return dir.delete();
     }
 
     public boolean isRunning() {

+ 2 - 48
ldap/src/test/java/org/springframework/security/ldap/AbstractLdapIntegrationTests.java

@@ -14,24 +14,14 @@
  */
 package org.springframework.security.ldap;
 
-import java.util.HashSet;
-import java.util.Set;
-
 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.Attribute;
-import javax.naming.directory.Attributes;
-import javax.naming.directory.BasicAttribute;
-import javax.naming.directory.BasicAttributes;
 import javax.naming.directory.DirContext;
 
-import org.apache.directory.server.configuration.MutableServerStartupConfiguration;
-import org.apache.directory.server.core.DirectoryService;
-import org.apache.directory.server.core.partition.impl.btree.MutableBTreePartitionConfiguration;
 import org.apache.directory.server.protocol.shared.store.LdifFileLoader;
 import org.junit.After;
 import org.junit.AfterClass;
@@ -56,35 +46,11 @@ public abstract class AbstractLdapIntegrationTests {
     protected AbstractLdapIntegrationTests() {
     }
 
-    @SuppressWarnings("unchecked")
     @BeforeClass
     public static void startServer() throws Exception {
-        shutdownRunningServers();
-        MutableBTreePartitionConfiguration partition =  new MutableBTreePartitionConfiguration();
-        partition.setName("springsecurity");
-
-        Attributes rootAttributes = new BasicAttributes("dc", "springsecurity");
-        Attribute a = new BasicAttribute("objectClass");
-        a.add("top");
-        a.add("domain");
-        a.add("extensibleObject");
-        rootAttributes.put(a);
-
-        partition.setContextEntry(rootAttributes);
-        partition.setSuffix("dc=springframework,dc=org");
-
-        Set partitions = new HashSet();
-        partitions.add(partition);
-
-        MutableServerStartupConfiguration cfg = new MutableServerStartupConfiguration();
-        cfg.setLdapPort(53389);
-        cfg.setShutdownHookEnabled(false);
-        cfg.setExitVmOnShutdown(false);
-        cfg.setContextPartitionConfigurations(partitions);
-
         contextSource = new DefaultSpringSecurityContextSource("ldap://127.0.0.1:53389/dc=springframework,dc=org");
         ((DefaultSpringSecurityContextSource)contextSource).afterPropertiesSet();
-        server = new ApacheDSContainer(cfg, contextSource, "classpath:test-server.ldif");
+        server = new ApacheDSContainer("dc=springframework,dc=org", "classpath:test-server.ldif");
         server.afterPropertiesSet();
     }
 
@@ -93,20 +59,8 @@ public abstract class AbstractLdapIntegrationTests {
         if (server != null) {
             server.stop();
         }
-        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 {
     }
@@ -127,7 +81,7 @@ public abstract class AbstractLdapIntegrationTests {
 
         try {
             clearSubContexts(ctx, startingPoint);
-            LdifFileLoader loader = new LdifFileLoader(ctx, ldifs.getFile().getAbsolutePath());
+            LdifFileLoader loader = new LdifFileLoader(server.getService().getAdminSession(), ldifs.getFile().getAbsolutePath());
             loader.execute();
         } finally {
             ctx.close();

+ 27 - 0
ldap/src/test/java/org/springframework/security/ldap/server/ApacheDSContainerTests.java

@@ -0,0 +1,27 @@
+package org.springframework.security.ldap.server;
+
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.junit.Test;
+
+/**
+ * Useful for debugging the container by itself.
+ *
+ * @author Luke Taylor
+ * @version $Id$
+ * @since 3.0
+ */
+public class ApacheDSContainerTests {
+
+    @Test
+    public void successfulStartupAndShutdown() throws Exception {
+        LdapDN people = new LdapDN("ou=people,dc=springframework,dc=org");
+        people.toString();
+
+//        ApacheDSContainer server = new ApacheDSContainer("dc=springframework,dc=org", "classpath:test-server.ldif");
+//        server.afterPropertiesSet();
+//
+//        server.getService().getAdminSession().lookup(people);
+//
+//        server.stop();
+    }
+}

+ 1 - 1
ldap/src/test/resources/log4j.properties

@@ -11,4 +11,4 @@ log4j.appender.stdout.layout.ConversionPattern=%p %c{1} - %m%n
 log4j.logger.org.springframework.security=DEBUG
 log4j.logger.org.springframework.ldap=DEBUG
 
-log4j.logger.org.apache.directory=ERROR
+log4j.logger.org.apache.directory=INFO

+ 1 - 0
ldap/template.mf

@@ -9,6 +9,7 @@ Ignored-Existing-Headers:
 Import-Template: 
  org.apache.commons.logging.*;version="[1.0.4, 2.0.0)",
  org.apache.directory.server.*;version="[1.0.2, 1.5)";resolution:=optional,
+ org.apache.directory.shared.ldap.*;version="[1.5.5,1.6)";resolution:=optional,
  org.springframework.ldap.*;version="[1.3.0,1.4.0)",
  org.springframework.security.core.*;version="[${version}, 3.1.0)",
  org.springframework.security.authentication.*;version="[${version}, 3.1.0)",