test/jdk/com/sun/jndi/ldap/lib/LDAPTestUtils.java
author xyin
Wed, 01 May 2019 00:06:22 -0700
branchJDK-8210696-branch
changeset 57345 ff884a2f247b
parent 48852 478e198da84b
permissions -rw-r--r--
JDK-8210696-branch: push initial fix change

/*
 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

import com.sun.jndi.ldap.LdapURL;

import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.SearchResult;
import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

public class LDAPTestUtils {
    public static final String TEST_LDAP_SERVER_THREAD = "test.ldap.server.thread";
    public static final int CERTS_LOOKUP_MAX_DEPTH = 4;

    protected static boolean debug = true;

    /*
     * Process command line arguments and return properties in a Hashtable.
     */
    public static Hashtable<Object, Object> initEnv(String testname,
            String[] args) {
        return initEnv(null, testname, args, false);
    }

    public static Hashtable<Object, Object> initEnv(ServerSocket socket,
            String testname, String[] args, boolean authInfo) {

        Hashtable<Object, Object> env = new Hashtable<>();
        String root = "o=IMC,c=US";
        String vendor = "Vendor1";
        String client = "Client1";
        String realm = "";
        Vector<String> refs = new Vector<>();

        // set defaults for some JNDI properties
        env.put(Context.INITIAL_CONTEXT_FACTORY,
                "com.sun.jndi.ldap.LdapCtxFactory");

        if (authInfo) {
            env.put(Context.SECURITY_AUTHENTICATION, "simple");
            env.put(Context.SECURITY_PRINCIPAL, "cn=admin,o=IMC,c=US");
            env.put(Context.SECURITY_CREDENTIALS, "secret99");
        }

        env.put("root", root);
        env.put("vendor", vendor);
        env.put("client", client);

        boolean traceEnable = false;
        for (int i = 0; i < args.length; i++) {
            if (args[i].equals("-D") && (args.length > i + 1)) {
                extractProperty(args[++i], env);
            } else if (args[i].startsWith("-D")) {
                extractProperty(args[i].substring(2), env);
            } else if (args[i].equals("-referral") && (args.length > i + 1)) {
                refs.addElement(args[++i]);
            } else if (args[i].equals("-trace")) {
                traceEnable = true;
            }
        }

        env.put("disabled.realm", realm);

        if (refs.size() > 0) {
            env.put("referrals", refs);
        }

        if (traceEnable) {
            enableLDAPTrace(env, testname);
        } else {
            if (socket != null) {
                env.put(TEST_LDAP_SERVER_THREAD,
                        startLDAPServer(socket, getCaptureFile(testname)));
                env.put("java.naming.provider.url",
                        "ldap://localhost:" + socket.getLocalPort());
            } else {
                // for tests which run against remote server or no server
                // required
                debug("Skip local LDAP Server creation "
                        + "since ServerSocket is null");
            }
        }

        return env;
    }

    /*
     * Clean-up the directory context.
     */
    public static void cleanup(DirContext ctx) {
        if (ctx != null) {
            try {
                ctx.close();
            } catch (NamingException e) {
                // ignore
            }
        }
    }

    /*
     * Clean-up the sub context.
     */
    public static void cleanupSubcontext(DirContext ctx, String name) {
        if (ctx != null) {
            try {
                ctx.destroySubcontext(name);
            } catch (NamingException ne) {
                // ignore
            }
        }
    }

    /*
     * Assemble a distinguished name from the base components and the
     * namespace root.
     *
     * The components are prefixed with 'dc=' if the root is a DC-style name.
     * Otherwise they are prefixed with 'ou='.
     */
    public static String buildDN(String[] bases, String root) {

        StringBuilder dn = new StringBuilder();
        String prefix;

        if (!root.contains("dc=")) {
            prefix = "ou=";
        } else {
            prefix = "dc=";
        }

        for (String base : bases) {
            dn.append(prefix).append(base).append(",");
        }

        return dn.append(root).toString();
    }

    /*
     * Scan the results to confirm that the expected name is present.
     */
    public static int checkResult(NamingEnumeration results, String name)
            throws NamingException {

        return checkResult(results, new String[] { name }, null);
    }

    /*
     * Scan the results to confirm that the expected names and attributes
     * are present.
     */
    public static int checkResult(NamingEnumeration results, String[] names,
            Attributes attrs) throws NamingException {

        int found = 0;

        while (results != null && results.hasMore()) {

            SearchResult entry = (SearchResult) results.next();
            String entryDN = entry.getName();

            debug(">>> received: " + entryDN);

            if (entry.isRelative()) {
                entryDN = entryDN.toLowerCase(); // normalize
            } else {
                LdapURL url = new LdapURL(entryDN); // extract DN
                entryDN = url.getDN().toLowerCase(); // normalize
            }

            for (String name : names) {
                if ((entryDN.contains(name.toLowerCase())) || (entryDN
                        .equalsIgnoreCase(name))) {

                    debug(">>> checked results: found '" + name + "'");

                    if (attrs == null || foundAttributes(entry, attrs)) {
                        found++;
                        break;
                    }
                }
            }
        }

        debug(">>> checked results: found " + found
                + " entries that meet the criteria.");

        return found;
    }

    /*
     * Confirm that the attributes are present in the entry.
     */
    public static boolean foundAttributes(SearchResult entry, Attributes attrs)
            throws NamingException {

        Attributes eattrs = entry.getAttributes();
        int found = 0;

        if ((eattrs == null) || (attrs == null)) {
            return false;
        }

        for (NamingEnumeration ne = attrs.getAll(); ne.hasMoreElements(); ) {

            Attribute attr = (Attribute) ne.next();

            if (equalsIgnoreCase(eattrs.get(attr.getID()), attr)) {
                found++;
            } else {
                debug(">>> foundAttributes: no match for " + attr.getID());
            }
        }
        debug(">>> foundAttributes: found " + found + " attributes");
        return (found == attrs.size());
    }

    public static Thread startLDAPServer(ServerSocket serverSocket,
            String fileName) {
        if (serverSocket == null) {
            throw new RuntimeException("Error: failed to create LDAPServer "
                    + "since ServerSocket is null");
        }

        if (!Files.exists(Paths.get(fileName))) {
            throw new RuntimeException(
                    "Error: failed to create LDAPServer, not found ldap "
                            + "cache file " + fileName);
        }

        Thread thread = new LdapPlaybackServer(serverSocket, fileName);

        thread.start();
        return thread;
    }

    private static boolean equalsIgnoreCase(Attribute received,
            Attribute expected) {

        if (received == null || !received.getID()
                .equalsIgnoreCase(expected.getID())) {
            return false;
        }

        try {

            Enumeration expectedVals = expected.getAll();
            Object obj;
            while (expectedVals.hasMoreElements()) {
                obj = expectedVals.nextElement();
                if (!received.contains(obj)) {
                    if (!(obj instanceof String)) {
                        return false;
                    }
                    if (!received.contains(((String) obj).toLowerCase())) {
                        return false;
                    }
                }
            }

        } catch (NamingException e) {
            return false;
        }

        return true;
    }

    private static void extractProperty(String propString,
            Hashtable<Object, Object> env) {
        int index;

        if ((index = propString.indexOf('=')) > 0) {
            env.put(propString.substring(0, index),
                    propString.substring(index + 1));
        } else {
            throw new RuntimeException(
                    "Failed to extract test args property from " + propString);
        }
    }

    private static void enableLDAPTrace(Hashtable<Object, Object> env,
            String testname) {
        try {
            PrintStream outStream = new PrintStream(getCaptureFile(testname));
            env.put("com.sun.jndi.ldap.trace.ber", outStream);
        } catch (FileNotFoundException e) {
            throw new RuntimeException(
                    "Error: failed to enable ldap trace: " + e.getMessage(), e);
        }
    }

    private static String getCaptureFile(String testname) {
        return Paths.get(System.getProperty("test.src"))
                .resolve(testname + ".ldap").toString();
    }

    public static void debug(Object object) {
        if (debug) {
            System.out.println(object);
        }
    }

    public static String findCertsHome(int depth) {
        Path path = Paths.get(System.getProperty("test.src", "."))
                .toAbsolutePath();
        for (int i = depth; i >= 0; i--) {
            Path homePath = path.resolve("certs");
            if (Files.exists(homePath) && Files.isDirectory(homePath)) {
                return homePath.toString();
            }

            path = path.getParent();
            if (path == null) {
                break;
            }
        }

        return System.getProperty("test.src", ".");
    }
}