diff --git a/doc/modules/cassandra/pages/managing/operating/denylisting_partitions.adoc b/doc/modules/cassandra/pages/managing/operating/denylisting_partitions.adoc index a81dc8009c35..a2f2e8d99158 100644 --- a/doc/modules/cassandra/pages/managing/operating/denylisting_partitions.adoc +++ b/doc/modules/cassandra/pages/managing/operating/denylisting_partitions.adoc @@ -13,6 +13,13 @@ partition key, the query will be immediately rejected with an `InvalidQueryExcep == How to denylist a partition key +Denylisting partitions is disabled by default. To use this feature, enable it in the `cassandra.yaml` file. + +.... +partition_denylist_enabled: true +.... + + The ``system_distributed.denylisted_partitions`` table can be used to denylist partitions. There are a couple of ways to interact with and mutate this data. First: directly via CQL by inserting a record with the following details: diff --git a/src/java/org/apache/cassandra/service/StorageProxy.java b/src/java/org/apache/cassandra/service/StorageProxy.java index 40b3d1fe828e..b051a4987294 100644 --- a/src/java/org/apache/cassandra/service/StorageProxy.java +++ b/src/java/org/apache/cassandra/service/StorageProxy.java @@ -3637,9 +3637,16 @@ public void initialLoadPartitionDenylist() @Override public void loadPartitionDenylist() { + checkPartitionDenylistEnabled(); partitionDenylist.load(); } + @Override + public boolean getPartitionDenylistEnabled() + { + return DatabaseDescriptor.getPartitionDenylistEnabled(); + } + @Override public int getPartitionDenylistLoadAttempts() { @@ -3698,6 +3705,7 @@ public void setDenylistMaxKeysTotal(int value) @Override public boolean denylistKey(String keyspace, String table, String partitionKeyAsString) { + checkPartitionDenylistEnabled(); if (!Schema.instance.getKeyspaces().contains(keyspace)) return false; @@ -3719,6 +3727,7 @@ public boolean denylistKey(String keyspace, String table, String partitionKeyAsS @Override public boolean removeDenylistKey(String keyspace, String table, String partitionKeyAsString) { + checkPartitionDenylistEnabled(); if (!Schema.instance.getKeyspaces().contains(keyspace)) return false; @@ -3735,6 +3744,7 @@ public boolean removeDenylistKey(String keyspace, String table, String partition */ public boolean isKeyDenylisted(String keyspace, String table, String partitionKeyAsString) { + checkPartitionDenylistEnabled(); if (!Schema.instance.getKeyspaces().contains(keyspace)) return false; @@ -3746,6 +3756,12 @@ public boolean isKeyDenylisted(String keyspace, String table, String partitionKe return !partitionDenylist.isKeyPermitted(keyspace, table, bytes); } + private void checkPartitionDenylistEnabled() { + if (!getPartitionDenylistEnabled()) { + throw new UnsupportedOperationException("Denylisting partitions is disabled"); + } + } + @Override public void logBlockingReadRepairAttemptsForNSeconds(int seconds) { diff --git a/src/java/org/apache/cassandra/service/StorageProxyMBean.java b/src/java/org/apache/cassandra/service/StorageProxyMBean.java index 1c4887f1e177..f174cb0cbd69 100644 --- a/src/java/org/apache/cassandra/service/StorageProxyMBean.java +++ b/src/java/org/apache/cassandra/service/StorageProxyMBean.java @@ -70,6 +70,7 @@ public interface StorageProxyMBean public int getOtcBacklogExpirationInterval(); public void loadPartitionDenylist(); + public boolean getPartitionDenylistEnabled(); public int getPartitionDenylistLoadAttempts(); public int getPartitionDenylistLoadSuccesses(); public void setEnablePartitionDenylist(boolean enabled); diff --git a/test/unit/org/apache/cassandra/service/PartitionDenylistTest.java b/test/unit/org/apache/cassandra/service/PartitionDenylistTest.java index e17dc2af1b54..358774fc65d3 100644 --- a/test/unit/org/apache/cassandra/service/PartitionDenylistTest.java +++ b/test/unit/org/apache/cassandra/service/PartitionDenylistTest.java @@ -448,6 +448,30 @@ public void testGlobalLimitRespected() confirmAllowed("table3", Integer.toString(i), Integer.toString(i)); } + @Test + public void testJmxThrowExcepitonWhenDenylistDisable() { + // Disable denylisting + DatabaseDescriptor.setPartitionDenylistEnabled(false); + // read and write work well + process("SELECT * FROM " + ks_cql + ".table1 WHERE keyone='aaa' and keytwo='bbb'", ConsistencyLevel.ONE); + process("SELECT * FROM " + ks_cql + ".table1 WHERE keyone='bbb' and keytwo='ccc'", ConsistencyLevel.ONE); + process("INSERT INTO " + ks_cql + ".table1 (keyone, keytwo, qux, quz, foo) VALUES ('eee', 'fff', 'ccc', 'ddd', 'v')", ConsistencyLevel.ONE); + process("INSERT INTO " + ks_cql + ".table1 (keyone, keytwo, qux, quz, foo) VALUES ('bbb', 'ccc', 'eee', 'fff', 'w')", ConsistencyLevel.ONE); + // when denylisting is disabled, JMX operation will throw excepiton + assertThatThrownBy(() -> denylist("table1", "foo:bar")) + .isInstanceOf(UnsupportedOperationException.class) + .hasMessageContaining("Denylisting partitions is disabled"); + assertThatThrownBy(() -> refreshList()) + .isInstanceOf(UnsupportedOperationException.class) + .hasMessageContaining("Denylisting partitions is disabled"); + assertThatThrownBy(() -> removeDenylist(ks_cql, "table1", "foo:bar")) + .isInstanceOf(UnsupportedOperationException.class) + .hasMessageContaining("Denylisting partitions is disabled"); + assertThatThrownBy(() -> StorageProxy.instance.isKeyDenylisted(ks_cql, "table1", "bbb:ccc")) + .isInstanceOf(UnsupportedOperationException.class) + .hasMessageContaining("Denylisting partitions is disabled"); + } + private void confirmDenied(String table, String keyOne, String keyTwo) { String query = String.format("SELECT * FROM " + ks_cql + "." + table + " WHERE keyone='%s' and keytwo='%s'", keyOne, keyTwo);