Skip to content

AxonOps — AI-Native Control Plane for Open Source Data Platforms

Cassandra CQL Role-Based Access Control

Role-Based Access Control (RBAC) manages authentication and authorization through roles and permissions.


  • CREATE ROLE creates a role definition stored in system_auth.roles
  • GRANT permission immediately takes effect for new operations
  • REVOKE permission immediately takes effect for new operations
  • Role inheritance is resolved at authorization time (transitive permissions)
  • DROP ROLE removes the role and cascades to revoke all grants
  • Superuser roles bypass all permission checks

Undefined Behavior

The following behaviors are undefined and must not be relied upon:

  • Immediate enforcement across all nodes: Permission changes propagate asynchronously; brief windows of inconsistent enforcement may occur
  • Existing connection state: Permission changes may not affect already-authenticated connections immediately
  • Cache invalidation timing: Permission cache (roles_validity_in_ms) may delay enforcement
  • Concurrent modification safety: Simultaneous role modifications may produce unexpected results
  • Recovery of dropped roles: Dropped roles and their permissions are not recoverable
OperationCheck LocationCaching
AuthenticationAny nodecredentials_validity_in_ms
AuthorizationCoordinatorroles_validity_in_ms
Permission resolutionCoordinatorpermissions_validity_in_ms
ScenarioPermission Resolution
Direct grantPermission applies
Inherited via GRANT rolePermission applies (transitive)
Multiple inheritance pathsPermission applies if any path grants it
Revoked from parentPermission revoked unless granted via another path
cassandra.yaml
roles_validity_in_ms: 2000 # Role information cache
permissions_validity_in_ms: 2000 # Permission cache
credentials_validity_in_ms: 2000 # Authentication cache
Failure ModeOutcomeClient Action
UnauthorizedExceptionOperation deniedRequest appropriate permissions
InvalidRequestExceptionInvalid role or permissionFix request syntax
Role not foundOperation failsCreate role or fix name
Circular grant attemptOperation rejectedRedesign role hierarchy
VersionBehavior
2.2+RBAC introduced (CASSANDRA-7653), replaces per-user model
3.0+Network authorization, improved role management
5.0+Data masking permissions (UNMASK, SELECT_MASKED)
5.0+Enhanced CIDR-based authorization

Prior to Cassandra 2.2, authentication and authorization operated on a per-user model. Administrators created users with CREATE USER, granted permissions directly to individual users, and had no mechanism for grouping permissions or creating hierarchical access structures.

The CASSANDRA-7653 proposal established several requirements:

  1. Role Unification - A single "role" concept replaces the separate notions of users and groups
  2. Role Inheritance - Roles can be granted to other roles, enabling hierarchical permission structures
  3. Backward Compatibility - Existing CREATE USER and ALTER USER syntax continues to function
  4. Centralized Management - Permissions defined once on a role apply to all members

The RBAC implementation introduced:

ComponentDescription
IRoleManagerNew pluggable interface for role management
CassandraRoleManagerDefault implementation storing roles in system_auth
system_auth.rolesReplaces system_auth.users for role definitions
system_auth.role_membersTracks role-to-role grants
system_auth.role_permissionsStores permission grants

When upgrading from pre-2.2 versions:

  • Existing users are automatically converted to roles with LOGIN = true
  • CREATE USER statements internally execute CREATE ROLE ... WITH LOGIN = true
  • Permissions granted to users remain intact on the converted roles

Cassandra uses roles for both authentication (users) and authorization (permission groups):

Role TypeLOGINPurpose
User accounttrueAuthenticates to the cluster
Permission groupfalseGroups permissions for assignment

Permissions control what actions a role can perform:

PermissionDescriptionApplicable Resources
ALLAll applicable permissionsAny
ALTERModify schemaKeyspace, Table, Role
AUTHORIZEGrant/revoke permissionsAny
CREATECreate objectsKeyspace, Table, Function, Role
DESCRIBEView role detailsRole
DROPDelete objectsKeyspace, Table, Function, Role
EXECUTEExecute functionsFunction
MODIFYINSERT, UPDATE, DELETEKeyspace, Table
SELECTRead dataKeyspace, Table
UNMASKView unmasked dataKeyspace, Table
SELECT_MASKEDView masked dataKeyspace, Table

Permissions are granted on resources:

ResourceSyntax
All keyspacesALL KEYSPACES
KeyspaceKEYSPACE keyspace_name
Table[keyspace_name.]table_name
All functionsALL FUNCTIONS [IN KEYSPACE keyspace_name]
FunctionFUNCTION [keyspace_name.]func_name(arg_types)
All rolesALL ROLES
RoleROLE role_name
All MBeansALL MBEANS
MBeanMBEAN mbean_name / MBEANS pattern

Create a role for authentication and/or authorization.

CREATE ROLE [ IF NOT EXISTS ] role_name
[ WITH option [ AND option ... ] ]
OptionTypeDefaultDescription
PASSWORDstring-Authentication password
HASHED PASSWORDstring-Pre-hashed password
LOGINbooleanfalseWhether role can authenticate
SUPERUSERbooleanfalseWhether role has all permissions
OPTIONSmap{}Authentication plugin options
-- User account
CREATE ROLE app_service
WITH PASSWORD = 'secret123'
AND LOGIN = true;
-- Superuser
CREATE ROLE admin
WITH PASSWORD = 'admin_secret'
AND LOGIN = true
AND SUPERUSER = true;
-- Permission group (no login)
CREATE ROLE read_only_access;
-- Idempotent creation
CREATE ROLE IF NOT EXISTS developer
WITH PASSWORD = 'dev_pwd'
AND LOGIN = true;
-- With hashed password (for migration)
CREATE ROLE migrated_user
WITH HASHED PASSWORD = '$2a$10$...'
AND LOGIN = true;
  • CREATE permission on ALL ROLES, or superuser
  • PASSWORD required when LOGIN = true

Modify role properties.

ALTER ROLE [ IF EXISTS ] role_name
[ WITH option [ AND option ... ] ]

Accepts same options as CREATE ROLE. With IF EXISTS, the statement is a no-op when the role does not exist instead of returning an error.

-- Change password
ALTER ROLE app_service WITH PASSWORD = 'new_password';
-- Disable login (lock account)
ALTER ROLE compromised_user WITH LOGIN = false;
-- Grant superuser
ALTER ROLE senior_dba WITH SUPERUSER = true;
-- Revoke superuser
ALTER ROLE former_admin WITH SUPERUSER = false;
  • ALTER permission on the role, or superuser
  • Non-superusers can only change their own password
  • Cannot demote the last superuser

Remove a role.

DROP ROLE [ IF EXISTS ] role_name
DROP ROLE former_employee;
DROP ROLE IF EXISTS temp_user;
  • DROP permission on the role, or superuser
  • Cannot drop the last superuser
  • Active sessions continue but re-authentication fails

Grant permissions on resources to a role.

GRANT ( ALL [ PERMISSIONS ] | permission ) ON resource TO role_name
ElementValues
permissionCREATE, ALTER, DROP, SELECT, MODIFY, AUTHORIZE, DESCRIBE, UNMASK, SELECT_MASKED, EXECUTE
resourceALL KEYSPACES, KEYSPACE name, [TABLE] name, ALL ROLES, ROLE name, ALL FUNCTIONS [IN KEYSPACE name], FUNCTION name, ALL MBEANS, MBEAN name, MBEANS pattern
-- Read access to keyspace
GRANT SELECT ON KEYSPACE production TO analyst;
-- Read/write to table
GRANT SELECT ON orders TO app_service;
GRANT MODIFY ON orders TO app_service;
-- Full access to keyspace
GRANT ALL PERMISSIONS ON KEYSPACE dev TO developer;
-- Schema management (no data access)
GRANT CREATE ON ALL KEYSPACES TO schema_admin;
GRANT ALTER ON ALL KEYSPACES TO schema_admin;
GRANT DROP ON ALL KEYSPACES TO schema_admin;
-- Function execution
GRANT EXECUTE ON FUNCTION my_ks.my_func(int) TO app_user;
-- Role management
GRANT ALTER ON ROLE app_user TO team_lead;
GRANT AUTHORIZE ON ALL ROLES TO security_admin;
-- MBean access (JMX)
GRANT SELECT ON ALL MBEANS TO monitoring;
GRANT EXECUTE ON MBEAN 'org.apache.cassandra.db:type=StorageService' TO ops;
  • AUTHORIZE permission on the resource, or superuser

Grant a role to another role, creating role membership.

GRANT role_name TO role_name
-- Create permission groups
CREATE ROLE prod_read;
CREATE ROLE prod_write;
GRANT SELECT ON KEYSPACE production TO prod_read;
GRANT MODIFY ON KEYSPACE production TO prod_write;
-- Assign groups to user
CREATE ROLE app_service WITH PASSWORD = 'secret' AND LOGIN = true;
GRANT prod_read TO app_service;
GRANT prod_write TO app_service;
-- Hierarchical roles
CREATE ROLE junior_dev;
CREATE ROLE senior_dev;
GRANT junior_dev TO senior_dev; -- senior inherits junior's permissions
  • AUTHORIZE permission on both roles, or superuser
  • Circular grants not allowed

Remove permissions from a role.

REVOKE ( ALL [ PERMISSIONS ] | permission ) ON resource FROM role_name

permission and resource accept the same forms as in GRANT (Permission).

-- Revoke specific permission
REVOKE MODIFY ON KEYSPACE production FROM app_service;
-- Revoke all permissions on resource
REVOKE ALL PERMISSIONS ON KEYSPACE sensitive FROM former_employee;
-- Revoke function execution
REVOKE EXECUTE ON FUNCTION my_ks.my_func(int) FROM app_user;
  • AUTHORIZE permission on the resource, or superuser
  • Revoking non-existent permission succeeds silently

Remove role membership.

REVOKE role_name FROM role_name
-- Remove role membership
REVOKE prod_write FROM restricted_user;
-- Demote from senior to junior
REVOKE senior_dev FROM demoted_user;

Display roles and their properties.

LIST ROLES [ OF role_name ] [ NORECURSIVE ]
OptionDescription
OF role_nameShow roles granted to specified role
NORECURSIVEShow only direct grants, not inherited
-- All roles
LIST ROLES;
-- Roles granted to user (including inherited)
LIST ROLES OF developer;
-- Direct grants only
LIST ROLES OF app_user NORECURSIVE;
role | super | login | options
------------+-------+-------+---------
cassandra | True | True | {}
admin | True | True | {}
app_user | False | True | {}
readers | False | False | {}

Display granted permissions.

LIST ( ALL [ PERMISSIONS ] | permission )
[ ON resource ]
[ OF role_name [ NORECURSIVE ] ]
OptionDescription
permissionFilter by permission type (or ALL)
ON resourceFilter by resource
OF role_nameFilter by role
NORECURSIVEShow only direct grants
-- All permissions in cluster
LIST ALL PERMISSIONS;
-- Permissions for role (including inherited)
LIST ALL PERMISSIONS OF app_service;
-- Direct grants only
LIST ALL PERMISSIONS OF app_user NORECURSIVE;
-- Specific permission type
LIST SELECT ON KEYSPACE production;
-- Permissions on specific table
LIST ALL PERMISSIONS ON TABLE production.orders;
role | username | resource | permission
-----------+------------+-----------------------+------------
app_user | app_user | <keyspace production> | SELECT
app_user | app_user | <keyspace production> | MODIFY

Associate a certificate identity with a role. Requires MutualTlsAuthenticator.

Available in Cassandra 5.0+

ADD IDENTITY [ IF NOT EXISTS ] 'identity_string' TO ROLE role_name

With IF NOT EXISTS, the statement is a no-op when the identity is already associated instead of returning an error.

-- SPIFFE identity
ADD IDENTITY 'spiffe://cluster.local/ns/default/sa/app-service' TO ROLE app_service;
-- Certificate subject
ADD IDENTITY 'CN=app-client,O=MyOrg' TO ROLE app_client;
-- Idempotent association
ADD IDENTITY IF NOT EXISTS 'CN=app-client,O=MyOrg' TO ROLE app_client;

See Mutual TLS Authentication for configuration.


Remove a certificate identity mapping.

Available in Cassandra 5.0+

DROP IDENTITY [ IF EXISTS ] 'identity_string'

With IF EXISTS, the statement is a no-op when the identity does not exist instead of returning an error.

DROP IDENTITY 'spiffe://cluster.local/ns/default/sa/app-service';
DROP IDENTITY IF EXISTS 'spiffe://cluster.local/ns/default/sa/missing';

The pre-2.2 user-based statements remain supported as a thin layer over the role model. They are accepted for backward compatibility, but CREATE ROLE / ALTER ROLE / DROP ROLE should be preferred for new deployments.

CREATE USER [ IF NOT EXISTS ] user_name
[ WITH PASSWORD 'password' ]
[ SUPERUSER | NOSUPERUSER ]

Internally executes CREATE ROLE ... WITH LOGIN = true. SUPERUSER and NOSUPERUSER map to SUPERUSER = true|false. The default is NOSUPERUSER.

ALTER USER [ IF EXISTS ] user_name
[ WITH PASSWORD 'password' ]
[ SUPERUSER | NOSUPERUSER ]
DROP USER [ IF EXISTS ] user_name
LIST USERS

Returns the subset of roles where LOGIN = true.

CREATE USER IF NOT EXISTS app_user WITH PASSWORD 'secret' NOSUPERUSER;
ALTER USER app_user WITH PASSWORD 'rotated_secret';
ALTER USER IF EXISTS app_user SUPERUSER;
DROP USER IF EXISTS legacy_user;
LIST USERS;

Role and permission data is stored in system_auth:

TableContents
rolesRole definitions
role_permissionsPermission grants
role_membersRole-to-role grants
identity_to_roleCertificate identity mappings (5.0+)
-- View all roles
SELECT * FROM system_auth.roles;
-- View permissions for a role
SELECT * FROM system_auth.role_permissions
WHERE role = 'app_service';
-- View role memberships
SELECT * FROM system_auth.role_members;

TicketDescription
CASSANDRA-547Original pluggable authentication framework (0.6)
CASSANDRA-7653Role-based access control implementation (2.2)
CASSANDRA-8394Cassandra 3.0 auth subsystem rework
CASSANDRA-7557UDF permissions
CASSANDRA-8082Fine-grained permissions
CASSANDRA-10091JMX authentication/authorization
CASSANDRA-18554mTLS authenticators (5.0)
CEPDescription
CEP-16CQLSH authentication plugin support
CEP-34mTLS client and internode authenticators
CEP-50Authentication negotiation (in progress)

TopicDescription
CQL Security OverviewSecurity features summary
Dynamic Data MaskingColumn-level data masking
AuthenticationAuthenticator configuration
AuthorizationAuthorizer configuration