From df6ecaae1c3ac3a05c8cd0c6a9c3da58d4e86a98 Mon Sep 17 00:00:00 2001 From: Benjamin Lerer Date: Fri, 8 Oct 2021 17:32:15 +0200 Subject: [PATCH] Allow to grant permission for all tables in a keyspace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Patch by Benjamin Lerer; Review by Andres de la Peña and Ekaterina Dimitrova for CASSANDRA-17027 In some cases it is useful to prevent users to alter or drop a keyspace while allowing them to create new tables. This patch add support for a new DataResource below KEYSPACE but above TABLE. The syntax to grant permission at this level in ALL TABLES IN KEYSPACE. --- CHANGES.txt | 1 + NEWS.txt | 2 + pylib/cqlshlib/cql3handling.py | 1 + pylib/cqlshlib/test/test_cqlsh_completion.py | 46 +++ src/antlr/Parser.g | 4 +- .../auth/AuthSchemaChangeListener.java | 1 + .../cassandra/auth/CassandraRoleManager.java | 33 +- .../apache/cassandra/auth/DataResource.java | 42 ++- .../cql3/statements/CreateRoleStatement.java | 1 + .../statements/schema/AlterTypeStatement.java | 2 +- .../schema/CreateTableStatement.java | 2 +- .../schema/CreateTypeStatement.java | 2 +- .../statements/schema/DropTypeStatement.java | 2 +- .../apache/cassandra/service/ClientState.java | 5 + .../cassandra/auth/GrantAndRevokeTest.java | 302 ++++++++++++++++++ .../org/apache/cassandra/cql3/CQLTester.java | 151 ++++++++- 16 files changed, 569 insertions(+), 28 deletions(-) create mode 100644 test/unit/org/apache/cassandra/auth/GrantAndRevokeTest.java diff --git a/CHANGES.txt b/CHANGES.txt index 21118ebfa6..0bf931da6d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 4.1 + * Allow to grant permission for all tables in a keyspace (CASSANDRA-17027) * Log time spent writing keys during compaction (CASSANDRA-17037) * Make nodetool compactionstats and sstable_tasks consistent (CASSANDRA-16976) * Add metrics and logging around index summary redistribution (CASSANDRA-17036) diff --git a/NEWS.txt b/NEWS.txt index 0bec6287e7..162241cbcc 100644 --- a/NEWS.txt +++ b/NEWS.txt @@ -38,6 +38,8 @@ using the provided 'sstableupgrade' tool. New features ------------ + - A new ALL TABLES IN KEYSPACE resource has been added. It allows to grant permissions for all tables and user types + in a keyspace while preventing the user to use those permissions on the keyspace itself. - Added support for type casting in the WHERE clause components and in the values of INSERT and UPDATE statements. - Warn/abort thresholds added to read queries notifying clients when these thresholds trigger (by emitting a client warning or aborting the query). This feature is disabled by default, scheduled diff --git a/pylib/cqlshlib/cql3handling.py b/pylib/cqlshlib/cql3handling.py index 68484f5fc8..a99e7790e4 100644 --- a/pylib/cqlshlib/cql3handling.py +++ b/pylib/cqlshlib/cql3handling.py @@ -1524,6 +1524,7 @@ syntax_rules += r''' ::= ( "ALL" "KEYSPACES" ) | ( "KEYSPACE" ) + | ( "ALL" "TABLES" "IN" "KEYSPACE" ) | ( "TABLE"? ) ; diff --git a/pylib/cqlshlib/test/test_cqlsh_completion.py b/pylib/cqlshlib/test/test_cqlsh_completion.py index c898cbed04..bc8203334c 100644 --- a/pylib/cqlshlib/test/test_cqlsh_completion.py +++ b/pylib/cqlshlib/test/test_cqlsh_completion.py @@ -826,3 +826,49 @@ class TestCqlshCompletion(CqlshCompletionCase): self.trycompletions('ALTER KEYSPACE system_trac', "es WITH replication = {'class': '") self.trycompletions("ALTER KEYSPACE system_traces WITH replication = {'class': '", '', choices=['NetworkTopologyStrategy', 'SimpleStrategy']) + + def test_complete_in_grant(self): + self.trycompletions("GR", + immediate='ANT ') + self.trycompletions("GRANT ", + choices=['ALL', 'ALTER', 'AUTHORIZE', 'CREATE', 'DESCRIBE', 'DROP', 'EXECUTE', 'MODIFY', 'SELECT'], + other_choices_ok=True) + self.trycompletions("GRANT MODIFY ", + choices=['ON', 'PERMISSION']) + self.trycompletions("GRANT MODIFY P", + immediate='ERMISSION ON ') + self.trycompletions("GRANT MODIFY PERMISSION O", + immediate='N ') + self.trycompletions("GRANT MODIFY ON ", + choices=['ALL', 'KEYSPACE', 'MBEANS', 'ROLE', 'FUNCTION', 'MBEAN', 'TABLE'], + other_choices_ok=True) + self.trycompletions("GRANT MODIFY ON ALL ", + choices=['KEYSPACES', 'TABLES'], + other_choices_ok=True) + self.trycompletions("GRANT MODIFY PERMISSION ON KEY", + immediate='SPACE ') + self.trycompletions("GRANT MODIFY PERMISSION ON KEYSPACE system_tr", + immediate='aces TO ') + + def test_complete_in_revoke(self): + self.trycompletions("RE", + immediate='VOKE ') + self.trycompletions("REVOKE ", + choices=['ALL', 'ALTER', 'AUTHORIZE', 'CREATE', 'DESCRIBE', 'DROP', 'EXECUTE', 'MODIFY', 'SELECT'], + other_choices_ok=True) + self.trycompletions("REVOKE MODIFY ", + choices=['ON', 'PERMISSION']) + self.trycompletions("REVOKE MODIFY P", + immediate='ERMISSION ON ') + self.trycompletions("REVOKE MODIFY PERMISSION O", + immediate='N ') + self.trycompletions("REVOKE MODIFY PERMISSION ON ", + choices=['ALL', 'KEYSPACE', 'MBEANS', 'ROLE', 'FUNCTION', 'MBEAN', 'TABLE'], + other_choices_ok=True) + self.trycompletions("REVOKE MODIFY ON ALL ", + choices=['KEYSPACES', 'TABLES'], + other_choices_ok=True) + self.trycompletions("REVOKE MODIFY PERMISSION ON KEY", + immediate='SPACE ') + self.trycompletions("REVOKE MODIFY PERMISSION ON KEYSPACE system_tr", + immediate='aces FROM ') diff --git a/src/antlr/Parser.g b/src/antlr/Parser.g index 39b3d9fb5e..caeedde5b4 100644 --- a/src/antlr/Parser.g +++ b/src/antlr/Parser.g @@ -1118,8 +1118,8 @@ resource returns [IResource res] dataResource returns [DataResource res] : K_ALL K_KEYSPACES { $res = DataResource.root(); } | K_KEYSPACE ks = keyspaceName { $res = DataResource.keyspace($ks.id); } - | ( K_COLUMNFAMILY )? cf = columnFamilyName - { $res = DataResource.table($cf.name.getKeyspace(), $cf.name.getName()); } + | ( K_COLUMNFAMILY )? cf = columnFamilyName { $res = DataResource.table($cf.name.getKeyspace(), $cf.name.getName()); } + | K_ALL K_TABLES K_IN K_KEYSPACE ks = keyspaceName { $res = DataResource.allTables($ks.id); } ; jmxResource returns [JMXResource res] diff --git a/src/java/org/apache/cassandra/auth/AuthSchemaChangeListener.java b/src/java/org/apache/cassandra/auth/AuthSchemaChangeListener.java index 6c21d7b97d..217eb1c4de 100644 --- a/src/java/org/apache/cassandra/auth/AuthSchemaChangeListener.java +++ b/src/java/org/apache/cassandra/auth/AuthSchemaChangeListener.java @@ -31,6 +31,7 @@ public class AuthSchemaChangeListener extends SchemaChangeListener public void onDropKeyspace(String ksName) { DatabaseDescriptor.getAuthorizer().revokeAllOn(DataResource.keyspace(ksName)); + DatabaseDescriptor.getAuthorizer().revokeAllOn(DataResource.allTables(ksName)); DatabaseDescriptor.getAuthorizer().revokeAllOn(FunctionResource.keyspace(ksName)); } diff --git a/src/java/org/apache/cassandra/auth/CassandraRoleManager.java b/src/java/org/apache/cassandra/auth/CassandraRoleManager.java index a79fb12ac8..0e49056777 100644 --- a/src/java/org/apache/cassandra/auth/CassandraRoleManager.java +++ b/src/java/org/apache/cassandra/auth/CassandraRoleManager.java @@ -137,25 +137,32 @@ public class CassandraRoleManager implements IRoleManager public CassandraRoleManager() { - supportedOptions = DatabaseDescriptor.getAuthenticator().getClass() == PasswordAuthenticator.class + supportedOptions = DatabaseDescriptor.getAuthenticator() instanceof PasswordAuthenticator ? ImmutableSet.of(Option.LOGIN, Option.SUPERUSER, Option.PASSWORD) : ImmutableSet.of(Option.LOGIN, Option.SUPERUSER); - alterableOptions = DatabaseDescriptor.getAuthenticator().getClass().equals(PasswordAuthenticator.class) + alterableOptions = DatabaseDescriptor.getAuthenticator() instanceof PasswordAuthenticator ? ImmutableSet.of(Option.PASSWORD) : ImmutableSet.