/* * 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.server.util.StaticUtils.stackTraceToSingleLineString; import static java.util.Arrays.asList; import static org.opends.messages.LoggerMessages.*; 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.HashMap; import java.util.List; import java.util.Map; import java.util.SortedSet; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; 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.filter.FilterPolicy; 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.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 { /** Transaction id used when the incoming request does not contain a transaction id. */ public static final String DEFAULT_TRANSACTION_ID = "0"; 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 normalized name. */ private final Map<String, AuditServiceProxy> auditServiceCache = new ConcurrentHashMap<>(10); /** Cache of PublisherConfig per http access configuration entry normalized name. */ private final Map<String, PublisherConfig> httpAccessPublishers = new ConcurrentHashMap<>(5); /** Cache of PublisherConfig per access configuration entry normalized name. */ private final Map<String, PublisherConfig> accessPublishers = new ConcurrentHashMap<>(5); /** Audit service shared by all HTTP access publishers. */ private final AuditServiceProxy httpAccessAuditService; private final AtomicBoolean trustTransactionIds = new AtomicBoolean(false); /** * Creates the common audit. * * @throws ConfigException * If an error occurs. */ public CommonAudit() throws ConfigException { configurationFramework = ConfigurationFramework.getInstance(); this.dependencyProvider = new CommonAuditDependencyProvider(); this.httpAccessAuditService = createAuditServiceWithoutHandlers(); } /** * Indicates if transactionIds received from requests should be trusted. * * @return {@code true} if transactionIds should be trusted, {@code false} otherwise */ public boolean shouldTrustTransactionIds() { return trustTransactionIds.get(); } /** * Sets the indicator for transactionIds trusting. * * @param shouldTrust * {@code true} if transactionIds should be trusted, {@code false} * otherwise */ public void setTrustTransactionIds(boolean shouldTrust) { trustTransactionIds.set(shouldTrust); } 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(getConfigNormalizedName(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(String.format("Setting up common audit for configuration entry: %s", newConfig.dn())); try { final PublisherConfig newPublisher = new PublisherConfig(newConfig); String normalizedName = getConfigNormalizedName(newConfig); if (newPublisher.isHttpAccessLog()) { // if an old version exists, it is replaced by the new one httpAccessPublishers.put(normalizedName, newPublisher); buildAuditService(httpAccessAuditServiceSetup()); } else // all other logs { final AuditServiceProxy existingService = auditServiceCache.get(normalizedName); AuditServiceProxy auditService = buildAuditService(new AuditServiceSetup(existingService) { @Override public void addHandlers(AuditServiceBuilder builder) throws ConfigException { registerHandlerName(newPublisher.getName()); addHandlerToBuilder(newPublisher, builder); } }); auditServiceCache.put(normalizedName, auditService); accessPublishers.put(normalizedName, newPublisher); } } 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 { logger.trace(String.format("Shutting down common audit for configuration entry:", config.dn())); String normalizedName = getConfigNormalizedName(config); try { if (httpAccessPublishers.containsKey(normalizedName)) { httpAccessPublishers.remove(normalizedName); buildAuditService(httpAccessAuditServiceSetup()); } else if (accessPublishers.containsKey(normalizedName)) { accessPublishers.remove(normalizedName); AuditServiceProxy auditService = auditServiceCache.remove(normalizedName); if (auditService != null) { auditService.shutdown(); } } // else it is not a registered publisher, nothing to do } 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.values()) { 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()); auditConfig.setFilterPolicies(getFilterPoliciesToPreventHttpHeadersLogging()); 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; } /** * Build filter policies at the AuditService level to prevent logging of the headers for HTTP requests. * <p> * HTTP Headers may contains authentication information. */ private Map<String, FilterPolicy> getFilterPoliciesToPreventHttpHeadersLogging() { Map<String, FilterPolicy> filterPolicies = new HashMap<>(); FilterPolicy policy = new FilterPolicy(); policy.setExcludeIf(asList("/http-access/http/request/headers")); filterPolicies.put("field", policy); return filterPolicies; } 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(publisher, 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(PublisherConfig publisher, 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(publisher, 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) { fileRetention.setMinFreeSpaceRequired(((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; } private String getSecurePassword(PublisherConfig publisher, CsvConfigData config) { String fileName = config.getKeystorePinFile(); File pinFile = getFileForPath(fileName); if (!pinFile.exists()) { logger.warn(ERR_COMMON_AUDIT_KEYSTORE_PIN_FILE_MISSING.get(publisher.getDn(), pinFile)); return ""; } try (BufferedReader br = new BufferedReader(new FileReader(pinFile))) { String pinStr = br.readLine(); if (pinStr == null) { logger.warn(ERR_COMMON_AUDIT_KEYSTORE_PIN_FILE_CONTAINS_EMPTY_PIN.get(publisher.getDn(), pinFile)); return ""; } return pinStr; } catch (IOException ioe) { logger.warn(ERR_COMMON_AUDIT_ERROR_READING_KEYSTORE_PIN_FILE.get(publisher.getDn(), pinFile, stackTraceToSingleLineString(ioe)), ioe); return ""; } } /** * Indicates if the provided log publisher configuration corresponds to a common audit publisher. * <p> * The common audit publisher may not already exist. * <p> * This method must not be used when the corresponding configuration is deleted, because it * implies checking the corresponding configuration entry in the server. * * @param config * The log publisher configuration. * @return {@code true} if publisher corresponds to a common audit publisher * @throws ConfigException * If an error occurs */ public boolean isCommonAuditConfig(LogPublisherCfg config) throws ConfigException { return new PublisherConfig(config).isCommonAudit(); } /** * 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 defined for common audit, {@code false} otherwise * @throws ConfigException * If an error occurs */ public boolean isExistingCommonAuditConfig(LogPublisherCfg config) throws ConfigException { String name = getConfigNormalizedName(config); return accessPublishers.containsKey(name) || httpAccessPublishers.containsKey(name); } /** * 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(); } private String getConfigNormalizedName(LogPublisherCfg config) { return config.dn().toNormalizedUrlSafeString(); } /** * 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; } } }