0N/A/*
0N/A * CDDL HEADER START
0N/A *
0N/A * The contents of this file are subject to the terms of the
0N/A * Common Development and Distribution License, Version 1.0 only
0N/A * (the "License"). You may not use this file except in compliance
0N/A * with the License.
0N/A *
6983N/A * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
6983N/A * or http://forgerock.org/license/CDDLv1.0.html.
0N/A * See the License for the specific language governing permissions
0N/A * and limitations under the License.
0N/A *
0N/A * When distributing Covered Code, include this CDDL HEADER in each
6983N/A * file and include the License file at legal-notices/CDDLv1_0.txt.
6983N/A * If applicable, add the following below this CDDL HEADER, with the
6983N/A * fields enclosed by brackets "[]" replaced with your own identifying
6983N/A * information:
0N/A * Portions Copyright [yyyy] [name of copyright owner]
0N/A *
0N/A * CDDL HEADER END
0N/A *
0N/A *
3215N/A * Copyright 2006-2008 Sun Microsystems, Inc.
6207N/A * Portions copyright 2011-2013 ForgeRock AS.
0N/A */
0N/Apackage org.opends.server.loggers;
3853N/A
3853N/A
0N/A
3853N/Aimport static org.opends.messages.ConfigMessages.*;
6207N/Aimport static org.opends.server.types.ResultCode.*;
6207N/Aimport static org.opends.server.util.ServerConstants.*;
6207N/Aimport static org.opends.server.util.StaticUtils.*;
0N/A
0N/Aimport java.io.File;
0N/Aimport java.io.IOException;
3853N/Aimport java.util.ArrayList;
3853N/Aimport java.util.List;
0N/A
3853N/Aimport org.opends.messages.Message;
1308N/Aimport org.opends.server.admin.server.ConfigurationChangeListener;
5595N/Aimport org.opends.server.admin.std.server.FileBasedAuditLogPublisherCfg;
0N/Aimport org.opends.server.config.ConfigException;
5600N/Aimport org.opends.server.core.*;
4134N/Aimport org.opends.server.types.*;
0N/Aimport org.opends.server.util.Base64;
0N/Aimport org.opends.server.util.StaticUtils;
1280N/Aimport org.opends.server.util.TimeThread;
0N/A
0N/A
0N/A
0N/A/**
0N/A * This class provides the implementation of the audit logger used by
0N/A * the directory server.
0N/A */
5600N/Apublic final class TextAuditLogPublisher extends
5600N/A AbstractTextAccessLogPublisher<FileBasedAuditLogPublisherCfg> implements
5595N/A ConfigurationChangeListener<FileBasedAuditLogPublisherCfg>
0N/A{
3853N/A
1280N/A private TextWriter writer;
1280N/A
5600N/A private FileBasedAuditLogPublisherCfg cfg;
1280N/A
1280N/A
1280N/A
1280N/A /**
1280N/A * {@inheritDoc}
1280N/A */
6207N/A @Override
3023N/A public ConfigChangeResult applyConfigurationChange(
5595N/A FileBasedAuditLogPublisherCfg config)
3023N/A {
3023N/A // Default result code.
3023N/A ResultCode resultCode = ResultCode.SUCCESS;
3023N/A boolean adminActionRequired = false;
3023N/A ArrayList<Message> messages = new ArrayList<Message>();
1280N/A
3023N/A File logFile = getFileForPath(config.getLogFile());
3023N/A FileNamingPolicy fnPolicy = new TimeStampNaming(logFile);
1280N/A
3023N/A try
3023N/A {
3853N/A FilePermission perm = FilePermission.decodeUNIXMode(config
3853N/A .getLogFilePermissions());
1280N/A
3853N/A boolean writerAutoFlush = config.isAutoFlush()
3853N/A && !config.isAsynchronous();
1280N/A
3023N/A TextWriter currentWriter;
3853N/A // Determine the writer we are using. If we were writing
6238N/A // asynchronously,
6238N/A // we need to modify the underlying writer.
6238N/A if (writer instanceof AsynchronousTextWriter)
3023N/A {
6238N/A currentWriter = ((AsynchronousTextWriter) writer).getWrappedWriter();
3023N/A }
3023N/A else
3023N/A {
3023N/A currentWriter = writer;
3023N/A }
1280N/A
3853N/A if (currentWriter instanceof MultifileTextWriter)
3023N/A {
3853N/A MultifileTextWriter mfWriter = (MultifileTextWriter) currentWriter;
1280N/A
3023N/A mfWriter.setNamingPolicy(fnPolicy);
3023N/A mfWriter.setFilePermissions(perm);
3023N/A mfWriter.setAppend(config.isAppend());
3023N/A mfWriter.setAutoFlush(writerAutoFlush);
3853N/A mfWriter.setBufferSize((int) config.getBufferSize());
3023N/A mfWriter.setInterval(config.getTimeInterval());
1280N/A
3023N/A mfWriter.removeAllRetentionPolicies();
3023N/A mfWriter.removeAllRotationPolicies();
1280N/A
3853N/A for (DN dn : config.getRotationPolicyDNs())
3023N/A {
3023N/A mfWriter.addRotationPolicy(DirectoryServer.getRotationPolicy(dn));
3023N/A }
1280N/A
3853N/A for (DN dn : config.getRetentionPolicyDNs())
3023N/A {
3023N/A mfWriter.addRetentionPolicy(DirectoryServer.getRetentionPolicy(dn));
3023N/A }
1280N/A
6238N/A if (writer instanceof AsynchronousTextWriter
6238N/A && !config.isAsynchronous())
3023N/A {
3023N/A // The asynronous setting is being turned off.
6238N/A AsynchronousTextWriter asyncWriter =
6238N/A ((AsynchronousTextWriter) writer);
3023N/A writer = mfWriter;
3023N/A asyncWriter.shutdown(false);
3023N/A }
1280N/A
6238N/A if (!(writer instanceof AsynchronousTextWriter)
3853N/A && config.isAsynchronous())
3023N/A {
6238N/A // The asynchronous setting is being turned on.
6238N/A writer = new AsynchronousTextWriter(
6238N/A "Asynchronous Text Writer for " +
6238N/A config.dn().toNormalizedString(),
3853N/A config.getQueueSize(), config.isAutoFlush(), mfWriter);
3023N/A }
1280N/A
5600N/A if ((cfg.isAsynchronous() && config.isAsynchronous())
5600N/A && (cfg.getQueueSize() != config.getQueueSize()))
3023N/A {
3023N/A adminActionRequired = true;
3023N/A }
1280N/A
5600N/A cfg = config;
3023N/A }
3023N/A }
3853N/A catch (Exception e)
3023N/A {
3853N/A Message message = ERR_CONFIG_LOGGING_CANNOT_CREATE_WRITER.get(config.dn()
3853N/A .toString(), stackTraceToSingleLineString(e));
3023N/A resultCode = DirectoryServer.getServerErrorResultCode();
3023N/A messages.add(message);
1280N/A
3023N/A }
1280N/A
3023N/A return new ConfigChangeResult(resultCode, adminActionRequired, messages);
3023N/A }
1280N/A
0N/A
0N/A
0N/A /**
1280N/A * {@inheritDoc}
1280N/A */
1308N/A @Override()
5600N/A protected void close0()
0N/A {
1280N/A writer.shutdown();
5600N/A cfg.removeFileBasedAuditChangeListener(this);
0N/A }
0N/A
0N/A
0N/A
0N/A /**
1308N/A * {@inheritDoc}
0N/A */
1308N/A @Override()
6207N/A public void initializeLogPublisher(FileBasedAuditLogPublisherCfg cfg)
3853N/A throws ConfigException, InitializationException
0N/A {
5600N/A File logFile = getFileForPath(cfg.getLogFile());
3853N/A FileNamingPolicy fnPolicy = new TimeStampNaming(logFile);
3853N/A
3853N/A try
3853N/A {
5600N/A FilePermission perm = FilePermission.decodeUNIXMode(cfg
3853N/A .getLogFilePermissions());
3853N/A
3853N/A LogPublisherErrorHandler errorHandler = new LogPublisherErrorHandler(
5600N/A cfg.dn());
3853N/A
5600N/A boolean writerAutoFlush = cfg.isAutoFlush()
5600N/A && !cfg.isAsynchronous();
3853N/A
3853N/A MultifileTextWriter writer = new MultifileTextWriter(
5600N/A "Multifile Text Writer for " + cfg.dn().toNormalizedString(),
5600N/A cfg.getTimeInterval(), fnPolicy, perm, errorHandler, "UTF-8",
5600N/A writerAutoFlush, cfg.isAppend(), (int) cfg.getBufferSize());
3853N/A
3853N/A // Validate retention and rotation policies.
5600N/A for (DN dn : cfg.getRotationPolicyDNs())
3853N/A {
3853N/A writer.addRotationPolicy(DirectoryServer.getRotationPolicy(dn));
3853N/A }
3853N/A
5600N/A for (DN dn : cfg.getRetentionPolicyDNs())
3853N/A {
3853N/A writer.addRetentionPolicy(DirectoryServer.getRetentionPolicy(dn));
3853N/A }
3853N/A
5600N/A if (cfg.isAsynchronous())
3853N/A {
6238N/A this.writer = new AsynchronousTextWriter("Asynchronous Text Writer for "
5600N/A + cfg.dn().toNormalizedString(), cfg.getQueueSize(), cfg
3853N/A .isAutoFlush(), writer);
3853N/A }
3853N/A else
3853N/A {
3853N/A this.writer = writer;
3853N/A }
3853N/A }
3853N/A catch (DirectoryException e)
3853N/A {
5600N/A Message message = ERR_CONFIG_LOGGING_CANNOT_CREATE_WRITER.get(cfg.dn()
3853N/A .toString(), String.valueOf(e));
3853N/A throw new InitializationException(message, e);
3853N/A
3853N/A }
3853N/A catch (IOException e)
3853N/A {
3853N/A Message message = ERR_CONFIG_LOGGING_CANNOT_OPEN_FILE.get(logFile
5600N/A .toString(), cfg.dn().toString(), String.valueOf(e));
3853N/A throw new InitializationException(message, e);
3853N/A
3853N/A }
3853N/A
5600N/A initializeFilters(cfg);
5600N/A this.cfg = cfg;
5600N/A cfg.addFileBasedAuditChangeListener(this);
0N/A }
0N/A
0N/A
1308N/A
0N/A /**
1308N/A * {@inheritDoc}
0N/A */
3853N/A @Override
5600N/A public boolean isConfigurationAcceptable(
5600N/A FileBasedAuditLogPublisherCfg configuration,
3853N/A List<Message> unacceptableReasons)
0N/A {
5603N/A return isFilterConfigurationAcceptable(configuration, unacceptableReasons)
5603N/A && isConfigurationChangeAcceptable(configuration, unacceptableReasons);
0N/A }
0N/A
0N/A
1308N/A
0N/A /**
1308N/A * {@inheritDoc}
0N/A */
6207N/A @Override
3853N/A public boolean isConfigurationChangeAcceptable(
5595N/A FileBasedAuditLogPublisherCfg config, List<Message> unacceptableReasons)
0N/A {
3853N/A // Make sure the permission is valid.
3853N/A try
3853N/A {
3853N/A FilePermission filePerm = FilePermission.decodeUNIXMode(config
3853N/A .getLogFilePermissions());
3853N/A if (!filePerm.isOwnerWritable())
3853N/A {
3853N/A Message message = ERR_CONFIG_LOGGING_INSANE_MODE.get(config
3853N/A .getLogFilePermissions());
3853N/A unacceptableReasons.add(message);
3853N/A return false;
3853N/A }
3853N/A }
3853N/A catch (DirectoryException e)
3853N/A {
3853N/A Message message = ERR_CONFIG_LOGGING_MODE_INVALID.get(config
3853N/A .getLogFilePermissions(), String.valueOf(e));
3853N/A unacceptableReasons.add(message);
3853N/A return false;
3853N/A }
3853N/A
3853N/A return true;
0N/A }
0N/A
0N/A
1308N/A
0N/A /**
1308N/A * {@inheritDoc}
0N/A */
1308N/A @Override()
0N/A public void logAddResponse(AddOperation addOperation)
0N/A {
3853N/A if (!isLoggable(addOperation))
0N/A {
3853N/A return;
3853N/A }
3853N/A
3853N/A StringBuilder buffer = new StringBuilder(50);
3853N/A appendHeader(addOperation, buffer);
3853N/A
3853N/A buffer.append("dn:");
3853N/A encodeValue(addOperation.getEntryDN().toString(), buffer);
3853N/A buffer.append(EOL);
3853N/A
3853N/A buffer.append("changetype: add");
3853N/A buffer.append(EOL);
3853N/A
3853N/A for (String ocName : addOperation.getObjectClasses().values())
3853N/A {
3853N/A buffer.append("objectClass: ");
3853N/A buffer.append(ocName);
3853N/A buffer.append(EOL);
3853N/A }
3853N/A
3853N/A for (List<Attribute> attrList : addOperation.getUserAttributes().values())
3853N/A {
3853N/A for (Attribute a : attrList)
1801N/A {
3853N/A for (AttributeValue v : a)
1801N/A {
3853N/A buffer.append(a.getName());
3853N/A buffer.append(":");
3853N/A encodeValue(v.getValue(), buffer);
3853N/A buffer.append(EOL);
1801N/A }
1801N/A }
0N/A }
0N/A
3853N/A for (List<Attribute> attrList : addOperation.getOperationalAttributes()
3853N/A .values())
0N/A {
3853N/A for (Attribute a : attrList)
0N/A {
3853N/A for (AttributeValue v : a)
0N/A {
3853N/A buffer.append(a.getName());
3853N/A buffer.append(":");
3853N/A encodeValue(v.getValue(), buffer);
3853N/A buffer.append(EOL);
0N/A }
1308N/A }
0N/A }
1308N/A
3853N/A writer.writeRecord(buffer.toString());
0N/A }
0N/A
0N/A
1308N/A
0N/A /**
1308N/A * {@inheritDoc}
0N/A */
1308N/A @Override()
0N/A public void logDeleteResponse(DeleteOperation deleteOperation)
0N/A {
3853N/A if (!isLoggable(deleteOperation))
0N/A {
3853N/A return;
0N/A }
0N/A
3853N/A StringBuilder buffer = new StringBuilder(50);
3853N/A appendHeader(deleteOperation, buffer);
3853N/A
3853N/A buffer.append("dn:");
3853N/A encodeValue(deleteOperation.getEntryDN().toString(), buffer);
3853N/A buffer.append(EOL);
3853N/A
3853N/A buffer.append("changetype: delete");
3853N/A buffer.append(EOL);
3853N/A
3853N/A writer.writeRecord(buffer.toString());
0N/A }
0N/A
0N/A
0N/A
0N/A /**
1308N/A * {@inheritDoc}
0N/A */
1308N/A @Override()
3853N/A public void logModifyDNResponse(ModifyDNOperation modifyDNOperation)
0N/A {
3853N/A if (!isLoggable(modifyDNOperation))
3853N/A {
3853N/A return;
3853N/A }
3853N/A
3853N/A StringBuilder buffer = new StringBuilder(50);
3853N/A appendHeader(modifyDNOperation, buffer);
0N/A
3853N/A buffer.append("dn:");
3853N/A encodeValue(modifyDNOperation.getEntryDN().toString(), buffer);
3853N/A buffer.append(EOL);
0N/A
3853N/A buffer.append("changetype: moddn");
3853N/A buffer.append(EOL);
3853N/A
3853N/A buffer.append("newrdn:");
3853N/A encodeValue(modifyDNOperation.getNewRDN().toString(), buffer);
3853N/A buffer.append(EOL);
0N/A
3853N/A buffer.append("deleteoldrdn: ");
3853N/A if (modifyDNOperation.deleteOldRDN())
3853N/A {
3853N/A buffer.append("1");
3853N/A }
3853N/A else
3853N/A {
3853N/A buffer.append("0");
3853N/A }
3853N/A buffer.append(EOL);
0N/A
3853N/A DN newSuperior = modifyDNOperation.getNewSuperior();
3853N/A if (newSuperior != null)
3853N/A {
3853N/A buffer.append("newsuperior:");
3853N/A encodeValue(newSuperior.toString(), buffer);
3853N/A buffer.append(EOL);
3853N/A }
0N/A
3853N/A writer.writeRecord(buffer.toString());
0N/A }
0N/A
0N/A
0N/A
0N/A /**
1308N/A * {@inheritDoc}
0N/A */
1308N/A @Override()
0N/A public void logModifyResponse(ModifyOperation modifyOperation)
0N/A {
3853N/A if (!isLoggable(modifyOperation))
0N/A {
3853N/A return;
3853N/A }
3853N/A
3853N/A StringBuilder buffer = new StringBuilder(50);
3853N/A appendHeader(modifyOperation, buffer);
3853N/A
3853N/A buffer.append("dn:");
3853N/A encodeValue(modifyOperation.getEntryDN().toString(), buffer);
3853N/A buffer.append(EOL);
3853N/A
3853N/A buffer.append("changetype: modify");
3853N/A buffer.append(EOL);
3853N/A
3853N/A boolean first = true;
3853N/A for (Modification mod : modifyOperation.getModifications())
3853N/A {
3853N/A if (first)
1801N/A {
3853N/A first = false;
1801N/A }
1801N/A else
1801N/A {
3853N/A buffer.append("-");
3853N/A buffer.append(EOL);
1801N/A }
0N/A
3853N/A switch (mod.getModificationType())
3853N/A {
3853N/A case ADD:
3853N/A buffer.append("add: ");
3853N/A break;
3853N/A case DELETE:
3853N/A buffer.append("delete: ");
3853N/A break;
3853N/A case REPLACE:
3853N/A buffer.append("replace: ");
3853N/A break;
3853N/A case INCREMENT:
3853N/A buffer.append("increment: ");
3853N/A break;
3853N/A default:
3853N/A continue;
3853N/A }
1308N/A
3853N/A Attribute a = mod.getAttribute();
3853N/A buffer.append(a.getName());
602N/A buffer.append(EOL);
1308N/A
3853N/A for (AttributeValue v : a)
0N/A {
3853N/A buffer.append(a.getName());
3853N/A buffer.append(":");
3853N/A encodeValue(v.getValue(), buffer);
3853N/A buffer.append(EOL);
3853N/A }
3853N/A }
1308N/A
3853N/A writer.writeRecord(buffer.toString());
0N/A }
0N/A
0N/A
0N/A
3853N/A // Appends the common log header information to the provided buffer.
3853N/A private void appendHeader(Operation operation, StringBuilder buffer)
0N/A {
3853N/A buffer.append("# ");
3853N/A buffer.append(TimeThread.getLocalTime());
3853N/A buffer.append("; conn=");
3853N/A buffer.append(operation.getConnectionID());
3853N/A buffer.append("; op=");
3853N/A buffer.append(operation.getOperationID());
3853N/A buffer.append(EOL);
0N/A }
0N/A
0N/A
0N/A
0N/A /**
3853N/A * Appends the appropriately-encoded attribute value to the provided
3853N/A * buffer.
0N/A *
3853N/A * @param str
3853N/A * The ASN.1 octet string containing the value to append.
3853N/A * @param buffer
3853N/A * The buffer to which to append the value.
0N/A */
4134N/A private void encodeValue(ByteSequence str, StringBuilder buffer)
0N/A {
4134N/A if(StaticUtils.needsBase64Encoding(str))
1514N/A {
1514N/A buffer.append(": ");
4134N/A buffer.append(Base64.encode(str));
3853N/A }
3853N/A else
1514N/A {
1514N/A buffer.append(" ");
4134N/A buffer.append(str.toString());
1514N/A }
0N/A }
1308N/A
1308N/A
1308N/A
1308N/A /**
3853N/A * Appends the appropriately-encoded attribute value to the provided
3853N/A * buffer.
1308N/A *
3853N/A * @param str
3853N/A * The string containing the value to append.
3853N/A * @param buffer
3853N/A * The buffer to which to append the value.
1308N/A */
1308N/A private void encodeValue(String str, StringBuilder buffer)
1308N/A {
3853N/A if (StaticUtils.needsBase64Encoding(str))
1514N/A {
1514N/A buffer.append(": ");
1514N/A buffer.append(Base64.encode(getBytes(str)));
3853N/A }
3853N/A else
1514N/A {
1514N/A buffer.append(" ");
1514N/A buffer.append(str);
1514N/A }
1514N/A }
1514N/A
3853N/A
3853N/A
3853N/A // Determines whether the provided operation should be logged.
3853N/A private boolean isLoggable(Operation operation)
1514N/A {
3853N/A if (operation.getResultCode() != SUCCESS)
1514N/A {
3853N/A return false;
1514N/A }
5600N/A else
5600N/A {
5600N/A return isResponseLoggable(operation);
5600N/A }
1308N/A }
0N/A}