From 19536840ae2c706f4ad9ff4742e52dfc5f6ebea7 Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Sat, 23 Mar 2013 01:46:37 +0000
Subject: [PATCH] Partial fix for OPENDJ-694: Implement HTTP BASIC authentication

---
 opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Rest2LDAP.java |  205 ++++++++++++++++++--------------------------------
 1 files changed, 74 insertions(+), 131 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 9a7bed6..2ad567f 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
@@ -35,20 +35,29 @@
 import org.forgerock.json.resource.BadRequestException;
 import org.forgerock.json.resource.CollectionResourceProvider;
 import org.forgerock.json.resource.ResourceException;
+import org.forgerock.opendj.ldap.AssertionFailureException;
 import org.forgerock.opendj.ldap.Attribute;
 import org.forgerock.opendj.ldap.AttributeDescription;
+import org.forgerock.opendj.ldap.AuthenticationException;
+import org.forgerock.opendj.ldap.AuthorizationException;
 import org.forgerock.opendj.ldap.ByteString;
+import org.forgerock.opendj.ldap.ConnectionException;
 import org.forgerock.opendj.ldap.ConnectionFactory;
 import org.forgerock.opendj.ldap.Connections;
 import org.forgerock.opendj.ldap.DN;
 import org.forgerock.opendj.ldap.Entry;
+import org.forgerock.opendj.ldap.EntryNotFoundException;
+import org.forgerock.opendj.ldap.ErrorResultException;
 import org.forgerock.opendj.ldap.FailoverLoadBalancingAlgorithm;
 import org.forgerock.opendj.ldap.Filter;
 import org.forgerock.opendj.ldap.LDAPConnectionFactory;
 import org.forgerock.opendj.ldap.LinkedAttribute;
+import org.forgerock.opendj.ldap.MultipleEntriesFoundException;
 import org.forgerock.opendj.ldap.RDN;
+import org.forgerock.opendj.ldap.ResultCode;
 import org.forgerock.opendj.ldap.RoundRobinLoadBalancingAlgorithm;
 import org.forgerock.opendj.ldap.SearchScope;
+import org.forgerock.opendj.ldap.TimeoutResultException;
 import org.forgerock.opendj.ldap.requests.BindRequest;
 import org.forgerock.opendj.ldap.requests.Requests;
 import org.forgerock.opendj.ldap.requests.SearchRequest;
@@ -67,11 +76,11 @@
      */
     public static final class Builder {
         private final List<Attribute> additionalLDAPAttributes = new LinkedList<Attribute>();
+        private AuthorizationPolicy authzPolicy = AuthorizationPolicy.NONE;
         private DN baseDN; // TODO: support template variables.
         private ConnectionFactory factory;
         private MVCCStrategy mvccStrategy;
         private NameStrategy nameStrategy;
-        private AuthorizationPolicy authzPolicy = AuthorizationPolicy.NONE;
         private AuthzIdTemplate proxiedAuthzTemplate;
         private ReadOnUpdatePolicy readOnUpdatePolicy = CONTROLS;
         private AttributeMapper rootMapper;
@@ -91,6 +100,11 @@
             return additionalLDAPAttribute(new LinkedAttribute(ad(attribute), values));
         }
 
+        public Builder authorizationPolicy(final AuthorizationPolicy policy) {
+            this.authzPolicy = ensureNotNull(policy);
+            return this;
+        }
+
         public Builder baseDN(final DN dn) {
             ensureNotNull(dn);
             this.baseDN = dn;
@@ -135,74 +149,9 @@
         /**
          * Configures the JSON to LDAP mapping using the provided JSON
          * configuration. The caller is still required to set the connection
-         * factory. The configuration should look like this, excluding the
-         * C-like comments:
-         *
-         * <pre>
-         * {
-         *     // The base DN beneath which LDAP entries are to be found.
-         *     "baseDN" : "ou=people,dc=example,dc=com",
-         *
-         *     // The mechanism which should be used for read resources during updates, must be
-         *     // one of "disabled", "controls", or "search".
-         *     "readOnUpdatePolicy" : "controls",
-         *
-         *     // Additional LDAP attributes which should be included with entries during add (create) operations.
-         *     "additionalLDAPAttributes" : [
-         *         {
-         *             "type" : "objectClass",
-         *             "values" : [
-         *                 "top",
-         *                 "person"
-         *             ]
-         *         }
-         *     ],
-         *
-         *     // The strategy which should be used for deriving LDAP entry names from JSON resources.
-         *     "namingStrategy" : {
-         *         // Option 1) the RDN and resource ID are both derived from a single user attribute in the entry.
-         *         "strategy" : "clientDNNaming",
-         *         "dnAttribute" : "uid"
-         *
-         *         // Option 2) the RDN and resource ID are derived from separate user attributes in the entry.
-         *         "strategy" : "clientNaming",
-         *         "dnAttribute" : "uid",
-         *         "idAttribute" : "mail"
-         *
-         *         // Option 3) the RDN and is derived from a user attribute and the resource ID from an operational
-         *         //           attribute in the entry.
-         *         "strategy" : "serverNaming",
-         *         "dnAttribute" : "uid",
-         *         "idAttribute" : "entryUUID"
-         *     },
-         *
-         *     // The attribute which will be used for performing MVCC.
-         *     "etagAttribute" : "etag",
-         *
-         *     // The JSON to LDAP attribute mappings.
-         *     "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"   : {
-         *             "givenName"  : { "simple"   : { "ldapAttribute" : "givenName", "isSingleValued" : true } },
-         *             "familyName" : { "simple"   : { "ldapAttribute" : "sn", "isSingleValued" : true, "isRequired" : true } },
-         *         },
-         *         "manager"     : { "reference" : {
-         *             "ldapAttribute" : "manager",
-         *             "baseDN"        : "ou=people,dc=example,dc=com",
-         *             "mapper"        : { "object" : {
-         *                 "id"          : { "simple"   : { "ldapAttribute" : "uid", "isSingleValued" : true } },
-         *                 "displayName" : { "simple"   : { "ldapAttribute" : "cn", "isSingleValued" : true } }
-         *             } }
-         *         },
-         *         ...
-         *     }
-         * }
-         * </pre>
-         *
+         * factory. See the sample configuration file for a detailed description
+         * of its content.
+         * 
          * @param configuration
          *            The JSON configuration.
          * @return A reference to this builder.
@@ -250,7 +199,7 @@
             return this;
         }
 
-        public Builder connectionFactory(final ConnectionFactory factory) {
+        public Builder ldapConnectionFactory(final ConnectionFactory factory) {
             this.factory = factory;
             return this;
         }
@@ -260,10 +209,15 @@
             return this;
         }
 
+        public Builder proxyAuthzIdTemplate(final String template) {
+            this.proxiedAuthzTemplate = template != null ? new AuthzIdTemplate(template) : null;
+            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.
-         *
+         * 
          * @param policy
          *            The policy which should be used in order to read an entry
          *            before it is deleted, or after it is added or modified.
@@ -277,7 +231,7 @@
         /**
          * Sets the schema which should be used when attribute types and
          * controls.
-         *
+         * 
          * @param schema
          *            The schema which should be used when attribute types and
          *            controls.
@@ -320,16 +274,6 @@
             return useEtagAttribute(ad(attribute));
         }
 
-        public Builder authorizationPolicy(final AuthorizationPolicy policy) {
-            this.authzPolicy = ensureNotNull(policy);
-            return this;
-        }
-
-        public Builder proxyAuthzIdTemplate(final String template) {
-            this.proxiedAuthzTemplate = template != null ? new AuthzIdTemplate(template) : null;
-            return this;
-        }
-
         public Builder useServerEntryUUIDNaming(final AttributeType dnAttribute) {
             return useServerNaming(dnAttribute, AttributeDescription
                     .create(getEntryUUIDAttributeType()));
@@ -551,61 +495,60 @@
 
     }
 
+    /**
+     * Adapts a {@code Throwable} to a {@code ResourceException}. If the
+     * {@code Throwable} is an LDAP {@code ErrorResultException} then an
+     * appropriate {@code ResourceException} is returned, otherwise an
+     * {@code InternalServerErrorException} is returned.
+     * 
+     * @param t
+     *            The {@code Throwable} to be converted.
+     * @return The equivalent resource exception.
+     */
+    public static ResourceException asResourceException(final Throwable t) {
+        int resourceResultCode;
+        try {
+            throw t;
+        } catch (final ResourceException e) {
+            return e;
+        } catch (final AssertionFailureException e) {
+            resourceResultCode = ResourceException.VERSION_MISMATCH;
+        } catch (final AuthenticationException e) {
+            resourceResultCode = 401;
+        } catch (final AuthorizationException e) {
+            resourceResultCode = ResourceException.FORBIDDEN;
+        } catch (final ConnectionException e) {
+            resourceResultCode = ResourceException.UNAVAILABLE;
+        } catch (final EntryNotFoundException e) {
+            resourceResultCode = ResourceException.NOT_FOUND;
+        } catch (final MultipleEntriesFoundException e) {
+            resourceResultCode = ResourceException.INTERNAL_ERROR;
+        } catch (final TimeoutResultException e) {
+            resourceResultCode = 408;
+        } catch (final ErrorResultException e) {
+            final ResultCode rc = e.getResult().getResultCode();
+            if (rc.equals(ResultCode.ADMIN_LIMIT_EXCEEDED)) {
+                resourceResultCode = 413; // Request Entity Too Large
+            } else if (rc.equals(ResultCode.SIZE_LIMIT_EXCEEDED)) {
+                resourceResultCode = 413; // Request Entity Too Large
+            } else {
+                resourceResultCode = ResourceException.INTERNAL_ERROR;
+            }
+        } catch (final Throwable tmp) {
+            resourceResultCode = ResourceException.INTERNAL_ERROR;
+        }
+        return ResourceException.getException(resourceResultCode, t.getMessage(), t);
+    }
+
     public static Builder builder() {
         return new Builder();
     }
 
     /**
      * Creates a new connection factory using the named configuration in the
-     * provided JSON list of factory configurations. Excluding the C-like
-     * comments, the configuration should look like this:
-     *
-     * <pre>
-     * {
-     *     // A default pool of servers using no authentication.
-     *     "default" : {
-     *         // The primary data center, must contain at least one LDAP server.
-     *         "primaryLDAPServers" : [
-     *             {
-     *                 "hostname" : "host1.example.com",
-     *                 "port"     : 389
-     *             },
-     *             {
-     *                 "hostname" : "host2.example.com",
-     *                 "port"     : 389
-     *             },
-     *         ],
-     *
-     *         // The optional secondary (fail-over) data center.
-     *         "secondaryLDAPServers" : [
-     *             {
-     *                 "hostname" : "host3.example.com",
-     *                 "port"     : 389
-     *             },
-     *             {
-     *                 "hostname" : "host4.example.com",
-     *                 "port"     : 389
-     *             },
-     *         ],
-     *
-     *         // Connection pool configuration.
-     *         "connectionPoolSize"       : 10,
-     *         "heartBeatIntervalSeconds" : 30
-     *     },
-     *
-     *     // The same pool of servers except authenticated as cn=directory manager.
-     *     "root" : {
-     *         "inheritFrom"    : "default",
-     *         "authentication" : {
-     *             "simple" : {
-     *                 "bindDN"       : "cn=directory manager",
-     *                 "bindPassword" : "password"
-     *             }
-     *         }
-     *     }
-     * }
-     * </pre>
-     *
+     * provided JSON list of factory configurations. See the sample
+     * configuration file for a detailed description of its content.
+     * 
      * @param configuration
      *            The JSON configuration.
      * @param name

--
Gitblit v1.10.0