337 lines
10 KiB
Java
337 lines
10 KiB
Java
/*
|
|
* Copyright (c) 1999, 2022, 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.*;
|
|
import java.net.MalformedURLException;
|
|
import java.io.UnsupportedEncodingException;
|
|
import java.net.URI;
|
|
import java.security.AccessController;
|
|
import java.security.PrivilegedAction;
|
|
import sun.security.action.GetPropertyAction;
|
|
import java.util.Locale;
|
|
import java.util.StringTokenizer;
|
|
import com.sun.jndi.toolkit.url.Uri;
|
|
import com.sun.jndi.toolkit.url.UrlUtil;
|
|
|
|
/*
|
|
* Extract components of an LDAP URL.
|
|
*
|
|
* The format of an LDAP URL is defined in RFC 2255 as follows:
|
|
*
|
|
* ldapurl = scheme "://" [hostport] ["/"
|
|
* [dn ["?" [attributes] ["?" [scope]
|
|
* ["?" [filter] ["?" extensions]]]]]]
|
|
* scheme = "ldap"
|
|
* attributes = attrdesc *("," attrdesc)
|
|
* scope = "base" / "one" / "sub"
|
|
* dn = distinguishedName from Section 3 of [1]
|
|
* hostport = hostport from Section 5 of RFC 1738 [5]
|
|
* attrdesc = AttributeDescription from Section 4.1.5 of [2]
|
|
* filter = filter from Section 4 of [4]
|
|
* extensions = extension *("," extension)
|
|
* extension = ["!"] extype ["=" exvalue]
|
|
* extype = token / xtoken
|
|
* exvalue = LDAPString from section 4.1.2 of [2]
|
|
* token = oid from section 4.1 of [3]
|
|
* xtoken = ("X-" / "x-") token
|
|
*
|
|
* For example,
|
|
*
|
|
* ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US
|
|
* ldap://host.com:6666/o=IMC,c=US??sub?(cn=Babs%20Jensen)
|
|
*
|
|
* This class also supports ldaps URLs.
|
|
*/
|
|
|
|
final public class LdapURL extends Uri {
|
|
|
|
private static final String PARSE_MODE_PROP = "com.sun.jndi.ldapURLParsing";
|
|
private static final ParseMode DEFAULT_PARSE_MODE = ParseMode.COMPAT;
|
|
|
|
public static final ParseMode PARSE_MODE;
|
|
static {
|
|
PrivilegedAction<String> action =
|
|
new GetPropertyAction(PARSE_MODE_PROP, DEFAULT_PARSE_MODE.toString());
|
|
ParseMode parseMode = DEFAULT_PARSE_MODE;
|
|
try {
|
|
String mode = AccessController.doPrivileged(action);
|
|
parseMode = ParseMode.valueOf(mode.toUpperCase(Locale.ROOT));
|
|
} catch (Throwable t) {
|
|
parseMode = DEFAULT_PARSE_MODE;
|
|
} finally {
|
|
PARSE_MODE = parseMode;
|
|
}
|
|
}
|
|
|
|
private boolean useSsl = false;
|
|
private String DN = null;
|
|
private String attributes = null;
|
|
private String scope = null;
|
|
private String filter = null;
|
|
private String extensions = null;
|
|
|
|
/**
|
|
* Creates an LdapURL object from an LDAP URL string.
|
|
*/
|
|
public LdapURL(String url) throws NamingException {
|
|
|
|
super();
|
|
|
|
try {
|
|
init(url); // scheme, host, port, path, query
|
|
useSsl = scheme.equalsIgnoreCase("ldaps");
|
|
|
|
if (! (scheme.equalsIgnoreCase("ldap") || useSsl)) {
|
|
throw newInvalidURISchemeException(url);
|
|
}
|
|
|
|
parsePathAndQuery(); // DN, attributes, scope, filter, extensions
|
|
|
|
} catch (MalformedURLException e) {
|
|
NamingException ne = new NamingException("Cannot parse url: " + url);
|
|
ne.setRootCause(e);
|
|
throw ne;
|
|
} catch (UnsupportedEncodingException e) {
|
|
NamingException ne = new NamingException("Cannot parse url: " + url);
|
|
ne.setRootCause(e);
|
|
throw ne;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected MalformedURLException newInvalidURISchemeException(String uri) {
|
|
return new MalformedURLException("Not an LDAP URL: " + uri);
|
|
}
|
|
|
|
@Override
|
|
protected boolean isSchemeOnly(String uri) {
|
|
return isLdapSchemeOnly(uri);
|
|
}
|
|
|
|
@Override
|
|
protected ParseMode parseMode() {
|
|
return PARSE_MODE;
|
|
}
|
|
|
|
/**
|
|
* Returns true if the URL is an LDAPS URL.
|
|
*/
|
|
public boolean useSsl() {
|
|
return useSsl;
|
|
}
|
|
|
|
/**
|
|
* Returns the LDAP URL's distinguished name.
|
|
*/
|
|
public String getDN() {
|
|
return DN;
|
|
}
|
|
|
|
/**
|
|
* Returns the LDAP URL's attributes.
|
|
*/
|
|
public String getAttributes() {
|
|
return attributes;
|
|
}
|
|
|
|
/**
|
|
* Returns the LDAP URL's scope.
|
|
*/
|
|
public String getScope() {
|
|
return scope;
|
|
}
|
|
|
|
/**
|
|
* Returns the LDAP URL's filter.
|
|
*/
|
|
public String getFilter() {
|
|
return filter;
|
|
}
|
|
|
|
/**
|
|
* Returns the LDAP URL's extensions.
|
|
*/
|
|
public String getExtensions() {
|
|
return extensions;
|
|
}
|
|
|
|
/**
|
|
* Given a space-separated list of LDAP URLs, returns an array of strings.
|
|
*/
|
|
public static String[] fromList(String urlList) throws NamingException {
|
|
|
|
String[] urls = new String[(urlList.length() + 1) / 2];
|
|
int i = 0; // next available index in urls
|
|
StringTokenizer st = new StringTokenizer(urlList, " ");
|
|
|
|
while (st.hasMoreTokens()) {
|
|
// we don't accept scheme-only URLs here
|
|
urls[i++] = validateURI(st.nextToken());
|
|
}
|
|
String[] trimmed = new String[i];
|
|
System.arraycopy(urls, 0, trimmed, 0, i);
|
|
return trimmed;
|
|
}
|
|
|
|
public static boolean isLdapSchemeOnly(String uri) {
|
|
return "ldap:".equals(uri) || "ldaps:".equals(uri);
|
|
}
|
|
|
|
public static String validateURI(String uri) {
|
|
// no validation in legacy mode parsing
|
|
if (PARSE_MODE == ParseMode.LEGACY) {
|
|
return uri;
|
|
}
|
|
|
|
// special case of scheme-only URIs
|
|
if (isLdapSchemeOnly(uri)) {
|
|
return uri;
|
|
}
|
|
|
|
// use java.net.URI to validate the uri syntax
|
|
return URI.create(uri).toString();
|
|
}
|
|
|
|
/**
|
|
* Derermines whether an LDAP URL has query components.
|
|
*/
|
|
public static boolean hasQueryComponents(String url) {
|
|
return (url.lastIndexOf('?') != -1);
|
|
}
|
|
|
|
/*
|
|
* Assembles an LDAP or LDAPS URL string from its components.
|
|
* If "host" is an IPv6 literal, it may optionally include delimiting
|
|
* brackets.
|
|
*/
|
|
static String toUrlString(String host, int port, String dn, boolean useSsl)
|
|
{
|
|
|
|
try {
|
|
String h = (host != null) ? host : "";
|
|
if ((h.indexOf(':') != -1) && (h.charAt(0) != '[')) {
|
|
h = "[" + h + "]"; // IPv6 literal
|
|
}
|
|
String p = (port != -1) ? (":" + port) : "";
|
|
String d = (dn != null) ? ("/" + UrlUtil.encode(dn, "UTF8")) : "";
|
|
|
|
String uri = useSsl ? "ldaps://" + h + p + d : "ldap://" + h + p + d;
|
|
return validateURI(uri);
|
|
} catch (UnsupportedEncodingException e) {
|
|
// UTF8 should always be supported
|
|
throw new IllegalStateException("UTF-8 encoding unavailable");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Parses the path and query components of an URL and sets this
|
|
* object's fields accordingly.
|
|
*/
|
|
private void parsePathAndQuery() throws MalformedURLException,
|
|
UnsupportedEncodingException {
|
|
|
|
// path begins with a '/' or is empty
|
|
|
|
if (path.equals("")) {
|
|
return;
|
|
}
|
|
|
|
DN = path.startsWith("/") ? path.substring(1) : path;
|
|
if (DN.length() > 0) {
|
|
DN = UrlUtil.decode(DN, "UTF8");
|
|
}
|
|
|
|
// query begins with a '?' or is null
|
|
|
|
if (query == null || query.length() < 2) {
|
|
return;
|
|
}
|
|
|
|
int currentIndex = 1;
|
|
int nextQmark;
|
|
int endIndex;
|
|
|
|
// attributes:
|
|
nextQmark = query.indexOf('?', currentIndex);
|
|
endIndex = nextQmark == -1 ? query.length() : nextQmark;
|
|
if (endIndex - currentIndex > 0) {
|
|
attributes = query.substring(currentIndex, endIndex);
|
|
}
|
|
currentIndex = endIndex + 1;
|
|
if (currentIndex >= query.length()) {
|
|
return;
|
|
}
|
|
|
|
// scope:
|
|
nextQmark = query.indexOf('?', currentIndex);
|
|
endIndex = nextQmark == -1 ? query.length() : nextQmark;
|
|
if (endIndex - currentIndex > 0) {
|
|
scope = query.substring(currentIndex, endIndex);
|
|
}
|
|
currentIndex = endIndex + 1;
|
|
if (currentIndex >= query.length()) {
|
|
return;
|
|
}
|
|
|
|
// filter:
|
|
nextQmark = query.indexOf('?', currentIndex);
|
|
endIndex = nextQmark == -1 ? query.length() : nextQmark;
|
|
if (endIndex - currentIndex > 0) {
|
|
filter = query.substring(currentIndex, endIndex);
|
|
filter = UrlUtil.decode(filter, "UTF8");
|
|
}
|
|
currentIndex = endIndex + 1;
|
|
if (currentIndex >= query.length()) {
|
|
return;
|
|
}
|
|
|
|
// extensions:
|
|
if (query.length() - currentIndex > 0) {
|
|
extensions = query.substring(currentIndex);
|
|
extensions = UrlUtil.decode(extensions, "UTF8");
|
|
}
|
|
}
|
|
|
|
/*
|
|
public static void main(String[] args) throws Exception {
|
|
|
|
LdapURL url = new LdapURL(args[0]);
|
|
|
|
System.out.println("Example LDAP URL: " + url.toString());
|
|
System.out.println(" scheme: " + url.getScheme());
|
|
System.out.println(" host: " + url.getHost());
|
|
System.out.println(" port: " + url.getPort());
|
|
System.out.println(" DN: " + url.getDN());
|
|
System.out.println(" attrs: " + url.getAttributes());
|
|
System.out.println(" scope: " + url.getScope());
|
|
System.out.println(" filter: " + url.getFilter());
|
|
System.out.println(" extens: " + url.getExtensions());
|
|
System.out.println("");
|
|
}
|
|
*/
|
|
}
|