diff --git a/test/cluster/test_tablets_lwt.py b/test/cluster/test_tablets_lwt.py index 6c4ebd3fa8..21193b53ac 100644 --- a/test/cluster/test_tablets_lwt.py +++ b/test/cluster/test_tablets_lwt.py @@ -5,9 +5,11 @@ # from cassandra import WriteFailure +from cassandra.auth import PlainTextAuthProvider from cassandra.pool import Host # type: ignore # pylint: disable=no-name-in-module from cassandra.query import SimpleStatement, ConsistencyLevel from cassandra.protocol import InvalidRequest +from cassandra import Unauthorized from test.cluster.util import new_test_keyspace, unique_name, reconnect_driver from test.pylib.manager_client import ManagerClient @@ -22,6 +24,7 @@ import pytest import asyncio import logging import time +import re logger = logging.getLogger(__name__) @@ -441,3 +444,98 @@ async def test_lwt_for_tablets_is_not_supported_without_raft(manager: ManagerCli logger.info("Run an LWT") with pytest.raises(Exception, match=f"Cannot create paxos state table for {ks}.test because raft-based schema management is not enabled."): await cql.run_async(f"INSERT INTO {ks}.test (pk, c) VALUES (1, 1) IF NOT EXISTS") + + +@pytest.mark.asyncio +async def test_paxos_state_table_permissions(manager: ManagerClient): + # This test checks permission handling for paxos state tables: + # * Only a superuser is allowed to access a paxos state table + # * Even a superuser is not allowed to run ALTER or DROP on paxos state tables + + cmdline = [ + '--logger-log-level', 'paxos=trace' + ] + config = { + 'rf_rack_valid_keyspaces': False, + 'authenticator': 'PasswordAuthenticator', + 'authorizer': 'CassandraAuthorizer' + } + + manager.auth_provider = PlainTextAuthProvider( + username="cassandra", password="cassandra") + + servers = [await manager.server_add(cmdline=cmdline, config=config)] + cql, _ = await manager.get_ready_cql(servers) + + logger.info("Create a keyspace") + async with new_test_keyspace(manager, "WITH replication = {'class': 'NetworkTopologyStrategy', 'replication_factor': 1} AND tablets = {'initial': 1}") as ks: + logger.info("Create a table") + await cql.run_async(f"CREATE TABLE {ks}.test (pk int PRIMARY KEY, c int);") + + logger.info("Run an LWT1") + await cql.run_async(f"INSERT INTO {ks}.test (pk, c) VALUES (1, 1) IF NOT EXISTS") + + logger.info("Create a role_1") + await cql.run_async("CREATE ROLE role_1 WITH SUPERUSER = true AND PASSWORD = 'psw' AND LOGIN = true") + + logger.info("Create a role_2") + await cql.run_async("CREATE ROLE role_2 WITH SUPERUSER = false AND PASSWORD = 'psw' AND LOGIN = true") + + logger.info("Grant all permissions on test table to role_2") + await cql.run_async(f"GRANT ALL PERMISSIONS ON TABLE {ks}.test TO role_2") + + # We don't attempt to prevent users from granting permissions on $paxos tables, + # for simplicity. These permissions simply won't take effect — for example, + # role_2 will still be denied access to the $paxos table. + logger.info("Grant all permissions on test$paxos table to role_2") + await cql.run_async(f"GRANT ALL PERMISSIONS ON TABLE \"{ks}\".\"test$paxos\" TO role_2") + + logger.info("Login as role_1") + await manager.driver_connect(auth_provider=PlainTextAuthProvider(username="role_1", password="psw")) + cql = manager.get_cql() + + logger.info("Run an LWT2") + await cql.run_async(f"UPDATE {ks}.test SET c = 2 WHERE pk = 1 IF c = 1") + + logger.info("Select data after LWT2") + rows = await cql.run_async(SimpleStatement(f"SELECT * FROM {ks}.test WHERE pk = 1;", + consistency_level=ConsistencyLevel.SERIAL)) + assert len(rows) == 1 + row = rows[0] + assert row.pk == 1 + assert row.c == 2 + + logger.info("Select paxos state as role_1") + rows = await cql.run_async(SimpleStatement(f"SELECT * FROM {ks}.\"test$paxos\"")) + assert len(rows) == 1 + + logger.info("Attempt to run DROP TABLE for paxos state table as role_1") + with pytest.raises(Unauthorized, match=re.escape(f"Cannot DROP