Browse Source

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 năm trước cách đây
mục cha
commit
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)",