8160768: Add capability to custom resolve host/domain names within the default JNDI LDAP provider
Reviewed-by: alanb, dfuchs, chegar, mchung, vtewari
This commit is contained in:
parent
b33edbf1ec
commit
d3ccf8a0ca
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.sun.jndi.ldap;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.ldap.spi.LdapDnsProvider;
|
||||
import javax.naming.ldap.spi.LdapDnsProviderResult;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class DefaultLdapDnsProvider {
|
||||
|
||||
public Optional<LdapDnsProviderResult> lookupEndpoints(String url,
|
||||
Map<?,?> env)
|
||||
throws NamingException
|
||||
{
|
||||
if (url == null || env == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
String domainName;
|
||||
List<String> endpoints = new ArrayList<>();
|
||||
LdapURL ldapUrl = new LdapURL(url);
|
||||
String dn = ldapUrl.getDN();
|
||||
String host = ldapUrl.getHost();
|
||||
int port = ldapUrl.getPort();
|
||||
String[] hostports;
|
||||
|
||||
// handle a URL with no hostport (ldap:/// or ldaps:///)
|
||||
// locate the LDAP service using the URL's distinguished name
|
||||
if (host == null
|
||||
&& port == -1
|
||||
&& dn != null
|
||||
&& (domainName = ServiceLocator.mapDnToDomainName(dn)) != null
|
||||
&& (hostports = ServiceLocator.getLdapService(domainName, env)) != null) {
|
||||
// Generate new URLs that include the discovered hostports.
|
||||
// Reuse the original URL scheme.
|
||||
String scheme = ldapUrl.getScheme() + "://";
|
||||
String query = ldapUrl.getQuery();
|
||||
String urlSuffix = ldapUrl.getPath() + (query != null ? query : "");
|
||||
for (String hostPort : hostports) {
|
||||
// the hostports come from the DNS SRV records
|
||||
// we assume the SRV record is scheme aware
|
||||
endpoints.add(scheme + hostPort + urlSuffix);
|
||||
}
|
||||
} else {
|
||||
// we don't have enough information to set the domain name
|
||||
// correctly
|
||||
domainName = "";
|
||||
endpoints.add(url);
|
||||
}
|
||||
|
||||
LdapDnsProviderResult res = new LdapDnsProviderResult(domainName, endpoints);
|
||||
if (res.getEndpoints().size() == 0 && res.getDomainName().isEmpty()) {
|
||||
return Optional.empty();
|
||||
} else {
|
||||
return Optional.of(res);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 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
|
||||
@ -31,6 +31,7 @@ import java.util.Enumeration;
|
||||
|
||||
import javax.naming.*;
|
||||
import javax.naming.directory.*;
|
||||
import javax.naming.ldap.spi.LdapDnsProviderResult;
|
||||
import javax.naming.spi.ObjectFactory;
|
||||
import javax.naming.spi.InitialContextFactory;
|
||||
import javax.naming.ldap.Control;
|
||||
@ -158,41 +159,73 @@ final public class LdapCtxFactory implements ObjectFactory, InitialContextFactor
|
||||
}
|
||||
|
||||
private static DirContext getUsingURL(String url, Hashtable<?,?> env)
|
||||
throws NamingException {
|
||||
DirContext ctx = null;
|
||||
LdapURL ldapUrl = new LdapURL(url);
|
||||
String dn = ldapUrl.getDN();
|
||||
String host = ldapUrl.getHost();
|
||||
int port = ldapUrl.getPort();
|
||||
String[] hostports;
|
||||
String domainName = null;
|
||||
throws NamingException
|
||||
{
|
||||
try {
|
||||
LdapDnsProviderResult r =
|
||||
LdapDnsProviderService.getInstance().lookupEndpoints(url, env);
|
||||
LdapCtx ctx;
|
||||
NamingException lastException = null;
|
||||
|
||||
// handle a URL with no hostport (ldap:/// or ldaps:///)
|
||||
// locate the LDAP service using the URL's distinguished name
|
||||
if (host == null &&
|
||||
port == -1 &&
|
||||
dn != null &&
|
||||
(domainName = ServiceLocator.mapDnToDomainName(dn)) != null &&
|
||||
(hostports = ServiceLocator.getLdapService(domainName, env))
|
||||
!= null) {
|
||||
// Generate new URLs that include the discovered hostports.
|
||||
// Reuse the original URL scheme.
|
||||
String scheme = ldapUrl.getScheme() + "://";
|
||||
String[] newUrls = new String[hostports.length];
|
||||
String query = ldapUrl.getQuery();
|
||||
String urlSuffix = ldapUrl.getPath() + (query != null ? query : "");
|
||||
for (int i = 0; i < hostports.length; i++) {
|
||||
newUrls[i] = scheme + hostports[i] + urlSuffix;
|
||||
/*
|
||||
* Prior to this change we had been assuming that the url.getDN()
|
||||
* should be converted to a domain name via
|
||||
* ServiceLocator.mapDnToDomainName(url.getDN())
|
||||
*
|
||||
* However this is incorrect as we can't assume that the supplied
|
||||
* url.getDN() is the same as the dns domain for the directory
|
||||
* server.
|
||||
*
|
||||
* This means that we depend on the dnsProvider to return both
|
||||
* the list of urls of individual hosts from which we attempt to
|
||||
* create an LdapCtx from *AND* the domain name that they serve
|
||||
*
|
||||
* In order to do this the dnsProvider must return an
|
||||
* {@link LdapDnsProviderResult}.
|
||||
*
|
||||
*/
|
||||
for (String u : r.getEndpoints()) {
|
||||
try {
|
||||
ctx = getLdapCtxFromUrl(
|
||||
r.getDomainName(), url, new LdapURL(u), env);
|
||||
return ctx;
|
||||
} catch (NamingException e) {
|
||||
// try the next element
|
||||
lastException = e;
|
||||
}
|
||||
}
|
||||
ctx = getUsingURLs(newUrls, env);
|
||||
// Associate the derived domain name with the context
|
||||
((LdapCtx)ctx).setDomainName(domainName);
|
||||
|
||||
} else {
|
||||
ctx = new LdapCtx(dn, host, port, env, ldapUrl.useSsl());
|
||||
// Record the URL that created the context
|
||||
((LdapCtx)ctx).setProviderUrl(url);
|
||||
if (lastException != null) {
|
||||
throw lastException;
|
||||
}
|
||||
|
||||
// lookupEndpoints returned an LdapDnsProviderResult with an empty
|
||||
// list of endpoints
|
||||
throw new NamingException("Could not resolve a valid ldap host");
|
||||
} catch (NamingException e) {
|
||||
// lookupEndpoints(url, env) may throw a NamingException, which
|
||||
// there is no need to wrap.
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
NamingException ex = new NamingException();
|
||||
ex.setRootCause(e);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
private static LdapCtx getLdapCtxFromUrl(String domain,
|
||||
String url,
|
||||
LdapURL u,
|
||||
Hashtable<?,?> env)
|
||||
throws NamingException
|
||||
{
|
||||
String dn = u.getDN();
|
||||
String host = u.getHost();
|
||||
int port = u.getPort();
|
||||
LdapCtx ctx = new LdapCtx(dn, host, port, env, u.useSsl());
|
||||
ctx.setDomainName(domain);
|
||||
// Record the URL that created the context
|
||||
ctx.setProviderUrl(url);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
@ -202,19 +235,17 @@ final public class LdapCtxFactory implements ObjectFactory, InitialContextFactor
|
||||
* Not pretty, but potentially more informative than returning null.
|
||||
*/
|
||||
private static DirContext getUsingURLs(String[] urls, Hashtable<?,?> env)
|
||||
throws NamingException {
|
||||
NamingException ne = null;
|
||||
DirContext ctx = null;
|
||||
for (int i = 0; i < urls.length; i++) {
|
||||
throws NamingException
|
||||
{
|
||||
NamingException ex = null;
|
||||
for (String u : urls) {
|
||||
try {
|
||||
return getUsingURL(urls[i], env);
|
||||
} catch (AuthenticationException e) {
|
||||
throw e;
|
||||
return getUsingURL(u, env);
|
||||
} catch (NamingException e) {
|
||||
ne = e;
|
||||
ex = e;
|
||||
}
|
||||
}
|
||||
throw ne;
|
||||
throw ex;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.sun.jndi.ldap;
|
||||
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.*;
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.ldap.spi.LdapDnsProvider;
|
||||
import javax.naming.ldap.spi.LdapDnsProviderResult;
|
||||
import sun.security.util.SecurityConstants;
|
||||
|
||||
/**
|
||||
* The {@code LdapDnsProviderService} is responsible for creating and providing
|
||||
* access to the registered {@code LdapDnsProvider}s. The {@link ServiceLoader}
|
||||
* is used to find and register any implementations of {@link LdapDnsProvider}.
|
||||
*/
|
||||
final class LdapDnsProviderService {
|
||||
|
||||
private static volatile LdapDnsProviderService service;
|
||||
private static final Object LOCK = new int[0];
|
||||
private final ServiceLoader<LdapDnsProvider> providers;
|
||||
|
||||
/**
|
||||
* Creates a new instance of LdapDnsProviderService
|
||||
*/
|
||||
private LdapDnsProviderService() {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm == null) {
|
||||
providers = ServiceLoader.load(
|
||||
LdapDnsProvider.class,
|
||||
ClassLoader.getSystemClassLoader());
|
||||
} else {
|
||||
final PrivilegedAction<ServiceLoader<LdapDnsProvider>> pa =
|
||||
() -> ServiceLoader.load(
|
||||
LdapDnsProvider.class,
|
||||
ClassLoader.getSystemClassLoader());
|
||||
|
||||
providers = AccessController.doPrivileged(
|
||||
pa,
|
||||
null,
|
||||
new RuntimePermission("ldapDnsProvider"),
|
||||
SecurityConstants.GET_CLASSLOADER_PERMISSION);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the singleton static instance of LdapDnsProviderService.
|
||||
*/
|
||||
static LdapDnsProviderService getInstance() {
|
||||
if (service != null) return service;
|
||||
synchronized(LOCK) {
|
||||
if (service != null) return service;
|
||||
service = new LdapDnsProviderService();
|
||||
}
|
||||
return service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve result from the first provider that successfully resolves
|
||||
* the endpoints. If no results are found when calling installed
|
||||
* subclasses of {@code LdapDnsProvider} then this method will fall back
|
||||
* to the {@code DefaultLdapDnsProvider}.
|
||||
*
|
||||
* @throws NamingException if the {@code url} in not valid or an error
|
||||
* occurred while performing the lookup.
|
||||
*/
|
||||
LdapDnsProviderResult lookupEndpoints(String url, Hashtable<?,?> env)
|
||||
throws NamingException
|
||||
{
|
||||
Iterator<LdapDnsProvider> iterator = providers.iterator();
|
||||
Hashtable<?, ?> envCopy = new Hashtable<>(env);
|
||||
LdapDnsProviderResult result = null;
|
||||
|
||||
while (result == null && iterator.hasNext()) {
|
||||
result = iterator.next().lookupEndpoints(url, envCopy)
|
||||
.filter(r -> r.getEndpoints().size() > 0)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
return new DefaultLdapDnsProvider().lookupEndpoints(url, env)
|
||||
.orElse(new LdapDnsProviderResult("", List.of()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2002, 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
|
||||
@ -25,11 +25,7 @@
|
||||
|
||||
package com.sun.jndi.ldap;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Random;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
import javax.naming.*;
|
||||
import javax.naming.directory.*;
|
||||
@ -103,6 +99,23 @@ class ServiceLocator {
|
||||
return (domain.length() != 0) ? domain.toString() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates the LDAP service for a given domain.
|
||||
* Queries DNS for a list of LDAP Service Location Records (SRV) for a
|
||||
* given domain name.
|
||||
*
|
||||
* @param domainName A string domain name.
|
||||
* @param environment The possibly null environment of the context.
|
||||
* @return An ordered list of hostports for the LDAP service or null if
|
||||
* the service has not been located.
|
||||
*/
|
||||
static String[] getLdapService(String domainName, Map<?,?> environment) {
|
||||
if (environment instanceof Hashtable) {
|
||||
return getLdapService(domainName, (Hashtable)environment);
|
||||
}
|
||||
return getLdapService(domainName, new Hashtable<>(environment));
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates the LDAP service for a given domain.
|
||||
* Queries DNS for a list of LDAP Service Location Records (SRV) for a
|
||||
|
@ -27,7 +27,6 @@
|
||||
package javax.naming.directory;
|
||||
|
||||
import java.util.Hashtable;
|
||||
import javax.naming.spi.NamingManager;
|
||||
import javax.naming.*;
|
||||
|
||||
/**
|
||||
@ -84,6 +83,36 @@ public class InitialDirContext extends InitialContext implements DirContext {
|
||||
* Environment properties are discussed in the
|
||||
* {@code javax.naming.InitialContext} class description.
|
||||
*
|
||||
* <p> If the {@code java.naming.provider.url} property of the supplied
|
||||
* environment consists of a URL (or a list of URLs) using the ldap
|
||||
* protocol the resulting {@link javax.naming.ldap.LdapContext} will use
|
||||
* an LDAP server resolved by the configured {@link
|
||||
* javax.naming.ldap.spi.LdapDnsProvider LdapDnsProviders}:
|
||||
* <ol>
|
||||
* <li>If this is the first {@code InitialDirContext} created with a
|
||||
* {@code java.naming.provider.url} using the ldap protocol then the
|
||||
* {@linkplain java.util.ServiceLoader ServiceLoader} mechanism is
|
||||
* used to locate {@linkplain javax.naming.ldap.spi.LdapDnsProvider
|
||||
* LdapDnsProvider} implementations using the system class loader.
|
||||
* The order that providers are located is implementation specific
|
||||
* and an implementation is free to cache the located providers.
|
||||
* <li>The {@code lookupEndpoints} method of each provider, if instantiated,
|
||||
* is invoked once with a combination of each of the URLs in the the
|
||||
* {@code java.naming.provider.url} property and the environment until
|
||||
* a provider returns non-empty or all providers have been exhausted.
|
||||
* If none of the
|
||||
* {@linkplain javax.naming.ldap.spi.LdapDnsProvider LdapDnsProviders}
|
||||
* return a non-empty
|
||||
* {@linkplain javax.naming.ldap.spi.LdapDnsProviderResult result} then
|
||||
* the implementation will make a best-effort attempt to determine an
|
||||
* endpoint. A
|
||||
* {@linkplain java.util.ServiceConfigurationError ServiceConfigurationError},
|
||||
* {@code Error} or {@code RuntimeException} thrown when loading or
|
||||
* calling an {@linkplain javax.naming.ldap.spi.LdapDnsProvider
|
||||
* LdapDnsProvider}, if encountered, will be propagated to the calling
|
||||
* thread.
|
||||
* </ol>
|
||||
*
|
||||
* <p> This constructor will not modify {@code environment}
|
||||
* or save a reference to it, but may save a clone.
|
||||
* Caller should not modify mutable keys and values in
|
||||
|
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package javax.naming.ldap.spi;
|
||||
|
||||
import javax.naming.Context;
|
||||
import javax.naming.NamingException;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Service-provider class for DNS lookups when performing LDAP operations.
|
||||
*
|
||||
* <p> An LDAP DNS provider is a concrete subclass of this class that
|
||||
* has a zero-argument constructor. LDAP DNS providers are located using the
|
||||
* ServiceLoader facility, as specified by
|
||||
* {@linkplain javax.naming.directory.InitialDirContext InitialDirectContext}.
|
||||
*
|
||||
* The
|
||||
* {@link java.util.ServiceLoader ServiceLoader} is used to create and register
|
||||
* implementations of {@code LdapDnsProvider}.
|
||||
*
|
||||
* <p> An LDAP DNS provider can be used in environments where the default
|
||||
* DNS resolution mechanism is not sufficient to accurately pinpoint the
|
||||
* correct LDAP servers needed to perform LDAP operations. For example, in an
|
||||
* environment containing a mix of {@code ldap} and {@code ldaps} servers
|
||||
* you may want the {@linkplain javax.naming.ldap.LdapContext LdapContext}
|
||||
* to query {@code ldaps} servers only.
|
||||
*
|
||||
* @since 12
|
||||
*/
|
||||
public abstract class LdapDnsProvider {
|
||||
|
||||
// The {@code RuntimePermission("ldapDnsProvider")} is
|
||||
// necessary to subclass and instantiate the {@code LdapDnsProvider} class.
|
||||
private static final RuntimePermission DNSPROVIDER_PERMISSION =
|
||||
new RuntimePermission("ldapDnsProvider");
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code LdapDnsProvider}.
|
||||
*
|
||||
* @throws SecurityException if a security manager is present and its
|
||||
* {@code checkPermission} method doesn't allow
|
||||
* the {@code RuntimePermission("ldapDnsProvider")}.
|
||||
*/
|
||||
protected LdapDnsProvider() {
|
||||
this(checkPermission());
|
||||
}
|
||||
|
||||
private LdapDnsProvider(Void unused) {
|
||||
// nothing to do.
|
||||
}
|
||||
|
||||
private static Void checkPermission() {
|
||||
final SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
sm.checkPermission(DNSPROVIDER_PERMISSION);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup the endpoints and domain name for the given {@link Context}
|
||||
* {@link Context#PROVIDER_URL provider URL} and environment. The resolved
|
||||
* endpoints and domain name are returned as an
|
||||
* {@link LdapDnsProviderResult}.
|
||||
*
|
||||
* <p> An endpoint is a {@code String} representation of an LDAP URL which
|
||||
* points to an LDAP server to be used for LDAP operations. The syntax of
|
||||
* an LDAP URL is defined by <a href="http://www.ietf.org/rfc/rfc2255.txt">
|
||||
* <i>RFC 2255: The LDAP URL Format</i></a>.
|
||||
*
|
||||
* @param url The {@link Context} {@link Context#PROVIDER_URL provider URL}
|
||||
* @param env The {@link Context} environment.
|
||||
*
|
||||
* @return an {@link LdapDnsProviderResult} or empty {@code Optional}
|
||||
* if the lookup fails.
|
||||
*
|
||||
* @throws NamingException if the {@code url} is not valid or an error
|
||||
* occurred while performing the lookup.
|
||||
* @throws NullPointerException if either {@code url} or {@code env} are
|
||||
* {@code null}.
|
||||
*/
|
||||
public abstract Optional<LdapDnsProviderResult> lookupEndpoints(
|
||||
String url, Map<?,?> env) throws NamingException;
|
||||
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package javax.naming.ldap.spi;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The result of a DNS lookup for an LDAP URL.
|
||||
*
|
||||
* <p> This class is used by an {@link LdapDnsProvider} to return the result
|
||||
* of a DNS lookup for a given LDAP URL. The result consists of a domain name
|
||||
* and its associated ldap server endpoints.
|
||||
*
|
||||
* <p> A {@code null} {@code domainName} is equivalent to and represented
|
||||
* by an empty string.
|
||||
*
|
||||
* @since 12
|
||||
*/
|
||||
public final class LdapDnsProviderResult {
|
||||
|
||||
private final String domainName;
|
||||
private final List<String> endpoints;
|
||||
|
||||
/**
|
||||
* Construct an LdapDnsProviderResult consisting of a resolved domain name
|
||||
* and the ldap server endpoints that serve the domain.
|
||||
*
|
||||
* @param domainName the resolved domain name; can be null.
|
||||
* @param endpoints the possibly empty list of resolved ldap server
|
||||
* endpoints
|
||||
*
|
||||
* @throws NullPointerException if {@code endpoints} contains {@code null}
|
||||
* elements.
|
||||
* @throws ClassCastException if {@code endpoints} contains non-
|
||||
* {@code String} elements.
|
||||
*/
|
||||
public LdapDnsProviderResult(String domainName, List<String> endpoints) {
|
||||
this.domainName = (domainName == null) ? "" : domainName;
|
||||
this.endpoints = List.copyOf(endpoints);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the domain name resolved from the ldap URL. This method returns
|
||||
* the empty string if the {@code LdapDnsProviderResult} is created with a
|
||||
* null domain name.
|
||||
*
|
||||
* @return the resolved domain name
|
||||
*/
|
||||
public String getDomainName() {
|
||||
return domainName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the possibly empty list of individual server endpoints resolved
|
||||
* from the ldap URL.
|
||||
*
|
||||
* @return a possibly empty unmodifiable {@link List} containing the
|
||||
* resolved ldap server endpoints
|
||||
*/
|
||||
public List<String> getEndpoints() {
|
||||
return endpoints;
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 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
|
||||
@ -26,6 +26,10 @@
|
||||
/**
|
||||
* Defines the Java Naming and Directory Interface (JNDI) API.
|
||||
*
|
||||
* @provides javax.naming.ldap.spi.LdapDnsProvider
|
||||
*
|
||||
* @uses javax.naming.ldap.spi.LdapDnsProvider
|
||||
*
|
||||
* @moduleGraph
|
||||
* @since 9
|
||||
*/
|
||||
@ -37,6 +41,7 @@ module java.naming {
|
||||
exports javax.naming.event;
|
||||
exports javax.naming.ldap;
|
||||
exports javax.naming.spi;
|
||||
exports javax.naming.ldap.spi;
|
||||
|
||||
exports com.sun.jndi.toolkit.ctx to
|
||||
jdk.naming.dns;
|
||||
@ -46,6 +51,7 @@ module java.naming {
|
||||
|
||||
uses javax.naming.ldap.StartTlsResponse;
|
||||
uses javax.naming.spi.InitialContextFactory;
|
||||
uses javax.naming.ldap.spi.LdapDnsProvider;
|
||||
|
||||
provides java.security.Provider with
|
||||
sun.security.provider.certpath.ldap.JdkLDAP;
|
||||
|
231
test/jdk/com/sun/jndi/ldap/LdapDnsProviderTest.java
Normal file
231
test/jdk/com/sun/jndi/ldap/LdapDnsProviderTest.java
Normal file
@ -0,0 +1,231 @@
|
||||
/*
|
||||
* 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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 java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.Permission;
|
||||
import java.util.Hashtable;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
import javax.naming.Context;
|
||||
import javax.naming.InitialContext;
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.directory.InitialDirContext;
|
||||
import javax.naming.directory.SearchControls;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8160768
|
||||
* @summary ctx provider tests for ldap
|
||||
* @modules java.naming/com.sun.jndi.ldap
|
||||
* @compile dnsprovider/TestDnsProvider.java
|
||||
* @run main/othervm LdapDnsProviderTest
|
||||
* @run main/othervm LdapDnsProviderTest nosm
|
||||
* @run main/othervm LdapDnsProviderTest smnodns
|
||||
* @run main/othervm LdapDnsProviderTest smdns
|
||||
* @run main/othervm LdapDnsProviderTest nosmbaddns
|
||||
*/
|
||||
|
||||
class DNSSecurityManager extends SecurityManager {
|
||||
|
||||
|
||||
|
||||
/* run main/othervm LdapDnsProviderTest
|
||||
|
||||
* run main/othervm LdapDnsProviderTest nosm
|
||||
* run main/othervm LdapDnsProviderTest smnodns
|
||||
* run main/othervm LdapDnsProviderTest smdns
|
||||
* run main/othervm LdapDnsProviderTest nosmbaddns
|
||||
*/
|
||||
|
||||
private boolean dnsProvider = false;
|
||||
|
||||
public void setAllowDnsProvider(boolean allow) {
|
||||
dnsProvider = allow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkPermission(Permission p) {
|
||||
if (p.getName().equals("ldapDnsProvider") && !dnsProvider) {
|
||||
throw new SecurityException(p.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ProviderTest implements Callable<Boolean> {
|
||||
|
||||
private final String url;
|
||||
private final String expected;
|
||||
private final Hashtable<String, String> env = new Hashtable<>(11);
|
||||
|
||||
public ProviderTest(String url, String expected) {
|
||||
this.url = url;
|
||||
this.expected = expected;
|
||||
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
|
||||
}
|
||||
|
||||
boolean shutItDown(InitialContext ctx) {
|
||||
try {
|
||||
if (ctx != null) ctx.close();
|
||||
return true;
|
||||
} catch (NamingException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public Boolean call() {
|
||||
boolean passed;
|
||||
InitialContext ctx = null;
|
||||
|
||||
if (url != null) {
|
||||
env.put(Context.PROVIDER_URL, url);
|
||||
}
|
||||
|
||||
try {
|
||||
ctx = new InitialDirContext(env);
|
||||
SearchControls scl = new SearchControls();
|
||||
scl.setSearchScope(SearchControls.SUBTREE_SCOPE);
|
||||
((InitialDirContext)ctx).search(
|
||||
"ou=People,o=Test", "(objectClass=*)", scl);
|
||||
throw new RuntimeException("Search should not complete");
|
||||
} catch (NamingException e) {
|
||||
e.printStackTrace();
|
||||
passed = e.toString().contains(expected);
|
||||
} finally {
|
||||
shutItDown(ctx);
|
||||
}
|
||||
return passed;
|
||||
}
|
||||
}
|
||||
|
||||
public class LdapDnsProviderTest {
|
||||
|
||||
private static final String TEST_CLASSES =
|
||||
System.getProperty("test.classes", ".");
|
||||
|
||||
public static void writeFile(String content, File dstFile)
|
||||
throws IOException
|
||||
{
|
||||
try (FileOutputStream dst = new FileOutputStream(dstFile)) {
|
||||
byte[] buf = content.getBytes();
|
||||
dst.write(buf, 0, buf.length);
|
||||
}
|
||||
}
|
||||
|
||||
public static void installServiceConfigurationFile(String content) {
|
||||
String filename = "javax.naming.ldap.spi.LdapDnsProvider";
|
||||
|
||||
File dstDir = new File(TEST_CLASSES, "META-INF/services");
|
||||
if (!dstDir.exists()) {
|
||||
if (!dstDir.mkdirs()) {
|
||||
throw new RuntimeException(
|
||||
"could not create META-INF/services directory " + dstDir);
|
||||
}
|
||||
}
|
||||
File dstFile = new File(dstDir, filename);
|
||||
|
||||
try {
|
||||
writeFile(content, dstFile);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("could not install " + dstFile, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args.length > 0 && args[0].equals("nosm")) {
|
||||
// no security manager, serviceloader
|
||||
installServiceConfigurationFile("dnsprovider.TestDnsProvider");
|
||||
runTest("ldap:///dc=example,dc=com", "yupyupyup:389");
|
||||
} else if (args.length > 0 && args[0].equals("smnodns")) {
|
||||
// security manager & serviceloader
|
||||
installServiceConfigurationFile("dnsprovider.TestDnsProvider");
|
||||
// install security manager
|
||||
System.setSecurityManager(new DNSSecurityManager());
|
||||
runTest("ldap:///dc=example,dc=com", "ServiceConfigurationError");
|
||||
} else if (args.length > 0 && args[0].equals("smdns")) {
|
||||
// security manager & serviceloader
|
||||
DNSSecurityManager sm = new DNSSecurityManager();
|
||||
installServiceConfigurationFile("dnsprovider.TestDnsProvider");
|
||||
// install security manager
|
||||
System.setSecurityManager(sm);
|
||||
sm.setAllowDnsProvider(true);
|
||||
runTest("ldap:///dc=example,dc=com", "yupyupyup:389");
|
||||
} else if (args.length > 0 && args[0].equals("nosmbaddns")) {
|
||||
// no security manager, no serviceloader
|
||||
// DefaultLdapDnsProvider
|
||||
installServiceConfigurationFile("dnsprovider.MissingDnsProvider");
|
||||
// no SecurityManager
|
||||
runTest("ldap:///dc=example,dc=com", "not found");
|
||||
} else {
|
||||
// no security manager, no serviceloader
|
||||
// DefaultLdapDnsProvider
|
||||
System.err.println("TEST_CLASSES:");
|
||||
System.err.println(TEST_CLASSES);
|
||||
File f = new File(
|
||||
TEST_CLASSES, "META-INF/services/javax.naming.ldap.spi.LdapDnsProvider");
|
||||
if (f.exists()) {
|
||||
f.delete();
|
||||
}
|
||||
|
||||
// no SecurityManager
|
||||
runTest("ldap:///dc=example,dc=com", "localhost:389");
|
||||
runTest("ldap://localhost/dc=example,dc=com", "localhost:389");
|
||||
runTest("ldap://localhost:111/dc=example,dc=com", "localhost:111");
|
||||
runTest("ldaps://localhost:111/dc=example,dc=com", "localhost:111");
|
||||
runTest("ldaps://localhost/dc=example,dc=com", "localhost:636");
|
||||
runTest(null, "localhost:389");
|
||||
runTest("", "ConfigurationException");
|
||||
}
|
||||
}
|
||||
|
||||
private static void runTest(String url, String expected) {
|
||||
FutureTask<Boolean> future =
|
||||
new FutureTask<>(
|
||||
new ProviderTest(url, expected));
|
||||
new Thread(future).start();
|
||||
|
||||
System.err.println("Testing: " + url + ", " + expected);
|
||||
while (!future.isDone()) {
|
||||
try {
|
||||
if (!future.get()) {
|
||||
System.err.println("Test failed");
|
||||
throw new RuntimeException(
|
||||
"Test failed, ProviderTest returned false");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (!e.toString().contains(expected)) {
|
||||
System.err.println("Test failed");
|
||||
throw new RuntimeException(
|
||||
"Test failed, unexpected result");
|
||||
}
|
||||
}
|
||||
}
|
||||
System.err.println("Test passed");
|
||||
}
|
||||
|
||||
}
|
||||
|
20
test/jdk/com/sun/jndi/ldap/dnsprovider/TestDnsProvider.java
Normal file
20
test/jdk/com/sun/jndi/ldap/dnsprovider/TestDnsProvider.java
Normal file
@ -0,0 +1,20 @@
|
||||
package dnsprovider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import javax.naming.ldap.spi.LdapDnsProvider;
|
||||
import javax.naming.ldap.spi.LdapDnsProviderResult;
|
||||
|
||||
public class TestDnsProvider extends LdapDnsProvider {
|
||||
@Override
|
||||
public Optional<LdapDnsProviderResult> lookupEndpoints(String url,
|
||||
Map<?, ?> env)
|
||||
{
|
||||
List<String> endpoints = new ArrayList<>();
|
||||
endpoints.add("ldap://yupyupyup:389");
|
||||
return Optional.of(
|
||||
new LdapDnsProviderResult("test.com", endpoints));
|
||||
}
|
||||
}
|
@ -6,4 +6,5 @@ grant codeBase "file:${test.classes}/*" {
|
||||
//permission java.net.SocketPermission "*:636", "connect";
|
||||
//permission javax.security.auth.AuthPermission "modifyPrincipals";
|
||||
permission java.lang.RuntimePermission "accessClassInPackage.com.sun.jndi.ldap";
|
||||
permission java.lang.RuntimePermission "ldapDnsProvider";
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user