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); final List<ConditionalFilter> filters = new ArrayList<>(policies.size()); if (policies.contains(Policy.BASIC)) { filters.add(buildBasicFilter(config.get("basic"))); } if (anonymous.getCondition().canApplyFilter(context, request)) { return anonymous.getFilter().filter(context, request, next); if (policies.contains(Policy.ANONYMOUS)) { filters.add(buildAnonymousFilter(config.get("anonymous"))); } return Response.newResponsePromise(new Response(Status.FORBIDDEN)); } }; return newAuthorizationFilter(filters); } /** opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/authz/AuthorizationFilter.java
New file @@ -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); } } 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