/* * 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 2009-2010 Sun Microsystems, Inc. * Portions Copyright 2011-2016 ForgeRock AS. */ package org.forgerock.opendj.examples; import java.util.concurrent.atomic.AtomicReference; import org.forgerock.opendj.ldap.Connection; import org.forgerock.opendj.ldap.ConnectionFactory; import org.forgerock.opendj.ldap.LdapException; import org.forgerock.opendj.ldap.IntermediateResponseHandler; import org.forgerock.opendj.ldap.RequestContext; import org.forgerock.opendj.ldap.RequestHandler; import org.forgerock.opendj.ldap.ResultCode; import org.forgerock.opendj.ldap.LdapResultHandler; import org.forgerock.opendj.ldap.SearchResultHandler; import org.forgerock.opendj.ldap.controls.ProxiedAuthV2RequestControl; import org.forgerock.opendj.ldap.requests.AddRequest; import org.forgerock.opendj.ldap.requests.BindRequest; import org.forgerock.opendj.ldap.requests.CancelExtendedRequest; import org.forgerock.opendj.ldap.requests.CompareRequest; import org.forgerock.opendj.ldap.requests.DeleteRequest; import org.forgerock.opendj.ldap.requests.ExtendedRequest; import org.forgerock.opendj.ldap.requests.ModifyDNRequest; import org.forgerock.opendj.ldap.requests.ModifyRequest; import org.forgerock.opendj.ldap.requests.Request; import org.forgerock.opendj.ldap.requests.SearchRequest; import org.forgerock.opendj.ldap.requests.StartTLSExtendedRequest; import org.forgerock.opendj.ldap.responses.BindResult; import org.forgerock.opendj.ldap.responses.CompareResult; import org.forgerock.opendj.ldap.responses.ExtendedResult; import org.forgerock.opendj.ldap.responses.Result; import org.forgerock.util.AsyncFunction; import org.forgerock.util.promise.Promise; import org.forgerock.util.promise.ResultHandler; import static org.forgerock.opendj.ldap.LdapException.*; import static org.forgerock.util.Utils.*; /** * A simple proxy back-end which forwards requests to a connection factory using * proxy authorization. Simple bind requests are performed on a separate * connection factory dedicated for authentication. *

* This implementation is very simple and is only intended as an example: *

* NOTE: a proxy back-end is stateful due to its use of proxy * authorization. Therefore, a proxy backend must be created for each inbound * client connection. The following code illustrates how this may be achieved: * *
 * final RequestHandlerFactory proxyFactory =
 *     new RequestHandlerFactory() {
 *         @Override
 *         public ProxyBackend handleAccept(LDAPClientContext clientContext) throws LdapException {
 *             return new ProxyBackend(factory, bindFactory);
 *         }
 *     };
 * final ServerConnectionFactory connectionHandler = Connections
 *     .newServerConnectionFactory(proxyFactory);}
 * 
*/ final class ProxyBackend implements RequestHandler { private final ConnectionFactory bindFactory; private final ConnectionFactory factory; private volatile ProxiedAuthV2RequestControl proxiedAuthControl; ProxyBackend(final ConnectionFactory factory, final ConnectionFactory bindFactory) { this.factory = factory; this.bindFactory = bindFactory; } @Override public void handleAdd(final RequestContext requestContext, final AddRequest request, final IntermediateResponseHandler intermediateResponseHandler, final LdapResultHandler resultHandler) { final AtomicReference connectionHolder = new AtomicReference<>(); addProxiedAuthControl(request); factory.getConnectionAsync().thenAsync(new AsyncFunction() { @Override public Promise apply(Connection connection) throws LdapException { connectionHolder.set(connection); return connection.addAsync(request, intermediateResponseHandler); } }).thenOnResult(resultHandler).thenOnException(resultHandler).thenAlways(close(connectionHolder)); } @Override public void handleBind(final RequestContext requestContext, final int version, final BindRequest request, final IntermediateResponseHandler intermediateResponseHandler, final LdapResultHandler resultHandler) { if (request.getAuthenticationType() != BindRequest.AUTHENTICATION_TYPE_SIMPLE) { // TODO: SASL authentication not implemented. resultHandler.handleException(newLdapException(ResultCode.PROTOCOL_ERROR, "non-SIMPLE authentication not supported: " + request.getAuthenticationType())); } else { // Authenticate using a separate bind connection pool, because // we don't want to change the state of the pooled connection. final AtomicReference connectionHolder = new AtomicReference<>(); proxiedAuthControl = null; bindFactory.getConnectionAsync() .thenAsync(new AsyncFunction() { @Override public Promise apply(Connection connection) throws LdapException { connectionHolder.set(connection); return connection.bindAsync(request, intermediateResponseHandler); } }).thenOnResult(new ResultHandler() { @Override public final void handleResult(final BindResult result) { proxiedAuthControl = ProxiedAuthV2RequestControl.newControl("dn:" + request.getName()); resultHandler.handleResult(result); } }).thenOnException(resultHandler).thenAlways(close(connectionHolder)); } } @Override public void handleCompare(final RequestContext requestContext, final CompareRequest request, final IntermediateResponseHandler intermediateResponseHandler, final LdapResultHandler resultHandler) { addProxiedAuthControl(request); final AtomicReference connectionHolder = new AtomicReference<>(); factory.getConnectionAsync().thenAsync(new AsyncFunction() { @Override public Promise apply(Connection connection) throws LdapException { connectionHolder.set(connection); return connection.compareAsync(request, intermediateResponseHandler); } }).thenOnResult(resultHandler).thenOnException(resultHandler).thenAlways(close(connectionHolder)); } @Override public void handleDelete(final RequestContext requestContext, final DeleteRequest request, final IntermediateResponseHandler intermediateResponseHandler, final LdapResultHandler resultHandler) { addProxiedAuthControl(request); final AtomicReference connectionHolder = new AtomicReference<>(); factory.getConnectionAsync().thenAsync(new AsyncFunction() { @Override public Promise apply(Connection connection) throws LdapException { connectionHolder.set(connection); return connection.deleteAsync(request, intermediateResponseHandler); } }).thenOnResult(resultHandler).thenOnException(resultHandler).thenAlways(close(connectionHolder)); } @Override public void handleExtendedRequest(final RequestContext requestContext, final ExtendedRequest request, final IntermediateResponseHandler intermediateResponseHandler, final LdapResultHandler resultHandler) { if (CancelExtendedRequest.OID.equals(request.getOID())) { // TODO: not implemented. resultHandler.handleException(newLdapException(ResultCode.PROTOCOL_ERROR, "Cancel extended request operation not supported")); } else if (StartTLSExtendedRequest.OID.equals(request.getOID())) { // TODO: not implemented. resultHandler.handleException(newLdapException(ResultCode.PROTOCOL_ERROR, "StartTLS extended request operation not supported")); } else { // Forward all other extended operations. addProxiedAuthControl(request); final AtomicReference connectionHolder = new AtomicReference<>(); factory.getConnectionAsync().thenAsync(new AsyncFunction() { @Override public Promise apply(Connection connection) throws LdapException { connectionHolder.set(connection); return connection.extendedRequestAsync(request, intermediateResponseHandler); } }).thenOnResult(resultHandler).thenOnException(resultHandler) .thenAlways(close(connectionHolder)); } } @Override public void handleModify(final RequestContext requestContext, final ModifyRequest request, final IntermediateResponseHandler intermediateResponseHandler, final LdapResultHandler resultHandler) { addProxiedAuthControl(request); final AtomicReference connectionHolder = new AtomicReference<>(); factory.getConnectionAsync().thenAsync(new AsyncFunction() { @Override public Promise apply(Connection connection) throws LdapException { connectionHolder.set(connection); return connection.modifyAsync(request, intermediateResponseHandler); } }).thenOnResult(resultHandler).thenOnException(resultHandler).thenAlways(close(connectionHolder)); } @Override public void handleModifyDN(final RequestContext requestContext, final ModifyDNRequest request, final IntermediateResponseHandler intermediateResponseHandler, final LdapResultHandler resultHandler) { addProxiedAuthControl(request); final AtomicReference connectionHolder = new AtomicReference<>(); factory.getConnectionAsync().thenAsync(new AsyncFunction() { @Override public Promise apply(Connection connection) throws LdapException { connectionHolder.set(connection); return connection.modifyDNAsync(request, intermediateResponseHandler); } }).thenOnResult(resultHandler).thenOnException(resultHandler).thenAlways(close(connectionHolder)); } @Override public void handleSearch(final RequestContext requestContext, final SearchRequest request, final IntermediateResponseHandler intermediateResponseHandler, final SearchResultHandler entryHandler, final LdapResultHandler resultHandler) { addProxiedAuthControl(request); final AtomicReference connectionHolder = new AtomicReference<>(); factory.getConnectionAsync().thenAsync(new AsyncFunction() { @Override public Promise apply(Connection connection) throws LdapException { connectionHolder.set(connection); return connection.searchAsync(request, intermediateResponseHandler, entryHandler); } }).thenOnResult(resultHandler).thenOnException(resultHandler).thenAlways(close(connectionHolder)); } private void addProxiedAuthControl(final Request request) { final ProxiedAuthV2RequestControl control = proxiedAuthControl; if (control != null) { request.addControl(control); } } private Runnable close(final AtomicReference c) { return new Runnable() { @Override public void run() { closeSilently(c.get()); } }; } }