2121import java .util .ArrayList ;
2222import java .util .Arrays ;
2323import java .util .Collections ;
24- import java .util .EnumSet ;
2524import java .util .HashMap ;
2625import java .util .HashSet ;
2726import java .util .List ;
9190import static org .apache .cassandra .utils .ByteBufferUtil .bytes ;
9291
9392/**
94- * Responsible for the creation, maintenance and deletion of roles
95- * for the purposes of authentication and authorization.
96- * Role data is stored internally, using the roles and role_members tables
97- * in the system_auth keyspace.
93+ * Responsible for the creation, maintenance and deletion of roles for the purposes of authentication and
94+ * authorization. Role data is stored internally, using the roles and role_members tables in the system_auth
95+ * keyspace.
9896 *
99- * Additionally, if org.apache.cassandra.auth.PasswordAuthenticator is used,
100- * encrypted passwords are also stored in the system_auth.roles table. This
101- * coupling between the IAuthenticator and IRoleManager implementations exists
102- * because setting a role's password via CQL is done with a CREATE ROLE or
103- * ALTER ROLE statement, the processing of which is handled by IRoleManager.
104- * As IAuthenticator is concerned only with credentials checking and has no
105- * means to modify passwords, PasswordAuthenticator depends on
106- * CassandraRoleManager for those functions.
107- *
108- * Alternative IAuthenticator implementations may be used in conjunction with
109- * CassandraRoleManager, but WITH PASSWORD = 'password' will not be supported
110- * in CREATE/ALTER ROLE statements.
111- *
112- * Such a configuration could be implemented using a custom IRoleManager that
113- * extends CassandraRoleManager and which includes Option.PASSWORD in the {@code Set<Option>}
114- * returned from supportedOptions/alterableOptions. Any additional processing
115- * of the password itself (such as storing it in an alternative location) would
116- * be added in overridden createRole and alterRole implementations.
97+ * Authenticators (implementations of {@link IAuthenticator}) can specify additional attributes to be stored.
98+ * For example, {@link org.apache.cassandra.auth.PasswordAuthenticator}, stores encrypted passwords in the
99+ * system_auth.roles table. This coupling between the IAuthenticator and IRoleManager implementations exists because
100+ * setting a role's password via CQL is done with a CREATE ROLE or ALTER ROLE statement, the processing of which is
101+ * handled by IRoleManager. Authenticators depend on CassandraRoleManager for those functions because IAuthenticator
102+ * is concerned only with credentials checking and has no means to directly modify passwords.
117103 */
118104public class CassandraRoleManager implements IRoleManager , CassandraRoleManagerMBean
119105{
@@ -123,8 +109,24 @@ public class CassandraRoleManager implements IRoleManager, CassandraRoleManagerM
123109 public static final String DEFAULT_SUPERUSER_NAME = "cassandra" ;
124110 public static final String DEFAULT_SUPERUSER_PASSWORD = "cassandra" ;
125111
112+ /**
113+ * Role options which are supported for all authentication mechanisms. IAuthenticator implementations can declare
114+ * additional supported role options via {@link IAuthenticator#getSupportedRoleOptions()}.
115+ */
116+ @ VisibleForTesting
117+ static final Set <Option > DEFAULT_SUPPORTED_ROLE_OPTIONS = Set .of (Option .LOGIN , Option .SUPERUSER );
118+
119+ /**
120+ * User-alterable role options which are supported for all authentication mechanisms. IAuthenticator
121+ * implementations can declare additional alterable role options via
122+ * {@link IAuthenticator#getAlterableRoleOptions()}.
123+ */
124+ @ VisibleForTesting
125+ static final Set <Option > DEFAULT_ALTERABLE_ROLE_OPTIONS = Set .of ();
126+
126127 @ VisibleForTesting
127128 static final String PARAM_INVALID_ROLE_DISCONNECT_TASK_PERIOD = "invalid_role_disconnect_task_period" ;
129+
128130 @ VisibleForTesting
129131 static final String PARAM_INVALID_ROLE_DISCONNECT_TASK_MAX_JITTER = "invalid_role_disconnect_task_max_jitter" ;
130132
@@ -191,17 +193,21 @@ public CassandraRoleManager()
191193
192194 public CassandraRoleManager (Map <String , String > parameters )
193195 {
194- Set <Option > allowedOptions = DatabaseDescriptor .getAuthenticator () instanceof PasswordAuthenticator
195- ? EnumSet .of (Option .LOGIN , Option .SUPERUSER , Option .PASSWORD , Option .HASHED_PASSWORD , Option .GENERATED_PASSWORD , Option .GENERATED_NAME )
196- : EnumSet .of (Option .LOGIN , Option .SUPERUSER );
196+ Set <Option > supportedOptions = Stream .concat (
197+ DEFAULT_SUPPORTED_ROLE_OPTIONS .stream (),
198+ DatabaseDescriptor .getAuthenticator ().getSupportedRoleOptions ().stream ()
199+ .filter (Objects ::nonNull ))
200+ .collect (Collectors .toSet ());
197201
198202 if (Guardrails .roleNamePolicy .getGenerator () != NoOpGenerator .INSTANCE )
199- allowedOptions .add (Option .OPTIONS );
203+ supportedOptions .add (Option .OPTIONS );
204+
205+ this .supportedOptions = Set .copyOf (supportedOptions );
200206
201- supportedOptions = ImmutableSet . copyOf ( allowedOptions );
202- alterableOptions = DatabaseDescriptor .getAuthenticator () instanceof PasswordAuthenticator
203- ? ImmutableSet . of ( Option . PASSWORD , Option . HASHED_PASSWORD , Option . GENERATED_PASSWORD )
204- : ImmutableSet .< Option > of ( );
207+ alterableOptions = Stream . concat ( DEFAULT_ALTERABLE_ROLE_OPTIONS . stream (),
208+ DatabaseDescriptor .getAuthenticator (). getAlterableRoleOptions (). stream ()
209+ . filter ( Objects :: nonNull ) )
210+ . collect ( Collectors . toUnmodifiableSet () );
205211
206212 // Inherit parsing and validation from existing config parser
207213 invalidClientDisconnectPeriodMillis = new DurationSpec .LongMillisecondsBound (parameters .getOrDefault (PARAM_INVALID_ROLE_DISCONNECT_TASK_PERIOD , "0h" )).toMilliseconds ();
@@ -505,8 +511,8 @@ public boolean isExistingRole(RoleResource role)
505511
506512 public Set <? extends IResource > protectedResources ()
507513 {
508- return ImmutableSet .of (DataResource .table (SchemaConstants .AUTH_KEYSPACE_NAME , AuthKeyspace .ROLES ),
509- DataResource .table (SchemaConstants .AUTH_KEYSPACE_NAME , AuthKeyspace .ROLE_MEMBERS ));
514+ return Set .of (DataResource .table (SchemaConstants .AUTH_KEYSPACE_NAME , AuthKeyspace .ROLES ),
515+ DataResource .table (SchemaConstants .AUTH_KEYSPACE_NAME , AuthKeyspace .ROLE_MEMBERS ));
510516 }
511517
512518 public void validateConfiguration () throws ConfigurationException
0 commit comments