From f09591a2852f9011249fec028d8021b3a6534b8d Mon Sep 17 00:00:00 2001 From: Paul Chandler Date: Thu, 12 Oct 2023 11:30:30 +0100 Subject: [PATCH 1/4] \#226 Add auditing for prepare statements --- .gitignore | 1 + .idea/misc.xml | 11 -- CHANGES.md | 1 + conf/audit.yaml | 5 + .../bss/cassandra/ecaudit/AuditAdapter.java | 24 ++++ .../cassandra/ecaudit/config/AuditConfig.java | 5 + .../ecaudit/config/AuditYamlConfig.java | 9 +- .../suppressor/PrepareAuditOperation.java | 43 ++++++ .../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 | 4 + .../ecaudit/filter/yaml/YamlAuditFilter.java | 6 + .../yamlandrole/YamlAndRoleAuditFilter.java | 6 + .../ecaudit/handler/AuditQueryHandler.java | 16 ++- .../TestAuditYamlConfigurationLoader.java | 2 + .../handler/TestAuditQueryHandler.java | 3 + .../test/resources/mock_configuration.yaml | 1 + .../querylogger/ITQueryLogger.java | 27 ++++ .../querylogger/PrepareAuditQueryLogger.java | 130 ++++++++++++++++++ .../src/test/resources/integration_audit.yaml | 2 + 22 files changed, 298 insertions(+), 14 deletions(-) delete mode 100644 .idea/misc.xml 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/.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 deleted file mode 100644 index 9896aeb3..00000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - \ 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..a6beb2db 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; @@ -107,7 +108,30 @@ public void auditRegular(String operation, ClientState state, Status status, lon auditor.audit(logEntry); } } + /** + * 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..cb378b6f 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 @@ -119,6 +119,11 @@ public void setWhitelistCacheMaxEntries(int whitelistCacheMaxEntries) loadConfigIfNeeded(); yamlConfig.setWhitelistCacheMaxEntries(whitelistCacheMaxEntries); } + public boolean isSuppressPrepareStatements() + { + loadConfigIfNeeded(); + return yamlConfig.isSuppressPrepareStatements(); + } private synchronized void loadConfigIfNeeded() { 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..642d7e45 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,10 @@ 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..0a7e50d4 --- /dev/null +++ b/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/entry/suppressor/PrepareAuditOperation.java @@ -0,0 +1,43 @@ +/* + * 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..e491e7d2 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,10 @@ 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..494a53f9 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..b8195fe4 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,8 @@ 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..3d0e57c1 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,11 @@ 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 +47,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 +93,28 @@ 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..29669d15 --- /dev/null +++ b/integration-test-query-logger/src/test/java/com/ericsson/bss/cassandra/ecaudit/integration/querylogger/PrepareAuditQueryLogger.java @@ -0,0 +1,130 @@ +/* + * 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 From 9b32d6de56cd393114a96a702ca184d7c43c155a Mon Sep 17 00:00:00 2001 From: Paul Chandler Date: Mon, 23 Oct 2023 15:15:06 +0100 Subject: [PATCH 2/4] #226 fix formatting issues --- .../com/ericsson/bss/cassandra/ecaudit/AuditAdapter.java | 2 ++ .../ericsson/bss/cassandra/ecaudit/config/AuditConfig.java | 1 + .../bss/cassandra/ecaudit/config/AuditYamlConfig.java | 1 + .../ecaudit/integration/querylogger/ITQueryLogger.java | 2 +- .../integration/querylogger/PrepareAuditQueryLogger.java | 5 ++--- 5 files changed, 7 insertions(+), 4 deletions(-) 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 a6beb2db..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 @@ -108,6 +108,7 @@ public void auditRegular(String operation, ClientState state, Status status, lon auditor.audit(logEntry); } } + /** * Audit a regular CQL statement. * @@ -132,6 +133,7 @@ public void auditPrepare(String operation, ClientState state, Status status, lon 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 cb378b6f..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 @@ -119,6 +119,7 @@ public void setWhitelistCacheMaxEntries(int whitelistCacheMaxEntries) loadConfigIfNeeded(); yamlConfig.setWhitelistCacheMaxEntries(whitelistCacheMaxEntries); } + public boolean isSuppressPrepareStatements() { loadConfigIfNeeded(); 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 642d7e45..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 @@ -158,6 +158,7 @@ public void setWhitelistCacheMaxEntries(Integer whitelistCacheMaxEntries) { this.whitelist_cache_max_entries = whitelistCacheMaxEntries; } + public Boolean isSuppressPrepareStatements() { return suppress_prepare_statements == null 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 3d0e57c1..dc04985b 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 @@ -103,8 +103,8 @@ public void testPrepareStatement() 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() { 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 index 29669d15..36ba16b8 100644 --- 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 @@ -32,7 +32,6 @@ 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; @@ -100,6 +99,7 @@ public void testPrepareStatement() 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() { @@ -111,8 +111,7 @@ public void testFailedPrepareStatement() 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"); From e4295824744750a83e4561757c6c734f10628b06 Mon Sep 17 00:00:00 2001 From: Paul Chandler Date: Tue, 24 Oct 2023 11:00:47 +0100 Subject: [PATCH 3/4] #226 fix formatting issues --- .idea/misc.xml | 10 ++++++++++ .../entry/suppressor/PrepareAuditOperation.java | 1 + .../cassandra/ecaudit/filter/role/RoleAuditFilter.java | 1 - .../filter/yamlandrole/YamlAndRoleAuditFilter.java | 2 +- .../ecaudit/handler/TestAuditQueryHandler.java | 1 - .../ecaudit/integration/querylogger/ITQueryLogger.java | 2 +- .../querylogger/PrepareAuditQueryLogger.java | 2 +- 7 files changed, 14 insertions(+), 5 deletions(-) create mode 100644 .idea/misc.xml diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..ffcd0f77 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file 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 index 0a7e50d4..692ca018 100644 --- 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 @@ -35,6 +35,7 @@ public String getOperationString() { return operationString; } + @Override public String getNakedOperationString() { 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 e491e7d2..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 @@ -77,7 +77,6 @@ public void 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/yamlandrole/YamlAndRoleAuditFilter.java b/ecaudit/src/main/java/com/ericsson/bss/cassandra/ecaudit/filter/yamlandrole/YamlAndRoleAuditFilter.java index 494a53f9..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,10 +57,10 @@ public void setup() yamlFilter.setup(); roleFilter.setup(); } + @Override public boolean shouldLogPrepareStatements() { return yamlFilter.shouldLogPrepareStatements(); } - } 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 b8195fe4..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 @@ -171,7 +171,6 @@ 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/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 dc04985b..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 @@ -32,7 +32,6 @@ 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; @@ -93,6 +92,7 @@ public static void afterClass() session.close(); cluster.close(); } + @Test public void testPrepareStatement() { 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 index 36ba16b8..1c852d8e 100644 --- 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 @@ -111,7 +111,7 @@ public void testFailedPrepareStatement() 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"); From d71732b3baf9a594f3fab488fc49c5841c825e74 Mon Sep 17 00:00:00 2001 From: Paul Chandler Date: Wed, 25 Oct 2023 09:54:17 +0100 Subject: [PATCH 4/4] #226 fix formatting issues --- .idea/misc.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index ffcd0f77..86b97c7b 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,3 +1,4 @@ + - \ No newline at end of file +