1 /* 2 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 import com.sun.jndi.ldap.LdapURL; 25 26 import javax.naming.Context; 27 import javax.naming.NamingEnumeration; 28 import javax.naming.NamingException; 29 import javax.naming.directory.Attribute; 30 import javax.naming.directory.Attributes; 31 import javax.naming.directory.DirContext; 32 import javax.naming.directory.SearchResult; 33 import java.io.FileNotFoundException; 34 import java.io.PrintStream; 35 import java.net.ServerSocket; 36 import java.nio.file.Files; 37 import java.nio.file.Path; 38 import java.nio.file.Paths; 39 import java.util.Enumeration; 40 import java.util.Hashtable; 41 import java.util.Vector; 42 43 public class LDAPTestUtils { 44 public static final String TEST_LDAP_SERVER_THREAD = "test.ldap.server.thread"; 45 public static final int CERTS_LOOKUP_MAX_DEPTH = 4; 46 47 protected static boolean debug = true; 48 49 /* 50 * Process command line arguments and return properties in a Hashtable. 51 */ initEnv(String testname, String[] args)52 public static Hashtable<Object, Object> initEnv(String testname, 53 String[] args) { 54 return initEnv(null, testname, args, false); 55 } 56 initEnv(ServerSocket socket, String testname, String[] args, boolean authInfo)57 public static Hashtable<Object, Object> initEnv(ServerSocket socket, 58 String testname, String[] args, boolean authInfo) { 59 60 Hashtable<Object, Object> env = new Hashtable<>(); 61 String root = "o=IMC,c=US"; 62 String vendor = "Vendor1"; 63 String client = "Client1"; 64 String realm = ""; 65 Vector<String> refs = new Vector<>(); 66 67 // set defaults for some JNDI properties 68 env.put(Context.INITIAL_CONTEXT_FACTORY, 69 "com.sun.jndi.ldap.LdapCtxFactory"); 70 71 if (authInfo) { 72 env.put(Context.SECURITY_AUTHENTICATION, "simple"); 73 env.put(Context.SECURITY_PRINCIPAL, "cn=admin,o=IMC,c=US"); 74 env.put(Context.SECURITY_CREDENTIALS, "secret99"); 75 } 76 77 env.put("root", root); 78 env.put("vendor", vendor); 79 env.put("client", client); 80 81 boolean traceEnable = false; 82 for (int i = 0; i < args.length; i++) { 83 if (args[i].equals("-D") && (args.length > i + 1)) { 84 extractProperty(args[++i], env); 85 } else if (args[i].startsWith("-D")) { 86 extractProperty(args[i].substring(2), env); 87 } else if (args[i].equals("-referral") && (args.length > i + 1)) { 88 refs.addElement(args[++i]); 89 } else if (args[i].equals("-trace")) { 90 traceEnable = true; 91 } 92 } 93 94 env.put("disabled.realm", realm); 95 96 if (refs.size() > 0) { 97 env.put("referrals", refs); 98 } 99 100 if (traceEnable) { 101 enableLDAPTrace(env, testname); 102 } else { 103 if (socket != null) { 104 env.put(TEST_LDAP_SERVER_THREAD, 105 startLDAPServer(socket, getCaptureFile(testname))); 106 env.put("java.naming.provider.url", 107 "ldap://localhost:" + socket.getLocalPort()); 108 } else { 109 // for tests which run against remote server or no server 110 // required 111 debug("Skip local LDAP Server creation " 112 + "since ServerSocket is null"); 113 } 114 } 115 116 return env; 117 } 118 119 /* 120 * Clean-up the directory context. 121 */ cleanup(DirContext ctx)122 public static void cleanup(DirContext ctx) { 123 if (ctx != null) { 124 try { 125 ctx.close(); 126 } catch (NamingException e) { 127 // ignore 128 } 129 } 130 } 131 132 /* 133 * Clean-up the sub context. 134 */ cleanupSubcontext(DirContext ctx, String name)135 public static void cleanupSubcontext(DirContext ctx, String name) { 136 if (ctx != null) { 137 try { 138 ctx.destroySubcontext(name); 139 } catch (NamingException ne) { 140 // ignore 141 } 142 } 143 } 144 145 /* 146 * Assemble a distinguished name from the base components and the 147 * namespace root. 148 * 149 * The components are prefixed with 'dc=' if the root is a DC-style name. 150 * Otherwise they are prefixed with 'ou='. 151 */ buildDN(String[] bases, String root)152 public static String buildDN(String[] bases, String root) { 153 154 StringBuilder dn = new StringBuilder(); 155 String prefix; 156 157 if (!root.contains("dc=")) { 158 prefix = "ou="; 159 } else { 160 prefix = "dc="; 161 } 162 163 for (String base : bases) { 164 dn.append(prefix).append(base).append(","); 165 } 166 167 return dn.append(root).toString(); 168 } 169 170 /* 171 * Scan the results to confirm that the expected name is present. 172 */ checkResult(NamingEnumeration results, String name)173 public static int checkResult(NamingEnumeration results, String name) 174 throws NamingException { 175 176 return checkResult(results, new String[] { name }, null); 177 } 178 179 /* 180 * Scan the results to confirm that the expected names and attributes 181 * are present. 182 */ checkResult(NamingEnumeration results, String[] names, Attributes attrs)183 public static int checkResult(NamingEnumeration results, String[] names, 184 Attributes attrs) throws NamingException { 185 186 int found = 0; 187 188 while (results != null && results.hasMore()) { 189 190 SearchResult entry = (SearchResult) results.next(); 191 String entryDN = entry.getName(); 192 193 debug(">>> received: " + entryDN); 194 195 if (entry.isRelative()) { 196 entryDN = entryDN.toLowerCase(); // normalize 197 } else { 198 LdapURL url = new LdapURL(entryDN); // extract DN 199 entryDN = url.getDN().toLowerCase(); // normalize 200 } 201 202 for (String name : names) { 203 if ((entryDN.contains(name.toLowerCase())) || (entryDN 204 .equalsIgnoreCase(name))) { 205 206 debug(">>> checked results: found '" + name + "'"); 207 208 if (attrs == null || foundAttributes(entry, attrs)) { 209 found++; 210 break; 211 } 212 } 213 } 214 } 215 216 debug(">>> checked results: found " + found 217 + " entries that meet the criteria."); 218 219 return found; 220 } 221 222 /* 223 * Confirm that the attributes are present in the entry. 224 */ foundAttributes(SearchResult entry, Attributes attrs)225 public static boolean foundAttributes(SearchResult entry, Attributes attrs) 226 throws NamingException { 227 228 Attributes eattrs = entry.getAttributes(); 229 int found = 0; 230 231 if ((eattrs == null) || (attrs == null)) { 232 return false; 233 } 234 235 for (NamingEnumeration ne = attrs.getAll(); ne.hasMoreElements(); ) { 236 237 Attribute attr = (Attribute) ne.next(); 238 239 if (equalsIgnoreCase(eattrs.get(attr.getID()), attr)) { 240 found++; 241 } else { 242 debug(">>> foundAttributes: no match for " + attr.getID()); 243 } 244 } 245 debug(">>> foundAttributes: found " + found + " attributes"); 246 return (found == attrs.size()); 247 } 248 startLDAPServer(ServerSocket serverSocket, String fileName)249 public static Thread startLDAPServer(ServerSocket serverSocket, 250 String fileName) { 251 if (serverSocket == null) { 252 throw new RuntimeException("Error: failed to create LDAPServer " 253 + "since ServerSocket is null"); 254 } 255 256 if (!Files.exists(Paths.get(fileName))) { 257 throw new RuntimeException( 258 "Error: failed to create LDAPServer, not found ldap " 259 + "cache file " + fileName); 260 } 261 262 Thread thread = new Thread(() -> { 263 try { 264 new test.LDAPServer(serverSocket, fileName); 265 } catch (Exception e) { 266 System.out.println("Warning: LDAP server running with issue"); 267 e.printStackTrace(); 268 } 269 }); 270 271 thread.start(); 272 return thread; 273 } 274 equalsIgnoreCase(Attribute received, Attribute expected)275 private static boolean equalsIgnoreCase(Attribute received, 276 Attribute expected) { 277 278 if (received == null || !received.getID() 279 .equalsIgnoreCase(expected.getID())) { 280 return false; 281 } 282 283 try { 284 285 Enumeration expectedVals = expected.getAll(); 286 Object obj; 287 while (expectedVals.hasMoreElements()) { 288 obj = expectedVals.nextElement(); 289 if (!received.contains(obj)) { 290 if (!(obj instanceof String)) { 291 return false; 292 } 293 if (!received.contains(((String) obj).toLowerCase())) { 294 return false; 295 } 296 } 297 } 298 299 } catch (NamingException e) { 300 return false; 301 } 302 303 return true; 304 } 305 extractProperty(String propString, Hashtable<Object, Object> env)306 private static void extractProperty(String propString, 307 Hashtable<Object, Object> env) { 308 int index; 309 310 if ((index = propString.indexOf('=')) > 0) { 311 env.put(propString.substring(0, index), 312 propString.substring(index + 1)); 313 } else { 314 throw new RuntimeException( 315 "Failed to extract test args property from " + propString); 316 } 317 } 318 enableLDAPTrace(Hashtable<Object, Object> env, String testname)319 private static void enableLDAPTrace(Hashtable<Object, Object> env, 320 String testname) { 321 try { 322 PrintStream outStream = new PrintStream(getCaptureFile(testname)); 323 env.put("com.sun.jndi.ldap.trace.ber", outStream); 324 } catch (FileNotFoundException e) { 325 throw new RuntimeException( 326 "Error: failed to enable ldap trace: " + e.getMessage(), e); 327 } 328 } 329 getCaptureFile(String testname)330 private static String getCaptureFile(String testname) { 331 return Paths.get(System.getProperty("test.src")) 332 .resolve(testname + ".ldap").toString(); 333 } 334 debug(Object object)335 public static void debug(Object object) { 336 if (debug) { 337 System.out.println(object); 338 } 339 } 340 findCertsHome(int depth)341 public static String findCertsHome(int depth) { 342 Path path = Paths.get(System.getProperty("test.src", ".")) 343 .toAbsolutePath(); 344 for (int i = depth; i >= 0; i--) { 345 Path homePath = path.resolve("certs"); 346 if (Files.exists(homePath) && Files.isDirectory(homePath)) { 347 return homePath.toString(); 348 } 349 350 path = path.getParent(); 351 if (path == null) { 352 break; 353 } 354 } 355 356 return System.getProperty("test.src", "."); 357 } 358 } 359