From bf5f8327f7e22b03cf1807aac9fbd0845346c8bb Mon Sep 17 00:00:00 2001
From: Mark Craig <mark.craig@forgerock.com>
Date: Fri, 18 May 2012 09:07:46 +0000
Subject: [PATCH] Draft example that does very basic DN and attribute rewriting
---
opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/RewriterProxy.java | 684 ++++++++++++++++++++++++++++++++++++++++++++++++++++
opendj3/src/main/docbkx/dev-guide/chap-simple-proxy.xml | 78 +++++
2 files changed, 761 insertions(+), 1 deletions(-)
diff --git a/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/RewriterProxy.java b/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/RewriterProxy.java
new file mode 100644
index 0000000..85426f4
--- /dev/null
+++ b/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/RewriterProxy.java
@@ -0,0 +1,684 @@
+/*
+ * 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-2012 ForgeRock AS
+ */
+
+package org.forgerock.opendj.examples;
+
+import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.forgerock.opendj.ldap.Attribute;
+import org.forgerock.opendj.ldap.AttributeDescription;
+import org.forgerock.opendj.ldap.Connection;
+import org.forgerock.opendj.ldap.ConnectionFactory;
+import org.forgerock.opendj.ldap.Connections;
+import org.forgerock.opendj.ldap.DN;
+import org.forgerock.opendj.ldap.ErrorResultException;
+import org.forgerock.opendj.ldap.Filter;
+import org.forgerock.opendj.ldap.IntermediateResponseHandler;
+import org.forgerock.opendj.ldap.LDAPClientContext;
+import org.forgerock.opendj.ldap.LDAPConnectionFactory;
+import org.forgerock.opendj.ldap.LDAPListener;
+import org.forgerock.opendj.ldap.LDAPListenerOptions;
+import org.forgerock.opendj.ldap.LinkedAttribute;
+import org.forgerock.opendj.ldap.Modification;
+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.ServerConnectionFactory;
+import org.forgerock.opendj.ldap.controls.Control;
+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.Requests;
+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;
+
+/**
+ * This example is based on the {@link Proxy}. This example does no load
+ * balancing, but instead rewrites attribute descriptions and DN suffixes in
+ * requests to and responses from a directory server using hard coded
+ * configuration.
+ * <ul>
+ * <li>It transforms DNs ending in {@code o=example} on the client side to end
+ * in {@code dc=example,dc=com} on the server side and vice versa.
+ * <li>It transforms the attribute description {@code fullname} on the client
+ * side to {@code cn} on the server side and vice versa.
+ * </ul>
+ *
+ * This example has a number of restrictions.
+ * <ul>
+ * <li>It does not support SSL connections.
+ * <li>It does not support StartTLS.
+ * <li>It does not support Abandon or Cancel requests.
+ * <li>It has very basic authentication and authorization support.
+ * <li>It does not rewrite bind DNs.
+ * <li>It uses proxied authorization, so if you use OpenDJ directory server, you
+ * must set the {@code proxied-auth} privilege for the proxy user.
+ * <li>It does not touch matched DNs in results.
+ * <li>It does not rewrite attributes with options in search result entries.
+ * <li>It does not touch search result references.
+ * <li>It does not rewrite LDAP Controls or Extended Operations, except to
+ * rewrite the DN used as authorization ID for proxied authorization.
+ * </ul>
+ * This example takes the following command line parameters:
+ *
+ * <pre>
+ * <localAddress> <localPort> <proxyDN> <proxyPassword> <serverAddress> <serverPort>
+ * </pre>
+ *
+ * If you have imported the users from <a
+ * href="http://opendj.forgerock.org/Example.ldif">Example.ldif</a>, then you
+ * can set {@code proxyUserDN} to {@code cn=My App,ou=Apps,dc=example,dc=com}
+ * and {@code proxyUserPassword} to {@code password}.
+ */
+public final class RewriterProxy {
+ private static final class ProxyBackend implements RequestHandler<RequestContext> {
+
+ // This example hard codes the attribute...
+ private final String clientAttributeTypeName = "fullname";
+ private final String serverAttributeTypeName = "cn";
+ private final AttributeDescription clientAttributeDescription =
+ AttributeDescription.valueOf(clientAttributeTypeName);
+ private final AttributeDescription serverAttributeDescription =
+ AttributeDescription.valueOf(serverAttributeTypeName);
+
+ // ...and DN rewriting configuration.
+ private final CharSequence clientSuffix = "o=example";
+ private final CharSequence serverSuffix = "dc=example,dc=com";
+
+ private final ConnectionFactory factory;
+ private final ConnectionFactory bindFactory;
+
+ private ProxyBackend(final ConnectionFactory factory, final ConnectionFactory bindFactory) {
+ this.factory = factory;
+ this.bindFactory = bindFactory;
+ }
+
+ private abstract class AbstractRequestCompletionHandler
+ <R extends Result, H extends ResultHandler<? super R>>
+ implements ResultHandler<R> {
+ final H resultHandler;
+ final Connection connection;
+
+ 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<R extends Result> implements
+ ResultHandler<Connection> {
+ private final ResultHandler<? super R> resultHandler;
+
+ ConnectionCompletionHandler(final ResultHandler<? super R> 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<R extends Result> extends
+ AbstractRequestCompletionHandler<R, ResultHandler<? super R>> {
+ RequestCompletionHandler(final Connection connection,
+ final ResultHandler<? super R> resultHandler) {
+ super(connection, resultHandler);
+ }
+ }
+
+ private final class SearchRequestCompletionHandler extends
+ AbstractRequestCompletionHandler<Result, SearchResultHandler> implements
+ SearchResultHandler {
+
+ SearchRequestCompletionHandler(final Connection connection,
+ final SearchResultHandler resultHandler) {
+ super(connection, resultHandler);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final boolean handleEntry(SearchResultEntry entry) {
+ return resultHandler.handleEntry(rewrite(entry));
+ }
+
+ private SearchResultEntry rewrite(SearchResultEntry entry) {
+
+ // Replace server attributes with client attributes.
+ // TODO: Handle attributes with options
+ Attribute serverAttribute = entry.getAttribute(
+ serverAttributeDescription);
+ Attribute clientAttribute = new LinkedAttribute(
+ clientAttributeDescription, serverAttribute.toArray());
+ entry.addAttribute(clientAttribute);
+ entry.removeAttribute(serverAttributeDescription);
+
+ // Transform the server DN suffix into a client DN suffix.
+ return entry.setName(entry.getName().toString()
+ .replace(serverSuffix, clientSuffix));
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final boolean handleReference(final SearchResultReference reference) {
+ return resultHandler.handleReference(reference);
+ }
+
+ }
+
+ private volatile ProxiedAuthV2RequestControl proxiedAuthControl = null;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void handleAdd(final RequestContext requestContext, final AddRequest request,
+ final IntermediateResponseHandler intermediateResponseHandler,
+ final ResultHandler<? super Result> resultHandler) {
+ addProxiedAuthControl(request);
+ final ConnectionCompletionHandler<Result> outerHandler =
+ new ConnectionCompletionHandler<Result>(resultHandler) {
+
+ @Override
+ public void handleResult(final Connection connection) {
+ final RequestCompletionHandler<Result> innerHandler =
+ new RequestCompletionHandler<Result>(connection, resultHandler);
+ connection.addAsync(rewrite(request), intermediateResponseHandler, innerHandler);
+ }
+
+ private AddRequest rewrite(final AddRequest request) {
+
+ // Transform the client DN into a server DN.
+ AddRequest rewrittenRequest =
+ Requests.copyOfAddRequest(request);
+ rewrittenRequest.setName(request.getName().toString()
+ .replace(clientSuffix, serverSuffix));
+
+ // Transform the client attribute names into server
+ // attribute names, fullname;lang-fr ==> cn;lang-fr.
+ for (Attribute clientAttribute
+ : request.getAllAttributes(clientAttributeDescription)) {
+ if (clientAttribute != null) {
+ String attrDesc = clientAttribute
+ .getAttributeDescriptionAsString()
+ .replaceFirst(clientAttributeTypeName,
+ serverAttributeTypeName);
+ Attribute serverAttribute =
+ new LinkedAttribute(
+ AttributeDescription.valueOf(attrDesc),
+ clientAttribute.toArray());
+ rewrittenRequest.addAttribute(serverAttribute);
+ rewrittenRequest.removeAttribute(
+ clientAttribute.getAttributeDescription());
+ }
+ }
+
+ return rewrittenRequest;
+ }
+
+ };
+
+ factory.getConnectionAsync(outerHandler);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void handleBind(final RequestContext requestContext, final int version,
+ final BindRequest request,
+ final IntermediateResponseHandler intermediateResponseHandler,
+ final ResultHandler<? super BindResult> resultHandler) {
+
+ if (request.getAuthenticationType() != ((byte) 0x80)) {
+ // 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<BindResult> outerHandler =
+ new ConnectionCompletionHandler<BindResult>(resultHandler) {
+
+ @Override
+ public void handleResult(final Connection connection) {
+ final ResultHandler<BindResult> innerHandler =
+ new ResultHandler<BindResult>() {
+
+ @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(rewrite(request), intermediateResponseHandler,
+ innerHandler);
+ }
+
+ private BindRequest rewrite(final BindRequest request) {
+ // TODO: Transform client DN into server DN.
+ return request;
+ }
+
+ };
+
+ proxiedAuthControl = null;
+ bindFactory.getConnectionAsync(outerHandler);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void handleCompare(final RequestContext requestContext,
+ final CompareRequest request,
+ final IntermediateResponseHandler intermediateResponseHandler,
+ final ResultHandler<? super CompareResult> resultHandler) {
+ addProxiedAuthControl(request);
+ final ConnectionCompletionHandler<CompareResult> outerHandler =
+ new ConnectionCompletionHandler<CompareResult>(resultHandler) {
+
+ @Override
+ public void handleResult(final Connection connection) {
+ final RequestCompletionHandler<CompareResult> innerHandler =
+ new RequestCompletionHandler<CompareResult>(connection,
+ resultHandler);
+ connection.compareAsync(rewrite(request), intermediateResponseHandler,
+ innerHandler);
+ }
+
+ private CompareRequest rewrite(CompareRequest request) {
+
+ // Transform the client attribute name into a server
+ // attribute name, fullname;lang-fr ==> cn;lang-fr.
+ String attrName = request.getAttributeDescription().toString();
+ if (attrName.toLowerCase().startsWith(
+ clientAttributeTypeName.toLowerCase())) {
+ String rewrittenAttrName = attrName
+ .replaceFirst(clientAttributeTypeName,
+ serverAttributeTypeName);
+ request.setAttributeDescription(
+ AttributeDescription.valueOf(
+ rewrittenAttrName));
+ }
+
+ // Transform the client DN into a server DN.
+ return request.setName(request.getName().toString()
+ .replace(clientSuffix, serverSuffix));
+ }
+
+ };
+
+ factory.getConnectionAsync(outerHandler);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void handleDelete(final RequestContext requestContext, final DeleteRequest request,
+ final IntermediateResponseHandler intermediateResponseHandler,
+ final ResultHandler<? super Result> resultHandler) {
+ addProxiedAuthControl(request);
+ final ConnectionCompletionHandler<Result> outerHandler =
+ new ConnectionCompletionHandler<Result>(resultHandler) {
+
+ @Override
+ public void handleResult(final Connection connection) {
+ final RequestCompletionHandler<Result> innerHandler =
+ new RequestCompletionHandler<Result>(connection, resultHandler);
+ connection.deleteAsync(rewrite(request), intermediateResponseHandler,
+ innerHandler);
+ }
+
+ private DeleteRequest rewrite(DeleteRequest request) {
+ // Transform the client DN into a server DN.
+ return request.setName(request.getName().toString()
+ .replace(clientSuffix, serverSuffix));
+ }
+
+ };
+
+ factory.getConnectionAsync(outerHandler);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public <R extends ExtendedResult> void handleExtendedRequest(
+ final RequestContext requestContext, final ExtendedRequest<R> request,
+ final IntermediateResponseHandler intermediateResponseHandler,
+ final ResultHandler<? super R> 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<R> outerHandler =
+ new ConnectionCompletionHandler<R>(resultHandler) {
+
+ @Override
+ public void handleResult(final Connection connection) {
+ final RequestCompletionHandler<R> innerHandler =
+ new RequestCompletionHandler<R>(connection, resultHandler);
+ connection.extendedRequestAsync(request,
+ intermediateResponseHandler, innerHandler);
+ }
+
+ // TODO: Rewrite PasswordModifyExtendedRequest,
+ // WhoAmIExtendedResult
+
+ };
+
+ factory.getConnectionAsync(outerHandler);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void handleModify(final RequestContext requestContext, final ModifyRequest request,
+ final IntermediateResponseHandler intermediateResponseHandler,
+ final ResultHandler<? super Result> resultHandler) {
+ addProxiedAuthControl(request);
+ final ConnectionCompletionHandler<Result> outerHandler =
+ new ConnectionCompletionHandler<Result>(resultHandler) {
+
+ @Override
+ public void handleResult(final Connection connection) {
+ final RequestCompletionHandler<Result> innerHandler =
+ new RequestCompletionHandler<Result>(connection, resultHandler);
+ connection.modifyAsync(rewrite(request), intermediateResponseHandler,
+ innerHandler);
+ }
+
+ private ModifyRequest rewrite(final ModifyRequest request) {
+
+ // Transform the client DN into a server DN.
+ ModifyRequest rewrittenRequest =
+ Requests.newModifyRequest(request.getName().toString()
+ .replace(clientSuffix, serverSuffix));
+
+ // Transform the client attribute names into server
+ // attribute names, fullname;lang-fr ==> cn;lang-fr.
+ List<Modification> mods = request.getModifications();
+ for (Modification mod : mods) {
+ AttributeDescription attrDesc =
+ mod.getAttribute().getAttributeDescription();
+
+ if (attrDesc.equals(clientAttributeDescription)) {
+ String rewrittenAttrName =
+ attrDesc.toString()
+ .replaceFirst(clientAttributeTypeName,
+ serverAttributeTypeName);
+ Attribute serverAttribute = new LinkedAttribute(
+ AttributeDescription.valueOf(rewrittenAttrName),
+ mod.getAttribute().toArray());
+ rewrittenRequest.addModification(new Modification(
+ mod.getModificationType(),
+ serverAttribute));
+ } else {
+ rewrittenRequest.addModification(mod);
+ }
+ }
+ for (Control control : request.getControls()) {
+ rewrittenRequest.addControl(control);
+ }
+
+ return rewrittenRequest;
+ }
+
+ };
+
+ factory.getConnectionAsync(outerHandler);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void handleModifyDN(final RequestContext requestContext,
+ final ModifyDNRequest request,
+ final IntermediateResponseHandler intermediateResponseHandler,
+ final ResultHandler<? super Result> resultHandler) {
+ addProxiedAuthControl(request);
+ final ConnectionCompletionHandler<Result> outerHandler =
+ new ConnectionCompletionHandler<Result>(resultHandler) {
+
+ @Override
+ public void handleResult(final Connection connection) {
+ final RequestCompletionHandler<Result> innerHandler =
+ new RequestCompletionHandler<Result>(connection, resultHandler);
+ connection.modifyDNAsync(rewrite(request), intermediateResponseHandler,
+ innerHandler);
+ }
+
+ private ModifyDNRequest rewrite(ModifyDNRequest request) {
+ // Transform the client DNs into server DNs.
+ if (request.getNewSuperior() != null) {
+ return request
+ .setName(request.getName().toString()
+ .replace(clientSuffix, serverSuffix))
+ .setNewSuperior(request.getNewSuperior().toString()
+ .replace(clientSuffix, serverSuffix));
+ } else {
+ return request
+ .setName(request.getName().toString()
+ .replace(clientSuffix, serverSuffix));
+ }
+ }
+
+ };
+
+ factory.getConnectionAsync(outerHandler);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void handleSearch(final RequestContext requestContext, final SearchRequest request,
+ final IntermediateResponseHandler intermediateResponseHandler,
+ final SearchResultHandler resultHandler) {
+ addProxiedAuthControl(request);
+ final ConnectionCompletionHandler<Result> outerHandler =
+ new ConnectionCompletionHandler<Result>(resultHandler) {
+
+ @Override
+ public void handleResult(final Connection connection) {
+ final SearchRequestCompletionHandler innerHandler =
+ new SearchRequestCompletionHandler(connection, resultHandler);
+ connection.searchAsync(rewrite(request), intermediateResponseHandler,
+ innerHandler);
+ }
+
+ private SearchRequest rewrite(final SearchRequest request) {
+ // Transform the client attribute names to a server
+ // attribute names, fullname;lang-fr ==> cn;lang-fr.
+ String[] attrNames =
+ new String[request.getAttributes().size()];
+ int count = 0;
+ for (String attrName : request.getAttributes()) {
+ if (attrName.equalsIgnoreCase(clientAttributeTypeName)) {
+ attrNames[count] = serverAttributeTypeName;
+ } else {
+ attrNames[count] = attrName;
+ }
+ ++count;
+ }
+
+ // Rewrite the baseDN, and rewrite the Filter in
+ // dangerously lazy fashion. All the filter rewrite
+ // does is a string replace, so if the client
+ // attribute name appears in the value part of the
+ // AVA, this implementation will not work.
+ return Requests.newSearchRequest(
+ DN.valueOf(request.getName().toString()
+ .replace(clientSuffix, serverSuffix)),
+ request.getScope(),
+ Filter.valueOf(request.getFilter().toString()
+ .replace(clientAttributeTypeName,
+ serverAttributeTypeName)),
+ attrNames);
+ }
+
+ };
+
+ factory.getConnectionAsync(outerHandler);
+ }
+
+ private void addProxiedAuthControl(final Request request) {
+ final ProxiedAuthV2RequestControl control = proxiedAuthControl;
+ if (control != null) {
+ request.addControl(control);
+ }
+ }
+
+ }
+
+ /**
+ * Main method.
+ *
+ * @param args
+ * The command line arguments: local address, local port, proxy
+ * user DN, proxy user password, server address, server port
+ */
+ public static void main(final String[] args) {
+ if (args.length != 6) {
+ System.err.println("Usage:"
+ + "\tlocalAddress localPort proxyDN proxyPassword "
+ + "serverAddress serverPort");
+ System.exit(1);
+ }
+
+ final String localAddress = args[0];
+ final int localPort = Integer.parseInt(args[1]);
+ final String proxyDN = args[2];
+ final String proxyPassword = args[3];
+ final String remoteAddress = args[4];
+ final int remotePort = Integer.parseInt(args[5]);
+
+ // Create connection factories.
+ final ConnectionFactory factory =
+ Connections.newFixedConnectionPool(
+ Connections.newAuthenticatedConnectionFactory(
+ new LDAPConnectionFactory(remoteAddress, remotePort),
+ Requests.newSimpleBindRequest(
+ proxyDN, proxyPassword.toCharArray())),
+ Integer.MAX_VALUE);
+ final ConnectionFactory bindFactory =
+ Connections.newFixedConnectionPool(new LDAPConnectionFactory(
+ remoteAddress, remotePort), Integer.MAX_VALUE);
+
+ // Create a server connection adapter.
+ final ProxyBackend backend = new ProxyBackend(factory, bindFactory);
+ final ServerConnectionFactory<LDAPClientContext, Integer> connectionHandler =
+ Connections.newServerConnectionFactory(backend);
+
+ // Create listener.
+ final LDAPListenerOptions options = new LDAPListenerOptions().setBacklog(4096);
+ LDAPListener listener = null;
+ try {
+ listener = new LDAPListener(localAddress, localPort, connectionHandler, options);
+ System.out.println("Press any key to stop the server...");
+ System.in.read();
+ } catch (final IOException e) {
+ System.out.println("Error listening on " + localAddress + ":" + localPort);
+ e.printStackTrace();
+ } finally {
+ if (listener != null) {
+ listener.close();
+ }
+ }
+ }
+
+ private RewriterProxy() {
+ // Not used.
+ }
+}
diff --git a/opendj3/src/main/docbkx/dev-guide/chap-simple-proxy.xml b/opendj3/src/main/docbkx/dev-guide/chap-simple-proxy.xml
index 12163ba..d4ed13f 100644
--- a/opendj3/src/main/docbkx/dev-guide/chap-simple-proxy.xml
+++ b/opendj3/src/main/docbkx/dev-guide/chap-simple-proxy.xml
@@ -114,9 +114,85 @@
and so forth.</para>
</section>
+ <section xml:id="handling-client-connections">
+ <title>Listening For & Handling Client Connections</title>
+
+ <para>You create an <literal>LDAPListener</literal> to handle incoming client
+ connections. The <literal>LDAPListener</literal> takes a connection handler
+ that deals with the connections, in this case connections back to the
+ directory servers handling client requests.</para>
+
+ <programlisting language="java">
+final LDAPListenerOptions options = new LDAPListenerOptions().setBacklog(4096);
+LDAPListener listener = null;
+try {
+ listener = new LDAPListener(localAddress, localPort, connectionHandler,
+ options);
+ System.out.println("Press any key to stop the server...");
+ System.in.read();
+} catch (final IOException e) {
+ System.out.println("Error listening on " + localAddress + ":" + localPort);
+ e.printStackTrace();
+} finally {
+ if (listener != null) {
+ listener.close();
+ }
+}
+</programlisting>
+
+ <para>You get a <literal>ServerConnectionFactory</literal> to handle requests
+ coming from clients. The <literal>ServerConnectionFactory</literal> takes a
+ request handler that deals with the incoming client requests. The request
+ handler implements handlers for all supported operations. The Proxy example
+ implements a <literal>ProxyBackend</literal> to handle requests. The
+ <literal>ProxyBackend</literal> sends the requests on to the backend
+ directory servers and routes the results returned back to client
+ applications.</para>
+
+ <programlisting language="java">
+final ProxyBackend backend = new ProxyBackend(factory, bindFactory);
+final ServerConnectionFactory<LDAPClientContext, Integer> connectionHandler =
+ Connections.newServerConnectionFactory(backend);
+</programlisting>
+
+ <para>See the Proxy example code for details about the
+ <literal>ProxyBackend</literal> implementation.</para>
+ </section>
+
<section xml:id="dn-attr-rewriting">
<title>DN & Attribute Rewriting</title>
- <para>TODO</para>
+ <para>Suppose you have a client application that expects a different
+ attribute name, such as <literal>fullname</literal> for a standard attribute
+ like <literal>cn</literal> (common name), and that expects a distinguished
+ name (DN) suffix different from what is stored in the directory. If you
+ cannot change the application, one possible alternative is a proxy layer
+ that does DN and attribute rewriting.<footnote><para>Some servers, such as
+ OpenDJ directory server, can do attribute rewriting without a proxy layer.
+ See your directory server's documentation for details.</para></footnote></para>
+
+ <screen># A search accessing the directory server
+$ ldapsearch -b dc=example,dc=com -p 1389 "(cn=Babs Jensen)" cn
+dn: uid=bjensen,ou=People,dc=example,dc=com
+cn: Barbara Jensen
+cn: Babs Jensen
+
+# The same search search accessing a proxy that rewrites requests and responses
+$ ldapsearch -b o=example -p 8389 "(fullname=Babs Jensen)" fullname
+dn: uid=bjensen,ou=People,o=example
+fullname: Barbara Jensen
+fullname: Babs Jensen
+</screen>
+
+ <para>The OpenDJ LDAP SDK <link xlink:show="new"
+ xlink:href="http://opendj.forgerock.org/opendj-ldap-sdk-examples/xref/org/forgerock/opendj/examples/RewriterProxy.html"
+ >RewriterProxy</link> example builds on the Proxy example, rewriting requests
+ and search result entries. When you read the example, look for the
+ <literal>rewrite()</literal> methods.</para>
+
+ <para>In the above output, the rewriter proxy listens on port 8389,
+ connecting to a directory server listening on 1389. The directory server
+ contains data from <link xlink:href="http://opendj.forgerock.org/Example.ldif"
+ xlink:show="new"><filename>Example.ldif</filename></link>.</para>
</section>
</chapter>
--
Gitblit v1.10.0