From b0a9d24d01b09b628ab4731decf70437af319e48 Mon Sep 17 00:00:00 2001 From: maoling Date: Fri, 5 Sep 2025 23:13:00 +0800 Subject: [PATCH] CASSANDRA-20892: Don't allow JMX operations for denylist when denylist is disabled --- .../operating/denylisting_partitions.adoc | 7 ++++++ .../cassandra/service/StorageProxy.java | 16 ++++++++++++ .../cassandra/service/StorageProxyMBean.java | 1 + .../service/PartitionDenylistTest.java | 25 +++++++++++++++++++ 4 files changed, 49 insertions(+) diff --git a/doc/modules/cassandra/pages/managing/operating/denylisting_partitions.adoc b/doc/modules/cassandra/pages/managing/operating/denylisting_partitions.adoc index a81dc8009c..a2f2e8d991 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 5c1ebc9ed5..9e6b129cfa 100644 --- a/src/java/org/apache/cassandra/service/StorageProxy.java +++ b/src/java/org/apache/cassandra/service/StorageProxy.java @@ -3658,9 +3658,16 @@ public class StorageProxy implements StorageProxyMBean @Override public void loadPartitionDenylist() { + ensurePartitionDenylistEnabled(); partitionDenylist.load(); } + @Override + public boolean getPartitionDenylistEnabled() + { + return DatabaseDescriptor.getPartitionDenylistEnabled(); + } + @Override public int getPartitionDenylistLoadAttempts() { @@ -3719,6 +3726,7 @@ public class StorageProxy implements StorageProxyMBean @Override public boolean denylistKey(String keyspace, String table, String partitionKeyAsString) { + ensurePartitionDenylistEnabled(); if (!Schema.instance.getKeyspaces().contains(keyspace)) return false; @@ -3740,6 +3748,7 @@ public class StorageProxy implements StorageProxyMBean @Override public boolean removeDenylistKey(String keyspace, String table, String partitionKeyAsString) { + ensurePartitionDenylistEnabled(); if (!Schema.instance.getKeyspaces().contains(keyspace)) return false; @@ -3756,6 +3765,7 @@ public class StorageProxy implements StorageProxyMBean */ public boolean isKeyDenylisted(String keyspace, String table, String partitionKeyAsString) { + ensurePartitionDenylistEnabled(); if (!Schema.instance.getKeyspaces().contains(keyspace)) return false; @@ -3767,6 +3777,12 @@ public class StorageProxy implements StorageProxyMBean return !partitionDenylist.isKeyPermitted(keyspace, table, bytes); } + private void ensurePartitionDenylistEnabled() + { + if (!getPartitionDenylistEnabled()) + throw new RuntimeException("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 1c4887f1e1..f174cb0cbd 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 e17dc2af1b..aa9da90aee 100644 --- a/test/unit/org/apache/cassandra/service/PartitionDenylistTest.java +++ b/test/unit/org/apache/cassandra/service/PartitionDenylistTest.java @@ -448,6 +448,31 @@ public class PartitionDenylistTest 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(RuntimeException.class) + .hasMessageContaining("Denylisting partitions is disabled"); + assertThatThrownBy(() -> refreshList()) + .isInstanceOf(RuntimeException.class) + .hasMessageContaining("Denylisting partitions is disabled"); + assertThatThrownBy(() -> removeDenylist(ks_cql, "table1", "foo:bar")) + .isInstanceOf(RuntimeException.class) + .hasMessageContaining("Denylisting partitions is disabled"); + assertThatThrownBy(() -> StorageProxy.instance.isKeyDenylisted(ks_cql, "table1", "bbb:ccc")) + .isInstanceOf(RuntimeException.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);