mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Matthew Swift
23.06.2012 9cb4730adfb2ad5fcf171c48a4d0aee46777902e
* simplify example code and add groups container
* allow AttributeMappers to throw ResourceExceptions instead of LDAP ErrorResultExceptions
* rename IdentityAttributeMapper -> SimpleAttributeMapper.
1 files added
1 files renamed
4 files modified
276 ■■■■■ changed files
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/AttributeMapper.java 41 ●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/AttributeMapperCompletionHandler.java 29 ●●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/CompositeAttributeMapper.java 30 ●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Example.java 129 ●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPResource.java 22 ●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SimpleAttributeMapper.java 25 ●●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/AttributeMapper.java
@@ -16,7 +16,6 @@
package org.forgerock.opendj.rest2ldap;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -25,7 +24,6 @@
import org.forgerock.json.fluent.JsonValue;
import org.forgerock.opendj.ldap.Attribute;
import org.forgerock.opendj.ldap.Entry;
import org.forgerock.opendj.ldap.ResultHandler;
import org.forgerock.resource.provider.Context;
/**
@@ -34,30 +32,33 @@
public interface AttributeMapper {
    /**
     * Returns an unmodifiable collection containing the names of the LDAP
     * attributes required by this attribute mapper. The returned collection
     * should only contain the names of attributes found in the LDAP entry
     * directly associated with the resource.
     *
     * @return An unmodifiable collection containing the names of the LDAP
     *         attributes required by this attribute mapper.
     */
    Collection<String> getAllLDAPAttributes();
    /**
     * Adds the names of the LDAP attributes required by this attribute mapper
     * which are associated with the provided resource attribute.
     * Adds the names of the all the LDAP attributes required by this attribute
     * mapper to the provided set.
     * <p>
     * Implementations should only add the names of attributes found in the LDAP
     * entry directly associated with the resource.
     *
     * @param resourceAttribute
     *            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 getLDAPAttributesFor(JsonPointer resourceAttribute, Set<String> ldapAttributes);
    void getLDAPAttributes(Set<String> ldapAttributes);
    /**
     * Adds the names of the LDAP attributes required by this attribute mapper
     * which are associated with the provided resource attribute to the provided
     * set.
     * <p>
     * Implementations should only add the names of attributes found in the LDAP
     * entry directly associated with the resource.
     *
     * @param ldapAttributes
     *            The set into which the required LDAP attribute names should be
     *            put.
     * @param resourceAttribute
     *            The name of the resource attribute requested by the client.
     */
    void getLDAPAttributes(Set<String> ldapAttributes, JsonPointer resourceAttribute);
    /**
     * Transforms attributes contained in the provided LDAP entry to JSON
@@ -72,7 +73,7 @@
     * @param e
     * @param h
     */
    void toJson(Context c, Entry e, ResultHandler<Map<String, Object>> h);
    void toJson(Context c, Entry e, AttributeMapperCompletionHandler<Map<String, Object>> h);
    /**
     * Transforms JSON content in the provided JSON value to LDAP attributes,
@@ -86,7 +87,7 @@
     * @param v
     * @param h
     */
    void toLDAP(Context c, JsonValue v, ResultHandler<List<Attribute>> h);
    void toLDAP(Context c, JsonValue v, AttributeMapperCompletionHandler<List<Attribute>> h);
    // TODO: methods for obtaining schema information (e.g. name, description,
    // type information).
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/AttributeMapperCompletionHandler.java
New file
@@ -0,0 +1,29 @@
/*
 * 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 Copyrighted [year] [name of copyright owner]".
 *
 * Copyright 2012 ForgeRock AS. All rights reserved.
 */
package org.forgerock.opendj.rest2ldap;
import org.forgerock.resource.exception.ResourceException;
/**
 *
 */
public interface AttributeMapperCompletionHandler<T> {
    void onSuccess(T result);
    void onFailure(ResourceException e);
}
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/CompositeAttributeMapper.java
@@ -29,8 +29,7 @@
import org.forgerock.json.fluent.JsonValue;
import org.forgerock.opendj.ldap.Attribute;
import org.forgerock.opendj.ldap.Entry;
import org.forgerock.opendj.ldap.ErrorResultException;
import org.forgerock.opendj.ldap.ResultHandler;
import org.forgerock.resource.exception.ResourceException;
import org.forgerock.resource.provider.Context;
/**
@@ -50,7 +49,7 @@
        this.attributeMappers = new ArrayList<AttributeMapper>(attributeMappers);
        Set<String> tmp = new LinkedHashSet<String>(attributeMappers.size());
        for (final AttributeMapper mapper : attributeMappers) {
            tmp.addAll(mapper.getAllLDAPAttributes());
            mapper.getLDAPAttributes(tmp);
        }
        allLDAPAttributes = Collections.unmodifiableSet(tmp);
    }
@@ -58,37 +57,38 @@
    /**
     * {@inheritDoc}
     */
    public Set<String> getAllLDAPAttributes() {
        return allLDAPAttributes;
    public void getLDAPAttributes(Set<String> ldapAttributes) {
        ldapAttributes.addAll(allLDAPAttributes);
    }
    /**
     * {@inheritDoc}
     */
    public void getLDAPAttributesFor(JsonPointer resourceAttribute, Set<String> ldapAttributes) {
    public void getLDAPAttributes(Set<String> ldapAttributes, JsonPointer resourceAttribute) {
        for (AttributeMapper attribute : attributeMappers) {
            attribute.getLDAPAttributesFor(resourceAttribute, ldapAttributes);
            attribute.getLDAPAttributes(ldapAttributes, resourceAttribute);
        }
    }
    /**
     * {@inheritDoc}
     */
    public void toJson(Context c, Entry e, final ResultHandler<Map<String, Object>> h) {
        ResultHandler<Map<String, Object>> resultAccumulater =
                new ResultHandler<Map<String, Object>>() {
    public void toJson(Context c, Entry e,
            final AttributeMapperCompletionHandler<Map<String, Object>> h) {
        AttributeMapperCompletionHandler<Map<String, Object>> resultAccumulater =
                new AttributeMapperCompletionHandler<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 handleErrorResult(ErrorResultException error) {
                    public void onFailure(ResourceException e) {
                        // Ensure that handler is only invoked once.
                        if (latch.getAndSet(0) > 0) {
                            h.handleErrorResult(error);
                            h.onFailure(e);
                        }
                    }
                    public void handleResult(Map<String, Object> result) {
                    public void onSuccess(Map<String, Object> result) {
                        synchronized (this) {
                            results.add(result);
                        }
@@ -106,7 +106,7 @@
                                mergeJsonValues(results, mergeResult);
                                break;
                            }
                            h.handleResult(mergeResult);
                            h.onSuccess(mergeResult);
                        }
                    }
                };
@@ -119,7 +119,7 @@
    /**
     * {@inheritDoc}
     */
    public void toLDAP(Context c, JsonValue v, ResultHandler<List<Attribute>> h) {
    public void toLDAP(Context c, JsonValue v, AttributeMapperCompletionHandler<List<Attribute>> h) {
        // TODO Auto-generated method stub
    }
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Example.java
@@ -16,10 +16,6 @@
package org.forgerock.opendj.rest2ldap;
import java.util.HashMap;
import java.util.Map;
import org.forgerock.json.fluent.JsonValue;
import org.forgerock.json.resource.restlet.JsonResourceRestlet;
import org.forgerock.opendj.ldap.ConnectionFactory;
import org.forgerock.opendj.ldap.Connections;
@@ -30,7 +26,6 @@
import org.forgerock.resource.framework.impl.ResourceInvoker;
import org.restlet.Application;
import org.restlet.Component;
import org.restlet.Request;
import org.restlet.Restlet;
import org.restlet.data.Protocol;
import org.restlet.routing.Router;
@@ -41,115 +36,53 @@
 */
public class Example {
    private static final String PATH_PROPERTY = "rest2ldap.restlet.path";
    private Application application = new Application();
    private final Router router = new Router();
    private HashMap<JsonResourceProvider, Restlet> restlets =
            new HashMap<JsonResourceProvider, Restlet>();
    protected synchronized void bindRestlet(Restlet restlet, Map<String, Object> properties) {
        Object path = properties.get(PATH_PROPERTY);
        if (path != null && path instanceof String) { // service is specified as
                                                      // internally routable
            attach((String) path, restlet);
        }
    }
    protected synchronized void unbindRestlet(Restlet restlet, Map<String, Object> properties) {
        Object path = properties.get(PATH_PROPERTY);
        if (path != null && path instanceof String) { // service is specified as
                                                      // internally routable
            detach(restlet);
        }
    }
    /**
     * Attaches a target Restlet to the Restlet router based on a given URI
     * prefix.
     *
     * @param path
     *            the path to attach it to.
     * @param restlet
     *            the restlet to route to if path matches.
     * @throws IllegalArgumentException
     *             if path does not begin with a '/' character.
     */
    public void attach(String path, Restlet restlet) {
    private void bindJsonResource(JsonResourceProvider resource, String path) {
        Restlet restlet = new JsonResourceRestlet(resource);
        restlet.setContext(application.getContext());
        router.attach(path, restlet, Template.MODE_EQUALS);
        router.attach(path + (path.equals("/") ? "" : "/"), restlet, Template.MODE_STARTS_WITH);
    }
    /**
     * Remove a restlet from restlet router
     *
     * @param restlet
     *            the restlet to remove
     */
    public void detach(Restlet restlet) {
        router.detach(restlet); // all routes to restlet are removed
    }
    protected synchronized void bindJsonResource(JsonResourceProvider resource,
            Map<String, Object> properties) {
        Restlet restlet = new CustomRestlet(resource);
        restlets.put(resource, restlet);
        bindRestlet(restlet, properties);
    }
    protected synchronized void unbindJsonResource(JsonResourceProvider resource,
            Map<String, Object> properties) {
        Restlet restlet = restlets.get(resource);
        if (restlet != null) {
            unbindRestlet(restlet, properties);
            restlets.remove(resource);
        }
    }
    private class CustomRestlet extends JsonResourceRestlet {
        public CustomRestlet(JsonResourceProvider resource) {
            super(resource);
        }
        @Override
        public JsonValue newContext(Request request) {
            JsonValue result = super.newContext(request);
            JsonValue security = result.get("security");
            security.put("openidm-roles", request.getAttributes().get("openidm.roles"));
            return result;
        }
    }
    public void start() throws Exception {
        Component component = new Component();
        // Add http listener
        component.getServers().add(Protocol.HTTP, 8080);
        application.getTunnelService().setQueryTunnel(false); // query string
                                                              // purism
        application.setInboundRoot(router);
        // Attach the json resource at the root path
        Map<String, Object> props = new HashMap<String, Object>();
        props.put(PATH_PROPERTY, "");
        AttributeMapper mapper = new IdentityAttributeMapper().excludeAttribute("entryUUID", "etag");
        // All LDAP resources will use this connection factory.
        ConnectionFactory factory =
                Connections.newAuthenticatedConnectionFactory(new LDAPConnectionFactory(
                        "localhost", 1389), Requests.newSimpleBindRequest("cn=directory manager",
                        "password".toCharArray()));
        EntryContainer container =
        // Create two entry containers whose members reference each other.
        EntryContainer userContainer =
                new EntryContainer(DN.valueOf("ou=people,dc=example,dc=com"), factory);
        LDAPResource resource = new LDAPResource(container, mapper);
        ResourceInvoker invoker = new ResourceInvoker();
        invoker.resource = resource;
        bindJsonResource(invoker, props);
        EntryContainer groupContainer =
                new EntryContainer(DN.valueOf("ou=groups,dc=example,dc=com"), factory);
        // Attach the sample application.
        component.getDefaultHost().attach("/example", application);
        // Create user resource.
        AttributeMapper userMapper =
                new SimpleAttributeMapper().includeAttribute("uid", "cn", "sn", "mail",
                        "isMemberOf", "modifyTimestamp");
        LDAPResource userResource = new LDAPResource(userContainer, userMapper);
        ResourceInvoker userResourceInvoker = new ResourceInvoker();
        userResourceInvoker.resource = userResource; // FIXME: Yuk!
        bindJsonResource(userResourceInvoker, "/users");
        // Start the component.
        // Create group resource.
        AttributeMapper groupMapper =
                new SimpleAttributeMapper().includeAttribute("cn", "ou", "description",
                        "uniquemember");
        LDAPResource groupResource = new LDAPResource(groupContainer, groupMapper);
        ResourceInvoker groupResourceInvoker = new ResourceInvoker();
        groupResourceInvoker.resource = groupResource; // FIXME: Yuk!
        bindJsonResource(groupResourceInvoker, "/groups");
        // Configure and start the application.
        application.getTunnelService().setQueryTunnel(false);
        application.setInboundRoot(router);
        Component component = new Component();
        component.getServers().add(Protocol.HTTP, 8080);
        component.getDefaultHost().attach("", application);
        component.start();
    }
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPResource.java
@@ -160,14 +160,13 @@
                        public void handleResult(final SearchResultEntry entry) {
                            final String revision = entryContainer.getEtagFromEntry(entry);
                            final ResultHandler<Map<String, Object>> mapHandler =
                                    new ResultHandler<Map<String, Object>>() {
                                        public void handleErrorResult(
                                                final ErrorResultException error) {
                                            out.setFailure(adaptErrorResult(error));
                            final AttributeMapperCompletionHandler<Map<String, Object>> mapHandler =
                                    new AttributeMapperCompletionHandler<Map<String, Object>>() {
                                        public void onFailure(final ResourceException e) {
                                            out.setFailure(e);
                                        }
                                        public void handleResult(final Map<String, Object> result) {
                                        public void onSuccess(final Map<String, Object> result) {
                                            out.setResult(id, revision, new JsonValue(result));
                                        }
                                    };
@@ -227,18 +226,19 @@
     *         attributes.
     */
    private Collection<String> getRequestedLDAPAttributes(final Set<JsonPointer> requestedAttributes) {
        final Set<String> requestedLDAPAttributes;
        if (requestedAttributes.isEmpty()) {
            // Full read.
            return attributeMapper.getAllLDAPAttributes();
            requestedLDAPAttributes = new LinkedHashSet<String>();
            attributeMapper.getLDAPAttributes(requestedLDAPAttributes);
        } else {
            // Partial read.
            final Set<String> requestedLDAPAttributes =
                    new LinkedHashSet<String>(requestedAttributes.size());
            requestedLDAPAttributes = new LinkedHashSet<String>(requestedAttributes.size());
            for (final JsonPointer requestedAttribute : requestedAttributes) {
                attributeMapper.getLDAPAttributesFor(requestedAttribute, requestedLDAPAttributes);
                attributeMapper.getLDAPAttributes(requestedLDAPAttributes, requestedAttribute);
            }
            }
            return requestedLDAPAttributes;
        }
    }
}
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SimpleAttributeMapper.java
File was renamed from opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/IdentityAttributeMapper.java
@@ -19,8 +19,6 @@
import static org.forgerock.opendj.rest2ldap.Utils.getAttributeName;
import static org.forgerock.opendj.rest2ldap.Utils.toLowerCase;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -30,46 +28,45 @@
import org.forgerock.json.fluent.JsonValue;
import org.forgerock.opendj.ldap.Attribute;
import org.forgerock.opendj.ldap.Entry;
import org.forgerock.opendj.ldap.ResultHandler;
import org.forgerock.resource.provider.Context;
/**
 *
 */
public final class IdentityAttributeMapper implements AttributeMapper {
public final class SimpleAttributeMapper implements AttributeMapper {
    // All user attributes by default.
    private final Map<String, String> includedAttributes = new LinkedHashMap<String, String>();
    private final Map<String, String> excludedAttributes = new LinkedHashMap<String, String>();
    public IdentityAttributeMapper() {
    public SimpleAttributeMapper() {
        // No implementation required.
    }
    public IdentityAttributeMapper includeAttribute(String... attributes) {
    public SimpleAttributeMapper includeAttribute(String... attributes) {
        for (String attribute : attributes) {
            includedAttributes.put(toLowerCase(attribute), attribute);
        }
        return this;
    }
    public IdentityAttributeMapper excludeAttribute(String... attributes) {
    public SimpleAttributeMapper excludeAttribute(String... attributes) {
        for (String attribute : attributes) {
            excludedAttributes.put(toLowerCase(attribute), attribute);
        }
        return this;
    }
    public Collection<String> getAllLDAPAttributes() {
    public void getLDAPAttributes(Set<String> ldapAttributes) {
        if (!includedAttributes.isEmpty()) {
            return includedAttributes.values();
            ldapAttributes.addAll(includedAttributes.values());
        } else {
            // All user attributes.
            return Collections.singleton("*");
            ldapAttributes.add("*");
        }
    }
    public void getLDAPAttributesFor(JsonPointer resourceAttribute, Set<String> ldapAttributes) {
    public void getLDAPAttributes(Set<String> ldapAttributes, JsonPointer resourceAttribute) {
        String name = resourceAttribute.leaf();
        if (name != null) {
            if (isIncludedAttribute(name)) {
@@ -80,7 +77,7 @@
        }
    }
    public void toJson(Context c, Entry e, ResultHandler<Map<String, Object>> h) {
    public void toJson(Context c, Entry e, AttributeMapperCompletionHandler<Map<String, Object>> h) {
        Map<String, Object> result = new LinkedHashMap<String, Object>(e.getAttributeCount());
        for (Attribute a : e.getAllAttributes()) {
            String name = getAttributeName(a);
@@ -88,7 +85,7 @@
                result.put(name, Utils.attributeToJson(a));
            }
        }
        h.handleResult(result);
        h.onSuccess(result);
    }
    private boolean isIncludedAttribute(String name) {
@@ -107,7 +104,7 @@
        return false;
    }
    public void toLDAP(Context c, JsonValue v, ResultHandler<List<Attribute>> h) {
    public void toLDAP(Context c, JsonValue v, AttributeMapperCompletionHandler<List<Attribute>> h) {
        // TODO:
    }
}