From 33db777770ba0a2e612d60d26a4e44f280263201 Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Wed, 06 Mar 2013 11:57:52 +0000
Subject: [PATCH] Partial fix for OPENDJ-699: Implement DN reference mapping

---
 opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ReferenceAttributeMapper.java |  127 +++++++++++++++++++++++++++++++++++++++--
 1 files changed, 119 insertions(+), 8 deletions(-)

diff --git a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ReferenceAttributeMapper.java b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ReferenceAttributeMapper.java
index 7b52566..132e76e 100644
--- a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ReferenceAttributeMapper.java
+++ b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ReferenceAttributeMapper.java
@@ -15,20 +15,23 @@
  */
 package org.forgerock.opendj.rest2ldap;
 
-import static org.forgerock.opendj.ldap.Filter.alwaysFalse;
+import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
 import static org.forgerock.opendj.rest2ldap.Utils.accumulate;
 import static org.forgerock.opendj.rest2ldap.Utils.adapt;
+import static org.forgerock.opendj.rest2ldap.Utils.ensureNotNull;
 import static org.forgerock.opendj.rest2ldap.Utils.transform;
 import static org.forgerock.opendj.rest2ldap.WritabilityPolicy.READ_WRITE;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.LinkedHashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
 
 import org.forgerock.json.fluent.JsonPointer;
 import org.forgerock.json.fluent.JsonValue;
+import org.forgerock.json.resource.ResourceException;
 import org.forgerock.json.resource.ResultHandler;
 import org.forgerock.opendj.ldap.Attribute;
 import org.forgerock.opendj.ldap.AttributeDescription;
@@ -37,23 +40,38 @@
 import org.forgerock.opendj.ldap.ErrorResultException;
 import org.forgerock.opendj.ldap.Filter;
 import org.forgerock.opendj.ldap.Function;
+import org.forgerock.opendj.ldap.ResultCode;
+import org.forgerock.opendj.ldap.SearchResultHandler;
+import org.forgerock.opendj.ldap.SearchScope;
+import org.forgerock.opendj.ldap.requests.Requests;
+import org.forgerock.opendj.ldap.requests.SearchRequest;
+import org.forgerock.opendj.ldap.responses.Result;
 import org.forgerock.opendj.ldap.responses.SearchResultEntry;
+import org.forgerock.opendj.ldap.responses.SearchResultReference;
 
 /**
  * An attribute mapper which provides a mapping from a JSON value to a single DN
  * valued LDAP attribute.
  */
 public final class ReferenceAttributeMapper extends AttributeMapper {
+    /**
+     * The maximum number of candidate references to allow in search filters.
+     */
+    private static final int SEARCH_MAX_CANDIDATES = 1000;
 
+    private final DN baseDN;
+    private Filter filter = null;
     private boolean isRequired = false;
     private boolean isSingleValued = false;
     private final AttributeDescription ldapAttributeName;
     private final AttributeMapper mapper;
+    private SearchScope scope = SearchScope.WHOLE_SUBTREE;
     private WritabilityPolicy writabilityPolicy = READ_WRITE;
 
-    ReferenceAttributeMapper(final AttributeDescription ldapAttributeName,
+    ReferenceAttributeMapper(final AttributeDescription ldapAttributeName, final DN baseDN,
             final AttributeMapper mapper) {
         this.ldapAttributeName = ldapAttributeName;
+        this.baseDN = baseDN;
         this.mapper = mapper;
     }
 
@@ -80,6 +98,47 @@
     }
 
     /**
+     * Sets the filter which should be used when searching for referenced LDAP
+     * entries. The default is {@code (objectClass=*)}.
+     *
+     * @param filter
+     *            The filter which should be used when searching for referenced
+     *            LDAP entries.
+     * @return This attribute mapper.
+     */
+    public ReferenceAttributeMapper searchFilter(final Filter filter) {
+        this.filter = ensureNotNull(filter);
+        return this;
+    }
+
+    /**
+     * Sets the filter which should be used when searching for referenced LDAP
+     * entries. The default is {@code (objectClass=*)}.
+     *
+     * @param filter
+     *            The filter which should be used when searching for referenced
+     *            LDAP entries.
+     * @return This attribute mapper.
+     */
+    public ReferenceAttributeMapper searchFilter(final String filter) {
+        return searchFilter(Filter.valueOf(filter));
+    }
+
+    /**
+     * Sets the search scope which should be used when searching for referenced
+     * LDAP entries. The default is {@link SearchScope#WHOLE_SUBTREE}.
+     *
+     * @param scope
+     *            The search scope which should be used when searching for
+     *            referenced LDAP entries.
+     * @return This attribute mapper.
+     */
+    public ReferenceAttributeMapper searchScope(final SearchScope scope) {
+        this.scope = ensureNotNull(scope);
+        return this;
+    }
+
+    /**
      * Indicates whether or not the LDAP attribute supports updates. The default
      * is {@link WritabilityPolicy#READ_WRITE}.
      *
@@ -101,10 +160,62 @@
     @Override
     void getLDAPFilter(final Context c, final FilterType type, final JsonPointer jsonAttribute,
             final String operator, final Object valueAssertion, final ResultHandler<Filter> h) {
-        // TODO: only presence and equality matching will be supported. Equality matching will
-        // only work for the primary key (whatever that is) by performing a reverse look up to
-        // convert the primary key to a DN.
-        h.handleResult(alwaysFalse());
+        // First construct a filter which can be used to find referenced resources.
+        final ResultHandler<Filter> filterHandler = new ResultHandler<Filter>() {
+            @Override
+            public void handleError(final ResourceException error) {
+                h.handleError(error); // Propagate.
+            }
+
+            @Override
+            public void handleResult(final Filter result) {
+                // Now construct a search to find candidate DNs.
+                final Filter searchFilter = filter != null ? Filter.and(filter, result) : result;
+                final SearchRequest request =
+                        Requests.newSearchRequest(baseDN, scope, searchFilter, "1.1");
+
+                // Create a result handler which will collect the returned entries and construct a search filter.
+                final SearchResultHandler searchHandler = new SearchResultHandler() {
+                    final List<Filter> subFilters = new LinkedList<Filter>();
+
+                    @Override
+                    public boolean handleEntry(final SearchResultEntry entry) {
+                        if (subFilters.size() < SEARCH_MAX_CANDIDATES) {
+                            subFilters.add(Filter.equality(ldapAttributeName.toString(), entry
+                                    .getName()));
+                            return true;
+                        } else {
+                            // No point in continuing - maximum candidates reached.
+                            return false;
+                        }
+                    }
+
+                    @Override
+                    public void handleErrorResult(final ErrorResultException error) {
+                        h.handleError(adapt(error)); // Propagate.
+                    }
+
+                    @Override
+                    public boolean handleReference(final SearchResultReference reference) {
+                        // Ignore references.
+                        return true;
+                    }
+
+                    @Override
+                    public void handleResult(final Result result) {
+                        if (subFilters.size() >= SEARCH_MAX_CANDIDATES) {
+                            handleErrorResult(newErrorResult(ResultCode.ADMIN_LIMIT_EXCEEDED));
+                        } else if (subFilters.size() == 1) {
+                            h.handleResult(subFilters.get(0));
+                        } else {
+                            h.handleResult(Filter.or(subFilters));
+                        }
+                    }
+                };
+                c.getConnection().searchAsync(request, null, searchHandler);
+            }
+        };
+        mapper.getLDAPFilter(c, type, jsonAttribute, operator, valueAssertion, filterHandler);
     }
 
     @Override
@@ -116,7 +227,7 @@
             try {
                 final DN dn = attribute.parse().usingSchema(c.getConfig().schema()).asDN();
                 readEntry(c, dn, h);
-            } catch (Exception ex) {
+            } catch (final Exception ex) {
                 // The LDAP attribute could not be decoded.
                 h.handleError(adapt(ex));
             }
@@ -146,7 +257,7 @@
                 for (final DN dn : dns) {
                     readEntry(c, dn, handler);
                 }
-            } catch (Exception ex) {
+            } catch (final Exception ex) {
                 // The LDAP attribute could not be decoded.
                 h.handleError(adapt(ex));
             }

--
Gitblit v1.10.0