/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
* or http://forgerock.org/license/CDDLv1.0.html.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at legal-notices/CDDLv1_0.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2009-2010 Sun Microsystems, Inc.
* Portions copyright 2011-2013 ForgeRock AS
*/
package org.forgerock.opendj.examples;
import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.ConnectionFactory;
import org.forgerock.opendj.ldap.ErrorResultException;
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.ResultHandler;
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.opendj.ldap.responses.SearchResultEntry;
import org.forgerock.opendj.ldap.responses.SearchResultReference;
/**
* 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 is implementation is very simple and is only intended as an example:
*
* - It does not support SSL connections
*
- It does not support StartTLS
*
- It does not support Abandon or Cancel requests
*
- Very basic authentication and authorization support.
*
* 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<LDAPClientContext, RequestContext> proxyFactory =
* new RequestHandlerFactory<LDAPClientContext, RequestContext>() {
* @Override
* public ProxyBackend handleAccept(LDAPClientContext clientContext)
* throws ErrorResultException {
* return new ProxyBackend(factory, bindFactory);
* }
* };
* final ServerConnectionFactory<LDAPClientContext, Integer> connectionHandler = Connections
* .newServerConnectionFactory(proxyFactory);
*
*/
final class ProxyBackend implements RequestHandler {
private abstract class AbstractRequestCompletionHandler>
implements ResultHandler {
final Connection connection;
final H resultHandler;
AbstractRequestCompletionHandler(final Connection connection, final H resultHandler) {
this.connection = connection;
this.resultHandler = resultHandler;
}
@Override
public final void handleErrorResult(final ErrorResultException error) {
connection.close();
resultHandler.handleErrorResult(error);
}
@Override
public final void handleResult(final R result) {
connection.close();
resultHandler.handleResult(result);
}
}
private abstract class ConnectionCompletionHandler implements
ResultHandler {
private final ResultHandler resultHandler;
ConnectionCompletionHandler(final ResultHandler resultHandler) {
this.resultHandler = resultHandler;
}
@Override
public final void handleErrorResult(final ErrorResultException error) {
resultHandler.handleErrorResult(error);
}
@Override
public abstract void handleResult(Connection connection);
}
private final class RequestCompletionHandler extends
AbstractRequestCompletionHandler> {
RequestCompletionHandler(final Connection connection, final ResultHandler resultHandler) {
super(connection, resultHandler);
}
}
private final class SearchRequestCompletionHandler extends
AbstractRequestCompletionHandler implements
SearchResultHandler {
SearchRequestCompletionHandler(final Connection connection,
final SearchResultHandler resultHandler) {
super(connection, resultHandler);
}
/**
* {@inheritDoc}
*/
@Override
public final boolean handleEntry(final SearchResultEntry entry) {
return resultHandler.handleEntry(entry);
}
/**
* {@inheritDoc}
*/
@Override
public final boolean handleReference(final SearchResultReference reference) {
return resultHandler.handleReference(reference);
}
}
private final ConnectionFactory bindFactory;
private final ConnectionFactory factory;
private volatile ProxiedAuthV2RequestControl proxiedAuthControl = null;
ProxyBackend(final ConnectionFactory factory, final ConnectionFactory bindFactory) {
this.factory = factory;
this.bindFactory = bindFactory;
}
/**
* {@inheritDoc}
*/
@Override
public void handleAdd(final RequestContext requestContext, final AddRequest request,
final IntermediateResponseHandler intermediateResponseHandler,
final ResultHandler resultHandler) {
addProxiedAuthControl(request);
final ConnectionCompletionHandler outerHandler =
new ConnectionCompletionHandler(resultHandler) {
@Override
public void handleResult(final Connection connection) {
final RequestCompletionHandler innerHandler =
new RequestCompletionHandler(connection, resultHandler);
connection.addAsync(request, intermediateResponseHandler, innerHandler);
}
};
factory.getConnectionAsync(outerHandler);
}
/**
* {@inheritDoc}
*/
@Override
public void handleBind(final RequestContext requestContext, final int version,
final BindRequest request,
final IntermediateResponseHandler intermediateResponseHandler,
final ResultHandler resultHandler) {
if (request.getAuthenticationType() != BindRequest.AUTHENTICATION_TYPE_SIMPLE) {
// TODO: SASL authentication not implemented.
resultHandler.handleErrorResult(newErrorResult(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 ConnectionCompletionHandler outerHandler =
new ConnectionCompletionHandler(resultHandler) {
@Override
public void handleResult(final Connection connection) {
final ResultHandler innerHandler =
new ResultHandler() {
@Override
public final void handleErrorResult(
final ErrorResultException error) {
connection.close();
resultHandler.handleErrorResult(error);
}
@Override
public final void handleResult(final BindResult result) {
connection.close();
proxiedAuthControl =
ProxiedAuthV2RequestControl.newControl("dn:"
+ request.getName());
resultHandler.handleResult(result);
}
};
connection
.bindAsync(request, intermediateResponseHandler, innerHandler);
}
};
proxiedAuthControl = null;
bindFactory.getConnectionAsync(outerHandler);
}
}
/**
* {@inheritDoc}
*/
@Override
public void handleCompare(final RequestContext requestContext, final CompareRequest request,
final IntermediateResponseHandler intermediateResponseHandler,
final ResultHandler resultHandler) {
addProxiedAuthControl(request);
final ConnectionCompletionHandler outerHandler =
new ConnectionCompletionHandler(resultHandler) {
@Override
public void handleResult(final Connection connection) {
final RequestCompletionHandler innerHandler =
new RequestCompletionHandler(connection,
resultHandler);
connection.compareAsync(request, intermediateResponseHandler, innerHandler);
}
};
factory.getConnectionAsync(outerHandler);
}
/**
* {@inheritDoc}
*/
@Override
public void handleDelete(final RequestContext requestContext, final DeleteRequest request,
final IntermediateResponseHandler intermediateResponseHandler,
final ResultHandler resultHandler) {
addProxiedAuthControl(request);
final ConnectionCompletionHandler outerHandler =
new ConnectionCompletionHandler(resultHandler) {
@Override
public void handleResult(final Connection connection) {
final RequestCompletionHandler innerHandler =
new RequestCompletionHandler(connection, resultHandler);
connection.deleteAsync(request, intermediateResponseHandler, innerHandler);
}
};
factory.getConnectionAsync(outerHandler);
}
/**
* {@inheritDoc}
*/
@Override
public void handleExtendedRequest(
final RequestContext requestContext, final ExtendedRequest request,
final IntermediateResponseHandler intermediateResponseHandler,
final ResultHandler resultHandler) {
if (request.getOID().equals(CancelExtendedRequest.OID)) {
// TODO: not implemented.
resultHandler.handleErrorResult(newErrorResult(ResultCode.PROTOCOL_ERROR,
"Cancel extended request operation not supported"));
} else if (request.getOID().equals(StartTLSExtendedRequest.OID)) {
// TODO: not implemented.
resultHandler.handleErrorResult(newErrorResult(ResultCode.PROTOCOL_ERROR,
"StartTLS extended request operation not supported"));
} else {
// Forward all other extended operations.
addProxiedAuthControl(request);
final ConnectionCompletionHandler outerHandler =
new ConnectionCompletionHandler(resultHandler) {
@Override
public void handleResult(final Connection connection) {
final RequestCompletionHandler innerHandler =
new RequestCompletionHandler(connection, resultHandler);
connection.extendedRequestAsync(request, intermediateResponseHandler,
innerHandler);
}
};
factory.getConnectionAsync(outerHandler);
}
}
/**
* {@inheritDoc}
*/
@Override
public void handleModify(final RequestContext requestContext, final ModifyRequest request,
final IntermediateResponseHandler intermediateResponseHandler,
final ResultHandler resultHandler) {
addProxiedAuthControl(request);
final ConnectionCompletionHandler outerHandler =
new ConnectionCompletionHandler(resultHandler) {
@Override
public void handleResult(final Connection connection) {
final RequestCompletionHandler innerHandler =
new RequestCompletionHandler(connection, resultHandler);
connection.modifyAsync(request, intermediateResponseHandler, innerHandler);
}
};
factory.getConnectionAsync(outerHandler);
}
/**
* {@inheritDoc}
*/
@Override
public void handleModifyDN(final RequestContext requestContext, final ModifyDNRequest request,
final IntermediateResponseHandler intermediateResponseHandler,
final ResultHandler resultHandler) {
addProxiedAuthControl(request);
final ConnectionCompletionHandler outerHandler =
new ConnectionCompletionHandler(resultHandler) {
@Override
public void handleResult(final Connection connection) {
final RequestCompletionHandler innerHandler =
new RequestCompletionHandler(connection, resultHandler);
connection
.modifyDNAsync(request, intermediateResponseHandler, innerHandler);
}
};
factory.getConnectionAsync(outerHandler);
}
/**
* {@inheritDoc}
*/
@Override
public void handleSearch(final RequestContext requestContext, final SearchRequest request,
final IntermediateResponseHandler intermediateResponseHandler,
final SearchResultHandler resultHandler) {
addProxiedAuthControl(request);
final ConnectionCompletionHandler outerHandler =
new ConnectionCompletionHandler(resultHandler) {
@Override
public void handleResult(final Connection connection) {
final SearchRequestCompletionHandler innerHandler =
new SearchRequestCompletionHandler(connection, resultHandler);
connection.searchAsync(request, intermediateResponseHandler, innerHandler);
}
};
factory.getConnectionAsync(outerHandler);
}
private void addProxiedAuthControl(final Request request) {
final ProxiedAuthV2RequestControl control = proxiedAuthControl;
if (control != null) {
request.addControl(control);
}
}
}