mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Gaetan Boismal
03.49.2016 3b15a98cb11aa68f681fa7ba0dd793cb56ba02d9
opendj-rest2ldap-servlet/pom.xml
@@ -37,6 +37,11 @@
  <dependencies>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-jdk14</artifactId>
    </dependency>
    <dependency>
      <groupId>org.forgerock.http</groupId>
      <artifactId>chf-http-servlet</artifactId>
    </dependency>
opendj-rest2ldap-servlet/src/main/webapp/WEB-INF/classes/logging.properties
New file
@@ -0,0 +1,13 @@
################################################################################################################
# Tomcat rest2ldap log file handler configuration
# By default: all rest2ldap logs will end up in ${catalina.base}/logs/rest2ldap.yyyy-MM-dd.log
# Set the log level to FINEST to enable ldap requests logging.
# See https://tomcat.apache.org/tomcat-8.0-doc/logging.html for more details about how to configure tomcat logs.
################################################################################################################
handlers = 5rest2ldap.org.apache.juli.FileHandler
5rest2ldap.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
5rest2ldap.org.apache.juli.FileHandler.prefix = rest2ldap.
org.forgerock.opendj.rest2ldap.level = ERROR
org.forgerock.opendj.rest2ldap.handlers = 5rest2ldap.org.apache.juli.FileHandler
opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ErrorLoggerFilter.java
New file
@@ -0,0 +1,69 @@
/*
 * 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;
import static org.forgerock.http.protocol.Responses.newInternalServerError;
import static org.forgerock.opendj.rest2ldap.Rest2ldapMessages.ERR_ERROR_RESPONSE;
import static org.forgerock.opendj.rest2ldap.Rest2ldapMessages.ERR_RUNTIME_EXCEPTION;
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.i18n.slf4j.LocalizedLogger;
import org.forgerock.services.context.Context;
import org.forgerock.util.Function;
import org.forgerock.util.promise.NeverThrowsException;
import org.forgerock.util.promise.Promise;
import org.forgerock.util.promise.ResultHandler;
/** Logs internal server errors and runtime exceptions once the request has been processed. */
public final class ErrorLoggerFilter implements Filter {
    private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
    @Override
    public Promise<Response, NeverThrowsException> filter(final Context context,
                                                          final Request request,
                                                          final Handler next) {
        return next.handle(context, request)
                .thenOnResult(new ResultHandler<Response>() {
                    @Override
                    public void handleResult(final Response response) {
                        final Exception cause = response.getCause();
                        final Status status = response.getStatus();
                        if (status.isServerError() && cause != null) {
                            logger.error(ERR_ERROR_RESPONSE,
                                         toLog(request),
                                         status.toString(),
                                         cause.getLocalizedMessage(),
                                         cause);
                        }
                    }
                }).thenCatchRuntimeException(new Function<RuntimeException, Response, NeverThrowsException>() {
                    @Override
                    public Response apply(final RuntimeException e) {
                        logger.error(ERR_RUNTIME_EXCEPTION, toLog(request), e.getLocalizedMessage(), e);
                        return newInternalServerError(e);
                    }
                });
    }
    private String toLog(final Request request) {
        return request.getMethod() + request.getUri();
    }
}
opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java
@@ -15,6 +15,7 @@
 */
package org.forgerock.opendj.rest2ldap;
import static org.forgerock.i18n.LocalizableMessage.raw;
import static org.forgerock.opendj.rest2ldap.Rest2ldapMessages.*;
import static java.util.Arrays.asList;
import static org.forgerock.opendj.ldap.Filter.alwaysFalse;
@@ -39,6 +40,8 @@
import java.util.List;
import java.util.Set;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.json.JsonPointer;
import org.forgerock.json.JsonValue;
import org.forgerock.json.JsonValueException;
@@ -95,6 +98,7 @@
import org.forgerock.services.context.Context;
import org.forgerock.services.context.SecurityContext;
import org.forgerock.util.AsyncFunction;
import org.forgerock.util.Function;
import org.forgerock.util.promise.ExceptionHandler;
import org.forgerock.util.promise.Promise;
@@ -109,6 +113,9 @@
 * resource collection to LDAP entries beneath a base DN.
 */
final class LDAPCollectionResourceProvider implements CollectionResourceProvider {
    private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
    /** Dummy exception used for signalling search success. */
    private static final ResourceException SUCCESS = new UncategorizedException(0, null, null);
@@ -171,7 +178,9 @@
            oldPassword = jsonContent.get("oldPassword").asString();
            newPassword = jsonContent.get("newPassword").asString();
        } catch (JsonValueException e) {
            final ResourceException ex = newBadRequestException(ERR_PASSWORD_MODIFY_REQUEST_IS_INVALID.get(), e);
            final LocalizableMessage msg = ERR_PASSWORD_MODIFY_REQUEST_IS_INVALID.get();
            final ResourceException ex = newBadRequestException(msg, e);
            logger.error(msg, e);
            return Promises.newExceptionPromise(ex);
        }
@@ -233,6 +242,7 @@
                                    request.getNewResourceId(),
                                    addRequest);
                        } catch (final ResourceException e) {
                            logger.error(raw(e.getLocalizedMessage()), e);
                            return Promises.newExceptionPromise(e);
                        }
                        if (config.readOnUpdatePolicy() == CONTROLS) {
@@ -669,7 +679,7 @@
                                        cookie = control.getCookie().toBase64String();
                                    }
                                } catch (final DecodeException e) {
                                    // FIXME: need some logging.
                                    logger.error(ERR_DECODING_CONTROL.get(e.getLocalizedMessage()), e);
                                }
                            }
                            completeIfNecessary(SUCCESS, promise);
@@ -930,7 +940,7 @@
                        }
                    }
                } catch (final DecodeException e) {
                    // FIXME: log something?
                    logger.error(ERR_DECODING_CONTROL.get(e.getLocalizedMessage()), e);
                    entry = null;
                }
                if (entry != null) {
opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Rest2LDAPHttpApplication.java
@@ -60,6 +60,8 @@
import org.forgerock.http.handler.HttpClientHandler;
import org.forgerock.http.io.Buffer;
import org.forgerock.http.protocol.Headers;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.json.JsonValue;
import org.forgerock.json.resource.RequestHandler;
import org.forgerock.json.resource.Router;
@@ -81,8 +83,6 @@
import org.forgerock.util.promise.Promise;
import org.forgerock.util.time.Duration;
import org.forgerock.util.time.TimeService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** Rest2ldap HTTP application. */
public class Rest2LDAPHttpApplication implements HttpApplication {
@@ -101,7 +101,7 @@
    private static final String CACHE_ENABLED = "enabled";
    private static final String CACHE_EXPIRATION = "cacheExpiration";
    private static final Logger LOG = LoggerFactory.getLogger(Rest2LDAPHttpApplication.class);
    private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
    /** URL to the JSON configuration file. */
    protected final URL configurationUrl;
@@ -178,12 +178,13 @@
            configureConnectionFactories(configuration.get("ldapConnectionFactories"));
            return Handlers.chainOf(
                    CrestHttp.newHttpHandler(configureRest2Ldap(configuration)),
                    new ErrorLoggerFilter(),
                    buildAuthorizationFilter(configuration.get("authorization").required()));
        } catch (final Exception e) {
            final String errorMsg = ERR_FAIL_PARSE_CONFIGURATION.get(e.getLocalizedMessage()).toString();
            LOG.error(errorMsg, e);
            final LocalizableMessage errorMsg = ERR_FAIL_PARSE_CONFIGURATION.get(e.getLocalizedMessage());
            logger.error(errorMsg, e);
            stop();
            throw new HttpApplicationException(errorMsg, e);
            throw new HttpApplicationException(errorMsg.toString(), e);
        }
    }
opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/authz/CachedReadConnectionDecorator.java
@@ -37,9 +37,13 @@
import org.forgerock.opendj.ldap.requests.SearchRequest;
import org.forgerock.opendj.ldap.responses.BindResult;
import org.forgerock.opendj.ldap.responses.ExtendedResult;
import org.forgerock.opendj.ldap.responses.Response;
import org.forgerock.opendj.ldap.responses.Result;
import org.forgerock.opendj.ldap.responses.SearchResultEntry;
import org.forgerock.opendj.ldap.responses.SearchResultReference;
import org.forgerock.util.promise.ResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * Cache entries by intercepting the result of base search requests. Entries in cache are automatically evicted if their
@@ -48,6 +52,21 @@
 */
final class CachedReadConnectionDecorator extends AbstractAsynchronousConnectionDecorator {
    private static final Logger logger = LoggerFactory.getLogger(CachedReadConnectionDecorator.class);
    private static final ResultHandler<Response> RESPONSE_LOGGER = new ResultHandler<Response>() {
        @Override
        public void handleResult(final Response response) {
            traceLog(response);
        }
    };
    private static void traceLog(final Object o) {
        if (logger.isTraceEnabled()) {
            logger.trace(o.toString());
        }
    }
    @SuppressWarnings("serial")
    private final Map<DN, CachedRead> cachedReads = new LinkedHashMap<DN, CachedRead>() {
        private static final int MAX_CACHED_ENTRIES = 32;
@@ -167,8 +186,9 @@
         * Simple brute force implementation in case the bind operation
         * modifies an entry: clear the cachedReads.
         */
        traceLog(request);
        evictAll();
        return delegate.bindAsync(request, intermediateResponseHandler);
        return delegate.bindAsync(request, intermediateResponseHandler).thenOnResult(RESPONSE_LOGGER);
    }
    private void evictAll() {
@@ -184,15 +204,17 @@
         * Simple brute force implementation in case the extended
         * operation modifies an entry: clear the cachedReads.
         */
        traceLog(request);
        evictAll();
        return delegate.extendedRequestAsync(request, intermediateResponseHandler);
        return delegate.extendedRequestAsync(request, intermediateResponseHandler).thenOnResult(RESPONSE_LOGGER);
    }
    @Override
    public LdapPromise<Result> modifyAsync(ModifyRequest request,
            IntermediateResponseHandler intermediateResponseHandler) {
        traceLog(request);
        evict(request.getName());
        return delegate.modifyAsync(request, intermediateResponseHandler);
        return delegate.modifyAsync(request, intermediateResponseHandler).thenOnResult(RESPONSE_LOGGER);
    }
    private void evict(final DN name) {
@@ -204,18 +226,20 @@
    @Override
    public LdapPromise<Result> deleteAsync(DeleteRequest request,
            IntermediateResponseHandler intermediateResponseHandler) {
        traceLog(request);
        // Simple brute force implementation: clear the cachedReads.
        if (request.containsControl(SubtreeDeleteRequestControl.OID)) {
            evictAll();
        } else {
            evict(request.getName());
        }
        return delegate.deleteAsync(request, intermediateResponseHandler);
        return delegate.deleteAsync(request, intermediateResponseHandler).thenOnResult(RESPONSE_LOGGER);
    }
    @Override
    public LdapPromise<Result> modifyDNAsync(ModifyDNRequest request,
            IntermediateResponseHandler intermediateResponseHandler) {
        traceLog(request);
        // Simple brute force implementation: clear the cachedReads.
        evictAll();
        return delegate.modifyDNAsync(request, intermediateResponseHandler);
@@ -229,8 +253,10 @@
         * object), or if the search request passed in an intermediate
         * response handler.
         */
        traceLog(request);
        if (!request.getScope().equals(SearchScope.BASE_OBJECT) || intermediateResponseHandler != null) {
            return delegate.searchAsync(request, intermediateResponseHandler, entryHandler);
            return delegate.searchAsync(request, intermediateResponseHandler, entryHandler)
                           .thenOnResult(RESPONSE_LOGGER);
        }
        // This is a read request and a candidate for caching.
@@ -252,7 +278,7 @@
                    .searchAsync(request, intermediateResponseHandler, pendingCachedRead)
                    .thenOnResult(pendingCachedRead).thenOnException(pendingCachedRead);
            pendingCachedRead.setPromise(promise);
            return promise;
            return promise.thenOnResult(RESPONSE_LOGGER);
        }
    }
}
opendj-server-legacy/src/main/java/org/opends/server/protocols/http/HTTPConnectionHandler.java
@@ -58,6 +58,7 @@
import org.forgerock.opendj.config.server.ConfigurationChangeListener;
import org.forgerock.opendj.ldap.DN;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.rest2ldap.ErrorLoggerFilter;
import org.forgerock.opendj.server.config.server.ConnectionHandlerCfg;
import org.forgerock.opendj.server.config.server.HTTPConnectionHandlerCfg;
import org.forgerock.services.context.Context;
@@ -909,7 +910,8 @@
    {
      return Handlers.chainOf(
          serverContext.getHTTPRouter(),
          new HttpLogFilter(serverContext),
          new HttpAccessLogFilter(serverContext),
          new ErrorLoggerFilter(),
          new ExecuteInWorkerThreadFilter(),
          new AllowDenyFilter(currentConfig.getDeniedClient(), currentConfig.getAllowedClient()),
          new CommonAuditTransactionIdFilter(serverContext),
opendj-server-legacy/src/main/java/org/opends/server/protocols/http/HttpAccessLogFilter.java
File was renamed from opendj-server-legacy/src/main/java/org/opends/server/protocols/http/HttpLogFilter.java
@@ -15,16 +15,11 @@
 */
package org.opends.server.protocols.http;
import static  org.opends.messages.ProtocolMessages.ERR_HTTP_ERROR_WHILE_PROCESSING_REQUEST;
import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
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.i18n.slf4j.LocalizedLogger;
import org.forgerock.services.context.Context;
import org.forgerock.util.promise.ExceptionHandler;
import org.forgerock.util.promise.NeverThrowsException;
import org.forgerock.util.promise.Promise;
import org.forgerock.util.promise.ResultHandler;
@@ -34,13 +29,11 @@
 * Creates and inject a {@link HttpLogContext} containing information that'll be logged once the request has been
 * processed.
 */
final class HttpLogFilter implements Filter
final class HttpAccessLogFilter implements Filter
{
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
  private final ServerContext serverContext;
  HttpLogFilter(final ServerContext serverContext)
  HttpAccessLogFilter(final ServerContext serverContext)
  {
    this.serverContext = serverContext;
  }
@@ -49,30 +42,13 @@
  public Promise<Response, NeverThrowsException> filter(Context context, final Request request, Handler next)
  {
    final HttpLogContext logContext = new HttpLogContext(context, serverContext, request);
    return next.handle(logContext, request).thenOnResultOrException(new ResultHandler<Response>()
    return next.handle(logContext, request).thenOnResult(new ResultHandler<Response>()
    {
      @Override
      public void handleResult(Response result)
      {
        logContext.log(result.getStatus().getCode());
        if (result.getCause() != null)
        {
          logError(request, result.getCause());
        }
      }
    }, new ExceptionHandler<Exception>()
    {
      @Override
      public void handleException(Exception exception)
      {
        logError(request, exception);
      }
    });
  }
  private static void logError(Request request, Exception exception)
  {
    logger.error(ERR_HTTP_ERROR_WHILE_PROCESSING_REQUEST
        .get(request.getUri(), stackTraceToSingleLineString(exception)));
  }
}
pom.xml
@@ -394,6 +394,7 @@
                            <disabledFile>**/THIRDPARTYREADME.txt</disabledFile>
                            <disabledFile>legal-notices/CDDLv1_0.txt</disabledFile>
                            <disabledFile>**/tests/unit-tests-testng/resource/config-changes.ldif</disabledFile>
                            <disabledFile>opendj-rest2ldap-servlet/src/main/webapp/WEB-INF/classes/logging.properties</disabledFile>
                        </disabledFiles>
                    </configuration>
                </plugin>