diff --git a/.gitignore b/.gitignore
index be816f6c..ed6bc8ac 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,4 +5,5 @@ target/
.idea
*.iml
dependency-reduced-pom.xml
+.idea/
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 9896aeb3..86b97c7b 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -8,4 +8,4 @@
-
\ No newline at end of file
+
diff --git a/CHANGES.md b/CHANGES.md
index 685d94d5..d1dbc406 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -4,6 +4,7 @@
* Use SnakeYaml's SafeConstructor to avoid CVE-2022-1471
* Support escaping characters in log messages - #207
+* Add Audit Prepare statements - #226
## Version 2.10.0
* Build with Cassandra 4.0.7 (only flavor ecaudit_c4.0)
diff --git a/conf/audit.yaml b/conf/audit.yaml
index 3aa86505..59274987 100644
--- a/conf/audit.yaml
+++ b/conf/audit.yaml
@@ -149,3 +149,8 @@ whitelist_cache_update_interval_in_ms: 20000
# Maximum number of entries in the whitelist cache
# Default to 10 x the value of roles_cache_max_entries (specified in cassandra.yaml)
whitelist_cache_max_entries: 10000
+
+# Whether to suppress the auditing of prepare statements
+# Default is to suppress the audit statements this is to match the previous versions which do not audit prepare statements
+
+suppress_prepare_statements: true
\ No newline at end of file
diff --git a/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/AuditAdapter.java b/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/AuditAdapter.java
index 31a49503..f20eb34d 100644
--- a/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/AuditAdapter.java
+++ b/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/AuditAdapter.java
@@ -30,6 +30,7 @@
import com.ericsson.bss.cassandra.ecaudit.entry.PreparedAuditOperation;
import com.ericsson.bss.cassandra.ecaudit.entry.factory.AuditEntryBuilderFactory;
import com.ericsson.bss.cassandra.ecaudit.entry.suppressor.BoundValueSuppressor;
+import com.ericsson.bss.cassandra.ecaudit.entry.suppressor.PrepareAuditOperation;
import com.ericsson.bss.cassandra.ecaudit.facade.Auditor;
import com.ericsson.bss.cassandra.ecaudit.utils.Exceptions;
import org.apache.cassandra.cql3.BatchQueryOptions;
@@ -108,6 +109,31 @@ public void auditRegular(String operation, ClientState state, Status status, lon
}
}
+ /**
+ * Audit a regular CQL statement.
+ *
+ * @param operation the CQL statement to audit
+ * @param state the client state accompanying the statement
+ * @param status the statement operation status
+ * @param timestamp the system timestamp for the request
+ */
+ public void auditPrepare(String operation, ClientState state, Status status, long timestamp)
+ {
+ if (auditor.shouldLogForStatus(status) && auditor.shouldLogPrepareStatements())
+ {
+ AuditEntry logEntry = entryBuilderFactory.createEntryBuilder(operation, state)
+ .client(state.getRemoteAddress())
+ .coordinator(FBUtilities.getBroadcastAddress())
+ .user(state.getUser().getName())
+ .operation(new PrepareAuditOperation(operation))
+ .status(status)
+ .timestamp(timestamp)
+ .build();
+
+ auditor.audit(logEntry);
+ }
+ }
+
/**
* Audit a prepared statement.
*
diff --git a/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/config/AuditConfig.java b/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/config/AuditConfig.java
index 4001245f..ae3cb51c 100644
--- a/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/config/AuditConfig.java
+++ b/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/config/AuditConfig.java
@@ -120,6 +120,12 @@ public void setWhitelistCacheMaxEntries(int whitelistCacheMaxEntries)
yamlConfig.setWhitelistCacheMaxEntries(whitelistCacheMaxEntries);
}
+ public boolean isSuppressPrepareStatements()
+ {
+ loadConfigIfNeeded();
+ return yamlConfig.isSuppressPrepareStatements();
+ }
+
private synchronized void loadConfigIfNeeded()
{
if (yamlConfig == null)
diff --git a/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/config/AuditYamlConfig.java b/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/config/AuditYamlConfig.java
index ff5d5e15..e00431a3 100644
--- a/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/config/AuditYamlConfig.java
+++ b/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/config/AuditYamlConfig.java
@@ -52,6 +52,7 @@ public final class AuditYamlConfig
public Integer whitelist_cache_validity_in_ms;
public Integer whitelist_cache_update_interval_in_ms;
public Integer whitelist_cache_max_entries;
+ public Boolean suppress_prepare_statements;
static AuditYamlConfig createWithoutFile()
{
@@ -157,4 +158,11 @@ public void setWhitelistCacheMaxEntries(Integer whitelistCacheMaxEntries)
{
this.whitelist_cache_max_entries = whitelistCacheMaxEntries;
}
-}
+
+ public Boolean isSuppressPrepareStatements()
+ {
+ return suppress_prepare_statements == null
+ ? Boolean.TRUE
+ : suppress_prepare_statements;
+ }
+ }
diff --git a/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/entry/suppressor/PrepareAuditOperation.java b/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/entry/suppressor/PrepareAuditOperation.java
new file mode 100644
index 00000000..692ca018
--- /dev/null
+++ b/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/entry/suppressor/PrepareAuditOperation.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2018 Telefonaktiebolaget LM Ericsson
+ *
+ * Licensed 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 com.ericsson.bss.cassandra.ecaudit.entry.suppressor;
+
+import com.ericsson.bss.cassandra.ecaudit.common.record.AuditOperation;
+
+public class PrepareAuditOperation implements AuditOperation
+{
+ private final String operationString;
+
+ /**
+ * Construct a new audit operation.
+ * @param operationString the operation/statement to wrap.
+ */
+ public PrepareAuditOperation(String operationString)
+ {
+ this.operationString = "Prepared: " + operationString;
+ }
+
+ @Override
+ public String getOperationString()
+ {
+ return operationString;
+ }
+
+ @Override
+ public String getNakedOperationString()
+ {
+ return operationString;
+ }
+}
diff --git a/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/facade/Auditor.java b/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/facade/Auditor.java
index c23ae520..f601b239 100644
--- a/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/facade/Auditor.java
+++ b/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/facade/Auditor.java
@@ -53,6 +53,8 @@ public interface Auditor
*/
boolean shouldLogFailedBatchSummary();
+ boolean shouldLogPrepareStatements();
+
/**
* Sets the log timing strategy to use.
*
diff --git a/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/facade/DefaultAuditor.java b/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/facade/DefaultAuditor.java
index 0ede13ae..20513368 100644
--- a/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/facade/DefaultAuditor.java
+++ b/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/facade/DefaultAuditor.java
@@ -122,6 +122,12 @@ public boolean shouldLogFailedBatchSummary()
return logTimingStrategy.shouldLogFailedBatchSummary();
}
+ @Override
+ public boolean shouldLogPrepareStatements()
+ {
+ return filter.shouldLogPrepareStatements();
+ }
+
@Override
public void setLogTimingStrategy(LogTimingStrategy logTimingStrategy)
{
diff --git a/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/filter/AuditFilter.java b/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/filter/AuditFilter.java
index e35ef38b..65630d7c 100644
--- a/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/filter/AuditFilter.java
+++ b/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/filter/AuditFilter.java
@@ -37,4 +37,6 @@ public interface AuditFilter {
* For example, use this method to create any required keyspaces/tables.
*/
void setup();
+
+ boolean shouldLogPrepareStatements();
}
diff --git a/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/filter/DefaultAuditFilter.java b/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/filter/DefaultAuditFilter.java
index 3cc2cf3e..2b66b805 100644
--- a/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/filter/DefaultAuditFilter.java
+++ b/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/filter/DefaultAuditFilter.java
@@ -32,4 +32,10 @@ public void setup()
{
// Intentionally left empty
}
+
+ @Override
+ public boolean shouldLogPrepareStatements()
+ {
+ return true;
+ }
}
diff --git a/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/filter/role/RoleAuditFilter.java b/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/filter/role/RoleAuditFilter.java
index 3903229d..26f4a376 100644
--- a/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/filter/role/RoleAuditFilter.java
+++ b/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/filter/role/RoleAuditFilter.java
@@ -74,6 +74,9 @@ public void setup()
whitelistDataAccess.setup();
}
+ @Override
+ public boolean shouldLogPrepareStatements() { return true; }
+
/**
* Returns true if the supplied log entry's role or any other role granted to it (directly or indirectly) is
* white-listed for the log entry's specified operations and resource.
diff --git a/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/filter/yaml/YamlAuditFilter.java b/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/filter/yaml/YamlAuditFilter.java
index b23686d2..15742522 100644
--- a/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/filter/yaml/YamlAuditFilter.java
+++ b/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/filter/yaml/YamlAuditFilter.java
@@ -53,4 +53,10 @@ public void setup()
{
whitelist = auditConfig.getYamlWhitelist();
}
+
+ @Override
+ public boolean shouldLogPrepareStatements()
+ {
+ return !auditConfig.isSuppressPrepareStatements();
+ }
}
diff --git a/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/filter/yamlandrole/YamlAndRoleAuditFilter.java b/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/filter/yamlandrole/YamlAndRoleAuditFilter.java
index 682c84d1..e2442cce 100644
--- a/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/filter/yamlandrole/YamlAndRoleAuditFilter.java
+++ b/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/filter/yamlandrole/YamlAndRoleAuditFilter.java
@@ -57,4 +57,10 @@ public void setup()
yamlFilter.setup();
roleFilter.setup();
}
+
+ @Override
+ public boolean shouldLogPrepareStatements()
+ {
+ return yamlFilter.shouldLogPrepareStatements();
+ }
}
diff --git a/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/handler/AuditQueryHandler.java b/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/handler/AuditQueryHandler.java
index 26e2cb18..1ec4e440 100644
--- a/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/handler/AuditQueryHandler.java
+++ b/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/handler/AuditQueryHandler.java
@@ -203,9 +203,21 @@ private ResultMessage processBatchWithAudit(BatchStatement statement, List customPayload)
throws RequestValidationException
{
- return wrappedQueryHandler.prepare(query, state, customPayload);
- }
+ long timestamp = System.currentTimeMillis();
+ auditAdapter.auditPrepare(query, state.getClientState(), Status.ATTEMPT, timestamp);
+ ResultMessage.Prepared preparedStatement;
+ try
+ {
+ preparedStatement = wrappedQueryHandler.prepare(query, state, customPayload);
+ }
+ catch (RuntimeException e)
+ {
+ auditAdapter.auditPrepare(query, state.getClientState(), Status.FAILED, timestamp);
+ throw e;
+ }
+ return preparedStatement;
+ }
@Override
public ParsedStatement.Prepared getPrepared(MD5Digest id)
{
diff --git a/ecaudit/src/test/java/com/ericsson/bss/cassandra/ecaudit/config/TestAuditYamlConfigurationLoader.java b/ecaudit/src/test/java/com/ericsson/bss/cassandra/ecaudit/config/TestAuditYamlConfigurationLoader.java
index 22e7950a..ab22d8b2 100644
--- a/ecaudit/src/test/java/com/ericsson/bss/cassandra/ecaudit/config/TestAuditYamlConfigurationLoader.java
+++ b/ecaudit/src/test/java/com/ericsson/bss/cassandra/ecaudit/config/TestAuditYamlConfigurationLoader.java
@@ -97,6 +97,7 @@ public void testDefaultConfiguration()
assertThat(config.getWhitelistCacheValidity()).isEqualTo(DatabaseDescriptor.getRolesValidity());
assertThat(config.getWhitelistCacheUpdateInterval()).isEqualTo(DatabaseDescriptor.getRolesUpdateInterval());
assertThat(config.getWhitelistCacheMaxEntries()).isEqualTo(DatabaseDescriptor.getRolesCacheMaxEntries() * 10);
+ assertThat(config.isSuppressPrepareStatements()).isEqualTo(true);
}
@Test
@@ -122,6 +123,7 @@ public void testCustomConfiguration()
assertThat(config.getWhitelistCacheValidity()).isEqualTo(42);
assertThat(config.getWhitelistCacheUpdateInterval()).isEqualTo(41);
assertThat(config.getWhitelistCacheMaxEntries()).isEqualTo(40);
+ assertThat(config.isSuppressPrepareStatements()).isEqualTo(false);
}
@Test
diff --git a/ecaudit/src/test/java/com/ericsson/bss/cassandra/ecaudit/handler/TestAuditQueryHandler.java b/ecaudit/src/test/java/com/ericsson/bss/cassandra/ecaudit/handler/TestAuditQueryHandler.java
index 04d62cd3..4e0b4245 100644
--- a/ecaudit/src/test/java/com/ericsson/bss/cassandra/ecaudit/handler/TestAuditQueryHandler.java
+++ b/ecaudit/src/test/java/com/ericsson/bss/cassandra/ecaudit/handler/TestAuditQueryHandler.java
@@ -148,6 +148,7 @@ public void testPrepareAndGetPrepared()
verify(mockHandler, times(1)).prepare(eq(query), eq(mockQueryState), eq(customPayload));
verify(mockHandler, times(1)).getPrepared(eq(statementId));
+ verify(mockAdapter, times(1)).auditPrepare(eq(query), eq(mockClientState), eq(Status.ATTEMPT), longThat(isCloseToNow()));
}
@Test
@@ -169,6 +170,7 @@ public void testPrepareAndGetPreparedWhenPreloaded()
verify(mockHandler, times(1)).prepare(eq(query), eq(mockQueryState), eq(customPayload));
verify(mockHandler, times(1)).getPrepared(eq(statementId));
+ verify(mockAdapter, times(1)).auditPrepare(eq(query), eq(mockClientState), eq(Status.ATTEMPT), longThat(isCloseToNow()));
}
@Test
diff --git a/ecaudit/src/test/resources/mock_configuration.yaml b/ecaudit/src/test/resources/mock_configuration.yaml
index 248f54a1..5f69ef10 100644
--- a/ecaudit/src/test/resources/mock_configuration.yaml
+++ b/ecaudit/src/test/resources/mock_configuration.yaml
@@ -27,3 +27,4 @@ bound_value_suppressor: SuppressBlobs
whitelist_cache_validity_in_ms: 42
whitelist_cache_update_interval_in_ms: 41
whitelist_cache_max_entries: 40
+suppress_prepare_statements: false
diff --git a/integration-test-query-logger/src/test/java/com/ericsson/bss/cassandra/ecaudit/integration/querylogger/ITQueryLogger.java b/integration-test-query-logger/src/test/java/com/ericsson/bss/cassandra/ecaudit/integration/querylogger/ITQueryLogger.java
index 747ca356..95fd39da 100644
--- a/integration-test-query-logger/src/test/java/com/ericsson/bss/cassandra/ecaudit/integration/querylogger/ITQueryLogger.java
+++ b/integration-test-query-logger/src/test/java/com/ericsson/bss/cassandra/ecaudit/integration/querylogger/ITQueryLogger.java
@@ -15,6 +15,7 @@
*/
package com.ericsson.bss.cassandra.ecaudit.integration.querylogger;
+import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
@@ -30,8 +31,10 @@
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import com.datastax.driver.core.Cluster;
+import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.SimpleStatement;
+import com.datastax.driver.core.exceptions.InvalidQueryException;
import com.datastax.driver.core.exceptions.UnauthorizedException;
import com.ericsson.bss.cassandra.ecaudit.logger.Slf4jAuditLogger;
import com.ericsson.bss.cassandra.ecaudit.test.daemon.CassandraDaemonForAuditTest;
@@ -43,6 +46,7 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -88,6 +92,29 @@ public static void afterClass()
session.close();
cluster.close();
}
+
+ @Test
+ public void testPrepareStatement()
+ {
+ givenTable("school", "students");
+ reset(mockAuditAppender);
+
+ PreparedStatement prepared = session.prepare("INSERT INTO school.students (key, value) VALUES (?, ?)");
+ session.execute(prepared.bind(42, "Kalle"));
+ assertThat(getLogEntries()).containsOnly( "client:'127.0.0.1'|user:'anonymous'|status:'ATTEMPT'|operation:'Prepared: INSERT INTO school.students (key, value) VALUES (?, ?)'",
+ "client:'127.0.0.1'|user:'anonymous'|status:'ATTEMPT'|operation:'INSERT INTO school.students (key, value) VALUES (?, ?)[42, 'Kalle']'");
+ }
+
+ @Test
+ public void testFailedPrepareStatement()
+ {
+ givenTable("school", "students");
+ reset(mockAuditAppender);
+
+ assertThatExceptionOfType(InvalidQueryException.class).isThrownBy(() -> session.prepare("INSERT INTO school.invalidestudents (key, value) VALUES (?, ?)"));
+ assertThat(getLogEntries()).containsOnly( "client:'127.0.0.1'|user:'anonymous'|status:'ATTEMPT'|operation:'Prepared: INSERT INTO school.invalidestudents (key, value) VALUES (?, ?)'",
+ "client:'127.0.0.1'|user:'anonymous'|status:'FAILED'|operation:'Prepared: INSERT INTO school.invalidestudents (key, value) VALUES (?, ?)'");
+ }
@Test
public void testBasicStatement()
diff --git a/integration-test-query-logger/src/test/java/com/ericsson/bss/cassandra/ecaudit/integration/querylogger/PrepareAuditQueryLogger.java b/integration-test-query-logger/src/test/java/com/ericsson/bss/cassandra/ecaudit/integration/querylogger/PrepareAuditQueryLogger.java
new file mode 100644
index 00000000..1c852d8e
--- /dev/null
+++ b/integration-test-query-logger/src/test/java/com/ericsson/bss/cassandra/ecaudit/integration/querylogger/PrepareAuditQueryLogger.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2020 Telefonaktiebolaget LM Ericsson
+ *
+ * Licensed 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 com.ericsson.bss.cassandra.ecaudit.integration.querylogger;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.slf4j.LoggerFactory;
+
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.Appender;
+import com.datastax.driver.core.Cluster;
+import com.datastax.driver.core.Session;
+import com.datastax.driver.core.PreparedStatement;
+import com.datastax.driver.core.exceptions.InvalidQueryException;
+import com.ericsson.bss.cassandra.ecaudit.logger.Slf4jAuditLogger;
+import com.ericsson.bss.cassandra.ecaudit.test.daemon.CassandraDaemonForAuditTest;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+@RunWith(MockitoJUnitRunner.class)
+public class PrepareAuditQueryLogger
+{
+ private static Cluster cluster;
+ private static Session session;
+
+ @Captor
+ private ArgumentCaptor loggingEventCaptor;
+
+ @Mock
+ private Appender mockAuditAppender;
+
+ @BeforeClass
+ public static void beforeClass() throws Exception
+ {
+ CassandraDaemonForAuditTest cdt = CassandraDaemonForAuditTest.getInstance();
+ cluster = cdt.createCluster();
+ session = cluster.connect();
+ }
+
+ @Before
+ public void before()
+ {
+ LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
+ loggerContext.getLogger(Slf4jAuditLogger.AUDIT_LOGGER_NAME).addAppender(mockAuditAppender);
+ }
+
+ @After
+ public void after()
+ {
+ verifyNoMoreInteractions(mockAuditAppender);
+ LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
+ loggerContext.getLogger(Slf4jAuditLogger.AUDIT_LOGGER_NAME).detachAppender(mockAuditAppender);
+ }
+
+ @AfterClass
+ public static void afterClass()
+ {
+ session.close();
+ }
+
+ @Test
+ public void testPrepareStatement()
+ {
+ givenTable("school", "students");
+ reset(mockAuditAppender);
+
+ PreparedStatement prepared = session.prepare("INSERT INTO school.students (key, value) VALUES (?, ?)");
+ session.execute(prepared.bind(42, "Kalle"));
+ assertThat(getLogEntries()).containsOnly( "client:'127.0.0.1'|user:'anonymous'|status:'ATTEMPT'|operation:'Prepared: INSERT INTO school.students (key, value) VALUES (?, ?)'",
+ "client:'127.0.0.1'|user:'anonymous'|status:'ATTEMPT'|operation:'INSERT INTO school.students (key, value) VALUES (?, ?)[42, 'Kalle']'");
+ }
+
+ @Test
+ public void testFailedPrepareStatement()
+ {
+ givenTable("school", "students");
+ reset(mockAuditAppender);
+
+ assertThatExceptionOfType(InvalidQueryException.class).isThrownBy(() -> session.prepare("INSERT INTO school.invalidestudents (key, value) VALUES (?, ?)"));
+
+ assertThat(getLogEntries()).containsOnly( "client:'127.0.0.1'|user:'anonymous'|status:'ATTEMPT'|operation:'Prepared: INSERT INTO school.invalidestudents (key, value) VALUES (?, ?)'",
+ "client:'127.0.0.1'|user:'anonymous'|status:'FAILED'|operation:'Prepared: INSERT INTO school.invalidestudents (key, value) VALUES (?, ?)'");
+ }
+
+ private void givenTable(String keyspace, String table)
+ {
+ session.execute("CREATE KEYSPACE IF NOT EXISTS " + keyspace + " WITH REPLICATION = {'class' : 'SimpleStrategy', 'replication_factor' : 1} AND DURABLE_WRITES = false");
+ session.execute("CREATE TABLE IF NOT EXISTS " + keyspace + "." + table + " (key int PRIMARY KEY, value text)");
+ }
+
+ private List getLogEntries()
+ {
+ verify(mockAuditAppender, atLeast(1)).doAppend(loggingEventCaptor.capture());
+ return loggingEventCaptor.getAllValues()
+ .stream()
+ .map(ILoggingEvent::getFormattedMessage)
+ .collect(Collectors.toList());
+ }
+}
diff --git a/integration-test-query-logger/src/test/resources/integration_audit.yaml b/integration-test-query-logger/src/test/resources/integration_audit.yaml
index cc45407d..010bd073 100644
--- a/integration-test-query-logger/src/test/resources/integration_audit.yaml
+++ b/integration-test-query-logger/src/test/resources/integration_audit.yaml
@@ -15,3 +15,5 @@
#
whitelist_cache_validity_in_ms: 0
+
+suppress_prepare_statements: false