org.geoserver.security.ldap.LDAPTestUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.geoserver.security.ldap.LDAPTestUtils.java

Source

/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
 * (c) 2001 - 2013 OpenPlans
 * This code is licensed under the GPL 2.0 license, available at the root
 * application directory.
 */
package org.geoserver.security.ldap;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.DatagramSocket;
import java.net.ServerSocket;

import javax.naming.Binding;
import javax.naming.ContextNotEmptyException;
import javax.naming.Name;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;

import org.apache.commons.io.IOUtils;
import org.apache.directory.server.core.DefaultDirectoryService;
import org.apache.directory.server.protocol.shared.store.LdifFileLoader;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.LdapAttributes;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.support.DefaultDirObjectFactory;
import org.springframework.ldap.core.support.LdapContextSource;
import org.springframework.ldap.ldif.parser.LdifParser;

/**
 * copied and modified from org.springframework.ldap.test.LdapTestUtils
 * to allow anonymous access (there was no alternative way)
 *  
 * @author "Mauro Bartolomeoli - mauro.bartolomeoli@geo-solutions.it"
 * @author Mattias Hellborg Arthursson
 * @author Niels Charlier
 * 
 */
public class LDAPTestUtils {
    private static final int LDAP_SERVER_PORT = 10389;
    public static final String LDAP_SERVER_URL = "ldap://127.0.0.1:10389";
    public static final String LDAP_BASE_PATH = "dc=example,dc=com";
    public static final String DEFAULT_PRINCIPAL = "uid=admin,ou=system";
    public static final String DEFAULT_PASSWORD = "secret";

    private static EmbeddedLdapServer embeddedServer;

    /**
     * Start an embedded Apache Directory Server. Only one embedded server will be permitted in the same JVM.
     *
     * @param port                   the port on which the server will be listening.
     * @param defaultPartitionSuffix The default base suffix that will be used
     *                               for the LDAP server.
     * @param defaultPartitionName   The name to use in the directory server
     *                               configuration for the default base suffix.
     *
     * @throws IllegalStateException if an embedded server is already started.
     * @since 1.3.2
     */
    public static void startEmbeddedServer(int port, String defaultPartitionSuffix, String defaultPartitionName,
            boolean allowAnonymousAccess) {
        if (embeddedServer != null) {
            throw new IllegalStateException("An embedded server is already started");
        }

        try {
            embeddedServer = EmbeddedLdapServer.newEmbeddedServer(defaultPartitionName, defaultPartitionSuffix,
                    port, allowAnonymousAccess);
        } catch (Exception e) {
            throw new RuntimeException("Failed to start embedded server");
        }
    }

    /**
     * Shuts down the embedded server, if there is one. If no server was previously started in this JVM
     * this is silently ignored.
     *
     * @since 1.3.2
     */
    public static void shutdownEmbeddedServer() throws Exception {
        if (embeddedServer != null) {
            embeddedServer.shutdown();
            embeddedServer = null;
        }
    }

    /**
     * Initializes an in-memory LDAP server to use for testing.
     * 
     * @param allowAnonymous
     *            anonymous access is allowed or not
     */
    public static boolean initLdapServer(boolean allowAnonymous, String ldapServerUrl, String basePath)
            throws Exception {
        return initLdapServer(allowAnonymous, ldapServerUrl, basePath, "data.ldif");
    }

    /**
     * Initializes an in-memory LDAP server to use for testing.
     * 
     * @param allowAnonymous
     *            anonymous access is allowed or not
     */
    public static boolean initLdapServer(boolean allowAnonymous, String ldapServerUrl, String basePath,
            String ldifPath) throws Exception {
        try {
            if (!portIsBusy("127.0.0.1", LDAP_SERVER_PORT)) {
                startEmbeddedServer(LDAP_SERVER_PORT, basePath, "test", allowAnonymous);

                // Bind to the directory
                LdapContextSource contextSource = new LdapContextSource();
                contextSource.setUrl(ldapServerUrl);
                contextSource.setUserDn(DEFAULT_PRINCIPAL);
                contextSource.setPassword(DEFAULT_PASSWORD);
                contextSource.setPooled(false);
                contextSource.afterPropertiesSet();

                // Create the Sprint LDAP template
                LdapTemplate template = new LdapTemplate(contextSource);

                // Clear out any old data - and load the test data
                cleanAndSetup(template.getContextSource(), new DistinguishedName("dc=example,dc=com"),
                        new ClassPathResource(ldifPath));
                return true;
            }
            return false;
        } catch (Exception ee) {
            return false;
        }
    }

    /**
     * Checks if a network host / port is already occupied.
     * 
     * @param host
     * @param port
     *
     */
    private static boolean portIsBusy(String host, int port) {
        ServerSocket ss = null;
        DatagramSocket ds = null;
        try {
            ss = new ServerSocket(port);
            ss.setReuseAddress(true);
            ds = new DatagramSocket(port);
            ds.setReuseAddress(true);
            return false;
        } catch (IOException e) {
        } finally {
            if (ds != null) {
                ds.close();
            }
            if (ss != null) {
                try {
                    ss.close();
                } catch (IOException e) {
                    /* should not be thrown */
                }
            }
        }
        return true;
    }

    /**
     * Clear the directory sub-tree starting with the node represented by the
     * supplied distinguished name.
     *
     * @param contextSource the ContextSource to use for getting a DirContext.
     * @param name          the distinguished name of the root node.
     * @throws NamingException if anything goes wrong removing the sub-tree.
     */
    public static void clearSubContexts(ContextSource contextSource, Name name) throws NamingException {
        DirContext ctx = null;
        try {
            ctx = contextSource.getReadWriteContext();
            clearSubContexts(ctx, name);
        } finally {
            try {
                ctx.close();
            } catch (Exception e) {
                // Never mind this
            }
        }
    }

    /**
     * Clear the directory sub-tree starting with the node represented by the
     * supplied distinguished name.
     *
     * @param ctx  The DirContext to use for cleaning the tree.
     * @param name the distinguished name of the root node.
     * @throws NamingException if anything goes wrong removing the sub-tree.
     */
    public static void clearSubContexts(DirContext ctx, Name name) throws NamingException {

        NamingEnumeration enumeration = null;
        try {
            enumeration = ctx.listBindings(name);
            while (enumeration.hasMore()) {
                Binding element = (Binding) enumeration.next();
                DistinguishedName childName = new DistinguishedName(element.getName());
                childName.prepend((DistinguishedName) name);

                try {
                    ctx.destroySubcontext(childName);
                } catch (ContextNotEmptyException e) {
                    clearSubContexts(ctx, childName);
                    ctx.destroySubcontext(childName);
                }
            }
        } catch (NamingException e) {
            e.printStackTrace();
        } finally {
            try {
                enumeration.close();
            } catch (Exception e) {
                // Never mind this
            }
        }
    }

    /**
     * Load an Ldif file into an LDAP server.
     *
     * @param contextSource ContextSource to use for getting a DirContext to
     *                      interact with the LDAP server.
     * @param ldifFile      a Resource representing a valid LDIF file.
     * @throws IOException if the Resource cannot be read.
     */
    public static void loadLdif(ContextSource contextSource, Resource ldifFile) throws IOException {
        DirContext context = contextSource.getReadWriteContext();
        try {
            loadLdif(context, ldifFile);
        } finally {
            try {
                context.close();
            } catch (Exception e) {
                // This is not the exception we are interested in.
            }
        }
    }

    public static void cleanAndSetup(ContextSource contextSource, DistinguishedName rootNode, Resource ldifFile)
            throws NamingException, IOException {

        clearSubContexts(contextSource, rootNode);
        loadLdif(contextSource, ldifFile);
    }

    private static void loadLdif(DirContext context, Resource ldifFile) throws IOException {
        try {
            DistinguishedName baseDn = (DistinguishedName) context.getEnvironment()
                    .get(DefaultDirObjectFactory.JNDI_ENV_BASE_PATH_KEY);

            LdifParser parser = new LdifParser(ldifFile);
            parser.open();
            while (parser.hasMoreRecords()) {
                LdapAttributes record = parser.getRecord();

                DistinguishedName dn = record.getDN();
                if (baseDn != null) {
                    dn.removeFirst(baseDn);
                }
                context.bind(dn, null, record);
            }
        } catch (NamingException e) {
            throw new RuntimeException("Failed to populate LDIF", e);
        }
    }

    public static void loadLdif(DefaultDirectoryService directoryService, Resource ldifFile) throws IOException {
        File tempFile = File.createTempFile("spring_ldap_test", ".ldif");
        try {
            InputStream inputStream = ldifFile.getInputStream();
            IOUtils.copy(inputStream, new FileOutputStream(tempFile));
            LdifFileLoader fileLoader = new LdifFileLoader(directoryService.getSession(),
                    tempFile.getAbsolutePath());
            fileLoader.execute();
        } finally {
            try {
                tempFile.delete();
            } catch (Exception e) {
                // Ignore this
            }
        }
    }

}