Path: blob/master/src/java.naming/share/classes/com/sun/jndi/ldap/LdapURL.java
67772 views
/*1* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.sun.jndi.ldap;2627import javax.naming.*;28import java.net.MalformedURLException;29import java.io.UnsupportedEncodingException;30import java.net.URI;31import java.security.AccessController;32import java.security.PrivilegedAction;33import java.util.Locale;34import java.util.StringTokenizer;35import com.sun.jndi.toolkit.url.Uri;36import com.sun.jndi.toolkit.url.UrlUtil;3738/*39* Extract components of an LDAP URL.40*41* The format of an LDAP URL is defined in RFC 2255 as follows:42*43* ldapurl = scheme "://" [hostport] ["/"44* [dn ["?" [attributes] ["?" [scope]45* ["?" [filter] ["?" extensions]]]]]]46* scheme = "ldap"47* attributes = attrdesc *("," attrdesc)48* scope = "base" / "one" / "sub"49* dn = distinguishedName from Section 3 of [1]50* hostport = hostport from Section 5 of RFC 1738 [5]51* attrdesc = AttributeDescription from Section 4.1.5 of [2]52* filter = filter from Section 4 of [4]53* extensions = extension *("," extension)54* extension = ["!"] extype ["=" exvalue]55* extype = token / xtoken56* exvalue = LDAPString from section 4.1.2 of [2]57* token = oid from section 4.1 of [3]58* xtoken = ("X-" / "x-") token59*60* For example,61*62* ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US63* ldap://host.com:6666/o=IMC,c=US??sub?(cn=Babs%20Jensen)64*65* This class also supports ldaps URLs.66*/6768public final class LdapURL extends Uri {6970private static final String PARSE_MODE_PROP = "com.sun.jndi.ldapURLParsing";71private static final ParseMode DEFAULT_PARSE_MODE = ParseMode.COMPAT;7273public static final ParseMode PARSE_MODE;74static {75PrivilegedAction<String> action = () ->76System.getProperty(PARSE_MODE_PROP, DEFAULT_PARSE_MODE.toString());77ParseMode parseMode = DEFAULT_PARSE_MODE;78try {79@SuppressWarnings("removal")80String mode = AccessController.doPrivileged(action);81parseMode = ParseMode.valueOf(mode.toUpperCase(Locale.ROOT));82} catch (Throwable t) {83parseMode = DEFAULT_PARSE_MODE;84} finally {85PARSE_MODE = parseMode;86}87}8889private boolean useSsl = false;90private String DN = null;91private String attributes = null;92private String scope = null;93private String filter = null;94private String extensions = null;9596/**97* Creates an LdapURL object from an LDAP URL string.98*/99public LdapURL(String url) throws NamingException {100101super();102103try {104init(url); // scheme, host, port, path, query105useSsl = scheme.equalsIgnoreCase("ldaps");106107if (! (scheme.equalsIgnoreCase("ldap") || useSsl)) {108throw newInvalidURISchemeException(url);109}110111parsePathAndQuery(); // DN, attributes, scope, filter, extensions112113} catch (MalformedURLException e) {114NamingException ne = new NamingException("Cannot parse url: " + url);115ne.setRootCause(e);116throw ne;117} catch (UnsupportedEncodingException e) {118NamingException ne = new NamingException("Cannot parse url: " + url);119ne.setRootCause(e);120throw ne;121}122}123124@Override125protected MalformedURLException newInvalidURISchemeException(String uri) {126return new MalformedURLException("Not an LDAP URL: " + uri);127}128129@Override130protected boolean isSchemeOnly(String uri) {131return isLdapSchemeOnly(uri);132}133134@Override135protected ParseMode parseMode() {136return PARSE_MODE;137}138139/**140* Returns true if the URL is an LDAPS URL.141*/142public boolean useSsl() {143return useSsl;144}145146/**147* Returns the LDAP URL's distinguished name.148*/149public String getDN() {150return DN;151}152153/**154* Returns the LDAP URL's attributes.155*/156public String getAttributes() {157return attributes;158}159160/**161* Returns the LDAP URL's scope.162*/163public String getScope() {164return scope;165}166167/**168* Returns the LDAP URL's filter.169*/170public String getFilter() {171return filter;172}173174/**175* Returns the LDAP URL's extensions.176*/177public String getExtensions() {178return extensions;179}180181/**182* Given a space-separated list of LDAP URLs, returns an array of strings.183*/184public static String[] fromList(String urlList) throws NamingException {185186String[] urls = new String[(urlList.length() + 1) / 2];187int i = 0; // next available index in urls188StringTokenizer st = new StringTokenizer(urlList, " ");189190while (st.hasMoreTokens()) {191// we don't accept scheme-only URLs here192urls[i++] = validateURI(st.nextToken());193}194String[] trimmed = new String[i];195System.arraycopy(urls, 0, trimmed, 0, i);196return trimmed;197}198199public static boolean isLdapSchemeOnly(String uri) {200return "ldap:".equals(uri) || "ldaps:".equals(uri);201}202203public static String validateURI(String uri) {204// no validation in legacy mode parsing205if (PARSE_MODE == ParseMode.LEGACY) {206return uri;207}208209// special case of scheme-only URIs210if (isLdapSchemeOnly(uri)) {211return uri;212}213214// use java.net.URI to validate the uri syntax215return URI.create(uri).toString();216}217218/**219* Determines whether an LDAP URL has query components.220*/221public static boolean hasQueryComponents(String url) {222return (url.lastIndexOf('?') != -1);223}224225/*226* Assembles an LDAP or LDAPS URL string from its components.227* If "host" is an IPv6 literal, it may optionally include delimiting228* brackets.229*/230static String toUrlString(String host, int port, String dn, boolean useSsl)231{232233try {234String h = (host != null) ? host : "";235if ((h.indexOf(':') != -1) && (h.charAt(0) != '[')) {236h = "[" + h + "]"; // IPv6 literal237}238String p = (port != -1) ? (":" + port) : "";239String d = (dn != null) ? ("/" + UrlUtil.encode(dn, "UTF8")) : "";240241String uri = useSsl ? "ldaps://" + h + p + d : "ldap://" + h + p + d;242return validateURI(uri);243} catch (UnsupportedEncodingException e) {244// UTF8 should always be supported245throw new IllegalStateException("UTF-8 encoding unavailable");246}247}248249/*250* Parses the path and query components of an URL and sets this251* object's fields accordingly.252*/253private void parsePathAndQuery() throws MalformedURLException,254UnsupportedEncodingException {255256// path begins with a '/' or is empty257258if (path.isEmpty()) {259return;260}261262DN = path.startsWith("/") ? path.substring(1) : path;263if (DN.length() > 0) {264DN = UrlUtil.decode(DN, "UTF8");265}266267// query begins with a '?' or is null268269if (query == null || query.length() < 2) {270return;271}272273int currentIndex = 1;274int nextQmark;275int endIndex;276277// attributes:278nextQmark = query.indexOf('?', currentIndex);279endIndex = nextQmark == -1 ? query.length() : nextQmark;280if (endIndex - currentIndex > 0) {281attributes = query.substring(currentIndex, endIndex);282}283currentIndex = endIndex + 1;284if (currentIndex >= query.length()) {285return;286}287288// scope:289nextQmark = query.indexOf('?', currentIndex);290endIndex = nextQmark == -1 ? query.length() : nextQmark;291if (endIndex - currentIndex > 0) {292scope = query.substring(currentIndex, endIndex);293}294currentIndex = endIndex + 1;295if (currentIndex >= query.length()) {296return;297}298299// filter:300nextQmark = query.indexOf('?', currentIndex);301endIndex = nextQmark == -1 ? query.length() : nextQmark;302if (endIndex - currentIndex > 0) {303filter = query.substring(currentIndex, endIndex);304filter = UrlUtil.decode(filter, "UTF8");305}306currentIndex = endIndex + 1;307if (currentIndex >= query.length()) {308return;309}310311// extensions:312if (query.length() - currentIndex > 0) {313extensions = query.substring(currentIndex);314extensions = UrlUtil.decode(extensions, "UTF8");315}316}317318/*319public static void main(String[] args) throws Exception {320321LdapURL url = new LdapURL(args[0]);322323System.out.println("Example LDAP URL: " + url.toString());324System.out.println(" scheme: " + url.getScheme());325System.out.println(" host: " + url.getHost());326System.out.println(" port: " + url.getPort());327System.out.println(" DN: " + url.getDN());328System.out.println(" attrs: " + url.getAttributes());329System.out.println(" scope: " + url.getScope());330System.out.println(" filter: " + url.getFilter());331System.out.println(" extens: " + url.getExtensions());332System.out.println("");333}334*/335}336337338