From 2a98df5779f11957712ea3b7a7a10a07f51558ba Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Thu, 21 Feb 2013 22:55:52 +0000
Subject: [PATCH] OPENDJ-757: Add Rest2LDAP gateway Servlet

---
 opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Rest2LDAP.java |  162 +++++++++++++++++++++++++++++++++++++++++------------
 1 files changed, 125 insertions(+), 37 deletions(-)

diff --git a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Rest2LDAP.java b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Rest2LDAP.java
index cfb3b5d..6ce5469 100644
--- a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Rest2LDAP.java
+++ b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Rest2LDAP.java
@@ -18,7 +18,7 @@
 
 import static org.forgerock.opendj.ldap.requests.Requests.newSearchRequest;
 import static org.forgerock.opendj.ldap.schema.CoreSchema.getEntryUUIDAttributeType;
-import static org.forgerock.opendj.rest2ldap.ReadOnUpdatePolicy.USE_READ_ENTRY_CONTROLS;
+import static org.forgerock.opendj.rest2ldap.ReadOnUpdatePolicy.CONTROLS;
 import static org.forgerock.opendj.rest2ldap.Utils.ensureNotNull;
 
 import java.util.ArrayList;
@@ -29,6 +29,7 @@
 import java.util.concurrent.TimeUnit;
 
 import org.forgerock.json.fluent.JsonValue;
+import org.forgerock.json.fluent.JsonValueException;
 import org.forgerock.json.resource.BadRequestException;
 import org.forgerock.json.resource.CollectionResourceProvider;
 import org.forgerock.json.resource.ResourceException;
@@ -66,8 +67,8 @@
         private ConnectionFactory factory;
         private MVCCStrategy mvccStrategy;
         private NameStrategy nameStrategy;
-        private ReadOnUpdatePolicy readOnUpdatePolicy = USE_READ_ENTRY_CONTROLS;
-        private final ObjectAttributeMapper rootMapper = new ObjectAttributeMapper();
+        private ReadOnUpdatePolicy readOnUpdatePolicy = CONTROLS;
+        private AttributeMapper rootMapper;
         private Schema schema = Schema.getDefaultSchema();
 
         Builder() {
@@ -81,23 +82,7 @@
         }
 
         public Builder additionalLDAPAttribute(final String attribute, final Object... values) {
-            additionalLDAPAttributes.add(new LinkedAttribute(attribute, values));
-            return this;
-        }
-
-        /**
-         * Creates a mapping for the named JSON attribute.
-         *
-         * @param name
-         *            The name of the JSON attribute to be mapped.
-         * @param mapper
-         *            The attribute mapper responsible for mapping the JSON
-         *            attribute to LDAP attribute(s).
-         * @return A reference to this builder.
-         */
-        public Builder attribute(final String name, final AttributeMapper mapper) {
-            rootMapper.attribute(name, mapper);
-            return this;
+            return additionalLDAPAttribute(new LinkedAttribute(ad(attribute), values));
         }
 
         public Builder baseDN(final DN dn) {
@@ -107,15 +92,13 @@
         }
 
         public Builder baseDN(final String dn) {
-            ensureNotNull(dn);
-            this.baseDN = DN.valueOf(dn);
-            return this;
+            return baseDN(DN.valueOf(dn, schema));
         }
 
         public CollectionResourceProvider build() {
             ensureNotNull(factory);
             ensureNotNull(baseDN);
-            if (rootMapper.isEmpty()) {
+            if (rootMapper == null) {
                 throw new IllegalStateException("No mappings provided");
             }
             return new LDAPCollectionResourceProvider(baseDN, rootMapper, factory, nameStrategy,
@@ -152,8 +135,8 @@
          *     "baseDN" : "ou=people,dc=example,dc=com",
          *
          *     // The mechanism which should be used for read resources during updates, must be
-         *     // one of "disabled", "useReadEntryControls", or "useSearch".
-         *     "readOnUpdatePolicy" : "useReadEntryControls",
+         *     // one of "disabled", "controls", or "search".
+         *     "readOnUpdatePolicy" : "controls",
          *
          *     // Additional LDAP attributes which should be included with entries during add (create) operations.
          *     "additionalLDAPAttributes" : [
@@ -188,18 +171,18 @@
          *     "etagAttribute" : "etag",
          *
          *     // The JSON to LDAP attribute mappings.
-         *     "attributes" : [
+         *     "attributes" : {
          *         "schemas"     : { "constant" : [ "urn:scim:schemas:core:1.0" ] },
          *         "id"          : { "simple"   : { "ldapAttribute" : "uid", "isSingleValued" : true, "isRequired" : true, "writability" : "createOnly" } },
          *         "rev"         : { "simple"   : { "ldapAttribute" : "etag", "isSingleValued" : true, "writability" : "readOnly" } },
          *         "userName"    : { "simple"   : { "ldapAttribute" : "mail", "isSingleValued" : true, "writability" : "readOnly" } },
          *         "displayName" : { "simple"   : { "ldapAttribute" : "cn", "isSingleValued" : true, "isRequired" : true } },
-         *         "name"        : { "object"   : [
+         *         "name"        : { "object"   : {
          *             "givenName"  : { "simple"   : { "ldapAttribute" : "givenName", "isSingleValued" : true } },
          *             "familyName" : { "simple"   : { "ldapAttribute" : "sn", "isSingleValued" : true, "isRequired" : true } },
-         *         ],
+         *         },
          *         ...
-         *     ]
+         *     }
          * }
          * </pre>
          *
@@ -211,6 +194,43 @@
          */
         public Builder configureMapping(final JsonValue configuration)
                 throws IllegalArgumentException {
+            baseDN(configuration.get("baseDN").required().asString());
+
+            final JsonValue readOnUpdatePolicy = configuration.get("readOnUpdatePolicy");
+            if (!readOnUpdatePolicy.isNull()) {
+                readOnUpdatePolicy(readOnUpdatePolicy.asEnum(ReadOnUpdatePolicy.class));
+            }
+
+            for (final JsonValue v : configuration.get("additionalLDAPAttributes")) {
+                final String type = v.get("type").required().asString();
+                final List<Object> values = v.get("values").required().asList();
+                additionalLDAPAttribute(new LinkedAttribute(type, values));
+            }
+
+            final JsonValue namingStrategy = configuration.get("namingStrategy");
+            if (!namingStrategy.isNull()) {
+                final String name = namingStrategy.get("strategy").required().asString();
+                if (name.equalsIgnoreCase("clientDNNaming")) {
+                    useClientDNNaming(namingStrategy.get("dnAttribute").required().asString());
+                } else if (name.equalsIgnoreCase("clientNaming")) {
+                    useClientNaming(namingStrategy.get("dnAttribute").required().asString(),
+                            namingStrategy.get("idAttribute").required().asString());
+                } else if (name.equalsIgnoreCase("serverNaming")) {
+                    useServerNaming(namingStrategy.get("dnAttribute").required().asString(),
+                            namingStrategy.get("idAttribute").required().asString());
+                } else {
+                    throw new IllegalArgumentException(
+                            "Illegal naming strategy. Must be one of: clientDNNaming, clientNaming, or serverNaming");
+                }
+            }
+
+            final JsonValue etagAttribute = configuration.get("etagAttribute");
+            if (!etagAttribute.isNull()) {
+                useEtagAttribute(etagAttribute.asString());
+            }
+
+            mapper(configureObjectMapper(configuration.get("attributes").required()));
+
             return this;
         }
 
@@ -220,6 +240,11 @@
             return this;
         }
 
+        public Builder mapper(final AttributeMapper mapper) {
+            this.rootMapper = mapper;
+            return this;
+        }
+
         /**
          * Sets the policy which should be used in order to read an entry before
          * it is deleted, or after it is added or modified.
@@ -254,7 +279,7 @@
         }
 
         public Builder useClientDNNaming(final String attribute) {
-            return useClientDNNaming(Schema.getDefaultSchema().getAttributeType(attribute));
+            return useClientDNNaming(at(attribute));
         }
 
         public Builder useClientNaming(final AttributeType dnAttribute,
@@ -264,8 +289,7 @@
         }
 
         public Builder useClientNaming(final String dnAttribute, final String idAttribute) {
-            return useClientNaming(Schema.getDefaultSchema().getAttributeType(dnAttribute),
-                    AttributeDescription.valueOf(idAttribute));
+            return useClientNaming(at(dnAttribute), ad(idAttribute));
         }
 
         public Builder useEtagAttribute() {
@@ -278,7 +302,7 @@
         }
 
         public Builder useEtagAttribute(final String attribute) {
-            return useEtagAttribute(AttributeDescription.valueOf(attribute));
+            return useEtagAttribute(ad(attribute));
         }
 
         public Builder useServerEntryUUIDNaming(final AttributeType dnAttribute) {
@@ -287,7 +311,7 @@
         }
 
         public Builder useServerEntryUUIDNaming(final String dnAttribute) {
-            return useServerEntryUUIDNaming(Schema.getDefaultSchema().getAttributeType(dnAttribute));
+            return useServerEntryUUIDNaming(at(dnAttribute));
         }
 
         public Builder useServerNaming(final AttributeType dnAttribute,
@@ -297,8 +321,72 @@
         }
 
         public Builder useServerNaming(final String dnAttribute, final String idAttribute) {
-            return useServerNaming(Schema.getDefaultSchema().getAttributeType(dnAttribute),
-                    AttributeDescription.valueOf(idAttribute));
+            return useServerNaming(at(dnAttribute), ad(idAttribute));
+        }
+
+        private AttributeDescription ad(final String attribute) {
+            return AttributeDescription.valueOf(attribute, schema);
+        }
+
+        private AttributeType at(final String attribute) {
+            return schema.getAttributeType(attribute);
+        }
+
+        private AttributeMapper configureMapper(final JsonValue mapper) {
+            if (mapper.isDefined("constant")) {
+                return constant(mapper.get("constant").getObject());
+            } else if (mapper.isDefined("simple")) {
+                final JsonValue config = mapper.get("simple");
+                final SimpleAttributeMapper s =
+                        simple(ad(config.get("ldapAttribute").required().asString()));
+                if (config.isDefined("defaultJSONValue")) {
+                    s.defaultJSONValue(config.get("defaultJSONValue").getObject());
+                }
+                if (config.isDefined("defaultLDAPValue")) {
+                    s.defaultLDAPValue(config.get("defaultLDAPValue").getObject());
+                }
+                if (config.get("isBinary").defaultTo(false).asBoolean()) {
+                    s.isBinary();
+                }
+                if (config.get("isRequired").defaultTo(false).asBoolean()) {
+                    s.isRequired();
+                }
+                if (config.get("isSingleValued").defaultTo(false).asBoolean()) {
+                    s.isSingleValued();
+                }
+                if (config.isDefined("writability")) {
+                    final String writability = config.get("writability").asString();
+                    if (writability.equalsIgnoreCase("readOnly")) {
+                        s.writability(WritabilityPolicy.READ_ONLY);
+                    } else if (writability.equalsIgnoreCase("readOnlyDiscardWrites")) {
+                        s.writability(WritabilityPolicy.READ_ONLY_DISCARD_WRITES);
+                    } else if (writability.equalsIgnoreCase("createOnly")) {
+                        s.writability(WritabilityPolicy.CREATE_ONLY);
+                    } else if (writability.equalsIgnoreCase("createOnlyDiscardWrites")) {
+                        s.writability(WritabilityPolicy.CREATE_ONLY_DISCARD_WRITES);
+                    } else if (writability.equalsIgnoreCase("readWrite")) {
+                        s.writability(WritabilityPolicy.READ_WRITE);
+                    } else {
+                        throw new JsonValueException(mapper,
+                                "Illegal writability: must be one of readOnly, readOnlyDiscardWrites, "
+                                        + "createOnly, createOnlyDiscardWrites, or readWrite");
+                    }
+                }
+                return s;
+            } else if (mapper.isDefined("object")) {
+                return configureObjectMapper(mapper.get("object"));
+            } else {
+                throw new JsonValueException(mapper,
+                        "Illegal mapping: must contain constant, simple, or object");
+            }
+        }
+
+        private ObjectAttributeMapper configureObjectMapper(final JsonValue mapper) {
+            final ObjectAttributeMapper object = object();
+            for (final String attribute : mapper.keys()) {
+                object.attribute(attribute, configureMapper(mapper.get(attribute)));
+            }
+            return object;
         }
     }
 

--
Gitblit v1.10.0