From a2bc68638f55ae0ad7b9e3a04c7a3c02d01384f8 Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Wed, 13 Feb 2013 23:44:11 +0000
Subject: [PATCH] Partial fix for OPENDJ-758 : Implement configurable update policy for simple and default mappers
---
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SimpleAttributeMapper.java | 156 ++++++++++++++++++++++++++++++++-------------------
1 files changed, 98 insertions(+), 58 deletions(-)
diff --git a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SimpleAttributeMapper.java b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SimpleAttributeMapper.java
index a8375e3..9546963 100644
--- a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SimpleAttributeMapper.java
+++ b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SimpleAttributeMapper.java
@@ -19,23 +19,23 @@
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;
+import static org.forgerock.opendj.rest2ldap.WritabilityPolicy.READ_ONLY;
+import static org.forgerock.opendj.rest2ldap.WritabilityPolicy.READ_WRITE;
import java.util.Collection;
import java.util.Collections;
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.json.resource.BadRequestException;
+import org.forgerock.json.resource.ResourceException;
import org.forgerock.json.resource.ResultHandler;
import org.forgerock.opendj.ldap.Attribute;
import org.forgerock.opendj.ldap.AttributeDescription;
@@ -46,37 +46,23 @@
import org.forgerock.opendj.ldap.LinkedAttribute;
/**
- * An attribute mapper which maps a single JSON attribute to a single LDAP
- * attribute.
+ * An attribute mapper which provides a simple mapping from a JSON value to a
+ * single LDAP attribute.
*/
public final class SimpleAttributeMapper extends AttributeMapper {
-
private Function<ByteString, ?, Void> decoder = null;
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;
- private final String jsonAttributeName;
+ private boolean isIgnoreUpdates = true;
+ private boolean isRequired = false;
+ private boolean isSingleValued = false;
private final AttributeDescription ldapAttributeName;
- private final String normalizedJsonAttributeName;
+ private WritabilityPolicy writabilityPolicy = READ_WRITE;
- /**
- * Creates a new simple attribute mapper which maps a single LDAP attribute
- * to an entry.
- *
- * @param jsonAttributeName
- * The name of the simple JSON attribute.
- * @param ldapAttributeName
- * The name of the LDAP attribute.
- */
- SimpleAttributeMapper(final String jsonAttributeName,
- final AttributeDescription ldapAttributeName) {
- this.jsonAttributeName = jsonAttributeName;
+ SimpleAttributeMapper(final AttributeDescription ldapAttributeName) {
this.ldapAttributeName = ldapAttributeName;
- this.normalizedJsonAttributeName = toLowerCase(jsonAttributeName);
}
/**
@@ -133,70 +119,118 @@
}
/**
- * Prevents the LDAP attribute from being updated.
+ * Indicates whether or not an attempt to update the LDAP attribute should
+ * be ignored when the update is incompatible with the writability policy.
+ * The default is {@code true}.
*
- * @param readOnly
- * {@code true} if the LDAP attribute is read-only.
+ * @param ignore
+ * {@code true} an attempt to update the LDAP attribute should be
+ * ignored.
* @return This attribute mapper.
*/
- public SimpleAttributeMapper readOnly(final boolean readOnly) {
- // TODO: enforcement policy: ignore, warn, or reject.
- // this.isReadOnly = readOnly;
+ public SimpleAttributeMapper ignoreUpdates(final boolean ignore) {
+ this.isIgnoreUpdates = ignore;
+ return this;
+ }
+
+ /**
+ * Indicates that the LDAP attribute is mandatory and must be provided
+ * during create requests. The default is {@code false}.
+ *
+ * @param isRequired
+ * {@code true} if the LDAP attribute is mandatory and must be
+ * provided during create requests.
+ * @return This attribute mapper.
+ */
+ public SimpleAttributeMapper required(final boolean isRequired) {
+ this.isRequired = isRequired;
return this;
}
/**
* Forces a multi-valued LDAP attribute to be represented as a single-valued
- * JSON value, rather than an array of values.
+ * JSON value, rather than an array of values. The default is {@code false}.
*
- * @param singleValued
+ * @param isSingleValued
* {@code true} if the LDAP attribute should be treated as a
* single-valued attribute.
* @return This attribute mapper.
*/
- public SimpleAttributeMapper singleValued(final boolean singleValued) {
- this.forceSingleValued = singleValued;
+ public SimpleAttributeMapper singleValued(final boolean isSingleValued) {
+ this.isSingleValued = isSingleValued;
+ return this;
+ }
+
+ /**
+ * Indicates whether or not the LDAP attribute supports updates. The default
+ * is {@link WritabilityPolicy#READ_WRITE}.
+ *
+ * @param policy
+ * The writability policy.
+ * @return This attribute mapper.
+ */
+ public SimpleAttributeMapper writability(final WritabilityPolicy policy) {
+ this.writabilityPolicy = policy;
return this;
}
@Override
void getLDAPAttributes(final Context c, final JsonPointer jsonAttribute,
final Set<String> ldapAttributes) {
- if (jsonAttribute.isEmpty() || matches(jsonAttribute)) {
- ldapAttributes.add(ldapAttributeName.toString());
- }
+ ldapAttributes.add(ldapAttributeName.toString());
}
@Override
void getLDAPFilter(final Context c, final FilterType type, final JsonPointer jsonAttribute,
final String operator, final Object valueAssertion, final ResultHandler<Filter> h) {
- if (matches(jsonAttribute)) {
+ if (jsonAttribute.isEmpty()) {
h.handleResult(toFilter(c, type, ldapAttributeName.toString(), valueAssertion));
} else {
- // This attribute mapper cannot handle the provided filter component.
- h.handleResult(null);
+ // This attribute mapper does not support partial filtering.
+ h.handleResult(c.getConfig().falseFilter());
}
}
@Override
- void toJSON(final Context c, final Entry e, final ResultHandler<Map<String, Object>> h) {
+ void toJSON(final Context c, final Entry e, final ResultHandler<JsonValue> h) {
final Function<ByteString, ?, Void> f =
decoder == null ? fixedFunction(byteStringToJson(), ldapAttributeName) : decoder;
final Object value;
- if (forceSingleValued || ldapAttributeName.getAttributeType().isSingleValue()) {
+ if (isSingleValued || ldapAttributeName.getAttributeType().isSingleValue()) {
value = e.parseAttribute(ldapAttributeName).as(f, defaultJSONValue);
} else {
value = e.parseAttribute(ldapAttributeName).asSetOf(f, defaultJSONValues);
}
- h.handleResult(singletonMap(jsonAttributeName, value));
+ h.handleResult(new JsonValue(value));
}
@Override
void toLDAP(final Context c, final JsonValue v, final ResultHandler<List<Attribute>> h) {
- if (v.isMap()) {
- final Object value = v.get(jsonAttributeName).getObject();
- try {
- final List<Attribute> result;
+ try {
+ final List<Attribute> result;
+ if (v == null || v.isNull()) {
+ if (isRequired()) {
+ // FIXME: improve error message.
+ throw new BadRequestException("no value provided");
+ } else if (defaultLDAPValue != null) {
+ result =
+ singletonList((Attribute) new LinkedAttribute(ldapAttributeName,
+ defaultLDAPValue));
+ } else {
+ result = emptyList();
+ }
+ } else if (v.isList() && isSingleValued()) {
+ // FIXME: improve error message.
+ throw new BadRequestException("expected single value, but got multiple values");
+ } else if (isCreate()) {
+ if (isIgnoreUpdates) {
+ result = emptyList();
+ } else {
+ // FIXME: improve error message.
+ throw new BadRequestException("attempted to create a read-only value");
+ }
+ } else {
+ final Object value = v.getObject();
if (value != null) {
final Function<Object, ByteString, Void> f =
encoder != null ? encoder : fixedFunction(jsonToByteString(),
@@ -209,21 +243,27 @@
} 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());
+ h.handleResult(result);
+ } catch (final ResourceException e) {
+ h.handleError(e);
+ } catch (final Exception e) {
+ // FIXME: improve error message.
+ h.handleError(new BadRequestException(e.getMessage()));
}
}
- private boolean matches(final JsonPointer jsonAttribute) {
- return !jsonAttribute.isEmpty()
- && toLowerCase(jsonAttribute.get(0)).equals(normalizedJsonAttributeName);
+ private boolean isCreate() {
+ return writabilityPolicy != READ_ONLY
+ && ldapAttributeName.getAttributeType().isNoUserModification();
+ }
+
+ private boolean isRequired() {
+ return isRequired && defaultJSONValue == null;
+ }
+
+ private boolean isSingleValued() {
+ return isSingleValued || ldapAttributeName.getAttributeType().isSingleValue();
}
}
--
Gitblit v1.10.0