| opendj3/opendj-rest2ldap/pom.xml | ●●●●● patch | view | raw | blame | history | |
| opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/EntryContainer.java | ●●●●● patch | view | raw | blame | history | |
| opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/EtagStrategy.java | ●●●●● patch | view | raw | blame | history | |
| opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Example.java | ●●●●● patch | view | raw | blame | history | |
| opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/IdentityAttributeMapper.java | ●●●●● patch | view | raw | blame | history | |
| opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPResource.java | ●●●●● patch | view | raw | blame | history | |
| opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Utils.java | ●●●●● patch | view | raw | blame | history | |
| opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Validator.java | ●●●●● 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