From 7d312ae9d0a69c9b6a36fb5002a5923b8e3492ff Mon Sep 17 00:00:00 2001
From: Yannick Lecaillez <yannick.lecaillez@forgerock.com>
Date: Mon, 23 May 2016 11:19:35 +0000
Subject: [PATCH] Rest2ldap: let the last authorization filter formulate an implementation specific error message.

---
 opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/authz/Authorizations.java      |   16 ++++++++
 opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Rest2LDAPHttpApplication.java  |   35 ++++++-----------
 opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/authz/AuthorizationFilter.java |   63 +++++++++++++++++++++++++++++++
 3 files changed, 91 insertions(+), 23 deletions(-)

diff --git a/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Rest2LDAPHttpApplication.java b/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Rest2LDAPHttpApplication.java
index 986463d..6362c6a 100644
--- a/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Rest2LDAPHttpApplication.java
+++ b/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Rest2LDAPHttpApplication.java
@@ -30,7 +30,9 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -42,9 +44,6 @@
 import org.forgerock.http.handler.Handlers;
 import org.forgerock.http.io.Buffer;
 import org.forgerock.http.protocol.Headers;
-import org.forgerock.http.protocol.Request;
-import org.forgerock.http.protocol.Response;
-import org.forgerock.http.protocol.Status;
 import org.forgerock.json.JsonValue;
 import org.forgerock.json.resource.RequestHandler;
 import org.forgerock.json.resource.Router;
@@ -56,13 +55,11 @@
 import org.forgerock.opendj.ldap.schema.Schema;
 import org.forgerock.opendj.rest2ldap.authz.AuthenticationStrategy;
 import org.forgerock.opendj.rest2ldap.authz.ConditionalFilters.ConditionalFilter;
-import org.forgerock.services.context.Context;
 import org.forgerock.services.context.SecurityContext;
 import org.forgerock.util.Factory;
 import org.forgerock.util.Function;
 import org.forgerock.util.Pair;
 import org.forgerock.util.promise.NeverThrowsException;
-import org.forgerock.util.promise.Promise;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -113,7 +110,7 @@
             configureConnectionFactories(configuration.get("ldapConnectionFactories"));
             return Handlers.chainOf(
                     CrestHttp.newHttpHandler(configureRest2Ldap(configuration)),
-                    newAuthorizationFilter(configuration.get("authorization").required()));
+                    buildAuthorizationFilter(configuration.get("authorization").required()));
         } catch (final Exception e) {
             // TODO i18n, once supported in opendj-rest2ldap
             final String errorMsg = "Unable to start Rest2Ldap Http Application";
@@ -160,24 +157,16 @@
         connectionFactories.clear();
     }
 
-    private Filter newAuthorizationFilter(final JsonValue config) {
+    private Filter buildAuthorizationFilter(final JsonValue config) {
         final Set<Policy> policies = config.get("policies").as(setOf(enumConstant(Policy.class)));
-        final ConditionalFilter anonymous =
-                policies.contains(Policy.ANONYMOUS) ? buildAnonymousFilter(config.get("anonymous")) : NEVER_APPLICABLE;
-        final ConditionalFilter basic =
-                policies.contains(Policy.BASIC) ? buildBasicFilter(config.get("basic")) : NEVER_APPLICABLE;
-        return new Filter() {
-            @Override
-            public Promise<Response, NeverThrowsException> filter(Context context, Request request, Handler next) {
-                if (basic.getCondition().canApplyFilter(context, request)) {
-                    return basic.getFilter().filter(context, request, next);
-                }
-                if (anonymous.getCondition().canApplyFilter(context, request)) {
-                    return anonymous.getFilter().filter(context, request, next);
-                }
-                return Response.newResponsePromise(new Response(Status.FORBIDDEN));
-            }
-        };
+        final List<ConditionalFilter> filters = new ArrayList<>(policies.size());
+        if (policies.contains(Policy.BASIC)) {
+            filters.add(buildBasicFilter(config.get("basic")));
+        }
+        if (policies.contains(Policy.ANONYMOUS)) {
+            filters.add(buildAnonymousFilter(config.get("anonymous")));
+        }
+        return newAuthorizationFilter(filters);
     }
 
     /**
diff --git a/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/authz/AuthorizationFilter.java b/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/authz/AuthorizationFilter.java
new file mode 100644
index 0000000..614aa61
--- /dev/null
+++ b/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/authz/AuthorizationFilter.java
@@ -0,0 +1,63 @@
+/*
+ * 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 2016 ForgeRock AS.
+ */
+package org.forgerock.opendj.rest2ldap.authz;
+
+import static org.forgerock.util.Reject.checkNotNull;
+
+import java.util.List;
+
+import org.forgerock.http.Filter;
+import org.forgerock.http.Handler;
+import org.forgerock.http.protocol.Request;
+import org.forgerock.http.protocol.Response;
+import org.forgerock.http.protocol.Status;
+import org.forgerock.opendj.rest2ldap.AuthenticatedConnectionContext;
+import org.forgerock.opendj.rest2ldap.authz.ConditionalFilters.ConditionalFilter;
+import org.forgerock.services.context.Context;
+import org.forgerock.util.promise.NeverThrowsException;
+import org.forgerock.util.promise.Promise;
+
+/**
+ * This {@link Filter} is in charge of injecting an {@link AuthenticatedConnectionContext}. It tries each of the
+ * provided filters until one can apply. If no filter can be applied, the last filter in the list will be applied
+ * allowing it to formulate a valid, implementation specific, error response.
+ */
+final class AuthorizationFilter implements Filter {
+    private static final Filter FORBIDDEN = new Filter() {
+        @Override
+        public Promise<Response, NeverThrowsException> filter(Context context, Request request, Handler next) {
+            return Response.newResponsePromise(new Response(Status.FORBIDDEN));
+        }
+    };
+    private final List<ConditionalFilter> filters;
+
+    AuthorizationFilter(List<ConditionalFilter> filters) {
+        this.filters = checkNotNull(filters, "filters cannot be null");
+    }
+
+    @Override
+    public Promise<Response, NeverThrowsException> filter(Context context, Request request, Handler next) {
+        Filter lastFilter = FORBIDDEN;
+        for (ConditionalFilter filter : filters) {
+            if (filter.getCondition().canApplyFilter(context, request)) {
+                return filter.getFilter().filter(context, request, next);
+            }
+            lastFilter = filter.getFilter();
+        }
+        // Let the last filter in the chain formulate a valid error response.
+        return lastFilter.filter(context, request, next);
+    }
+}
diff --git a/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/authz/Authorizations.java b/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/authz/Authorizations.java
index 471c03e..96a7d36 100644
--- a/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/authz/Authorizations.java
+++ b/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/authz/Authorizations.java
@@ -18,6 +18,8 @@
 import static org.forgerock.opendj.rest2ldap.authz.ConditionalFilters.asConditionalFilter;
 import static org.forgerock.opendj.rest2ldap.authz.ConditionalFilters.newConditionalFilter;
 
+import java.util.List;
+
 import org.forgerock.http.Filter;
 import org.forgerock.http.protocol.Headers;
 import org.forgerock.http.protocol.Request;
@@ -42,6 +44,20 @@
     }
 
     /**
+     * Creates a new {@link Filter} in charge of injecting an {@link AuthenticatedConnectionContext}. This
+     * {@link Filter} tries each of the provided filters until one can apply. If no filter can be applied, the last
+     * filter in the list will be applied allowing it to formulate a valid, implementation specific, error response.
+     *
+     * @param filters
+     *            List of authorization {@link ConditionalFilters} to try. If empty, the returned filter will always
+     *            respond with 403 Forbidden.
+     * @return A new authorization {@link Filter}
+     */
+    public static Filter newAuthorizationFilter(List<ConditionalFilter> filters) {
+        return new AuthorizationFilter(filters);
+    }
+
+    /**
      * Creates a new {@link ConditionalFilter} performing authentication. If authentication succeed, it injects a
      * {@link SecurityContext} with the authenticationId provided by the user. Otherwise, returns a HTTP 401 -
      * Unauthorized response. The condition of this {@link ConditionalFilter} will return true if the supplied requests

--
Gitblit v1.10.0