| opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/AttributeMapper.java | ●●●●● patch | view | raw | blame | history | |
| opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/CompositeAttributeMapper.java | ●●●●● 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/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/AttributeMapper.java
@@ -18,6 +18,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import org.forgerock.json.fluent.JsonPointer; @@ -33,30 +34,30 @@ public interface AttributeMapper { /** * Returns an unmodifiable set containing the names of the LDAP attributes * required by this attribute mapper. The returned set should only contain * the names of attributes found in the LDAP entry directly associated with * the resource. * 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 set containing the names of the LDAP attributes * required by this attribute mapper. * @return An unmodifiable collection containing the names of the LDAP * attributes required by this attribute mapper. */ Set<String> getAllLDAPAttributes(); Collection<String> getAllLDAPAttributes(); /** * Returns an unmodifiable collection containing the names of the LDAP * attributes required by this attribute mapper and which are associated * with the provided resource attribute. The returned set should only * contain the names of attributes found in the LDAP entry directly * associated with the resource. * Adds the names of the LDAP attributes required by this attribute mapper * which are associated with the provided resource attribute. * <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. * @return An unmodifiable collection containing the names of the LDAP * attributes required by this attribute mapper and which are * associated with the provided resource attribute. * @param ldapAttributes * The set into which the required LDAP attribute names should be * put. */ Collection<String> getLDAPAttributesFor(JsonPointer resourceAttribute); void getLDAPAttributesFor(JsonPointer resourceAttribute, Set<String> ldapAttributes); /** * Transforms attributes contained in the provided LDAP entry to JSON @@ -69,10 +70,9 @@ * * @param c * @param e * @param v * @param h */ void toJson(Context c, Entry e, JsonValue v, ResultHandler<JsonValue> h); void toJson(Context c, Entry e, ResultHandler<Map<String, Object>> h); /** * Transforms JSON content in the provided JSON value to LDAP attributes, @@ -84,10 +84,9 @@ * * @param c * @param v * @param a * @param h */ void toLDAP(Context c, JsonValue v, List<Attribute> a, ResultHandler<Entry> h); void toLDAP(Context c, JsonValue v, ResultHandler<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/CompositeAttributeMapper.java
New file @@ -0,0 +1,178 @@ /* * 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.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedHashSet; 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.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.provider.Context; /** * */ public final class CompositeAttributeMapper implements AttributeMapper { private final Set<String> allLDAPAttributes; private final List<AttributeMapper> attributeMappers; /** * Creates a new composite attribute mapper. * * @param attributeMappers * The list of attribute mappers. */ public CompositeAttributeMapper(final List<AttributeMapper> attributeMappers) { this.attributeMappers = new ArrayList<AttributeMapper>(attributeMappers); Set<String> tmp = new LinkedHashSet<String>(attributeMappers.size()); for (final AttributeMapper mapper : attributeMappers) { tmp.addAll(mapper.getAllLDAPAttributes()); } allLDAPAttributes = Collections.unmodifiableSet(tmp); } /** * {@inheritDoc} */ public Set<String> getAllLDAPAttributes() { return allLDAPAttributes; } /** * {@inheritDoc} */ public void getLDAPAttributesFor(JsonPointer resourceAttribute, Set<String> ldapAttributes) { for (AttributeMapper attribute : attributeMappers) { attribute.getLDAPAttributesFor(resourceAttribute, ldapAttributes); } } /** * {@inheritDoc} */ public void toJson(Context c, Entry e, final ResultHandler<Map<String, Object>> h) { 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 handleErrorResult(ErrorResultException error) { // Ensure that handler is only invoked once. if (latch.getAndSet(0) > 0) { h.handleErrorResult(error); } } public void handleResult(Map<String, Object> result) { 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); } } }; for (AttributeMapper mapper : attributeMappers) { mapper.toJson(c, e, resultAccumulater); } } /** * {@inheritDoc} */ public void toLDAP(Context c, JsonValue v, ResultHandler<List<Attribute>> h) { // TODO Auto-generated method stub } /** * 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(List<Map<String, Object>> srcValues, Map<String, Object> dstValue) { for (Map<String, Object> value : srcValues) { mergeJsonValue(value, dstValue); } } /** * 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(Map<String, Object> srcValue, Map<String, Object> dstValue) { for (Map.Entry<String, Object> record : srcValue.entrySet()) { String key = record.getKey(); Object newValue = record.getValue(); Object existingValue = dstValue.get(key); if (existingValue == null) { // Value is new, so just add it. dstValue.put(key, newValue); } 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); 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 // is unmodifiable. List<Object> tmp = new ArrayList<Object>((List<Object>) existingValue); tmp.addAll((List<Object>) newValue); existingValue = tmp; } // Replace the existing value. dstValue.put(key, newValue); } } } opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/EntryContainer.java
@@ -16,7 +16,7 @@ package org.forgerock.opendj.rest2ldap; import java.util.Set; import java.util.Collection; import org.forgerock.opendj.ldap.ResultHandler; import org.forgerock.opendj.ldap.SearchResultHandler; @@ -58,7 +58,7 @@ * @param h * The result handler. */ public void readEntry(final Context c, final String id, final Set<String> attributes, public void readEntry(final Context c, final String id, final Collection<String> attributes, final ResultHandler<SearchResultEntry> h) { // TODO Auto-generated method stub opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/EtagStrategy.java
New file @@ -0,0 +1,26 @@ /* * 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.opendj.ldap.Entry; /** * */ public interface EtagStrategy { String getEtagFromEntry(final Entry entry); } opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/IdentityAttributeMapper.java
New file @@ -0,0 +1,112 @@ /* * 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 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; import java.util.Set; import org.forgerock.json.fluent.JsonPointer; 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 { // 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() { // No implementation required. } public IdentityAttributeMapper includeAttribute(String... attributes) { for (String attribute : attributes) { includedAttributes.put(toLowerCase(attribute), attribute); } return this; } public IdentityAttributeMapper excludeAttribute(String... attributes) { for (String attribute : attributes) { excludedAttributes.put(toLowerCase(attribute), attribute); } return this; } public Collection<String> getAllLDAPAttributes() { if (!includedAttributes.isEmpty()) { return includedAttributes.values(); } else { // All user attributes. return Collections.emptySet(); } } public void getLDAPAttributesFor(JsonPointer resourceAttribute, Set<String> ldapAttributes) { String name = resourceAttribute.leaf(); if (name != null) { if (isIncludedAttribute(name)) { ldapAttributes.add(name); } else { // FIXME: log something or return a ResourceException? } } } public void toJson(Context c, Entry e, ResultHandler<Map<String, Object>> h) { Map<String, Object> result = new LinkedHashMap<String, Object>(e.getAttributeCount()); for (Attribute a : e.getAllAttributes()) { String name = getAttributeName(a); if (isIncludedAttribute(name)) { result.put(name, Utils.attributeToJson(a)); } } } private boolean isIncludedAttribute(String name) { String lowerName = toLowerCase(name); // Ignore the requested attribute if it has been excluded. if (excludedAttributes.containsKey(lowerName)) { return false; } // Include all attributes by default. if (includedAttributes.isEmpty() || includedAttributes.containsKey(lowerName)) { return true; } return false; } public void toLDAP(Context c, JsonValue v, ResultHandler<List<Attribute>> h) { // TODO: } } opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPResource.java
@@ -17,16 +17,24 @@ package org.forgerock.opendj.rest2ldap; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.forgerock.json.fluent.JsonPointer; import org.forgerock.json.fluent.JsonValue; import org.forgerock.opendj.ldap.Entry; import org.forgerock.opendj.ldap.AssertionFailureException; import org.forgerock.opendj.ldap.AuthenticationException; import org.forgerock.opendj.ldap.AuthorizationException; import org.forgerock.opendj.ldap.ConnectionException; import org.forgerock.opendj.ldap.EntryNotFoundException; import org.forgerock.opendj.ldap.ErrorResultException; import org.forgerock.opendj.ldap.MultipleEntriesFoundException; import org.forgerock.opendj.ldap.ResultHandler; import org.forgerock.opendj.ldap.SearchResultHandler; import org.forgerock.opendj.ldap.TimeoutResultException; import org.forgerock.opendj.ldap.responses.Result; import org.forgerock.opendj.ldap.responses.SearchResultEntry; import org.forgerock.opendj.ldap.responses.SearchResultReference; @@ -53,23 +61,27 @@ * */ public class LDAPResource implements Resource { private Set<String> allLDAPAttributes; private final EntryContainer container; private final List<AttributeMapper> mappers; private final EntryContainer entryContainer; private final AttributeMapper attributeMapper; private final EtagStrategy etagStrategy; /** * Creates a new LDAP resource. * * @param container * @param entryContainer * The entry container which will be used to interact with the * LDAP server. * @param mappers * The list of attribute mappers. * @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 container, final List<AttributeMapper> mappers) { this.container = container; this.mappers = mappers; cacheAllLDAPAttributes(); public LDAPResource(final EntryContainer entryContainer, final EtagStrategy etagStrategy, final AttributeMapper attributeMapper) { this.entryContainer = entryContainer; this.etagStrategy = etagStrategy; this.attributeMapper = attributeMapper; } /** @@ -125,7 +137,7 @@ public boolean handleEntry(final SearchResultEntry entry) { // TODO: should the resource or the container define the ID // mapping? resourceIDs.add(container.getIDFromEntry(entry)); resourceIDs.add(entryContainer.getIDFromEntry(entry)); return true; } @@ -135,7 +147,7 @@ public boolean handleReference(final SearchResultReference reference) { // TODO: should this be classed as an error since rest2ldap // assumes entries are all colocated. // assumes entries are all colocated? return true; } @@ -143,13 +155,13 @@ out.setResult(id, null, new JsonValue(resourceIDs)); } }; container.listEntries(context, handler); entryContainer.listEntries(context, handler); } else { // Read a single entry. // TODO: Determine the set of LDAP attributes that need to be read. final Set<JsonPointer> requestedAttributes = new LinkedHashSet<JsonPointer>(); final Set<String> requestedLDAPAttributes = final Collection<String> requestedLDAPAttributes = getRequestedLDAPAttributes(requestedAttributes); final ResultHandler<SearchResultEntry> handler = @@ -159,23 +171,22 @@ } public void handleResult(final SearchResultEntry entry) { final String revision = getRevisionFromEntry(entry); final ResultHandler<JsonValue> mapHandler = new ResultHandler<JsonValue>() { final String revision = etagStrategy.getEtagFromEntry(entry); final ResultHandler<Map<String, Object>> mapHandler = new ResultHandler<Map<String, Object>>() { public void handleErrorResult( final ErrorResultException error) { out.setFailure(adaptErrorResult(error)); } public void handleResult(final JsonValue result) { out.setResult(id, revision, result); public void handleResult(final Map<String, Object> result) { out.setResult(id, revision, new JsonValue(result)); } }; mapEntryToJson(context, requestedAttributes, entry, mapHandler); attributeMapper.toJson(context, entry, mapHandler); } }; container.readEntry(context, id, requestedLDAPAttributes, handler); entryContainer.readEntry(context, id, requestedLDAPAttributes, handler); } } @@ -187,20 +198,35 @@ out.setFailure(new NotSupportedException("Not yet implemented")); } private ResourceException adaptErrorResult(final ErrorResultException error) { // TODO Auto-generated method stub return null; } /** * Caches the set of LDAP attributes associated with all of this resource's * mappers. * Adapts an LDAP result code to a resource exception. * * @param error * The LDAP error that should be adapted. * @return The equivalent resource exception. */ private void cacheAllLDAPAttributes() { allLDAPAttributes = new LinkedHashSet<String>(mappers.size()); for (final AttributeMapper mapper : mappers) { allLDAPAttributes.addAll(mapper.getAllLDAPAttributes()); private ResourceException adaptErrorResult(final ErrorResultException error) { int resourceResultCode; try { throw error; } catch (AssertionFailureException e) { resourceResultCode = ResourceException.VERSION_MISMATCH; } catch (AuthenticationException e) { resourceResultCode = 401; } catch (AuthorizationException e) { resourceResultCode = ResourceException.FORBIDDEN; } catch (ConnectionException e) { resourceResultCode = ResourceException.UNAVAILABLE; } catch (EntryNotFoundException e) { resourceResultCode = ResourceException.NOT_FOUND; } catch (MultipleEntriesFoundException e) { resourceResultCode = ResourceException.INTERNAL_ERROR; } catch (TimeoutResultException e) { resourceResultCode = 408; } catch (ErrorResultException e) { resourceResultCode = ResourceException.INTERNAL_ERROR; } return ResourceException.getException(resourceResultCode, null, error.getMessage(), error); } /** @@ -212,31 +238,18 @@ * @return The set of LDAP attributes associated with the resource * attributes. */ private Set<String> getRequestedLDAPAttributes(final Set<JsonPointer> requestedAttributes) { private Collection<String> getRequestedLDAPAttributes(final Set<JsonPointer> requestedAttributes) { if (requestedAttributes.isEmpty()) { // Full read. return allLDAPAttributes; return attributeMapper.getAllLDAPAttributes(); } else { // Partial read. final Set<String> requestedLDAPAttributes = new LinkedHashSet<String>(requestedAttributes.size()); for (final JsonPointer requestedAttribute : requestedAttributes) { for (final AttributeMapper mapper : mappers) { requestedLDAPAttributes.addAll(mapper.getLDAPAttributesFor(requestedAttribute)); } attributeMapper.getLDAPAttributesFor(requestedAttribute, requestedLDAPAttributes); } return requestedLDAPAttributes; } } private String getRevisionFromEntry(final SearchResultEntry entry) { // TODO Auto-generated method stub return null; } private void mapEntryToJson(final Context c, final Set<JsonPointer> requestedAttributes, final Entry result, final ResultHandler<JsonValue> h) { // TODO Auto-generated method stub } } opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Utils.java
New file @@ -0,0 +1,89 @@ /* * 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 static org.forgerock.opendj.ldap.schema.CoreSchema.getBooleanSyntax; import static org.forgerock.opendj.ldap.schema.CoreSchema.getGeneralizedTimeSyntax; import static org.forgerock.opendj.ldap.schema.CoreSchema.getIntegerSyntax; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Locale; import javax.xml.bind.DatatypeConverter; import org.forgerock.opendj.ldap.Attribute; import org.forgerock.opendj.ldap.ByteString; import org.forgerock.opendj.ldap.Function; import org.forgerock.opendj.ldap.Functions; import org.forgerock.opendj.ldap.schema.Syntax; /** * */ public final class Utils { private static final Function<ByteString, Object, Syntax> BYTESTRING_TO_JSON = new Function<ByteString, Object, Syntax>() { public Object apply(final ByteString value, final Syntax syntax) { 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()); } } }; // Prevent instantiation. private Utils() { // No implementation required. } private static <T> List<T> asList(Collection<T> c) { if (c instanceof List) { return (List<T>) c; } else { List<T> result = new ArrayList<T>(c.size()); result.addAll(c); return result; } } public 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) { return a.getAttributeDescription().withoutOption("binary").toString(); } public static String toLowerCase(String s) { return s != null ? s.toLowerCase(Locale.ENGLISH) : null; } }