From d74ed4b78886c0202f27ebf20cd2f8684f6cbcaa Mon Sep 17 00:00:00 2001 From: Stefan Podkowinski Date: Mon, 31 Jul 2017 14:15:51 +0200 Subject: [PATCH] Log warn message until legacy auth tables have been migrated patch by S. Podkowinski and R. Stupp; reviewed by Robert Stupp for CASSANDRA-13371 --- CHANGES.txt | 1 + .../cassandra/service/StartupChecks.java | 34 ++++++- .../cassandra/service/LegacyAuthFailTest.java | 89 +++++++++++++++++++ 3 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 test/unit/org/apache/cassandra/service/LegacyAuthFailTest.java diff --git a/CHANGES.txt b/CHANGES.txt index 4038ac718d..7e518ed89f 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 3.0.15 + * Log warn message until legacy auth tables have been migrated (CASSANDRA-13371) * Fix incorrect [2.1 <- 3.0] serialization of counter cells created in 2.0 (CASSANDRA-13691) * Fix invalid writetime for null cells (CASSANDRA-13711) * Fix ALTER TABLE statement to atomically propagate changes to the table and its MVs (CASSANDRA-12952) diff --git a/src/java/org/apache/cassandra/service/StartupChecks.java b/src/java/org/apache/cassandra/service/StartupChecks.java index 19b6620aeb..e9f99ee776 100644 --- a/src/java/org/apache/cassandra/service/StartupChecks.java +++ b/src/java/org/apache/cassandra/service/StartupChecks.java @@ -23,22 +23,29 @@ import java.io.IOException; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; import java.util.*; +import java.util.stream.Collectors; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.apache.cassandra.auth.AuthKeyspace; import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.config.Config; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.config.Schema; +import org.apache.cassandra.cql3.QueryProcessor; +import org.apache.cassandra.cql3.UntypedResultSet; import org.apache.cassandra.db.*; import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.exceptions.StartupException; import org.apache.cassandra.io.sstable.Descriptor; import org.apache.cassandra.io.util.FileUtils; +import org.apache.cassandra.schema.SchemaKeyspace; import org.apache.cassandra.utils.*; /** @@ -81,7 +88,8 @@ public class StartupChecks checkSSTablesFormat, checkSystemKeyspaceState, checkDatacenter, - checkRack); + checkRack, + checkLegacyAuthTables); public StartupChecks withDefaultTests() { @@ -403,4 +411,28 @@ public class StartupChecks } } }; + + public static final StartupCheck checkLegacyAuthTables = () -> checkLegacyAuthTablesMessage().ifPresent(logger::warn); + + static final Set LEGACY_AUTH_TABLES = ImmutableSet.of("credentials", "users", "permissions"); + + @VisibleForTesting + static Optional checkLegacyAuthTablesMessage() + { + List existing = new ArrayList<>(LEGACY_AUTH_TABLES).stream().filter((legacyAuthTable) -> + { + UntypedResultSet result = QueryProcessor.executeOnceInternal(String.format("SELECT table_name FROM %s.%s WHERE keyspace_name='%s' AND table_name='%s'", + SchemaKeyspace.NAME, + "tables", + AuthKeyspace.NAME, + legacyAuthTable)); + return result != null && !result.isEmpty(); + }).collect(Collectors.toList()); + + if (!existing.isEmpty()) + return Optional.of(String.format("Legacy auth tables %s in keyspace %s still exist and have not been properly migrated.", + Joiner.on(", ").join(existing), AuthKeyspace.NAME)); + else + return Optional.empty(); + }; } diff --git a/test/unit/org/apache/cassandra/service/LegacyAuthFailTest.java b/test/unit/org/apache/cassandra/service/LegacyAuthFailTest.java new file mode 100644 index 0000000000..079543f611 --- /dev/null +++ b/test/unit/org/apache/cassandra/service/LegacyAuthFailTest.java @@ -0,0 +1,89 @@ +/* + * 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.service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import com.google.common.base.Joiner; +import org.junit.Test; + +import org.apache.cassandra.auth.AuthKeyspace; +import org.apache.cassandra.cql3.CQLTester; + +import static java.lang.String.format; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +public class LegacyAuthFailTest extends CQLTester +{ + @Test + public void testStartupChecks() throws Throwable + { + createKeyspace(); + + List legacyTables = new ArrayList<>(StartupChecks.LEGACY_AUTH_TABLES); + + // test reporting for individual tables + for (String legacyTable : legacyTables) + { + createLegacyTable(legacyTable); + + Optional errMsg = StartupChecks.checkLegacyAuthTablesMessage(); + assertEquals(format("Legacy auth tables %s in keyspace %s still exist and have not been properly migrated.", + legacyTable, + AuthKeyspace.NAME), errMsg.get()); + dropLegacyTable(legacyTable); + } + + // test reporting of multiple existing tables + for (String legacyTable : legacyTables) + createLegacyTable(legacyTable); + + while (!legacyTables.isEmpty()) + { + Optional errMsg = StartupChecks.checkLegacyAuthTablesMessage(); + assertEquals(format("Legacy auth tables %s in keyspace %s still exist and have not been properly migrated.", + Joiner.on(", ").join(legacyTables), + AuthKeyspace.NAME), errMsg.get()); + + dropLegacyTable(legacyTables.remove(0)); + } + + // no legacy tables found + Optional errMsg = StartupChecks.checkLegacyAuthTablesMessage(); + assertFalse(errMsg.isPresent()); + } + + private void dropLegacyTable(String legacyTable) throws Throwable + { + execute(format("DROP TABLE %s.%s", AuthKeyspace.NAME, legacyTable)); + } + + private void createLegacyTable(String legacyTable) throws Throwable + { + execute(format("CREATE TABLE %s.%s (id int PRIMARY KEY, val text)", AuthKeyspace.NAME, legacyTable)); + } + + private void createKeyspace() throws Throwable + { + execute(format("CREATE KEYSPACE %s WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1}", AuthKeyspace.NAME)); + } +}