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

Nicolas Capponi
24.08.2015 e433824f46514e6ed863eb6b95c70038331c8c24
OPENDJ-2260 OPENDJ-2271 Integration of common audit into the server
16 files added
24 files modified
4121 ■■■■■ changed files
opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/CsvFileAccessLogPublisherConfiguration.xml 348 ●●●●● patch | view | raw | blame | history
opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/CsvFileHTTPAccessLogPublisherConfiguration.xml 330 ●●●●● patch | view | raw | blame | history
opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/ExternalAccessLogPublisherConfiguration.xml 100 ●●●●● patch | view | raw | blame | history
opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/ExternalHTTPAccessLogPublisherConfiguration.xml 82 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/pom.xml 18 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/resource/config/config.ldif 37 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/resource/schema/02-config.ldif 84 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/admin/AdministrationConnector.java 15 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/api/ConnectionHandler.java 7 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/controls/TransactionIdControl.java 129 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/core/ConnectionHandlerConfigManager.java 4 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/core/DirectoryServer.java 11 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/core/ServerContext.java 8 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/loggers/AbstractLogger.java 71 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/loggers/AccessLogger.java 1 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/loggers/CommonAudit.java 943 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/loggers/CommonAuditAccessLogPublisher.java 554 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/loggers/CommonAuditDependencyProvider.java 85 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/loggers/CommonAuditHTTPAccessLogPublisher.java 109 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/loggers/CommonAuditLogPublisher.java 44 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/loggers/CsvFileAccessLogPublisher.java 63 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/loggers/ExternalAccessLogPublisher.java 63 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/loggers/HTTPAccessLogger.java 1 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/loggers/OpenDJAccessEventBuilder.java 267 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/protocols/LDIFConnectionHandler.java 3 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/protocols/http/CommonAuditHttpAccessAuditFilter.java 169 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/protocols/http/CommonAuditHttpAccessCheckEnabledFilter.java 68 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/protocols/http/HTTPConnectionHandler.java 10 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/protocols/http/LdapHttpApplication.java 18 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/protocols/internal/InternalConnectionHandler.java 6 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/protocols/jmx/JmxConnectionHandler.java 3 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/protocols/ldap/LDAPConnectionHandler.java 3 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/util/ServerConstants.java 5 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/resources/org/opends/server/loggers/audit-config.json 402 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/messages/org/opends/messages/config.properties 6 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/messages/org/opends/messages/logger.properties 31 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/messages/org/opends/messages/protocol.properties 2 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/snmp/src/org/opends/server/snmp/SNMPConnectionHandler.java 10 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/protocols/internal/InternalConnectionHandlerTestCase.java 6 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/protocols/ldap/LdapTestCase.java 5 ●●●● patch | view | raw | blame | history
opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/CsvFileAccessLogPublisherConfiguration.xml
New file
@@ -0,0 +1,348 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
  ! 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 legal-notices/CDDLv1_0.txt
  ! or http://forgerock.org/license/CDDLv1.0.html.
  ! 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 legal-notices/CDDLv1_0.txt.
  ! 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 2015 ForgeRock AS.
  ! -->
<adm:managed-object name="csv-file-access-log-publisher"
  plural-name="csv-file-access-log-publishers"
  package="org.forgerock.opendj.server.config" extends="access-log-publisher"
  xmlns:adm="http://opendj.forgerock.org/admin"
  xmlns:ldap="http://opendj.forgerock.org/admin-ldap">
  <adm:synopsis>
    <adm:user-friendly-plural-name />
    publish access messages to CSV files.
  </adm:synopsis>
  <adm:profile name="ldap">
    <ldap:object-class>
      <ldap:name>ds-cfg-csv-file-access-log-publisher</ldap:name>
      <ldap:superior>ds-cfg-access-log-publisher</ldap:superior>
    </ldap:object-class>
  </adm:profile>
  <adm:property-override name="java-class" advanced="true">
    <adm:default-behavior>
      <adm:defined>
        <adm:value>
          org.opends.server.loggers.CommonAuditAccessLogPublisher
        </adm:value>
      </adm:defined>
    </adm:default-behavior>
  </adm:property-override>
  <adm:property name="log-directory" mandatory="true">
    <adm:synopsis>
      The directory to use for the log files generated by the
      <adm:user-friendly-name />.
      The path to the directory is relative to the server root.
    </adm:synopsis>
    <adm:requires-admin-action>
      <adm:component-restart />
    </adm:requires-admin-action>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>logs</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
     <adm:string>
       <adm:pattern>
        <adm:regex>.*</adm:regex>
        <adm:usage>DIRECTORY</adm:usage>
          <adm:synopsis>
            A path to an existing directory that is readable and writable by the server.
          </adm:synopsis>
        </adm:pattern>
      </adm:string>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-log-directory</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="asynchronous" mandatory="true" advanced="true">
    <adm:synopsis>
      Indicates whether the
      <adm:user-friendly-name />
      will publish records asynchronously.
    </adm:synopsis>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>true</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:boolean />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-asynchronous</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="auto-flush" advanced="true">
    <adm:synopsis>
      Specifies whether to flush the writer after every log record.
    </adm:synopsis>
    <adm:description>
      If the asynchronous writes option is used, the writer is
      flushed after all the log records in the queue are written.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>true</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:boolean />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-auto-flush</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="rotation-policy" multi-valued="true">
    <adm:synopsis>
      The rotation policy to use for the
      <adm:user-friendly-name />
      .
    </adm:synopsis>
    <adm:description>
      When multiple policies are used, rotation will occur if any
      policy's conditions are met.
    </adm:description>
    <adm:default-behavior>
      <adm:alias>
        <adm:synopsis>
          No rotation policy is used and log rotation will not occur.
        </adm:synopsis>
      </adm:alias>
    </adm:default-behavior>
    <adm:syntax>
      <adm:aggregation parent-path="/"
        relation-name="log-rotation-policy" />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-rotation-policy</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="retention-policy" multi-valued="true">
    <adm:synopsis>
      The retention policy to use for the
      <adm:user-friendly-name />
      .
    </adm:synopsis>
    <adm:description>
      When multiple policies are used, log files are cleaned when
      any of the policy's conditions are met.
    </adm:description>
    <adm:default-behavior>
      <adm:alias>
        <adm:synopsis>
          No retention policy is used and log files are never cleaned.
        </adm:synopsis>
      </adm:alias>
    </adm:default-behavior>
    <adm:syntax>
      <adm:aggregation parent-path="/"
        relation-name="log-retention-policy" />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-retention-policy</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="log-control-oids">
    <adm:synopsis>
      Specifies whether control OIDs will be included in operation log records.
    </adm:synopsis>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>false</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:boolean />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-log-control-oids</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="tamper-evident">
    <adm:synopsis>
      Specifies whether the log should be signed in order to detect tampering.
    </adm:synopsis>
    <adm:description>
      Every log record will be signed, making it possible to verify that the log has not been tampered with.
      This feature has a significative impact on performance of the server.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>false</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:boolean />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-tamper-evident</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="signature-time-interval" advanced="true">
    <adm:synopsis>
      Specifies the interval at which to sign the log file when the tamper-evident option
      is enabled.
    </adm:synopsis>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>3s</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:duration base-unit="ms" lower-limit="1" />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-signature-time-interval</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="key-store-file">
    <adm:synopsis>
      Specifies the path to the file that contains the private key
      information. This may be an absolute path, or a path that is
      relative to the <adm:product-name /> instance root.
    </adm:synopsis>
    <adm:description>
      Changes to this property will take effect the next
      time that the key store is accessed.
    </adm:description>
    <adm:default-behavior>
        <adm:undefined />
    </adm:default-behavior>
    <adm:syntax>
     <adm:string>
        <adm:pattern>
         <adm:regex>.*</adm:regex>
         <adm:usage>FILE</adm:usage>
          <adm:synopsis>
            A path to an existing file that is readable by the server.
          </adm:synopsis>
        </adm:pattern>
      </adm:string>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-key-store-file</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property-reference name="key-store-pin-file" />
  <adm:property name="csv-quote-char" advanced="true">
    <adm:synopsis>
      The character to append and prepend to a CSV field when writing in CSV format.
    </adm:synopsis>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>\"</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
     <adm:string>
       <adm:pattern>
        <adm:regex>.*</adm:regex>
        <adm:usage>QUOTE CHARACTER</adm:usage>
          <adm:synopsis>
            The quote character to use when writting in CSV format.
          </adm:synopsis>
        </adm:pattern>
      </adm:string>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-csv-quote-char</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="csv-delimiter-char">
    <adm:synopsis>
      The delimiter character to use when writing in CSV format.
    </adm:synopsis>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>,</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
     <adm:string>
       <adm:pattern>
        <adm:regex>.*</adm:regex>
        <adm:usage>DELIMITER CHARACTER</adm:usage>
          <adm:synopsis>
            The delimiter character to use when writing in CSV format.
          </adm:synopsis>
        </adm:pattern>
      </adm:string>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-csv-delimiter-char</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="csv-eol-symbols" advanced="true">
    <adm:synopsis>
      The string that marks the end of a line.
    </adm:synopsis>
    <adm:default-behavior>
      <adm:alias>
        <adm:synopsis>
          Use the platform specific end of line character sequence.
        </adm:synopsis>
      </adm:alias>
    </adm:default-behavior>
    <adm:syntax>
     <adm:string>
       <adm:pattern>
        <adm:regex>.*</adm:regex>
        <adm:usage>STRING</adm:usage>
          <adm:synopsis>
            The string that marks the end of a line.
          </adm:synopsis>
        </adm:pattern>
      </adm:string>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-csv-eol-symbols</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
</adm:managed-object>
opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/CsvFileHTTPAccessLogPublisherConfiguration.xml
New file
@@ -0,0 +1,330 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
  ! 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 legal-notices/CDDLv1_0.txt
  ! or http://forgerock.org/license/CDDLv1.0.html.
  ! 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 legal-notices/CDDLv1_0.txt.
  ! 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 2015 ForgeRock AS.
  ! -->
<adm:managed-object name="csv-file-http-access-log-publisher"
  plural-name="csv-file-http-access-log-publishers"
  package="org.forgerock.opendj.server.config" extends="http-access-log-publisher"
  xmlns:adm="http://opendj.forgerock.org/admin"
  xmlns:ldap="http://opendj.forgerock.org/admin-ldap">
  <adm:synopsis>
    <adm:user-friendly-plural-name />
    publish HTTP access messages to CSV files.
  </adm:synopsis>
  <adm:profile name="ldap">
    <ldap:object-class>
      <ldap:name>ds-cfg-csv-file-http-access-log-publisher</ldap:name>
      <ldap:superior>ds-cfg-http-access-log-publisher</ldap:superior>
    </ldap:object-class>
  </adm:profile>
  <adm:property-override name="java-class" advanced="true">
    <adm:default-behavior>
      <adm:defined>
        <adm:value>
          org.opends.server.loggers.CommonAuditHTTPAccessLogPublisher
        </adm:value>
      </adm:defined>
    </adm:default-behavior>
  </adm:property-override>
  <adm:property name="log-directory" mandatory="true">
    <adm:synopsis>
      The directory to use for the log files generated by the
      <adm:user-friendly-name />.
      The path to the directory is relative to the server root.
    </adm:synopsis>
    <adm:requires-admin-action>
      <adm:component-restart />
    </adm:requires-admin-action>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>logs</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
     <adm:string>
       <adm:pattern>
        <adm:regex>.*</adm:regex>
        <adm:usage>DIRECTORY</adm:usage>
          <adm:synopsis>
            A path to an existing directory that is readable and writable by the server.
          </adm:synopsis>
        </adm:pattern>
      </adm:string>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-log-directory</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="asynchronous" mandatory="true" advanced="true">
    <adm:synopsis>
      Indicates whether the
      <adm:user-friendly-name />
      will publish records asynchronously.
    </adm:synopsis>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>true</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:boolean />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-asynchronous</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="auto-flush" advanced="true">
    <adm:synopsis>
      Specifies whether to flush the writer after every log record.
    </adm:synopsis>
    <adm:description>
      If the asynchronous writes option is used, the writer is
      flushed after all the log records in the queue are written.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>true</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:boolean />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-auto-flush</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="rotation-policy" multi-valued="true">
    <adm:synopsis>
      The rotation policy to use for the
      <adm:user-friendly-name />
      .
    </adm:synopsis>
    <adm:description>
      When multiple policies are used, rotation will occur if any
      policy's conditions are met.
    </adm:description>
    <adm:default-behavior>
      <adm:alias>
        <adm:synopsis>
          No rotation policy is used and log rotation will not occur.
        </adm:synopsis>
      </adm:alias>
    </adm:default-behavior>
    <adm:syntax>
      <adm:aggregation parent-path="/"
        relation-name="log-rotation-policy" />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-rotation-policy</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="retention-policy" multi-valued="true">
    <adm:synopsis>
      The retention policy to use for the
      <adm:user-friendly-name />
      .
    </adm:synopsis>
    <adm:description>
      When multiple policies are used, log files are cleaned when
      any of the policy's conditions are met.
    </adm:description>
    <adm:default-behavior>
      <adm:alias>
        <adm:synopsis>
          No retention policy is used and log files are never cleaned.
        </adm:synopsis>
      </adm:alias>
    </adm:default-behavior>
    <adm:syntax>
      <adm:aggregation parent-path="/"
        relation-name="log-retention-policy" />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-retention-policy</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="tamper-evident">
    <adm:synopsis>
      Specifies whether the log should be signed in order to detect tampering.
    </adm:synopsis>
    <adm:description>
      Every log record will be signed, making it possible to verify that the log has not been tampered with.
      This feature has a significative impact on performance of the server.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>false</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:boolean />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-tamper-evident</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="signature-time-interval" advanced="true">
    <adm:synopsis>
      Specifies the interval at which to sign the log file when secure option
      is enabled.
    </adm:synopsis>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>3s</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:duration base-unit="ms" lower-limit="1" />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-signature-time-interval</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="key-store-file">
    <adm:synopsis>
      Specifies the path to the file that contains the private key
      information. This may be an absolute path, or a path that is
      relative to the <adm:product-name /> instance root.
    </adm:synopsis>
    <adm:description>
      Changes to this property will take effect the next
      time that the key store is accessed.
    </adm:description>
    <adm:default-behavior>
        <adm:undefined />
    </adm:default-behavior>
    <adm:syntax>
     <adm:string>
        <adm:pattern>
         <adm:regex>.*</adm:regex>
         <adm:usage>FILE</adm:usage>
          <adm:synopsis>
            A path to an existing file that is readable by the server.
          </adm:synopsis>
        </adm:pattern>
      </adm:string>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-key-store-file</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property-reference name="key-store-pin-file" />
  <adm:property name="csv-quote-char" advanced="true">
    <adm:synopsis>
      The character to append and prepend to a CSV field when writing in CSV format.
    </adm:synopsis>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>\"</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
     <adm:string>
       <adm:pattern>
        <adm:regex>.*</adm:regex>
        <adm:usage>QUOTE CHARACTER</adm:usage>
          <adm:synopsis>
            The quote character to use when writing in CSV format.
          </adm:synopsis>
        </adm:pattern>
      </adm:string>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-csv-quote-char</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="csv-delimiter-char">
    <adm:synopsis>
      The delimiter character to use when writing in CSV format.
    </adm:synopsis>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>,</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
     <adm:string>
       <adm:pattern>
        <adm:regex>.*</adm:regex>
        <adm:usage>DELIMITER CHARACTER</adm:usage>
          <adm:synopsis>
            The delimiter character to use when writing in CSV format.
          </adm:synopsis>
        </adm:pattern>
      </adm:string>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-csv-delimiter-char</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="csv-eol-symbols" advanced="true">
    <adm:synopsis>
      The string that marks the end of a line.
    </adm:synopsis>
    <adm:default-behavior>
      <adm:alias>
        <adm:synopsis>
          Use the platform specific end of line character sequence.
        </adm:synopsis>
      </adm:alias>
    </adm:default-behavior>
    <adm:syntax>
     <adm:string>
       <adm:pattern>
        <adm:regex>.*</adm:regex>
        <adm:usage>EOL SYMBOLS</adm:usage>
          <adm:synopsis>
            The string that marks the end of a line.
          </adm:synopsis>
        </adm:pattern>
      </adm:string>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-csv-eol-symbols</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
</adm:managed-object>
opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/ExternalAccessLogPublisherConfiguration.xml
New file
@@ -0,0 +1,100 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
  ! 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 legal-notices/CDDLv1_0.txt
  ! or http://forgerock.org/license/CDDLv1.0.html.
  ! 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 legal-notices/CDDLv1_0.txt.
  ! 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 2015 ForgeRock AS.
  ! -->
<adm:managed-object name="external-access-log-publisher"
  plural-name="external-access-log-publishers"
  package="org.forgerock.opendj.server.config" extends="access-log-publisher"
  xmlns:adm="http://opendj.forgerock.org/admin"
  xmlns:ldap="http://opendj.forgerock.org/admin-ldap">
  <adm:synopsis>
    <adm:user-friendly-plural-name />
    publish access messages to an external handler.
  </adm:synopsis>
  <adm:profile name="ldap">
    <ldap:object-class>
      <ldap:name>ds-cfg-external-access-log-publisher</ldap:name>
      <ldap:superior>ds-cfg-access-log-publisher</ldap:superior>
    </ldap:object-class>
  </adm:profile>
  <adm:property-override name="java-class" advanced="true">
    <adm:default-behavior>
      <adm:defined>
        <adm:value>
          org.opends.server.loggers.CommonAuditAccessLogPublisher
        </adm:value>
      </adm:defined>
    </adm:default-behavior>
  </adm:property-override>
  <adm:property name="config-file" mandatory="true">
    <adm:synopsis>
      The JSON configuration file that defines the
      <adm:user-friendly-name />.
      The content of the JSON configuration file depends on
      the type of external audit event handler.
      The path to the file is relative to the server root.
    </adm:synopsis>
    <adm:requires-admin-action>
      <adm:component-restart />
    </adm:requires-admin-action>
    <adm:default-behavior>
        <adm:undefined />
    </adm:default-behavior>
    <adm:syntax>
     <adm:string>
       <adm:pattern>
        <adm:regex>.*</adm:regex>
        <adm:usage>FILE</adm:usage>
          <adm:synopsis>
            A path to an existing file that is readable by the server.
          </adm:synopsis>
        </adm:pattern>
      </adm:string>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-config-file</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="log-control-oids">
    <adm:synopsis>
      Specifies whether control OIDs will be included in operation log records.
    </adm:synopsis>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>false</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:boolean />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-log-control-oids</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
</adm:managed-object>
opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/ExternalHTTPAccessLogPublisherConfiguration.xml
New file
@@ -0,0 +1,82 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
  ! 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 legal-notices/CDDLv1_0.txt
  ! or http://forgerock.org/license/CDDLv1.0.html.
  ! 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 legal-notices/CDDLv1_0.txt.
  ! 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 2015 ForgeRock AS.
  ! -->
<adm:managed-object name="external-http-access-log-publisher"
  plural-name="external-http-access-log-publishers"
  package="org.forgerock.opendj.server.config" extends="http-access-log-publisher"
  xmlns:adm="http://opendj.forgerock.org/admin"
  xmlns:ldap="http://opendj.forgerock.org/admin-ldap">
  <adm:synopsis>
    <adm:user-friendly-plural-name />
    publish HTTP access messages to an external handler.
  </adm:synopsis>
  <adm:profile name="ldap">
    <ldap:object-class>
      <ldap:name>ds-cfg-external-http-access-log-publisher</ldap:name>
      <ldap:superior>ds-cfg-http-access-log-publisher</ldap:superior>
    </ldap:object-class>
  </adm:profile>
  <adm:property-override name="java-class" advanced="true">
    <adm:default-behavior>
      <adm:defined>
        <adm:value>
          org.opends.server.loggers.CommonAuditHTTPAccessLogPublisher
        </adm:value>
      </adm:defined>
    </adm:default-behavior>
  </adm:property-override>
  <adm:property name="config-file" mandatory="true">
    <adm:synopsis>
      The JSON configuration file that defines the
      <adm:user-friendly-name />.
      The content of the JSON configuration file depends on
      the type of external audit event handler.
      The path to the file is relative to the server root.
    </adm:synopsis>
    <adm:requires-admin-action>
      <adm:component-restart />
    </adm:requires-admin-action>
    <adm:default-behavior>
        <adm:undefined />
    </adm:default-behavior>
    <adm:syntax>
     <adm:string>
       <adm:pattern>
        <adm:regex>.*</adm:regex>
        <adm:usage>FILE</adm:usage>
          <adm:synopsis>
            A path to an existing file that is readable by the server.
          </adm:synopsis>
        </adm:pattern>
      </adm:string>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-config-file</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
</adm:managed-object>
opendj-server-legacy/pom.xml
@@ -125,6 +125,11 @@
    <dependency>
      <groupId>org.forgerock.commons</groupId>
      <artifactId>forgerock-util</artifactId>
    </dependency>
    <dependency>
      <groupId>org.forgerock.commons</groupId>
      <artifactId>json-resource</artifactId>
    </dependency>
@@ -142,6 +147,19 @@
      <groupId>org.forgerock.http</groupId>
      <artifactId>chf-http-servlet</artifactId>
    </dependency>
    <dependency>
      <groupId>org.forgerock.commons</groupId>
      <artifactId>forgerock-audit-core</artifactId>
    </dependency>
    <dependency>
      <groupId>org.forgerock.commons</groupId>
      <artifactId>forgerock-audit-handler-csv</artifactId>
    </dependency>
    <dependency>
      <groupId>org.forgerock.commons</groupId>
      <artifactId>forgerock-audit-json</artifactId>
    </dependency>
    <!-- servlet and mail -->
    <dependency>
opendj-server-legacy/resource/config/config.ldif
@@ -605,6 +605,26 @@
ds-cfg-rotation-policy: cn=Size Limit Rotation Policy,cn=Log Rotation Policies,cn=config
ds-cfg-retention-policy: cn=File Count Retention Policy,cn=Log Retention Policies,cn=config
dn: cn=Common Audit Csv File Access Logger,cn=Loggers,cn=config
objectClass: top
objectClass: ds-cfg-log-publisher
objectClass: ds-cfg-access-log-publisher
objectClass: ds-cfg-csv-file-access-log-publisher
cn: Csv File Access Logger
ds-cfg-java-class: org.opends.server.loggers.CsvFileAccessLogPublisher
ds-cfg-enabled: false
ds-cfg-log-directory: logs
ds-cfg-suppress-internal-operations: true
ds-cfg-suppress-synchronization-operations: false
ds-cfg-log-control-oids: false
ds-cfg-asynchronous: true
ds-cfg-rotation-policy: cn=24 Hours Time Limit Rotation Policy,cn=Log Rotation Policies,cn=config
ds-cfg-rotation-policy: cn=Size Limit Rotation Policy,cn=Log Rotation Policies,cn=config
ds-cfg-retention-policy: cn=File Count Retention Policy,cn=Log Retention Policies,cn=config
ds-cfg-tamper-evident: false
ds-cfg-key-store-file: config/audit-keystore
ds-cfg-key-store-pin-file: config/audit-keystore.pin
dn: cn=File-Based HTTP Access Logger,cn=Loggers,cn=config
objectClass: top
objectClass: ds-cfg-log-publisher
@@ -620,6 +640,23 @@
ds-cfg-rotation-policy: cn=Size Limit Rotation Policy,cn=Log Rotation Policies,cn=config
ds-cfg-retention-policy: cn=File Count Retention Policy,cn=Log Retention Policies,cn=config
dn: cn=Common Audit Csv File HTTP Access Logger,cn=Loggers,cn=config
objectClass: top
objectClass: ds-cfg-log-publisher
objectClass: ds-cfg-http-access-log-publisher
objectClass: ds-cfg-csv-file-http-access-log-publisher
cn: Csv File HTTP Access Logger
ds-cfg-java-class: org.opends.server.loggers.CommonAuditHTTPAccessLogPublisher
ds-cfg-enabled: false
ds-cfg-log-directory: logs
ds-cfg-asynchronous: true
ds-cfg-rotation-policy: cn=24 Hours Time Limit Rotation Policy,cn=Log Rotation Policies,cn=config
ds-cfg-rotation-policy: cn=Size Limit Rotation Policy,cn=Log Rotation Policies,cn=config
ds-cfg-retention-policy: cn=File Count Retention Policy,cn=Log Retention Policies,cn=config
ds-cfg-tamper-evident: false
ds-cfg-key-store-file: config/audit-keystore
ds-cfg-key-store-pin-file: config/audit-keystore.pin
dn: cn=File-Based Audit Logger,cn=Loggers,cn=config
objectClass: top
objectClass: ds-cfg-log-publisher
opendj-server-legacy/resource/schema/02-config.ldif
@@ -3806,6 +3806,41 @@
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
  SINGLE-VALUE
  X-ORIGIN 'OpenDJ Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.149
  NAME 'ds-cfg-signature-time-interval'
  EQUALITY caseIgnoreMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  X-ORIGIN 'OpenDJ Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.150
  NAME 'ds-cfg-log-directory'
  EQUALITY caseIgnoreMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  SINGLE-VALUE
  X-ORIGIN 'OpenDJ Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.151
  NAME 'ds-cfg-csv-quote-char'
  EQUALITY caseIgnoreMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  SINGLE-VALUE
  X-ORIGIN 'OpenDJ Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.152
  NAME 'ds-cfg-csv-delimiter-char'
  EQUALITY caseIgnoreMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  SINGLE-VALUE
  X-ORIGIN 'OpenDJ Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.153
  NAME 'ds-cfg-csv-eol-symbols'
  EQUALITY caseIgnoreMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  SINGLE-VALUE
  X-ORIGIN 'OpenDJ Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.154
  NAME 'ds-cfg-tamper-evident'
  EQUALITY booleanMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
  SINGLE-VALUE
  X-ORIGIN 'OpenDJ Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
  NAME 'ds-cfg-access-control-handler'
  SUP top
@@ -5844,8 +5879,55 @@
objectClasses: ( 1.3.6.1.4.1.36733.2.1.2.27
  NAME 'ds-task-reset-change-number'
  SUP ds-task
  STRUCTURAL
  MUST ( ds-task-reset-change-number-to $
        ds-task-reset-change-number-base-dn $
        ds-task-reset-change-number-csn )
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.36733.2.1.2.28
  NAME 'ds-cfg-csv-file-access-log-publisher'
  SUP ds-cfg-access-log-publisher
  STRUCTURAL
  MUST ( ds-cfg-log-directory )
  MAY ( ds-cfg-log-control-oids $
        ds-cfg-csv-quote-char $
        ds-cfg-csv-delimiter-char $
        ds-cfg-csv-eol-symbols $
        ds-cfg-auto-flush $
        ds-cfg-asynchronous $
        ds-cfg-rotation-policy $
        ds-cfg-retention-policy $
        ds-cfg-tamper-evident $
        ds-cfg-signature-time-interval $
        ds-cfg-key-store-file $
        ds-cfg-key-store-pin-file )
  X-ORIGIN 'OpenDJ Directory Server' )
objectClasses: ( 1.3.6.1.4.1.36733.2.1.2.29
  NAME 'ds-cfg-csv-file-http-access-log-publisher'
  SUP ds-cfg-http-access-log-publisher
  STRUCTURAL
  MUST ( ds-cfg-log-directory )
  MAY ( ds-cfg-csv-quote-char $
        ds-cfg-csv-delimiter-char $
        ds-cfg-csv-eol-symbols $
        ds-cfg-auto-flush $
        ds-cfg-asynchronous $
        ds-cfg-rotation-policy $
        ds-cfg-retention-policy $
        ds-cfg-tamper-evident $
        ds-cfg-signature-time-interval $
        ds-cfg-key-store-file $
        ds-cfg-key-store-pin-file )
  X-ORIGIN 'OpenDJ Directory Server' )
objectClasses: ( 1.3.6.1.4.1.36733.2.1.2.30
  NAME 'ds-cfg-external-access-log-publisher'
  SUP ds-cfg-access-log-publisher
  STRUCTURAL
  MUST ( ds-cfg-config-file )
  MAY ( ds-cfg-log-control-oids )
  X-ORIGIN 'OpenDJ Directory Server' )
objectClasses: ( 1.3.6.1.4.1.36733.2.1.2.31
  NAME 'ds-cfg-external-http-access-log-publisher'
  SUP ds-cfg-http-access-log-publisher
  STRUCTURAL
  MUST ( ds-cfg-config-file )
  X-ORIGIN 'OpenDJ Directory Server' )
opendj-server-legacy/src/main/java/org/opends/server/admin/AdministrationConnector.java
@@ -116,6 +116,8 @@
  private static final SSLClientAuthPolicy ADMIN_SSL_CLIENT_AUTH_POLICY =
    SSLClientAuthPolicy.DISABLED;
  private final ServerContext serverContext;
  /**
   * Initializes this administration connector provider based on the
   * information in the provided administration connector
@@ -142,7 +144,7 @@
    // Administration Connector uses the LDAP connection handler implementation
    adminConnectionHandler = new LDAPConnectionHandler(
        new SynchronousStrategy(), FRIENDLY_NAME);
    adminConnectionHandler.initializeConnectionHandler(new LDAPConnectionCfgAdapter(config));
    adminConnectionHandler.initializeConnectionHandler(serverContext, new LDAPConnectionCfgAdapter(config));
    adminConnectionHandler.setAdminConnectionHandler();
    // Register this as a change listener.
@@ -150,10 +152,15 @@
  }
  /** Create an instance of the administration connector. */
  public AdministrationConnector()
  /**
   * Creates an instance of the administration connector.
   *
   * @param serverContext
   *            The server context.
   **/
  public AdministrationConnector(ServerContext serverContext)
  {
    // Do nothing.
    this.serverContext = serverContext;
  }
  /**
opendj-server-legacy/src/main/java/org/opends/server/api/ConnectionHandler.java
@@ -27,13 +27,16 @@
package org.opends.server.api;
import static org.opends.messages.ProtocolMessages.*;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.forgerock.i18n.LocalizableMessage;
import org.opends.server.admin.std.server.ConnectionHandlerCfg;
import org.opends.server.core.ServerContext;
import org.forgerock.opendj.config.server.ConfigException;
import org.opends.server.monitors.ConnectionHandlerMonitor;
import org.opends.server.types.DN;
@@ -191,6 +194,8 @@
   * Initializes this connection handler provider based on the
   * information in the provided connection handler configuration.
   *
   * @param serverContext
   *            The server context.
   * @param configuration
   *          The connection handler configuration that contains the
   *          information to use to initialize this connection
@@ -203,7 +208,7 @@
   *           If a problem occurs during initialization that is not
   *           related to the server configuration.
   */
  public abstract void initializeConnectionHandler(T configuration)
  public abstract void initializeConnectionHandler(ServerContext serverContext, T configuration)
      throws ConfigException, InitializationException;
opendj-server-legacy/src/main/java/org/opends/server/controls/TransactionIdControl.java
New file
@@ -0,0 +1,129 @@
/*
 * 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 legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * 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 legal-notices/CDDLv1_0.txt.
 * 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 2015 ForgeRock AS
 */
package org.opends.server.controls;
import static org.opends.server.util.ServerConstants.OID_TRANSACTION_ID_CONTROL;
import java.io.IOException;
import org.forgerock.opendj.io.ASN1Writer;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.ResultCode;
import org.opends.messages.ProtocolMessages;
import org.opends.server.types.Control;
import org.opends.server.types.DirectoryException;
/**
 * Control that provides a transaction ID.
 * <p>
 * The transaction ID is related to Common Audit : it is used for tracking the
 * processing of a user-interaction as it passes through the Forgerock stack
 * <p>
 * The control's value is the UTF-8 encoding of the transaction ID.
 */
public class TransactionIdControl extends Control
{
  /** ControlDecoder implementation to decode this control from a ByteString. */
  private static final class Decoder implements ControlDecoder<TransactionIdControl>
  {
    @Override
    public TransactionIdControl decode(boolean isCritical, ByteString value) throws DirectoryException
    {
      if (value == null)
      {
        throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
            ProtocolMessages.ERR_TRANSACTION_ID_CONTROL_HAS_NO_VALUE.get());
      }
      return new TransactionIdControl(isCritical, value.toString());
    }
    @Override
    public String getOID()
    {
      return OID_TRANSACTION_ID_CONTROL;
    }
  }
  /** The Control Decoder that can be used to decode this control. */
  public static final ControlDecoder<TransactionIdControl> DECODER = new Decoder();
  /** The id value of this control. */
  private final String transactionId;
  /**
   * Creates a new Transaction Id Control.
   *
   * @param  isCritical  Indicates whether this control should be considered
   *                     critical to the operation processing.
   * @param  transactionId The id to pass through this control.
   */
  public TransactionIdControl(boolean isCritical, String transactionId)
  {
    super(OID_TRANSACTION_ID_CONTROL, isCritical);
    this.transactionId = transactionId;
  }
  /**
   * Writes this control's value to an ASN.1 writer. The value (if any) must be
   * written as an ASN1OctetString.
   *
   * @param writer
   *          The ASN.1 output stream to write to.
   * @throws IOException
   *           If a problem occurs while writing to the stream.
   */
  @Override
  public void writeValue(ASN1Writer writer) throws IOException
  {
    writer.writeOctetString(transactionId);
  }
  /**
   * Retrieves the transaction id associated with this control.
   *
   * @return  The transaction id associated with this control.
   */
  public String getTransactionId()
  {
    return transactionId;
  }
  /**
   * Appends a string representation of this control to the provided buffer.
   *
   * @param buffer
   *          The buffer to which the information should be appended.
   */
  @Override
  public void toString(StringBuilder buffer)
  {
    buffer.append("TransactionIdControl(id=");
    buffer.append(transactionId);
    buffer.append(")");
  }
}
opendj-server-legacy/src/main/java/org/opends/server/core/ConnectionHandlerConfigManager.java
@@ -294,7 +294,7 @@
    AdministrationConnectorCfg administrationConnectorCfg =
      root.getAdministrationConnector();
    AdministrationConnector ac = new AdministrationConnector();
    AdministrationConnector ac = new AdministrationConnector(serverContext);
    ac.initializeAdministrationConnector(administrationConnectorCfg);
    // Put this connection handler in the hash so that we will be
@@ -354,7 +354,7 @@
          pd.loadClass(className, ConnectionHandler.class);
      ConnectionHandler<T> connectionHandler = theClass.newInstance();
      connectionHandler.initializeConnectionHandler(config);
      connectionHandler.initializeConnectionHandler(serverContext, config);
      return connectionHandler;
    } catch (Exception e) {
opendj-server-legacy/src/main/java/org/opends/server/core/DirectoryServer.java
@@ -138,6 +138,7 @@
import org.opends.server.extensions.DiskSpaceMonitor;
import org.opends.server.extensions.JMXAlertHandler;
import org.opends.server.loggers.AccessLogger;
import org.opends.server.loggers.CommonAudit;
import org.opends.server.loggers.DebugLogPublisher;
import org.opends.server.loggers.DebugLogger;
import org.opends.server.loggers.ErrorLogPublisher;
@@ -726,6 +727,9 @@
  /** Entry point for server configuration. */
  private org.forgerock.opendj.config.server.ServerManagementContext serverManagementContext;
  /** Entry point to common audit service, where all audit events must be published. */
  private CommonAudit commonAudit;
  /** Class that prints the version of OpenDJ server to System.out. */
  public static final class DirectoryServerVersionHandler implements VersionHandler
  {
@@ -824,6 +828,12 @@
    {
      return directoryServer.diskSpaceMonitor;
    }
    @Override
    public CommonAudit getCommonAudit()
    {
      return directoryServer.commonAudit;
    }
  }
  /**
@@ -1354,6 +1364,7 @@
      retentionPolicyConfigManager = new LogRetentionPolicyConfigManager(serverContext);
      retentionPolicyConfigManager.initializeLogRetentionPolicyConfig();
      commonAudit = new CommonAudit();
      loggerConfigManager = new LoggerConfigManager(serverContext);
      loggerConfigManager.initializeLoggerConfig();
opendj-server-legacy/src/main/java/org/opends/server/core/ServerContext.java
@@ -27,6 +27,7 @@
import org.forgerock.opendj.config.server.ServerManagementContext;
import org.opends.server.extensions.DiskSpaceMonitor;
import org.opends.server.loggers.CommonAudit;
import org.opends.server.schema.SchemaUpdater;
import org.opends.server.types.DirectoryEnvironmentConfig;
import org.opends.server.types.Schema;
@@ -107,4 +108,11 @@
   * @return the Disk Space Monioring service
   */
  DiskSpaceMonitor getDiskSpaceMonitor();
  /**
   * Returns the common audit manager.
   *
   * @return the common audit manager
   */
  CommonAudit getCommonAudit();
}
opendj-server-legacy/src/main/java/org/opends/server/loggers/AbstractLogger.java
@@ -26,6 +26,7 @@
 */
package org.opends.server.loggers;
import static org.opends.server.loggers.CommonAudit.*;
import static org.opends.messages.ConfigMessages.*;
import static org.opends.server.util.StaticUtils.*;
@@ -185,7 +186,7 @@
  private final Arg3<Object, Object, Object>
      invalidLoggerClassErrorMessage;
  ServerContext serverContext;
  private ServerContext serverContext;
  /**
   * The constructor for this class.
@@ -226,13 +227,20 @@
    {
      config.addChangeListener((ConfigurationChangeListener) this);
      if(config.isEnabled())
      if (config.isEnabled())
      {
        addLogPublisher(getLogPublisher(config));
        final P logPublisher = isCommonAuditConfig(config) ?
            getLogPublisherForCommonAudit(config) : getLogPublisher(config);
        addLogPublisher(logPublisher);
      }
    }
  }
  ServerContext getServerContext()
  {
    return serverContext;
  }
  /** {@inheritDoc} */
  @Override
  public boolean isConfigurationAddAcceptable(C config,
@@ -263,7 +271,9 @@
    {
      try
      {
        addLogPublisher(getLogPublisher(config));
        final P logPublisher = isCommonAuditConfig(config) ?
            getLogPublisherForCommonAudit(config) : getLogPublisher(config);
        addLogPublisher(logPublisher);
      }
      catch(ConfigException e)
      {
@@ -274,8 +284,7 @@
      catch (Exception e)
      {
        LocalizedLogger.getLoggerForThisClass().traceException(e);
        ccr.addMessage(ERR_CONFIG_LOGGER_CANNOT_CREATE_LOGGER.get(
            config.dn(), stackTraceToSingleLineString(e)));
        ccr.addMessage(ERR_CONFIG_LOGGER_CANNOT_CREATE_LOGGER.get(config.dn(), stackTraceToSingleLineString(e)));
        ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
      }
    }
@@ -301,9 +310,9 @@
    final ConfigChangeResult ccr = new ConfigChangeResult();
    P logPublisher = findLogPublisher(config.dn());
    if(logPublisher == null)
    if (logPublisher == null)
    {
      if(config.isEnabled())
      if (config.isEnabled())
      {
        // Needs to be added and enabled.
        return applyConfigurationAdd(config);
@@ -311,26 +320,38 @@
    }
    else
    {
      if(config.isEnabled())
      if (config.isEnabled())
      {
        // The publisher is currently active, so we don't need to do anything.
        // Changes to the class name cannot be
        // applied dynamically, so if the class name did change then
        // indicate that administrative action is required for that
        // change to take effect.
        String className = config.getJavaClass();
        if(!className.equals(logPublisher.getClass().getName()))
        if (!className.equals(logPublisher.getClass().getName()))
        {
          ccr.setAdminActionRequired(true);
        }
        try
        {
          if (isCommonAuditConfig(config))
          {
            serverContext.getCommonAudit().addOrUpdatePublisher(config);
          } // else the publisher is currently active, so we don't need to do
            // anything.
        }
        catch (Exception e)
        {
          LocalizedLogger.getLoggerForThisClass().traceException(e);
          ccr.addMessage(ERR_CONFIG_LOGGER_CANNOT_UPDATE_LOGGER.get(config.dn(), stackTraceToSingleLineString(e)));
          ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
        }
      }
      else
      {
        // The publisher is being disabled so shut down and remove.
        removeLogPublisher(logPublisher);
        return applyConfigurationDelete(config);
      }
    }
    return ccr;
  }
@@ -342,7 +363,6 @@
    return findLogPublisher(config.dn()) != null;
  }
  /** {@inheritDoc} */
  @Override
  public ConfigChangeResult applyConfigurationDelete(C config)
  {
@@ -352,6 +372,19 @@
    if(logPublisher != null)
    {
      removeLogPublisher(logPublisher);
      try
      {
        if (isCommonAuditConfig(config))
        {
          serverContext.getCommonAudit().removePublisher(config);
        }
      }
      catch (ConfigException e)
      {
        LocalizedLogger.getLoggerForThisClass().traceException(e);
        ccr.addMessage(ERR_CONFIG_LOGGER_CANNOT_DELETE_LOGGER.get(config.dn(), stackTraceToSingleLineString(e)));
        ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
      }
    }
    else
    {
@@ -391,4 +424,14 @@
    }
  }
  private P getLogPublisherForCommonAudit(C config) throws InitializationException, ConfigException
  {
    CommonAudit commonAudit = serverContext.getCommonAudit();
    commonAudit.addOrUpdatePublisher(config);
    P logPublisher = getLogPublisher(config);
    CommonAuditLogPublisher publisher = (CommonAuditLogPublisher) logPublisher;
    publisher.setRequestHandler(commonAudit.getRequestHandler(config));
    return logPublisher;
  }
}
opendj-server-legacy/src/main/java/org/opends/server/loggers/AccessLogger.java
@@ -521,6 +521,7 @@
  public final synchronized void removeAllLogPublishers()
  {
    loggerStorage.removeAllLogPublishers();
    getServerContext().getCommonAudit().shutdown();
  }
}
opendj-server-legacy/src/main/java/org/opends/server/loggers/CommonAudit.java
New file
@@ -0,0 +1,943 @@
/*
 * 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 legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * 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 legal-notices/CDDLv1_0.txt.
 * 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 2015 ForgeRock AS.
 */
package org.opends.server.loggers;
import static org.opends.messages.LoggerMessages.*;
import static java.util.Collections.newSetFromMap;
import static org.forgerock.audit.AuditServiceBuilder.newAuditService;
import static org.forgerock.audit.events.EventTopicsMetaDataBuilder.coreTopicSchemas;
import static org.forgerock.audit.json.AuditJsonConfig.registerHandlerToService;
import static org.opends.server.util.StaticUtils.getFileForPath;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
import org.forgerock.audit.AuditException;
import org.forgerock.audit.AuditService;
import org.forgerock.audit.AuditServiceBuilder;
import org.forgerock.audit.AuditServiceConfiguration;
import org.forgerock.audit.AuditServiceProxy;
import org.forgerock.audit.DependencyProvider;
import org.forgerock.audit.events.EventTopicsMetaData;
import org.forgerock.audit.events.handlers.FileBasedEventHandlerConfiguration.FileRetention;
import org.forgerock.audit.events.handlers.FileBasedEventHandlerConfiguration.FileRotation;
import org.forgerock.audit.handlers.csv.CsvAuditEventHandler;
import org.forgerock.audit.handlers.csv.CsvAuditEventHandlerConfiguration;
import org.forgerock.audit.handlers.csv.CsvAuditEventHandlerConfiguration.CsvFormatting;
import org.forgerock.audit.handlers.csv.CsvAuditEventHandlerConfiguration.CsvSecurity;
import org.forgerock.audit.handlers.csv.CsvAuditEventHandlerConfiguration.EventBufferingConfiguration;
import org.forgerock.audit.json.AuditJsonConfig;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.json.JsonValue;
import org.forgerock.json.resource.RequestHandler;
import org.forgerock.opendj.config.ConfigurationFramework;
import org.forgerock.opendj.config.server.ConfigException;
import org.opends.server.admin.Configuration;
import org.opends.server.admin.server.ServerManagementContext;
import org.opends.server.admin.std.server.CsvFileAccessLogPublisherCfg;
import org.opends.server.admin.std.server.CsvFileHTTPAccessLogPublisherCfg;
import org.opends.server.admin.std.server.ExternalAccessLogPublisherCfg;
import org.opends.server.admin.std.server.ExternalHTTPAccessLogPublisherCfg;
import org.opends.server.admin.std.server.FileCountLogRetentionPolicyCfg;
import org.opends.server.admin.std.server.FixedTimeLogRotationPolicyCfg;
import org.opends.server.admin.std.server.FreeDiskSpaceLogRetentionPolicyCfg;
import org.opends.server.admin.std.server.LogPublisherCfg;
import org.opends.server.admin.std.server.LogRetentionPolicyCfg;
import org.opends.server.admin.std.server.LogRotationPolicyCfg;
import org.opends.server.admin.std.server.RootCfg;
import org.opends.server.admin.std.server.SizeLimitLogRetentionPolicyCfg;
import org.opends.server.admin.std.server.SizeLimitLogRotationPolicyCfg;
import org.opends.server.admin.std.server.TimeLimitLogRotationPolicyCfg;
import org.opends.server.config.ConfigEntry;
import org.opends.server.core.DirectoryServer;
import org.opends.server.types.DN;
import org.opends.server.util.StaticUtils;
/**
 * Entry point for the common audit facility.
 * <p>
 * This class manages the AuditService instances and Audit Event Handlers that correspond to the
 * publishers defined in OpenDJ configuration.
 * <p>
 * In theory there should be only one instance of AuditService for all the event handlers but
 * defining one service per handler allow to perform filtering at the DJ server level.
 */
public class CommonAudit
{
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
  private static final String AUDIT_SERVICE_JSON_CONFIGURATION_FILE = "audit-config.json";
  /** Dependency provider used to instantiate the handlers. */
  private final DependencyProvider dependencyProvider;
  /** Configuration framework is used to get an up-to-date class loader with any external library available. */
  private final ConfigurationFramework configurationFramework;
  /** Cache of audit services per configuration entry name. */
  private final Map<String, AuditServiceProxy> auditServiceCache = new ConcurrentHashMap<>(10);
  private final Set<PublisherConfig> httpAccessPublishers =
      newSetFromMap(new ConcurrentHashMap<PublisherConfig, Boolean>(10));
  /** Audit service shared by all HTTP access publishers. */
  private final AuditServiceProxy httpAccessAuditService;
  /**
   * Creates the common audit.
   *
   * @throws ConfigException
   *           If an error occurs.
   */
  public CommonAudit() throws ConfigException
  {
    configurationFramework = ConfigurationFramework.getInstance();
    this.dependencyProvider = new CommonAuditDependencyProvider();
    this.httpAccessAuditService = createAuditServiceWithoutHandlers();
  }
  private AuditServiceProxy createAuditServiceWithoutHandlers() throws ConfigException
  {
    try
    {
      return buildAuditService(new AuditServiceSetup()
      {
        @Override
        public void addHandlers(AuditServiceBuilder builder)
        {
          // no handler to add
        }
      });
    }
    catch (IOException | ConfigException | AuditException e)
    {
      throw new ConfigException(ERR_COMMON_AUDIT_CREATE.get(e), e);
    }
  }
  /**
   * Returns the Common Audit request handler for the provided configuration.
   *
   * @param config
   *            The log publisher configuration
   * @return the request handler associated to the log publisher
   * @throws ConfigException
   *            If an error occurs
   */
  public RequestHandler getRequestHandler(LogPublisherCfg config) throws ConfigException
  {
    if (new PublisherConfig(config).isHttpAccessLog())
    {
      return httpAccessAuditService;
    }
    return auditServiceCache.get(getNameFromConfig(config));
  }
  /**
   * Adds or updates the publisher corresponding to the provided configuration to common audit.
   *
   * @param newConfig
   *          Configuration of the publisher
   * @throws ConfigException
   *           If an error occurs.
   */
  public void addOrUpdatePublisher(final LogPublisherCfg newConfig) throws ConfigException
  {
    if (newConfig.isEnabled())
    {
      logger.trace("Setting up common audit for configuration entry: " + newConfig.dn());
      try
      {
        final PublisherConfig newPublisher = new PublisherConfig(newConfig);
        if (newPublisher.isHttpAccessLog())
        {
          if (httpAccessPublishers.contains(newPublisher))
          {
            // remove the old version before adding the new one
            httpAccessPublishers.remove(newPublisher);
          }
          httpAccessPublishers.add(newPublisher);
          buildAuditService(httpAccessAuditServiceSetup());
        }
        else // all other logs
        {
          String name = newPublisher.getName();
          final AuditServiceProxy existingService =
              auditServiceCache.containsKey(name) ? auditServiceCache.get(name): null;
          AuditServiceProxy auditService = buildAuditService(new AuditServiceSetup(existingService)
          {
            @Override
            public void addHandlers(AuditServiceBuilder builder) throws ConfigException
            {
              registerHandlerName(newPublisher.getName());
              addHandlerToBuilder(newPublisher, builder);
            }
          });
          auditServiceCache.put(name, auditService);
        }
      }
      catch (Exception e)
      {
        throw new ConfigException(ERR_COMMON_AUDIT_ADD_OR_UPDATE_LOG_PUBLISHER.get(newConfig.dn(), e), e);
      }
    }
  }
  /**
   * Removes the publisher corresponding to the provided configuration from common audit.
   *
   * @param config
   *          Configuration of publisher to remove
   * @throws ConfigException
   *            If an error occurs.
   */
  public void removePublisher(LogPublisherCfg config) throws ConfigException
  {
    final PublisherConfig publisher = new PublisherConfig(config);
    logger.trace("Shutting down common audit for configuration entry:" + config.dn());
    try
    {
      if (publisher.isHttpAccessLog())
      {
        httpAccessPublishers.remove(publisher);
        buildAuditService(httpAccessAuditServiceSetup());
      }
      else // all other logs
      {
        AuditServiceProxy auditService = auditServiceCache.remove(publisher.getName());
        if (auditService != null)
        {
          auditService.shutdown();
        }
      }
    }
    catch (Exception e)
    {
      throw new ConfigException(ERR_COMMON_AUDIT_REMOVE_LOG_PUBLISHER.get(config.dn(), e), e);
    }
  }
  /** Shutdown common audit. */
  public void shutdown()
  {
    httpAccessAuditService.shutdown();
    for (AuditServiceProxy service : auditServiceCache.values())
    {
      service.shutdown();
    }
  }
  private AuditServiceSetup httpAccessAuditServiceSetup()
  {
    return new AuditServiceSetup(httpAccessAuditService)
    {
      @Override
      public void addHandlers(AuditServiceBuilder builder) throws ConfigException
      {
        for (PublisherConfig publisher : httpAccessPublishers)
        {
          registerHandlerName(publisher.getName());
          addHandlerToBuilder(publisher, builder);
        }
      }
    };
  }
  /**
   * Strategy for the setup of AuditService.
   * <p>
   * Unless no handler must be added, this class should be extended and
   * implementations should override the {@code addHandlers()} method.
   */
  static abstract class AuditServiceSetup
  {
    private final AuditServiceProxy existingAuditServiceProxy;
    private final List<String> names = new ArrayList<>();
    /** Creation with no existing audit service. */
    AuditServiceSetup()
    {
      this.existingAuditServiceProxy = null;
    }
    /** Creation with an existing audit service. */
    AuditServiceSetup(AuditServiceProxy existingAuditService)
    {
      this.existingAuditServiceProxy = existingAuditService;
    }
    abstract void addHandlers(AuditServiceBuilder builder) throws ConfigException;
    void registerHandlerName(String name)
    {
      names.add(name);
    }
    List<String> getHandlerNames()
    {
      return names;
    }
    boolean mustCreateAuditServiceProxy()
    {
      return existingAuditServiceProxy == null;
    }
    AuditServiceProxy getExistingAuditServiceProxy()
    {
      return existingAuditServiceProxy;
    }
  }
  private AuditServiceProxy buildAuditService(AuditServiceSetup setup)
      throws IOException, AuditException, ConfigException
  {
    final JsonValue jsonConfig;
    try (InputStream input = getClass().getResourceAsStream(AUDIT_SERVICE_JSON_CONFIGURATION_FILE))
    {
      jsonConfig = AuditJsonConfig.getJson(input);
    }
    EventTopicsMetaData eventTopicsMetaData = coreTopicSchemas()
        .withCoreTopicSchemaExtensions(jsonConfig.get("extensions"))
        .withAdditionalTopicSchemas(jsonConfig.get("additionalTopics"))
        .build();
    AuditServiceBuilder builder = newAuditService()
        .withEventTopicsMetaData(eventTopicsMetaData)
        .withDependencyProvider(dependencyProvider);
    setup.addHandlers(builder);
    AuditServiceConfiguration auditConfig = new AuditServiceConfiguration();
    auditConfig.setAvailableAuditEventHandlers(setup.getHandlerNames());
    builder.withConfiguration(auditConfig);
    AuditService audit = builder.build();
    final AuditServiceProxy proxy;
    if (setup.mustCreateAuditServiceProxy())
    {
      proxy = new AuditServiceProxy(audit);
      logger.trace("Starting up new common audit service");
      proxy.startup();
    }
    else
    {
      proxy = setup.getExistingAuditServiceProxy();
      proxy.setDelegate(audit);
      logger.trace("Starting up existing updated common audit service");
    }
    return proxy;
  }
  private void addHandlerToBuilder(PublisherConfig publisher, AuditServiceBuilder builder) throws ConfigException
  {
    if (publisher.isCsv())
    {
      addCsvHandler(publisher, builder);
    }
    else if (publisher.isExternal())
    {
      addExternalHandler(publisher, builder);
    }
    else
    {
      throw new ConfigException(ERR_COMMON_AUDIT_UNSUPPORTED_HANDLER_TYPE.get(publisher.getDn()));
    }
  }
  /** Add a handler defined externally in a JSON configuration file. */
  private void addExternalHandler(PublisherConfig publisher, AuditServiceBuilder builder) throws ConfigException
  {
    ExternalConfigData config = publisher.getExternalConfig();
    File configFile = getFileForPath(config.getConfigurationFile());
    try (InputStream input = new BufferedInputStream(new FileInputStream(configFile)))
    {
      JsonValue jsonConfig = AuditJsonConfig.getJson(input);
      registerHandlerToService(jsonConfig, builder, configurationFramework.getClassLoader());
    }
    catch (IOException e)
    {
      throw new ConfigException(ERR_COMMON_AUDIT_EXTERNAL_HANDLER_JSON_FILE.get(configFile, publisher.getDn(), e), e);
    }
    catch (Exception e)
    {
      throw new ConfigException(ERR_COMMON_AUDIT_EXTERNAL_HANDLER_CREATION.get(publisher.getDn(), e), e);
    }
  }
  private void addCsvHandler(PublisherConfig publisher, AuditServiceBuilder builder) throws ConfigException
  {
    String name = publisher.getName();
    try
    {
      CsvConfigData config = publisher.getCsvConfig();
      CsvAuditEventHandlerConfiguration csvConfig = new CsvAuditEventHandlerConfiguration();
      File logDirectory = getFileForPath(config.getLogDirectory());
      csvConfig.setLogDirectory(logDirectory.getAbsolutePath());
      csvConfig.setName(name);
      csvConfig.setTopics(Collections.singleton(publisher.getCommonAuditTopic()));
      addCsvHandlerFormattingConfig(config, csvConfig);
      addCsvHandlerBufferingConfig(config, csvConfig);
      addCsvHandlerSecureConfig(config, csvConfig);
      addCsvHandlerRotationConfig(publisher, config, csvConfig);
      addCsvHandlerRetentionConfig(publisher, config, csvConfig);
      builder.withAuditEventHandler(CsvAuditEventHandler.class, csvConfig);
    }
    catch (Exception e)
    {
      throw new ConfigException(ERR_COMMON_AUDIT_CSV_HANDLER_CREATION.get(publisher.getDn(), e), e);
    }
  }
  private void addCsvHandlerFormattingConfig(CsvConfigData config, CsvAuditEventHandlerConfiguration auditConfig)
      throws ConfigException
  {
    CsvFormatting formatting = new CsvFormatting();
    formatting.setQuoteChar(config.getQuoteChar());
    formatting.setDelimiterChar(config.getDelimiterChar());
    String endOfLineSymbols = config.getEndOfLineSymbols();
    if (endOfLineSymbols != null && !endOfLineSymbols.isEmpty())
    {
      formatting.setEndOfLineSymbols(endOfLineSymbols);
    }
    auditConfig.setFormatting(formatting);
  }
  private void addCsvHandlerBufferingConfig(CsvConfigData config, CsvAuditEventHandlerConfiguration auditConfig)
  {
    EventBufferingConfiguration bufferingConfig = new EventBufferingConfiguration();
    bufferingConfig.setEnabled(config.isAsynchronous());
    bufferingConfig.setAutoFlush(config.isAutoFlush());
    auditConfig.setBufferingConfiguration(bufferingConfig);
  }
  private void addCsvHandlerSecureConfig(CsvConfigData config, CsvAuditEventHandlerConfiguration auditConfig)
  {
    if (config.isTamperEvident())
    {
      CsvSecurity security = new CsvSecurity();
      security.setSignatureInterval(config.getSignatureTimeInterval() + "ms");
      security.setEnabled(true);
      String keyStoreFile = config.getKeystoreFile();
      security.setFilename(getFileForPath(keyStoreFile).getPath());
      security.setPassword(getSecurePassword(config));
      auditConfig.setSecurity(security);
    }
  }
  private void addCsvHandlerRotationConfig(PublisherConfig publisher, CsvConfigData config,
      CsvAuditEventHandlerConfiguration auditConfig) throws ConfigException
  {
    RootCfg root = ServerManagementContext.getInstance().getRootConfiguration();
    SortedSet<String> rotationPolicies = config.getRotationPolicies();
    if (rotationPolicies.isEmpty())
    {
      return;
    }
    FileRotation fileRotation = new FileRotation();
    fileRotation.setRotationEnabled(true);
    for (final String policy : rotationPolicies)
    {
      LogRotationPolicyCfg policyConfig = root.getLogRotationPolicy(policy);
      if (policyConfig instanceof FixedTimeLogRotationPolicyCfg)
      {
        List<String> times = convertTimesOfDay(publisher, (FixedTimeLogRotationPolicyCfg) policyConfig);
        fileRotation.setRotationTimes(times);
      }
      else if (policyConfig instanceof SizeLimitLogRotationPolicyCfg)
      {
        fileRotation.setMaxFileSize(((SizeLimitLogRotationPolicyCfg) policyConfig).getFileSizeLimit());
      }
      else if (policyConfig instanceof TimeLimitLogRotationPolicyCfg)
      {
        long rotationInterval = ((TimeLimitLogRotationPolicyCfg) policyConfig).getRotationInterval();
        fileRotation.setRotationInterval(String.valueOf(rotationInterval) + " ms");
      }
      else
      {
        throw new ConfigException(
            ERR_COMMON_AUDIT_UNSUPPORTED_LOG_ROTATION_POLICY.get(publisher.getDn(), policyConfig.dn()));
      }
    }
    auditConfig.setFileRotation(fileRotation);
  }
  private void addCsvHandlerRetentionConfig(PublisherConfig publisher, CsvConfigData config,
      CsvAuditEventHandlerConfiguration auditConfig) throws ConfigException
  {
    RootCfg root = ServerManagementContext.getInstance().getRootConfiguration();
    SortedSet<String> retentionPolicies = config.getRetentionPolicies();
    if (retentionPolicies.isEmpty())
    {
      return;
    }
    FileRetention fileRetention = new FileRetention();
    for (final String policy : retentionPolicies)
    {
      LogRetentionPolicyCfg policyConfig = root.getLogRetentionPolicy(policy);
      if (policyConfig instanceof FileCountLogRetentionPolicyCfg)
      {
        fileRetention.setMaxNumberOfHistoryFiles(((FileCountLogRetentionPolicyCfg) policyConfig).getNumberOfFiles());
      }
      else if (policyConfig instanceof FreeDiskSpaceLogRetentionPolicyCfg)
      {
        // TODO: remove the cast to int when fixed
        fileRetention.setMinFreeSpaceRequired(
            (int)((FreeDiskSpaceLogRetentionPolicyCfg) policyConfig).getFreeDiskSpace());
      }
      else if (policyConfig instanceof SizeLimitLogRetentionPolicyCfg)
      {
        fileRetention.setMaxDiskSpaceToUse(((SizeLimitLogRetentionPolicyCfg) policyConfig).getDiskSpaceUsed());
      }
      else
      {
        throw new ConfigException(
            ERR_COMMON_AUDIT_UNSUPPORTED_LOG_RETENTION_POLICY.get(publisher.getDn(), policyConfig.dn()));
      }
    }
    auditConfig.setFileRetention(fileRetention);
  }
  /**
   * Convert the set of provided times of day using 24-hour format "HHmm" to a list of
   * times of day using duration in minutes, e.g "20 minutes".
   * <p>
   * Example: "0230" => "150 minutes"
   */
  private List<String> convertTimesOfDay(PublisherConfig publisher, FixedTimeLogRotationPolicyCfg policyConfig)
      throws ConfigException
  {
    SortedSet<String> timesOfDay = policyConfig.getTimeOfDay();
    List<String> times = new ArrayList<>();
    for (String timeOfDay : timesOfDay)
    {
      try
      {
        int time = Integer.valueOf(timeOfDay.substring(0, 2)) * 60 + Integer.valueOf(timeOfDay.substring(2, 4));
        times.add(String.valueOf(time) + " minutes");
      }
      catch (NumberFormatException | IndexOutOfBoundsException e)
      {
        throw new ConfigException(ERR_COMMON_AUDIT_INVALID_TIME_OF_DAY.get(publisher.getDn(), timeOfDay,
            StaticUtils.stackTraceToSingleLineString(e)));
      }
    }
    return times;
  }
  // TODO : this method will be deleted, because a keystore handler will be used instead
  private String getSecurePassword(CsvConfigData config)
  {
    String fileName = config.getKeystorePinFile();
    File   pinFile  = getFileForPath(fileName);
    if (!pinFile.exists())
    {
      // should log error "no pin file"
    }
    else
    {
      String pinStr = null;
      BufferedReader br = null;
      try {
        br = new BufferedReader(new FileReader(pinFile));
        pinStr = br.readLine();
      }
      catch (IOException ioe)
      {
        // should log error "unable to read pin file"
      }
      finally
      {
        StaticUtils.close(br);
      }
      if (pinStr == null)
      {
        // should log error pin file empty
      }
      else
      {
        return pinStr;
      }
    }
    return "";
  }
  private String getNameFromConfig(Configuration config)
  {
    return config.dn().getRDN(0).getAttributeValue(0).toString();
  }
  /**
   * Indicates if the provided log publisher configuration corresponds to a common audit publisher.
   *
   * @param config
   *            The log publisher configuration.
   * @return {@code true} if publisher is for common audit
   * @throws ConfigException
   *            If an error occurs
   */
  public static boolean isCommonAuditConfig(LogPublisherCfg config) throws ConfigException
  {
    return new PublisherConfig(config).isCommonAudit();
  }
  /**
   * Indicates if HTTP access logging is enabled for common audit.
   *
   * @return {@code true} if there is at least one HTTP access logger enabled for common audit.
   */
  public boolean isHttpAccessLogEnabled()
  {
    return !httpAccessPublishers.isEmpty();
  }
  /**
   * Returns the audit service that manages HTTP Access logging.
   *
   * @return the request handler that accepts audit events
   */
  public RequestHandler getAuditServiceForHttpAccessLog()
  {
    return httpAccessAuditService;
  }
  /**
   * This class hides all ugly code needed to determine which type of publisher and audit event handler is needed.
   * <p>
   * In particular, it allows to retrieve a common configuration that can be used for log publishers that
   * publish to the same kind of handler.
   * For example: for CSV handler, DJ configurations for the log publishers contain the same methods but
   * do not have a common interface (CsvFileAccessLogPublisherCfg vs CsvFileHTTPAccessLogPublisherCfg).
   */
  private static class PublisherConfig
  {
    private final LogPublisherCfg config;
    private final boolean isCommonAudit;
    private LogType logType;
    private AuditType auditType;
    PublisherConfig(LogPublisherCfg config) throws ConfigException
    {
      this.config = config;
      ConfigEntry configEntry = DirectoryServer.getConfigEntry(config.dn());
      if (configEntry.hasObjectClass("ds-cfg-csv-file-access-log-publisher"))
      {
        auditType = AuditType.CSV;
        logType = LogType.ACCESS;
      }
      else if (configEntry.hasObjectClass("ds-cfg-csv-file-http-access-log-publisher"))
      {
        auditType = AuditType.CSV;
        logType = LogType.HTTP_ACCESS;
      }
      else if (configEntry.hasObjectClass("ds-cfg-external-access-log-publisher"))
      {
        auditType = AuditType.EXTERNAL;
        logType = LogType.ACCESS;
      }
      else if (configEntry.hasObjectClass("ds-cfg-external-http-access-log-publisher"))
      {
        auditType = AuditType.EXTERNAL;
        logType = LogType.HTTP_ACCESS;
      }
      isCommonAudit = auditType != null;
    }
    DN getDn()
    {
      return config.dn();
    }
    String getName()
    {
      return config.dn().getRDN(0).getAttributeValue(0).toString();
    }
    String getCommonAuditTopic() throws ConfigException
    {
      if (isAccessLog())
      {
        return "ldap-access";
      }
      else if (isHttpAccessLog())
      {
        return "http-access";
      }
      throw new ConfigException(ERR_COMMON_AUDIT_UNSUPPORTED_LOG_PUBLISHER.get(config.dn()));
    }
    boolean isExternal()
    {
      return AuditType.EXTERNAL == auditType;
    }
    boolean isCsv()
    {
      return AuditType.CSV == auditType;
    }
    boolean isAccessLog()
    {
      return LogType.ACCESS == logType;
    }
    boolean isHttpAccessLog()
    {
      return LogType.HTTP_ACCESS == logType;
    }
    boolean isCommonAudit()
    {
      return isCommonAudit;
    }
    CsvConfigData getCsvConfig() throws ConfigException
    {
      if (isAccessLog())
      {
        CsvFileAccessLogPublisherCfg conf = (CsvFileAccessLogPublisherCfg) config;
        return new CsvConfigData(conf.getLogDirectory(), conf.getCsvQuoteChar(), conf.getCsvDelimiterChar(), conf
            .getCsvEolSymbols(), conf.isAsynchronous(), conf.isAutoFlush(), conf.isTamperEvident(), conf
            .getSignatureTimeInterval(), conf.getKeyStoreFile(), conf.getKeyStorePinFile(), conf.getRotationPolicy(),
            conf.getRetentionPolicy());
      }
      if (isHttpAccessLog())
      {
        CsvFileHTTPAccessLogPublisherCfg conf = (CsvFileHTTPAccessLogPublisherCfg) config;
        return new CsvConfigData(conf.getLogDirectory(), conf.getCsvQuoteChar(), conf.getCsvDelimiterChar(), conf
            .getCsvEolSymbols(), conf.isAsynchronous(), conf.isAutoFlush(), conf.isTamperEvident(), conf
            .getSignatureTimeInterval(), conf.getKeyStoreFile(), conf.getKeyStorePinFile(), conf.getRotationPolicy(),
            conf.getRetentionPolicy());
      }
      throw new ConfigException(ERR_COMMON_AUDIT_UNSUPPORTED_LOG_PUBLISHER.get(config.dn()));
    }
    ExternalConfigData getExternalConfig() throws ConfigException
    {
      if (isAccessLog())
      {
        ExternalAccessLogPublisherCfg conf = (ExternalAccessLogPublisherCfg) config;
        return new ExternalConfigData(conf.getConfigFile());
      }
      if (isHttpAccessLog())
      {
        ExternalHTTPAccessLogPublisherCfg conf = (ExternalHTTPAccessLogPublisherCfg) config;
        return new ExternalConfigData(conf.getConfigFile());
      }
      throw new ConfigException(ERR_COMMON_AUDIT_UNSUPPORTED_LOG_PUBLISHER.get(config.dn()));
    }
    @Override
    public boolean equals(Object obj)
    {
      if (this == obj)
      {
        return true;
      }
      if (!(obj instanceof PublisherConfig))
      {
        return false;
      }
      PublisherConfig other = (PublisherConfig) obj;
      return config.dn().equals(other.config.dn());
    }
    @Override
    public int hashCode()
    {
      return config.dn().hashCode();
    }
  }
  /** Types of audit handlers managed. */
  private enum AuditType
  {
    CSV, EXTERNAL
  }
  /** Types of log managed. */
  private enum LogType
  {
    ACCESS, HTTP_ACCESS
  }
  /**
   * Contains the parameters for a CSV handler.
   * <p>
   * OpenDJ log publishers that logs to a CSV handler have the same parameters but do not share
   * a common ancestor with all the parameters (e.g Access Log, HTTP Access Log, ...), hence this class
   * is necessary to avoid duplicating code that setup the configuration of the CSV handler.
   */
  private static class CsvConfigData
  {
    private final String logDirectory;
    private final String eolSymbols;
    private final String delimiterChar;
    private final String quoteChar;
    private final boolean asynchronous;
    private final boolean autoFlush;
    private final boolean tamperEvident;
    private final long signatureTimeInterval;
    private final String keystoreFile;
    private final String keystorePinFile;
    private final SortedSet<String> rotationPolicies;
    private final SortedSet<String> retentionPolicies;
    CsvConfigData(String logDirectory, String quoteChar, String delimiterChar, String eolSymbols, boolean asynchronous,
        boolean autoFlush, boolean tamperEvident, long signatureTimeInterval, String keystoreFile,
        String keystorePinFile, SortedSet<String> rotationPolicies, SortedSet<String> retentionPolicies)
    {
      this.logDirectory = logDirectory;
      this.quoteChar = quoteChar;
      this.delimiterChar = delimiterChar;
      this.eolSymbols = eolSymbols;
      this.asynchronous = asynchronous;
      this.autoFlush = autoFlush;
      this.tamperEvident = tamperEvident;
      this.signatureTimeInterval = signatureTimeInterval;
      this.keystoreFile = keystoreFile;
      this.keystorePinFile = keystorePinFile;
      this.rotationPolicies = rotationPolicies;
      this.retentionPolicies = retentionPolicies;
    }
    String getEndOfLineSymbols()
    {
      return eolSymbols;
    }
    char getDelimiterChar() throws ConfigException
    {
      String filtered = delimiterChar.replaceAll(Pattern.quote("\\"), "");
      if (filtered.length() != 1)
      {
        throw new ConfigException(ERR_COMMON_AUDIT_CSV_HANDLER_DELIMITER_CHAR.get("", filtered));
      }
      return filtered.charAt(0);
    }
    public char getQuoteChar() throws ConfigException
    {
      String filtered = quoteChar.replaceAll(Pattern.quote("\\"), "");
      if (filtered.length() != 1)
      {
        throw new ConfigException(ERR_COMMON_AUDIT_CSV_HANDLER_QUOTE_CHAR.get("", filtered));
      }
      return filtered.charAt(0);
    }
    String getLogDirectory()
    {
      return logDirectory;
    }
    boolean isAsynchronous()
    {
      return asynchronous;
    }
    boolean isAutoFlush()
    {
      return autoFlush;
    }
    boolean isTamperEvident()
    {
      return tamperEvident;
    }
    long getSignatureTimeInterval()
    {
      return signatureTimeInterval;
    }
    String getKeystoreFile()
    {
      return keystoreFile;
    }
    String getKeystorePinFile()
    {
      return keystorePinFile;
    }
    SortedSet<String> getRotationPolicies()
    {
      return rotationPolicies;
    }
    SortedSet<String> getRetentionPolicies()
    {
      return retentionPolicies;
    }
  }
  /**
   * Contains the parameters for an external handler.
   * <p>
   * OpenDJ log publishers that logs to an external handler have the same parameters but do not share
   * a common ancestor with all the parameters (e.g Access Log, HTTP Access Log, ...), hence this class
   * is necessary to avoid duplicating code that setup the configuration of an external handler.
   */
  private static class ExternalConfigData
  {
    private final String configurationFile;
    ExternalConfigData(String configurationFile)
    {
      this.configurationFile = configurationFile;
    }
    String getConfigurationFile()
    {
      return configurationFile;
    }
  }
}
opendj-server-legacy/src/main/java/org/opends/server/loggers/CommonAuditAccessLogPublisher.java
New file
@@ -0,0 +1,554 @@
/*
 * 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 legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * 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 legal-notices/CDDLv1_0.txt.
 * 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 2015 ForgeRock AS
 */
package org.opends.server.loggers;
import static org.opends.messages.LoggerMessages.*;
import static org.forgerock.json.JsonValue.json;
import static org.forgerock.json.resource.Requests.newCreateRequest;
import static org.forgerock.json.resource.ResourcePath.resourcePath;
import static org.opends.server.loggers.OpenDJAccessAuditEventBuilder.openDJAccessEvent;
import static org.opends.server.types.AuthenticationType.SASL;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.forgerock.audit.events.AccessAuditEventBuilder.ResponseStatus;
import org.forgerock.audit.events.AuditEvent;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.LocalizableMessageBuilder;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.json.resource.CreateRequest;
import org.forgerock.json.resource.RequestHandler;
import org.forgerock.json.resource.ResourceException;
import org.forgerock.opendj.config.server.ConfigException;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.services.context.RootContext;
import org.forgerock.util.Pair;
import org.forgerock.util.promise.ExceptionHandler;
import org.forgerock.util.promise.RuntimeExceptionHandler;
import org.opends.server.admin.std.server.AccessLogPublisherCfg;
import org.opends.server.api.ClientConnection;
import org.opends.server.api.ExtendedOperationHandler;
import org.opends.server.controls.TransactionIdControl;
import org.opends.server.core.AbandonOperation;
import org.opends.server.core.AddOperation;
import org.opends.server.core.BindOperation;
import org.opends.server.core.CompareOperation;
import org.opends.server.core.DeleteOperation;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ExtendedOperation;
import org.opends.server.core.ModifyDNOperation;
import org.opends.server.core.ModifyOperation;
import org.opends.server.core.SearchOperation;
import org.opends.server.core.ServerContext;
import org.opends.server.core.UnbindOperation;
import org.opends.server.types.AuthenticationInfo;
import org.opends.server.types.Control;
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.DisconnectReason;
import org.opends.server.types.InitializationException;
import org.opends.server.types.Operation;
import org.opends.server.util.ServerConstants;
import org.opends.server.util.StaticUtils;
/**
 * Publishes access events to Common Audit.
 *
 * @param <T> the type of configuration
 */
abstract class CommonAuditAccessLogPublisher<T extends AccessLogPublisherCfg>
  extends AbstractTextAccessLogPublisher<T>
  implements CommonAuditLogPublisher
{
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
  /** Transaction id used when the incoming request does not contain a transaction id. */
  private static final String DEFAULT_TRANSACTION_ID = "0";
  /** Audit service handler. */
  private RequestHandler requestHandler;
  /** Current configuration for this publisher. */
  private T config;
  @Override
  public void setRequestHandler(RequestHandler handler)
  {
    this.requestHandler = handler;
  }
  abstract boolean shouldLogControlOids();
  T getConfig()
  {
    return config;
  }
  void setConfig(T config)
  {
    this.config = config;
  }
  @Override
  public void initializeLogPublisher(final T cfg, ServerContext serverContext)
      throws ConfigException, InitializationException
  {
    initializeFilters(cfg);
    config = cfg;
  }
  @Override
  public boolean isConfigurationAcceptable(final T configuration, final List<LocalizableMessage> unacceptableReasons)
  {
    return isFilterConfigurationAcceptable(configuration, unacceptableReasons);
  }
  @Override
  public void logAbandonResult(final AbandonOperation abandonOperation)
  {
    if (!isResponseLoggable(abandonOperation))
    {
      return;
    }
    OpenDJAccessAuditEventBuilder<?> builder = getEventBuilder(abandonOperation, "ABANDON");
    appendAbandonRequest(abandonOperation, builder);
    appendResultCodeAndMessage(abandonOperation, builder);
    sendEvent(builder.toEvent());
  }
  @Override
  public void logAddResponse(final AddOperation addOperation)
  {
    if (!isResponseLoggable(addOperation))
    {
      return;
    }
    OpenDJAccessAuditEventBuilder<?> builder = getEventBuilder(addOperation, "ADD");
    appendAddRequest(addOperation, builder);
    appendResultCodeAndMessage(addOperation, builder);
    DN proxiedAuthorizationDN = addOperation.getProxiedAuthorizationDN();
    appendProxiedAuthorizationDNIfNeeded(builder, proxiedAuthorizationDN);
    sendEvent(builder.toEvent());
  }
  @Override
  public void logBindResponse(final BindOperation bindOperation)
  {
    if (!isResponseLoggable(bindOperation))
    {
      return;
    }
    OpenDJAccessAuditEventBuilder<?> builder = getEventBuilder(bindOperation, "BIND");
    appendBindRequest(bindOperation, builder);
    appendResultCodeAndMessage(bindOperation, builder);
    final LocalizableMessage failureMessage = bindOperation.getAuthFailureReason();
    if (failureMessage != null)
    {
      // this code path is mutually exclusive with the if result code is success
      // down below
      builder.ldapFailureMessage(failureMessage.toString());
      if (bindOperation.getSASLMechanism() != null && bindOperation.getSASLAuthUserEntry() != null)
      { // SASL bind and we have successfully found a user entry for auth
        builder.userId(bindOperation.getSASLAuthUserEntry().getName().toString());
      }
      else
      { // SASL bind failed to find user entry for auth or simple bind
        builder.userId(bindOperation.getRawBindDN().toString());
      }
    }
    if (bindOperation.getResultCode() == ResultCode.SUCCESS)
    {
      // this code path is mutually exclusive with the if failure message exist
      // just above
      final AuthenticationInfo authInfo = bindOperation.getAuthenticationInfo();
      if (authInfo != null)
      {
        final DN authDN = authInfo.getAuthenticationDN();
        if (authDN != null)
        {
          builder.userId(authDN.toString());
          final DN authzDN = authInfo.getAuthorizationDN();
          if (!authDN.equals(authzDN))
          {
            builder.runAs(authzDN.toString());
          }
        }
        else
        {
          builder.userId("");
        }
      }
    }
    sendEvent(builder.toEvent());
  }
  @Override
  public void logCompareResponse(final CompareOperation compareOperation)
  {
    if (!isResponseLoggable(compareOperation))
    {
      return;
    }
    OpenDJAccessAuditEventBuilder<?> builder = getEventBuilder(compareOperation, "COMPARE");
    appendCompareRequest(compareOperation, builder);
    appendResultCodeAndMessage(compareOperation, builder);
    DN proxiedAuthorizationDN = compareOperation.getProxiedAuthorizationDN();
    appendProxiedAuthorizationDNIfNeeded(builder, proxiedAuthorizationDN);
    sendEvent(builder.toEvent());
  }
  private void appendProxiedAuthorizationDNIfNeeded(OpenDJAccessAuditEventBuilder<?> builder, DN proxiedAuthorizationDN)
  {
    if (proxiedAuthorizationDN != null)
    {
      builder.runAs(proxiedAuthorizationDN.toString());
    }
  }
  @Override
  public void logConnect(final ClientConnection clientConnection)
  {
    if (!isConnectLoggable(clientConnection))
    {
      return;
    }
    OpenDJAccessAuditEventBuilder<?> builder = openDJAccessEvent()
        .eventName("DJ-" + clientConnection.getProtocol() + "-" + "CONNECT")
        .client(clientConnection.getClientAddress(), clientConnection.getClientPort())
        .server(clientConnection.getServerAddress(), clientConnection.getServerPort())
        .request(clientConnection.getProtocol(), "CONNECT")
        .transactionId(DEFAULT_TRANSACTION_ID)
        .response(ResponseStatus.SUCCESSFUL, String.valueOf(ResultCode.SUCCESS.intValue()), 0, TimeUnit.MILLISECONDS)
        .ldapConnectionId(clientConnection.getConnectionID());
    sendEvent(builder.toEvent());
  }
  @Override
  public void logDeleteResponse(final DeleteOperation deleteOperation)
  {
    if (!isResponseLoggable(deleteOperation))
    {
      return;
    }
    OpenDJAccessAuditEventBuilder<?> builder = getEventBuilder(deleteOperation, "DELETE");
    appendDeleteRequest(deleteOperation, builder);
    appendResultCodeAndMessage(deleteOperation, builder);
    DN proxiedAuthorizationDN = deleteOperation.getProxiedAuthorizationDN();
    appendProxiedAuthorizationDNIfNeeded(builder, proxiedAuthorizationDN);
    sendEvent(builder.toEvent());
  }
  @Override
  public void logDisconnect(final ClientConnection clientConnection, final DisconnectReason disconnectReason,
      final LocalizableMessage message)
  {
    if (!isDisconnectLoggable(clientConnection))
    {
      return;
    }
    OpenDJAccessAuditEventBuilder<?> builder = openDJAccessEvent()
        .eventName("DJ-" + clientConnection.getProtocol() + "-" + "DISCONNECT")
        .client(clientConnection.getClientAddress(), clientConnection.getClientPort())
        .server(clientConnection.getServerAddress(), clientConnection.getServerPort())
        .request(clientConnection.getProtocol(),"DISCONNECT")
        .transactionId(DEFAULT_TRANSACTION_ID)
        .response(ResponseStatus.SUCCESSFUL, String.valueOf(ResultCode.SUCCESS.intValue()), 0, TimeUnit.MILLISECONDS)
        .ldapConnectionId(clientConnection.getConnectionID())
        .ldapReason(disconnectReason.toString())
        .ldapMessage(message.toString());
    sendEvent(builder.toEvent());
  }
  @Override
  public void logExtendedResponse(final ExtendedOperation extendedOperation)
  {
    if (!isResponseLoggable(extendedOperation))
    {
      return;
    }
    OpenDJAccessAuditEventBuilder<?> builder = getEventBuilder(extendedOperation, "EXTENDED");
    appendExtendedRequest(extendedOperation, builder);
    appendResultCodeAndMessage(extendedOperation, builder);
    final String oid = extendedOperation.getResponseOID();
    if (oid != null)
    {
      final ExtendedOperationHandler<?> extOpHandler = DirectoryServer.getExtendedOperationHandler(oid);
      if (extOpHandler != null)
      {
        String name = extOpHandler.getExtendedOperationName();
        builder.ldapName(name);
      }
      builder.ldapOid(oid);
    }
    sendEvent(builder.toEvent());
  }
  @Override
  public void logModifyDNResponse(final ModifyDNOperation modifyDNOperation)
  {
    if (!isResponseLoggable(modifyDNOperation))
    {
      return;
    }
    OpenDJAccessAuditEventBuilder<?> builder = getEventBuilder(modifyDNOperation, "MODIFYDN");
    appendModifyDNRequest(modifyDNOperation, builder);
    appendResultCodeAndMessage(modifyDNOperation, builder);
    DN proxiedAuthorizationDN = modifyDNOperation.getProxiedAuthorizationDN();
    appendProxiedAuthorizationDNIfNeeded(builder, proxiedAuthorizationDN);
    sendEvent(builder.toEvent());
  }
  @Override
  public void logModifyResponse(final ModifyOperation modifyOperation)
  {
    if (!isResponseLoggable(modifyOperation))
    {
      return;
    }
    OpenDJAccessAuditEventBuilder<?> builder = getEventBuilder(modifyOperation, "MODIFY");
    appendModifyRequest(modifyOperation, builder);
    appendResultCodeAndMessage(modifyOperation, builder);
    DN proxiedAuthorizationDN = modifyOperation.getProxiedAuthorizationDN();
    appendProxiedAuthorizationDNIfNeeded(builder, proxiedAuthorizationDN);
    sendEvent(builder.toEvent());
  }
  @Override
  public void logSearchResultDone(final SearchOperation searchOperation)
  {
    if (!isResponseLoggable(searchOperation))
    {
      return;
    }
    OpenDJAccessAuditEventBuilder<?> builder = getEventBuilder(searchOperation, "SEARCH");
    builder
        .ldapSearch(searchOperation)
        .ldapNEntries(searchOperation.getEntriesSent());
        appendResultCodeAndMessage(searchOperation, builder);
    DN proxiedAuthorizationDN = searchOperation.getProxiedAuthorizationDN();
    appendProxiedAuthorizationDNIfNeeded(builder, proxiedAuthorizationDN);
    sendEvent(builder.toEvent());
  }
  @Override
  public void logUnbind(final UnbindOperation unbindOperation)
  {
    if (!isRequestLoggable(unbindOperation))
    {
      return;
    }
    sendEvent(getEventBuilder(unbindOperation, "UNBIND").toEvent());
  }
  @Override
  protected void close0()
  {
    // nothing to do because closing is managed in the CommonAudit class
  }
  private void appendAbandonRequest(final AbandonOperation abandonOperation,
      final OpenDJAccessAuditEventBuilder<?> builder)
  {
    builder.ldapIdToAbandon(abandonOperation.getIDToAbandon());
  }
  private void appendAddRequest(final AddOperation addOperation, OpenDJAccessAuditEventBuilder<?> builder)
  {
    builder.ldapDn(addOperation.getRawEntryDN().toString());
  }
  private void appendBindRequest(final BindOperation bindOperation, final OpenDJAccessAuditEventBuilder<?> builder)
  {
    builder.ldapProtocolVersion(bindOperation.getProtocolVersion());
    final String authType = bindOperation.getAuthenticationType() != SASL ?
        bindOperation.getAuthenticationType().toString() : "SASL mechanism=" + bindOperation.getSASLMechanism();
    builder.ldapAuthType(authType);
    builder.ldapDn(bindOperation.getRawBindDN().toString());
  }
  private void appendCompareRequest(final CompareOperation compareOperation,
      final OpenDJAccessAuditEventBuilder<?> builder)
  {
    builder.ldapDn(compareOperation.getRawEntryDN().toString());
    builder.ldapAttr(compareOperation.getAttributeType().getNameOrOID());
  }
  private void appendDeleteRequest(final DeleteOperation deleteOperation,
      final OpenDJAccessAuditEventBuilder<?> builder)
  {
    builder.ldapDn(deleteOperation.getRawEntryDN().toString());
  }
  private void appendExtendedRequest(final ExtendedOperation extendedOperation,
      final OpenDJAccessAuditEventBuilder<?> builder)
  {
    final String oid = extendedOperation.getRequestOID();
    final ExtendedOperationHandler<?> extOpHandler = DirectoryServer.getExtendedOperationHandler(oid);
    if (extOpHandler != null)
    {
      final String name = extOpHandler.getExtendedOperationName();
      builder.ldapName(name);
    }
    builder.ldapOid(oid);
  }
  private void appendModifyDNRequest(final ModifyDNOperation modifyDNOperation,
      final OpenDJAccessAuditEventBuilder<?> builder)
  {
    builder.ldapDn(modifyDNOperation.getRawEntryDN().toString());
    builder.ldapModifyDN(modifyDNOperation);
  }
  private void appendModifyRequest(final ModifyOperation modifyOperation,
      final OpenDJAccessAuditEventBuilder<?> builder)
  {
    builder.ldapDn(modifyOperation.getRawEntryDN().toString());
  }
  private OpenDJAccessAuditEventBuilder<?> appendResultCodeAndMessage(
      Operation operation, OpenDJAccessAuditEventBuilder<?> builder)
  {
    final LocalizableMessageBuilder message = operation.getErrorMessage();
    int resultCode = operation.getResultCode().intValue();
    ResponseStatus status = resultCode == 0 ? ResponseStatus.SUCCESSFUL : ResponseStatus.FAILED;
    Pair<Long, TimeUnit> executionTime = getExecutionTime(operation);
    if (message != null && message.length() > 0)
    {
      builder.responseWithDetail(status, String.valueOf(resultCode), executionTime.getFirst(),
          executionTime.getSecond(), json(message.toString()));
    }
    else
    {
      builder.response(status, String.valueOf(resultCode), executionTime.getFirst(), executionTime.getSecond());
    }
    builder.ldapMaskedResultAndMessage(operation);
    return builder;
  }
  /** Returns an event builder with all common fields filled. */
  private OpenDJAccessAuditEventBuilder<?> getEventBuilder(final Operation operation, final String opType)
  {
    ClientConnection clientConn = operation.getClientConnection();
    OpenDJAccessAuditEventBuilder<?> builder = openDJAccessEvent()
      .eventName("DJ-" + clientConn.getProtocol() + "-" + opType)
      .client(clientConn.getClientAddress(), clientConn.getClientPort())
      .server(clientConn.getServerAddress(), clientConn.getServerPort())
      .request(clientConn.getProtocol(), opType)
      .ldapAdditionalItems(operation)
      .ldapSync(operation)
      .ldapIds(operation)
      .transactionId(getTransactionId(operation));
    if (shouldLogControlOids())
    {
      builder.ldapControls(operation);
    }
    return builder;
  }
  private String getTransactionId(Operation operation)
  {
    String transactionId = getTransactionIdFromControl(operation);
    if (transactionId == null)
    {
      // use a default value because transaction id has no usage in this case
      transactionId = DEFAULT_TRANSACTION_ID;
    }
    return transactionId;
  }
  private String getTransactionIdFromControl(Operation operation)
  {
    for (Control control : operation.getRequestControls())
    {
      if (control.getOID().equals(ServerConstants.OID_TRANSACTION_ID_CONTROL))
      {
        try
        {
          return operation.getRequestControl(TransactionIdControl.DECODER).getTransactionId();
        }
        catch (DirectoryException e)
        {
          logger.error(ERR_COMMON_AUDIT_INVALID_TRANSACTION_ID.get(StaticUtils.stackTraceToSingleLineString(e)));
        }
      }
    }
    return null;
  }
  private Pair<Long,TimeUnit> getExecutionTime(final Operation operation)
  {
    Long etime = operation.getProcessingNanoTime();
    // if not configured for nanos, use millis
    return etime <= -1 ?
        Pair.of(operation.getProcessingTime(), TimeUnit.MILLISECONDS) :
        Pair.of(etime, TimeUnit.NANOSECONDS);
  }
  /** Sends an JSON-encoded event to the audit service. */
  private void sendEvent(AuditEvent event)
  {
    CreateRequest request = newCreateRequest(resourcePath("/ldap-access"), event.getValue());
    requestHandler
      .handleCreate(new RootContext(), request)
      .thenOnException(new ExceptionHandler<ResourceException>()
        {
          @Override
          public void handleException(ResourceException e)
          {
            logger.error(ERR_COMMON_AUDIT_UNABLE_TO_PROCESS_LOG_EVENT.get(StaticUtils.stackTraceToSingleLineString(e)));
          }
        })
      .thenOnRuntimeException(new RuntimeExceptionHandler()
        {
          @Override
          public void handleRuntimeException(RuntimeException e)
          {
            logger.error(ERR_COMMON_AUDIT_UNABLE_TO_PROCESS_LOG_EVENT.get(StaticUtils.stackTraceToSingleLineString(e)));
          }
        });
  }
}
opendj-server-legacy/src/main/java/org/opends/server/loggers/CommonAuditDependencyProvider.java
New file
@@ -0,0 +1,85 @@
/*
 * 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 legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * 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 legal-notices/CDDLv1_0.txt.
 * 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 2013-2015 ForgeRock AS
 */
package org.opends.server.loggers;
import java.net.InetAddress;
import java.net.UnknownHostException;
import org.forgerock.audit.DependencyProvider;
import org.forgerock.audit.providers.LocalHostNameProvider;
import org.forgerock.audit.providers.ProductInfoProvider;
import org.opends.server.util.DynamicConstants;
/**
 * Simple implementation of dependency provider for common audit.
 */
class CommonAuditDependencyProvider implements DependencyProvider
{
  @Override
  public <T> T getDependency(Class<T> clazz) throws ClassNotFoundException
  {
    if (clazz.isAssignableFrom(LocalHostNameProvider.class))
    {
      return (T) new DJLocalHostNameProvider();
    }
    else if (clazz.isAssignableFrom(ProductInfoProvider.class))
    {
      return (T) new DJProductInfoProvider();
    }
    throw new ClassNotFoundException(String.format("Class %s could not be found", clazz));
  }
  /** DJ implementation for LocalHostNameProvider. */
  private static class DJLocalHostNameProvider implements LocalHostNameProvider
  {
    @Override
    public String getLocalHostName()
    {
      try
      {
        return InetAddress.getLocalHost().getHostName();
      }
      catch (UnknownHostException uhe)
      {
        return null;
      }
    }
  }
  /** DJ implementation for ProductInfoProvider. */
  private static class DJProductInfoProvider implements ProductInfoProvider
  {
    @Override
    public String getProductName()
    {
      return DynamicConstants.PRODUCT_NAME;
    }
  }
}
opendj-server-legacy/src/main/java/org/opends/server/loggers/CommonAuditHTTPAccessLogPublisher.java
New file
@@ -0,0 +1,109 @@
/*
 * 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 legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * 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 legal-notices/CDDLv1_0.txt.
 * 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 2015 ForgeRock AS
 */
package org.opends.server.loggers;
import java.util.List;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.json.resource.RequestHandler;
import org.forgerock.opendj.config.server.ConfigChangeResult;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.std.server.HTTPAccessLogPublisherCfg;
import org.opends.server.core.ServerContext;
import org.opends.server.types.DN;
/**
 * Publishes HTTP access events to the common audit service.
 * <p>
 * This class actually does nothing because HTTP access logging is managed by a CHF filter.
 * (See LdapHttpApplication)
 *
 * @param <T> the type of configuration
 */
class CommonAuditHTTPAccessLogPublisher<T extends HTTPAccessLogPublisherCfg>
  extends HTTPAccessLogPublisher<T>
  implements CommonAuditLogPublisher, ConfigurationChangeListener<T>
{
  /** Current configuration for this publisher. */
  private T config;
  @Override
  public void setRequestHandler(RequestHandler handler)
  {
    // nothing to do
  }
  T getConfig()
  {
    return config;
  }
  @Override
  public void initializeLogPublisher(final T cfg, ServerContext serverContext)
  {
    config = cfg;
  }
  @Override
  public final DN getDN()
  {
    return config != null ? config.dn() : null;
  }
  @Override
  public ConfigChangeResult applyConfigurationChange(final T config)
  {
    this.config = config;
    return new ConfigChangeResult();
  }
  @Override
  public boolean isConfigurationAcceptable(final T configuration,
      final List<LocalizableMessage> unacceptableReasons)
  {
    return true;
  }
  @Override
  public boolean isConfigurationChangeAcceptable(final T config, final List<LocalizableMessage> unacceptableReasons)
  {
    return true;
  }
  @Override
  public void logRequestInfo(HTTPRequestInfo requestInfo)
  {
    // nothing to do
  }
  @Override
  public void close()
  {
    // nothing to do
  }
}
opendj-server-legacy/src/main/java/org/opends/server/loggers/CommonAuditLogPublisher.java
New file
@@ -0,0 +1,44 @@
/*
 * 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 legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * 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 legal-notices/CDDLv1_0.txt.
 * 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 2015 ForgeRock AS
 */
package org.opends.server.loggers;
import org.forgerock.json.resource.RequestHandler;
/**
 * Common interface for all log publisher that log to Common Audit.
 */
interface CommonAuditLogPublisher
{
  /**
   * Sets the handler to use for publishing audit events.
   *
   * @param handler
   *          The common audit handler accepting requests.
   */
  void setRequestHandler(RequestHandler handler);
}
opendj-server-legacy/src/main/java/org/opends/server/loggers/CsvFileAccessLogPublisher.java
New file
@@ -0,0 +1,63 @@
/*
 * 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 legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * 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 legal-notices/CDDLv1_0.txt.
 * 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 2015 ForgeRock AS
 */
package org.opends.server.loggers;
import java.util.List;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.opendj.config.server.ConfigChangeResult;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.std.server.CsvFileAccessLogPublisherCfg;
/**
 * Common Audit publisher which publishes access events to CSV files.
 */
final class CsvFileAccessLogPublisher
  extends CommonAuditAccessLogPublisher<CsvFileAccessLogPublisherCfg>
  implements ConfigurationChangeListener<CsvFileAccessLogPublisherCfg>
{
  @Override
  boolean shouldLogControlOids()
  {
    return getConfig().isLogControlOids();
  }
  @Override
  public ConfigChangeResult applyConfigurationChange(final CsvFileAccessLogPublisherCfg config)
  {
    setConfig(config);
    return new ConfigChangeResult();
  }
  @Override
  public boolean isConfigurationChangeAcceptable(final CsvFileAccessLogPublisherCfg config,
      final List<LocalizableMessage> unacceptableReasons)
  {
    return true;
  }
}
opendj-server-legacy/src/main/java/org/opends/server/loggers/ExternalAccessLogPublisher.java
New file
@@ -0,0 +1,63 @@
/*
 * 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 legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * 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 legal-notices/CDDLv1_0.txt.
 * 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 2015 ForgeRock AS
 */
package org.opends.server.loggers;
import java.util.List;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.opendj.config.server.ConfigChangeResult;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.std.server.ExternalAccessLogPublisherCfg;
/**
 * Common audit publisher which publishes access events to some external
 * publisher.
 */
final class ExternalAccessLogPublisher
  extends CommonAuditAccessLogPublisher<ExternalAccessLogPublisherCfg>
  implements ConfigurationChangeListener<ExternalAccessLogPublisherCfg>
{
  @Override
  boolean shouldLogControlOids()
  {
    return getConfig().isLogControlOids();
  }
  @Override
  public ConfigChangeResult applyConfigurationChange(final ExternalAccessLogPublisherCfg config)
  {
    setConfig(config);
    return new ConfigChangeResult();
  }
  @Override
  public boolean isConfigurationChangeAcceptable(final ExternalAccessLogPublisherCfg config,
      final List<LocalizableMessage> unacceptableReasons)
  {
    return true;
  }
}
opendj-server-legacy/src/main/java/org/opends/server/loggers/HTTPAccessLogger.java
@@ -128,6 +128,7 @@
  public final synchronized void removeAllLogPublishers()
  {
    loggerStorage.removeAllLogPublishers();
    getServerContext().getCommonAudit().shutdown();
  }
}
opendj-server-legacy/src/main/java/org/opends/server/loggers/OpenDJAccessEventBuilder.java
New file
@@ -0,0 +1,267 @@
/*
 * 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 legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * 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 legal-notices/CDDLv1_0.txt.
 * 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 2015 ForgeRock AS
 */
package org.opends.server.loggers;
import static org.forgerock.json.JsonValue.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import org.forgerock.audit.events.AccessAuditEventBuilder;
import org.forgerock.i18n.LocalizableMessageBuilder;
import org.forgerock.json.JsonValue;
import org.forgerock.util.Reject;
import org.opends.server.core.ModifyDNOperation;
import org.opends.server.core.SearchOperation;
import org.opends.server.types.AdditionalLogItem;
import org.opends.server.types.Control;
import org.opends.server.types.Operation;
/**
 * Builder for /audit/access events specific to OpenDJ. This builder add LDAP
 * specific fields to the common fields defined in AccessAuditEventBuilder.
 *
 * @param <T>
 *          This builder.
 */
class OpenDJAccessAuditEventBuilder<T extends OpenDJAccessAuditEventBuilder<T>> extends AccessAuditEventBuilder<T>
{
  private OpenDJAccessAuditEventBuilder()
  {
    super();
  }
  @SuppressWarnings("rawtypes")
  public static <T> OpenDJAccessAuditEventBuilder<?> openDJAccessEvent()
  {
    return new OpenDJAccessAuditEventBuilder();
  }
  public T ldapAdditionalItems(Operation op)
  {
    String items = getAdditionalItemsAsString(op);
    if (!items.isEmpty())
    {
      getLdapValue().put("items", items);
    }
    return self();
  }
  public T ldapAttr(String attr)
  {
    getLdapValue().put("attr", attr);
    return self();
  }
  public T ldapConnectionId(long id)
  {
    getLdapValue().put("connId", id);
    return self();
  }
  public T ldapControls(Operation operation)
  {
    JsonValue ldapValue = getLdapValue();
    List<Control> requestControls = operation.getRequestControls();
    if (!requestControls.isEmpty())
    {
      ldapValue.put("reqControls", getControlsAsString(requestControls));
    }
    List<Control> responseControls = operation.getResponseControls();
    if (!responseControls.isEmpty())
    {
      ldapValue.put("respControls", getControlsAsString(responseControls));
    }
    return self();
  }
  public T ldapDn(String dn)
  {
    getLdapValue().put("dn", dn);
    return self();
  }
  public T ldapFailureMessage(String msg)
  {
    getLdapValue().put("failureReason", msg);
    return self();
  }
  public T ldapIds(Operation op)
  {
    JsonValue ldapValue = getLdapValue();
    ldapValue.put("connId", op.getConnectionID());
    ldapValue.put("msgId", op.getMessageID());
    return self();
  }
  public T ldapIdToAbandon(int id)
  {
    getLdapValue().put("idToAbandon", id);
    return self();
  }
  public T ldapMaskedResultAndMessage(Operation operation)
  {
    JsonValue ldapValue = getLdapValue();
    if (operation.getMaskedResultCode() != null)
    {
      ldapValue.put("maskedResult", operation.getMaskedResultCode().intValue());
    }
    final LocalizableMessageBuilder maskedMsg = operation.getMaskedErrorMessage();
    if (maskedMsg != null && maskedMsg.length() > 0)
    {
      ldapValue.put("maskedMessage", maskedMsg.toString());
    }
    return self();
  }
  public T ldapMessage(String msg)
  {
    getLdapValue().put("message", msg);
    return self();
  }
  public T ldapName(String name)
  {
    getLdapValue().put("name", name);
    return self();
  }
  public T ldapModifyDN(ModifyDNOperation modifyDNOperation)
  {
    JsonValue ldapValue = getLdapValue();
    ldapValue.put("newRDN", modifyDNOperation.getRawNewRDN().toString());
    ldapValue.put("newSup", modifyDNOperation.getRawNewSuperior().toString());
    ldapValue.put("deleteOldRDN", modifyDNOperation.deleteOldRDN());
    return self();
  }
  public T ldapNEntries(int nbEntries)
  {
    getLdapValue().put("nentries", nbEntries);
    return self();
  }
  public T ldapOid(String oid)
  {
    getLdapValue().put("oid", oid);
    return self();
  }
  public T ldapProtocolVersion(String version)
  {
    getLdapValue().put("version", version);
    return self();
  }
  public T ldapReason(String msg)
  {
    getLdapValue().put("reason", msg);
    return self();
  }
  public T ldapSearch(SearchOperation searchOperation)
  {
    JsonValue ldapValue = getLdapValue();
    // for search base, re-uses the "dn" field
    ldapValue.put("dn", searchOperation.getRawBaseDN().toString());
    ldapValue.put("scope", searchOperation.getScope().toString());
    ldapValue.put("filter", searchOperation.getRawFilter().toString());
    final Set<String> attrs = searchOperation.getAttributes();
    if ((attrs == null) || attrs.isEmpty())
    {
      ldapValue.put("attrs", Arrays.asList("ALL"));
    }
    else
    {
      ldapValue.put("attrs", new ArrayList<>(attrs));
    }
    return self();
  }
  public T ldapSync(Operation operation)
  {
    if (operation.isSynchronizationOperation())
    {
      getLdapValue().put("opType", "sync");
    }
    return self();
  }
  public T ldapAuthType(String type)
  {
    getLdapValue().put("authType", type);
    return self();
  }
  public T runAs(String id)
  {
    Reject.ifNull(id);
    jsonValue.put("runAs", id);
    return self();
  }
  private List<String> getControlsAsString(List<Control> controls)
  {
    List<String> list = new ArrayList<>();
    for (final Control control : controls)
    {
      list.add(control.getOID());
    }
    return list;
  }
  private String getAdditionalItemsAsString(Operation operation)
  {
    StringBuilder items = new StringBuilder();
    for (final AdditionalLogItem item : operation.getAdditionalLogItems())
    {
      items.append(' ');
      item.toString(items);
    }
    return items.toString();
  }
  private JsonValue getLdapValue()
  {
    final JsonValue ldapValue;
    if (jsonValue.isDefined("ldap"))
    {
      ldapValue = jsonValue.get("ldap");
    }
    else
    {
      ldapValue = json(object());
      jsonValue.put("ldap", ldapValue);
    }
    return ldapValue;
  }
}
opendj-server-legacy/src/main/java/org/opends/server/protocols/LDIFConnectionHandler.java
@@ -42,6 +42,7 @@
import org.opends.server.api.ClientConnection;
import org.opends.server.api.ConnectionHandler;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ServerContext;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.forgerock.opendj.config.server.ConfigChangeResult;
@@ -122,7 +123,7 @@
  /** {@inheritDoc} */
  @Override
  public void initializeConnectionHandler(LDIFConnectionHandlerCfg
  public void initializeConnectionHandler(ServerContext serverContext, LDIFConnectionHandlerCfg
                                               configuration)
  {
    String ldifDirectoryPath = configuration.getLDIFDirectory();
opendj-server-legacy/src/main/java/org/opends/server/protocols/http/CommonAuditHttpAccessAuditFilter.java
New file
@@ -0,0 +1,169 @@
/*
 * 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 legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * 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 legal-notices/CDDLv1_0.txt.
 * 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 2013-2015 ForgeRock AS
 */
package org.opends.server.protocols.http;
import static org.forgerock.audit.events.AccessAuditEventBuilder.accessEvent;
import static org.forgerock.json.resource.Requests.newCreateRequest;
import static org.forgerock.json.resource.ResourcePath.resourcePath;
import java.util.concurrent.TimeUnit;
import org.forgerock.audit.events.AccessAuditEventBuilder;
import org.forgerock.http.Filter;
import org.forgerock.http.Handler;
import org.forgerock.http.MutableUri;
import org.forgerock.http.protocol.Form;
import org.forgerock.http.protocol.Request;
import org.forgerock.http.protocol.Response;
import org.forgerock.http.protocol.Status;
import org.forgerock.json.resource.CreateRequest;
import org.forgerock.json.resource.RequestHandler;
import org.forgerock.services.context.ClientContext;
import org.forgerock.services.context.Context;
import org.forgerock.services.context.RequestAuditContext;
import org.forgerock.services.context.TransactionIdContext;
import org.forgerock.util.promise.NeverThrowsException;
import org.forgerock.util.promise.Promise;
import org.forgerock.util.promise.ResultHandler;
import org.forgerock.util.promise.RuntimeExceptionHandler;
import org.forgerock.util.time.TimeService;
/**
 * This filter aims to send some access audit events to the AuditService managed as a CREST handler.
 */
public class CommonAuditHttpAccessAuditFilter implements Filter {
    private final RequestHandler auditServiceHandler;
    private final TimeService time;
    private final String productName;
    /**
     * Constructs a new HttpAccessAuditFilter.
     *
     * @param productName The name of product generating the event.
     * @param auditServiceHandler The {@link RequestHandler} to publish the events.
     * @param time The {@link TimeService} to use.
     */
    public CommonAuditHttpAccessAuditFilter(String productName, RequestHandler auditServiceHandler, TimeService time) {
        this.productName = productName;
        this.auditServiceHandler = auditServiceHandler;
        this.time = time;
    }
    @Override
    public Promise<Response, NeverThrowsException> filter(Context context, Request request, Handler next) {
        TransactionIdContext txContext = context.asContext(TransactionIdContext.class);
        ClientContext clientContext = context.asContext(ClientContext.class);
        AccessAuditEventBuilder<?> accessAuditEventBuilder = accessEvent();
        String protocol = clientContext.isSecure() ? "HTTPS" : "HTTP";
        accessAuditEventBuilder
                .eventName(productName + "-" + protocol + "-ACCESS")
                .timestamp(time.now())
                .transactionId(txContext.getTransactionId().getValue())
                .server(clientContext.getLocalAddress(),
                        clientContext.getLocalPort(),
                        clientContext.getLocalName())
                .client(clientContext.getRemoteAddress(),
                        clientContext.getRemotePort(),
                        clientContext.getRemoteHost())
                .httpRequest(clientContext.isSecure(),
                             request.getMethod(),
                             getRequestPath(request.getUri()),
                             new Form().fromRequestQuery(request),
                             request.getHeaders().copyAsMultiMapOfStrings());
        try {
            final Promise<Response, NeverThrowsException> promise;
            promise = next.handle(context, request)
                    .thenOnResult(onResult(context, accessAuditEventBuilder));
            promise.thenOnRuntimeException(onRuntimeException(context, accessAuditEventBuilder));
            return promise;
        } catch (RuntimeException re) {
            onRuntimeException(context, accessAuditEventBuilder).handleRuntimeException(re);
            throw re;
        }
    }
    // See HttpContext.getRequestPath
    private String getRequestPath(MutableUri uri) {
        return new StringBuilder()
            .append(uri.getScheme())
            .append("://")
            .append(uri.getRawAuthority())
            .append(uri.getRawPath()).toString();
    }
    private ResultHandler<? super Response> onResult(final Context context,
                                                     final AccessAuditEventBuilder<?> accessAuditEventBuilder) {
        return new ResultHandler<Response>() {
            @Override
            public void handleResult(Response response) {
                sendAuditEvent(response, context, accessAuditEventBuilder);
            }
        };
    }
    private RuntimeExceptionHandler onRuntimeException(final Context context,
                                                       final AccessAuditEventBuilder<?> accessAuditEventBuilder) {
        return new RuntimeExceptionHandler() {
            @Override
            public void handleRuntimeException(RuntimeException exception) {
                // TODO How to be sure that the final status code sent back with the response will be a 500 ?
                Response response = new Response(Status.INTERNAL_SERVER_ERROR);
                sendAuditEvent(response, context, accessAuditEventBuilder);
            }
        };
    }
    private void sendAuditEvent(final Response response,
                                final Context context,
                                final AccessAuditEventBuilder<?> accessAuditEventBuilder) {
        RequestAuditContext requestAuditContext = context.asContext(RequestAuditContext.class);
        long elapsedTime = time.now() - requestAuditContext.getRequestReceivedTime();
        accessAuditEventBuilder.httpResponse(response.getHeaders().copyAsMultiMapOfStrings());
        accessAuditEventBuilder.response(mapResponseStatus(response.getStatus()),
                                         String.valueOf(response.getStatus().getCode()),
                                         elapsedTime,
                                         TimeUnit.MILLISECONDS);
        CreateRequest request =
            newCreateRequest(resourcePath("/http-access"), accessAuditEventBuilder.toEvent().getValue());
        auditServiceHandler.handleCreate(context, request);
    }
    private static AccessAuditEventBuilder.ResponseStatus mapResponseStatus(Status status) {
        switch(status.getFamily()) {
        case CLIENT_ERROR:
        case SERVER_ERROR:
            return AccessAuditEventBuilder.ResponseStatus.FAILED;
        default:
            return AccessAuditEventBuilder.ResponseStatus.SUCCESSFUL;
        }
    }
}
opendj-server-legacy/src/main/java/org/opends/server/protocols/http/CommonAuditHttpAccessCheckEnabledFilter.java
New file
@@ -0,0 +1,68 @@
/*
 * 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 legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * 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 legal-notices/CDDLv1_0.txt.
 * 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 2013-2015 ForgeRock AS
 */
package org.opends.server.protocols.http;
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.services.context.Context;
import org.forgerock.util.promise.NeverThrowsException;
import org.forgerock.util.promise.Promise;
import org.opends.server.core.ServerContext;
/**
 * Filter that checks if at least one HTTP access logger is enabled for common audit, and short-circuit
 * the corresponding filter if this is not the case.
 */
class CommonAuditHttpAccessCheckEnabledFilter implements Filter
{
  private final ServerContext serverContext;
  /** The HTTP access audit filter to go through if HTTP access logger is enabled. */
  private final Filter httpAccessAuditFilter;
  CommonAuditHttpAccessCheckEnabledFilter(ServerContext serverContext, Filter httpAccessAuditFilter)
  {
    this.serverContext = serverContext;
    this.httpAccessAuditFilter = httpAccessAuditFilter;
  }
  @Override
  public Promise<Response, NeverThrowsException> filter(
      final Context context, final Request request, final Handler next)
  {
    if (serverContext.getCommonAudit().isHttpAccessLogEnabled())
    {
      // introduce HttpAccessAuditFilter into the filter chain
      return httpAccessAuditFilter.filter(context, request, next);
    }
    // avoid HttpAccessAuditFilter, follow the filter chain
    return next.handle(context, request);
  }
}
opendj-server-legacy/src/main/java/org/opends/server/protocols/http/HTTPConnectionHandler.java
@@ -36,6 +36,7 @@
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import java.io.IOException;
import java.net.InetAddress;
import java.util.Arrays;
@@ -80,6 +81,7 @@
import org.opends.server.api.ServerShutdownListener;
import org.opends.server.api.TrustManagerProvider;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ServerContext;
import org.opends.server.extensions.NullKeyManagerProvider;
import org.opends.server.extensions.NullTrustManagerProvider;
import org.opends.server.loggers.HTTPAccessLogger;
@@ -162,6 +164,8 @@
  /** The SSL engine configurator is used for obtaining default SSL parameters. */
  private SSLEngineConfigurator sslEngineConfigurator;
  private ServerContext serverContext;
  /** Default constructor. It is invoked by reflection to create this {@link ConnectionHandler}. */
  public HTTPConnectionHandler()
  {
@@ -419,9 +423,10 @@
  }
  @Override
  public void initializeConnectionHandler(HTTPConnectionHandlerCfg config)
  public void initializeConnectionHandler(ServerContext serverContext, HTTPConnectionHandlerCfg config)
      throws ConfigException, InitializationException
  {
    this.serverContext = serverContext;
    this.enabled = config.isEnabled();
    if (friendlyName == null)
@@ -782,7 +787,8 @@
  {
    // Create and deploy the Web app context
    final WebappContext ctx = new WebappContext(servletName);
    ctx.addServlet(servletName, new HttpFrameworkServlet(new LdapHttpApplication(this))).addMapping(urlPatterns);
    ctx.addServlet(servletName,
        new HttpFrameworkServlet(new LdapHttpApplication(serverContext, this))).addMapping(urlPatterns);
    ctx.deploy(this.httpServer);
  }
opendj-server-legacy/src/main/java/org/opends/server/protocols/http/LdapHttpApplication.java
@@ -34,6 +34,7 @@
import org.forgerock.http.Handler;
import org.forgerock.http.HttpApplication;
import org.forgerock.http.HttpApplicationException;
import org.forgerock.http.filter.TransactionIdInboundFilter;
import org.forgerock.http.handler.Handlers;
import org.forgerock.http.io.Buffer;
import org.forgerock.http.protocol.Request;
@@ -57,7 +58,10 @@
import org.forgerock.util.Factory;
import org.forgerock.util.promise.NeverThrowsException;
import org.forgerock.util.promise.Promise;
import org.forgerock.util.time.TimeService;
import org.opends.messages.ProtocolMessages;
import org.opends.server.core.ServerContext;
import org.opends.server.util.DynamicConstants;
/** Main class of the HTTP Connection Handler web application */
class LdapHttpApplication implements HttpApplication
@@ -115,9 +119,11 @@
  private HTTPConnectionHandler connectionHandler;
  private LdapHttpHandler handler;
  private CollectClientConnectionsFilter filter;
  private final ServerContext serverContext;
  LdapHttpApplication(HTTPConnectionHandler connectionHandler)
  LdapHttpApplication(ServerContext serverContext, HTTPConnectionHandler connectionHandler)
  {
    this.serverContext = serverContext;
    this.connectionHandler = connectionHandler;
  }
@@ -132,7 +138,15 @@
      handler = new LdapHttpHandler(configuration);
      filter = new CollectClientConnectionsFilter(connectionHandler, getAuthenticationConfig(configuration));
      configuration.verifyAllKeysAccessed();
      return Handlers.chainOf(handler, filter);
      TransactionIdInboundFilter transactionIdFilter = new TransactionIdInboundFilter();
      RequestHandler requestHandler = serverContext.getCommonAudit().getAuditServiceForHttpAccessLog();
      CommonAuditHttpAccessAuditFilter httpAccessFilter =
          new CommonAuditHttpAccessAuditFilter(DynamicConstants.PRODUCT_NAME, requestHandler, TimeService.SYSTEM);
      CommonAuditHttpAccessCheckEnabledFilter checkFilter =
          new CommonAuditHttpAccessCheckEnabledFilter(serverContext, httpAccessFilter);
      return Handlers.chainOf(handler, transactionIdFilter, checkFilter, filter);
    }
    catch (final Exception e)
    {
opendj-server-legacy/src/main/java/org/opends/server/protocols/internal/InternalConnectionHandler.java
@@ -29,12 +29,14 @@
import java.util.Collection;
import java.util.LinkedList;
import org.opends.server.admin.std.server.*;
import org.opends.server.api.ClientConnection;
import org.opends.server.api.ConnectionHandler;
import org.opends.server.core.ServerContext;
import org.forgerock.opendj.config.server.ConfigException;
import org.opends.server.types.DN;
import org.opends.server.types.InitializationException;
@@ -108,6 +110,7 @@
   * Initializes this connection handler provider based on the
   * information in the provided connection handler configuration.
   *
   * @param  serverContext  The server context.
   * @param  configuration  The connection handler configuration that
   *                        contains the information to use to
   *                        initialize this connection handler.
@@ -123,8 +126,7 @@
   *                                   configuration.
   */
  @Override
  public void initializeConnectionHandler(
                   ConnectionHandlerCfg configuration)
  public void initializeConnectionHandler(ServerContext serverContext, ConnectionHandlerCfg configuration)
      throws ConfigException, InitializationException
  {
    this.configuration = configuration;
opendj-server-legacy/src/main/java/org/opends/server/protocols/jmx/JmxConnectionHandler.java
@@ -49,6 +49,7 @@
import org.opends.server.api.ConnectionHandler;
import org.opends.server.api.ServerShutdownListener;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ServerContext;
import org.forgerock.opendj.config.server.ConfigChangeResult;
import org.opends.server.types.DN;
import org.opends.server.types.HostPort;
@@ -299,7 +300,7 @@
  /** {@inheritDoc} */
  @Override
  public void initializeConnectionHandler(JMXConnectionHandlerCfg config)
  public void initializeConnectionHandler(ServerContext serverContext, JMXConnectionHandlerCfg config)
         throws ConfigException, InitializationException
  {
    // Configuration is ok.
opendj-server-legacy/src/main/java/org/opends/server/protocols/ldap/LDAPConnectionHandler.java
@@ -58,6 +58,7 @@
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.PluginConfigManager;
import org.opends.server.core.QueueingStrategy;
import org.opends.server.core.ServerContext;
import org.opends.server.core.WorkQueueStrategy;
import org.opends.server.extensions.NullKeyManagerProvider;
import org.opends.server.extensions.NullTrustManagerProvider;
@@ -642,7 +643,7 @@
  /** {@inheritDoc} */
  @Override
  public void initializeConnectionHandler(LDAPConnectionHandlerCfg config)
  public void initializeConnectionHandler(ServerContext serverContext, LDAPConnectionHandlerCfg config)
      throws ConfigException, InitializationException
  {
    if (friendlyName == null)
opendj-server-legacy/src/main/java/org/opends/server/util/ServerConstants.java
@@ -2305,7 +2305,10 @@
  public static final String OID_SUBTREE_DELETE_CONTROL =
       "1.2.840.113556.1.4.805";
  /**
   * The OID for the transactionId control.
   */
  public static final String OID_TRANSACTION_ID_CONTROL = "1.3.6.1.4.1.36733.2.1.5.1";
  /**
   * The OID for the paged results control defined in RFC 2696.
opendj-server-legacy/src/main/resources/org/opends/server/loggers/audit-config.json
New file
@@ -0,0 +1,402 @@
{
  "additionalTopics" : {
      "http-access": {
        "schema": {
          "$schema": "http://json-schema.org/draft-04/schema#",
          "id": "/",
          "type": "object",
          "properties": {
            "_id": {
              "description": "org.forgerock.audit.events.access.id",
              "type": "string"
            },
            "timestamp": {
              "description": "org.forgerock.audit.events.access.timestamp",
              "type": "string"
            },
            "eventName": {
              "description": "org.forgerock.audit.events.access.eventName",
              "type": "string"
            },
            "transactionId": {
              "description": "org.forgerock.audit.events.access.transactionId",
              "type": "string"
            },
            "userId": {
              "description": "org.forgerock.audit.events.access.userId",
              "type": "string"
            },
            "trackingIds": {
              "description": "org.forgerock.audit.events.access.trackingIds",
              "type": "array",
              "items": {
                "id": "0",
                "type": "string"
              }
            },
            "server": {
              "type": "object",
              "properties": {
                "ip": {
                  "description": "org.forgerock.audit.events.access.server.ip",
                  "type": "string"
                },
                "port": {
                  "description": "org.forgerock.audit.events.access.server.port",
                  "type": "string"
                }
              }
            },
            "client": {
              "type": "object",
              "properties": {
                "host": {
                  "description": "org.forgerock.audit.events.access.client.host",
                  "type": "string"
                },
                "ip": {
                  "description": "org.forgerock.audit.events.access.client.ip",
                  "type": "string"
                },
                "port": {
                  "description": "org.forgerock.audit.events.access.client.port",
                  "type": "string"
                }
              }
            },
            "request": {
              "type": "object",
              "properties": {
                "protocol": {
                  "description": "org.forgerock.audit.events.access.request.protocol",
                  "type": "string"
                },
                "operation": {
                  "description": "org.forgerock.audit.events.access.request.operation",
                  "type": "string"
                },
                "detail": {
                  "description": "org.forgerock.audit.events.access.request.detail",
                  "type": "object"
                }
              }
            },
            "http": {
              "type": "object",
              "properties": {
                "request": {
                  "description": "org.forgerock.audit.events.access.http.request",
                  "type": "object",
                  "properties": {
                    "secure": {
                      "description": "org.forgerock.audit.events.access.http.secure",
                      "type": "boolean"
                    },
                    "method": {
                      "description": "org.forgerock.audit.events.access.http.method",
                      "type": "string"
                    },
                    "path": {
                      "description": "org.forgerock.audit.events.access.http.path",
                      "type": "string"
                    },
                    "queryParameters": {
                      "description": "org.forgerock.audit.events.access.http.queryParameters",
                      "type": "object",
                      "additionalProperties": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      }
                    },
                    "headers": {
                      "description": "org.forgerock.audit.events.access.http.headers",
                      "type": "object",
                      "additionalProperties": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      }
                    },
                    "cookies": {
                      "description": "org.forgerock.audit.events.access.http.cookies",
                      "type": "object",
                      "additionalProperties": {
                        "type": "string"
                      }
                    }
                  }
                },
                "response": {
                  "description": "org.forgerock.audit.events.access.http.response",
                  "type": "object",
                  "properties": {
                    "headers": {
                      "description": "org.forgerock.audit.events.access.http.request.headers",
                      "type": "object",
                      "additionalProperties": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            },
            "response": {
              "type": "object",
              "properties": {
                "status": {
                  "description": "org.forgerock.audit.events.access.response.status",
                  "type": "string"
                },
                "statusCode": {
                  "description": "org.forgerock.audit.events.access.response.statusCode",
                  "type": "string"
                },
                "detail": {
                  "description": "org.forgerock.audit.events.access.response.detail",
                  "type": "string"
                },
                "elapsedTime": {
                  "description": "org.forgerock.audit.events.access.response.elapsedTime",
                  "type": "integer"
                },
                "elapsedTimeUnits": {
                  "description": "org.forgerock.audit.events.access.response.elapsedTimeUnits",
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "ldap-access": {
        "schema": {
          "$schema": "http://json-schema.org/draft-04/schema#",
          "id": "/",
          "type": "object",
          "properties": {
            "_id": {
              "description": "org.forgerock.audit.events.access.id",
              "type": "string"
            },
            "timestamp": {
              "description": "org.forgerock.audit.events.access.timestamp",
              "type": "string"
            },
            "eventName": {
              "description": "org.forgerock.audit.events.access.eventName",
              "type": "string"
            },
            "transactionId": {
              "description": "org.forgerock.audit.events.access.transactionId",
              "type": "string"
            },
            "userId": {
              "description": "org.forgerock.audit.events.access.userId",
              "type": "string"
            },
            "runAs": {
              "description": "org.forgerock.audit.events.activity.runAs",
              "type": "string"
            },
            "server": {
              "type": "object",
              "properties": {
                "ip": {
                  "description": "org.forgerock.audit.events.access.server.ip",
                  "type": "string"
                },
                "port": {
                  "description": "org.forgerock.audit.events.access.server.port",
                  "type": "string"
                }
              }
            },
            "client": {
              "type": "object",
              "properties": {
                "host": {
                  "description": "org.forgerock.audit.events.access.client.host",
                  "type": "string"
                },
                "ip": {
                  "description": "org.forgerock.audit.events.access.client.ip",
                  "type": "string"
                },
                "port": {
                  "description": "org.forgerock.audit.events.access.client.port",
                  "type": "string"
                }
              }
            },
            "request": {
              "type": "object",
              "properties": {
                "protocol": {
                  "description": "org.forgerock.audit.events.access.request.protocol",
                  "type": "string"
                },
                "operation": {
                  "description": "org.forgerock.audit.events.access.request.operation",
                  "type": "string"
                }
              }
            },
            "response": {
              "type": "object",
              "properties": {
                "status": {
                  "description": "org.forgerock.audit.events.access.response.status",
                  "type": "string"
                },
                "statusCode": {
                  "description": "org.forgerock.audit.events.access.response.statusCode",
                  "type": "string"
                },
                "detail": {
                  "description": "org.forgerock.audit.events.access.response.detail",
                  "type": "string"
                },
                "elapsedTime": {
                  "description": "org.forgerock.audit.events.access.response.elapsedTime",
                  "type": "integer"
                },
                "elapsedTimeUnits": {
                  "description": "org.forgerock.audit.events.access.response.elapsedTimeUnits",
                  "type": "string"
                }
              }
            },
            "ldap": {
                  "id": "ldap",
                  "type": "object",
                  "properties": {
                    "connId": {
                      "id": "connId",
                      "type": "string"
                    },
                    "msgId": {
                      "id": "msgId",
                      "type": "string"
                    },
                    "dn": {
                      "id": "dn",
                      "type": "string"
                    },
                    "scope": {
                      "id": "scope",
                      "type": "string"
                    },
                    "filter": {
                      "id": "filter",
                      "type": "string"
                    },
                    "attrs": {
                      "id": "attrs",
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "nentries": {
                      "id": "nentries",
                      "type": "string"
                    },
                    "authType": {
                      "id": "authType",
                      "type": "string"
                    },
                    "reqControls": {
                      "id": "reqControls",
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "respControls": {
                      "id": "respControls",
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "additionalItems": {
                      "id": "additionalItems",
                      "type": "string"
                    },
                    "items" : {
                      "id" : "items",
                      "type" : "string"
                    },
                    "attr" : {
                      "id" : "attr",
                      "type" : "string"
                    },
                    "failureReason" : {
                      "id" : "failureReason",
                      "type" : "string"
                    },
                    "idToAbandon" : {
                      "id" : "idToAbandon",
                      "type" : "integer"
                    },
                    "maskedResult" : {
                      "id" : "maskedResult",
                      "type" : "integer"
                    },
                    "maskedMessage" : {
                      "id" : "maskedMessage",
                      "type" : "string"
                    },
                     "message" : {
                      "id" : "message",
                      "type" : "string"
                    },
                     "name" : {
                      "id" : "name",
                      "type" : "string"
                    },
                     "newRDN" : {
                      "id" : "newRDN",
                      "type" : "string"
                    },
                     "newSup" : {
                      "id" : "newSup",
                      "type" : "string"
                    },
                     "deleteOldRDN" : {
                      "id" : "deleteOldRDN",
                      "type" : "boolean"
                    },
                     "oid" : {
                      "id" : "oid",
                      "type" : "string"
                    },
                     "version" : {
                      "id" : "version",
                      "type" : "string"
                    },
                     "reason" : {
                      "id" : "reason",
                      "type" : "string"
                    },
                    "opType": {
                      "id": "opType",
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
opendj-server-legacy/src/messages/org/opends/messages/config.properties
@@ -942,3 +942,9 @@
WARN_CONFIG_LOGGING_UNSUPPORTED_FIELDS_IN_LOG_FORMAT_734=The log format \
 for %s contains the folowing unsupported fields: %s. Their output will be \
 replaced with a dash ("-") character
ERR_CONFIG_LOGGER_CANNOT_UPDATE_LOGGER_735=An error occurred while \
 attempting to update a Directory Server logger from the information in \
 configuration entry %s: %s
ERR_CONFIG_LOGGER_CANNOT_DELETE_LOGGER_736=An error occurred while \
 attempting to delete a Directory Server logger from the information in \
 configuration entry %s: %s
opendj-server-legacy/src/messages/org/opends/messages/logger.properties
@@ -69,3 +69,34 @@
 free disk space in the partition containing log file %s: %s
ERR_LOGGER_ERROR_ENFORCING_RETENTION_POLICY_12=Error occurred while \
 enforcing retention policy %s for logger %s: %s
ERR_COMMON_AUDIT_CREATE_13=Error occurred while creating common audit \
 facility: %s
ERR_COMMON_AUDIT_ADD_OR_UPDATE_LOG_PUBLISHER_14=Error while creating \
 or updating common audit log publisher %s: %s
ERR_COMMON_AUDIT_REMOVE_LOG_PUBLISHER_15=Error while removing common audit log \
 publisher %s: %s
ERR_COMMON_AUDIT_UNSUPPORTED_HANDLER_TYPE_16=Error while adding common audit log \
 publisher %s, the publisher has an unsupported handler type
ERR_COMMON_AUDIT_EXTERNAL_HANDLER_JSON_FILE_17=Error while reading \
 JSON configuration file %s while creating common audit external log publisher %s: %s
ERR_COMMON_AUDIT_EXTERNAL_HANDLER_CREATION_18=Error while creating \
 common audit external log publisher %s: %s
ERR_COMMON_AUDIT_CSV_HANDLER_CREATION_19=Error while creating \
 CSV log publisher %s: %s
ERR_COMMON_AUDIT_UNSUPPORTED_LOG_ROTATION_POLICY_20=Error while adding common audit \
 CSV log publisher %s, the publisher defines an unsupported log rotation policy %s
ERR_COMMON_AUDIT_UNSUPPORTED_LOG_RETENTION_POLICY_21=Error while adding common audit \
 CSV log publisher %s, the publisher defines an unsupported log retention policy %s
ERR_COMMON_AUDIT_UNSUPPORTED_LOG_PUBLISHER_22=Error while processing common audit \
 log publisher %s, this type of log publisher is unsupported
ERR_COMMON_AUDIT_CSV_HANDLER_DELIMITER_CHAR_23=Error while processing common audit \
 log publisher %s, delimiter char '%s' should not contains more than one character
ERR_COMMON_AUDIT_CSV_HANDLER_QUOTE_CHAR_24=Error while processing common audit \
 log publisher %s, quote char '%s' should not contains more than one character
ERR_COMMON_AUDIT_INVALID_TIME_OF_DAY_25=Error while processing common audit \
 log publisher %s, time of the day value '%s' for fixed time log rotation policy \
 is not valid, it should use a 24-hour format "HHmm" : %s
ERR_COMMON_AUDIT_INVALID_TRANSACTION_ID_26=Error while decoding a transaction id control \
 received from a request: %s
ERR_COMMON_AUDIT_UNABLE_TO_PROCESS_LOG_EVENT_27=Error while processing a log event \
 for common audit: %s
opendj-server-legacy/src/messages/org/opends/messages/protocol.properties
@@ -917,3 +917,5 @@
ERR_INVALID_KEYSTORE_1527=No usable key was found for '%s'. Verify the keystore content
INFO_DISABLE_CONNECTION_1528=Disabling %s
ERR_INITIALIZE_HTTP_CONNECTION_HANDLER_1529=Failed to initialize Http Connection Handler
ERR_TRANSACTION_ID_CONTROL_HAS_NO_VALUE_1530=No value was provided for the transaction \
 id control, whereas an UTF-8 encoded value is expected
opendj-server-legacy/src/snmp/src/org/opends/server/snmp/SNMPConnectionHandler.java
@@ -47,6 +47,7 @@
import org.opends.server.admin.std.server.SNMPConnectionHandlerCfg;
import org.forgerock.opendj.config.server.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ServerContext;
import org.opends.server.types.InitializationException;
import static org.opends.messages.ProtocolMessages.*;
@@ -95,8 +96,8 @@
    /**
     * {@inheritDoc}
     */
    public void initializeConnectionHandler(
            SNMPConnectionHandlerCfg configuration)
    @Override
    public void initializeConnectionHandler(ServerContext serverContext, SNMPConnectionHandlerCfg configuration)
            throws ConfigException, InitializationException {
        if (configuration == null) {
@@ -207,6 +208,7 @@
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isConfigurationChangeAcceptable(
            SNMPConnectionHandlerCfg configuration,
            List<LocalizableMessage> unacceptableReasons) {
@@ -217,6 +219,7 @@
    /**
     * {@inheritDoc}
     */
    @Override
    public ConfigChangeResult applyConfigurationChange(
            SNMPConnectionHandlerCfg configuration) {
        if ((this.isOperational) && (this.provider!=null)){
@@ -228,6 +231,7 @@
    /**
     * {@inheritDoc}
     */
    @Override
    public DN getComponentEntryDN() {
        return this.currentConfig.dn();
    }
@@ -235,6 +239,7 @@
    /**
     * {@inheritDoc}
     */
    @Override
    public String getClassName() {
        return SNMPConnectionHandler.class.getName();
    }
@@ -242,6 +247,7 @@
    /**
     * {@inheritDoc}
     */
    @Override
    public LinkedHashMap<String, String> getAlerts() {
        LinkedHashMap<String, String> alerts =
          new LinkedHashMap<String, String>();
opendj-server-legacy/src/test/java/org/opends/server/protocols/internal/InternalConnectionHandlerTestCase.java
@@ -28,11 +28,13 @@
import static org.mockito.Mockito.mock;
import java.util.Collection;
import org.testng.annotations.Test;
import org.opends.server.api.ClientConnection;
import org.opends.server.core.ServerContext;
import static org.testng.Assert.*;
@@ -57,7 +59,7 @@
    InternalConnectionHandler handler = InternalConnectionHandler.getInstance();
    assertNotNull(handler);
    handler.initializeConnectionHandler(null);
    handler.initializeConnectionHandler(mock(ServerContext.class), null);
  }
opendj-server-legacy/src/test/java/org/opends/server/protocols/ldap/LdapTestCase.java
@@ -26,6 +26,8 @@
 */
package org.opends.server.protocols.ldap ;
import static org.mockito.Mockito.mock;
import static org.opends.server.config.ConfigConstants.*;
import java.util.Iterator;
@@ -36,6 +38,7 @@
import org.opends.server.admin.server.AdminTestCaseUtils;
import org.opends.server.admin.std.meta.LDAPConnectionHandlerCfgDefn;
import org.opends.server.admin.std.server.LDAPConnectionHandlerCfg;
import org.opends.server.core.ServerContext;
import org.forgerock.opendj.config.server.ConfigException;
import org.opends.server.types.Attribute;
import org.opends.server.types.Attributes;
@@ -112,7 +115,7 @@
    handlerEntry.addAttribute(a, null);
    LDAPConnectionHandlerCfg config = getConfiguration(handlerEntry);
    LDAPConnectionHandler handler = new LDAPConnectionHandler();
    handler.initializeConnectionHandler(config);
    handler.initializeConnectionHandler(mock(ServerContext.class), config);
    return handler;
  }