blob: e9e31c9040c565963738fe152ecdbeea29aa38dd [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cassandra.audit;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import com.google.common.base.Strings;
import org.apache.commons.lang3.StringUtils;
import org.apache.cassandra.config.CassandraRelevantProperties;
import org.apache.cassandra.config.ParameterizedClass;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.io.util.File;
import org.apache.cassandra.utils.binlog.BinLogOptions;
public class AuditLogOptions extends BinLogOptions
{
public volatile boolean enabled = false;
public ParameterizedClass logger = new ParameterizedClass(BinAuditLogger.class.getSimpleName(), Collections.emptyMap());
public String included_keyspaces = StringUtils.EMPTY;
// CASSANDRA-14498: By default, system, system_schema and system_virtual_schema are excluded, but these can be included via cassandra.yaml
public String excluded_keyspaces = "system,system_schema,system_virtual_schema";
public String included_categories = StringUtils.EMPTY;
public String excluded_categories = StringUtils.EMPTY;
public String included_users = StringUtils.EMPTY;
public String excluded_users = StringUtils.EMPTY;
public String audit_logs_dir;
public AuditLogOptions()
{
String auditLogDir = CassandraRelevantProperties.LOG_DIR_AUDIT.getString();
String logDir = CassandraRelevantProperties.LOG_DIR.getString() + "/audit";
Path path = auditLogDir == null ? File.getPath(logDir) : File.getPath(auditLogDir);
audit_logs_dir = path.normalize().toString();
}
public static AuditLogOptions validate(final AuditLogOptions options) throws ConfigurationException
{
// not validating keyspaces nor users on purpose,
// logging might be enabled on these entities before they exist
// so they are picked up automatically
validateCategories(options.included_categories);
validateCategories(options.excluded_categories);
// other fields in BinLogOptions are validated upon BinAuditLogger initialisation
return options;
}
public static class Builder
{
private boolean enabled;
private ParameterizedClass logger;
private String includedKeyspaces;
private String excludedKeyspaces;
private String includedCategories;
private String excludedCategories;
private String includedUsers;
private String excludedUsers;
private String auditLogDir;
private int maxQueueWeight;
private int maxArchiveRetries;
private String rollCycle;
private String archiveCommand;
private boolean block;
private long maxLogSize;
public Builder()
{
this(new AuditLogOptions());
}
public Builder(final AuditLogOptions opts)
{
this.enabled = opts.enabled;
this.logger = opts.logger;
this.includedKeyspaces = opts.included_keyspaces;
this.excludedKeyspaces = opts.excluded_keyspaces;
this.includedCategories = opts.included_categories;
this.excludedCategories = opts.excluded_categories;
this.includedUsers = opts.included_users;
this.excludedUsers = opts.excluded_users;
this.auditLogDir = opts.audit_logs_dir;
this.maxQueueWeight = opts.max_queue_weight;
this.maxArchiveRetries = opts.max_archive_retries;
this.rollCycle = opts.roll_cycle;
this.archiveCommand = opts.archive_command;
this.block = opts.block;
this.maxLogSize = opts.max_log_size;
}
public Builder withEnabled(boolean enabled)
{
this.enabled = enabled;
return this;
}
public Builder withLogger(final String loggerName, Map<String, String> parameters)
{
if (loggerName != null && !loggerName.trim().isEmpty())
{
this.logger = new ParameterizedClass(loggerName.trim(), parameters);
}
return this;
}
public Builder withIncludedKeyspaces(final String includedKeyspaces)
{
sanitise(includedKeyspaces).map(v -> this.includedKeyspaces = v);
return this;
}
public Builder withExcludedKeyspaces(final String excludedKeyspaces)
{
sanitise(excludedKeyspaces).map(v -> this.excludedKeyspaces = v);
return this;
}
public Builder withIncludedCategories(final String includedCategories)
{
sanitise(includedCategories).map(v -> this.includedCategories = v.toUpperCase());
return this;
}
public Builder withExcludedCategories(final String excludedCategories)
{
sanitise(excludedCategories).map(v -> this.excludedCategories = v.toUpperCase());
return this;
}
public Builder withIncludedUsers(final String includedUsers)
{
sanitise(includedUsers).map(v -> this.includedUsers = v);
return this;
}
public Builder withExcludedUsers(final String excludedUsers)
{
sanitise(excludedUsers).map(v -> this.excludedUsers = v);
return this;
}
public Builder withAuditLogDir(final String auditLogDir)
{
this.auditLogDir = auditLogDir;
return this;
}
public Builder withRollCycle(final String rollCycle)
{
sanitise(rollCycle).map(v -> this.rollCycle = v.toUpperCase());
return this;
}
public Builder withArchiveCommand(final String archiveCommand)
{
if (archiveCommand != null)
{
this.archiveCommand = archiveCommand;
}
return this;
}
public Builder withBlock(final Boolean block)
{
if (block != null)
{
this.block = block;
}
return this;
}
public Builder withMaxLogSize(final long maxLogSize)
{
if (maxLogSize != Long.MIN_VALUE)
{
this.maxLogSize = maxLogSize;
}
return this;
}
public Builder withMaxArchiveRetries(final int maxArchiveRetries)
{
if (maxArchiveRetries != Integer.MIN_VALUE)
{
this.maxArchiveRetries = maxArchiveRetries;
}
return this;
}
public Builder withMaxQueueWeight(final int maxQueueWeight)
{
if (maxQueueWeight != Integer.MIN_VALUE)
{
this.maxQueueWeight = maxQueueWeight;
}
return this;
}
public AuditLogOptions build()
{
final AuditLogOptions opts = new AuditLogOptions();
opts.enabled = this.enabled;
opts.logger = this.logger;
sanitise(this.includedKeyspaces).map(v -> opts.included_keyspaces = v);
sanitise(this.excludedKeyspaces).map(v -> opts.excluded_keyspaces = v);
sanitise(this.includedCategories).map(v -> opts.included_categories = v.toUpperCase());
sanitise(this.excludedCategories).map(v -> opts.excluded_categories = v.toUpperCase());
sanitise(this.includedUsers).map(v -> opts.included_users = v);
sanitise(this.excludedUsers).map(v -> opts.excluded_users = v);
opts.roll_cycle = this.rollCycle;
opts.audit_logs_dir = this.auditLogDir;
opts.max_queue_weight = this.maxQueueWeight;
opts.max_archive_retries = this.maxArchiveRetries;
opts.archive_command = this.archiveCommand;
opts.block = this.block;
opts.max_log_size = this.maxLogSize;
AuditLogOptions.validate(opts);
return opts;
}
private static Optional<String> sanitise(final String input)
{
if (input == null || input.trim().isEmpty())
return Optional.empty();
return Optional.of(Arrays.stream(input.split(","))
.map(String::trim)
.map(Strings::emptyToNull)
.filter(Objects::nonNull)
.collect(Collectors.joining(",")));
}
}
private static void validateCategories(final String categories)
{
assert categories != null;
if (categories.isEmpty())
return;
for (final String includedCategory : categories.split(","))
{
try
{
AuditLogEntryCategory.valueOf(includedCategory);
}
catch (final IllegalArgumentException ex)
{
throw new ConfigurationException(String.format("category %s not found in %s",
includedCategory,
AuditLogEntryCategory.class.getName()),
ex);
}
}
}
public String toString()
{
return "AuditLogOptions{" +
"enabled=" + enabled +
", logger='" + logger + '\'' +
", included_keyspaces='" + included_keyspaces + '\'' +
", excluded_keyspaces='" + excluded_keyspaces + '\'' +
", included_categories='" + included_categories + '\'' +
", excluded_categories='" + excluded_categories + '\'' +
", included_users='" + included_users + '\'' +
", excluded_users='" + excluded_users + '\'' +
", audit_logs_dir='" + audit_logs_dir + '\'' +
", archive_command='" + archive_command + '\'' +
", roll_cycle='" + roll_cycle + '\'' +
", block=" + block +
", max_queue_weight=" + max_queue_weight +
", max_log_size=" + max_log_size +
", max_archive_retries=" + max_archive_retries +
'}';
}
}