mirror of https://github.com/apache/cassandra
Merge bf4ba5393c into 209ef2b6c4
This commit is contained in:
commit
2273805fbe
File diff suppressed because it is too large
Load Diff
1
NEWS.txt
1
NEWS.txt
|
|
@ -199,7 +199,6 @@ New features
|
|||
- Added a new CQL function, maxwritetime. It shows the largest unix timestamp that the data was written, similar to
|
||||
its sibling CQL function, writetime.
|
||||
- New Guardrails added:
|
||||
- Whether ALTER TABLE commands are allowed to mutate columns
|
||||
- Whether SimpleStrategy is allowed on keyspace creation or alteration
|
||||
- Maximum replication factor
|
||||
- Whether DROP KEYSPACE commands are allowed.
|
||||
|
|
|
|||
|
|
@ -2084,6 +2084,10 @@ drop_compact_storage_enabled: false
|
|||
# Guardrail to allow/disallow DROP KEYSPACE statements
|
||||
# drop_keyspace_enabled: true
|
||||
#
|
||||
# Guardrail to allow/disallow DDL/DCL statements
|
||||
# ddl_enabled: true
|
||||
# dcl_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
|
||||
|
|
@ -2185,9 +2189,6 @@ drop_compact_storage_enabled: false
|
|||
# vector_dimensions_warn_threshold: -1
|
||||
# vector_dimensions_fail_threshold: -1
|
||||
#
|
||||
# Guardrail to indicate whether or not users are allowed to use ALTER TABLE commands to make column changes to tables
|
||||
# alter_table_enabled: true
|
||||
#
|
||||
# Guardrail to warn or fail when local data disk usage percentage exceeds threshold. Valid values are in [1, 100].
|
||||
# This is only used for the disks storing data directories, so it won't count any separate disks used for storing
|
||||
# the commitlog, hints nor saved caches. The disk usage is the ratio between the amount of space used by the data
|
||||
|
|
|
|||
|
|
@ -2056,6 +2056,10 @@ drop_compact_storage_enabled: false
|
|||
# Guardrail to allow/disallow DROP KEYSPACE statements
|
||||
# drop_keyspace_enabled: true
|
||||
#
|
||||
# Guardrail to allow/disallow DDL/DCL statements
|
||||
# ddl_enabled: true
|
||||
# dcl_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
|
||||
|
|
@ -2157,9 +2161,6 @@ drop_compact_storage_enabled: false
|
|||
# vector_dimensions_warn_threshold: -1
|
||||
# vector_dimensions_fail_threshold: -1
|
||||
#
|
||||
# Guardrail to indicate whether or not users are allowed to use ALTER TABLE commands to make column changes to tables
|
||||
# alter_table_enabled: true
|
||||
#
|
||||
# Guardrail to warn or fail when local data disk usage percentage exceeds threshold. Valid values are in [1, 100].
|
||||
# This is only used for the disks storing data directories, so it won't count any separate disks used for storing
|
||||
# the commitlog, hints nor saved caches. The disk usage is the ratio between the amount of space used by the data
|
||||
|
|
|
|||
|
|
@ -884,8 +884,9 @@ public class Config
|
|||
public volatile Set<ConsistencyLevel> write_consistency_levels_warned = Collections.emptySet();
|
||||
public volatile Set<ConsistencyLevel> write_consistency_levels_disallowed = Collections.emptySet();
|
||||
public volatile boolean user_timestamps_enabled = true;
|
||||
public volatile boolean alter_table_enabled = true;
|
||||
public volatile boolean group_by_enabled = true;
|
||||
public volatile boolean ddl_enabled = true;
|
||||
public volatile boolean dcl_enabled = true;
|
||||
public volatile boolean drop_truncate_table_enabled = true;
|
||||
public volatile boolean drop_keyspace_enabled = true;
|
||||
public volatile boolean secondary_indexes_enabled = true;
|
||||
|
|
|
|||
|
|
@ -368,6 +368,34 @@ public class GuardrailsOptions implements GuardrailsConfig
|
|||
x -> config.drop_keyspace_enabled = x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getDDLEnabled()
|
||||
{
|
||||
return config.ddl_enabled;
|
||||
}
|
||||
|
||||
public void setDDLEnabled(boolean enabled)
|
||||
{
|
||||
updatePropertyWithLogging("ddl_enabled",
|
||||
enabled,
|
||||
() -> config.ddl_enabled,
|
||||
x -> config.ddl_enabled = x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getDCLEnabled()
|
||||
{
|
||||
return config.dcl_enabled;
|
||||
}
|
||||
|
||||
public void setDCLEnabled(boolean enabled)
|
||||
{
|
||||
updatePropertyWithLogging("dcl_enabled",
|
||||
enabled,
|
||||
() -> config.dcl_enabled,
|
||||
x -> config.dcl_enabled = x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getSecondaryIndexesEnabled()
|
||||
{
|
||||
|
|
@ -410,20 +438,6 @@ public class GuardrailsOptions implements GuardrailsConfig
|
|||
x -> config.compact_tables_enabled = x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAlterTableEnabled()
|
||||
{
|
||||
return config.alter_table_enabled;
|
||||
}
|
||||
|
||||
public void setAlterTableEnabled(boolean enabled)
|
||||
{
|
||||
updatePropertyWithLogging("alter_table_enabled",
|
||||
enabled,
|
||||
() -> config.alter_table_enabled,
|
||||
x -> config.alter_table_enabled = x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getReadBeforeWriteListOperationsEnabled()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -100,6 +100,16 @@ public interface CQLStatement
|
|||
return false;
|
||||
}
|
||||
|
||||
default boolean isDDLStatement()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
default boolean isDCLStatement()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public static abstract class Raw
|
||||
{
|
||||
protected VariableSpecifications bindVariables;
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ import org.antlr.runtime.*;
|
|||
import org.apache.cassandra.concurrent.ImmediateExecutor;
|
||||
import org.apache.cassandra.concurrent.ScheduledExecutors;
|
||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
import org.apache.cassandra.db.guardrails.Guardrails;
|
||||
import org.apache.cassandra.db.partitions.UnfilteredPartitionIterators;
|
||||
import org.apache.cassandra.locator.InetAddressAndPort;
|
||||
import org.apache.cassandra.metrics.ClientRequestMetrics;
|
||||
|
|
@ -271,6 +272,11 @@ public class QueryProcessor implements QueryHandler
|
|||
statement.authorize(clientState);
|
||||
statement.validate(clientState);
|
||||
|
||||
if (statement.isDDLStatement())
|
||||
Guardrails.ddlEnabled.ensureEnabled(clientState);
|
||||
else if (statement.isDCLStatement())
|
||||
Guardrails.dclEnabled.ensureEnabled(clientState);
|
||||
|
||||
ResultMessage result = options.getConsistency() == ConsistencyLevel.NODE_LOCAL
|
||||
? processNodeLocalStatement(statement, queryState, options)
|
||||
: statement.execute(queryState, options, requestTime);
|
||||
|
|
|
|||
|
|
@ -71,5 +71,11 @@ public abstract class AuthenticationStatement extends CQLStatement.Raw implement
|
|||
{
|
||||
return query;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDCLStatement()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -69,4 +69,10 @@ public abstract class AuthorizationStatement extends CQLStatement.Raw implements
|
|||
{
|
||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDCLStatement()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,12 @@ abstract public class AlterSchemaStatement implements CQLStatement.SingleKeyspac
|
|||
this.keyspaceName = keyspaceName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDDLStatement()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public void validate(ClientState state)
|
||||
{
|
||||
// validation is performed while executing the statement, in apply()
|
||||
|
|
|
|||
|
|
@ -277,7 +277,6 @@ public abstract class AlterTableStatement extends AlterSchemaStatement
|
|||
|
||||
public KeyspaceMetadata apply(KeyspaceMetadata keyspace, TableMetadata table)
|
||||
{
|
||||
Guardrails.alterTableEnabled.ensureEnabled("ALTER TABLE changing columns", state);
|
||||
TableMetadata.Builder tableBuilder = table.unbuild();
|
||||
Views.Builder viewsBuilder = keyspace.views.unbuild();
|
||||
newColumns.forEach(c -> addColumn(keyspace, table, c, ifColumnNotExists, tableBuilder, viewsBuilder));
|
||||
|
|
@ -393,7 +392,6 @@ public abstract class AlterTableStatement extends AlterSchemaStatement
|
|||
|
||||
public KeyspaceMetadata apply(KeyspaceMetadata keyspace, TableMetadata table)
|
||||
{
|
||||
Guardrails.alterTableEnabled.ensureEnabled("ALTER TABLE changing columns", state);
|
||||
TableMetadata.Builder builder = table.unbuild();
|
||||
removedColumns.forEach(c -> dropColumn(keyspace, table, c, ifColumnExists, builder));
|
||||
return keyspace.withSwapped(keyspace.tables.withSwapped(builder.build()));
|
||||
|
|
@ -461,7 +459,6 @@ public abstract class AlterTableStatement extends AlterSchemaStatement
|
|||
|
||||
public KeyspaceMetadata apply(KeyspaceMetadata keyspace, TableMetadata table)
|
||||
{
|
||||
Guardrails.alterTableEnabled.ensureEnabled("ALTER TABLE changing columns", state);
|
||||
TableMetadata.Builder tableBuilder = table.unbuild();
|
||||
Views.Builder viewsBuilder = keyspace.views.unbuild();
|
||||
renamedColumns.forEach((o, n) -> renameColumn(keyspace, table, o, n, ifColumnsExists, tableBuilder, viewsBuilder));
|
||||
|
|
|
|||
|
|
@ -157,15 +157,6 @@ public final class Guardrails implements GuardrailsMBean
|
|||
state -> CONFIG_PROVIDER.getOrCreate(state).getGroupByEnabled(),
|
||||
"GROUP BY functionality");
|
||||
|
||||
/**
|
||||
* Guardrail disabling ALTER TABLE column mutation access.
|
||||
*/
|
||||
public static final EnableFlag alterTableEnabled =
|
||||
new EnableFlag("alter_table",
|
||||
null,
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getAlterTableEnabled(),
|
||||
"User access to ALTER TABLE statement for column mutation");
|
||||
|
||||
/**
|
||||
* Guardrail disabling DROP / TRUNCATE TABLE behavior
|
||||
*/
|
||||
|
|
@ -184,6 +175,24 @@ public final class Guardrails implements GuardrailsMBean
|
|||
state -> CONFIG_PROVIDER.getOrCreate(state).getDropKeyspaceEnabled(),
|
||||
"DROP KEYSPACE functionality");
|
||||
|
||||
/**
|
||||
* Guardrail disabling DDL statements
|
||||
*/
|
||||
public static final EnableFlag ddlEnabled =
|
||||
new EnableFlag("ddl_enabled",
|
||||
null,
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getDDLEnabled(),
|
||||
"DDL statement");
|
||||
|
||||
/**
|
||||
* Guardrail disabling DCL statements
|
||||
*/
|
||||
public static final EnableFlag dclEnabled =
|
||||
new EnableFlag("dcl_enabled",
|
||||
null,
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getDCLEnabled(),
|
||||
"DCL statement");
|
||||
|
||||
/**
|
||||
* Guardrail disabling user's ability to turn off compression
|
||||
*/
|
||||
|
|
@ -762,18 +771,6 @@ public final class Guardrails implements GuardrailsMBean
|
|||
DEFAULT_CONFIG.setUserTimestampsEnabled(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAlterTableEnabled()
|
||||
{
|
||||
return DEFAULT_CONFIG.getAlterTableEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlterTableEnabled(boolean enabled)
|
||||
{
|
||||
DEFAULT_CONFIG.setAlterTableEnabled(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAllowFilteringEnabled()
|
||||
{
|
||||
|
|
@ -858,6 +855,30 @@ public final class Guardrails implements GuardrailsMBean
|
|||
DEFAULT_CONFIG.setDropKeyspaceEnabled(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getDDLEnabled()
|
||||
{
|
||||
return DEFAULT_CONFIG.getDDLEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDDLEnabled(boolean enabled)
|
||||
{
|
||||
DEFAULT_CONFIG.setDDLEnabled(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getDCLEnabled()
|
||||
{
|
||||
return DEFAULT_CONFIG.getDCLEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDCLEnabled(boolean enabled)
|
||||
{
|
||||
DEFAULT_CONFIG.setDCLEnabled(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPageSizeWarnThreshold()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -133,13 +133,6 @@ public interface GuardrailsConfig
|
|||
*/
|
||||
boolean getUserTimestampsEnabled();
|
||||
|
||||
/**
|
||||
* Returns whether users are allowed access to the ALTER TABLE statement to mutate columns or not
|
||||
*
|
||||
* @return {@code true} if ALTER TABLE ADD/REMOVE/RENAME is allowed, {@code false} otherwise.
|
||||
*/
|
||||
boolean getAlterTableEnabled();
|
||||
|
||||
/**
|
||||
* Returns whether tables can be uncompressed
|
||||
*
|
||||
|
|
@ -175,6 +168,20 @@ public interface GuardrailsConfig
|
|||
*/
|
||||
boolean getDropKeyspaceEnabled();
|
||||
|
||||
/**
|
||||
* Returns whether DDL statement is allowed
|
||||
*
|
||||
* @return {@code true} if allowed, {@code false} otherwise.
|
||||
*/
|
||||
boolean getDDLEnabled();
|
||||
|
||||
/**
|
||||
* Returns whether DCL statement is allowed
|
||||
*
|
||||
* @return {@code true} if allowed, {@code false} otherwise.
|
||||
*/
|
||||
boolean getDCLEnabled();
|
||||
|
||||
/**
|
||||
* @return The threshold to warn when page size exceeds given size.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -264,20 +264,6 @@ public interface GuardrailsMBean
|
|||
*/
|
||||
void setCompactTablesEnabled(boolean enabled);
|
||||
|
||||
/**
|
||||
* Gets whether users can use the ALTER TABLE statement to change columns
|
||||
*
|
||||
* @return {@code true} if ALTER TABLE is allowed, {@code false} otherwise.
|
||||
*/
|
||||
boolean getAlterTableEnabled();
|
||||
|
||||
/**
|
||||
* Sets whether users can use the ALTER TABLE statement to change columns
|
||||
*
|
||||
* @param enabled {@code true} if changing columns is allowed, {@code false} otherwise.
|
||||
*/
|
||||
void setAlterTableEnabled(boolean enabled);
|
||||
|
||||
/**
|
||||
* Returns whether GROUP BY queries are allowed.
|
||||
*
|
||||
|
|
@ -316,6 +302,30 @@ public interface GuardrailsMBean
|
|||
*/
|
||||
void setDropKeyspaceEnabled(boolean enabled);
|
||||
|
||||
/**
|
||||
* Returns whether DDL statement is allowed
|
||||
*
|
||||
* @return {@code true} if allowed, {@code false} otherwise.
|
||||
*/
|
||||
boolean getDDLEnabled();
|
||||
|
||||
/**
|
||||
* Sets whether DDL statement is allowed
|
||||
*/
|
||||
void setDDLEnabled(boolean enabled);
|
||||
|
||||
/**
|
||||
* Returns whether DCL statement is allowed
|
||||
*
|
||||
* @return {@code true} if allowed, {@code false} otherwise.
|
||||
*/
|
||||
boolean getDCLEnabled();
|
||||
|
||||
/**
|
||||
* Sets whether DCL statement is allowed
|
||||
*/
|
||||
void setDCLEnabled(boolean enabled);
|
||||
|
||||
/**
|
||||
* @return The threshold to warn when requested page size greater than threshold.
|
||||
* -1 means disabled.
|
||||
|
|
|
|||
|
|
@ -52,7 +52,8 @@ roles_validity: "org.apache.cassandra.config.DurationSpec.IntMillisecondsBound"
|
|||
coordinator_read_size_warn_threshold: "org.apache.cassandra.config.DataStorageSpec.LongBytesBound"
|
||||
scripted_user_defined_functions_enabled: "java.lang.Boolean"
|
||||
auth_cache_warming_enabled: "java.lang.Boolean"
|
||||
alter_table_enabled: "java.lang.Boolean"
|
||||
ddl_enabled: "java.lang.Boolean"
|
||||
dcl_enabled: "java.lang.Boolean"
|
||||
client_request_size_metrics_enabled: "java.lang.Boolean"
|
||||
cdc_on_repair_enabled: "java.lang.Boolean"
|
||||
entire_sstable_stream_throughput_outbound: "org.apache.cassandra.config.DataRateSpec.LongBytesPerSecondBound"
|
||||
|
|
|
|||
|
|
@ -22,25 +22,33 @@ import org.junit.After;
|
|||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.datastax.driver.core.exceptions.InvalidQueryException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
/**
|
||||
* Tests the guardrail for disabling user access to the ALTER TABLE statement, {@link Guardrails#alterTableEnabled}.
|
||||
*
|
||||
* Tests the guardrail for disabling user access to the ALTER TABLE statement, {@link Guardrails#ddlEnabled}.
|
||||
* <p>
|
||||
* NOTE: This test class depends on {@link #currentTable()} method for setup, cleanup, and execution of tests. You'll
|
||||
* need to refactor this if you add tests that make changes to the current table as the test teardown will no longer match
|
||||
* setup.
|
||||
* <p>
|
||||
* This test is a leftover from CASSANDRA-17495 which was accommodated for the purposes of CASSANDRA-19556.
|
||||
*/
|
||||
public class GuardrailAlterTableTest extends GuardrailTester
|
||||
{
|
||||
private static final String DDL_ERROR_MSG = "DDL statement is not allowed";
|
||||
|
||||
public GuardrailAlterTableTest()
|
||||
{
|
||||
super(Guardrails.alterTableEnabled);
|
||||
super(Guardrails.ddlEnabled);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setupTest() throws Throwable
|
||||
{
|
||||
createTable("CREATE TABLE IF NOT EXISTS %s (k INT, c INT, v TEXT, PRIMARY KEY(k, c))");
|
||||
super.beforeGuardrailTest();
|
||||
}
|
||||
|
||||
@After
|
||||
|
|
@ -52,82 +60,60 @@ public class GuardrailAlterTableTest extends GuardrailTester
|
|||
|
||||
private void setGuardrail(boolean alterTableEnabled)
|
||||
{
|
||||
guardrails().setAlterTableEnabled(alterTableEnabled);
|
||||
guardrails().setDDLEnabled(alterTableEnabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm that ALTER TABLE queries either work (guardrail enabled) or fail (guardrail disabled) appropriately
|
||||
* @throws Throwable
|
||||
*/
|
||||
@Test
|
||||
public void testGuardrailEnabledAndDisabled() throws Throwable
|
||||
public void testGuardrailEnabledAndDisabled()
|
||||
{
|
||||
setGuardrail(false);
|
||||
assertFails("ALTER TABLE %s ADD test_one text;", "changing columns");
|
||||
assertFailQuery("ALTER TABLE %s ADD test_one text");
|
||||
|
||||
setGuardrail(true);
|
||||
assertValid("ALTER TABLE %s ADD test_two text;");
|
||||
executeNet("ALTER TABLE %s ADD test_two text;");
|
||||
|
||||
setGuardrail(false);
|
||||
assertFails("ALTER TABLE %s ADD test_three text;", "changing columns");
|
||||
assertFailQuery("ALTER TABLE %s ADD test_three text");
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm the guardrail appropriately catches the ALTER DROP case on a column
|
||||
* @throws Throwable
|
||||
*/
|
||||
@Test
|
||||
public void testAppliesToAlterDropColumn() throws Throwable
|
||||
public void testAppliesToAlterDropColumn()
|
||||
{
|
||||
setGuardrail(true);
|
||||
assertValid("ALTER TABLE %s ADD test_one text;");
|
||||
executeNet("ALTER TABLE %s ADD test_one text;");
|
||||
|
||||
setGuardrail(false);
|
||||
assertFails("ALTER TABLE %s DROP test_one", "changing columns");
|
||||
assertFailQuery("ALTER TABLE %s DROP test_one");
|
||||
|
||||
setGuardrail(true);
|
||||
assertValid("ALTER TABLE %s DROP test_one");
|
||||
executeNet("ALTER TABLE %s DROP test_one");
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm the guardrail appropriately catches the ALTER RENAME case on a column
|
||||
* @throws Throwable
|
||||
*/
|
||||
@Test
|
||||
public void testAppliesToAlterRenameColumn() throws Throwable
|
||||
public void testAppliesToAlterRenameColumn()
|
||||
{
|
||||
setGuardrail(false);
|
||||
assertFails("ALTER TABLE %s RENAME c TO renamed_c", "changing columns");
|
||||
|
||||
setGuardrail(false);
|
||||
assertFailQuery("ALTER TABLE %s RENAME c TO renamed_c");
|
||||
|
||||
setGuardrail(true);
|
||||
assertValid("ALTER TABLE %s RENAME c TO renamed_c");
|
||||
executeNet("ALTER TABLE %s RENAME c TO renamed_c");
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm we can always alter properties via the options map regardless of guardrail state
|
||||
* @throws Throwable
|
||||
*/
|
||||
@Test
|
||||
public void testAlterViaMapAlwaysWorks() throws Throwable
|
||||
private void assertFailQuery(String query)
|
||||
{
|
||||
setGuardrail(false);
|
||||
assertValid("ALTER TABLE %s WITH compression = { 'class' : 'SnappyCompressor', 'chunk_length_in_kb' : 32 };");
|
||||
|
||||
setGuardrail(true);
|
||||
assertValid("ALTER TABLE %s WITH compression = { 'class' : 'SnappyCompressor', 'chunk_length_in_kb' : 32 };");
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm the other form of ALTER TABLE property map changing always works regardless of guardrail state
|
||||
* @throws Throwable
|
||||
*/
|
||||
@Test
|
||||
public void testAlterOptionsAlwaysWorks() throws Throwable
|
||||
{
|
||||
setGuardrail(true);
|
||||
assertValid("ALTER TABLE %s WITH GC_GRACE_SECONDS = 456; ");
|
||||
|
||||
setGuardrail(false);
|
||||
assertValid("ALTER TABLE %s WITH GC_GRACE_SECONDS = 123; ");
|
||||
assertThatThrownBy(() -> executeNet(query))
|
||||
.isInstanceOf(InvalidQueryException.class)
|
||||
.hasMessageContaining(DDL_ERROR_MSG);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,210 @@
|
|||
/*
|
||||
* 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.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.datastax.driver.core.exceptions.InvalidQueryException;
|
||||
import org.apache.cassandra.cql3.QueryOptions;
|
||||
import org.apache.cassandra.cql3.QueryProcessor;
|
||||
import org.apache.cassandra.cql3.statements.AuthenticationStatement;
|
||||
import org.apache.cassandra.cql3.statements.AuthorizationStatement;
|
||||
import org.apache.cassandra.service.QueryState;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.Mockito.doCallRealMethod;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
public class GuardrailDCLEnabledTest extends GuardrailTester
|
||||
{
|
||||
private static final String DCL_ERROR_MSG = "DCL statement is not allowed";
|
||||
private static final String DCL_TEST_NEW_USER = "dcltest";
|
||||
|
||||
private void setGuardrail(boolean enabled)
|
||||
{
|
||||
Guardrails.instance.setDCLEnabled(enabled);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void beforeGuardrailTest() throws Throwable
|
||||
{
|
||||
super.beforeGuardrailTest();
|
||||
useSuperUser();
|
||||
// need permission on roles to test dcl queries
|
||||
executeNet(format("GRANT ALL ON ALL ROLES TO %s", USERNAME));
|
||||
useUser(USERNAME, PASSWORD);
|
||||
createTable(KEYSPACE, "CREATE TABLE IF NOT EXISTS %s (k INT, c INT, v TEXT, PRIMARY KEY(k, c))");
|
||||
}
|
||||
|
||||
@After
|
||||
public void afterTest()
|
||||
{
|
||||
setGuardrail(true);
|
||||
executeNet(dropRole(DCL_TEST_NEW_USER));
|
||||
dropTable("DROP TABLE IF EXISTS %s");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCannotCreateRoleWhileFeatureDisabled() throws Throwable
|
||||
{
|
||||
setGuardrail(false);
|
||||
shouldFailWithDCLErrorMsg(getCreateRoleCQL(DCL_TEST_NEW_USER));
|
||||
// no role is created
|
||||
assertEmpty(execute(format("SELECT * FROM system_auth.roles WHERE role='%s'",
|
||||
DCL_TEST_NEW_USER)));
|
||||
|
||||
setGuardrail(true);
|
||||
executeNet(getCreateRoleCQL(DCL_TEST_NEW_USER));
|
||||
// role is created
|
||||
assertRowCount(execute(format("SELECT * FROM system_auth.roles WHERE role='%s'",
|
||||
DCL_TEST_NEW_USER)),
|
||||
1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCannotDropRoleWhileFeatureDisabled() throws Throwable
|
||||
{
|
||||
executeNet(getCreateRoleCQL(DCL_TEST_NEW_USER));
|
||||
setGuardrail(false);
|
||||
shouldFailWithDCLErrorMsg(dropRole(DCL_TEST_NEW_USER));
|
||||
// role is not dropped
|
||||
assertRowCount(execute(format("SELECT * FROM system_auth.roles WHERE role='%s'",
|
||||
DCL_TEST_NEW_USER)),
|
||||
1);
|
||||
|
||||
setGuardrail(true);
|
||||
executeNet(dropRole(DCL_TEST_NEW_USER));
|
||||
// role is dropped
|
||||
assertEmpty(execute(format("SELECT * FROM system_auth.roles WHERE role='%s'",
|
||||
DCL_TEST_NEW_USER)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCannotListRoleWhileFeatureDisabled()
|
||||
{
|
||||
setGuardrail(false);
|
||||
shouldFailWithDCLErrorMsg("LIST ROLES");
|
||||
shouldFailWithDCLErrorMsg(format("LIST ALL PERMISSIONS OF %s", USERNAME));
|
||||
shouldFailWithDCLErrorMsg("LIST USERS");
|
||||
|
||||
setGuardrail(true);
|
||||
executeNet("LIST ROLES");
|
||||
executeNet(format("LIST ALL PERMISSIONS OF %s", USERNAME));
|
||||
executeNet("LIST USERS");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCannotGrantPermissionWhileFeatureDisabled() throws Throwable
|
||||
{
|
||||
executeNet(getCreateRoleCQL(DCL_TEST_NEW_USER));
|
||||
|
||||
setGuardrail(false);
|
||||
shouldFailWithDCLErrorMsg(getGrantPermissionCQL(DCL_TEST_NEW_USER, KEYSPACE, currentTable()));
|
||||
// DCL_TEST_NEW_USER doesn't get permission
|
||||
assertEmpty(execute(format("SELECT * FROM system_auth.role_permissions WHERE role='%s' AND resource='data/%s/%s'",
|
||||
DCL_TEST_NEW_USER, KEYSPACE, currentTable())));
|
||||
|
||||
setGuardrail(true);
|
||||
executeNet(getGrantPermissionCQL(DCL_TEST_NEW_USER, KEYSPACE, currentTable()));
|
||||
assertRowCount(execute(format("SELECT * FROM system_auth.role_permissions WHERE role='%s' AND resource='data/%s/%s'",
|
||||
DCL_TEST_NEW_USER, KEYSPACE, currentTable())),
|
||||
1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCannotRevokePermissionWhileFeatureDisabled() throws Throwable
|
||||
{
|
||||
executeNet(getCreateRoleCQL(DCL_TEST_NEW_USER));
|
||||
executeNet(getGrantPermissionCQL(DCL_TEST_NEW_USER, KEYSPACE, currentTable()));
|
||||
|
||||
setGuardrail(false);
|
||||
shouldFailWithDCLErrorMsg(getRevokePermissionCQL(DCL_TEST_NEW_USER, KEYSPACE, currentTable()));
|
||||
// DCL_TEST_NEW_USER permission wasn't revoked on KEYSPACE
|
||||
assertRowCount(execute(format("SELECT * FROM system_auth.role_permissions WHERE role='%s' AND resource='data/%s/%s'",
|
||||
DCL_TEST_NEW_USER, KEYSPACE, currentTable())),
|
||||
1);
|
||||
|
||||
setGuardrail(true);
|
||||
executeNet(getRevokePermissionCQL(DCL_TEST_NEW_USER, KEYSPACE, currentTable()));
|
||||
assertEmpty(execute(format("SELECT * FROM system_auth.role_permissions WHERE role='%s' AND resource='data/%s/%s'",
|
||||
DCL_TEST_NEW_USER, KEYSPACE, currentTable())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMockDCLStatementsWhileFeaturesDisabled()
|
||||
{
|
||||
AuthorizationStatement authorizationStatement = mock(AuthorizationStatement.class);
|
||||
AuthenticationStatement authenticationStatement = mock(AuthenticationStatement.class);
|
||||
QueryState queryState = mock(QueryState.class);
|
||||
|
||||
doReturn(userClientState).when(queryState).getClientState();
|
||||
doCallRealMethod().when(authorizationStatement).isDCLStatement();
|
||||
doCallRealMethod().when(authenticationStatement).isDCLStatement();
|
||||
QueryProcessor qp = QueryProcessor.instance;
|
||||
|
||||
setGuardrail(false);
|
||||
|
||||
assertThatThrownBy(() -> qp.processStatement(authorizationStatement, queryState, QueryOptions.DEFAULT, 0L))
|
||||
.isInstanceOf(GuardrailViolatedException.class);
|
||||
|
||||
verify(authorizationStatement).isDCLStatement();
|
||||
|
||||
assertThatThrownBy(() -> qp.processStatement(authenticationStatement, queryState, QueryOptions.DEFAULT, 0L))
|
||||
.isInstanceOf(GuardrailViolatedException.class);
|
||||
|
||||
verify(authenticationStatement).isDCLStatement();
|
||||
|
||||
setGuardrail(true);
|
||||
qp.processStatement(authorizationStatement, queryState, QueryOptions.DEFAULT, 0L);
|
||||
qp.processStatement(authenticationStatement, queryState, QueryOptions.DEFAULT, 0L);
|
||||
}
|
||||
|
||||
private void shouldFailWithDCLErrorMsg(String query)
|
||||
{
|
||||
assertThatThrownBy(() -> executeNet(query))
|
||||
.isInstanceOf(InvalidQueryException.class)
|
||||
.hasMessageContaining(DCL_ERROR_MSG);
|
||||
}
|
||||
|
||||
private static String getCreateRoleCQL(String role)
|
||||
{
|
||||
return format("CREATE ROLE IF NOT EXISTS %s WITH PASSWORD = 'test'",
|
||||
role);
|
||||
}
|
||||
|
||||
private static String getGrantPermissionCQL(String role, String ks, String tbl)
|
||||
{
|
||||
return format("GRANT ALL PERMISSIONS ON %s.%s TO %s;", ks, tbl, role);
|
||||
}
|
||||
|
||||
private static String getRevokePermissionCQL(String role, String ks, String tbl)
|
||||
{
|
||||
return format("REVOKE ALL ON %s.%s FROM %s;", ks, tbl, role);
|
||||
}
|
||||
|
||||
private static String dropRole(String role)
|
||||
{
|
||||
return format("DROP ROLE IF EXISTS %s", role);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,338 @@
|
|||
/*
|
||||
* 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.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.datastax.driver.core.exceptions.InvalidQueryException;
|
||||
import org.apache.cassandra.cql3.QueryOptions;
|
||||
import org.apache.cassandra.cql3.QueryProcessor;
|
||||
import org.apache.cassandra.cql3.statements.schema.AlterSchemaStatement;
|
||||
import org.apache.cassandra.schema.SchemaConstants;
|
||||
import org.apache.cassandra.schema.SchemaKeyspaceTables;
|
||||
import org.apache.cassandra.service.QueryState;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.Mockito.doCallRealMethod;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
public class GuardrailDDLEnabledTest extends GuardrailTester
|
||||
{
|
||||
private static final String TEST_KS = "ddlks";
|
||||
private static final String TEST_TABLE = "ddltbl";
|
||||
private static final String TEST_VIEW = "ddlview";
|
||||
private static final String DDL_ERROR_MSG = "DDL statement is not allowed";
|
||||
|
||||
private void setGuardrail(boolean enabled)
|
||||
{
|
||||
Guardrails.instance.setDDLEnabled(enabled);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void beforeGuardrailTest() throws Throwable
|
||||
{
|
||||
super.beforeGuardrailTest();
|
||||
// grant current user permission to all keyspaces
|
||||
useSuperUser();
|
||||
executeNet(format("GRANT ALL ON ALL KEYSPACES TO %s", USERNAME));
|
||||
useUser(USERNAME, PASSWORD);
|
||||
}
|
||||
|
||||
@After
|
||||
public void afterTest()
|
||||
{
|
||||
setGuardrail(true);
|
||||
executeNet(getDropViewCQL(TEST_KS, TEST_VIEW));
|
||||
executeNet(getDropKeyspaceCQL(TEST_KS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCannotCreateKeyspaceWhileFeatureDisabled() throws Throwable
|
||||
{
|
||||
setGuardrail(false);
|
||||
shouldFailWithDDLErrorMsg(getCreateKeyspaceCQL(TEST_KS));
|
||||
// No new keyspace should be created
|
||||
assertEmpty(execute(getSystemSchemaKeyspaceCQL(TEST_KS)));
|
||||
|
||||
setGuardrail(true);
|
||||
executeNet(getCreateKeyspaceCQL(TEST_KS));
|
||||
assertRowCount(execute(getSystemSchemaKeyspaceCQL(TEST_KS)), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCannotCreateTableWhileFeatureDisabled() throws Throwable
|
||||
{
|
||||
executeNet(getCreateKeyspaceCQL(TEST_KS));
|
||||
|
||||
setGuardrail(false);
|
||||
shouldFailWithDDLErrorMsg(getCreateTableCQL(TEST_KS, TEST_TABLE));
|
||||
// No new table should be created
|
||||
assertEmpty(execute(getSystemSchemaTableCQL(TEST_KS, TEST_TABLE)));
|
||||
|
||||
setGuardrail(true);
|
||||
executeNet(getCreateTableCQL(TEST_KS, TEST_TABLE));
|
||||
assertRowCount(execute(getSystemSchemaTableCQL(TEST_KS, TEST_TABLE)), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCannotCreateViewWhileFeatureDisabled() throws Throwable
|
||||
{
|
||||
executeNet(getCreateKeyspaceCQL(TEST_KS));
|
||||
executeNet(getCreateTableCQL(TEST_KS, TEST_TABLE));
|
||||
|
||||
setGuardrail(false);
|
||||
shouldFailWithDDLErrorMsg(getCreateViewCQL(TEST_KS, TEST_VIEW));
|
||||
// No new view should be created
|
||||
assertEmpty(execute(getSystemSchemaViewCQL(TEST_KS, TEST_VIEW)));
|
||||
|
||||
setGuardrail(true);
|
||||
executeNet(getCreateViewCQL(TEST_KS, TEST_VIEW));
|
||||
assertRowCount(execute(getSystemSchemaViewCQL(TEST_KS, TEST_VIEW)), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCannotDropKeyspaceWhileFeatureDisabled() throws Throwable
|
||||
{
|
||||
executeNet(getCreateKeyspaceCQL(TEST_KS));
|
||||
|
||||
setGuardrail(false);
|
||||
shouldFailWithDDLErrorMsg(getDropKeyspaceCQL(TEST_KS));
|
||||
assertRowCount(execute(getSystemSchemaKeyspaceCQL(TEST_KS)), 1);
|
||||
|
||||
setGuardrail(true);
|
||||
executeNet(getDropKeyspaceCQL(TEST_KS));
|
||||
assertEmpty(execute(getSystemSchemaKeyspaceCQL(TEST_KS)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCannotDropTableWhileFeatureDisabled() throws Throwable
|
||||
{
|
||||
executeNet(getCreateKeyspaceCQL(TEST_KS));
|
||||
executeNet(getCreateTableCQL(TEST_KS, TEST_TABLE));
|
||||
|
||||
setGuardrail(false);
|
||||
shouldFailWithDDLErrorMsg(getDropTableCQL(TEST_KS, TEST_TABLE));
|
||||
assertRowCount(execute(getSystemSchemaTableCQL(TEST_KS, TEST_TABLE)), 1);
|
||||
|
||||
setGuardrail(true);
|
||||
executeNet(getDropTableCQL(TEST_KS, TEST_TABLE));
|
||||
assertEmpty(execute(getSystemSchemaTableCQL(TEST_KS, TEST_TABLE)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCannotDropViewWhileFeatureDisabled() throws Throwable
|
||||
{
|
||||
executeNet(getCreateKeyspaceCQL(TEST_KS));
|
||||
executeNet(getCreateTableCQL(TEST_KS, TEST_TABLE));
|
||||
executeNet(getCreateViewCQL(TEST_KS, TEST_VIEW));
|
||||
|
||||
setGuardrail(false);
|
||||
shouldFailWithDDLErrorMsg(getDropViewCQL(TEST_KS, TEST_VIEW));
|
||||
assertRowCount(execute(getSystemSchemaViewCQL(TEST_KS, TEST_VIEW)), 1);
|
||||
|
||||
setGuardrail(true);
|
||||
executeNet(getDropViewCQL(TEST_KS, TEST_VIEW));
|
||||
assertEmpty(execute(getSystemSchemaViewCQL(TEST_KS, TEST_VIEW)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCannotDropColumnWhileFeatureDisabled() throws Throwable
|
||||
{
|
||||
executeNet(getCreateKeyspaceCQL(TEST_KS));
|
||||
executeNet(getCreateTableCQL(TEST_KS, TEST_TABLE));
|
||||
setGuardrail(false);
|
||||
// table have column col1
|
||||
assertRowCount(execute(format("SELECT * FROM %s.%s WHERE keyspace_name='%s' AND table_name='%s' AND column_name='col1'",
|
||||
SchemaConstants.SCHEMA_KEYSPACE_NAME,
|
||||
SchemaKeyspaceTables.COLUMNS,
|
||||
TEST_KS,
|
||||
TEST_TABLE)), 1);
|
||||
shouldFailWithDDLErrorMsg(format("ALTER TABLE %s.%s DROP IF EXISTS col1", TEST_KS, TEST_TABLE));
|
||||
// column col1 should not be dropped
|
||||
assertRowCount(execute(format("SELECT * FROM %s.%s WHERE keyspace_name='%s' AND table_name='%s' AND column_name='col1'",
|
||||
SchemaConstants.SCHEMA_KEYSPACE_NAME,
|
||||
SchemaKeyspaceTables.COLUMNS,
|
||||
TEST_KS,
|
||||
TEST_TABLE)), 1);
|
||||
|
||||
setGuardrail(true);
|
||||
executeNet(format("ALTER TABLE %s.%s DROP IF EXISTS col1", TEST_KS, TEST_TABLE));
|
||||
assertEmpty(execute(format("SELECT * FROM %s.%s WHERE keyspace_name='%s' AND table_name='%s' AND column_name='col1'",
|
||||
SchemaConstants.SCHEMA_KEYSPACE_NAME,
|
||||
SchemaKeyspaceTables.COLUMNS,
|
||||
TEST_KS,
|
||||
TEST_TABLE)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCannotAlterKeyspaceWhileFeatureDisabled() throws Throwable
|
||||
{
|
||||
executeNet(getCreateKeyspaceCQL(TEST_KS));
|
||||
setGuardrail(false);
|
||||
// keyspace should have durable_write=true by default
|
||||
assertEmpty(execute(format("SELECT * FROM %s.%s WHERE keyspace_name='%s' AND durable_writes=false ALLOW FILTERING",
|
||||
SchemaConstants.SCHEMA_KEYSPACE_NAME,
|
||||
SchemaKeyspaceTables.KEYSPACES,
|
||||
TEST_KS)));
|
||||
shouldFailWithDDLErrorMsg(format("ALTER KEYSPACE %s WITH durable_writes=false", TEST_KS));
|
||||
// keyspace should still have durable_write=true
|
||||
assertEmpty(execute(format("SELECT * FROM %s.%s WHERE keyspace_name='%s' AND durable_writes=false ALLOW FILTERING",
|
||||
SchemaConstants.SCHEMA_KEYSPACE_NAME,
|
||||
SchemaKeyspaceTables.KEYSPACES,
|
||||
TEST_KS)));
|
||||
|
||||
setGuardrail(true);
|
||||
executeNet(format("ALTER KEYSPACE %s WITH durable_writes=false", TEST_KS));
|
||||
assertRowCount(execute(format("SELECT * FROM %s.%s WHERE keyspace_name='%s' AND durable_writes=false ALLOW FILTERING",
|
||||
SchemaConstants.SCHEMA_KEYSPACE_NAME,
|
||||
SchemaKeyspaceTables.KEYSPACES,
|
||||
TEST_KS)), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCannotAlterTableWhileFeatureDisabled() throws Throwable
|
||||
{
|
||||
executeNet(getCreateKeyspaceCQL(TEST_KS));
|
||||
executeNet(getCreateTableCQL(TEST_KS, TEST_TABLE));
|
||||
setGuardrail(false);
|
||||
// table doesn't have comment
|
||||
assertEmpty(execute(format("SELECT * FROM %s.%s WHERE keyspace_name='%s' AND table_name='%s' AND comment='test' ALLOW FILTERING",
|
||||
SchemaConstants.SCHEMA_KEYSPACE_NAME,
|
||||
SchemaKeyspaceTables.TABLES,
|
||||
TEST_KS, TEST_TABLE)));
|
||||
shouldFailWithDDLErrorMsg(format("ALTER TABLE %s.%s WITH comment='test'", TEST_KS, TEST_TABLE));
|
||||
// table should not have comment
|
||||
assertEmpty(execute(format("SELECT * FROM %s.%s WHERE keyspace_name='%s' AND table_name='%s' AND comment='test' ALLOW FILTERING",
|
||||
SchemaConstants.SCHEMA_KEYSPACE_NAME,
|
||||
SchemaKeyspaceTables.TABLES,
|
||||
TEST_KS, TEST_TABLE)));
|
||||
|
||||
setGuardrail(true);
|
||||
executeNet(format("ALTER TABLE %s.%s WITH comment='test'", TEST_KS, TEST_TABLE));
|
||||
assertRowCount(execute(format("SELECT * FROM %s.%s WHERE keyspace_name='%s' AND table_name='%s' AND comment='test' ALLOW FILTERING",
|
||||
SchemaConstants.SCHEMA_KEYSPACE_NAME,
|
||||
SchemaKeyspaceTables.TABLES,
|
||||
TEST_KS, TEST_TABLE)), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCannotAddColumnWhileFeatureDisabled() throws Throwable
|
||||
{
|
||||
executeNet(getCreateKeyspaceCQL(TEST_KS));
|
||||
executeNet(getCreateTableCQL(TEST_KS, TEST_TABLE));
|
||||
setGuardrail(false);
|
||||
// table doesn't have new column col3
|
||||
assertEmpty(execute(format("SELECT * FROM %s.%s WHERE keyspace_name='%s' AND table_name='%s' AND column_name='col3'",
|
||||
SchemaConstants.SCHEMA_KEYSPACE_NAME,
|
||||
SchemaKeyspaceTables.COLUMNS,
|
||||
TEST_KS, TEST_TABLE)));
|
||||
shouldFailWithDDLErrorMsg(format("ALTER TABLE %s.%s ADD col3 text", TEST_KS, TEST_TABLE));
|
||||
// table should not have new column col3
|
||||
assertEmpty(execute(format("SELECT * FROM %s.%s WHERE keyspace_name='%s' AND table_name='%s' AND column_name='col3'",
|
||||
SchemaConstants.SCHEMA_KEYSPACE_NAME,
|
||||
SchemaKeyspaceTables.COLUMNS,
|
||||
TEST_KS, TEST_TABLE)));
|
||||
|
||||
setGuardrail(true);
|
||||
executeNet(format("ALTER TABLE %s.%s ADD col3 text", TEST_KS, TEST_TABLE));
|
||||
// table should not have new column col3
|
||||
assertRowCount(execute(format("SELECT * FROM %s.%s WHERE keyspace_name='%s' AND table_name='%s' AND column_name='col3'",
|
||||
SchemaConstants.SCHEMA_KEYSPACE_NAME,
|
||||
SchemaKeyspaceTables.COLUMNS,
|
||||
TEST_KS, TEST_TABLE)), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMockAlterSchemaStatementsWhileFeaturesDisabled()
|
||||
{
|
||||
AlterSchemaStatement alterSchemaStatement = mock(AlterSchemaStatement.class);
|
||||
QueryState queryState = mock(QueryState.class);
|
||||
|
||||
doReturn(userClientState).when(queryState).getClientState();
|
||||
doCallRealMethod().when(alterSchemaStatement).isDDLStatement();
|
||||
QueryProcessor qp = QueryProcessor.instance;
|
||||
|
||||
setGuardrail(false);
|
||||
|
||||
assertThatThrownBy(() -> qp.processStatement(alterSchemaStatement, queryState, QueryOptions.DEFAULT, 0L))
|
||||
.isInstanceOf(GuardrailViolatedException.class);
|
||||
|
||||
verify(alterSchemaStatement).isDDLStatement();
|
||||
|
||||
setGuardrail(true);
|
||||
qp.processStatement(alterSchemaStatement, queryState, QueryOptions.DEFAULT, 0L);
|
||||
}
|
||||
|
||||
private void shouldFailWithDDLErrorMsg(String query)
|
||||
{
|
||||
assertThatThrownBy(() -> executeNet(query))
|
||||
.isInstanceOf(InvalidQueryException.class)
|
||||
.hasMessageContaining(DDL_ERROR_MSG);
|
||||
}
|
||||
|
||||
private String getCreateKeyspaceCQL(String ks)
|
||||
{
|
||||
return format("CREATE KEYSPACE IF NOT EXISTS %s WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '1'}", ks);
|
||||
}
|
||||
|
||||
private String getDropKeyspaceCQL(String ks)
|
||||
{
|
||||
return format("DROP KEYSPACE IF EXISTS %s", ks);
|
||||
}
|
||||
|
||||
private String getCreateTableCQL(String ks, String table)
|
||||
{
|
||||
return format("CREATE TABLE IF NOT EXISTS %s.%s (key text PRIMARY KEY, col1 text, col2 text)", ks, table);
|
||||
}
|
||||
|
||||
private String getDropTableCQL(String ks, String table)
|
||||
{
|
||||
return format("DROP TABLE IF EXISTS %s.%s", ks, table);
|
||||
}
|
||||
|
||||
private String getCreateViewCQL(String ks, String table)
|
||||
{
|
||||
return format("CREATE MATERIALIZED VIEW IF NOT EXISTS %s.%s AS SELECT key FROM %s.%s WHERE key IS NOT NULL PRIMARY KEY (key)", ks, table, TEST_KS, TEST_TABLE);
|
||||
}
|
||||
|
||||
private String getDropViewCQL(String ks, String view)
|
||||
{
|
||||
return format("DROP MATERIALIZED VIEW IF EXISTS %s.%s", ks, view);
|
||||
}
|
||||
|
||||
private String getSystemSchemaKeyspaceCQL(String ks)
|
||||
{
|
||||
return format("SELECT * FROM %s.%s WHERE keyspace_name='%s'", SchemaConstants.SCHEMA_KEYSPACE_NAME, SchemaKeyspaceTables.KEYSPACES, ks);
|
||||
}
|
||||
|
||||
private String getSystemSchemaTableCQL(String ks, String table)
|
||||
{
|
||||
return format("SELECT * FROM %s.%s WHERE keyspace_name='%s' AND table_name='%s'", SchemaConstants.SCHEMA_KEYSPACE_NAME, SchemaKeyspaceTables.TABLES, ks, table);
|
||||
}
|
||||
|
||||
private String getSystemSchemaViewCQL(String ks, String view)
|
||||
{
|
||||
return format("SELECT * FROM %s.%s WHERE keyspace_name='%s' AND view_name='%s'", SchemaConstants.SCHEMA_KEYSPACE_NAME, SchemaKeyspaceTables.VIEWS, ks, view);
|
||||
}
|
||||
}
|
||||
|
|
@ -86,8 +86,8 @@ public abstract class GuardrailTester extends CQLTester
|
|||
// previously created table, which is not what we want).
|
||||
protected static final String FAIL_TABLE = "abort_table_creation_test";
|
||||
|
||||
private static final String USERNAME = "guardrail_user";
|
||||
private static final String PASSWORD = "guardrail_password";
|
||||
protected static final String USERNAME = "guardrail_user";
|
||||
protected static final String PASSWORD = "guardrail_password";
|
||||
|
||||
protected static ClientState systemClientState, userClientState, superClientState;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue