From 0335c7d2771de4cc5f0d3b99b9353dcd94942c74 Mon Sep 17 00:00:00 2001 From: Paul Chandler Date: Wed, 25 Oct 2023 11:54:37 +0100 Subject: [PATCH] Audit Prepare statements (#241) --- CHANGES.md | 1 + conf/audit.yaml | 5 + .../bss/cassandra/ecaudit/AuditAdapter.java | 26 ++++ .../cassandra/ecaudit/config/AuditConfig.java | 6 + .../ecaudit/config/AuditYamlConfig.java | 8 ++ .../suppressor/PrepareAuditOperation.java | 44 ++++++ .../bss/cassandra/ecaudit/facade/Auditor.java | 2 + .../ecaudit/facade/DefaultAuditor.java | 6 + .../cassandra/ecaudit/filter/AuditFilter.java | 2 + .../ecaudit/filter/DefaultAuditFilter.java | 6 + .../ecaudit/filter/role/RoleAuditFilter.java | 3 + .../ecaudit/filter/yaml/YamlAuditFilter.java | 6 + .../yamlandrole/YamlAndRoleAuditFilter.java | 5 + .../ecaudit/handler/AuditQueryHandler.java | 15 ++- .../TestAuditYamlConfigurationLoader.java | 2 + .../handler/TestAuditQueryHandler.java | 1 + .../test/resources/mock_configuration.yaml | 1 + .../querylogger/ITQueryLogger.java | 28 ++++ .../querylogger/PrepareAuditQueryLogger.java | 127 ++++++++++++++++++ .../src/test/resources/integration_audit.yaml | 2 + .../ecaudit/test/mode/ClientInitializer.java | 6 +- 21 files changed, 298 insertions(+), 4 deletions(-) create mode 100644 ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/entry/suppressor/PrepareAuditOperation.java create mode 100644 integration-test-query-logger/src/test/java/com/ericsson/bss/cassandra/ecaudit/integration/querylogger/PrepareAuditQueryLogger.java diff --git a/CHANGES.md b/CHANGES.md index 764f4a85..c8341107 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,6 +9,7 @@ ## Version 2.12.0 * Test with java11 (only flavor ecaudit_c4.0) - #214 +* Audit Prepare statements ## Version 2.11.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 b80c1946..0dbe1b23 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.getJustBroadcastAddress()) + .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 74b6d2da..964db060 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 @@ -132,6 +132,12 @@ public void setWhitelistCacheActiveUpdate(boolean whitelistCacheActiveUpdate) yamlConfig.setWhitelistCacheActiveUpdate(whitelistCacheActiveUpdate); } + 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 5d60848d..35a4a6e9 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 @@ -53,6 +53,7 @@ public final class AuditYamlConfig public Integer whitelist_cache_update_interval_in_ms; public Integer whitelist_cache_max_entries; public Boolean whitelist_cache_active_update; + public Boolean suppress_prepare_statements; static AuditYamlConfig createWithoutFile() { @@ -170,4 +171,11 @@ public void setWhitelistCacheActiveUpdate(Boolean whitelistCacheActiveUpdate) { this.whitelist_cache_active_update = whitelistCacheActiveUpdate; } + + 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..62a9d8ae 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,9 @@ 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 0ca679f1..7497530d 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 @@ -199,7 +199,20 @@ private ResultMessage processBatchWithAudit(BatchStatement statement, List customPayload) throws RequestValidationException { - return wrappedQueryHandler.prepare(query, state, customPayload); + long timestamp = System.currentTimeMillis(); + auditAdapter.auditPrepare(query, state, Status.ATTEMPT, timestamp); + ResultMessage.Prepared preparedStatement; + try + { + preparedStatement = wrappedQueryHandler.prepare(query, state, customPayload); + } + catch (RuntimeException e) + { + auditAdapter.auditPrepare(query, state, Status.FAILED, timestamp); + throw e; + } + + return preparedStatement; } @Override 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 47b08ab9..8fa60c1d 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 @@ -146,6 +146,7 @@ public void testPrepareAndGetPrepared() verify(mockHandler, times(1)).prepare(eq(query), eq(mockClientState), 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 fff0bdac..6aef68ef 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,6 +31,8 @@ import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.Appender; import com.datastax.oss.driver.api.core.CqlSession; +import com.datastax.oss.driver.api.core.cql.PreparedStatement; +import com.datastax.oss.driver.api.core.servererrors.InvalidQueryException; import com.datastax.oss.driver.api.core.servererrors.UnauthorizedException; import com.ericsson.bss.cassandra.ecaudit.logger.Slf4jAuditLogger; import com.ericsson.bss.cassandra.ecaudit.test.daemon.CassandraDaemonForAuditTest; @@ -41,6 +44,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; @@ -95,6 +99,30 @@ public void testBasicStatement() assertThat(getLogEntries()).containsOnly("client:'127.0.0.1'|user:'anonymous'|status:'ATTEMPT'|operation:'INSERT INTO school.students (key, value) VALUES (42, 'Kalle')'"); } + @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 testGrantFails() { 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..500222aa --- /dev/null +++ b/integration-test-query-logger/src/test/java/com/ericsson/bss/cassandra/ecaudit/integration/querylogger/PrepareAuditQueryLogger.java @@ -0,0 +1,127 @@ +/* + * 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.oss.driver.api.core.CqlSession; +import com.datastax.oss.driver.api.core.cql.PreparedStatement; +import com.datastax.oss.driver.api.core.servererrors.InvalidQueryException; +import com.datastax.oss.driver.api.core.servererrors.UnauthorizedException; +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 CqlSession session; + + @Captor + private ArgumentCaptor loggingEventCaptor; + + @Mock + private Appender mockAuditAppender; + + @BeforeClass + public static void beforeClass() throws Exception + { + CassandraDaemonForAuditTest cdt = CassandraDaemonForAuditTest.getInstance(); + session = cdt.createSession(); + } + + @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 diff --git a/test-utils/src/main/java/com/ericsson/bss/cassandra/ecaudit/test/mode/ClientInitializer.java b/test-utils/src/main/java/com/ericsson/bss/cassandra/ecaudit/test/mode/ClientInitializer.java index dbd5d1cb..a2bfd0b8 100644 --- a/test-utils/src/main/java/com/ericsson/bss/cassandra/ecaudit/test/mode/ClientInitializer.java +++ b/test-utils/src/main/java/com/ericsson/bss/cassandra/ecaudit/test/mode/ClientInitializer.java @@ -23,8 +23,8 @@ import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.dht.RandomPartitioner; +import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; public final class ClientInitializer { @@ -39,9 +39,9 @@ public static void beforeClass() { DatabaseDescriptor.clientInitialization(true); DatabaseDescriptor.setAuthenticator(mock(IAuthenticator.class)); - when(authorizerMock.bulkLoader()).thenReturn(Collections::emptyMap); + lenient().when(authorizerMock.bulkLoader()).thenReturn(Collections::emptyMap); DatabaseDescriptor.setAuthorizer(authorizerMock); - when(networkAuthorizerMock.bulkLoader()).thenReturn(Collections::emptyMap); + lenient().when(networkAuthorizerMock.bulkLoader()).thenReturn(Collections::emptyMap); DatabaseDescriptor.setNetworkAuthorizer(networkAuthorizerMock); DatabaseDescriptor.setPartitionerUnsafe(RandomPartitioner.instance); }