From f3612b1aa023f12906a71cb12c6ca1cae12aa5dd Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Fri, 22 Mar 2013 13:01:05 +0000
Subject: [PATCH] Partial fix for OPENDJ-694: Implement HTTP BASIC authentication

---
 opendj3/opendj-rest2ldap-servlet/src/main/webapp/opendj-rest2ldap-servlet.json                                                |   23 ++++++-
 opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Context.java                                            |    8 ++
 opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Rest2LDAP.java                                          |   35 +++++++++--
 opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/AuthorizationPolicy.java                                |   42 ++++++++++++++
 opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Config.java                                             |   30 +++++----
 opendj3/opendj-rest2ldap-servlet/src/main/java/org/forgerock/opendj/rest2ldap/servlet/Rest2LDAPConnectionFactoryProvider.java |   11 +++
 6 files changed, 120 insertions(+), 29 deletions(-)

diff --git a/opendj3/opendj-rest2ldap-servlet/src/main/java/org/forgerock/opendj/rest2ldap/servlet/Rest2LDAPConnectionFactoryProvider.java b/opendj3/opendj-rest2ldap-servlet/src/main/java/org/forgerock/opendj/rest2ldap/servlet/Rest2LDAPConnectionFactoryProvider.java
index 011afac..782bacd 100644
--- a/opendj3/opendj-rest2ldap-servlet/src/main/java/org/forgerock/opendj/rest2ldap/servlet/Rest2LDAPConnectionFactoryProvider.java
+++ b/opendj3/opendj-rest2ldap-servlet/src/main/java/org/forgerock/opendj/rest2ldap/servlet/Rest2LDAPConnectionFactoryProvider.java
@@ -28,6 +28,7 @@
 import org.forgerock.json.resource.ConnectionFactory;
 import org.forgerock.json.resource.Resources;
 import org.forgerock.json.resource.Router;
+import org.forgerock.opendj.rest2ldap.AuthorizationPolicy;
 import org.forgerock.opendj.rest2ldap.Rest2LDAP;
 import org.forgerock.opendj.rest2ldap.Rest2LDAP.Builder;
 
@@ -77,8 +78,13 @@
             final JsonValue configuration = new JsonValue(content);
 
             // Parse the authorization configuration.
+            final AuthorizationPolicy authzPolicy =
+                    configuration.get("servlet").get("authorizationPolicy").required().asEnum(
+                            AuthorizationPolicy.class);
             final String proxyAuthzTemplate =
                     configuration.get("servlet").get("proxyAuthzIdTemplate").asString();
+
+            // Parse the connection factory if present.
             final String ldapFactoryName =
                     configuration.get("servlet").get("ldapConnectionFactory").asString();
             final org.forgerock.opendj.ldap.ConnectionFactory ldapFactory;
@@ -96,8 +102,9 @@
             for (final String mappingUrl : mappings.keys()) {
                 final JsonValue mapping = mappings.get(mappingUrl);
                 final CollectionResourceProvider provider =
-                        Rest2LDAP.builder().connectionFactory(ldapFactory).useProxiedAuthorization(
-                                proxyAuthzTemplate).configureMapping(mapping).build();
+                        Rest2LDAP.builder().connectionFactory(ldapFactory).authorizationPolicy(
+                                authzPolicy).proxyAuthzIdTemplate(proxyAuthzTemplate)
+                                .configureMapping(mapping).build();
                 router.addRoute(mappingUrl, provider);
             }
             return Resources.newInternalConnectionFactory(router);
diff --git a/opendj3/opendj-rest2ldap-servlet/src/main/webapp/opendj-rest2ldap-servlet.json b/opendj3/opendj-rest2ldap-servlet/src/main/webapp/opendj-rest2ldap-servlet.json
index fe6c9d4..14645cc 100644
--- a/opendj3/opendj-rest2ldap-servlet/src/main/webapp/opendj-rest2ldap-servlet.json
+++ b/opendj3/opendj-rest2ldap-servlet/src/main/webapp/opendj-rest2ldap-servlet.json
@@ -100,19 +100,32 @@
     "servlet" : {
         // The connection factory which will be used for performing LDAP
         // operations. Pre-authenticated connections passed through from the
-        // authentication filter see "reuseAuthenticatedConnection") will be
+        // authentication filter (see "reuseAuthenticatedConnection") will be
         // used in preference to this factory. Specifically, a connection
         // factory does not need to be configured if a connection will always
         // be passed on from the filter, which may not always be the case
         // if the filter is configured to use HTTP sessions.
         "ldapConnectionFactory" : "root",
         
-        // The AuthzID template which will be used for proxied authorization. If
-        // no template is specified then proxied authorization will be disabled.
+        // Specifies how LDAP authorization should be performed. The method
+        // must be one of:
+        //
+        // "none"        - use connections acquired from the LDAP connection
+        //                 factory. Don't use proxied authorization, and don't
+        //                 use cached pre-authenticated connections,
+        // "reuse"       - use the connection obtained during LDAP
+        //                 authentication. If no connection was passed through
+        //                 the authorization will fail,
+        // "proxy"       - use proxied authorization with an authorization ID
+        //                 derived from the "proxyAuthzIdTemplate". Proxied
+        //                 authorization will only be used if there is no
+        //                 pre-authenticated connection available.
+        "authorizationPolicy" : "none",
+        
+        // The AuthzID template which will be used for proxied authorization.
         // The template should contain fields which are expected to be found in
         // the security context create during authentication, e.g. "dn" and "id".
-        
-        // "proxyAuthzIdTemplate" : "dn:{dn}",
+        "proxyAuthzIdTemplate" : "dn:{dn}",
         
         // The REST APIs and their LDAP attribute mappings.
         "mappings" : {
diff --git a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/AuthorizationPolicy.java b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/AuthorizationPolicy.java
new file mode 100644
index 0000000..cb7d0fb
--- /dev/null
+++ b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/AuthorizationPolicy.java
@@ -0,0 +1,42 @@
+/*
+ * 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 copyright [year] [name of copyright owner]".
+ *
+ * Copyright 2013 ForgeRock AS.
+ */
+
+package org.forgerock.opendj.rest2ldap;
+
+/**
+ * The policy which should be for performing authorization.
+ */
+public enum AuthorizationPolicy {
+    /**
+     * Use connections acquired from the LDAP connection factory. Don't use
+     * proxied authorization, and don't use cached pre-authenticated
+     * connections.
+     */
+    NONE,
+
+    /**
+     * Use the connection obtained during LDAP authentication. If no connection
+     * was passed through the authorization will fail.
+     */
+    REUSE,
+
+    /**
+     * Use proxied authorization with an authorization ID derived from the
+     * proxied authorization ID template. Proxied authorization will only be
+     * used if there is no pre-authenticated connection available.
+     */
+    PROXY;
+}
diff --git a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Config.java b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Config.java
index f8f2c54..cbabe03 100644
--- a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Config.java
+++ b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Config.java
@@ -25,14 +25,17 @@
 final class Config {
     private final ConnectionFactory factory;
     private final DecodeOptions options;
+    private final AuthorizationPolicy authzPolicy;
     private final AuthzIdTemplate proxiedAuthzTemplate;
     private final ReadOnUpdatePolicy readOnUpdatePolicy;
     private final Schema schema;
 
     Config(final ConnectionFactory factory, final ReadOnUpdatePolicy readOnUpdatePolicy,
-            final AuthzIdTemplate proxiedAuthzTemplate, final Schema schema) {
+            final AuthorizationPolicy authzPolicy, final AuthzIdTemplate proxiedAuthzTemplate,
+            final Schema schema) {
         this.factory = factory;
         this.readOnUpdatePolicy = readOnUpdatePolicy;
+        this.authzPolicy = authzPolicy;
         this.proxiedAuthzTemplate = proxiedAuthzTemplate;
         this.schema = schema;
         this.options = new DecodeOptions().setSchema(schema);
@@ -61,12 +64,22 @@
     }
 
     /**
+     * Returns the authorization policy which should be used for performing LDAP
+     * operations.
+     *
+     * @return The authorization policy which should be used for performing LDAP
+     *         operations.
+     */
+    AuthorizationPolicy getAuthorizationPolicy() {
+        return authzPolicy;
+    }
+
+    /**
      * Returns the authorization ID template which should be used when proxied
      * authorization is enabled.
      *
      * @return The authorization ID template which should be used when proxied
-     *         authorization is enabled, or {@code null} if proxied
-     *         authorization is disabled.
+     *         authorization is enabled.
      */
     AuthzIdTemplate getProxiedAuthorizationTemplate() {
         return proxiedAuthzTemplate;
@@ -93,15 +106,4 @@
     Schema schema() {
         return schema;
     }
-
-    /**
-     * Returns {@code true} if the proxied authorization should be used for
-     * authorizing LDAP requests.
-     *
-     * @return {@code true} if the proxied authorization should be used for
-     *         authorizing LDAP requests.
-     */
-    boolean useProxiedAuthorization() {
-        return proxiedAuthzTemplate != null;
-    }
 }
diff --git a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Context.java b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Context.java
index 4136956..f6c9579 100644
--- a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Context.java
+++ b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Context.java
@@ -205,7 +205,10 @@
     Context(final Config config, final ServerContext context) {
         this.config = config;
         this.context = context;
-        if (context.containsContext(AuthenticatedConnectionContext.class)) {
+
+        // Re-use the pre-authenticated connection if available and the authorization policy allows.
+        if (config.getAuthorizationPolicy() != AuthorizationPolicy.NONE
+                && context.containsContext(AuthenticatedConnectionContext.class)) {
             final Connection connection =
                     context.asContext(AuthenticatedConnectionContext.class).getConnection();
             this.preAuthenticatedConnection = connection != null ? wrap(connection) : null;
@@ -265,7 +268,8 @@
          * cached connection since cached connections are supposed to have been
          * pre-authenticated and therefore do not require proxied authorization.
          */
-        if (preAuthenticatedConnection == null && config.useProxiedAuthorization()) {
+        if (preAuthenticatedConnection == null
+                && config.getAuthorizationPolicy() == AuthorizationPolicy.PROXY) {
             if (context.containsContext(SecurityContext.class)) {
                 try {
                     final SecurityContext securityContext =
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 b69da29..9a7bed6 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
@@ -71,6 +71,7 @@
         private ConnectionFactory factory;
         private MVCCStrategy mvccStrategy;
         private NameStrategy nameStrategy;
+        private AuthorizationPolicy authzPolicy = AuthorizationPolicy.NONE;
         private AuthzIdTemplate proxiedAuthzTemplate;
         private ReadOnUpdatePolicy readOnUpdatePolicy = CONTROLS;
         private AttributeMapper rootMapper;
@@ -105,13 +106,30 @@
             if (rootMapper == null) {
                 throw new IllegalStateException("No mappings provided");
             }
-            if (proxiedAuthzTemplate != null && factory == null) {
-                throw new IllegalStateException(
-                        "No connection factory specified for use with proxied authorization");
+            switch (authzPolicy) {
+            case NONE:
+                if (factory == null) {
+                    throw new IllegalStateException(
+                            "A connection factory must be specified when the authorization policy is 'none'");
+                }
+                break;
+            case PROXY:
+                if (proxiedAuthzTemplate == null) {
+                    throw new IllegalStateException(
+                            "Proxied authorization enabled but no template defined");
+                }
+                if (factory == null) {
+                    throw new IllegalStateException(
+                            "A connection factory must be specified when using proxied authorization");
+                }
+                break;
+            case REUSE:
+                // This is always ok.
+                break;
             }
             return new LDAPCollectionResourceProvider(baseDN, rootMapper, nameStrategy,
-                    mvccStrategy, new Config(factory, readOnUpdatePolicy, proxiedAuthzTemplate,
-                            schema), additionalLDAPAttributes);
+                    mvccStrategy, new Config(factory, readOnUpdatePolicy, authzPolicy,
+                            proxiedAuthzTemplate, schema), additionalLDAPAttributes);
         }
 
         /**
@@ -302,7 +320,12 @@
             return useEtagAttribute(ad(attribute));
         }
 
-        public Builder useProxiedAuthorization(final String template) {
+        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;
         }

--
Gitblit v1.10.0