Fix OPENDJ-690: Rest2LDAP - Implement basic search support
3 files added
12 files modified
| | |
| | | * Header, with the fields enclosed by brackets [] replaced by your own identifying |
| | | * information: "Portions Copyright [year] [name of copyright owner]". |
| | | * |
| | | * Copyright 2012 ForgeRock AS. |
| | | * Copyright 2012-2013 ForgeRock AS. |
| | | */ |
| | | package org.forgerock.opendj.rest2ldap; |
| | | |
| | |
| | | import org.forgerock.json.fluent.JsonPointer; |
| | | import org.forgerock.json.fluent.JsonValue; |
| | | import org.forgerock.json.resource.ResultHandler; |
| | | import org.forgerock.json.resource.ServerContext; |
| | | import org.forgerock.opendj.ldap.Attribute; |
| | | import org.forgerock.opendj.ldap.Entry; |
| | | import org.forgerock.opendj.ldap.Filter; |
| | | |
| | | /** |
| | | * An attribute mapper is responsible for converting JSON values to and from |
| | |
| | | * Implementations should only add the names of attributes found in the LDAP |
| | | * entry directly associated with the resource. |
| | | * |
| | | * @param c |
| | | * The context. |
| | | * @param jsonAttribute |
| | | * The name of the resource attribute requested by the client. |
| | | * @param ldapAttributes |
| | | * The set into which the required LDAP attribute names should be |
| | | * put. |
| | | */ |
| | | void getLDAPAttributes(JsonPointer jsonAttribute, Set<String> ldapAttributes); |
| | | void getLDAPAttributes(Context c, JsonPointer jsonAttribute, Set<String> ldapAttributes); |
| | | |
| | | /** |
| | | * Transforms the provided REST comparison filter parameters to an LDAP |
| | | * filter representation, invoking a completion handler once the |
| | | * transformation has completed. |
| | | * <p> |
| | | * If this attribute mapper is not responsible for mapping the provided JSON |
| | | * attribute then the result handler's {@link ResultHandler#handleResult |
| | | * handleResult} method must be invoked with the value {@code null}. If this |
| | | * attribute mapper is responsible for mapping the JSON attribute, but an |
| | | * error occurred while constructing the LDAP filter, then the result |
| | | * handler's {@link ResultHandler#handleError handleError} method must be |
| | | * invoked with an appropriate exception indicating the problem which |
| | | * occurred. |
| | | * |
| | | * @param c |
| | | * The context. |
| | | * @param type |
| | | * The type of REST comparison filter. |
| | | * @param jsonAttribute |
| | | * The name of the resource attribute to be filtered. |
| | | * @param operator |
| | | * The name of the extended operator to use for the comparison, |
| | | * or {@code null} if {@code type} is not |
| | | * {@link FilterType#EXTENDED}. |
| | | * @param valueAssertion |
| | | * The value assertion, or {@code null} if {@code type} is |
| | | * {@link FilterType#PRESENT}. |
| | | * @param h |
| | | * The result handler. |
| | | */ |
| | | void getLDAPFilter(Context c, FilterType type, JsonPointer jsonAttribute, String operator, |
| | | Object valueAssertion, ResultHandler<Filter> h); |
| | | |
| | | /** |
| | | * Transforms attributes contained in the provided LDAP entry to JSON |
| | |
| | | * requests. |
| | | * |
| | | * @param c |
| | | * The server context. |
| | | * The context. |
| | | * @param e |
| | | * The LDAP entry to be converted to JSON. |
| | | * @param h |
| | | * The result handler. |
| | | */ |
| | | void toJSON(ServerContext c, Entry e, ResultHandler<Map<String, Object>> h); |
| | | void toJSON(Context c, Entry e, ResultHandler<Map<String, Object>> h); |
| | | |
| | | /** |
| | | * Transforms JSON content in the provided JSON value to LDAP attributes, |
| | |
| | | * requests. |
| | | * |
| | | * @param c |
| | | * The server context. |
| | | * The context. |
| | | * @param v |
| | | * The JSON value to be converted to LDAP attributes. |
| | | * @param h |
| | | * The result handler. |
| | | */ |
| | | void toLDAP(ServerContext c, JsonValue v, ResultHandler<List<Attribute>> h); |
| | | void toLDAP(Context c, JsonValue v, ResultHandler<List<Attribute>> h); |
| | | |
| | | // TODO: methods for obtaining schema information (e.g. name, description, |
| | | // type information). |
| | | // TODO: methods for creating filters createLDAPEqualityFilter(). |
| | | // TODO: methods for creating sort controls. |
| | | } |
| | |
| | | * Header, with the fields enclosed by brackets [] replaced by your own identifying |
| | | * information: "Portions Copyright [year] [name of copyright owner]". |
| | | * |
| | | * Copyright 2012 ForgeRock AS. |
| | | * Copyright 2012-2013 ForgeRock AS. |
| | | */ |
| | | package org.forgerock.opendj.rest2ldap; |
| | | |
| | |
| | | import org.forgerock.json.fluent.JsonValue; |
| | | import org.forgerock.json.resource.ResourceException; |
| | | import org.forgerock.json.resource.ResultHandler; |
| | | import org.forgerock.json.resource.ServerContext; |
| | | import org.forgerock.opendj.ldap.Attribute; |
| | | import org.forgerock.opendj.ldap.Entry; |
| | | import org.forgerock.opendj.ldap.Filter; |
| | | |
| | | /** |
| | | * An attribute mapper which maps a single JSON attribute to the result of |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void getLDAPAttributes(final JsonPointer jsonAttribute, final Set<String> ldapAttributes) { |
| | | if (attributeMatchesPointer(jsonAttribute)) { |
| | | public void getLDAPAttributes(final Context c, final JsonPointer jsonAttribute, |
| | | final Set<String> ldapAttributes) { |
| | | if (jsonAttribute.isEmpty() || matches(jsonAttribute)) { |
| | | final JsonPointer relativePointer = jsonAttribute.relativePointer(); |
| | | mapper.getLDAPAttributes(relativePointer, ldapAttributes); |
| | | mapper.getLDAPAttributes(c, relativePointer, ldapAttributes); |
| | | } |
| | | } |
| | | |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void toJSON(final ServerContext c, final Entry e, |
| | | public void getLDAPFilter(final Context c, final FilterType type, |
| | | final JsonPointer jsonAttribute, final String operator, final Object valueAssertion, |
| | | final ResultHandler<Filter> h) { |
| | | if (matches(jsonAttribute)) { |
| | | final JsonPointer relativePointer = jsonAttribute.relativePointer(); |
| | | mapper.getLDAPFilter(c, type, relativePointer, operator, valueAssertion, h); |
| | | } else { |
| | | // This attribute mapper cannot handle the provided filter component. |
| | | h.handleResult(null); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void toJSON(final Context c, final Entry e, |
| | | final ResultHandler<Map<String, Object>> h) { |
| | | final ResultHandler<Map<String, Object>> wrapper = new ResultHandler<Map<String, Object>>() { |
| | | final ResultHandler<Map<String, Object>> wrapper = |
| | | new ResultHandler<Map<String, Object>>() { |
| | | |
| | | @Override |
| | | public void handleError(final ResourceException e) { |
| | | h.handleError(e); |
| | | } |
| | | @Override |
| | | public void handleError(final ResourceException e) { |
| | | h.handleError(e); |
| | | } |
| | | |
| | | @Override |
| | | public void handleResult(final Map<String, Object> result) { |
| | | final Map<String, Object> complexResult = Collections.singletonMap( |
| | | jsonAttributeName, (Object) result); |
| | | h.handleResult(complexResult); |
| | | } |
| | | }; |
| | | @Override |
| | | public void handleResult(final Map<String, Object> result) { |
| | | final Map<String, Object> complexResult = |
| | | Collections.singletonMap(jsonAttributeName, (Object) result); |
| | | h.handleResult(complexResult); |
| | | } |
| | | }; |
| | | mapper.toJSON(c, e, wrapper); |
| | | } |
| | | |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void toLDAP(final ServerContext c, final JsonValue v, |
| | | public void toLDAP(final Context c, final JsonValue v, |
| | | final ResultHandler<List<Attribute>> h) { |
| | | // TODO Auto-generated method stub |
| | | |
| | | } |
| | | |
| | | private boolean attributeMatchesPointer(final JsonPointer resourceAttribute) { |
| | | return resourceAttribute.isEmpty() |
| | | || toLowerCase(resourceAttribute.get(0)).equals(normalizedJsonAttributeName); |
| | | private boolean matches(final JsonPointer jsonAttribute) { |
| | | return !jsonAttribute.isEmpty() |
| | | && toLowerCase(jsonAttribute.get(0)).equals(normalizedJsonAttributeName); |
| | | } |
| | | |
| | | } |
| | |
| | | * Header, with the fields enclosed by brackets [] replaced by your own identifying |
| | | * information: "Portions Copyright [year] [name of copyright owner]". |
| | | * |
| | | * Copyright 2012 ForgeRock AS. |
| | | * Copyright 2012-2013 ForgeRock AS. |
| | | */ |
| | | package org.forgerock.opendj.rest2ldap; |
| | | |
| | | import static org.forgerock.opendj.rest2ldap.Utils.accumulate; |
| | | import static org.forgerock.opendj.rest2ldap.Utils.transform; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.Iterator; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.LinkedList; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | import java.util.concurrent.atomic.AtomicInteger; |
| | | |
| | | 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.json.resource.ServerContext; |
| | | import org.forgerock.opendj.ldap.Attribute; |
| | | import org.forgerock.opendj.ldap.Entry; |
| | | import org.forgerock.opendj.ldap.Filter; |
| | | import org.forgerock.opendj.ldap.Function; |
| | | |
| | | /** |
| | | * An attribute mapper which combines the results of a set of subordinate |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void getLDAPAttributes(final JsonPointer jsonAttribute, final Set<String> ldapAttributes) { |
| | | public void getLDAPAttributes(final Context c, final JsonPointer jsonAttribute, |
| | | final Set<String> ldapAttributes) { |
| | | for (final AttributeMapper attribute : attributeMappers) { |
| | | attribute.getLDAPAttributes(jsonAttribute, ldapAttributes); |
| | | attribute.getLDAPAttributes(c, jsonAttribute, ldapAttributes); |
| | | } |
| | | } |
| | | |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void toJSON(final ServerContext c, final Entry e, |
| | | final ResultHandler<Map<String, Object>> h) { |
| | | final ResultHandler<Map<String, Object>> resultAccumulater = new ResultHandler<Map<String, Object>>() { |
| | | private final AtomicInteger latch = new AtomicInteger(attributeMappers.size()); |
| | | private final List<Map<String, Object>> results = new ArrayList<Map<String, Object>>( |
| | | latch.get()); |
| | | public void getLDAPFilter(final Context c, final FilterType type, |
| | | final JsonPointer jsonAttribute, final String operator, final Object valueAssertion, |
| | | final ResultHandler<Filter> h) { |
| | | final ResultHandler<Filter> handler = |
| | | accumulate(attributeMappers.size(), transform( |
| | | new Function<List<Filter>, Filter, Void>() { |
| | | @Override |
| | | public Filter apply(final List<Filter> value, final Void p) { |
| | | // Remove unmapped filters and combine using logical-OR. |
| | | final Iterator<Filter> i = value.iterator(); |
| | | while (i.hasNext()) { |
| | | final Filter f = i.next(); |
| | | if (f == null) { |
| | | // No mapping so remove. |
| | | i.remove(); |
| | | } else if (f == c.getConfig().getFalseFilter()) { |
| | | return c.getConfig().getFalseFilter(); |
| | | } else if (f == c.getConfig().getTrueFilter()) { |
| | | return c.getConfig().getTrueFilter(); |
| | | } |
| | | } |
| | | switch (value.size()) { |
| | | case 0: |
| | | // No mappings found. |
| | | return null; |
| | | case 1: |
| | | return value.get(0); |
| | | default: |
| | | return Filter.or(value); |
| | | } |
| | | } |
| | | }, h)); |
| | | for (final AttributeMapper subMapper : attributeMappers) { |
| | | subMapper.getLDAPFilter(c, type, jsonAttribute, operator, valueAssertion, handler); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void handleError(final ResourceException e) { |
| | | // Ensure that handler is only invoked once. |
| | | if (latch.getAndSet(0) > 0) { |
| | | h.handleError(e); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void handleResult(final Map<String, Object> result) { |
| | | if (result != null && !result.isEmpty()) { |
| | | synchronized (this) { |
| | | results.add(result); |
| | | } |
| | | } |
| | | if (latch.decrementAndGet() == 0) { |
| | | final Map<String, Object> mergeResult; |
| | | switch (results.size()) { |
| | | case 0: |
| | | mergeResult = Collections.<String, Object> emptyMap(); |
| | | break; |
| | | case 1: |
| | | mergeResult = results.get(0); |
| | | break; |
| | | default: |
| | | mergeResult = new LinkedHashMap<String, Object>(); |
| | | mergeJsonValues(results, mergeResult); |
| | | break; |
| | | } |
| | | h.handleResult(mergeResult); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void toJSON(final Context c, final Entry e, final ResultHandler<Map<String, Object>> h) { |
| | | final ResultHandler<Map<String, Object>> handler = |
| | | accumulate(attributeMappers.size(), transform( |
| | | new Function<List<Map<String, Object>>, Map<String, Object>, Void>() { |
| | | @Override |
| | | public Map<String, Object> apply(final List<Map<String, Object>> value, |
| | | final Void p) { |
| | | switch (value.size()) { |
| | | case 0: |
| | | return Collections.<String, Object> emptyMap(); |
| | | case 1: |
| | | return value.get(0) != null ? value.get(0) : Collections |
| | | .<String, Object> emptyMap(); |
| | | default: |
| | | return mergeJsonValues(value, |
| | | new LinkedHashMap<String, Object>()); |
| | | } |
| | | } |
| | | }, h)); |
| | | for (final AttributeMapper mapper : attributeMappers) { |
| | | mapper.toJSON(c, e, resultAccumulater); |
| | | mapper.toJSON(c, e, handler); |
| | | } |
| | | } |
| | | |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void toLDAP(final ServerContext c, final JsonValue v, |
| | | final ResultHandler<List<Attribute>> h) { |
| | | public void toLDAP(final Context c, final JsonValue v, final ResultHandler<List<Attribute>> h) { |
| | | // TODO Auto-generated method stub |
| | | |
| | | } |
| | | |
| | | /** |
| | | * Merge one JSON value into another. |
| | | * |
| | | * @param srcValue |
| | | * The source value. |
| | | * @param dstValue |
| | | * The destination value, into which which the value should be |
| | | * merged. |
| | | */ |
| | | @SuppressWarnings("unchecked") |
| | | private void mergeJsonValue(final Map<String, Object> srcValue, |
| | | final Map<String, Object> dstValue) { |
| | |
| | | } else if ((existingValue instanceof Map) && (newValue instanceof Map)) { |
| | | // Merge two maps - create a new Map, in case the existing one |
| | | // is unmodifiable. |
| | | existingValue = new LinkedHashMap<String, Object>( |
| | | (Map<String, Object>) existingValue); |
| | | existingValue = |
| | | new LinkedHashMap<String, Object>((Map<String, Object>) existingValue); |
| | | mergeJsonValue((Map<String, Object>) newValue, (Map<String, Object>) existingValue); |
| | | } else if ((existingValue instanceof List) && (newValue instanceof List)) { |
| | | // Merge two lists- create a new List, in case the existing one |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Merge the provided list of JSON values into a single value. |
| | | * |
| | | * @param srcValues |
| | | * The source values. |
| | | * @param dstValue |
| | | * The destination value, into which which the values should be |
| | | * merged. |
| | | */ |
| | | private void mergeJsonValues(final List<Map<String, Object>> srcValues, |
| | | private Map<String, Object> mergeJsonValues(final List<Map<String, Object>> srcValues, |
| | | final Map<String, Object> dstValue) { |
| | | for (final Map<String, Object> value : srcValues) { |
| | | mergeJsonValue(value, dstValue); |
| | | if (value != null) { |
| | | mergeJsonValue(value, dstValue); |
| | | } |
| | | } |
| | | return dstValue; |
| | | } |
| | | } |
| New file |
| | |
| | | /* |
| | | * 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 2013 ForgeRock AS. |
| | | */ |
| | | package org.forgerock.opendj.rest2ldap; |
| | | |
| | | import org.forgerock.opendj.ldap.Filter; |
| | | |
| | | /** |
| | | * Common configuration options. |
| | | */ |
| | | public final class Config { |
| | | |
| | | private final Filter trueFilter = Filter.objectClassPresent(); |
| | | private final Filter falseFilter = Filter.present("1.1"); |
| | | |
| | | /** |
| | | * Returns the absolute true filter. |
| | | * |
| | | * @return The absolute true filter. |
| | | */ |
| | | public Filter getTrueFilter() { |
| | | return trueFilter; |
| | | } |
| | | |
| | | /** |
| | | * Returns the absolute false filter. |
| | | * |
| | | * @return The absolute false filter. |
| | | */ |
| | | public Filter getFalseFilter() { |
| | | return falseFilter; |
| | | } |
| | | } |
| | |
| | | * Header, with the fields enclosed by brackets [] replaced by your own identifying |
| | | * information: "Portions Copyright [year] [name of copyright owner]". |
| | | * |
| | | * Copyright 2012 ForgeRock AS. |
| | | * Copyright 2012-2013 ForgeRock AS. |
| | | */ |
| | | package org.forgerock.opendj.rest2ldap; |
| | | |
| | | import static org.forgerock.opendj.rest2ldap.Utils.toLowerCase; |
| | | |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | |
| | | import org.forgerock.json.fluent.JsonPointer; |
| | | import org.forgerock.json.fluent.JsonValue; |
| | | import org.forgerock.json.resource.ResultHandler; |
| | | import org.forgerock.json.resource.ServerContext; |
| | | import org.forgerock.opendj.ldap.Attribute; |
| | | import org.forgerock.opendj.ldap.Entry; |
| | | import org.forgerock.opendj.ldap.Filter; |
| | | |
| | | /** |
| | | * An attribute mapper which maps a single JSON attribute to a fixed value. |
| | | */ |
| | | public class ConstantAttributeMapper implements AttributeMapper { |
| | | |
| | | private final String jsonAttributeName; |
| | | private final Object jsonAttributeValue; |
| | | |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void getLDAPAttributes(final JsonPointer jsonAttribute, final Set<String> ldapAttributes) { |
| | | public void getLDAPAttributes(final Context c, final JsonPointer jsonAttribute, |
| | | final Set<String> ldapAttributes) { |
| | | // Nothing to do. |
| | | } |
| | | |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void toJSON(final ServerContext c, final Entry e, |
| | | final ResultHandler<Map<String, Object>> h) { |
| | | public void getLDAPFilter(final Context c, final FilterType type, |
| | | final JsonPointer jsonAttribute, final String operator, final Object valueAssertion, |
| | | final ResultHandler<Filter> h) { |
| | | if (jsonAttribute.size() == 1 && jsonAttribute.get(0).equalsIgnoreCase(jsonAttributeName)) { |
| | | final Filter filter; |
| | | if (type == FilterType.PRESENT) { |
| | | filter = c.getConfig().getTrueFilter(); |
| | | } else if (jsonAttributeValue instanceof String && valueAssertion instanceof String) { |
| | | final String v1 = toLowerCase((String) jsonAttributeValue); |
| | | final String v2 = toLowerCase((String) valueAssertion); |
| | | switch (type) { |
| | | case CONTAINS: |
| | | filter = |
| | | v1.contains(v2) ? c.getConfig().getTrueFilter() : c.getConfig() |
| | | .getFalseFilter(); |
| | | break; |
| | | case STARTS_WITH: |
| | | filter = |
| | | v1.startsWith(v2) ? c.getConfig().getTrueFilter() : c.getConfig() |
| | | .getFalseFilter(); |
| | | break; |
| | | default: |
| | | filter = compare(c, type, v1, v2); |
| | | break; |
| | | } |
| | | } else if (jsonAttributeValue instanceof Number && valueAssertion instanceof Number) { |
| | | final Double v1 = ((Number) jsonAttributeValue).doubleValue(); |
| | | final Double v2 = ((Number) valueAssertion).doubleValue(); |
| | | filter = compare(c, type, v1, v2); |
| | | } else if (jsonAttributeValue instanceof Boolean && valueAssertion instanceof Boolean) { |
| | | final Boolean v1 = (Boolean) jsonAttributeValue; |
| | | final Boolean v2 = (Boolean) valueAssertion; |
| | | filter = compare(c, type, v1, v2); |
| | | } else { |
| | | // This attribute mapper is a candidate but it does not match. |
| | | filter = c.getConfig().getFalseFilter(); |
| | | } |
| | | h.handleResult(filter); |
| | | } else { |
| | | // This attribute mapper cannot handle the provided filter component. |
| | | h.handleResult(null); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void toJSON(final Context c, final Entry e, final ResultHandler<Map<String, Object>> h) { |
| | | // FIXME: how do we know if the user requested it??? |
| | | h.handleResult(Collections.singletonMap(jsonAttributeName, jsonAttributeValue)); |
| | | |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void toLDAP(final ServerContext c, final JsonValue v, |
| | | final ResultHandler<List<Attribute>> h) { |
| | | public void toLDAP(final Context c, final JsonValue v, final ResultHandler<List<Attribute>> h) { |
| | | // TODO Auto-generated method stub |
| | | } |
| | | |
| | | private <T extends Comparable<T>> Filter compare(Context c, final FilterType type, final T v1, |
| | | final T v2) { |
| | | final Filter filter; |
| | | switch (type) { |
| | | case EQUAL_TO: |
| | | filter = v1.equals(v2) ? c.getConfig().getTrueFilter() : c.getConfig().getFalseFilter(); |
| | | break; |
| | | case GREATER_THAN: |
| | | filter = |
| | | v1.compareTo(v2) > 0 ? c.getConfig().getTrueFilter() : c.getConfig() |
| | | .getFalseFilter(); |
| | | break; |
| | | case GREATER_THAN_OR_EQUAL_TO: |
| | | filter = |
| | | v1.compareTo(v2) >= 0 ? c.getConfig().getTrueFilter() : c.getConfig() |
| | | .getFalseFilter(); |
| | | break; |
| | | case LESS_THAN: |
| | | filter = |
| | | v1.compareTo(v2) < 0 ? c.getConfig().getTrueFilter() : c.getConfig() |
| | | .getFalseFilter(); |
| | | break; |
| | | case LESS_THAN_OR_EQUAL_TO: |
| | | filter = |
| | | v1.compareTo(v2) <= 0 ? c.getConfig().getTrueFilter() : c.getConfig() |
| | | .getFalseFilter(); |
| | | break; |
| | | default: |
| | | filter = c.getConfig().getFalseFilter(); // Not supported. |
| | | break; |
| | | } |
| | | return filter; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | /* |
| | | * 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 2013 ForgeRock AS. |
| | | */ |
| | | package org.forgerock.opendj.rest2ldap; |
| | | |
| | | import org.forgerock.json.resource.ServerContext; |
| | | |
| | | /** |
| | | * Common context information passed to containers and mappers. |
| | | */ |
| | | public final class Context { |
| | | private final Config config; |
| | | private final ServerContext context; |
| | | |
| | | Context(Config config, ServerContext context) { |
| | | this.config = config; |
| | | this.context = context; |
| | | } |
| | | |
| | | /** |
| | | * Returns the common configuration options. |
| | | * |
| | | * @return The common configuration options. |
| | | */ |
| | | public Config getConfig() { |
| | | return config; |
| | | } |
| | | |
| | | /** |
| | | * Returns the commons REST server context passed in with the REST request. |
| | | * |
| | | * @return The commons REST server context passed in with the REST request. |
| | | */ |
| | | public ServerContext getServerContext() { |
| | | return context; |
| | | } |
| | | } |
| | |
| | | * Header, with the fields enclosed by brackets [] replaced by your own identifying |
| | | * information: "Portions Copyright [year] [name of copyright owner]". |
| | | * |
| | | * Copyright 2012 ForgeRock AS. |
| | | * Copyright 2012-2013 ForgeRock AS. |
| | | */ |
| | | package org.forgerock.opendj.rest2ldap; |
| | | |
| | | import static org.forgerock.opendj.rest2ldap.Utils.attributeToJson; |
| | | import static org.forgerock.opendj.rest2ldap.Utils.getAttributeName; |
| | | import static org.forgerock.opendj.rest2ldap.Utils.toFilter; |
| | | import static org.forgerock.opendj.rest2ldap.Utils.toLowerCase; |
| | | |
| | | import java.util.LinkedHashMap; |
| | |
| | | import org.forgerock.json.fluent.JsonPointer; |
| | | import org.forgerock.json.fluent.JsonValue; |
| | | import org.forgerock.json.resource.ResultHandler; |
| | | import org.forgerock.json.resource.ServerContext; |
| | | import org.forgerock.opendj.ldap.Attribute; |
| | | import org.forgerock.opendj.ldap.Entry; |
| | | import org.forgerock.opendj.ldap.Filter; |
| | | |
| | | /** |
| | | * An attribute mapper that directly maps a configurable selection of attributes |
| | | * to and from LDAP without any transformation. |
| | | */ |
| | | public final class DefaultAttributeMapper implements AttributeMapper { |
| | | |
| | | private final Map<String, String> excludedAttributes = new LinkedHashMap<String, String>(); |
| | | // All user attributes by default. |
| | | private final Map<String, String> excludedAttributes = new LinkedHashMap<String, String>(); |
| | | private final Map<String, String> includedAttributes = new LinkedHashMap<String, String>(); |
| | | |
| | | /** |
| | |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void getLDAPAttributes(final JsonPointer jsonAttribute, final Set<String> ldapAttributes) { |
| | | public void getLDAPAttributes(final Context c, final JsonPointer jsonAttribute, |
| | | final Set<String> ldapAttributes) { |
| | | switch (jsonAttribute.size()) { |
| | | case 0: |
| | | // Requested everything. |
| | |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void getLDAPFilter(final Context c, final FilterType type, |
| | | final JsonPointer jsonAttribute, final String operator, final Object valueAssertion, |
| | | final ResultHandler<Filter> h) { |
| | | if (jsonAttribute.size() == 1 && isIncludedAttribute(jsonAttribute.get(0))) { |
| | | h.handleResult(toFilter(c, type, jsonAttribute.get(0), valueAssertion)); |
| | | } else { |
| | | // This attribute mapper cannot handle the provided filter component. |
| | | h.handleResult(null); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Includes one or more LDAP attributes in this mapping. |
| | | * |
| | | * @param attributes |
| | |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void toJSON(final ServerContext c, final Entry e, |
| | | final ResultHandler<Map<String, Object>> h) { |
| | | public void toJSON(final Context c, final Entry e, final ResultHandler<Map<String, Object>> h) { |
| | | final Map<String, Object> result = new LinkedHashMap<String, Object>(e.getAttributeCount()); |
| | | for (final Attribute a : e.getAllAttributes()) { |
| | | final String name = getAttributeName(a); |
| | |
| | | h.handleResult(result); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void toLDAP(final ServerContext c, final JsonValue v, |
| | | final ResultHandler<List<Attribute>> h) { |
| | | public void toLDAP(final Context c, final JsonValue v, final ResultHandler<List<Attribute>> h) { |
| | | // TODO: |
| | | } |
| | | |
| | |
| | | * Header, with the fields enclosed by brackets [] replaced by your own identifying |
| | | * information: "Portions Copyright [year] [name of copyright owner]". |
| | | * |
| | | * Copyright 2012 ForgeRock AS. |
| | | * Copyright 2012-2013 ForgeRock AS. |
| | | */ |
| | | package org.forgerock.opendj.rest2ldap; |
| | | |
| | | import java.util.Collection; |
| | | |
| | | import org.forgerock.json.resource.Context; |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.ConnectionFactory; |
| | | import org.forgerock.opendj.ldap.DN; |
| | |
| | | // FIXME: make this configurable, also allow use of DN. |
| | | private static final String UUID_ATTRIBUTE = "entryUUID"; |
| | | |
| | | // TODO: support template variables. |
| | | private final DN baseDN; |
| | | |
| | | private final ConnectionFactory factory; |
| | |
| | | * |
| | | * @param context |
| | | * The request context. |
| | | * @param filter |
| | | * The LDAP search filter. |
| | | * @param attributes |
| | | * The list of LDAP attributes to be returned. |
| | | * @param handler |
| | | * The search result handler. |
| | | */ |
| | | public void listEntries(final Context context, final Collection<String> attributes, |
| | | final SearchResultHandler handler) { |
| | | public void listEntries(final Context context, final Filter filter, |
| | | final Collection<String> attributes, final SearchResultHandler handler) { |
| | | final String[] tmp = getSearchAttributes(attributes); |
| | | final ConnectionCompletionHandler<Result> outerHandler = new ConnectionCompletionHandler<Result>( |
| | | handler) { |
| | | final ConnectionCompletionHandler<Result> outerHandler = |
| | | new ConnectionCompletionHandler<Result>(handler) { |
| | | |
| | | @Override |
| | | public void handleResult(final Connection connection) { |
| | | final SearchRequestCompletionHandler innerHandler = new SearchRequestCompletionHandler( |
| | | connection, handler); |
| | | final SearchRequest request = Requests.newSearchRequest(baseDN, |
| | | SearchScope.SINGLE_LEVEL, Filter.objectClassPresent(), tmp); |
| | | connection.searchAsync(request, null, innerHandler); |
| | | } |
| | | @Override |
| | | public void handleResult(final Connection connection) { |
| | | final SearchRequestCompletionHandler innerHandler = |
| | | new SearchRequestCompletionHandler(connection, handler); |
| | | final SearchRequest request = |
| | | Requests.newSearchRequest(baseDN, SearchScope.SINGLE_LEVEL, filter, |
| | | tmp); |
| | | connection.searchAsync(request, null, innerHandler); |
| | | } |
| | | |
| | | }; |
| | | }; |
| | | |
| | | factory.getConnectionAsync(outerHandler); |
| | | } |
| | |
| | | final ConnectionCompletionHandler<SearchResultEntry> outerHandler = |
| | | new ConnectionCompletionHandler<SearchResultEntry>(handler) { |
| | | |
| | | @Override |
| | | public void handleResult(final Connection connection) { |
| | | final RequestCompletionHandler<SearchResultEntry> innerHandler = |
| | | new RequestCompletionHandler<SearchResultEntry>(connection, handler); |
| | | final SearchRequest request = Requests.newSearchRequest(baseDN, |
| | | SearchScope.SINGLE_LEVEL, Filter.equality(UUID_ATTRIBUTE, id), tmp); |
| | | connection.searchSingleEntryAsync(request, innerHandler); |
| | | } |
| | | @Override |
| | | public void handleResult(final Connection connection) { |
| | | final RequestCompletionHandler<SearchResultEntry> innerHandler = |
| | | new RequestCompletionHandler<SearchResultEntry>(connection, handler); |
| | | final SearchRequest request = |
| | | Requests.newSearchRequest(baseDN, SearchScope.SINGLE_LEVEL, Filter |
| | | .equality(UUID_ATTRIBUTE, id), tmp); |
| | | connection.searchSingleEntryAsync(request, innerHandler); |
| | | } |
| | | |
| | | }; |
| | | }; |
| | | // @Checkstyle:on |
| | | factory.getConnectionAsync(outerHandler); |
| | | } |
| New file |
| | |
| | | /* |
| | | * 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 2013 ForgeRock AS. |
| | | */ |
| | | package org.forgerock.opendj.rest2ldap; |
| | | |
| | | import org.forgerock.json.resource.QueryFilter; |
| | | |
| | | /** |
| | | * An enumeration of the commons REST query comparison filter types. |
| | | */ |
| | | public enum FilterType { |
| | | |
| | | /** |
| | | * Substring filter. |
| | | * |
| | | * @see QueryFilter#contains |
| | | */ |
| | | CONTAINS, |
| | | |
| | | /** |
| | | * Equality filter. |
| | | * |
| | | * @see QueryFilter#equalTo |
| | | */ |
| | | EQUAL_TO, |
| | | |
| | | /** |
| | | * Extended match filter. |
| | | * |
| | | * @see QueryFilter#comparisonFilter |
| | | */ |
| | | EXTENDED, |
| | | |
| | | /** |
| | | * Greater than ordering filter. |
| | | * |
| | | * @see QueryFilter#greaterThan |
| | | */ |
| | | GREATER_THAN, |
| | | |
| | | /** |
| | | * Greater than or equal to ordering filter. |
| | | * |
| | | * @see QueryFilter#greaterThanOrEqualTo |
| | | */ |
| | | GREATER_THAN_OR_EQUAL_TO, |
| | | |
| | | /** |
| | | * Less than ordering filter. |
| | | * |
| | | * @see QueryFilter#lessThan |
| | | */ |
| | | LESS_THAN, |
| | | |
| | | /** |
| | | * Less than or equal to ordering filter. |
| | | * |
| | | * @see QueryFilter#lessThanOrEqualTo |
| | | */ |
| | | LESS_THAN_OR_EQUAL_TO, |
| | | |
| | | /** |
| | | * Presence filter. |
| | | * |
| | | * @see QueryFilter#present |
| | | */ |
| | | PRESENT, |
| | | |
| | | /** |
| | | * Initial sub-string filter. |
| | | * |
| | | * @see QueryFilter#startsWith |
| | | */ |
| | | STARTS_WITH; |
| | | } |
| | |
| | | * Header, with the fields enclosed by brackets [] replaced by your own identifying |
| | | * information: "Portions Copyright [year] [name of copyright owner]". |
| | | * |
| | | * Copyright 2012 ForgeRock AS. |
| | | * Copyright 2012-2013 ForgeRock AS. |
| | | */ |
| | | package org.forgerock.opendj.rest2ldap; |
| | | |
| | | import static org.forgerock.opendj.rest2ldap.Utils.accumulate; |
| | | import static org.forgerock.opendj.rest2ldap.Utils.transform; |
| | | |
| | | import java.util.Collection; |
| | | import java.util.Iterator; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | import java.util.concurrent.atomic.AtomicBoolean; |
| | |
| | | import org.forgerock.json.resource.DeleteRequest; |
| | | import org.forgerock.json.resource.NotSupportedException; |
| | | import org.forgerock.json.resource.PatchRequest; |
| | | import org.forgerock.json.resource.QueryFilter; |
| | | import org.forgerock.json.resource.QueryFilterVisitor; |
| | | import org.forgerock.json.resource.QueryRequest; |
| | | import org.forgerock.json.resource.QueryResult; |
| | | import org.forgerock.json.resource.QueryResultHandler; |
| | |
| | | import org.forgerock.opendj.ldap.ConnectionException; |
| | | import org.forgerock.opendj.ldap.EntryNotFoundException; |
| | | import org.forgerock.opendj.ldap.ErrorResultException; |
| | | import org.forgerock.opendj.ldap.Filter; |
| | | import org.forgerock.opendj.ldap.Function; |
| | | import org.forgerock.opendj.ldap.MultipleEntriesFoundException; |
| | | import org.forgerock.opendj.ldap.SearchResultHandler; |
| | | import org.forgerock.opendj.ldap.TimeoutResultException; |
| | |
| | | // Dummy exception used for signalling search success. |
| | | private static final ResourceException SUCCESS = new UncategorizedException(0, null, null); |
| | | private final AttributeMapper attributeMapper; |
| | | |
| | | private final EntryContainer entryContainer; |
| | | private final Config config = new Config(); |
| | | |
| | | /** |
| | | * Creates a new LDAP resource. |
| | |
| | | @Override |
| | | public void queryCollection(final ServerContext context, final QueryRequest request, |
| | | final QueryResultHandler handler) { |
| | | final Set<JsonPointer> requestedAttributes = new LinkedHashSet<JsonPointer>(); |
| | | final Collection<String> requestedLDAPAttributes = getRequestedLDAPAttributes(requestedAttributes); |
| | | |
| | | // List the entries. |
| | | final Context c = wrap(context); |
| | | final SearchResultHandler searchHandler = new SearchResultHandler() { |
| | | private final AtomicInteger pendingResourceCount = new AtomicInteger(); |
| | | private final AtomicReference<ResourceException> pendingResult = new AtomicReference<ResourceException>(); |
| | | private final AtomicReference<ResourceException> pendingResult = |
| | | new AtomicReference<ResourceException>(); |
| | | private final AtomicBoolean resultSent = new AtomicBoolean(); |
| | | |
| | | @Override |
| | |
| | | // mapping? |
| | | final String id = entryContainer.getIDFromEntry(entry); |
| | | final String revision = entryContainer.getEtagFromEntry(entry); |
| | | final ResultHandler<Map<String, Object>> mapHandler = new ResultHandler<Map<String, Object>>() { |
| | | @Override |
| | | public void handleError(final ResourceException e) { |
| | | pendingResult.compareAndSet(null, e); |
| | | pendingResourceCount.decrementAndGet(); |
| | | completeIfNecessary(); |
| | | } |
| | | final ResultHandler<Map<String, Object>> mapHandler = |
| | | new ResultHandler<Map<String, Object>>() { |
| | | @Override |
| | | public void handleError(final ResourceException e) { |
| | | pendingResult.compareAndSet(null, e); |
| | | pendingResourceCount.decrementAndGet(); |
| | | completeIfNecessary(); |
| | | } |
| | | |
| | | @Override |
| | | public void handleResult(final Map<String, Object> result) { |
| | | final Resource resource = new Resource(id, revision, new JsonValue(result)); |
| | | handler.handleResource(resource); |
| | | pendingResourceCount.decrementAndGet(); |
| | | completeIfNecessary(); |
| | | } |
| | | }; |
| | | @Override |
| | | public void handleResult(final Map<String, Object> result) { |
| | | final Resource resource = |
| | | new Resource(id, revision, new JsonValue(result)); |
| | | handler.handleResource(resource); |
| | | pendingResourceCount.decrementAndGet(); |
| | | completeIfNecessary(); |
| | | } |
| | | }; |
| | | |
| | | pendingResourceCount.incrementAndGet(); |
| | | attributeMapper.toJSON(context, entry, mapHandler); |
| | | attributeMapper.toJSON(c, entry, mapHandler); |
| | | return true; |
| | | } |
| | | |
| | |
| | | } |
| | | } |
| | | }; |
| | | entryContainer.listEntries(context, requestedLDAPAttributes, searchHandler); |
| | | |
| | | final Collection<String> ldapAttributes = getLDAPAttributes(c, request.getFieldFilters()); |
| | | getLDAPFilter(c, request.getQueryFilter(), new ResultHandler<Filter>() { |
| | | @Override |
| | | public void handleError(final ResourceException error) { |
| | | handler.handleError(error); |
| | | } |
| | | |
| | | @Override |
| | | public void handleResult(final Filter ldapFilter) { |
| | | // Avoid performing a search if the filter could not be mapped or if it will never match. |
| | | if (ldapFilter == null || ldapFilter == c.getConfig().getFalseFilter()) { |
| | | handler.handleResult(new QueryResult()); |
| | | } else { |
| | | entryContainer.listEntries(c, ldapFilter, ldapAttributes, searchHandler); |
| | | } |
| | | } |
| | | }); |
| | | } |
| | | |
| | | /** |
| | |
| | | @Override |
| | | public void readInstance(final ServerContext context, final String resourceId, |
| | | final ReadRequest request, final ResultHandler<Resource> handler) { |
| | | // TODO: Determine the set of LDAP attributes that need to be read. |
| | | final Set<JsonPointer> requestedAttributes = new LinkedHashSet<JsonPointer>(); |
| | | final Collection<String> requestedLDAPAttributes = getRequestedLDAPAttributes(requestedAttributes); |
| | | |
| | | final Context c = wrap(context); |
| | | // @Checkstyle:off |
| | | final org.forgerock.opendj.ldap.ResultHandler<SearchResultEntry> searchHandler = |
| | | new org.forgerock.opendj.ldap.ResultHandler<SearchResultEntry>() { |
| | | @Override |
| | | public void handleErrorResult(final ErrorResultException error) { |
| | | handler.handleError(adaptErrorResult(error)); |
| | | } |
| | | |
| | | @Override |
| | | public void handleResult(final SearchResultEntry entry) { |
| | | final String revision = entryContainer.getEtagFromEntry(entry); |
| | | final ResultHandler<Map<String, Object>> mapHandler = new ResultHandler<Map<String, Object>>() { |
| | | @Override |
| | | public void handleError(final ResourceException e) { |
| | | handler.handleError(e); |
| | | public void handleErrorResult(final ErrorResultException error) { |
| | | handler.handleError(adaptErrorResult(error)); |
| | | } |
| | | |
| | | @Override |
| | | public void handleResult(final Map<String, Object> result) { |
| | | final Resource resource = new Resource(resourceId, revision, new JsonValue( |
| | | result)); |
| | | handler.handleResult(resource); |
| | | public void handleResult(final SearchResultEntry entry) { |
| | | final String revision = entryContainer.getEtagFromEntry(entry); |
| | | final ResultHandler<Map<String, Object>> mapHandler = |
| | | new ResultHandler<Map<String, Object>>() { |
| | | @Override |
| | | public void handleError(final ResourceException e) { |
| | | handler.handleError(e); |
| | | } |
| | | |
| | | @Override |
| | | public void handleResult(final Map<String, Object> result) { |
| | | final Resource resource = |
| | | new Resource(resourceId, revision, new JsonValue( |
| | | result)); |
| | | handler.handleResult(resource); |
| | | } |
| | | }; |
| | | attributeMapper.toJSON(c, entry, mapHandler); |
| | | } |
| | | }; |
| | | attributeMapper.toJSON(context, entry, mapHandler); |
| | | } |
| | | }; |
| | | // @Checkstyle:on |
| | | entryContainer.readEntry(context, resourceId, requestedLDAPAttributes, searchHandler); |
| | | final Collection<String> ldapAttributes = getLDAPAttributes(c, request.getFieldFilters()); |
| | | entryContainer.readEntry(c, resourceId, ldapAttributes, searchHandler); |
| | | } |
| | | |
| | | /** |
| | |
| | | |
| | | /** |
| | | * Determines the set of LDAP attributes to request in an LDAP read (search, |
| | | * post-read), based on the provided set of JSON pointers. |
| | | * post-read), based on the provided list of JSON pointers. |
| | | * |
| | | * @param requestedAttributes |
| | | * The set of resource attributes to be read. |
| | | * The list of resource attributes to be read. |
| | | * @return The set of LDAP attributes associated with the resource |
| | | * attributes. |
| | | */ |
| | | private Collection<String> getRequestedLDAPAttributes(final Set<JsonPointer> requestedAttributes) { |
| | | private Collection<String> getLDAPAttributes(final Context c, |
| | | final Collection<JsonPointer> requestedAttributes) { |
| | | final Set<String> requestedLDAPAttributes; |
| | | if (requestedAttributes.isEmpty()) { |
| | | // Full read. |
| | | requestedLDAPAttributes = new LinkedHashSet<String>(); |
| | | attributeMapper.getLDAPAttributes(new JsonPointer(), requestedLDAPAttributes); |
| | | attributeMapper.getLDAPAttributes(c, new JsonPointer(), requestedLDAPAttributes); |
| | | } else { |
| | | // Partial read. |
| | | requestedLDAPAttributes = new LinkedHashSet<String>(requestedAttributes.size()); |
| | | for (final JsonPointer requestedAttribute : requestedAttributes) { |
| | | attributeMapper.getLDAPAttributes(requestedAttribute, requestedLDAPAttributes); |
| | | attributeMapper.getLDAPAttributes(c, requestedAttribute, requestedLDAPAttributes); |
| | | } |
| | | } |
| | | return requestedLDAPAttributes; |
| | | } |
| | | |
| | | private void getLDAPFilter(final Context c, final QueryFilter queryFilter, |
| | | final ResultHandler<Filter> h) { |
| | | final QueryFilterVisitor<Void, ResultHandler<Filter>> visitor = |
| | | new QueryFilterVisitor<Void, ResultHandler<Filter>>() { |
| | | @Override |
| | | public Void visitAndFilter(final ResultHandler<Filter> p, |
| | | final List<QueryFilter> subFilters) { |
| | | final ResultHandler<Filter> handler = |
| | | accumulate(subFilters.size(), transform( |
| | | new Function<List<Filter>, Filter, Void>() { |
| | | @Override |
| | | public Filter apply(final List<Filter> value, |
| | | final Void p) { |
| | | // Check for unmapped filter components and optimize. |
| | | final Iterator<Filter> i = value.iterator(); |
| | | while (i.hasNext()) { |
| | | final Filter f = i.next(); |
| | | if (f == null) { |
| | | // Filter component did not match any attribute mappers. |
| | | return c.getConfig().getFalseFilter(); |
| | | } else if (f == c.getConfig().getFalseFilter()) { |
| | | return c.getConfig().getFalseFilter(); |
| | | } else if (f == c.getConfig().getTrueFilter()) { |
| | | i.remove(); |
| | | } |
| | | } |
| | | switch (value.size()) { |
| | | case 0: |
| | | return c.getConfig().getTrueFilter(); |
| | | case 1: |
| | | return value.get(0); |
| | | default: |
| | | return Filter.and(value); |
| | | } |
| | | } |
| | | }, p)); |
| | | for (final QueryFilter subFilter : subFilters) { |
| | | subFilter.accept(this, handler); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | | public Void visitBooleanLiteralFilter(final ResultHandler<Filter> p, |
| | | final boolean value) { |
| | | p.handleResult(value ? c.getConfig().getTrueFilter() : c.getConfig() |
| | | .getFalseFilter()); |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | | public Void visitContainsFilter(final ResultHandler<Filter> p, |
| | | final JsonPointer field, final Object valueAssertion) { |
| | | attributeMapper.getLDAPFilter(c, FilterType.CONTAINS, field, null, |
| | | valueAssertion, p); |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | | public Void visitEqualsFilter(final ResultHandler<Filter> p, |
| | | final JsonPointer field, final Object valueAssertion) { |
| | | attributeMapper.getLDAPFilter(c, FilterType.EQUAL_TO, field, null, |
| | | valueAssertion, p); |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | | public Void visitExtendedMatchFilter(final ResultHandler<Filter> p, |
| | | final JsonPointer field, final String operator, |
| | | final Object valueAssertion) { |
| | | attributeMapper.getLDAPFilter(c, FilterType.EXTENDED, field, operator, |
| | | valueAssertion, p); |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | | public Void visitGreaterThanFilter(final ResultHandler<Filter> p, |
| | | final JsonPointer field, final Object valueAssertion) { |
| | | attributeMapper.getLDAPFilter(c, FilterType.GREATER_THAN, field, null, |
| | | valueAssertion, p); |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | | public Void visitGreaterThanOrEqualToFilter(final ResultHandler<Filter> p, |
| | | final JsonPointer field, final Object valueAssertion) { |
| | | attributeMapper.getLDAPFilter(c, FilterType.GREATER_THAN_OR_EQUAL_TO, |
| | | field, null, valueAssertion, p); |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | | public Void visitLessThanFilter(final ResultHandler<Filter> p, |
| | | final JsonPointer field, final Object valueAssertion) { |
| | | attributeMapper.getLDAPFilter(c, FilterType.LESS_THAN, field, null, |
| | | valueAssertion, p); |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | | public Void visitLessThanOrEqualToFilter(final ResultHandler<Filter> p, |
| | | final JsonPointer field, final Object valueAssertion) { |
| | | attributeMapper.getLDAPFilter(c, FilterType.LESS_THAN_OR_EQUAL_TO, field, |
| | | null, valueAssertion, p); |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | | public Void visitNotFilter(final ResultHandler<Filter> p, |
| | | final QueryFilter subFilter) { |
| | | subFilter.accept(this, transform(new Function<Filter, Filter, Void>() { |
| | | @Override |
| | | public Filter apply(final Filter value, final Void p) { |
| | | if (value == null) { |
| | | // Filter component did not match any attribute mappers. |
| | | return c.getConfig().getTrueFilter(); |
| | | } else if (value == c.getConfig().getFalseFilter()) { |
| | | return c.getConfig().getTrueFilter(); |
| | | } else if (value == c.getConfig().getTrueFilter()) { |
| | | return c.getConfig().getFalseFilter(); |
| | | } else { |
| | | return Filter.not(value); |
| | | } |
| | | } |
| | | }, p)); |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | | public Void visitOrFilter(final ResultHandler<Filter> p, |
| | | final List<QueryFilter> subFilters) { |
| | | final ResultHandler<Filter> handler = |
| | | accumulate(subFilters.size(), transform( |
| | | new Function<List<Filter>, Filter, Void>() { |
| | | @Override |
| | | public Filter apply(final List<Filter> value, |
| | | final Void p) { |
| | | // Check for unmapped filter components and optimize. |
| | | final Iterator<Filter> i = value.iterator(); |
| | | while (i.hasNext()) { |
| | | final Filter f = i.next(); |
| | | if (f == null) { |
| | | // Filter component did not match any attribute mappers. |
| | | i.remove(); |
| | | } else if (f == c.getConfig().getFalseFilter()) { |
| | | i.remove(); |
| | | } else if (f == c.getConfig().getTrueFilter()) { |
| | | return c.getConfig().getTrueFilter(); |
| | | } |
| | | } |
| | | switch (value.size()) { |
| | | case 0: |
| | | return c.getConfig().getFalseFilter(); |
| | | case 1: |
| | | return value.get(0); |
| | | default: |
| | | return Filter.or(value); |
| | | } |
| | | } |
| | | }, p)); |
| | | for (final QueryFilter subFilter : subFilters) { |
| | | subFilter.accept(this, handler); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | | public Void visitPresentFilter(final ResultHandler<Filter> p, |
| | | final JsonPointer field) { |
| | | attributeMapper.getLDAPFilter(c, FilterType.PRESENT, field, null, null, p); |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | | public Void visitStartsWithFilter(final ResultHandler<Filter> p, |
| | | final JsonPointer field, final Object valueAssertion) { |
| | | attributeMapper.getLDAPFilter(c, FilterType.STARTS_WITH, field, null, |
| | | valueAssertion, p); |
| | | return null; |
| | | } |
| | | |
| | | }; |
| | | // Note that the returned LDAP filter may be null if it could not be mapped by any attribute mappers. |
| | | queryFilter.accept(visitor, h); |
| | | } |
| | | |
| | | private Context wrap(ServerContext context) { |
| | | return new Context(config, context); |
| | | } |
| | | } |
| | |
| | | * Header, with the fields enclosed by brackets [] replaced by your own identifying |
| | | * information: "Portions Copyright [year] [name of copyright owner]". |
| | | * |
| | | * Copyright 2012 ForgeRock AS. |
| | | * Copyright 2012-2013 ForgeRock AS. |
| | | */ |
| | | package org.forgerock.opendj.rest2ldap; |
| | | |
| | |
| | | import org.forgerock.json.fluent.JsonPointer; |
| | | import org.forgerock.json.fluent.JsonValue; |
| | | import org.forgerock.json.resource.ResultHandler; |
| | | import org.forgerock.json.resource.ServerContext; |
| | | import org.forgerock.opendj.ldap.Attribute; |
| | | import org.forgerock.opendj.ldap.Entry; |
| | | import org.forgerock.opendj.ldap.Filter; |
| | | |
| | | /** |
| | | * An attribute mapper which maps DN valued LDAP attributes to resource IDs. |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void getLDAPAttributes(final JsonPointer jsonAttribute, final Set<String> ldapAttributes) { |
| | | public void getLDAPAttributes(final Context c, final JsonPointer jsonAttribute, |
| | | final Set<String> ldapAttributes) { |
| | | // TODO Auto-generated method stub |
| | | |
| | | } |
| | | |
| | | @Override |
| | | public void getLDAPFilter(final Context c, final FilterType type, |
| | | final JsonPointer jsonAttribute, final String operator, final Object valueAssertion, |
| | | final ResultHandler<Filter> h) { |
| | | // TODO Auto-generated method stub |
| | | |
| | | } |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void toJSON(final ServerContext c, final Entry e, |
| | | final ResultHandler<Map<String, Object>> h) { |
| | | public void toJSON(final Context c, final Entry e, final ResultHandler<Map<String, Object>> h) { |
| | | // TODO Auto-generated method stub |
| | | |
| | | } |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void toLDAP(final ServerContext c, final JsonValue v, |
| | | final ResultHandler<List<Attribute>> h) { |
| | | public void toLDAP(final Context c, final JsonValue v, final ResultHandler<List<Attribute>> h) { |
| | | // TODO Auto-generated method stub |
| | | |
| | | } |
| | | |
| | | } |
| | |
| | | * Header, with the fields enclosed by brackets [] replaced by your own identifying |
| | | * information: "Portions Copyright [year] [name of copyright owner]". |
| | | * |
| | | * Copyright 2012 ForgeRock AS. |
| | | * Copyright 2012-2013 ForgeRock AS. |
| | | */ |
| | | package org.forgerock.opendj.rest2ldap; |
| | | |
| | | import static org.forgerock.opendj.rest2ldap.Utils.byteStringToJson; |
| | | import static org.forgerock.opendj.rest2ldap.Utils.toFilter; |
| | | import static org.forgerock.opendj.rest2ldap.Utils.toLowerCase; |
| | | |
| | | import java.util.Collections; |
| | |
| | | import org.forgerock.json.fluent.JsonPointer; |
| | | import org.forgerock.json.fluent.JsonValue; |
| | | import org.forgerock.json.resource.ResultHandler; |
| | | import org.forgerock.json.resource.ServerContext; |
| | | import org.forgerock.opendj.ldap.Attribute; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.Entry; |
| | | import org.forgerock.opendj.ldap.Filter; |
| | | import org.forgerock.opendj.ldap.Function; |
| | | import org.forgerock.opendj.ldap.Functions; |
| | | |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void getLDAPAttributes(final JsonPointer jsonAttribute, final Set<String> ldapAttributes) { |
| | | if (attributeMatchesPointer(jsonAttribute)) { |
| | | public void getLDAPAttributes(final Context c, final JsonPointer jsonAttribute, |
| | | final Set<String> ldapAttributes) { |
| | | if (jsonAttribute.isEmpty() || matches(jsonAttribute)) { |
| | | ldapAttributes.add(ldapAttributeName); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void getLDAPFilter(final Context c, final FilterType type, |
| | | final JsonPointer jsonAttribute, final String operator, final Object valueAssertion, |
| | | final ResultHandler<Filter> h) { |
| | | if (matches(jsonAttribute)) { |
| | | h.handleResult(toFilter(c, type, ldapAttributeName, valueAssertion)); |
| | | } else { |
| | | // This attribute mapper cannot handle the provided filter component. |
| | | h.handleResult(null); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Prevents the LDAP attribute from being updated. |
| | | * |
| | | * @param readOnly |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void toJSON(final ServerContext c, final Entry e, |
| | | final ResultHandler<Map<String, Object>> h) { |
| | | public void toJSON(final Context c, final Entry e, final ResultHandler<Map<String, Object>> h) { |
| | | final Attribute a = e.getAttribute(ldapAttributeName); |
| | | if (a != null) { |
| | | final Function<ByteString, ?, Void> f = decoder == null ? Functions.fixedFunction( |
| | | byteStringToJson(), a) : decoder; |
| | | final Function<ByteString, ?, Void> f = |
| | | decoder == null ? Functions.fixedFunction(byteStringToJson(), a) : decoder; |
| | | final Object value; |
| | | if (forceSingleValued || a.getAttributeDescription().getAttributeType().isSingleValue()) { |
| | | value = a.parse().as(f, defaultValue); |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void toLDAP(final ServerContext c, final JsonValue v, |
| | | final ResultHandler<List<Attribute>> h) { |
| | | public void toLDAP(final Context c, final JsonValue v, final ResultHandler<List<Attribute>> h) { |
| | | // TODO Auto-generated method stub |
| | | |
| | | } |
| | | |
| | | private boolean attributeMatchesPointer(final JsonPointer resourceAttribute) { |
| | | return resourceAttribute.isEmpty() |
| | | || toLowerCase(resourceAttribute.get(0)).equals(normalizedJsonAttributeName); |
| | | private boolean matches(final JsonPointer jsonAttribute) { |
| | | return !jsonAttribute.isEmpty() |
| | | && toLowerCase(jsonAttribute.get(0)).equals(normalizedJsonAttributeName); |
| | | } |
| | | |
| | | } |
| | |
| | | * Header, with the fields enclosed by brackets [] replaced by your own identifying |
| | | * information: "Portions Copyright [year] [name of copyright owner]". |
| | | * |
| | | * Copyright 2012 ForgeRock AS. |
| | | * Copyright 2012-2013 ForgeRock AS. |
| | | */ |
| | | package org.forgerock.opendj.rest2ldap; |
| | | |
| | |
| | | import org.forgerock.json.fluent.JsonPointer; |
| | | import org.forgerock.json.fluent.JsonValue; |
| | | import org.forgerock.json.resource.ResultHandler; |
| | | import org.forgerock.json.resource.ServerContext; |
| | | import org.forgerock.opendj.ldap.Attribute; |
| | | import org.forgerock.opendj.ldap.Entry; |
| | | import org.forgerock.opendj.ldap.Filter; |
| | | |
| | | /** |
| | | * An attribute mapper which inlines LDAP attributes from subordinate LDAP |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void getLDAPAttributes(final JsonPointer jsonAttribute, final Set<String> ldapAttributes) { |
| | | public void getLDAPAttributes(final Context c, final JsonPointer jsonAttribute, |
| | | final Set<String> ldapAttributes) { |
| | | // TODO Auto-generated method stub |
| | | |
| | | } |
| | | |
| | | @Override |
| | | public void getLDAPFilter(final Context c, final FilterType type, |
| | | final JsonPointer jsonAttribute, final String operator, final Object valueAssertion, |
| | | final ResultHandler<Filter> h) { |
| | | // TODO Auto-generated method stub |
| | | |
| | | } |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void toJSON(final ServerContext c, final Entry e, |
| | | final ResultHandler<Map<String, Object>> h) { |
| | | public void toJSON(final Context c, final Entry e, final ResultHandler<Map<String, Object>> h) { |
| | | // TODO Auto-generated method stub |
| | | |
| | | } |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void toLDAP(final ServerContext c, final JsonValue v, |
| | | final ResultHandler<List<Attribute>> h) { |
| | | public void toLDAP(final Context c, final JsonValue v, final ResultHandler<List<Attribute>> h) { |
| | | // TODO Auto-generated method stub |
| | | |
| | | } |
| | |
| | | * Header, with the fields enclosed by brackets [] replaced by your own identifying |
| | | * information: "Portions Copyright [year] [name of copyright owner]". |
| | | * |
| | | * Copyright 2012 ForgeRock AS. |
| | | * Copyright 2012-2013 ForgeRock AS. |
| | | */ |
| | | package org.forgerock.opendj.rest2ldap; |
| | | |
| | |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | import java.util.Locale; |
| | | import java.util.concurrent.atomic.AtomicInteger; |
| | | |
| | | import javax.xml.bind.DatatypeConverter; |
| | | |
| | | import org.forgerock.json.resource.ResourceException; |
| | | import org.forgerock.json.resource.ResultHandler; |
| | | import org.forgerock.opendj.ldap.Attribute; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.Filter; |
| | | import org.forgerock.opendj.ldap.Function; |
| | | import org.forgerock.opendj.ldap.Functions; |
| | | import org.forgerock.opendj.ldap.schema.Syntax; |
| | |
| | | * Internal utility methods. |
| | | */ |
| | | final class Utils { |
| | | /** |
| | | * Implementation class for {@link #accumulate}. |
| | | * |
| | | * @param <V> |
| | | * The type of result. |
| | | */ |
| | | private static final class AccumulatingResultHandler<V> implements ResultHandler<V> { |
| | | private final ResultHandler<List<V>> handler; |
| | | private final AtomicInteger latch; |
| | | private final List<V> results; |
| | | |
| | | private AccumulatingResultHandler(final int size, final ResultHandler<List<V>> handler) { |
| | | this.latch = new AtomicInteger(size); |
| | | this.results = new ArrayList<V>(size); |
| | | this.handler = handler; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void handleError(final ResourceException e) { |
| | | // Ensure that handler is only invoked once. |
| | | if (latch.getAndSet(0) > 0) { |
| | | handler.handleError(e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void handleResult(final V result) { |
| | | synchronized (results) { |
| | | results.add(result); |
| | | } |
| | | if (latch.decrementAndGet() == 0) { |
| | | handler.handleResult(results); |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | // @Checkstyle:off |
| | | private static final Function<ByteString, Object, Attribute> BYTESTRING_TO_JSON = |
| | | new Function<ByteString, Object, Attribute>() { |
| | | @Override |
| | | public Object apply(final ByteString value, final Attribute a) { |
| | | final Syntax syntax = a.getAttributeDescription().getAttributeType().getSyntax(); |
| | | if (syntax.equals(getBooleanSyntax())) { |
| | | return Functions.byteStringToBoolean().apply(value, null); |
| | | } else if (syntax.equals(getIntegerSyntax())) { |
| | | return Functions.byteStringToLong().apply(value, null); |
| | | } else if (syntax.equals(getGeneralizedTimeSyntax())) { |
| | | return DatatypeConverter.printDateTime(Functions.byteStringToGeneralizedTime() |
| | | .apply(value, null).toCalendar()); |
| | | } else if (syntax.isHumanReadable()) { |
| | | return Functions.byteStringToString().apply(value, null); |
| | | } else { |
| | | // Base 64 encoded binary. |
| | | return DatatypeConverter.printBase64Binary(value.toByteArray()); |
| | | } |
| | | } |
| | | }; |
| | | // @Checkstyle:on |
| | | @Override |
| | | public Object apply(final ByteString value, final Attribute a) { |
| | | final Syntax syntax = |
| | | a.getAttributeDescription().getAttributeType().getSyntax(); |
| | | if (syntax.equals(getBooleanSyntax())) { |
| | | return Functions.byteStringToBoolean().apply(value, null); |
| | | } else if (syntax.equals(getIntegerSyntax())) { |
| | | return Functions.byteStringToLong().apply(value, null); |
| | | } else if (syntax.equals(getGeneralizedTimeSyntax())) { |
| | | return DatatypeConverter.printDateTime(Functions |
| | | .byteStringToGeneralizedTime().apply(value, null).toCalendar()); |
| | | } else if (syntax.isHumanReadable()) { |
| | | return Functions.byteStringToString().apply(value, null); |
| | | } else { |
| | | // Base 64 encoded binary. |
| | | return DatatypeConverter.printBase64Binary(value.toByteArray()); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | static <V> ResultHandler<V> accumulate(final int size, final ResultHandler<List<V>> handler) { |
| | | return new AccumulatingResultHandler<V>(size, handler); |
| | | } |
| | | |
| | | static Object attributeToJson(final Attribute a) { |
| | | final Function<ByteString, Object, Void> f = Functions.fixedFunction(BYTESTRING_TO_JSON, a); |
| | | final boolean isSingleValued = a.getAttributeDescription().getAttributeType() |
| | | .isSingleValue(); |
| | | final boolean isSingleValued = |
| | | a.getAttributeDescription().getAttributeType().isSingleValue(); |
| | | return isSingleValued ? a.parse().as(f) : asList(a.parse().asSetOf(f)); |
| | | } |
| | | |
| | | // @Checkstyle:on |
| | | |
| | | static Function<ByteString, Object, Attribute> byteStringToJson() { |
| | | return BYTESTRING_TO_JSON; |
| | | } |
| | |
| | | return a.getAttributeDescription().withoutOption("binary").toString(); |
| | | } |
| | | |
| | | static Filter toFilter(final Context c, final FilterType type, final String ldapAttribute, |
| | | final Object valueAssertion) { |
| | | final String v = String.valueOf(valueAssertion); |
| | | final Filter filter; |
| | | switch (type) { |
| | | case CONTAINS: |
| | | filter = Filter.substrings(ldapAttribute, null, Collections.singleton(v), null); |
| | | break; |
| | | case STARTS_WITH: |
| | | filter = Filter.substrings(ldapAttribute, v, null, null); |
| | | break; |
| | | case EQUAL_TO: |
| | | filter = Filter.equality(ldapAttribute, v); |
| | | break; |
| | | case GREATER_THAN: |
| | | filter = Filter.greaterThan(ldapAttribute, v); |
| | | break; |
| | | case GREATER_THAN_OR_EQUAL_TO: |
| | | filter = Filter.greaterOrEqual(ldapAttribute, v); |
| | | break; |
| | | case LESS_THAN: |
| | | filter = Filter.lessThan(ldapAttribute, v); |
| | | break; |
| | | case LESS_THAN_OR_EQUAL_TO: |
| | | filter = Filter.lessOrEqual(ldapAttribute, v); |
| | | break; |
| | | case PRESENT: |
| | | filter = Filter.present(ldapAttribute); |
| | | break; |
| | | case EXTENDED: |
| | | default: |
| | | filter = c.getConfig().getFalseFilter(); // Not supported. |
| | | break; |
| | | } |
| | | return filter; |
| | | } |
| | | |
| | | static String toLowerCase(final String s) { |
| | | return s != null ? s.toLowerCase(Locale.ENGLISH) : null; |
| | | } |
| | | |
| | | static <M, N> ResultHandler<M> transform(final Function<M, N, Void> f, |
| | | final ResultHandler<N> handler) { |
| | | return new ResultHandler<M>() { |
| | | @Override |
| | | public void handleError(final ResourceException error) { |
| | | handler.handleError(error); |
| | | } |
| | | |
| | | @Override |
| | | public void handleResult(final M result) { |
| | | handler.handleResult(f.apply(result, null)); |
| | | } |
| | | }; |
| | | } |
| | | |
| | | private static <T> List<T> asList(final Collection<T> c) { |
| | | if (c instanceof List) { |
| | | return (List<T>) c; |
| | |
| | | * 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 Copyrighted [year] [name of copyright owner]". |
| | | * information: "Portions Copyright [year] [name of copyright owner]". |
| | | * |
| | | * Copyright © 2012 ForgeRock AS. All rights reserved. |
| | | * Copyright 2012-2013 ForgeRock AS. |
| | | */ |
| | | |
| | | package org.forgerock.opendj.rest2ldap; |
| | |
| | | import java.util.logging.Logger; |
| | | |
| | | import org.forgerock.json.resource.Router; |
| | | import org.forgerock.json.resource.RoutingMode; |
| | | import org.forgerock.json.resource.servlet.HttpServlet; |
| | | import org.forgerock.opendj.ldap.ConnectionFactory; |
| | | import org.forgerock.opendj.ldap.DN; |
| | |
| | | |
| | | // Create the router. |
| | | final Router router = new Router(); |
| | | router.addRoute(RoutingMode.EQUALS, "/users", userResource); |
| | | router.addRoute(RoutingMode.EQUALS, "/groups", groupResource); |
| | | router.addRoute("/users", userResource); |
| | | router.addRoute("/groups", groupResource); |
| | | |
| | | final org.forgerock.json.resource.ConnectionFactory resourceFactory = newInternalConnectionFactory(router); |
| | | final HttpServer httpServer = HttpServer.createSimpleServer("./", PORT); |