This is an automated email from the ASF dual-hosted git repository.

maedhroz pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 28eea6e  Runtime-configurable YAML option to prohibit USE statements
28eea6e is described below

commit 28eea6e8cd4055c8d21f872c72f8bd14fd2467ba
Author: Caleb Rackliffe <[email protected]>
AuthorDate: Thu Feb 3 18:11:32 2022 -0600

    Runtime-configurable YAML option to prohibit USE statements
    
    patch by Caleb Rackliffe; reviewed by David Capwell for CASSANDRA-17318
---
 CHANGES.txt                                        |  1 +
 conf/cassandra.yaml                                |  3 ++
 src/java/org/apache/cassandra/config/Config.java   |  2 ++
 .../cassandra/config/DatabaseDescriptor.java       | 15 ++++++++-
 .../cassandra/cql3/statements/UseStatement.java    |  6 ++++
 .../org/apache/cassandra/metrics/CQLMetrics.java   | 12 +++----
 .../org/apache/cassandra/service/StorageProxy.java | 13 +++++++-
 .../cassandra/service/StorageProxyMBean.java       |  3 ++
 .../cql3/validation/operations/UseTest.java        | 26 +++++++++++++++
 .../apache/cassandra/metrics/CQLMetricsTest.java   | 39 ++++++++++++++++++----
 10 files changed, 104 insertions(+), 16 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index a16f5f3..2a09415 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 4.1
+ * Runtime-configurable YAML option to prohibit USE statements 
(CASSANDRA-17318)
  * When streaming sees a ClosedChannelException this triggers the disk failure 
policy (CASSANDRA-17116)
  * Add a virtual table for exposing prepared statements metrics 
(CASSANDRA-17224)
  * Remove python 2.x support from cqlsh (CASSANDRA-17242)
diff --git a/conf/cassandra.yaml b/conf/cassandra.yaml
index ae7d598..283095a 100644
--- a/conf/cassandra.yaml
+++ b/conf/cassandra.yaml
@@ -1550,6 +1550,9 @@ enable_transient_replication: false
 # 'ALTER ... DROP COMPACT STORAGE' is considered experimental and is not 
recommended for production use.
 enable_drop_compact_storage: false
 
+# Whether or not USE <keyspace> is allowed. This is enabled by default to 
avoid failure on upgrade.
+#use_statements_enabled: true
+
 # When the client triggers a protocol exception or unknown issue (Cassandra 
bug) we increment
 # a client metric showing this; this logic will exclude specific subnets from 
updating these
 # metrics
diff --git a/src/java/org/apache/cassandra/config/Config.java 
b/src/java/org/apache/cassandra/config/Config.java
index f0e52a2..d7100e1 100644
--- a/src/java/org/apache/cassandra/config/Config.java
+++ b/src/java/org/apache/cassandra/config/Config.java
@@ -411,6 +411,8 @@ public class Config
 
     public volatile boolean enable_drop_compact_storage = false;
 
+    public volatile boolean use_statements_enabled = true;
+
     /**
      * Optionally disable asynchronous UDF execution.
      * Disabling asynchronous UDF execution also implicitly disables the 
security-manager!
diff --git a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java 
b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
index 3968484..471102d 100644
--- a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
+++ b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
@@ -70,7 +70,6 @@ import org.apache.cassandra.security.EncryptionContext;
 import org.apache.cassandra.security.SSLFactory;
 import org.apache.cassandra.service.CacheService.CacheType;
 import org.apache.cassandra.service.paxos.Paxos;
-import org.apache.cassandra.utils.Clock;
 import org.apache.cassandra.utils.FBUtilities;
 
 import org.apache.commons.lang3.ArrayUtils;
@@ -3810,4 +3809,18 @@ public class DatabaseDescriptor
 
         conf.minimum_keyspace_rf = value;
     }
+
+    public static boolean getUseStatementsEnabled()
+    {
+        return conf.use_statements_enabled;
+    }
+
+    public static void setUseStatementsEnabled(boolean enabled)
+    {
+        if (enabled != conf.use_statements_enabled)
+        {
+            logger.info("Setting use_statements_enabled to {}", enabled);
+            conf.use_statements_enabled = enabled;
+        }
+    }
 }
diff --git a/src/java/org/apache/cassandra/cql3/statements/UseStatement.java 
b/src/java/org/apache/cassandra/cql3/statements/UseStatement.java
index a0928e8..c7f0c24 100644
--- a/src/java/org/apache/cassandra/cql3/statements/UseStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/UseStatement.java
@@ -19,8 +19,10 @@ package org.apache.cassandra.cql3.statements;
 
 import org.apache.cassandra.audit.AuditLogContext;
 import org.apache.cassandra.audit.AuditLogEntryType;
+import org.apache.cassandra.config.DatabaseDescriptor;
 import org.apache.cassandra.cql3.CQLStatement;
 import org.apache.cassandra.cql3.QueryOptions;
+import org.apache.cassandra.cql3.QueryProcessor;
 import org.apache.cassandra.exceptions.InvalidRequestException;
 import org.apache.cassandra.exceptions.UnauthorizedException;
 import org.apache.cassandra.transport.messages.ResultMessage;
@@ -29,6 +31,7 @@ import org.apache.cassandra.service.QueryState;
 import org.apache.commons.lang3.builder.ToStringBuilder;
 import org.apache.commons.lang3.builder.ToStringStyle;
 
+import static 
org.apache.cassandra.cql3.statements.RequestValidations.checkTrue;
 import static org.apache.cassandra.utils.Clock.Global.nanoTime;
 
 public class UseStatement extends CQLStatement.Raw implements CQLStatement
@@ -50,12 +53,15 @@ public class UseStatement extends CQLStatement.Raw 
implements CQLStatement
         state.validateLogin();
     }
 
+    @Override
     public void validate(ClientState state) throws InvalidRequestException
     {
+        checkTrue(DatabaseDescriptor.getUseStatementsEnabled(), "USE 
statements prohibited. (see use_statements_enabled in cassandra.yaml)");
     }
 
     public ResultMessage execute(QueryState state, QueryOptions options, long 
queryStartNanoTime) throws InvalidRequestException
     {
+        QueryProcessor.metrics.useStatementsExecuted.inc();
         state.getClientState().setKeyspace(keyspace);
         return new ResultMessage.SetKeyspace(keyspace);
     }
diff --git a/src/java/org/apache/cassandra/metrics/CQLMetrics.java 
b/src/java/org/apache/cassandra/metrics/CQLMetrics.java
index 1020e92..ce91333 100644
--- a/src/java/org/apache/cassandra/metrics/CQLMetrics.java
+++ b/src/java/org/apache/cassandra/metrics/CQLMetrics.java
@@ -32,6 +32,8 @@ public class CQLMetrics
     public final Counter preparedStatementsExecuted;
     public final Counter preparedStatementsEvicted;
 
+    public final Counter useStatementsExecuted;
+
     public final Gauge<Integer> preparedStatementsCount;
     public final Gauge<Double> preparedStatementsRatio;
 
@@ -41,13 +43,9 @@ public class CQLMetrics
         preparedStatementsExecuted = 
Metrics.counter(factory.createMetricName("PreparedStatementsExecuted"));
         preparedStatementsEvicted = 
Metrics.counter(factory.createMetricName("PreparedStatementsEvicted"));
 
-        preparedStatementsCount = 
Metrics.register(factory.createMetricName("PreparedStatementsCount"), new 
Gauge<Integer>()
-        {
-            public Integer getValue()
-            {
-                return QueryProcessor.preparedStatementsCount();
-            }
-        });
+        useStatementsExecuted = 
Metrics.counter(factory.createMetricName("UseStatementsExecuted"));
+
+        preparedStatementsCount = 
Metrics.register(factory.createMetricName("PreparedStatementsCount"), 
QueryProcessor::preparedStatementsCount);
         preparedStatementsRatio = 
Metrics.register(factory.createMetricName("PreparedStatementsRatio"), new 
RatioGauge()
         {
             public Ratio getRatio()
diff --git a/src/java/org/apache/cassandra/service/StorageProxy.java 
b/src/java/org/apache/cassandra/service/StorageProxy.java
index 4780e45..e273348 100644
--- a/src/java/org/apache/cassandra/service/StorageProxy.java
+++ b/src/java/org/apache/cassandra/service/StorageProxy.java
@@ -416,7 +416,6 @@ public class StorageProxy implements StorageProxyMBean
      *     {@link ConsistencyLevel#LOCAL_SERIAL}).
      * @param consistencyForReplayCommits the consistency for the commit phase 
of "replayed" in-progress operations.
      * @param consistencyForCommit the consistency for the commit phase of 
_this_ operation update.
-     * @param state the client state.
      * @param queryStartNanoTime the nano time for the start of the query this 
is part of. This is the base time for
      *     timeouts.
      * @param casMetrics the metrics to update for this operation.
@@ -2981,4 +2980,16 @@ public class StorageProxy implements StorageProxyMBean
     {
         return Paxos.getPaxosVariant().toString();
     }
+
+    @Override
+    public boolean getUseStatementsEnabled()
+    {
+        return DatabaseDescriptor.getUseStatementsEnabled();
+    }
+
+    @Override
+    public void setUseStatementsEnabled(boolean enabled)
+    {
+        DatabaseDescriptor.setUseStatementsEnabled(enabled);
+    }
 }
diff --git a/src/java/org/apache/cassandra/service/StorageProxyMBean.java 
b/src/java/org/apache/cassandra/service/StorageProxyMBean.java
index 5ce1b9e..cce7ff0 100644
--- a/src/java/org/apache/cassandra/service/StorageProxyMBean.java
+++ b/src/java/org/apache/cassandra/service/StorageProxyMBean.java
@@ -121,4 +121,7 @@ public interface StorageProxyMBean
 
     void setPaxosVariant(String variant);
     String getPaxosVariant();
+
+    boolean getUseStatementsEnabled();
+    void setUseStatementsEnabled(boolean enabled);
 }
diff --git 
a/test/unit/org/apache/cassandra/cql3/validation/operations/UseTest.java 
b/test/unit/org/apache/cassandra/cql3/validation/operations/UseTest.java
index e1498b6..b2a40a3 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/UseTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/UseTest.java
@@ -17,15 +17,41 @@
  */
 package org.apache.cassandra.cql3.validation.operations;
 
+import org.assertj.core.api.Assertions;
 import org.junit.Test;
 
+import org.apache.cassandra.config.DatabaseDescriptor;
 import org.apache.cassandra.cql3.CQLTester;
+import org.apache.cassandra.cql3.QueryProcessor;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
 
 public class UseTest extends CQLTester
 {
     @Test
     public void testUseStatementWithBindVariable() throws Throwable
     {
+        DatabaseDescriptor.setUseStatementsEnabled(true);
         assertInvalidSyntaxMessage("Bind variables cannot be used for keyspace 
names", "USE ?");
     }
+
+    @Test
+    public void shouldRejectUseStatementWhenProhibited() throws Throwable
+    {
+        long useCountBefore = 
QueryProcessor.metrics.useStatementsExecuted.getCount();
+
+        try
+        {
+            DatabaseDescriptor.setUseStatementsEnabled(false);
+            execute("USE cql_test_keyspace");
+            fail("expected USE statement to fail with use_statements_enabled = 
false");
+        }
+        catch (InvalidRequestException e)
+        {
+            assertEquals(useCountBefore, 
QueryProcessor.metrics.useStatementsExecuted.getCount());
+            Assertions.assertThat(e).hasMessageContaining("USE statements 
prohibited");
+        }
+    }
 }
diff --git a/test/unit/org/apache/cassandra/metrics/CQLMetricsTest.java 
b/test/unit/org/apache/cassandra/metrics/CQLMetricsTest.java
index 3971b9f..e14861e 100644
--- a/test/unit/org/apache/cassandra/metrics/CQLMetricsTest.java
+++ b/test/unit/org/apache/cassandra/metrics/CQLMetricsTest.java
@@ -20,12 +20,14 @@ package org.apache.cassandra.metrics;
 
 import java.io.IOException;
 
+import org.junit.Assert;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
 import com.datastax.driver.core.Cluster;
 import com.datastax.driver.core.PreparedStatement;
 import com.datastax.driver.core.Session;
+import com.datastax.driver.core.exceptions.InvalidQueryException;
 import org.apache.cassandra.SchemaLoader;
 import org.apache.cassandra.config.DatabaseDescriptor;
 import org.apache.cassandra.schema.Schema;
@@ -33,12 +35,12 @@ import org.apache.cassandra.cql3.QueryProcessor;
 import org.apache.cassandra.exceptions.ConfigurationException;
 import org.apache.cassandra.service.EmbeddedCassandraService;
 
-import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 public class CQLMetricsTest extends SchemaLoader
 {
-    private static EmbeddedCassandraService cassandra;
-
     private static Cluster cluster;
     private static Session session;
 
@@ -47,7 +49,7 @@ public class CQLMetricsTest extends SchemaLoader
     {
         Schema.instance.clear();
 
-        cassandra = new EmbeddedCassandraService();
+        EmbeddedCassandraService cassandra = new EmbeddedCassandraService();
         cassandra.start();
 
         cluster = 
Cluster.builder().addContactPoint("127.0.0.1").withPort(DatabaseDescriptor.getNativeTransportPort()).build();
@@ -58,10 +60,33 @@ public class CQLMetricsTest extends SchemaLoader
     }
 
     @Test
+    public void testConnectionWithUseDisabled()
+    {
+        long useCountBefore = 
QueryProcessor.metrics.useStatementsExecuted.getCount();
+        DatabaseDescriptor.setUseStatementsEnabled(false);
+
+        try (Session ignored = cluster.connect("junit"))
+        {
+            fail("expected USE statement to fail with use_statements_enabled = 
false");
+        }
+        catch (InvalidQueryException e)
+        {
+            Assert.assertEquals(useCountBefore, 
QueryProcessor.metrics.useStatementsExecuted.getCount());
+            assertTrue(e.getMessage().contains("USE statements prohibited"));
+        }
+        finally
+        {
+            DatabaseDescriptor.setUseStatementsEnabled(true);
+        }
+    }
+
+    @Test
     public void testPreparedStatementsCount()
     {
         int n = QueryProcessor.metrics.preparedStatementsCount.getValue();
+        long useCountBefore = 
QueryProcessor.metrics.useStatementsExecuted.getCount();
         session.execute("use junit");
+        Assert.assertEquals(useCountBefore + 1, 
QueryProcessor.metrics.useStatementsExecuted.getCount());
         session.prepare("SELECT * FROM junit.metricstest WHERE id = ?");
         assertEquals(n+2, (int) 
QueryProcessor.metrics.preparedStatementsCount.getValue());
     }
@@ -104,15 +129,15 @@ public class CQLMetricsTest extends SchemaLoader
         clearMetrics();
         PreparedStatement metricsStatement = session.prepare("INSERT INTO 
junit.metricstest (id, val) VALUES (?, ?)");
 
-        assertEquals(Double.NaN, 
QueryProcessor.metrics.preparedStatementsRatio.getValue());
+        assertEquals(Double.NaN, 
QueryProcessor.metrics.preparedStatementsRatio.getValue(), 0.0);
 
         for (int i = 0; i < 10; i++)
             session.execute(metricsStatement.bind(i, "val" + i));
-        assertEquals(1.0, 
QueryProcessor.metrics.preparedStatementsRatio.getValue());
+        assertEquals(1.0, 
QueryProcessor.metrics.preparedStatementsRatio.getValue(), 0.0);
 
         for (int i = 0; i < 10; i++)
             session.execute(String.format("INSERT INTO junit.metricstest (id, 
val) VALUES (%d, '%s')", i, "val" + i));
-        assertEquals(0.5, 
QueryProcessor.metrics.preparedStatementsRatio.getValue());
+        assertEquals(0.5, 
QueryProcessor.metrics.preparedStatementsRatio.getValue(), 0.0);
     }
 
     private void clearMetrics()

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to