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

Matthew Swift
22.28.2012 b362d9da285a8dc1b86db29b945feb3bc245b03b
Checkpoint basic GET functionality, including example gateway for testing.

curl -X GET http://localhost:8080/example
curl -X GET http://localhost:8080/example/8029a8f1-fe4c-3e5c-a3b5-78a65825f127
2 files deleted
1 files added
5 files modified
481 ■■■■ changed files
opendj3/opendj-rest2ldap/pom.xml 41 ●●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/EntryContainer.java 167 ●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/EtagStrategy.java 26 ●●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Example.java 160 ●●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/IdentityAttributeMapper.java 3 ●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPResource.java 21 ●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Utils.java 22 ●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Validator.java 41 ●●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/pom.xml
@@ -24,7 +24,8 @@
 !      Copyright 2012 ForgeRock AS
 !    
 -->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <artifactId>opendj-project</artifactId>
@@ -37,6 +38,13 @@
    This module includes APIs for implementing REST to LDAP gateways.
  </description>
  <packaging>jar</packaging>
    <repositories>
        <repository>
            <id>maven-restlet</id>
            <name>Public online Restlet repository</name>
            <url>http://maven.restlet.org</url>
        </repository>
    </repositories>
  <dependencies>
    <dependency>
      <groupId>org.forgerock.opendj</groupId>
@@ -57,11 +65,42 @@
        <scope>compile</scope>
    </dependency>
    <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
        <groupId>org.forgerock.commons</groupId>
        <artifactId>org.forgerock.json.resource</artifactId>
        <version>1.3.0-SNAPSHOT</version>
        <scope>compile</scope>
    </dependency>
        <dependency>
            <groupId>org.forgerock.commons</groupId>
            <artifactId>org.forgerock.json.resource.restlet</artifactId>
            <version>1.3.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.forgerock.commons</groupId>
            <artifactId>org.forgerock.restlet</artifactId>
            <version>1.1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.forgerock.commons</groupId>
            <artifactId>json-patch</artifactId>
            <version>1.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.restlet.jse</groupId>
            <artifactId>org.restlet</artifactId>
            <version>2.0.9</version>
        </dependency>
        <dependency>
            <groupId>org.restlet.jse</groupId>
            <artifactId>org.restlet.ext.jackson</artifactId>
            <version>2.0.9</version>
        </dependency>
  </dependencies>
  <build>
    <plugins>
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/EntryContainer.java
@@ -18,50 +18,167 @@
import java.util.Collection;
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.ConnectionFactory;
import org.forgerock.opendj.ldap.DN;
import org.forgerock.opendj.ldap.Entry;
import org.forgerock.opendj.ldap.ErrorResultException;
import org.forgerock.opendj.ldap.Filter;
import org.forgerock.opendj.ldap.ResultHandler;
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;
import org.forgerock.resource.provider.Context;
/**
 *
 */
public final class EntryContainer {
    // FIXME: make this configurable, also allow use of DN.
    private static final String UUID_ATTRIBUTE = "entryUUID";
    /**
     * @param entry
     * @return
     */
    public String getIDFromEntry(final SearchResultEntry entry) {
        // TODO Auto-generated method stub
        return null;
    // FIXME: make this configurable.
    private static final String ETAG_ATTRIBUTE = "etag";
    private final ConnectionFactory factory;
    private final DN baseDN;
    private abstract class AbstractRequestCompletionHandler<R, H extends ResultHandler<? super R>>
            implements ResultHandler<R> {
        final H resultHandler;
        final Connection connection;
        AbstractRequestCompletionHandler(final Connection connection, final H resultHandler) {
            this.connection = connection;
            this.resultHandler = resultHandler;
        }
        @Override
        public final void handleErrorResult(final ErrorResultException error) {
            connection.close();
            resultHandler.handleErrorResult(error);
        }
        @Override
        public final void handleResult(final R result) {
            connection.close();
            resultHandler.handleResult(result);
        }
    }
    private abstract class ConnectionCompletionHandler<R> implements ResultHandler<Connection> {
        private final ResultHandler<? super R> resultHandler;
        ConnectionCompletionHandler(final ResultHandler<? super R> resultHandler) {
            this.resultHandler = resultHandler;
        }
        @Override
        public final void handleErrorResult(final ErrorResultException error) {
            resultHandler.handleErrorResult(error);
        }
        @Override
        public abstract void handleResult(Connection connection);
    }
    private final class RequestCompletionHandler<R> extends
            AbstractRequestCompletionHandler<R, ResultHandler<? super R>> {
        RequestCompletionHandler(final Connection connection,
                final ResultHandler<? super R> resultHandler) {
            super(connection, resultHandler);
        }
    }
    private final class SearchRequestCompletionHandler extends
            AbstractRequestCompletionHandler<Result, SearchResultHandler> implements
            SearchResultHandler {
        SearchRequestCompletionHandler(final Connection connection,
                final SearchResultHandler resultHandler) {
            super(connection, resultHandler);
    }
    /**
     * @param context
     * @param handler
         * {@inheritDoc}
     */
        @Override
        public final boolean handleEntry(final SearchResultEntry entry) {
            return resultHandler.handleEntry(entry);
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public final boolean handleReference(final SearchResultReference reference) {
            return resultHandler.handleReference(reference);
        }
    }
    public EntryContainer(DN baseDN, ConnectionFactory factory) {
        this.baseDN = baseDN;
        this.factory = factory;
    }
    public void listEntries(final Context context, final SearchResultHandler handler) {
        // TODO Auto-generated method stub
        final ConnectionCompletionHandler<Result> outerHandler =
                new ConnectionCompletionHandler<Result>(handler) {
                    @Override
                    public void handleResult(final Connection connection) {
                        final SearchRequestCompletionHandler innerHandler =
                                new SearchRequestCompletionHandler(connection, handler);
                        SearchRequest request =
                                Requests.newSearchRequest(baseDN, SearchScope.SINGLE_LEVEL, Filter
                                        .objectClassPresent(), UUID_ATTRIBUTE, ETAG_ATTRIBUTE);
                        connection.searchAsync(request, null, innerHandler);
    }
    /**
     * Reads the entry having the specified resource ID.
     *
     * @param c
     *            The request context.
     * @param id
     *            The resource ID.
     * @param attributes
     *            The set of LDAP attributes to be read.
     * @param h
     *            The result handler.
     */
    public void readEntry(final Context c, final String id, final Collection<String> attributes,
            final ResultHandler<SearchResultEntry> h) {
        // TODO Auto-generated method stub
                };
        factory.getConnectionAsync(outerHandler);
    }
    public void readEntry(final Context c, final String id, final Collection<String> attributes,
            final ResultHandler<SearchResultEntry> handler) {
        final ConnectionCompletionHandler<SearchResultEntry> outerHandler =
                new ConnectionCompletionHandler<SearchResultEntry>(handler) {
                    @Override
                    public void handleResult(final Connection connection) {
                        final RequestCompletionHandler<SearchResultEntry> innerHandler =
                                new RequestCompletionHandler<SearchResultEntry>(connection, handler);
                        // FIXME: who is responsible for adding the UUID and
                        // etag attributes to this search?
                        String[] tmp = attributes.toArray(new String[attributes.size() + 2]);
                        tmp[tmp.length - 2] = UUID_ATTRIBUTE;
                        tmp[tmp.length - 1] = ETAG_ATTRIBUTE;
                        SearchRequest request =
                                Requests.newSearchRequest(baseDN, SearchScope.SINGLE_LEVEL, Filter
                                        .equality(UUID_ATTRIBUTE, id), tmp);
                        connection.searchSingleEntryAsync(request, innerHandler);
                    }
                };
        factory.getConnectionAsync(outerHandler);
    }
    public String getIDFromEntry(final Entry entry) {
        return entry.parseAttribute(UUID_ATTRIBUTE).asString();
    }
    public String getEtagFromEntry(final Entry entry) {
        return entry.parseAttribute(ETAG_ATTRIBUTE).asString();
    }
}
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/EtagStrategy.java
File was deleted
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Example.java
New file
@@ -0,0 +1,160 @@
/*
 * 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 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;
import org.forgerock.opendj.ldap.DN;
import org.forgerock.opendj.ldap.LDAPConnectionFactory;
import org.forgerock.opendj.ldap.requests.Requests;
import org.forgerock.resource.framework.JsonResourceProvider;
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;
import org.restlet.routing.Template;
/**
 * Example
 */
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) {
        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();
        ConnectionFactory factory =
                Connections.newAuthenticatedConnectionFactory(new LDAPConnectionFactory(
                        "localhost", 1389), Requests.newSimpleBindRequest("cn=directory manager",
                        "password".toCharArray()));
        EntryContainer container =
                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);
        // Attach the sample application.
        component.getDefaultHost().attach("/example", application);
        // Start the component.
        component.start();
    }
    public static void main(String[] args) throws Exception {
        Example instance = new Example();
        instance.start();
    }
}
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/IdentityAttributeMapper.java
@@ -65,7 +65,7 @@
            return includedAttributes.values();
        } else {
            // All user attributes.
            return Collections.emptySet();
            return Collections.singleton("*");
        }
    }
@@ -88,6 +88,7 @@
                result.put(name, Utils.attributeToJson(a));
            }
        }
        h.handleResult(result);
    }
    private boolean isIncludedAttribute(String name) {
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPResource.java
@@ -63,25 +63,13 @@
public class LDAPResource implements Resource {
    private final EntryContainer entryContainer;
    private final AttributeMapper attributeMapper;
    private final EtagStrategy etagStrategy;
    /**
     * Creates a new LDAP resource.
     *
     * @param entryContainer
     *            The entry container which will be used to interact with the
     *            LDAP server.
     * @param etagStrategy
     *            The algorithm which should be used to obtain a resource's etag
     *            from an entry.
     * @param attributeMapper
     *            The attribute mapper.
     */
    public LDAPResource(final EntryContainer entryContainer, final EtagStrategy etagStrategy,
            final AttributeMapper attributeMapper) {
        this.entryContainer = entryContainer;
        this.etagStrategy = etagStrategy;
        this.attributeMapper = attributeMapper;
    public LDAPResource(final EntryContainer container, final AttributeMapper mapper) {
        this.entryContainer = container;
        this.attributeMapper = mapper;
    }
    /**
@@ -171,7 +159,7 @@
                        }
                        public void handleResult(final SearchResultEntry entry) {
                            final String revision = etagStrategy.getEtagFromEntry(entry);
                            final String revision = entryContainer.getEtagFromEntry(entry);
                            final ResultHandler<Map<String, Object>> mapHandler =
                                    new ResultHandler<Map<String, Object>>() {
                                        public void handleErrorResult(
@@ -252,4 +240,5 @@
            return requestedLDAPAttributes;
        }
    }
}
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Utils.java
@@ -36,7 +36,7 @@
/**
 *
 */
public final class Utils {
final class Utils {
    private static final Function<ByteString, Object, Syntax> BYTESTRING_TO_JSON =
            new Function<ByteString, Object, Syntax>() {
@@ -72,18 +72,32 @@
        }
    }
    public static Object attributeToJson(Attribute a) {
    static Object attributeToJson(Attribute a) {
        Syntax syntax = a.getAttributeDescription().getAttributeType().getSyntax();
        Function<ByteString, Object, Void> f = Functions.fixedFunction(BYTESTRING_TO_JSON, syntax);
        boolean isSingleValued = a.getAttributeDescription().getAttributeType().isSingleValue();
        return isSingleValued ? a.parse().as(f) : asList(a.parse().asSetOf(f));
    }
    public static String getAttributeName(Attribute a) {
    static String getAttributeName(Attribute a) {
        return a.getAttributeDescription().withoutOption("binary").toString();
    }
    public static String toLowerCase(String s) {
    static String toLowerCase(String s) {
        return s != null ? s.toLowerCase(Locale.ENGLISH) : null;
    }
    static <T> T ensureNotNull(final T object) {
        if (object == null) {
            throw new NullPointerException();
        }
        return object;
    }
    static <T> T ensureNotNull(final T object, final String message) {
        if (object == null) {
            throw new NullPointerException(message);
        }
        return object;
    }
}
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Validator.java
File was deleted