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.