/* * The contents of this file are subject to the terms of the Common Development and * Distribution License (the License). You may not use this file except in compliance with the * License. * * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the * specific language governing permission and limitations under the License. * * When distributing Covered Software, include this CDDL Header Notice in each file and include * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL * Header, with the fields enclosed by brackets [] replaced by your own identifying * information: "Portions Copyright [year] [name of copyright owner]". * * Copyright 2016 ForgeRock AS. */ package org.opends.admin.ads.util; import static org.forgerock.opendj.config.client.ldap.LDAPManagementContext.*; import static org.forgerock.opendj.ldap.LDAPConnectionFactory.*; import static org.forgerock.opendj.ldap.requests.Requests.*; import static org.forgerock.util.time.Duration.*; import static org.opends.admin.ads.util.ConnectionUtils.*; import static org.opends.admin.ads.util.PreferredConnection.Type.*; import static org.opends.messages.AdminToolMessages.*; import java.io.Closeable; import java.net.URI; import java.net.URISyntaxException; import java.security.GeneralSecurityException; import java.util.concurrent.TimeUnit; import javax.naming.NamingException; import javax.naming.NoPermissionException; import javax.naming.ldap.InitialLdapContext; import javax.net.ssl.KeyManager; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import org.forgerock.opendj.config.LDAPProfile; import org.forgerock.opendj.ldap.Connection; import org.forgerock.opendj.ldap.DN; import org.forgerock.opendj.ldap.LDAPConnectionFactory; import org.forgerock.opendj.ldap.LdapException; import org.forgerock.opendj.ldap.SSLContextBuilder; import org.forgerock.opendj.ldap.requests.SimpleBindRequest; import org.forgerock.opendj.server.config.client.RootCfgClient; import org.forgerock.util.Options; import org.opends.admin.ads.util.PreferredConnection.Type; import org.opends.server.types.HostPort; import org.opends.server.util.StaticUtils; /** * Wraps a connection to a directory, either relying on JNDI or relying on OpenDJ Connection. *

* You can either: *

*/ public class ConnectionWrapper implements Closeable { private final LDAPConnectionFactory connectionFactory; private final Connection connection; private final InitialLdapContext ldapContext; private final HostPort hostPort; private DN bindDn; private String bindPwd; private final int connectTimeout; private final TrustManager trustManager; private final KeyManager keyManager; private Type connectionType; /** * Creates a connection wrapper. * * @param ldapUrl * the ldap URL containing the host name and port number to connect to * @param connectionType * the type of connection (LDAP, LDAPS, START_TLS) * @param bindDn * the bind DN * @param bindPwd * the bind password * @param connectTimeout * connect timeout to use for the connection * @param trustManager * trust manager to use for a secure connection * @throws NamingException * If an error occurs */ public ConnectionWrapper(String ldapUrl, Type connectionType, String bindDn, String bindPwd, int connectTimeout, TrustManager trustManager) throws NamingException { this(toHostPort(ldapUrl), connectionType, bindDn, bindPwd, connectTimeout, trustManager, null); } /** * Converts an ldapUrl to a HostPort. * * @param ldapUrl * the ldapUrl to convert * @return the host and port extracted from the ldapUrl * @throws NamingException * if the ldapUrl is not a valid URL */ public static HostPort toHostPort(String ldapUrl) throws NamingException { try { URI uri = new URI(ldapUrl); return new HostPort(uri.getHost(), uri.getPort()); } catch (URISyntaxException e) { throw new NamingException(e.getLocalizedMessage() + ". LDAP URL was: \"" + ldapUrl + "\""); } } /** * Creates a connection wrapper. * * @param hostPort * the host name and port number to connect to * @param connectionType * the type of connection (LDAP, LDAPS, START_TLS) * @param bindDn * the bind DN * @param bindPwd * the bind password * @param connectTimeout * connect timeout to use for the connection * @param trustManager * trust manager to use for a secure connection * @throws NamingException * If an error occurs */ public ConnectionWrapper(HostPort hostPort, Type connectionType, String bindDn, String bindPwd, int connectTimeout, TrustManager trustManager) throws NamingException { this(hostPort, connectionType, bindDn, bindPwd, connectTimeout, trustManager, null); } /** * Creates a connection wrapper by copying the provided one. * * @param other * the {@link ConnectionWrapper} to copy * @throws NamingException * If an error occurs */ public ConnectionWrapper(ConnectionWrapper other) throws NamingException { this(other.hostPort, other.connectionType, other.bindDn.toString(), other.bindPwd, other.connectTimeout, other.trustManager, other.keyManager); } /** * Creates a connection wrapper. * * @param hostPort * the host name and port number to connect to * @param connectionType * the type of connection (LDAP, LDAPS, START_TLS) * @param bindDn * the bind DN * @param bindPwd * the bind password * @param connectTimeout * connect timeout to use for the connection * @param trustManager * trust manager to use for a secure connection * @param keyManager * key manager to use for a secure connection * @throws NamingException * If an error occurs */ public ConnectionWrapper(HostPort hostPort, PreferredConnection.Type connectionType, String bindDn, String bindPwd, int connectTimeout, TrustManager trustManager, KeyManager keyManager) throws NamingException { this.hostPort = hostPort; this.connectionType = connectionType; this.bindDn = DN.valueOf(bindDn); this.bindPwd = bindPwd; this.connectTimeout = connectTimeout; this.trustManager = trustManager; this.keyManager = keyManager; final Options options = toOptions(connectionType, bindDn, bindPwd, connectTimeout, trustManager, keyManager); ldapContext = createAdministrativeContext(options); connectionFactory = new LDAPConnectionFactory(hostPort.getHost(), hostPort.getPort(), options); connection = buildConnection(); } private static Options toOptions(Type connectionType, String bindDn, String bindPwd, long connectTimeout, TrustManager trustManager, KeyManager keyManager) throws NamingException { final boolean isStartTls = START_TLS.equals(connectionType); final boolean isLdaps = LDAPS.equals(connectionType); Options options = Options.defaultOptions() .set(CONNECT_TIMEOUT, duration(connectTimeout, TimeUnit.MILLISECONDS)); if (isLdaps || isStartTls) { options.set(SSL_CONTEXT, getSSLContext(trustManager, keyManager)) .set(SSL_USE_STARTTLS, isStartTls); } SimpleBindRequest request = bindDn != null && bindPwd != null ? newSimpleBindRequest(bindDn, bindPwd.toCharArray()) : newSimpleBindRequest(); // anonymous bind options.set(AUTHN_BIND_REQUEST, request); return options; } private static SSLContext getSSLContext(TrustManager trustManager, KeyManager keyManager) throws NamingException { try { return new SSLContextBuilder() .setTrustManager(trustManager != null ? trustManager : new BlindTrustManager()) .setKeyManager(keyManager).getSSLContext(); } catch (GeneralSecurityException e) { throw new NamingException("Unable to perform SSL initialization:" + e.getMessage()); } } /** * Returns the bind DN used by this connection. * * @return the bind DN used by this connection. */ public DN getBindDn() { return bindDn; } /** * Returns the bind password used by this connection. * * @return the bind password used by this connection. */ public String getBindPassword() { return bindPwd; } /** * Returns the LDAP URL used by this connection. * * @return the LDAP URL used by this connection. */ public String getLdapUrl() { return ConnectionUtils.getLdapUrl(ldapContext); } /** * Returns whether this connection uses SSL. * * @return {@code true} if this connection uses SSL {@code false} otherwise. */ public boolean isSSL() { return ConnectionUtils.isSSL(ldapContext); } /** * Returns whether this connection uses StartTLS. * * @return {@code true} if this connection uses StartTLS {@code false} otherwise. */ public boolean isStartTLS() { return ConnectionUtils.isStartTLS(ldapContext); } private InitialLdapContext createAdministrativeContext(Options options) throws NamingException { final InitialLdapContext ctx = createAdministrativeContext0(options); if (!connectedAsAdministrativeUser(ctx)) { throw new NoPermissionException(ERR_NOT_ADMINISTRATIVE_USER.get().toString()); } return ctx; } private InitialLdapContext createAdministrativeContext0(Options options) throws NamingException { boolean useSSL = options.get(SSL_CONTEXT) != null; final String ldapUrl = getLDAPUrl(getHostPort(), useSSL); final String bindDnStr = bindDn.toString(); switch (connectionType) { case LDAPS: return createLdapsContext(ldapUrl, bindDnStr, bindPwd, connectTimeout, null, trustManager, keyManager); case START_TLS: return createStartTLSContext(ldapUrl, bindDnStr, bindPwd, connectTimeout, null, trustManager, keyManager, null); case LDAP: return createLdapContext(ldapUrl, bindDnStr, bindPwd, connectTimeout, null); default: throw new RuntimeException("Not implemented for connection type: " + connectionType); } } private Connection buildConnection() throws NamingException { try { return connectionFactory.getConnection(); } catch (LdapException e) { throw new NamingException("Unable to get a connection from connection factory:" + e.getMessage()); } } /** * Returns the connection. * * @return the connection */ public Connection getConnection() { return connection; } /** * Returns the ldap context (JNDI). * * @return the ldap context */ public InitialLdapContext getLdapContext() { return ldapContext; } /** * Returns the host name and port number of this connection. * * @return the hostPort of this connection */ public HostPort getHostPort() { return hostPort; } /** * Returns the root configuration client by using the inrnal Connection. * * @return the root configuration client */ public RootCfgClient getRootConfiguration() { return newManagementContext(getConnection(), LDAPProfile.getInstance()).getRootConfiguration(); } @Override public void close() { StaticUtils.close(connectionFactory, connection); StaticUtils.close(ldapContext); } }