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]