blob: 47350a0a90bf5007be44ea6dbde23c028d56f5ab [file] [log] [blame]
/*
* 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.cql3.functions.masking;
import java.util.Arrays;
import java.util.Collection;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.apache.cassandra.cql3.CQL3Type;
import static java.lang.String.format;
import static org.apache.cassandra.cql3.CQL3Type.Native.ASCII;
import static org.apache.cassandra.cql3.CQL3Type.Native.BIGINT;
import static org.apache.cassandra.cql3.CQL3Type.Native.BLOB;
import static org.apache.cassandra.cql3.CQL3Type.Native.INT;
import static org.apache.cassandra.cql3.CQL3Type.Native.TEXT;
import static org.apache.cassandra.cql3.CQL3Type.Native.VARINT;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
/**
* {@link ColumnMaskTester} for masks using functions that might return values with a type different to the type of the
* masked column. Those queries should fail.
*/
@RunWith(Parameterized.class)
public class ColumnMaskWithTypeAlteringFunctionTest extends ColumnMaskTester
{
/** The column mask as expressed in CQL statements right after the {@code MASKED WITH} keywords. */
@Parameterized.Parameter
public String mask;
/** The type of the masked column. */
@Parameterized.Parameter(1)
public CQL3Type.Native type;
/** The type returned by the tested masking function. */
@Parameterized.Parameter(2)
public CQL3Type returnedType;
private boolean shouldSucceed;
private String errorMessage;
@Parameterized.Parameters(name = "mask={0} type={1}")
public static Collection<Object[]> options()
{
return Arrays.asList(new Object[][]{
{ "mask_default()", INT, INT },
{ "mask_default()", TEXT, TEXT },
{ "mask_null()", INT, INT },
{ "mask_null()", TEXT, TEXT },
{ "mask_hash()", INT, BLOB },
{ "mask_hash('SHA-512')", INT, BLOB },
{ "mask_inner(1,2)", TEXT, TEXT },
{ "mask_outer(1,2)", TEXT, TEXT },
{ "mask_replace(0)", INT, INT },
{ "mask_replace(0)", BIGINT, BIGINT },
{ "mask_replace(0)", VARINT, VARINT },
{ "mask_replace('redacted')", ASCII, ASCII },
{ "mask_replace('redacted')", TEXT, TEXT } });
}
@Before
public void setupExpectedResults()
{
shouldSucceed = returnedType == type;
errorMessage = shouldSucceed ? null : format("Masking function %s return type is %s.", mask, returnedType);
}
@Test
public void testTypeAlteringFunctionOnCreateTable()
{
testOnCreateTable("CREATE TABLE %s.%s (k int PRIMARY KEY, v %s MASKED WITH %s)");
testOnCreateTable("CREATE TABLE %s.%s (k %s MASKED WITH %s PRIMARY KEY, v int)");
testOnCreateTable("CREATE TABLE %s.%s (k1 int, k2 %s MASKED WITH %s, PRIMARY KEY((k1, k2)))");
testOnCreateTable("CREATE TABLE %s.%s (k int, c %s MASKED WITH %s, PRIMARY KEY(k, c)) WITH CLUSTERING ORDER BY (c ASC)");
testOnCreateTable("CREATE TABLE %s.%s (k int, c %s MASKED WITH %s, PRIMARY KEY(k, c)) WITH CLUSTERING ORDER BY (c DESC)");
testOnCreateTable("CREATE TABLE %s.%s (k int, c int, s %s STATIC MASKED WITH %s, PRIMARY KEY(k, c))");
}
private void testOnCreateTable(String query)
{
String formattedQuery = format(query, KEYSPACE, createTableName(), type, mask);
if (shouldSucceed)
createTable(formattedQuery);
else
assertThatThrownBy(() -> execute(formattedQuery)).hasMessageContaining(errorMessage);
}
@Test
public void testTypeAlteringFunctionOnMaskColumn()
{
testOnAlterColumn("CREATE TABLE %s.%s (k int PRIMARY KEY, v %s)",
"ALTER TABLE %s.%s ALTER v MASKED WITH %s");
testOnAlterColumn("CREATE TABLE %s.%s (k %s PRIMARY KEY, v int)",
"ALTER TABLE %s.%s ALTER k MASKED WITH %s");
testOnAlterColumn("CREATE TABLE %s.%s (k1 int, k2 %s, PRIMARY KEY((k1, k2)))",
"ALTER TABLE %s.%s ALTER k2 MASKED WITH %s");
testOnAlterColumn("CREATE TABLE %s.%s (k int, c %s, PRIMARY KEY(k, c)) WITH CLUSTERING ORDER BY (c ASC)",
"ALTER TABLE %s.%s ALTER c MASKED WITH %s");
testOnAlterColumn("CREATE TABLE %s.%s (k int, c %s, PRIMARY KEY(k, c)) WITH CLUSTERING ORDER BY (c DESC)",
"ALTER TABLE %s.%s ALTER c MASKED WITH %s");
testOnAlterColumn("CREATE TABLE %s.%s (k int, c int, s %s STATIC, PRIMARY KEY(k, c))",
"ALTER TABLE %s.%s ALTER s MASKED WITH %s");
}
private void testOnAlterColumn(String createQuery, String alterQuery)
{
String table = createTableName();
createTable(format(createQuery, KEYSPACE, table, type));
String formattedQuery = format(alterQuery, KEYSPACE, table, mask);
if (shouldSucceed)
alterTable(formattedQuery);
else
assertThatThrownBy(() -> execute(formattedQuery)).hasMessageContaining(errorMessage);
}
@Test
public void testTypeAlteringFunctionOnAddColumn()
{
String table = createTable(format("CREATE TABLE %%s (k int PRIMARY KEY, v %s)", type));
String query = format("ALTER TABLE %s.%s ADD n %s MASKED WITH %s", KEYSPACE, table, type, mask);
if (shouldSucceed)
alterTable(query);
else
assertThatThrownBy(() -> execute(query)).hasMessageContaining(errorMessage);
}
}