8196770: Add JNDI test com/sun/jndi/ldap/blits/AddTests/AddNewEntry.java
authorxyin
Tue, 13 Feb 2018 12:26:22 +0800
changeset 48852 478e198da84b
parent 48851 1d8f882f2b2f
child 48853 84b4ffbba8b0
8196770: Add JNDI test com/sun/jndi/ldap/blits/AddTests/AddNewEntry.java Reviewed-by: vtewari, rriggs
test/jdk/com/sun/jndi/ldap/blits/AddTests/AddNewEntry.java
test/jdk/com/sun/jndi/ldap/blits/AddTests/AddNewEntry.ldap
test/jdk/com/sun/jndi/ldap/lib/LDAPTestUtils.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/com/sun/jndi/ldap/blits/AddTests/AddNewEntry.java	Tue Feb 13 12:26:22 2018 +0800
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8196770
+ * @summary Verify capability to add a new entry to the directory using the
+ *          ADD operation.
+ * @modules java.xml.bind
+ *          java.naming/com.sun.jndi.ldap
+ * @library ../../lib/ /javax/naming/module/src/test/test/
+ * @build LDAPServer LDAPTestUtils
+ * @run main AddNewEntry
+ */
+
+import javax.naming.NamingEnumeration;
+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 javax.naming.directory.InitialDirContext;
+import javax.naming.directory.SearchControls;
+import java.net.ServerSocket;
+import java.util.Hashtable;
+
+public class AddNewEntry {
+
+    public static void main(String[] args) throws Exception {
+        ServerSocket serverSocket = new ServerSocket(0);
+
+        Hashtable<Object, Object> env;
+
+        // initialize test
+        env = LDAPTestUtils
+                .initEnv(serverSocket, AddNewEntry.class.getName(), args, true);
+
+        /* Build attribute set */
+        String[] ids = { "objectClass", "sn", "cn", "telephoneNumber", "mail",
+                "description", "uid" };
+        Attribute objectClass = new BasicAttribute(ids[0]);
+        objectClass.add("top");
+        objectClass.add("person");
+        objectClass.add("organizationalPerson");
+        objectClass.add("inetOrgPerson");
+
+        Attribute sn = new BasicAttribute(ids[1], "Powers");
+        Attribute cn = new BasicAttribute(ids[2],
+                "Austin \\\"Danger\\\" Powers");
+        Attribute telephoneNumber = new BasicAttribute(ids[3], "+44 582 10101");
+        Attribute mail = new BasicAttribute(ids[4], "secret_agent_man@imc.org");
+        Attribute description = new BasicAttribute(ids[5], "Yea Baby!!");
+        description.add("Behave!");
+        Attribute uid = new BasicAttribute(ids[6], "secret_agent_man");
+
+        Attributes attrs = new BasicAttributes();
+        attrs.put(objectClass);
+        attrs.put(sn);
+        attrs.put(cn);
+        attrs.put(telephoneNumber);
+        attrs.put(mail);
+        attrs.put(description);
+        attrs.put(uid);
+
+        DirContext ctx = null;
+        String[] bases = new String[] { (String) env.get("client"),
+                (String) env.get("vendor"), "Add" };
+        String baseDN = LDAPTestUtils.buildDN(bases, (String) env.get("root"));
+        String entryDN = "cn=Austin Powers," + baseDN;
+        String expect = ""; // relative name
+
+        try {
+            // connect to server
+            ctx = new InitialDirContext(env);
+
+            // add entry
+            ctx.createSubcontext(entryDN, attrs);
+
+            // specify base search
+            SearchControls constraints = new SearchControls();
+            constraints.setSearchScope(SearchControls.OBJECT_SCOPE);
+
+            NamingEnumeration results = ctx
+                    .search(entryDN, "(objectclass=*)", constraints);
+
+            int found = LDAPTestUtils.checkResult(results, expect);
+
+            if (found != 1) {
+                throw new RuntimeException(
+                        "Check result failed, expect found 1 but actual is "
+                                + found);
+            }
+
+        } finally {
+            LDAPTestUtils.cleanupSubcontext(ctx, entryDN);
+            LDAPTestUtils.cleanup(ctx);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/com/sun/jndi/ldap/blits/AddTests/AddNewEntry.ldap	Tue Feb 13 12:26:22 2018 +0800
@@ -0,0 +1,135 @@
+#
+# 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.
+#
+
+################################################################################
+# Capture file for AddNewEntry.java
+#
+# NOTE: This hexadecimal dump of LDAP protocol messages was generated by
+#       running the AddNewEntry application program against a real LDAP
+#       server and setting the JNDI/LDAP environment property:
+#       com.sun.jndi.ldap.trace.ber to activate LDAP message tracing.
+#
+################################################################################
+
+
+# LDAP BindRequest
+
+0000: 30 27 02 01 01 60 22 02   01 03 04 13 63 6E 3D 61  0'...`".....cn=a
+0010: 64 6D 69 6E 2C 6F 3D 49   4D 43 2C 63 3D 55 53 80  dmin,o=IMC,c=US.
+0020: 08 73 65 63 72 65 74 39   39                       .secret99
+
+
+# LDAP BindResponse
+
+0000: 30 0C 02 01 01 61 07 0A   01 00 04 00 04 00        0....a........
+
+
+# LDAP AddRequest
+
+0000: 30 82 01 5F 02 01 02 68   82 01 3B 04 38 63 6E 3D  0.._...h..;.8cn=
+0010: 41 75 73 74 69 6E 20 50   6F 77 65 72 73 2C 6F 75  Austin Powers,ou
+0020: 3D 43 6C 69 65 6E 74 31   2C 6F 75 3D 56 65 6E 64  =Client1,ou=Vend
+0030: 6F 72 31 2C 6F 75 3D 41   64 64 2C 6F 3D 49 4D 43  or1,ou=Add,o=IMC
+0040: 2C 63 3D 55 53 30 81 FE   30 41 04 0B 6F 62 6A 65  ,c=US0..0A..obje
+0050: 63 74 43 6C 61 73 73 31   32 04 03 74 6F 70 04 06  ctClass12..top..
+0060: 70 65 72 73 6F 6E 04 14   6F 72 67 61 6E 69 7A 61  person..organiza
+0070: 74 69 6F 6E 61 6C 50 65   72 73 6F 6E 04 0D 69 6E  tionalPerson..in
+0080: 65 74 4F 72 67 50 65 72   73 6F 6E 30 22 04 04 6D  etOrgPerson0"..m
+0090: 61 69 6C 31 1A 04 18 73   65 63 72 65 74 5F 61 67  ail1...secret_ag
+00A0: 65 6E 74 5F 6D 61 6E 40   69 6D 63 2E 6F 72 67 30  ent_man@imc.org0
+00B0: 19 04 03 75 69 64 31 12   04 10 73 65 63 72 65 74  ...uid1...secret
+00C0: 5F 61 67 65 6E 74 5F 6D   61 6E 30 24 04 0B 64 65  _agent_man0$..de
+00D0: 73 63 72 69 70 74 69 6F   6E 31 15 04 0A 59 65 61  scription1...Yea
+00E0: 20 42 61 62 79 21 21 04   07 42 65 68 61 76 65 21   Baby!!..Behave!
+00F0: 30 0E 04 02 73 6E 31 08   04 06 50 6F 77 65 72 73  0...sn1...Powers
+0100: 30 22 04 0F 74 65 6C 65   70 68 6F 6E 65 4E 75 6D  0"..telephoneNum
+0110: 62 65 72 31 0F 04 0D 2B   34 34 20 35 38 32 20 31  ber1...+44 582 1
+0120: 30 31 30 31 30 20 04 02   63 6E 31 1A 04 18 41 75  01010 ..cn1...Au
+0130: 73 74 69 6E 20 5C 22 44   61 6E 67 65 72 5C 22 20  stin \"Danger\" 
+0140: 50 6F 77 65 72 73 A0 1B   30 19 04 17 32 2E 31 36  Powers..0...2.16
+0150: 2E 38 34 30 2E 31 2E 31   31 33 37 33 30 2E 33 2E  .840.1.113730.3.
+0160: 34 2E 32                                           4.2
+
+
+# LDAP AddResponse
+
+0000: 30 0C 02 01 02 69 07 0A   01 00 04 00 04 00        0....i........
+
+
+# LDAP SearchRequest
+
+0000: 30 7A 02 01 03 63 58 04   38 63 6E 3D 41 75 73 74  0z...cX.8cn=Aust
+0010: 69 6E 20 50 6F 77 65 72   73 2C 6F 75 3D 43 6C 69  in Powers,ou=Cli
+0020: 65 6E 74 31 2C 6F 75 3D   56 65 6E 64 6F 72 31 2C  ent1,ou=Vendor1,
+0030: 6F 75 3D 41 64 64 2C 6F   3D 49 4D 43 2C 63 3D 55  ou=Add,o=IMC,c=U
+0040: 53 0A 01 00 0A 01 03 02   01 00 02 01 00 01 01 00  S...............
+0050: 87 0B 6F 62 6A 65 63 74   63 6C 61 73 73 30 00 A0  ..objectclass0..
+0060: 1B 30 19 04 17 32 2E 31   36 2E 38 34 30 2E 31 2E  .0...2.16.840.1.
+0070: 31 31 33 37 33 30 2E 33   2E 34 2E 32              113730.3.4.2
+
+
+# LDAP SearchResultEntry
+
+0000: 30 82 01 52 02 01 03 64   82 01 4B 04 38 63 6E 3D  0..R...d..K.8cn=
+0010: 41 75 73 74 69 6E 20 50   6F 77 65 72 73 2C 6F 75  Austin Powers,ou
+0020: 3D 43 6C 69 65 6E 74 31   2C 6F 75 3D 56 65 6E 64  =Client1,ou=Vend
+0030: 6F 72 31 2C 6F 75 3D 41   64 64 2C 6F 3D 49 4D 43  or1,ou=Add,o=IMC
+0040: 2C 63 3D 55 53 30 82 01   0D 30 41 04 0B 6F 62 6A  ,c=US0...0A..obj
+0050: 65 63 74 43 6C 61 73 73   31 32 04 03 74 6F 70 04  ectClass12..top.
+0060: 06 70 65 72 73 6F 6E 04   14 6F 72 67 61 6E 69 7A  .person..organiz
+0070: 61 74 69 6F 6E 61 6C 50   65 72 73 6F 6E 04 0D 69  ationalPerson..i
+0080: 6E 65 74 4F 72 67 50 65   72 73 6F 6E 30 22 04 04  netOrgPerson0"..
+0090: 6D 61 69 6C 31 1A 04 18   73 65 63 72 65 74 5F 61  mail1...secret_a
+00A0: 67 65 6E 74 5F 6D 61 6E   40 69 6D 63 2E 6F 72 67  gent_man@imc.org
+00B0: 30 19 04 03 75 69 64 31   12 04 10 73 65 63 72 65  0...uid1...secre
+00C0: 74 5F 61 67 65 6E 74 5F   6D 61 6E 30 24 04 0B 64  t_agent_man0$..d
+00D0: 65 73 63 72 69 70 74 69   6F 6E 31 15 04 0A 59 65  escription1...Ye
+00E0: 61 20 42 61 62 79 21 21   04 07 42 65 68 61 76 65  a Baby!!..Behave
+00F0: 21 30 0E 04 02 73 6E 31   08 04 06 50 6F 77 65 72  !0...sn1...Power
+0100: 73 30 22 04 0F 74 65 6C   65 70 68 6F 6E 65 4E 75  s0"..telephoneNu
+0110: 6D 62 65 72 31 0F 04 0D   2B 34 34 20 35 38 32 20  mber1...+44 582 
+0120: 31 30 31 30 31 30 2F 04   02 63 6E 31 29 04 18 41  101010/..cn1)..A
+0130: 75 73 74 69 6E 20 5C 22   44 61 6E 67 65 72 5C 22  ustin \"Danger\"
+0140: 20 50 6F 77 65 72 73 04   0D 41 75 73 74 69 6E 20   Powers..Austin 
+0150: 50 6F 77 65 72 73                                  Powers
+
+
+# LDAP SearchResultDone
+
+0000: 30 0C 02 01 03 65 07 0A   01 00 04 00 04 00        0....e........
+
+
+# LDAP DeleteRequest
+
+0000: 30 5A 02 01 04 4A 38 63   6E 3D 41 75 73 74 69 6E  0Z...J8cn=Austin
+0010: 20 50 6F 77 65 72 73 2C   6F 75 3D 43 6C 69 65 6E   Powers,ou=Clien
+0020: 74 31 2C 6F 75 3D 56 65   6E 64 6F 72 31 2C 6F 75  t1,ou=Vendor1,ou
+0030: 3D 41 64 64 2C 6F 3D 49   4D 43 2C 63 3D 55 53 A0  =Add,o=IMC,c=US.
+0040: 1B 30 19 04 17 32 2E 31   36 2E 38 34 30 2E 31 2E  .0...2.16.840.1.
+0050: 31 31 33 37 33 30 2E 33   2E 34 2E 32              113730.3.4.2
+
+
+# LDAP DeleteResponse
+
+0000: 30 0C 02 01 04 6B 07 0A   01 00 04 00 04 00        0....k........
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/com/sun/jndi/ldap/lib/LDAPTestUtils.java	Tue Feb 13 12:26:22 2018 +0800
@@ -0,0 +1,358 @@
+/*
+ * 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 Thread(() -> {
+            try {
+                new test.LDAPServer(serverSocket, fileName);
+            } catch (Exception e) {
+                System.out.println("Warning: LDAP server running with issue");
+                e.printStackTrace();
+            }
+        });
+
+        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", ".");
+    }
+}