This is an automated email from the ASF dual-hosted git repository. jmckenzie 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 6bd373f5d2 Add guardrail to disallow TRUNCATE and DROP TABLE commands 6bd373f5d2 is described below commit 6bd373f5d27754f80384caa31d1b2a4cfc43bc19 Author: Josh McKenzie <jmcken...@apache.org> AuthorDate: Mon Apr 18 14:53:17 2022 -0400 Add guardrail to disallow TRUNCATE and DROP TABLE commands Patch by Josh McKenzie; reviewed by Aleksey Yeschenko for CASSANDRA-17558 --- CHANGES.txt | 1 + conf/cassandra.yaml | 2 + src/java/org/apache/cassandra/config/Config.java | 1 + .../apache/cassandra/config/GuardrailsOptions.java | 14 ++++ .../cql3/statements/TruncateStatement.java | 2 + .../cql3/statements/schema/DropTableStatement.java | 3 + .../apache/cassandra/db/guardrails/Guardrails.java | 17 ++++ .../cassandra/db/guardrails/GuardrailsConfig.java | 7 ++ .../cassandra/db/guardrails/GuardrailsMBean.java | 12 +++ .../db/guardrails/GuardrailTruncateDropTest.java | 91 ++++++++++++++++++++++ 10 files changed, 150 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 7850ca8e5b..8a4e661719 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 4.1 + * Add guardrail to disallow TRUNCATE and DROP TABLE commands (CASSANDRA-17558) * Add plugin support for CQLSH (CASSANDRA-16456) * Add guardrail to disallow querying with ALLOW FILTERING (CASSANDRA-17370) * Enhance SnakeYAML properties to be reusable outside of YAML parsing, support camel case conversion to snake case, and add support to ignore properties (CASSANDRA-17166) diff --git a/conf/cassandra.yaml b/conf/cassandra.yaml index f6f0a3ccad..c3e183fbd3 100644 --- a/conf/cassandra.yaml +++ b/conf/cassandra.yaml @@ -1626,6 +1626,8 @@ drop_compact_storage_enabled: false # user_timestamps_enabled: true # Guardrail to allow/disallow GROUP BY functionality. # group_by_enabled: true +# Guardrail to allow/disallow TRUNCATE and DROP statements +# truncate_drop_enabled: true # Guardrail to warn or fail when using a page size greater than threshold. # The two thresholds default to -1 to disable. # page_size_warn_threshold: -1 diff --git a/src/java/org/apache/cassandra/config/Config.java b/src/java/org/apache/cassandra/config/Config.java index a3d6348fa1..1c277cf0c7 100644 --- a/src/java/org/apache/cassandra/config/Config.java +++ b/src/java/org/apache/cassandra/config/Config.java @@ -808,6 +808,7 @@ public class Config public volatile Set<ConsistencyLevel> write_consistency_levels_disallowed = Collections.emptySet(); public volatile boolean user_timestamps_enabled = true; public volatile boolean group_by_enabled = true; + public volatile boolean truncate_drop_enabled = true; public volatile boolean secondary_indexes_enabled = true; public volatile boolean uncompressed_tables_enabled = true; public volatile boolean compact_tables_enabled = true; diff --git a/src/java/org/apache/cassandra/config/GuardrailsOptions.java b/src/java/org/apache/cassandra/config/GuardrailsOptions.java index d32f3b1bc6..6654b4fc74 100644 --- a/src/java/org/apache/cassandra/config/GuardrailsOptions.java +++ b/src/java/org/apache/cassandra/config/GuardrailsOptions.java @@ -329,6 +329,20 @@ public class GuardrailsOptions implements GuardrailsConfig x -> config.group_by_enabled = x); } + @Override + public boolean getTruncateDropEnabled() + { + return config.truncate_drop_enabled; + } + + public void setTruncateDropEnabled(boolean enabled) + { + updatePropertyWithLogging("truncate_drop_enabled", + enabled, + () -> config.truncate_drop_enabled, + x -> config.truncate_drop_enabled = x); + } + @Override public boolean getSecondaryIndexesEnabled() { diff --git a/src/java/org/apache/cassandra/cql3/statements/TruncateStatement.java b/src/java/org/apache/cassandra/cql3/statements/TruncateStatement.java index 2c1c9941ce..bdd4c7b870 100644 --- a/src/java/org/apache/cassandra/cql3/statements/TruncateStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/TruncateStatement.java @@ -25,6 +25,7 @@ import org.apache.cassandra.auth.Permission; import org.apache.cassandra.cql3.*; import org.apache.cassandra.db.ColumnFamilyStore; import org.apache.cassandra.db.Keyspace; +import org.apache.cassandra.db.guardrails.Guardrails; import org.apache.cassandra.db.virtual.VirtualKeyspaceRegistry; import org.apache.cassandra.exceptions.*; import org.apache.cassandra.schema.Schema; @@ -57,6 +58,7 @@ public class TruncateStatement extends QualifiedStatement implements CQLStatemen public void validate(ClientState state) throws InvalidRequestException { Schema.instance.validateTable(keyspace(), name()); + Guardrails.truncateDropEnabled.ensureEnabled(state); } public ResultMessage execute(QueryState state, QueryOptions options, long queryStartNanoTime) throws InvalidRequestException, TruncateException diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/DropTableStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/DropTableStatement.java index 15c2a03a3a..371b957845 100644 --- a/src/java/org/apache/cassandra/cql3/statements/schema/DropTableStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/schema/DropTableStatement.java @@ -22,6 +22,7 @@ import org.apache.cassandra.audit.AuditLogEntryType; import org.apache.cassandra.auth.Permission; import org.apache.cassandra.cql3.CQLStatement; import org.apache.cassandra.cql3.QualifiedName; +import org.apache.cassandra.db.guardrails.Guardrails; import org.apache.cassandra.schema.*; import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff; import org.apache.cassandra.service.ClientState; @@ -48,6 +49,8 @@ public final class DropTableStatement extends AlterSchemaStatement public Keyspaces apply(Keyspaces schema) { + Guardrails.truncateDropEnabled.ensureEnabled(state); + KeyspaceMetadata keyspace = schema.getNullable(keyspaceName); TableMetadata table = null == keyspace diff --git a/src/java/org/apache/cassandra/db/guardrails/Guardrails.java b/src/java/org/apache/cassandra/db/guardrails/Guardrails.java index 8451aff8f3..3578ceb1ec 100644 --- a/src/java/org/apache/cassandra/db/guardrails/Guardrails.java +++ b/src/java/org/apache/cassandra/db/guardrails/Guardrails.java @@ -145,6 +145,11 @@ public final class Guardrails implements GuardrailsMBean state -> !CONFIG_PROVIDER.getOrCreate(state).getGroupByEnabled(), "GROUP BY functionality"); + public static final DisableFlag truncateDropEnabled = + new DisableFlag("truncate_drop", + state -> !CONFIG_PROVIDER.getOrCreate(state).getTruncateDropEnabled(), + "TRUNCATE and DROP TABLE functionality"); + /** * Guardrail disabling user's ability to turn off compression */ @@ -569,6 +574,18 @@ public final class Guardrails implements GuardrailsMBean DEFAULT_CONFIG.setGroupByEnabled(enabled); } + @Override + public boolean getTruncateDropEnabled() + { + return DEFAULT_CONFIG.getTruncateDropEnabled(); + } + + @Override + public void setTruncateDropEnabled(boolean enabled) + { + DEFAULT_CONFIG.setTruncateDropEnabled(enabled); + } + @Override public int getPageSizeWarnThreshold() { diff --git a/src/java/org/apache/cassandra/db/guardrails/GuardrailsConfig.java b/src/java/org/apache/cassandra/db/guardrails/GuardrailsConfig.java index 6dbc0bb91e..cb2bd313b9 100644 --- a/src/java/org/apache/cassandra/db/guardrails/GuardrailsConfig.java +++ b/src/java/org/apache/cassandra/db/guardrails/GuardrailsConfig.java @@ -153,6 +153,13 @@ public interface GuardrailsConfig */ boolean getGroupByEnabled(); + /** + * Returns whether TRUNCATE or DROP table are allowed + * + * @return {@code true} if allowed, {@code false} otherwise. + */ + boolean getTruncateDropEnabled(); + /** * @return The threshold to warn when page size exceeds given size. */ diff --git a/src/java/org/apache/cassandra/db/guardrails/GuardrailsMBean.java b/src/java/org/apache/cassandra/db/guardrails/GuardrailsMBean.java index 3d94410f4e..0240f430a4 100644 --- a/src/java/org/apache/cassandra/db/guardrails/GuardrailsMBean.java +++ b/src/java/org/apache/cassandra/db/guardrails/GuardrailsMBean.java @@ -264,6 +264,18 @@ public interface GuardrailsMBean */ void setGroupByEnabled(boolean enabled); + /** + * Returns whether users can TRUNCATE or DROP TABLE + * + * @return {@code true} if allowed, {@code false} otherwise. + */ + boolean getTruncateDropEnabled(); + + /** + * Sets whether users can TRUNCATE or DROP TABLE + */ + void setTruncateDropEnabled(boolean enabled); + /** * @return The threshold to warn when requested page size greater than threshold. * -1 means disabled. diff --git a/test/unit/org/apache/cassandra/db/guardrails/GuardrailTruncateDropTest.java b/test/unit/org/apache/cassandra/db/guardrails/GuardrailTruncateDropTest.java new file mode 100644 index 0000000000..a3264fa4fe --- /dev/null +++ b/test/unit/org/apache/cassandra/db/guardrails/GuardrailTruncateDropTest.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.cassandra.db.guardrails; + +import org.junit.After; +import org.junit.Test; + +import static java.lang.String.format; + +public class GuardrailTruncateDropTest extends GuardrailTester +{ + private String tableQuery = "CREATE TABLE %s(pk int, ck int, v int, PRIMARY KEY(pk, ck))"; + + private void setGuardrail(boolean enabled) + { + Guardrails.instance.setTruncateDropEnabled(enabled); + } + + @After + public void afterTest() + { + setGuardrail(true); + } + + @Test + public void testCanDropWhileFeatureEnabled() throws Throwable + { + setGuardrail(true); + createTable(tableQuery); + assertValid(String.format("DROP TABLE %s", currentTable())); + } + + @Test + public void testCannotDropWhileFeatureDisabled() throws Throwable + { + setGuardrail(false); + createTable(tableQuery); + assertFails("DROP TABLE %s", "TRUNCATE and DROP TABLE functionality is not allowed"); + } + + @Test + public void testIfExistsDoesNotBreakGuardrail() throws Throwable + { + setGuardrail(false); + createTable(tableQuery); + assertFails("DROP TABLE IF EXISTS %s", "TRUNCATE and DROP TABLE functionality is not allowed"); + } + + @Test + public void testCanTruncateWhileFeatureEnabled() throws Throwable + { + setGuardrail(true); + createTable(tableQuery); + assertValid(String.format("TRUNCATE TABLE %s", currentTable())); + } + + @Test + public void testCannotTruncateWhileFeatureDisabled() throws Throwable + { + setGuardrail(false); + createTable(tableQuery); + assertFails("TRUNCATE TABLE %s", "TRUNCATE and DROP TABLE functionality is not allowed"); + } + + @Test + public void testExcludedUsersCanAlwaysDropAndTruncate() throws Throwable + { + String table = keyspace() + '.' + createTableName(); + setGuardrail(false); + testExcludedUsers(() -> format("CREATE TABLE %s (k int PRIMARY KEY, v1 int, v2 int, v3 int, v4 int)", table), + () -> format("TRUNCATE TABLE %s", table), + () -> format("DROP TABLE %s", table)); + + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@cassandra.apache.org For additional commands, e-mail: commits-h...@cassandra.apache.org