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

Matthew Swift
11.13.2013 4ba67b612ac446dd8e0a83689983499491324eef
Fix OPENDJ-691: Implement add/create support

6 files modified
242 ■■■■ changed files
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/DefaultAttributeMapper.java 35 ●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPConstantAttributeMapper.java 14 ●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Rest2LDAP.java 8 ●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SimpleAttributeMapper.java 85 ●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Utils.java 79 ●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/Example.java 21 ●●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/DefaultAttributeMapper.java
@@ -17,11 +17,11 @@
import static org.forgerock.opendj.rest2ldap.Utils.attributeToJson;
import static org.forgerock.opendj.rest2ldap.Utils.getAttributeName;
import static org.forgerock.opendj.rest2ldap.Utils.jsonToAttribute;
import static org.forgerock.opendj.rest2ldap.Utils.toFilter;
import static org.forgerock.opendj.rest2ldap.Utils.toLowerCase;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
@@ -34,7 +34,6 @@
import org.forgerock.json.resource.ResultHandler;
import org.forgerock.opendj.ldap.Attribute;
import org.forgerock.opendj.ldap.AttributeDescription;
import org.forgerock.opendj.ldap.Attributes;
import org.forgerock.opendj.ldap.Entry;
import org.forgerock.opendj.ldap.Filter;
@@ -138,38 +137,16 @@
                if (!isIncludedAttribute(field.getKey())) {
                    continue;
                }
                final AttributeDescription ad;
                try {
                    ad = AttributeDescription.valueOf(field.getKey(), c.getConfig().schema());
                    final AttributeDescription ad =
                            AttributeDescription.valueOf(field.getKey(), c.getConfig().schema());
                    result.add(jsonToAttribute(field.getValue(), ad));
                } catch (final Exception e) {
                    // FIXME: improve error message.
                    h.handleError(new BadRequestException("The field " + field.getKey()
                            + " is invalid"));
                    return;
                }
                final Object value = field.getValue();
                if (isJSONPrimitive(value)) {
                    result.add(Attributes.singletonAttribute(ad, value));
                } else if (value instanceof Collection<?>) {
                    final Attribute a =
                            c.getConfig().decodeOptions().getAttributeFactory().newAttribute(ad);
                    for (final Object o : (Collection<?>) value) {
                        if (isJSONPrimitive(o)) {
                            a.add(o);
                        } else {
                            // FIXME: improve error message.
                            h.handleError(new BadRequestException("The field " + field.getKey()
                                    + " is invalid"));
                            return;
                        }
                    }
                    result.add(a);
                } else {
                    // FIXME: improve error message.
                    h.handleError(new BadRequestException("The field " + field.getKey()
                            + " is invalid"));
                    return;
                }
            }
            h.handleResult(result);
        } else {
@@ -192,8 +169,4 @@
        return false;
    }
    private boolean isJSONPrimitive(final Object value) {
        return value instanceof String || value instanceof Boolean || value instanceof Number;
    }
}
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPConstantAttributeMapper.java
@@ -15,6 +15,7 @@
 */
package org.forgerock.opendj.rest2ldap;
import static java.util.Collections.singletonList;
import static org.forgerock.opendj.ldap.Attributes.singletonAttribute;
import java.util.Collections;
@@ -29,6 +30,7 @@
import org.forgerock.opendj.ldap.AttributeDescription;
import org.forgerock.opendj.ldap.Entry;
import org.forgerock.opendj.ldap.Filter;
import org.forgerock.opendj.ldap.LinkedAttribute;
/**
 * An attribute mapper which maps a single LDAP attribute to a fixed value.
@@ -37,8 +39,16 @@
    private final List<Attribute> attributes;
    LDAPConstantAttributeMapper(final AttributeDescription attributeName,
            final Object attributeValue) {
        attributes = Collections.singletonList(singletonAttribute(attributeName, attributeValue));
            final Object... attributeValues) {
        if (attributeValues.length == 1) {
            attributes = singletonList(singletonAttribute(attributeName, attributeValues[0]));
        } else {
            Attribute attribute = new LinkedAttribute(attributeName);
            for (Object o : attributeValues) {
                attribute.add(o);
            }
            attributes = singletonList(attribute);
        }
    }
    @Override
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Rest2LDAP.java
@@ -359,13 +359,13 @@
    }
    public static AttributeMapper mapLDAPConstant(final AttributeDescription attribute,
            final Object attributeValue) {
        return new LDAPConstantAttributeMapper(attribute, attributeValue);
            final Object... attributeValues) {
        return new LDAPConstantAttributeMapper(attribute, attributeValues);
    }
    public static AttributeMapper mapLDAPConstant(final String attribute,
            final Object attributeValue) {
        return mapLDAPConstant(AttributeDescription.valueOf(attribute), attributeValue);
            final Object... attributeValues) {
        return mapLDAPConstant(AttributeDescription.valueOf(attribute), attributeValues);
    }
    private static AttributeMapper mapOf(final Collection<AttributeMapper> mappers) {
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SimpleAttributeMapper.java
@@ -15,7 +15,15 @@
 */
package org.forgerock.opendj.rest2ldap;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptySet;
import static java.util.Collections.singleton;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static org.forgerock.opendj.ldap.Functions.fixedFunction;
import static org.forgerock.opendj.rest2ldap.Utils.byteStringToJson;
import static org.forgerock.opendj.rest2ldap.Utils.jsonToAttribute;
import static org.forgerock.opendj.rest2ldap.Utils.jsonToByteString;
import static org.forgerock.opendj.rest2ldap.Utils.toFilter;
import static org.forgerock.opendj.rest2ldap.Utils.toLowerCase;
@@ -27,6 +35,7 @@
import org.forgerock.json.fluent.JsonPointer;
import org.forgerock.json.fluent.JsonValue;
import org.forgerock.json.resource.BadRequestException;
import org.forgerock.json.resource.ResultHandler;
import org.forgerock.opendj.ldap.Attribute;
import org.forgerock.opendj.ldap.AttributeDescription;
@@ -34,7 +43,7 @@
import org.forgerock.opendj.ldap.Entry;
import org.forgerock.opendj.ldap.Filter;
import org.forgerock.opendj.ldap.Function;
import org.forgerock.opendj.ldap.Functions;
import org.forgerock.opendj.ldap.LinkedAttribute;
/**
 * An attribute mapper which maps a single JSON attribute to a single LDAP
@@ -43,8 +52,10 @@
public final class SimpleAttributeMapper extends AttributeMapper {
    private Function<ByteString, ?, Void> decoder = null;
    private Object defaultValue = null;
    private Collection<Object> defaultValues = Collections.emptySet();
    private Object defaultJSONValue = null;
    private Collection<Object> defaultJSONValues = Collections.emptySet();
    private ByteString defaultLDAPValue = null;
    private Function<Object, ByteString, Void> encoder = null;
    private boolean forceSingleValued = false;
    // private boolean isReadOnly = false;
@@ -90,9 +101,34 @@
     * @return This attribute mapper.
     */
    public SimpleAttributeMapper defaultJSONValue(final Object defaultValue) {
        this.defaultValue = defaultValue;
        this.defaultValues =
                defaultValue != null ? Collections.singleton(defaultValue) : Collections.emptySet();
        this.defaultJSONValue = defaultValue;
        this.defaultJSONValues = defaultValue != null ? singleton(defaultValue) : emptySet();
        return this;
    }
    /**
     * Sets the default LDAP value which should be substituted when the JSON
     * attribute is not found in the JSON value.
     *
     * @param defaultValue
     *            The default LDAP value.
     * @return This attribute mapper.
     */
    public SimpleAttributeMapper defaultLDAPValue(final Object defaultValue) {
        this.defaultLDAPValue = defaultValue != null ? ByteString.valueOf(defaultValue) : null;
        return this;
    }
    /**
     * Sets the encoder which will be used for converting JSON values to LDAP
     * attribute values.
     *
     * @param f
     *            The function to use for encoding LDAP attribute values.
     * @return This attribute mapper.
     */
    public SimpleAttributeMapper encoder(final Function<Object, ByteString, Void> f) {
        this.encoder = f;
        return this;
    }
@@ -145,21 +181,44 @@
    @Override
    void toJSON(final Context c, final Entry e, final ResultHandler<Map<String, Object>> h) {
        final Function<ByteString, ?, Void> f =
                decoder == null ? Functions.fixedFunction(byteStringToJson(), ldapAttributeName)
                        : decoder;
                decoder == null ? fixedFunction(byteStringToJson(), ldapAttributeName) : decoder;
        final Object value;
        if (forceSingleValued || ldapAttributeName.getAttributeType().isSingleValue()) {
            value = e.parseAttribute(ldapAttributeName).as(f, defaultValue);
            value = e.parseAttribute(ldapAttributeName).as(f, defaultJSONValue);
        } else {
            value = e.parseAttribute(ldapAttributeName).asSetOf(f, defaultValues);
            value = e.parseAttribute(ldapAttributeName).asSetOf(f, defaultJSONValues);
        }
        h.handleResult(Collections.singletonMap(jsonAttributeName, value));
        h.handleResult(singletonMap(jsonAttributeName, value));
    }
    @Override
    void toLDAP(final Context c, final JsonValue v, final ResultHandler<List<Attribute>> h) {
        // TODO Auto-generated method stub
        if (v.isMap()) {
            final Object value = v.get(jsonAttributeName).getObject();
            try {
                final List<Attribute> result;
                if (value != null) {
                    final Function<Object, ByteString, Void> f =
                            encoder != null ? encoder : fixedFunction(jsonToByteString(),
                                    ldapAttributeName);
                    result = singletonList(jsonToAttribute(value, ldapAttributeName, f));
                } else if (defaultLDAPValue != null) {
                    result =
                            singletonList((Attribute) new LinkedAttribute(ldapAttributeName,
                                    defaultLDAPValue));
                } else {
                    result = emptyList();
                }
                h.handleResult(result);
            } catch (final Exception e) {
                // FIXME: improve error message.
                h.handleError(new BadRequestException("The field " + jsonAttributeName
                        + " is invalid"));
                return;
            }
        } else {
            h.handleResult(Collections.<Attribute> emptyList());
        }
    }
    private boolean matches(final JsonPointer jsonAttribute) {
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Utils.java
@@ -15,6 +15,13 @@
 */
package org.forgerock.opendj.rest2ldap;
import static javax.xml.bind.DatatypeConverter.parseDateTime;
import static javax.xml.bind.DatatypeConverter.printDateTime;
import static org.forgerock.opendj.ldap.Functions.byteStringToBoolean;
import static org.forgerock.opendj.ldap.Functions.byteStringToGeneralizedTime;
import static org.forgerock.opendj.ldap.Functions.byteStringToLong;
import static org.forgerock.opendj.ldap.Functions.byteStringToString;
import static org.forgerock.opendj.ldap.Functions.fixedFunction;
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;
@@ -26,8 +33,6 @@
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;
@@ -35,7 +40,8 @@
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.GeneralizedTime;
import org.forgerock.opendj.ldap.LinkedAttribute;
import org.forgerock.opendj.ldap.schema.Syntax;
/**
@@ -85,24 +91,45 @@
    }
    // @Checkstyle:off
    private static final Function<ByteString, Object, AttributeDescription> BYTESTRING_TO_JSON =
            new Function<ByteString, Object, AttributeDescription>() {
                @Override
                public Object apply(final ByteString value, final AttributeDescription ad) {
                    final Syntax syntax = ad.getAttributeType().getSyntax();
                    if (syntax.equals(getBooleanSyntax())) {
                        return Functions.byteStringToBoolean().apply(value, null);
                        return byteStringToBoolean().apply(value, null);
                    } else if (syntax.equals(getIntegerSyntax())) {
                        return Functions.byteStringToLong().apply(value, null);
                        return byteStringToLong().apply(value, null);
                    } else if (syntax.equals(getGeneralizedTimeSyntax())) {
                        return DatatypeConverter.printDateTime(Functions
                                .byteStringToGeneralizedTime().apply(value, null).toCalendar());
                        return printDateTime(byteStringToGeneralizedTime().apply(value, null)
                                .toCalendar());
                    } else if (syntax.isHumanReadable()) {
                        return Functions.byteStringToString().apply(value, null);
                        return byteStringToString().apply(value, null);
                    } else {
                        // Base 64 encoded binary.
                        return DatatypeConverter.printBase64Binary(value.toByteArray());
                        return value.toBase64String();
                    }
                }
            };
    private static final Function<Object, ByteString, AttributeDescription> JSON_TO_BYTESTRING =
            new Function<Object, ByteString, AttributeDescription>() {
                @Override
                public ByteString apply(final Object value, final AttributeDescription ad) {
                    if (isJSONPrimitive(value)) {
                        final Syntax syntax = ad.getAttributeType().getSyntax();
                        if (syntax.equals(getGeneralizedTimeSyntax())) {
                            return ByteString.valueOf(GeneralizedTime.valueOf(parseDateTime(value
                                    .toString())));
                        } else if (syntax.isHumanReadable()) {
                            return ByteString.valueOf(value);
                        } else {
                            // Base 64 encoded binary.
                            return ByteString.valueOfBase64(value.toString());
                        }
                    } else {
                        throw new IllegalArgumentException("Unrecognized type of JSON value: "
                                + value.getClass().getName());
                    }
                }
            };
@@ -113,14 +140,12 @@
    static Object attributeToJson(final Attribute a) {
        final Function<ByteString, Object, Void> f =
                Functions.fixedFunction(BYTESTRING_TO_JSON, a.getAttributeDescription());
                fixedFunction(BYTESTRING_TO_JSON, a.getAttributeDescription());
        final boolean isSingleValued =
                a.getAttributeDescription().getAttributeType().isSingleValue();
        return isSingleValued ? a.parse().as(f) : asList(a.parse().asSetOf(f));
    }
    // @Checkstyle:on
    static Function<ByteString, Object, AttributeDescription> byteStringToJson() {
        return BYTESTRING_TO_JSON;
    }
@@ -143,6 +168,34 @@
        return a.getAttributeDescription().withoutOption("binary").toString();
    }
    static boolean isJSONPrimitive(final Object value) {
        return value instanceof String || value instanceof Boolean || value instanceof Number;
    }
    static Attribute jsonToAttribute(final Object value, final AttributeDescription ad) {
        return jsonToAttribute(value, ad, fixedFunction(jsonToByteString(), ad));
    }
    static Attribute jsonToAttribute(final Object value, final AttributeDescription ad,
            final Function<Object, ByteString, Void> f) {
        if (isJSONPrimitive(value)) {
            return new LinkedAttribute(ad, f.apply(value, null));
        } else if (value instanceof Collection<?>) {
            final Attribute a = new LinkedAttribute(ad);
            for (final Object o : (Collection<?>) value) {
                a.add(f.apply(o, null));
            }
            return a;
        } else {
            throw new IllegalArgumentException("Unrecognized type of JSON value: "
                    + value.getClass().getName());
        }
    }
    static Function<Object, ByteString, AttributeDescription> jsonToByteString() {
        return JSON_TO_BYTESTRING;
    }
    static Filter toFilter(final Context c, final FilterType type, final String ldapAttribute,
            final Object valueAssertion) {
        final String v = String.valueOf(valueAssertion);
opendj3/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/Example.java
@@ -18,11 +18,9 @@
import static org.forgerock.json.resource.Resources.newInternalConnectionFactory;
import static org.forgerock.opendj.ldap.Connections.newAuthenticatedConnectionFactory;
import static org.forgerock.opendj.rest2ldap.Rest2LDAP.builder;
import static org.forgerock.opendj.rest2ldap.Rest2LDAP.map;
import static org.forgerock.opendj.rest2ldap.Rest2LDAP.mapAllOf;
import static org.forgerock.opendj.rest2ldap.Rest2LDAP.mapComplex;
import static org.forgerock.opendj.rest2ldap.Rest2LDAP.*;
import java.util.Arrays;
import java.util.logging.Logger;
import org.forgerock.json.resource.CollectionResourceProvider;
@@ -65,12 +63,19 @@
        // Create user resource.
        CollectionResourceProvider users =
                builder().factory(ldapFactory).baseDN("ou=people,dc=example,dc=com").map(
                        mapJSONConstant("schemas", Arrays.asList("urn:scim:schemas:core:1.0")),
                        map("id", "entryUUID").singleValued(true),
                        mapAllOf("uid", "isMemberOf", "modifyTimestamp"),
                        mapComplex("name", mapAllOf("cn", "sn", "givenName")),
                        map("externalId", "uid").singleValued(true),
                        map("userName", "mail").singleValued(true),
                        map("displayName", "cn").singleValued(true),
                        mapComplex("name", map("givenName", "givenName").singleValued(true), map(
                                "familyName", "sn").singleValued(true)),
                        mapComplex("contactInformation", map("telephoneNumber").decoder(
                                Functions.byteStringToString()).singleValued(true), map(
                                "emailAddress", "mail").singleValued(true))).build();
                                Functions.byteStringToString()).encoder(
                                Functions.objectToByteString()).singleValued(true), map(
                                "emailAddress", "mail").singleValued(true)),
                        mapLDAPConstant("objectClass", "top", "person", "organizationalPerson", "inetOrgPerson"))
                        .build();
        router.addRoute("/users", users);
        // Create group resource.