From 8750a1a2fa4c91aec7c95ca65c901b859e4378bd Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Thu, 13 Oct 2011 22:23:00 +0000
Subject: [PATCH] OPENDJ-308: Implement access log filtering and configurable message format
---
opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/AccessLogPublisherConfiguration.xml | 46
opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/FileBasedAccessLogPublisherConfiguration.xml | 45
opendj-sdk/opends/src/server/org/opends/server/loggers/TextAuditLogPublisher.java | 132 -
opendj-sdk/opends/src/server/org/opends/server/loggers/TextAccessLogPublisher.java | 1235 ------------------------
opendj-sdk/opends/src/server/org/opends/server/api/AccessLogPublisher.java | 3
opendj-sdk/opends/src/admin/messages/AccessLogPublisherCfgDefn.properties | 7
opendj-sdk/opends/src/admin/messages/FileBasedAuditLogPublisherCfgDefn.properties | 7
opendj-sdk/opends/src/server/org/opends/server/loggers/AbstractTextAccessLogPublisher.java | 1365 ++++++++++++++++++++++++++++
opendj-sdk/opends/resource/schema/02-config.ldif | 6
9 files changed, 1,508 insertions(+), 1,338 deletions(-)
diff --git a/opendj-sdk/opends/resource/schema/02-config.ldif b/opendj-sdk/opends/resource/schema/02-config.ldif
index a64fb8f..3f7a575 100644
--- a/opendj-sdk/opends/resource/schema/02-config.ldif
+++ b/opendj-sdk/opends/resource/schema/02-config.ldif
@@ -2973,7 +2973,8 @@
NAME 'ds-cfg-access-log-publisher'
SUP ds-cfg-log-publisher
STRUCTURAL
- MAY ( ds-cfg-suppress-internal-operations $
+ MAY ( ds-cfg-filtering-policy $
+ ds-cfg-suppress-internal-operations $
ds-cfg-suppress-synchronization-operations )
X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.23
@@ -3008,8 +3009,7 @@
ds-cfg-buffer-size $
ds-cfg-auto-flush $
ds-cfg-append $
- ds-cfg-queue-size $
- ds-cfg-filtering-policy )
+ ds-cfg-queue-size )
X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.26
NAME 'ds-cfg-file-based-debug-log-publisher'
diff --git a/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/AccessLogPublisherConfiguration.xml b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/AccessLogPublisherConfiguration.xml
index c797d2b..2ff68ea 100644
--- a/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/AccessLogPublisherConfiguration.xml
+++ b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/AccessLogPublisherConfiguration.xml
@@ -24,6 +24,7 @@
!
!
! Copyright 2007-2008 Sun Microsystems, Inc.
+ ! Portions copyright 2011 ForgeRock AS.
! -->
<adm:managed-object name="access-log-publisher"
plural-name="access-log-publishers"
@@ -49,6 +50,15 @@
<adm:profile name="cli">
<cli:managed-object custom="true" />
</adm:profile>
+ <adm:relation name="access-log-filtering-criteria">
+ <adm:synopsis>
+ The set of criteria which will be used to filter log records.
+ </adm:synopsis>
+ <adm:one-to-many/>
+ <adm:profile name="ldap">
+ <ldap:rdn-sequence>cn=Filtering Criteria</ldap:rdn-sequence>
+ </adm:profile>
+ </adm:relation>
<adm:property name="java-class" mandatory="true">
<adm:synopsis>
The fully-qualified name of the Java class that provides the
@@ -68,6 +78,42 @@
</ldap:attribute>
</adm:profile>
</adm:property>
+ <adm:property name="filtering-policy">
+ <adm:synopsis>
+ Specifies how filtering criteria should be applied to log records.
+ </adm:synopsis>
+ <adm:default-behavior>
+ <adm:defined>
+ <adm:value>no-filtering</adm:value>
+ </adm:defined>
+ </adm:default-behavior>
+ <adm:syntax>
+ <adm:enumeration>
+ <adm:value name="no-filtering">
+ <adm:synopsis>
+ No filtering will be performed, and all records will be logged.
+ </adm:synopsis>
+ </adm:value>
+ <adm:value name="inclusive">
+ <adm:synopsis>
+ Records must match at least one of the filtering criteria in order
+ to be logged.
+ </adm:synopsis>
+ </adm:value>
+ <adm:value name="exclusive">
+ <adm:synopsis>
+ Records must not match any of the filtering criteria in order to be
+ logged.
+ </adm:synopsis>
+ </adm:value>
+ </adm:enumeration>
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:name>ds-cfg-filtering-policy</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
<adm:property name="suppress-internal-operations" advanced="true">
<adm:synopsis>
Indicates whether internal operations (for example, operations
diff --git a/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/FileBasedAccessLogPublisherConfiguration.xml b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/FileBasedAccessLogPublisherConfiguration.xml
index 3249731..9934bc6 100644
--- a/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/FileBasedAccessLogPublisherConfiguration.xml
+++ b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/FileBasedAccessLogPublisherConfiguration.xml
@@ -41,15 +41,6 @@
<ldap:superior>ds-cfg-access-log-publisher</ldap:superior>
</ldap:object-class>
</adm:profile>
- <adm:relation name="access-log-filtering-criteria">
- <adm:synopsis>
- The set of criteria which will be used to filter log records.
- </adm:synopsis>
- <adm:one-to-many/>
- <adm:profile name="ldap">
- <ldap:rdn-sequence>cn=Filtering Criteria</ldap:rdn-sequence>
- </adm:profile>
- </adm:relation>
<adm:property-override name="java-class" advanced="true">
<adm:default-behavior>
<adm:defined>
@@ -295,40 +286,4 @@
</ldap:attribute>
</adm:profile>
</adm:property>
- <adm:property name="filtering-policy">
- <adm:synopsis>
- Specifies how filtering criteria should be applied to log records.
- </adm:synopsis>
- <adm:default-behavior>
- <adm:defined>
- <adm:value>no-filtering</adm:value>
- </adm:defined>
- </adm:default-behavior>
- <adm:syntax>
- <adm:enumeration>
- <adm:value name="no-filtering">
- <adm:synopsis>
- No filtering will be performed, and all records will be logged.
- </adm:synopsis>
- </adm:value>
- <adm:value name="inclusive">
- <adm:synopsis>
- Records must match at least one of the filtering criteria in order
- to be logged.
- </adm:synopsis>
- </adm:value>
- <adm:value name="exclusive">
- <adm:synopsis>
- Records must not match any of the filtering criteria in order to be
- logged.
- </adm:synopsis>
- </adm:value>
- </adm:enumeration>
- </adm:syntax>
- <adm:profile name="ldap">
- <ldap:attribute>
- <ldap:name>ds-cfg-filtering-policy</ldap:name>
- </ldap:attribute>
- </adm:profile>
- </adm:property>
</adm:managed-object>
diff --git a/opendj-sdk/opends/src/admin/messages/AccessLogPublisherCfgDefn.properties b/opendj-sdk/opends/src/admin/messages/AccessLogPublisherCfgDefn.properties
index 14822ef..d302cc1 100644
--- a/opendj-sdk/opends/src/admin/messages/AccessLogPublisherCfgDefn.properties
+++ b/opendj-sdk/opends/src/admin/messages/AccessLogPublisherCfgDefn.properties
@@ -3,6 +3,13 @@
synopsis=Access Log Publishers are responsible for distributing access log messages from the access logger to a destination.
description=Access log messages provide information about the types of operations processed by the server.
property.enabled.synopsis=Indicates whether the Access Log Publisher is enabled for use.
+property.filtering-policy.synopsis=Specifies how filtering criteria should be applied to log records.
+property.filtering-policy.syntax.enumeration.value.exclusive.synopsis=Records must not match any of the filtering criteria in order to be logged.
+property.filtering-policy.syntax.enumeration.value.inclusive.synopsis=Records must match at least one of the filtering criteria in order to be logged.
+property.filtering-policy.syntax.enumeration.value.no-filtering.synopsis=No filtering will be performed, and all records will be logged.
property.java-class.synopsis=The fully-qualified name of the Java class that provides the Access Log Publisher implementation.
property.suppress-internal-operations.synopsis=Indicates whether internal operations (for example, operations that are initiated by plugins) should be logged along with the operations that are requested by users.
property.suppress-synchronization-operations.synopsis=Indicates whether access messages that are generated by synchronization operations should be suppressed.
+relation.access-log-filtering-criteria.user-friendly-name=Access Log Filtering Criteria
+relation.access-log-filtering-criteria.user-friendly-plural-name=Access Log Filtering Criteria
+relation.access-log-filtering-criteria.synopsis=The set of criteria which will be used to filter log records.
diff --git a/opendj-sdk/opends/src/admin/messages/FileBasedAuditLogPublisherCfgDefn.properties b/opendj-sdk/opends/src/admin/messages/FileBasedAuditLogPublisherCfgDefn.properties
index c684c21..8712b77 100644
--- a/opendj-sdk/opends/src/admin/messages/FileBasedAuditLogPublisherCfgDefn.properties
+++ b/opendj-sdk/opends/src/admin/messages/FileBasedAuditLogPublisherCfgDefn.properties
@@ -7,6 +7,10 @@
property.auto-flush.description=If the asynchronous writes option is used, the writer is flushed after all the log records in the queue are written.
property.buffer-size.synopsis=Specifies the log file buffer size.
property.enabled.synopsis=Indicates whether the File Based Audit Log Publisher is enabled for use.
+property.filtering-policy.synopsis=Specifies how filtering criteria should be applied to log records.
+property.filtering-policy.syntax.enumeration.value.exclusive.synopsis=Records must not match any of the filtering criteria in order to be logged.
+property.filtering-policy.syntax.enumeration.value.inclusive.synopsis=Records must match at least one of the filtering criteria in order to be logged.
+property.filtering-policy.syntax.enumeration.value.no-filtering.synopsis=No filtering will be performed, and all records will be logged.
property.java-class.synopsis=The fully-qualified name of the Java class that provides the File Based Audit Log Publisher implementation.
property.log-file.synopsis=The file name to use for the log files generated by the File Based Audit Log Publisher. The path to the file is relative to the server root.
property.log-file.syntax.string.pattern.synopsis=A path to an existing file that is readable by the server.
@@ -24,3 +28,6 @@
property.suppress-internal-operations.synopsis=Indicates whether internal operations (for example, operations that are initiated by plugins) should be logged along with the operations that are requested by users.
property.suppress-synchronization-operations.synopsis=Indicates whether access messages that are generated by synchronization operations should be suppressed.
property.time-interval.synopsis=Specifies the interval at which to check whether the log files need to be rotated.
+relation.access-log-filtering-criteria.user-friendly-name=Access Log Filtering Criteria
+relation.access-log-filtering-criteria.user-friendly-plural-name=Access Log Filtering Criteria
+relation.access-log-filtering-criteria.synopsis=The set of criteria which will be used to filter log records.
diff --git a/opendj-sdk/opends/src/server/org/opends/server/api/AccessLogPublisher.java b/opendj-sdk/opends/src/server/org/opends/server/api/AccessLogPublisher.java
index 4468ff9..df2de94 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/api/AccessLogPublisher.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/api/AccessLogPublisher.java
@@ -96,8 +96,7 @@
* @return {@code true} if the provided configuration is acceptable
* for this access log publisher, or {@code false} if not.
*/
- public boolean isConfigurationAcceptable(
- AccessLogPublisherCfg configuration,
+ public boolean isConfigurationAcceptable(T configuration,
List<Message> unacceptableReasons)
{
// This default implementation does not perform any special
diff --git a/opendj-sdk/opends/src/server/org/opends/server/loggers/AbstractTextAccessLogPublisher.java b/opendj-sdk/opends/src/server/org/opends/server/loggers/AbstractTextAccessLogPublisher.java
new file mode 100644
index 0000000..07f528d
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/loggers/AbstractTextAccessLogPublisher.java
@@ -0,0 +1,1365 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2011 ForgeRock AS
+ */
+package org.opends.server.loggers;
+
+
+
+import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
+import static org.opends.server.loggers.debug.DebugLogger.getTracer;
+import static org.opends.server.util.StaticUtils.toLowerCase;
+
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.List;
+
+import org.opends.messages.Message;
+import org.opends.server.admin.server.ConfigurationAddListener;
+import org.opends.server.admin.server.ConfigurationChangeListener;
+import org.opends.server.admin.server.ConfigurationDeleteListener;
+import org.opends.server.admin.std.meta.AccessLogFilteringCriteriaCfgDefn.*;
+import org.opends.server.admin.std.meta.AccessLogPublisherCfgDefn.*;
+import org.opends.server.admin.std.server.AccessLogFilteringCriteriaCfg;
+import org.opends.server.admin.std.server.AccessLogPublisherCfg;
+import org.opends.server.api.AccessLogPublisher;
+import org.opends.server.api.ClientConnection;
+import org.opends.server.api.Group;
+import org.opends.server.authorization.dseecompat.PatternDN;
+import org.opends.server.config.ConfigException;
+import org.opends.server.core.*;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.types.*;
+
+
+
+/**
+ * This class provides the base implementation of the access loggers used by the
+ * directory server.
+ *
+ * @param <T>
+ * The type of access log publisher configuration.
+ */
+public abstract class AbstractTextAccessLogPublisher
+ <T extends AccessLogPublisherCfg> extends AccessLogPublisher<T>
+{
+ /**
+ * Criteria based filter.
+ */
+ static final class CriteriaFilter implements Filter
+ {
+ private final AccessLogFilteringCriteriaCfg cfg;
+ private final boolean logConnectRecords;
+ private final boolean logDisconnectRecords;
+ private final EnumSet<OperationType> logOperationRecords;
+ private final AddressMask[] clientAddressEqualTo;
+ private final AddressMask[] clientAddressNotEqualTo;
+ private final PatternDN[] userDNEqualTo;
+ private final PatternDN[] userDNNotEqualTo;
+ private final PatternDN[] targetDNEqualTo;
+ private final PatternDN[] targetDNNotEqualTo;
+ private final DN[] userIsMemberOf;
+ private final DN[] userIsNotMemberOf;
+ private final String attachmentName;
+
+
+
+ /**
+ * Creates a new criteria based filter.
+ *
+ * @param cfg
+ * The access log filter criteria.
+ * @throws DirectoryException
+ * If the configuration cannot be parsed.
+ */
+ CriteriaFilter(final AccessLogFilteringCriteriaCfg cfg)
+ throws DirectoryException
+ {
+ this.cfg = cfg;
+
+ // Generate a unique identifier for attaching partial results to
+ // operations.
+ attachmentName = this.getClass().getName() + "#" + hashCode();
+
+ // Pre-parse the log record types for more efficient queries.
+ if (cfg.getLogRecordType().isEmpty())
+ {
+ logConnectRecords = true;
+ logDisconnectRecords = true;
+
+ logOperationRecords = EnumSet.allOf(OperationType.class);
+ }
+ else
+ {
+ logConnectRecords = cfg.getLogRecordType().contains(
+ LogRecordType.CONNECT);
+ logDisconnectRecords = cfg.getLogRecordType().contains(
+ LogRecordType.DISCONNECT);
+
+ logOperationRecords = EnumSet.noneOf(OperationType.class);
+ for (final LogRecordType type : cfg.getLogRecordType())
+ {
+ switch (type)
+ {
+ case ABANDON:
+ logOperationRecords.add(OperationType.ABANDON);
+ break;
+ case ADD:
+ logOperationRecords.add(OperationType.ADD);
+ break;
+ case BIND:
+ logOperationRecords.add(OperationType.BIND);
+ break;
+ case COMPARE:
+ logOperationRecords.add(OperationType.COMPARE);
+ break;
+ case DELETE:
+ logOperationRecords.add(OperationType.DELETE);
+ break;
+ case EXTENDED:
+ logOperationRecords.add(OperationType.EXTENDED);
+ break;
+ case MODIFY:
+ logOperationRecords.add(OperationType.MODIFY);
+ break;
+ case RENAME:
+ logOperationRecords.add(OperationType.MODIFY_DN);
+ break;
+ case SEARCH:
+ logOperationRecords.add(OperationType.SEARCH);
+ break;
+ case UNBIND:
+ logOperationRecords.add(OperationType.UNBIND);
+ break;
+ default: // Ignore CONNECT/DISCONNECT
+ break;
+ }
+ }
+ }
+
+ clientAddressEqualTo = cfg.getClientAddressEqualTo().toArray(
+ new AddressMask[0]);
+ clientAddressNotEqualTo = cfg.getClientAddressNotEqualTo().toArray(
+ new AddressMask[0]);
+
+ userDNEqualTo = new PatternDN[cfg.getUserDNEqualTo().size()];
+ int i = 0;
+ for (final String s : cfg.getUserDNEqualTo())
+ {
+ userDNEqualTo[i++] = PatternDN.decode(s);
+ }
+
+ userDNNotEqualTo = new PatternDN[cfg.getUserDNNotEqualTo().size()];
+ i = 0;
+ for (final String s : cfg.getUserDNNotEqualTo())
+ {
+ userDNNotEqualTo[i++] = PatternDN.decode(s);
+ }
+
+ userIsMemberOf = cfg.getUserIsMemberOf().toArray(new DN[0]);
+ userIsNotMemberOf = cfg.getUserIsNotMemberOf().toArray(new DN[0]);
+
+ targetDNEqualTo = new PatternDN[cfg.getRequestTargetDNEqualTo().size()];
+ i = 0;
+ for (final String s : cfg.getRequestTargetDNEqualTo())
+ {
+ targetDNEqualTo[i++] = PatternDN.decode(s);
+ }
+
+ targetDNNotEqualTo = new PatternDN[cfg.getRequestTargetDNNotEqualTo()
+ .size()];
+ i = 0;
+ for (final String s : cfg.getRequestTargetDNNotEqualTo())
+ {
+ targetDNNotEqualTo[i++] = PatternDN.decode(s);
+ }
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isConnectLoggable(final ClientConnection connection)
+ {
+ if (!logConnectRecords)
+ {
+ return false;
+ }
+
+ if (!filterClientConnection(connection))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isDisconnectLoggable(final ClientConnection connection)
+ {
+ if (!logDisconnectRecords)
+ {
+ return false;
+ }
+
+ if (!filterClientConnection(connection))
+ {
+ return false;
+ }
+
+ if (!filterUser(connection))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isRequestLoggable(final Operation operation)
+ {
+ final ClientConnection connection = operation.getClientConnection();
+ final boolean matches = logOperationRecords.contains(operation
+ .getOperationType())
+ && filterClientConnection(connection)
+ && filterUser(connection) && filterRequest(operation);
+
+ // Cache the result so that it does not need to be recomputed for the
+ // response.
+ operation.setAttachment(attachmentName, matches);
+
+ return matches;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isResponseLoggable(final Operation operation)
+ {
+ // First check the result that was computed for the initial request.
+ Boolean requestMatched = (Boolean) operation
+ .getAttachment(attachmentName);
+ if (requestMatched == null)
+ {
+ // This should not happen.
+ if (debugEnabled())
+ {
+ TRACER.debugWarning(
+ "Operation attachment %s not found while logging response",
+ attachmentName);
+ }
+ requestMatched = isRequestLoggable(operation);
+ }
+
+ if (!requestMatched)
+ {
+ return false;
+ }
+
+ // Check the response parameters.
+ if (!filterResponse(operation))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+
+
+ private boolean filterClientConnection(final ClientConnection connection)
+ {
+ // Check client address.
+ final InetAddress ipAddr = connection.getRemoteAddress();
+ if (clientAddressNotEqualTo.length > 0)
+ {
+ if (AddressMask.maskListContains(ipAddr, clientAddressNotEqualTo))
+ {
+ return false;
+ }
+ }
+ if (clientAddressEqualTo.length > 0)
+ {
+ if (!AddressMask.maskListContains(ipAddr, clientAddressEqualTo))
+ {
+ return false;
+ }
+ }
+
+ // Check server port.
+ if (!cfg.getClientPortEqualTo().isEmpty())
+ {
+ if (!cfg.getClientPortEqualTo().contains(connection.getServerPort()))
+ {
+ return false;
+ }
+ }
+
+ // Check protocol.
+ if (!cfg.getClientProtocolEqualTo().isEmpty())
+ {
+ if (!cfg.getClientProtocolEqualTo().contains(
+ toLowerCase(connection.getProtocol())))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+
+ private boolean filterRequest(final Operation operation)
+ {
+ // Check target DN.
+ if (targetDNNotEqualTo.length > 0 || targetDNEqualTo.length > 0)
+ {
+ if (!filterRequestTargetDN(operation))
+ {
+ return false;
+ }
+ }
+
+ // TODO: check required controls.
+
+ return true;
+ }
+
+
+
+ private boolean filterRequestTargetDN(final Operation operation)
+ {
+ // Obtain both the parsed and unparsed target DNs. Requests are logged
+ // before parsing so usually only the raw unparsed target DN will be
+ // present, and it may even be invalid.
+ DN targetDN = null;
+ ByteString rawTargetDN = null;
+
+ switch (operation.getOperationType())
+ {
+ case ABANDON:
+ case UNBIND:
+ // These operations don't have parameters which we can filter so
+ // always match them.
+ return true;
+ case EXTENDED:
+ // These operations could have parameters which can be filtered but
+ // we'd need to decode the request in order to find out. This is
+ // beyond the scope of the access log. Therefore, treat extended
+ // operations like abandon/unbind.
+ return true;
+ case ADD:
+ targetDN = ((AddOperation) operation).getEntryDN();
+ rawTargetDN = ((AddOperation) operation).getRawEntryDN();
+ break;
+ case BIND:
+ // For SASL bind operations the bind DN, if provided, will require the
+ // SASL credentials to be decoded which is beyond the scope of the
+ // access log.
+ targetDN = ((BindOperation) operation).getBindDN();
+ rawTargetDN = ((BindOperation) operation).getRawBindDN();
+ break;
+ case COMPARE:
+ targetDN = ((CompareOperation) operation).getEntryDN();
+ rawTargetDN = ((CompareOperation) operation).getRawEntryDN();
+ break;
+ case DELETE:
+ targetDN = ((DeleteOperation) operation).getEntryDN();
+ rawTargetDN = ((DeleteOperation) operation).getRawEntryDN();
+ break;
+ case MODIFY:
+ targetDN = ((ModifyOperation) operation).getEntryDN();
+ rawTargetDN = ((ModifyOperation) operation).getRawEntryDN();
+ break;
+ case MODIFY_DN:
+ targetDN = ((ModifyDNOperation) operation).getEntryDN();
+ rawTargetDN = ((ModifyDNOperation) operation).getRawEntryDN();
+ break;
+ case SEARCH:
+ targetDN = ((SearchOperation) operation).getBaseDN();
+ rawTargetDN = ((SearchOperation) operation).getRawBaseDN();
+ break;
+ }
+
+ // Attempt to parse the raw target DN if needed.
+ if (targetDN == null)
+ {
+ try
+ {
+ targetDN = DN.decode(rawTargetDN);
+ }
+ catch (final DirectoryException e)
+ {
+ // The DN raw target DN was invalid. It will never match any
+ // not-equal-to nor equal-to patterns, so return appropriate result.
+ if (targetDNEqualTo.length != 0)
+ {
+ // Invalid DN will never match equal-to patterns.
+ return false;
+ }
+ else
+ {
+ // Invalid DN does not match any not-equal-to patterns.
+ return true;
+ }
+ }
+ }
+
+ if (targetDNNotEqualTo.length > 0)
+ {
+ for (final PatternDN pattern : targetDNNotEqualTo)
+ {
+ if (pattern.matchesDN(targetDN))
+ {
+ return false;
+ }
+ }
+ }
+
+ if (targetDNEqualTo.length > 0)
+ {
+ for (final PatternDN pattern : targetDNNotEqualTo)
+ {
+ if (pattern.matchesDN(targetDN))
+ {
+ return true;
+ }
+ }
+ }
+
+ // The target DN did not match.
+ return false;
+ }
+
+
+
+ private boolean filterResponse(final Operation operation)
+ {
+ // Check response code.
+ final Integer resultCode = operation.getResultCode().getIntValue();
+
+ if (!cfg.getResponseResultCodeNotEqualTo().isEmpty())
+ {
+ if (cfg.getResponseResultCodeNotEqualTo().contains(resultCode))
+ {
+ return false;
+ }
+ }
+
+ if (!cfg.getResponseResultCodeEqualTo().isEmpty())
+ {
+ if (!cfg.getResponseResultCodeNotEqualTo().contains(resultCode))
+ {
+ return false;
+ }
+ }
+
+ // Check etime.
+ final long etime = operation.getProcessingTime();
+
+ final Integer etimeGT = cfg.getResponseEtimeLessThan();
+ if (etimeGT != null)
+ {
+ if (etime <= ((long) etimeGT))
+ {
+ return false;
+ }
+ }
+
+ final Integer etimeLT = cfg.getResponseEtimeLessThan();
+ if (etimeLT != null)
+ {
+ if (etime >= ((long) etimeLT))
+ {
+ return false;
+ }
+ }
+
+ // Check search response fields.
+ if (operation instanceof SearchOperation)
+ {
+ final SearchOperation searchOperation = (SearchOperation) operation;
+ final Boolean isIndexed = cfg.isSearchResponseIsIndexed();
+ if (isIndexed != null)
+ {
+ boolean wasUnindexed = false;
+ for (final AdditionalLogItem item : operation.getAdditionalLogItems())
+ {
+ if (item.getKey().equals("unindexed"))
+ {
+ wasUnindexed = true;
+ break;
+ }
+ }
+
+ if (isIndexed)
+ {
+ if (wasUnindexed)
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if (!wasUnindexed)
+ {
+ return false;
+ }
+ }
+ }
+
+ final int nentries = searchOperation.getEntriesSent();
+
+ final Integer nentriesGT = cfg.getSearchResponseNentriesGreaterThan();
+ if (nentriesGT != null)
+ {
+ if (nentries <= nentriesGT)
+ {
+ return false;
+ }
+ }
+
+ final Integer nentriesLT = cfg.getSearchResponseNentriesLessThan();
+ if (nentriesLT != null)
+ {
+ if (nentries >= nentriesLT)
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+
+
+ private boolean filterUser(final ClientConnection connection)
+ {
+ // Check user DN.
+ if (userDNNotEqualTo.length > 0 || userDNEqualTo.length > 0)
+ {
+ if (!filterUserBindDN(connection))
+ {
+ return false;
+ }
+ }
+
+ // Check group membership.
+ if (userIsNotMemberOf.length > 0 || userIsNotMemberOf.length > 0)
+ {
+ if (!filterUserIsMemberOf(connection))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+
+ private boolean filterUserBindDN(final ClientConnection connection)
+ {
+ final DN userDN = connection.getAuthenticationInfo()
+ .getAuthenticationDN();
+
+ // Fast-path for unauthenticated clients.
+ if (userDN == null)
+ {
+ return userDNEqualTo.length == 0;
+ }
+
+ if (userDNNotEqualTo.length > 0)
+ {
+ for (final PatternDN pattern : userDNNotEqualTo)
+ {
+ if (pattern.matchesDN(userDN))
+ {
+ return false;
+ }
+ }
+ }
+
+ if (userDNEqualTo.length > 0)
+ {
+ for (final PatternDN pattern : userDNNotEqualTo)
+ {
+ if (pattern.matchesDN(userDN))
+ {
+ return true;
+ }
+ }
+ }
+
+ // The user DN did not match.
+ return false;
+ }
+
+
+
+ private boolean filterUserIsMemberOf(final ClientConnection connection)
+ {
+ final Entry userEntry = connection.getAuthenticationInfo()
+ .getAuthenticationEntry();
+
+ // Fast-path for unauthenticated clients.
+ if (userEntry == null)
+ {
+ return userIsMemberOf.length == 0;
+ }
+
+ final GroupManager groupManager = DirectoryServer.getGroupManager();
+ if (userIsNotMemberOf.length > 0)
+ {
+ for (final DN groupDN : userIsNotMemberOf)
+ {
+ final Group<?> group = groupManager.getGroupInstance(groupDN);
+ try
+ {
+ if ((group != null) && group.isMember(userEntry))
+ {
+ return false;
+ }
+ }
+ catch (final DirectoryException e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+ }
+ }
+
+ if (userIsMemberOf.length > 0)
+ {
+ for (final DN groupDN : userIsMemberOf)
+ {
+ final Group<?> group = groupManager.getGroupInstance(groupDN);
+ try
+ {
+ if ((group != null) && group.isMember(userEntry))
+ {
+ return true;
+ }
+ }
+ catch (final DirectoryException e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+ }
+ }
+
+ // The user entry did not match.
+ return false;
+ }
+
+ }
+
+
+
+ /**
+ * Log message filter predicate.
+ */
+ static interface Filter
+ {
+ /**
+ * Returns {@code true} if the provided client connect should be logged.
+ *
+ * @param connection
+ * The client connection.
+ * @return {@code true} if the provided client connect should be logged.
+ */
+ boolean isConnectLoggable(ClientConnection connection);
+
+
+
+ /**
+ * Returns {@code true} if the provided client disconnect should be logged.
+ *
+ * @param connection
+ * The client connection.
+ * @return {@code true} if the provided client disconnect should be logged.
+ */
+ boolean isDisconnectLoggable(ClientConnection connection);
+
+
+
+ /**
+ * Returns {@code true} if the provided request should be logged.
+ *
+ * @param operation
+ * The request.
+ * @return {@code true} if the provided request should be logged.
+ */
+ boolean isRequestLoggable(Operation operation);
+
+
+
+ /**
+ * Returns {@code true} if the provided response should be logged.
+ *
+ * @param operation
+ * The response.
+ * @return {@code true} if the provided response should be logged.
+ */
+ boolean isResponseLoggable(Operation operation);
+ }
+
+
+
+ /**
+ * A filter which performs a logical OR over a set of sub-filters.
+ */
+ static final class OrFilter implements Filter
+ {
+ private final Filter[] subFilters;
+
+
+
+ /**
+ * Creates a new OR filter.
+ *
+ * @param subFilters
+ * The sub-filters.
+ */
+ OrFilter(final Filter[] subFilters)
+ {
+ this.subFilters = subFilters;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isConnectLoggable(final ClientConnection connection)
+ {
+ for (final Filter filter : subFilters)
+ {
+ if (filter.isConnectLoggable(connection))
+ {
+ // Succeed fast.
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isDisconnectLoggable(final ClientConnection connection)
+ {
+ for (final Filter filter : subFilters)
+ {
+ if (filter.isDisconnectLoggable(connection))
+ {
+ // Succeed fast.
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isRequestLoggable(final Operation operation)
+ {
+ for (final Filter filter : subFilters)
+ {
+ if (filter.isRequestLoggable(operation))
+ {
+ // Succeed fast.
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isResponseLoggable(final Operation operation)
+ {
+ for (final Filter filter : subFilters)
+ {
+ if (filter.isResponseLoggable(operation))
+ {
+ // Succeed fast.
+ return true;
+ }
+ }
+ return false;
+ }
+
+ }
+
+
+
+ /**
+ * The root filter which first checks the logger configuration, delegating to
+ * a sub-filter if needed.
+ */
+ static final class RootFilter implements Filter
+ {
+ private final Filter subFilter;
+ private final boolean suppressInternalOperations;
+ private final boolean suppressSynchronizationOperations;
+ private final FilteringPolicy policy;
+
+
+
+ /**
+ * Creates a new root filter.
+ *
+ * @param suppressInternal
+ * Indicates whether internal operations should be suppressed.
+ * @param suppressSynchronization
+ * Indicates whether sync operations should be suppressed.
+ * @param policy
+ * The filtering policy.
+ * @param subFilter
+ * The sub-filters.
+ */
+ RootFilter(final boolean suppressInternal,
+ final boolean suppressSynchronization, final FilteringPolicy policy,
+ final Filter subFilter)
+ {
+ this.suppressInternalOperations = suppressInternal;
+ this.suppressSynchronizationOperations = suppressSynchronization;
+ this.policy = policy;
+ this.subFilter = subFilter;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isConnectLoggable(final ClientConnection connection)
+ {
+ final long connectionID = connection.getConnectionID();
+ if (connectionID >= 0 || !suppressInternalOperations)
+ {
+ switch (policy)
+ {
+ case INCLUSIVE:
+ return subFilter.isConnectLoggable(connection);
+ case EXCLUSIVE:
+ return !subFilter.isConnectLoggable(connection);
+ default: // NO_FILTERING:
+ return true;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isDisconnectLoggable(final ClientConnection connection)
+ {
+ final long connectionID = connection.getConnectionID();
+ if (connectionID >= 0 || !suppressInternalOperations)
+ {
+ switch (policy)
+ {
+ case INCLUSIVE:
+ return subFilter.isDisconnectLoggable(connection);
+ case EXCLUSIVE:
+ return !subFilter.isDisconnectLoggable(connection);
+ default: // NO_FILTERING:
+ return true;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isRequestLoggable(final Operation operation)
+ {
+ if (isLoggable(operation))
+ {
+ switch (policy)
+ {
+ case INCLUSIVE:
+ return subFilter.isRequestLoggable(operation);
+ case EXCLUSIVE:
+ return !subFilter.isRequestLoggable(operation);
+ default: // NO_FILTERING:
+ return true;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isResponseLoggable(final Operation operation)
+ {
+ if (isLoggable(operation))
+ {
+ switch (policy)
+ {
+ case INCLUSIVE:
+ return subFilter.isResponseLoggable(operation);
+ case EXCLUSIVE:
+ return !subFilter.isResponseLoggable(operation);
+ default: // NO_FILTERING:
+ return true;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+
+
+ // Determines whether the provided operation should be logged.
+ private boolean isLoggable(final Operation operation)
+ {
+ final long connectionID = operation.getConnectionID();
+ if (connectionID < 0)
+ {
+ // This is an internal operation.
+ if (operation.isSynchronizationOperation())
+ {
+ return !suppressSynchronizationOperations;
+ }
+ else
+ {
+ return !suppressInternalOperations;
+ }
+ }
+
+ return true;
+ }
+ }
+
+
+
+ /**
+ * Configuration change listener.
+ */
+ private final class ChangeListener implements
+ ConfigurationChangeListener<AccessLogPublisherCfg>
+ {
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final ConfigChangeResult applyConfigurationChange(
+ final AccessLogPublisherCfg configuration)
+ {
+ // Update the configuration.
+ cfg = configuration;
+
+ // Rebuild the filter using the new configuration and criteria.
+ buildFilters();
+
+ return new ConfigChangeResult(ResultCode.SUCCESS, false);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final boolean isConfigurationChangeAcceptable(
+ final AccessLogPublisherCfg configuration,
+ final List<Message> unacceptableReasons)
+ {
+ return true;
+ }
+ }
+
+
+
+ /**
+ * Filter criteria configuration listener.
+ */
+ private final class FilterListener implements
+ ConfigurationChangeListener<AccessLogFilteringCriteriaCfg>,
+ ConfigurationAddListener<AccessLogFilteringCriteriaCfg>,
+ ConfigurationDeleteListener<AccessLogFilteringCriteriaCfg>
+ {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ConfigChangeResult applyConfigurationAdd(
+ final AccessLogFilteringCriteriaCfg configuration)
+ {
+ // Rebuild the filter using the new configuration and criteria.
+ buildFilters();
+ configuration.addChangeListener(this);
+ return new ConfigChangeResult(ResultCode.SUCCESS, false);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ConfigChangeResult applyConfigurationChange(
+ final AccessLogFilteringCriteriaCfg configuration)
+ {
+ // Rebuild the filter using the new configuration and criteria.
+ buildFilters();
+ return new ConfigChangeResult(ResultCode.SUCCESS, false);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ConfigChangeResult applyConfigurationDelete(
+ final AccessLogFilteringCriteriaCfg configuration)
+ {
+ // Rebuild the filter using the new configuration and criteria.
+ buildFilters();
+ return new ConfigChangeResult(ResultCode.SUCCESS, false);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isConfigurationAddAcceptable(
+ final AccessLogFilteringCriteriaCfg configuration,
+ final List<Message> unacceptableReasons)
+ {
+ return true;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isConfigurationChangeAcceptable(
+ final AccessLogFilteringCriteriaCfg configuration,
+ final List<Message> unacceptableReasons)
+ {
+ return true;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isConfigurationDeleteAcceptable(
+ final AccessLogFilteringCriteriaCfg configuration,
+ final List<Message> unacceptableReasons)
+ {
+ return true;
+ }
+ }
+
+
+
+ /**
+ * The tracer object for the debug logger.
+ */
+ protected static final DebugTracer TRACER = getTracer();
+ private AccessLogPublisherCfg cfg = null;
+ private Filter filter = null;
+ private final ChangeListener changeListener = new ChangeListener();
+ private final FilterListener filterListener = new FilterListener();
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final void close()
+ {
+ try
+ {
+ close0();
+ }
+ finally
+ {
+ if (cfg != null)
+ {
+ cfg.removeAccessChangeListener(changeListener);
+ for (final String criteriaName : cfg.listAccessLogFilteringCriteria())
+ {
+ try
+ {
+ cfg.getAccessLogFilteringCriteria(criteriaName)
+ .removeChangeListener(filterListener);
+ }
+ catch (final ConfigException e)
+ {
+ // Ignore.
+ }
+ }
+ cfg.removeAccessLogFilteringCriteriaAddListener(filterListener);
+ cfg.removeAccessLogFilteringCriteriaDeleteListener(filterListener);
+ }
+ }
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final DN getDN()
+ {
+ return cfg != null ? cfg.dn() : null;
+ }
+
+
+
+ /**
+ * Perform any initialization required by the sub-implementation.
+ *
+ * @param config
+ * The access publisher configuration that contains the information
+ * to use to initialize this access publisher.
+ * @throws ConfigException
+ * If an unrecoverable problem arises in the process of performing
+ * the initialization as a result of the server configuration.
+ * @throws InitializationException
+ * If a problem occurs during initialization that is not related to
+ * the server configuration.
+ */
+ protected final void initializeFilters(T config) throws ConfigException,
+ InitializationException
+ {
+ // Now initialize filters and listeners.
+ cfg = config;
+
+ // Rebuild the filter using the new configuration and criteria.
+ buildFilters();
+
+ // Add change listeners.
+ for (final String criteriaName : cfg.listAccessLogFilteringCriteria())
+ {
+ try
+ {
+ cfg.getAccessLogFilteringCriteria(criteriaName).addChangeListener(
+ filterListener);
+ }
+ catch (final ConfigException e)
+ {
+ // Ignore.
+ }
+ }
+ cfg.addAccessLogFilteringCriteriaAddListener(filterListener);
+ cfg.addAccessLogFilteringCriteriaDeleteListener(filterListener);
+ cfg.addAccessChangeListener(changeListener);
+ }
+
+
+
+ /**
+ * Release any resources owned by the sub-implementation.
+ */
+ protected abstract void close0();
+
+
+
+ /**
+ * Returns {@code true} if the provided client connect should be logged.
+ *
+ * @param c
+ * The client connection.
+ * @return {@code true} if the provided client connect should be logged.
+ */
+ protected final boolean isConnectLoggable(final ClientConnection c)
+ {
+ return filter.isConnectLoggable(c);
+ }
+
+
+
+ /**
+ * Returns {@code true} if the provided client disconnect should be logged.
+ *
+ * @param c
+ * The client connection.
+ * @return {@code true} if the provided client disconnect should be logged.
+ */
+ protected final boolean isDisconnectLoggable(final ClientConnection c)
+ {
+ return filter.isDisconnectLoggable(c);
+ }
+
+
+
+ /**
+ * Returns {@code true} if the provided request should be logged.
+ *
+ * @param o
+ * The request.
+ * @return {@code true} if the provided request should be logged.
+ */
+ protected final boolean isRequestLoggable(final Operation o)
+ {
+ return filter.isRequestLoggable(o);
+ }
+
+
+
+ /**
+ * Returns {@code true} if the provided response should be logged.
+ *
+ * @param o
+ * The response.
+ * @return {@code true} if the provided response should be logged.
+ */
+ protected final boolean isResponseLoggable(final Operation o)
+ {
+ return filter.isResponseLoggable(o);
+ }
+
+
+
+ // Build an appropriate set of filters based on the configuration.
+ private void buildFilters()
+ {
+ buildFilters(cfg.isSuppressInternalOperations(),
+ cfg.isSuppressSynchronizationOperations(), cfg.getFilteringPolicy());
+ }
+
+
+
+ /**
+ * For startup access logger.
+ *
+ * @param suppressInternal
+ * {@code true} if internal operations should be suppressed.
+ */
+ protected void buildFilters(final boolean suppressInternal)
+ {
+ buildFilters(suppressInternal, false, FilteringPolicy.NO_FILTERING);
+ }
+
+
+
+ private void buildFilters(final boolean suppressInternal,
+ final boolean suppressSynchronization, final FilteringPolicy policy)
+ {
+ final ArrayList<Filter> subFilters = new ArrayList<Filter>();
+ if (cfg != null)
+ {
+ for (final String criteriaName : cfg.listAccessLogFilteringCriteria())
+ {
+ try
+ {
+ final AccessLogFilteringCriteriaCfg criteriaCfg = cfg
+ .getAccessLogFilteringCriteria(criteriaName);
+ subFilters.add(new CriteriaFilter(criteriaCfg));
+ }
+ catch (final ConfigException e)
+ {
+ // TODO: Unable to decode this access log criteria, so log a warning
+ // and continue.
+ }
+ catch (final DirectoryException e)
+ {
+ // TODO: Unable to decode this access log criteria, so log a warning
+ // and continue.
+ }
+ }
+ }
+ final Filter orFilter = new OrFilter(subFilters.toArray(new Filter[0]));
+ filter = new RootFilter(suppressInternal, suppressSynchronization, policy,
+ orFilter);
+ }
+
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/loggers/TextAccessLogPublisher.java b/opendj-sdk/opends/src/server/org/opends/server/loggers/TextAccessLogPublisher.java
index 346e4f5..6b039ce 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/loggers/TextAccessLogPublisher.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/loggers/TextAccessLogPublisher.java
@@ -30,35 +30,24 @@
import static org.opends.messages.ConfigMessages.*;
-import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
-import static org.opends.server.loggers.debug.DebugLogger.getTracer;
import static org.opends.server.util.StaticUtils.getFileForPath;
import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
-import static org.opends.server.util.StaticUtils.toLowerCase;
import java.io.File;
import java.io.IOException;
-import java.net.InetAddress;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
import org.opends.messages.Message;
import org.opends.messages.MessageBuilder;
-import org.opends.server.admin.server.ConfigurationAddListener;
import org.opends.server.admin.server.ConfigurationChangeListener;
-import org.opends.server.admin.server.ConfigurationDeleteListener;
-import org.opends.server.admin.std.meta.AccessLogFilteringCriteriaCfgDefn.*;
-import org.opends.server.admin.std.meta.FileBasedAccessLogPublisherCfgDefn.*;
-import org.opends.server.admin.std.server.AccessLogFilteringCriteriaCfg;
-import org.opends.server.admin.std.server.AccessLogPublisherCfg;
import org.opends.server.admin.std.server.FileBasedAccessLogPublisherCfg;
-import org.opends.server.api.AccessLogPublisher;
import org.opends.server.api.ClientConnection;
import org.opends.server.api.ExtendedOperationHandler;
-import org.opends.server.api.Group;
-import org.opends.server.authorization.dseecompat.PatternDN;
import org.opends.server.config.ConfigException;
import org.opends.server.core.*;
-import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.*;
import org.opends.server.util.TimeThread;
@@ -68,1041 +57,10 @@
* This class provides the implementation of the access logger used by the
* directory server.
*/
-public class TextAccessLogPublisher extends
- AccessLogPublisher<FileBasedAccessLogPublisherCfg> implements
+public final class TextAccessLogPublisher extends
+ AbstractTextAccessLogPublisher<FileBasedAccessLogPublisherCfg> implements
ConfigurationChangeListener<FileBasedAccessLogPublisherCfg>
{
- /**
- * Criteria based filter.
- */
- static final class CriteriaFilter implements Filter
- {
- private final AccessLogFilteringCriteriaCfg cfg;
- private final boolean logConnectRecords;
- private final boolean logDisconnectRecords;
- private final EnumSet<OperationType> logOperationRecords;
- private final AddressMask[] clientAddressEqualTo;
- private final AddressMask[] clientAddressNotEqualTo;
- private final PatternDN[] userDNEqualTo;
- private final PatternDN[] userDNNotEqualTo;
- private final PatternDN[] targetDNEqualTo;
- private final PatternDN[] targetDNNotEqualTo;
- private final DN[] userIsMemberOf;
- private final DN[] userIsNotMemberOf;
- private final String attachmentName;
-
-
-
- /**
- * Creates a new criteria based filter.
- *
- * @param cfg
- * The access log filter criteria.
- * @throws DirectoryException
- * If the configuration cannot be parsed.
- */
- CriteriaFilter(final AccessLogFilteringCriteriaCfg cfg)
- throws DirectoryException
- {
- this.cfg = cfg;
-
- // Generate a unique identifier for attaching partial results to
- // operations.
- attachmentName = this.getClass().getName() + "#" + hashCode();
-
- // Pre-parse the log record types for more efficient queries.
- if (cfg.getLogRecordType().isEmpty())
- {
- logConnectRecords = true;
- logDisconnectRecords = true;
-
- logOperationRecords = EnumSet.allOf(OperationType.class);
- }
- else
- {
- logConnectRecords = cfg.getLogRecordType().contains(
- LogRecordType.CONNECT);
- logDisconnectRecords = cfg.getLogRecordType().contains(
- LogRecordType.DISCONNECT);
-
- logOperationRecords = EnumSet.noneOf(OperationType.class);
- for (final LogRecordType type : cfg.getLogRecordType())
- {
- switch (type)
- {
- case ABANDON:
- logOperationRecords.add(OperationType.ABANDON);
- break;
- case ADD:
- logOperationRecords.add(OperationType.ADD);
- break;
- case BIND:
- logOperationRecords.add(OperationType.BIND);
- break;
- case COMPARE:
- logOperationRecords.add(OperationType.COMPARE);
- break;
- case DELETE:
- logOperationRecords.add(OperationType.DELETE);
- break;
- case EXTENDED:
- logOperationRecords.add(OperationType.EXTENDED);
- break;
- case MODIFY:
- logOperationRecords.add(OperationType.MODIFY);
- break;
- case RENAME:
- logOperationRecords.add(OperationType.MODIFY_DN);
- break;
- case SEARCH:
- logOperationRecords.add(OperationType.SEARCH);
- break;
- case UNBIND:
- logOperationRecords.add(OperationType.UNBIND);
- break;
- default: // Ignore CONNECT/DISCONNECT
- break;
- }
- }
- }
-
- clientAddressEqualTo = cfg.getClientAddressEqualTo().toArray(
- new AddressMask[0]);
- clientAddressNotEqualTo = cfg.getClientAddressNotEqualTo().toArray(
- new AddressMask[0]);
-
- userDNEqualTo = new PatternDN[cfg.getUserDNEqualTo().size()];
- int i = 0;
- for (final String s : cfg.getUserDNEqualTo())
- {
- userDNEqualTo[i++] = PatternDN.decode(s);
- }
-
- userDNNotEqualTo = new PatternDN[cfg.getUserDNNotEqualTo().size()];
- i = 0;
- for (final String s : cfg.getUserDNNotEqualTo())
- {
- userDNNotEqualTo[i++] = PatternDN.decode(s);
- }
-
- userIsMemberOf = cfg.getUserIsMemberOf().toArray(new DN[0]);
- userIsNotMemberOf = cfg.getUserIsNotMemberOf().toArray(new DN[0]);
-
- targetDNEqualTo = new PatternDN[cfg.getRequestTargetDNEqualTo().size()];
- i = 0;
- for (final String s : cfg.getRequestTargetDNEqualTo())
- {
- targetDNEqualTo[i++] = PatternDN.decode(s);
- }
-
- targetDNNotEqualTo = new PatternDN[cfg.getRequestTargetDNNotEqualTo()
- .size()];
- i = 0;
- for (final String s : cfg.getRequestTargetDNNotEqualTo())
- {
- targetDNNotEqualTo[i++] = PatternDN.decode(s);
- }
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- public boolean isConnectLoggable(final ClientConnection connection)
- {
- if (!logConnectRecords)
- {
- return false;
- }
-
- if (!filterClientConnection(connection))
- {
- return false;
- }
-
- return true;
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- public boolean isDisconnectLoggable(final ClientConnection connection)
- {
- if (!logDisconnectRecords)
- {
- return false;
- }
-
- if (!filterClientConnection(connection))
- {
- return false;
- }
-
- if (!filterUser(connection))
- {
- return false;
- }
-
- return true;
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- public boolean isRequestLoggable(final Operation operation)
- {
- final ClientConnection connection = operation.getClientConnection();
- final boolean matches = logOperationRecords.contains(operation
- .getOperationType())
- && filterClientConnection(connection)
- && filterUser(connection) && filterRequest(operation);
-
- // Cache the result so that it does not need to be recomputed for the
- // response.
- operation.setAttachment(attachmentName, matches);
-
- return matches;
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- public boolean isResponseLoggable(final Operation operation)
- {
- // First check the result that was computed for the initial request.
- Boolean requestMatched = (Boolean) operation
- .getAttachment(attachmentName);
- if (requestMatched == null)
- {
- // This should not happen.
- if (debugEnabled())
- {
- TRACER.debugWarning(
- "Operation attachment %s not found while logging response",
- attachmentName);
- }
- requestMatched = isRequestLoggable(operation);
- }
-
- if (!requestMatched)
- {
- return false;
- }
-
- // Check the response parameters.
- if (!filterResponse(operation))
- {
- return false;
- }
-
- return true;
- }
-
-
-
- private boolean filterClientConnection(final ClientConnection connection)
- {
- // Check client address.
- final InetAddress ipAddr = connection.getRemoteAddress();
- if (clientAddressNotEqualTo.length > 0)
- {
- if (AddressMask.maskListContains(ipAddr, clientAddressNotEqualTo))
- {
- return false;
- }
- }
- if (clientAddressEqualTo.length > 0)
- {
- if (!AddressMask.maskListContains(ipAddr, clientAddressEqualTo))
- {
- return false;
- }
- }
-
- // Check server port.
- if (!cfg.getClientPortEqualTo().isEmpty())
- {
- if (!cfg.getClientPortEqualTo().contains(connection.getServerPort()))
- {
- return false;
- }
- }
-
- // Check protocol.
- if (!cfg.getClientProtocolEqualTo().isEmpty())
- {
- if (!cfg.getClientProtocolEqualTo().contains(
- toLowerCase(connection.getProtocol())))
- {
- return false;
- }
- }
-
- return true;
- }
-
-
-
- private boolean filterRequest(final Operation operation)
- {
- // Check target DN.
- if (targetDNNotEqualTo.length > 0 || targetDNEqualTo.length > 0)
- {
- if (!filterRequestTargetDN(operation))
- {
- return false;
- }
- }
-
- // TODO: check required controls.
-
- return true;
- }
-
-
-
- private boolean filterRequestTargetDN(final Operation operation)
- {
- // Obtain both the parsed and unparsed target DNs. Requests are logged
- // before parsing so usually only the raw unparsed target DN will be
- // present, and it may even be invalid.
- DN targetDN = null;
- ByteString rawTargetDN = null;
-
- switch (operation.getOperationType())
- {
- case ABANDON:
- case UNBIND:
- // These operations don't have parameters which we can filter so
- // always match them.
- return true;
- case EXTENDED:
- // These operations could have parameters which can be filtered but
- // we'd need to decode the request in order to find out. This is
- // beyond the scope of the access log. Therefore, treat extended
- // operations like abandon/unbind.
- return true;
- case ADD:
- targetDN = ((AddOperation) operation).getEntryDN();
- rawTargetDN = ((AddOperation) operation).getRawEntryDN();
- break;
- case BIND:
- // For SASL bind operations the bind DN, if provided, will require the
- // SASL credentials to be decoded which is beyond the scope of the
- // access log.
- targetDN = ((BindOperation) operation).getBindDN();
- rawTargetDN = ((BindOperation) operation).getRawBindDN();
- break;
- case COMPARE:
- targetDN = ((CompareOperation) operation).getEntryDN();
- rawTargetDN = ((CompareOperation) operation).getRawEntryDN();
- break;
- case DELETE:
- targetDN = ((DeleteOperation) operation).getEntryDN();
- rawTargetDN = ((DeleteOperation) operation).getRawEntryDN();
- break;
- case MODIFY:
- targetDN = ((ModifyOperation) operation).getEntryDN();
- rawTargetDN = ((ModifyOperation) operation).getRawEntryDN();
- break;
- case MODIFY_DN:
- targetDN = ((ModifyDNOperation) operation).getEntryDN();
- rawTargetDN = ((ModifyDNOperation) operation).getRawEntryDN();
- break;
- case SEARCH:
- targetDN = ((SearchOperation) operation).getBaseDN();
- rawTargetDN = ((SearchOperation) operation).getRawBaseDN();
- break;
- }
-
- // Attempt to parse the raw target DN if needed.
- if (targetDN == null)
- {
- try
- {
- targetDN = DN.decode(rawTargetDN);
- }
- catch (final DirectoryException e)
- {
- // The DN raw target DN was invalid. It will never match any
- // not-equal-to nor equal-to patterns, so return appropriate result.
- if (targetDNEqualTo.length != 0)
- {
- // Invalid DN will never match equal-to patterns.
- return false;
- }
- else
- {
- // Invalid DN does not match any not-equal-to patterns.
- return true;
- }
- }
- }
-
- if (targetDNNotEqualTo.length > 0)
- {
- for (final PatternDN pattern : targetDNNotEqualTo)
- {
- if (pattern.matchesDN(targetDN))
- {
- return false;
- }
- }
- }
-
- if (targetDNEqualTo.length > 0)
- {
- for (final PatternDN pattern : targetDNNotEqualTo)
- {
- if (pattern.matchesDN(targetDN))
- {
- return true;
- }
- }
- }
-
- // The target DN did not match.
- return false;
- }
-
-
-
- private boolean filterResponse(final Operation operation)
- {
- // Check response code.
- final Integer resultCode = operation.getResultCode().getIntValue();
-
- if (!cfg.getResponseResultCodeNotEqualTo().isEmpty())
- {
- if (cfg.getResponseResultCodeNotEqualTo().contains(resultCode))
- {
- return false;
- }
- }
-
- if (!cfg.getResponseResultCodeEqualTo().isEmpty())
- {
- if (!cfg.getResponseResultCodeNotEqualTo().contains(resultCode))
- {
- return false;
- }
- }
-
- // Check etime.
- final long etime = operation.getProcessingTime();
-
- final Integer etimeGT = cfg.getResponseEtimeLessThan();
- if (etimeGT != null)
- {
- if (etime <= ((long) etimeGT))
- {
- return false;
- }
- }
-
- final Integer etimeLT = cfg.getResponseEtimeLessThan();
- if (etimeLT != null)
- {
- if (etime >= ((long) etimeLT))
- {
- return false;
- }
- }
-
- // Check search response fields.
- if (operation instanceof SearchOperation)
- {
- final SearchOperation searchOperation = (SearchOperation) operation;
- final Boolean isIndexed= cfg.isSearchResponseIsIndexed();
- if (isIndexed != null)
- {
- boolean wasUnindexed = false;
- for (final AdditionalLogItem item : operation.getAdditionalLogItems())
- {
- if (item.getKey().equals("unindexed"))
- {
- wasUnindexed = true;
- break;
- }
- }
-
- if (isIndexed)
- {
- if (wasUnindexed)
- {
- return false;
- }
- }
- else
- {
- if (!wasUnindexed)
- {
- return false;
- }
- }
- }
-
- final int nentries = searchOperation.getEntriesSent();
-
- final Integer nentriesGT = cfg.getSearchResponseNentriesGreaterThan();
- if (nentriesGT != null)
- {
- if (nentries <= nentriesGT)
- {
- return false;
- }
- }
-
- final Integer nentriesLT = cfg.getSearchResponseNentriesLessThan();
- if (nentriesLT != null)
- {
- if (nentries >= nentriesLT)
- {
- return false;
- }
- }
- }
-
- return true;
- }
-
-
-
- private boolean filterUser(final ClientConnection connection)
- {
- // Check user DN.
- if (userDNNotEqualTo.length > 0 || userDNEqualTo.length > 0)
- {
- if (!filterUserBindDN(connection))
- {
- return false;
- }
- }
-
- // Check group membership.
- if (userIsNotMemberOf.length > 0 || userIsNotMemberOf.length > 0)
- {
- if (!filterUserIsMemberOf(connection))
- {
- return false;
- }
- }
-
- return true;
- }
-
-
-
- private boolean filterUserBindDN(final ClientConnection connection)
- {
- final DN userDN = connection.getAuthenticationInfo()
- .getAuthenticationDN();
-
- // Fast-path for unauthenticated clients.
- if (userDN == null)
- {
- return userDNEqualTo.length == 0;
- }
-
- if (userDNNotEqualTo.length > 0)
- {
- for (final PatternDN pattern : userDNNotEqualTo)
- {
- if (pattern.matchesDN(userDN))
- {
- return false;
- }
- }
- }
-
- if (userDNEqualTo.length > 0)
- {
- for (final PatternDN pattern : userDNNotEqualTo)
- {
- if (pattern.matchesDN(userDN))
- {
- return true;
- }
- }
- }
-
- // The user DN did not match.
- return false;
- }
-
-
-
- private boolean filterUserIsMemberOf(final ClientConnection connection)
- {
- final Entry userEntry = connection.getAuthenticationInfo()
- .getAuthenticationEntry();
-
- // Fast-path for unauthenticated clients.
- if (userEntry == null)
- {
- return userIsMemberOf.length == 0;
- }
-
- final GroupManager groupManager = DirectoryServer.getGroupManager();
- if (userIsNotMemberOf.length > 0)
- {
- for (final DN groupDN : userIsNotMemberOf)
- {
- final Group<?> group = groupManager.getGroupInstance(groupDN);
- try
- {
- if ((group != null) && group.isMember(userEntry))
- {
- return false;
- }
- }
- catch (final DirectoryException e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
- }
- }
- }
-
- if (userIsMemberOf.length > 0)
- {
- for (final DN groupDN : userIsMemberOf)
- {
- final Group<?> group = groupManager.getGroupInstance(groupDN);
- try
- {
- if ((group != null) && group.isMember(userEntry))
- {
- return true;
- }
- }
- catch (final DirectoryException e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
- }
- }
- }
-
- // The user entry did not match.
- return false;
- }
-
- }
-
-
-
- // TODO: update assigned OIDs WIKI page when complete.
-
- /**
- * Log message filter predicate.
- */
- static interface Filter
- {
- /**
- * Returns {@code true} if the provided client connect should be logged.
- *
- * @param connection
- * The client connection.
- * @return {@code true} if the provided client connect should be logged.
- */
- boolean isConnectLoggable(ClientConnection connection);
-
-
-
- /**
- * Returns {@code true} if the provided client disconnect should be logged.
- *
- * @param connection
- * The client connection.
- * @return {@code true} if the provided client disconnect should be logged.
- */
- boolean isDisconnectLoggable(ClientConnection connection);
-
-
-
- /**
- * Returns {@code true} if the provided request should be logged.
- *
- * @param operation
- * The request.
- * @return {@code true} if the provided request should be logged.
- */
- boolean isRequestLoggable(Operation operation);
-
-
-
- /**
- * Returns {@code true} if the provided response should be logged.
- *
- * @param operation
- * The response.
- * @return {@code true} if the provided response should be logged.
- */
- boolean isResponseLoggable(Operation operation);
- }
-
-
-
- /**
- * A filter which performs a logical OR over a set of sub-filters.
- */
- static final class OrFilter implements Filter
- {
- private final Filter[] subFilters;
-
-
-
- /**
- * Creates a new OR filter.
- *
- * @param subFilters
- * The sub-filters.
- */
- OrFilter(final Filter[] subFilters)
- {
- this.subFilters = subFilters;
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- public boolean isConnectLoggable(final ClientConnection connection)
- {
- for (final Filter filter : subFilters)
- {
- if (filter.isConnectLoggable(connection))
- {
- // Succeed fast.
- return true;
- }
- }
- return false;
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- public boolean isDisconnectLoggable(final ClientConnection connection)
- {
- for (final Filter filter : subFilters)
- {
- if (filter.isDisconnectLoggable(connection))
- {
- // Succeed fast.
- return true;
- }
- }
- return false;
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- public boolean isRequestLoggable(final Operation operation)
- {
- for (final Filter filter : subFilters)
- {
- if (filter.isRequestLoggable(operation))
- {
- // Succeed fast.
- return true;
- }
- }
- return false;
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- public boolean isResponseLoggable(final Operation operation)
- {
- for (final Filter filter : subFilters)
- {
- if (filter.isResponseLoggable(operation))
- {
- // Succeed fast.
- return true;
- }
- }
- return false;
- }
-
- }
-
-
-
- /**
- * The root filter which first checks the logger configuration, delegating to
- * a sub-filter if needed.
- */
- static final class RootFilter implements Filter
- {
- private final Filter subFilter;
- private final boolean suppressInternalOperations;
- private final boolean suppressSynchronizationOperations;
- private final FilteringPolicy policy;
-
-
-
- /**
- * Creates a new root filter.
- *
- * @param suppressInternal
- * Indicates whether internal operations should be suppressed.
- * @param suppressSynchronization
- * Indicates whether sync operations should be suppressed.
- * @param policy
- * The filtering policy.
- * @param subFilter
- * The sub-filters.
- */
- RootFilter(final boolean suppressInternal,
- final boolean suppressSynchronization, final FilteringPolicy policy,
- final Filter subFilter)
- {
- this.suppressInternalOperations = suppressInternal;
- this.suppressSynchronizationOperations = suppressSynchronization;
- this.policy = policy;
- this.subFilter = subFilter;
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- public boolean isConnectLoggable(final ClientConnection connection)
- {
- final long connectionID = connection.getConnectionID();
- if (connectionID >= 0 || !suppressInternalOperations)
- {
- switch (policy)
- {
- case INCLUSIVE:
- return subFilter.isConnectLoggable(connection);
- case EXCLUSIVE:
- return !subFilter.isConnectLoggable(connection);
- default: // NO_FILTERING:
- return true;
- }
- }
- else
- {
- return false;
- }
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- public boolean isDisconnectLoggable(final ClientConnection connection)
- {
- final long connectionID = connection.getConnectionID();
- if (connectionID >= 0 || !suppressInternalOperations)
- {
- switch (policy)
- {
- case INCLUSIVE:
- return subFilter.isDisconnectLoggable(connection);
- case EXCLUSIVE:
- return !subFilter.isDisconnectLoggable(connection);
- default: // NO_FILTERING:
- return true;
- }
- }
- else
- {
- return false;
- }
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- public boolean isRequestLoggable(final Operation operation)
- {
- if (isLoggable(operation))
- {
- switch (policy)
- {
- case INCLUSIVE:
- return subFilter.isRequestLoggable(operation);
- case EXCLUSIVE:
- return !subFilter.isRequestLoggable(operation);
- default: // NO_FILTERING:
- return true;
- }
- }
- else
- {
- return false;
- }
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- public boolean isResponseLoggable(final Operation operation)
- {
- if (isLoggable(operation))
- {
- switch (policy)
- {
- case INCLUSIVE:
- return subFilter.isResponseLoggable(operation);
- case EXCLUSIVE:
- return !subFilter.isResponseLoggable(operation);
- default: // NO_FILTERING:
- return true;
- }
- }
- else
- {
- return false;
- }
- }
-
-
-
- // Determines whether the provided operation should be logged.
- private boolean isLoggable(final Operation operation)
- {
- final long connectionID = operation.getConnectionID();
- if (connectionID < 0)
- {
- // This is an internal operation.
- if (operation.isSynchronizationOperation())
- {
- return !suppressSynchronizationOperations;
- }
- else
- {
- return !suppressInternalOperations;
- }
- }
-
- return true;
- }
- }
-
-
-
- /**
- * Filter criteria configuration listener.
- */
- private final class FilterChangeListener implements
- ConfigurationChangeListener<AccessLogFilteringCriteriaCfg>,
- ConfigurationAddListener<AccessLogFilteringCriteriaCfg>,
- ConfigurationDeleteListener<AccessLogFilteringCriteriaCfg>
- {
-
- /**
- * {@inheritDoc}
- */
- public ConfigChangeResult applyConfigurationAdd(
- final AccessLogFilteringCriteriaCfg configuration)
- {
- // Rebuild the filter using the new configuration and criteria.
- buildFilters();
- configuration.addChangeListener(this);
- return new ConfigChangeResult(ResultCode.SUCCESS, false);
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- public ConfigChangeResult applyConfigurationChange(
- final AccessLogFilteringCriteriaCfg configuration)
- {
- // Rebuild the filter using the new configuration and criteria.
- buildFilters();
- return new ConfigChangeResult(ResultCode.SUCCESS, false);
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- public ConfigChangeResult applyConfigurationDelete(
- final AccessLogFilteringCriteriaCfg configuration)
- {
- // Rebuild the filter using the new configuration and criteria.
- buildFilters();
- return new ConfigChangeResult(ResultCode.SUCCESS, false);
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- public boolean isConfigurationAddAcceptable(
- final AccessLogFilteringCriteriaCfg configuration,
- final List<Message> unacceptableReasons)
- {
- return true;
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- public boolean isConfigurationChangeAcceptable(
- final AccessLogFilteringCriteriaCfg configuration,
- final List<Message> unacceptableReasons)
- {
- return true;
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- public boolean isConfigurationDeleteAcceptable(
- final AccessLogFilteringCriteriaCfg configuration,
- final List<Message> unacceptableReasons)
- {
- return true;
- }
- }
-
-
-
- /**
- * The tracer object for the debug logger.
- */
- private static final DebugTracer TRACER = getTracer();
/**
* The category to use when logging responses.
@@ -1134,17 +92,14 @@
final TextAccessLogPublisher startupPublisher =
new TextAccessLogPublisher();
startupPublisher.writer = writer;
- startupPublisher.buildFilters(suppressInternal, false,
- FilteringPolicy.NO_FILTERING);
+ startupPublisher.buildFilters(suppressInternal);
return startupPublisher;
}
- private FileBasedAccessLogPublisherCfg currentConfig = null;
private TextWriter writer = null;
- private Filter filter = null;
- private FilterChangeListener filterChangeListener = null;
+ private FileBasedAccessLogPublisherCfg cfg = null;
@@ -1246,16 +201,13 @@
writer = asyncWriter;
}
- if ((currentConfig.isAsynchronous() && config.isAsynchronous())
- && (currentConfig.getQueueSize() != config.getQueueSize()))
+ if ((cfg.isAsynchronous() && config.isAsynchronous())
+ && (cfg.getQueueSize() != config.getQueueSize()))
{
adminActionRequired = true;
}
- currentConfig = config;
-
- // Rebuild the filter using the new configuration and criteria.
- buildFilters();
+ cfg = config;
}
}
catch (final Exception e)
@@ -1276,49 +228,12 @@
* {@inheritDoc}
*/
@Override
- public void close()
+ protected void close0()
{
writer.shutdown();
-
- if (currentConfig != null)
+ if (cfg != null)
{
- currentConfig.removeFileBasedAccessChangeListener(this);
-
- for (final String criteriaName : currentConfig
- .listAccessLogFilteringCriteria())
- {
- try
- {
- currentConfig.getAccessLogFilteringCriteria(criteriaName)
- .removeChangeListener(filterChangeListener);
- }
- catch (final ConfigException e)
- {
- // Ignore.
- }
- }
- currentConfig
- .removeAccessLogFilteringCriteriaAddListener(filterChangeListener);
- currentConfig
- .removeAccessLogFilteringCriteriaDeleteListener(filterChangeListener);
- }
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public DN getDN()
- {
- if (currentConfig != null)
- {
- return currentConfig.dn();
- }
- else
- {
- return null;
+ cfg.removeFileBasedAccessChangeListener(this);
}
}
@@ -1386,7 +301,6 @@
final Message message = ERR_CONFIG_LOGGING_CANNOT_CREATE_WRITER.get(cfg
.dn().toString(), String.valueOf(e));
throw new InitializationException(message, e);
-
}
catch (final IOException e)
{
@@ -1396,32 +310,9 @@
}
- currentConfig = cfg;
-
- // Rebuild the filter using the new configuration and criteria.
- buildFilters();
-
- // Add change listeners.
- filterChangeListener = new FilterChangeListener();
- for (final String criteriaName : currentConfig
- .listAccessLogFilteringCriteria())
- {
- try
- {
- currentConfig.getAccessLogFilteringCriteria(criteriaName)
- .addChangeListener(filterChangeListener);
- }
- catch (final ConfigException e)
- {
- // Ignore.
- }
- }
- currentConfig
- .addAccessLogFilteringCriteriaAddListener(filterChangeListener);
- currentConfig
- .addAccessLogFilteringCriteriaDeleteListener(filterChangeListener);
-
- currentConfig.addFileBasedAccessChangeListener(this);
+ initializeFilters(cfg);
+ this.cfg = cfg;
+ cfg.addFileBasedAccessChangeListener(this);
}
@@ -1431,12 +322,10 @@
*/
@Override
public boolean isConfigurationAcceptable(
- final AccessLogPublisherCfg configuration,
+ final FileBasedAccessLogPublisherCfg configuration,
final List<Message> unacceptableReasons)
{
- final FileBasedAccessLogPublisherCfg config =
- (FileBasedAccessLogPublisherCfg) configuration;
- return isConfigurationChangeAcceptable(config, unacceptableReasons);
+ return isConfigurationChangeAcceptable(configuration, unacceptableReasons);
}
@@ -1485,7 +374,7 @@
@Override
public void logAbandonRequest(final AbandonOperation abandonOperation)
{
- if (!filter.isRequestLoggable(abandonOperation))
+ if (!isRequestLoggable(abandonOperation))
{
return;
}
@@ -1515,7 +404,7 @@
@Override
public void logAbandonResult(final AbandonOperation abandonOperation)
{
- if (!filter.isResponseLoggable(abandonOperation))
+ if (!isResponseLoggable(abandonOperation))
{
return;
}
@@ -1553,7 +442,7 @@
@Override
public void logAddRequest(final AddOperation addOperation)
{
- if (!filter.isRequestLoggable(addOperation))
+ if (!isRequestLoggable(addOperation))
{
return;
}
@@ -1584,7 +473,7 @@
@Override
public void logAddResponse(final AddOperation addOperation)
{
- if (!filter.isResponseLoggable(addOperation))
+ if (!isResponseLoggable(addOperation))
{
return;
}
@@ -1636,7 +525,7 @@
@Override
public void logBindRequest(final BindOperation bindOperation)
{
- if (!filter.isRequestLoggable(bindOperation))
+ if (!isRequestLoggable(bindOperation))
{
return;
}
@@ -1690,7 +579,7 @@
@Override
public void logBindResponse(final BindOperation bindOperation)
{
- if (!filter.isResponseLoggable(bindOperation))
+ if (!isResponseLoggable(bindOperation))
{
return;
}
@@ -1774,7 +663,7 @@
@Override
public void logCompareRequest(final CompareOperation compareOperation)
{
- if (!filter.isRequestLoggable(compareOperation))
+ if (!isRequestLoggable(compareOperation))
{
return;
}
@@ -1806,7 +695,7 @@
@Override
public void logCompareResponse(final CompareOperation compareOperation)
{
- if (!filter.isResponseLoggable(compareOperation))
+ if (!isResponseLoggable(compareOperation))
{
return;
}
@@ -1858,7 +747,7 @@
@Override
public void logConnect(final ClientConnection clientConnection)
{
- if (!filter.isConnectLoggable(clientConnection))
+ if (!isConnectLoggable(clientConnection))
{
return;
}
@@ -1894,7 +783,7 @@
@Override
public void logDeleteRequest(final DeleteOperation deleteOperation)
{
- if (!filter.isRequestLoggable(deleteOperation))
+ if (!isRequestLoggable(deleteOperation))
{
return;
}
@@ -1925,7 +814,7 @@
@Override
public void logDeleteResponse(final DeleteOperation deleteOperation)
{
- if (!filter.isResponseLoggable(deleteOperation))
+ if (!isResponseLoggable(deleteOperation))
{
return;
}
@@ -1982,7 +871,7 @@
public void logDisconnect(final ClientConnection clientConnection,
final DisconnectReason disconnectReason, final Message message)
{
- if (!filter.isDisconnectLoggable(clientConnection))
+ if (!isDisconnectLoggable(clientConnection))
{
return;
}
@@ -2021,7 +910,7 @@
@Override
public void logExtendedRequest(final ExtendedOperation extendedOperation)
{
- if (!filter.isRequestLoggable(extendedOperation))
+ if (!isRequestLoggable(extendedOperation))
{
return;
}
@@ -2066,7 +955,7 @@
@Override
public void logExtendedResponse(final ExtendedOperation extendedOperation)
{
- if (!filter.isResponseLoggable(extendedOperation))
+ if (!isResponseLoggable(extendedOperation))
{
return;
}
@@ -2132,7 +1021,7 @@
@Override
public void logModifyDNRequest(final ModifyDNOperation modifyDNOperation)
{
- if (!filter.isRequestLoggable(modifyDNOperation))
+ if (!isRequestLoggable(modifyDNOperation))
{
return;
}
@@ -2173,7 +1062,7 @@
@Override
public void logModifyDNResponse(final ModifyDNOperation modifyDNOperation)
{
- if (!filter.isResponseLoggable(modifyDNOperation))
+ if (!isResponseLoggable(modifyDNOperation))
{
return;
}
@@ -2225,7 +1114,7 @@
@Override
public void logModifyRequest(final ModifyOperation modifyOperation)
{
- if (!filter.isRequestLoggable(modifyOperation))
+ if (!isRequestLoggable(modifyOperation))
{
return;
}
@@ -2256,7 +1145,7 @@
@Override
public void logModifyResponse(final ModifyOperation modifyOperation)
{
- if (!filter.isResponseLoggable(modifyOperation))
+ if (!isResponseLoggable(modifyOperation))
{
return;
}
@@ -2308,7 +1197,7 @@
@Override
public void logSearchRequest(final SearchOperation searchOperation)
{
- if (!filter.isRequestLoggable(searchOperation))
+ if (!isRequestLoggable(searchOperation))
{
return;
}
@@ -2362,7 +1251,7 @@
@Override
public void logSearchResultDone(final SearchOperation searchOperation)
{
- if (!filter.isResponseLoggable(searchOperation))
+ if (!isResponseLoggable(searchOperation))
{
return;
}
@@ -2418,7 +1307,7 @@
public void logUnbind(final UnbindOperation unbindOperation)
{
// FIXME: ensure that these are logged in combined mode.
- if (!filter.isRequestLoggable(unbindOperation))
+ if (!isRequestLoggable(unbindOperation))
{
return;
}
@@ -2455,50 +1344,6 @@
- // Build an appropriate set of filters based on the configuration.
- private void buildFilters()
- {
- buildFilters(currentConfig.isSuppressInternalOperations(),
- currentConfig.isSuppressSynchronizationOperations(),
- currentConfig.getFilteringPolicy());
- }
-
-
-
- private void buildFilters(final boolean suppressInternal,
- final boolean suppressSynchronization, final FilteringPolicy policy)
- {
- final ArrayList<Filter> subFilters = new ArrayList<Filter>();
- if (currentConfig != null)
- {
- for (final String criteriaName : currentConfig
- .listAccessLogFilteringCriteria())
- {
- try
- {
- final AccessLogFilteringCriteriaCfg cfg = currentConfig
- .getAccessLogFilteringCriteria(criteriaName);
- subFilters.add(new CriteriaFilter(cfg));
- }
- catch (final ConfigException e)
- {
- // TODO: Unable to decode this access log criteria, so log a warning
- // and continue.
- }
- catch (final DirectoryException e)
- {
- // TODO: Unable to decode this access log criteria, so log a warning
- // and continue.
- }
- }
- }
- final Filter orFilter = new OrFilter(subFilters.toArray(new Filter[0]));
- filter = new RootFilter(suppressInternal, suppressSynchronization, policy,
- orFilter);
- }
-
-
-
// Appends additional log items to the provided builder.
private void logAdditionalLogItems(final Operation operation,
final StringBuilder builder)
diff --git a/opendj-sdk/opends/src/server/org/opends/server/loggers/TextAuditLogPublisher.java b/opendj-sdk/opends/src/server/org/opends/server/loggers/TextAuditLogPublisher.java
index 2e639f7..7683d89 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/loggers/TextAuditLogPublisher.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/loggers/TextAuditLogPublisher.java
@@ -30,9 +30,11 @@
import static org.opends.messages.ConfigMessages.*;
-import static org.opends.server.types.ResultCode.*;
-import static org.opends.server.util.ServerConstants.*;
-import static org.opends.server.util.StaticUtils.*;
+import static org.opends.server.types.ResultCode.SUCCESS;
+import static org.opends.server.util.ServerConstants.EOL;
+import static org.opends.server.util.StaticUtils.getBytes;
+import static org.opends.server.util.StaticUtils.getFileForPath;
+import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
import java.io.File;
import java.io.IOException;
@@ -41,15 +43,9 @@
import org.opends.messages.Message;
import org.opends.server.admin.server.ConfigurationChangeListener;
-import org.opends.server.admin.std.server.AccessLogPublisherCfg;
import org.opends.server.admin.std.server.FileBasedAuditLogPublisherCfg;
-import org.opends.server.api.AccessLogPublisher;
import org.opends.server.config.ConfigException;
-import org.opends.server.core.AddOperation;
-import org.opends.server.core.DeleteOperation;
-import org.opends.server.core.DirectoryServer;
-import org.opends.server.core.ModifyDNOperation;
-import org.opends.server.core.ModifyOperation;
+import org.opends.server.core.*;
import org.opends.server.types.*;
import org.opends.server.util.Base64;
import org.opends.server.util.StaticUtils;
@@ -61,18 +57,14 @@
* This class provides the implementation of the audit logger used by
* the directory server.
*/
-public class TextAuditLogPublisher extends
- AccessLogPublisher<FileBasedAuditLogPublisherCfg> implements
+public final class TextAuditLogPublisher extends
+ AbstractTextAccessLogPublisher<FileBasedAuditLogPublisherCfg> implements
ConfigurationChangeListener<FileBasedAuditLogPublisherCfg>
{
- private boolean suppressInternalOperations = true;
-
- private boolean suppressSynchronizationOperations = false;
-
private TextWriter writer;
- private FileBasedAuditLogPublisherCfg currentConfig;
+ private FileBasedAuditLogPublisherCfg cfg;
@@ -87,10 +79,6 @@
boolean adminActionRequired = false;
ArrayList<Message> messages = new ArrayList<Message>();
- suppressInternalOperations = config.isSuppressInternalOperations();
- suppressSynchronizationOperations = config
- .isSuppressSynchronizationOperations();
-
File logFile = getFileForPath(config.getLogFile());
FileNamingPolicy fnPolicy = new TimeStampNaming(logFile);
@@ -157,13 +145,13 @@
writer = asyncWriter;
}
- if ((currentConfig.isAsynchronous() && config.isAsynchronous())
- && (currentConfig.getQueueSize() != config.getQueueSize()))
+ if ((cfg.isAsynchronous() && config.isAsynchronous())
+ && (cfg.getQueueSize() != config.getQueueSize()))
{
adminActionRequired = true;
}
- currentConfig = config;
+ cfg = config;
}
}
catch (Exception e)
@@ -184,28 +172,10 @@
* {@inheritDoc}
*/
@Override()
- public void close()
+ protected void close0()
{
writer.shutdown();
- currentConfig.removeFileBasedAuditChangeListener(this);
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public DN getDN()
- {
- if (currentConfig != null)
- {
- return currentConfig.dn();
- }
- else
- {
- return null;
- }
+ cfg.removeFileBasedAuditChangeListener(this);
}
@@ -214,44 +184,43 @@
* {@inheritDoc}
*/
@Override()
- public void initializeAccessLogPublisher(
- FileBasedAuditLogPublisherCfg config)
+ public void initializeAccessLogPublisher(FileBasedAuditLogPublisherCfg cfg)
throws ConfigException, InitializationException
{
- File logFile = getFileForPath(config.getLogFile());
+ File logFile = getFileForPath(cfg.getLogFile());
FileNamingPolicy fnPolicy = new TimeStampNaming(logFile);
try
{
- FilePermission perm = FilePermission.decodeUNIXMode(config
+ FilePermission perm = FilePermission.decodeUNIXMode(cfg
.getLogFilePermissions());
LogPublisherErrorHandler errorHandler = new LogPublisherErrorHandler(
- config.dn());
+ cfg.dn());
- boolean writerAutoFlush = config.isAutoFlush()
- && !config.isAsynchronous();
+ boolean writerAutoFlush = cfg.isAutoFlush()
+ && !cfg.isAsynchronous();
MultifileTextWriter writer = new MultifileTextWriter(
- "Multifile Text Writer for " + config.dn().toNormalizedString(),
- config.getTimeInterval(), fnPolicy, perm, errorHandler, "UTF-8",
- writerAutoFlush, config.isAppend(), (int) config.getBufferSize());
+ "Multifile Text Writer for " + cfg.dn().toNormalizedString(),
+ cfg.getTimeInterval(), fnPolicy, perm, errorHandler, "UTF-8",
+ writerAutoFlush, cfg.isAppend(), (int) cfg.getBufferSize());
// Validate retention and rotation policies.
- for (DN dn : config.getRotationPolicyDNs())
+ for (DN dn : cfg.getRotationPolicyDNs())
{
writer.addRotationPolicy(DirectoryServer.getRotationPolicy(dn));
}
- for (DN dn : config.getRetentionPolicyDNs())
+ for (DN dn : cfg.getRetentionPolicyDNs())
{
writer.addRetentionPolicy(DirectoryServer.getRetentionPolicy(dn));
}
- if (config.isAsynchronous())
+ if (cfg.isAsynchronous())
{
this.writer = new AsyncronousTextWriter("Asyncronous Text Writer for "
- + config.dn().toNormalizedString(), config.getQueueSize(), config
+ + cfg.dn().toNormalizedString(), cfg.getQueueSize(), cfg
.isAutoFlush(), writer);
}
else
@@ -261,7 +230,7 @@
}
catch (DirectoryException e)
{
- Message message = ERR_CONFIG_LOGGING_CANNOT_CREATE_WRITER.get(config.dn()
+ Message message = ERR_CONFIG_LOGGING_CANNOT_CREATE_WRITER.get(cfg.dn()
.toString(), String.valueOf(e));
throw new InitializationException(message, e);
@@ -269,18 +238,14 @@
catch (IOException e)
{
Message message = ERR_CONFIG_LOGGING_CANNOT_OPEN_FILE.get(logFile
- .toString(), config.dn().toString(), String.valueOf(e));
+ .toString(), cfg.dn().toString(), String.valueOf(e));
throw new InitializationException(message, e);
}
- suppressInternalOperations = config.isSuppressInternalOperations();
- suppressSynchronizationOperations = config
- .isSuppressSynchronizationOperations();
-
- currentConfig = config;
-
- config.addFileBasedAuditChangeListener(this);
+ initializeFilters(cfg);
+ this.cfg = cfg;
+ cfg.addFileBasedAuditChangeListener(this);
}
@@ -289,12 +254,11 @@
* {@inheritDoc}
*/
@Override
- public boolean isConfigurationAcceptable(AccessLogPublisherCfg configuration,
+ public boolean isConfigurationAcceptable(
+ FileBasedAuditLogPublisherCfg configuration,
List<Message> unacceptableReasons)
{
- FileBasedAuditLogPublisherCfg config =
- (FileBasedAuditLogPublisherCfg) configuration;
- return isConfigurationChangeAcceptable(config, unacceptableReasons);
+ return isConfigurationChangeAcceptable(configuration, unacceptableReasons);
}
@@ -605,31 +569,13 @@
// Determines whether the provided operation should be logged.
private boolean isLoggable(Operation operation)
{
- long connectionID = operation.getConnectionID();
- if (connectionID < 0)
- {
- // This is an internal operation.
- if (operation.isSynchronizationOperation())
- {
- if (suppressSynchronizationOperations)
- {
- return false;
- }
- }
- else
- {
- if (suppressInternalOperations)
- {
- return false;
- }
- }
- }
-
if (operation.getResultCode() != SUCCESS)
{
return false;
}
-
- return true;
+ else
+ {
+ return isResponseLoggable(operation);
+ }
}
}
--
Gitblit v1.10.0