1 /* 2 * Copyright (c) 1999, 2013, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.jndi.ldap; 27 28 import java.util.Hashtable; 29 import java.util.Vector; 30 import java.util.Enumeration; 31 32 import javax.naming.*; 33 import javax.naming.directory.*; 34 import javax.naming.spi.ObjectFactory; 35 import javax.naming.spi.InitialContextFactory; 36 import javax.naming.ldap.Control; 37 38 import com.sun.jndi.url.ldap.ldapURLContextFactory; 39 40 final public class LdapCtxFactory implements ObjectFactory, InitialContextFactory { 41 /** 42 * The type of each address in an LDAP reference. 43 */ 44 public final static String ADDRESS_TYPE = "URL"; 45 46 // ----------------- ObjectFactory interface -------------------- 47 48 public Object getObjectInstance(Object ref, Name name, Context nameCtx, 49 Hashtable<?,?> env) throws Exception { 50 51 if (!isLdapRef(ref)) { 52 return null; 53 } 54 ObjectFactory factory = new ldapURLContextFactory(); 55 String[] urls = getURLs((Reference)ref); 56 return factory.getObjectInstance(urls, name, nameCtx, env); 57 } 58 59 // ----------------- InitialContext interface -------------------- 60 61 public Context getInitialContext(Hashtable<?,?> envprops) 62 throws NamingException { 63 64 try { 65 String providerUrl = (envprops != null) ? 66 (String)envprops.get(Context.PROVIDER_URL) : null; 67 68 // If URL not in environment, use defaults 69 if (providerUrl == null) { 70 return new LdapCtx("", LdapCtx.DEFAULT_HOST, 71 LdapCtx.DEFAULT_PORT, envprops, false); 72 } 73 74 // Extract URL(s) 75 String[] urls = LdapURL.fromList(providerUrl); 76 77 if (urls.length == 0) { 78 throw new ConfigurationException(Context.PROVIDER_URL + 79 " property does not contain a URL"); 80 } 81 82 // Generate an LDAP context 83 return getLdapCtxInstance(urls, envprops); 84 85 } catch (LdapReferralException e) { 86 87 if (envprops != null && 88 "throw".equals(envprops.get(Context.REFERRAL))) { 89 throw e; 90 } 91 92 Control[] bindCtls = (envprops != null)? 93 (Control[])envprops.get(LdapCtx.BIND_CONTROLS) : null; 94 95 return (LdapCtx)e.getReferralContext(envprops, bindCtls); 96 } 97 } 98 99 /** 100 * Returns true if argument is an LDAP reference. 101 */ 102 private static boolean isLdapRef(Object obj) { 103 104 if (!(obj instanceof Reference)) { 105 return false; 106 } 107 String thisClassName = LdapCtxFactory.class.getName(); 108 Reference ref = (Reference)obj; 109 110 return thisClassName.equals(ref.getFactoryClassName()); 111 } 112 113 /** 114 * Returns the URLs contained within an LDAP reference. 115 */ 116 private static String[] getURLs(Reference ref) throws NamingException { 117 118 int size = 0; // number of URLs 119 String[] urls = new String[ref.size()]; 120 121 Enumeration<RefAddr> addrs = ref.getAll(); 122 while (addrs.hasMoreElements()) { 123 RefAddr addr = addrs.nextElement(); 124 125 if ((addr instanceof StringRefAddr) && 126 addr.getType().equals(ADDRESS_TYPE)) { 127 128 urls[size++] = (String)addr.getContent(); 129 } 130 } 131 if (size == 0) { 132 throw (new ConfigurationException( 133 "Reference contains no valid addresses")); 134 } 135 136 // Trim URL array down to size. 137 if (size == ref.size()) { 138 return urls; 139 } 140 String[] urls2 = new String[size]; 141 System.arraycopy(urls, 0, urls2, 0, size); 142 return urls2; 143 } 144 145 // ------------ Utilities used by other classes ---------------- 146 147 public static DirContext getLdapCtxInstance(Object urlInfo, Hashtable<?,?> env) 148 throws NamingException { 149 150 if (urlInfo instanceof String) { 151 return getUsingURL((String)urlInfo, env); 152 } else if (urlInfo instanceof String[]) { 153 return getUsingURLs((String[])urlInfo, env); 154 } else { 155 throw new IllegalArgumentException( 156 "argument must be an LDAP URL String or array of them"); 157 } 158 } 159 160 private static DirContext getUsingURL(String url, Hashtable<?,?> env) 161 throws NamingException { 162 DirContext ctx = null; 163 LdapURL ldapUrl = new LdapURL(url); 164 String dn = ldapUrl.getDN(); 165 String host = ldapUrl.getHost(); 166 int port = ldapUrl.getPort(); 167 String[] hostports; 168 String domainName = null; 169 170 // handle a URL with no hostport (ldap:/// or ldaps:///) 171 // locate the LDAP service using the URL's distinguished name 172 if (host == null && 173 port == -1 && 174 dn != null && 175 (domainName = ServiceLocator.mapDnToDomainName(dn)) != null && 176 (hostports = ServiceLocator.getLdapService(domainName, env)) 177 != null) { 178 // Generate new URLs that include the discovered hostports. 179 // Reuse the original URL scheme. 180 String scheme = ldapUrl.getScheme() + "://"; 181 String[] newUrls = new String[hostports.length]; 182 String query = ldapUrl.getQuery(); 183 String urlSuffix = ldapUrl.getPath() + (query != null ? query : ""); 184 for (int i = 0; i < hostports.length; i++) { 185 newUrls[i] = scheme + hostports[i] + urlSuffix; 186 } 187 ctx = getUsingURLs(newUrls, env); 188 // Associate the derived domain name with the context 189 ((LdapCtx)ctx).setDomainName(domainName); 190 191 } else { 192 ctx = new LdapCtx(dn, host, port, env, ldapUrl.useSsl()); 193 // Record the URL that created the context 194 ((LdapCtx)ctx).setProviderUrl(url); 195 } 196 return ctx; 197 } 198 199 /* 200 * Try each URL until one of them succeeds. 201 * If all URLs fail, throw one of the exceptions arbitrarily. 202 * Not pretty, but potentially more informative than returning null. 203 */ 204 private static DirContext getUsingURLs(String[] urls, Hashtable<?,?> env) 205 throws NamingException { 206 NamingException ne = null; 207 DirContext ctx = null; 208 for (int i = 0; i < urls.length; i++) { 209 try { 210 return getUsingURL(urls[i], env); 211 } catch (AuthenticationException e) { 212 throw e; 213 } catch (NamingException e) { 214 ne = e; 215 } 216 } 217 throw ne; 218 } 219 220 /** 221 * Used by Obj and obj/RemoteToAttrs too so must be public 222 */ 223 public static Attribute createTypeNameAttr(Class<?> cl) { 224 Vector<String> v = new Vector<>(10); 225 String[] types = getTypeNames(cl, v); 226 if (types.length > 0) { 227 BasicAttribute tAttr = 228 new BasicAttribute(Obj.JAVA_ATTRIBUTES[Obj.TYPENAME]); 229 for (int i = 0; i < types.length; i++) { 230 tAttr.add(types[i]); 231 } 232 return tAttr; 233 } 234 return null; 235 } 236 237 private static String[] getTypeNames(Class<?> currentClass, Vector<String> v) { 238 239 getClassesAux(currentClass, v); 240 Class<?>[] members = currentClass.getInterfaces(); 241 for (int i = 0; i < members.length; i++) { 242 getClassesAux(members[i], v); 243 } 244 String[] ret = new String[v.size()]; 245 int i = 0; 246 247 for (String name : v) { 248 ret[i++] = name; 249 } 250 return ret; 251 } 252 253 private static void getClassesAux(Class<?> currentClass, Vector<String> v) { 254 if (!v.contains(currentClass.getName())) { 255 v.addElement(currentClass.getName()); 256 } 257 currentClass = currentClass.getSuperclass(); 258 259 while (currentClass != null) { 260 getTypeNames(currentClass, v); 261 currentClass = currentClass.getSuperclass(); 262 } 263 } 264 }