diff --git a/CHANGES.txt b/CHANGES.txt index 041b524cc9..27d68e9c80 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 5.0.2 + * Use ParameterizedClass for all auth-related implementations (CASSANDRA-19946) * Correct out-of-date metrics and configuration documentation for SAI (CASSANDRA-19898) Merged from 4.1: * Fix race condition in DecayingEstimatedHistogramReservoir during rescale (CASSANDRA-19365) diff --git a/conf/cassandra.yaml b/conf/cassandra.yaml index 2e3955102c..5562a2c156 100644 --- a/conf/cassandra.yaml +++ b/conf/cassandra.yaml @@ -159,14 +159,17 @@ batchlog_replay_throttle: 1024KiB # Authentication backend, implementing IAuthenticator; used to identify users # Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllAuthenticator, # PasswordAuthenticator}. +# Optional parameters can be specified in the form of: +# parameters: +# param_key1: param_value1 +# ... # # - AllowAllAuthenticator performs no checks - set it to disable authentication. # - PasswordAuthenticator relies on username/password pairs to authenticate # users. It keeps usernames and hashed passwords in system_auth.roles table. # Please increase system_auth keyspace replication factor if you use this authenticator. # If using PasswordAuthenticator, CassandraRoleManager must also be used (see below) -authenticator: - class_name : org.apache.cassandra.auth.AllowAllAuthenticator +authenticator: AllowAllAuthenticator # MutualTlsAuthenticator can be configured using the following configuration. One can add their own validator # which implements MutualTlsCertificateValidator class and provide logic for extracting identity out of certificates # and validating certificates. @@ -177,6 +180,10 @@ authenticator: # Authorization backend, implementing IAuthorizer; used to limit access/provide permissions # Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllAuthorizer, # CassandraAuthorizer}. +# Optional parameters can be specified in the form of: +# parameters: +# param_key1: param_value1 +# ... # # - AllowAllAuthorizer allows any action to any user - set it to disable authorization. # - CassandraAuthorizer stores permissions in system_auth.role_permissions table. Please @@ -189,6 +196,10 @@ authorizer: AllowAllAuthorizer # which stores role information in the system_auth keyspace. Most functions of the # IRoleManager require an authenticated login, so unless the configured IAuthenticator # actually implements authentication, most of this functionality will be unavailable. +# Optional parameters can be specified in the form of: +# parameters: +# param_key1: param_value1 +# ... # # - CassandraRoleManager stores role data in the system_auth keyspace. Please # increase system_auth keyspace replication factor if you use this role manager. @@ -198,6 +209,10 @@ role_manager: CassandraRoleManager # access to certain DCs # Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllNetworkAuthorizer, # CassandraNetworkAuthorizer}. +# Optional parameters can be specified in the form of: +# parameters: +# param_key1: param_value1 +# ... # # - AllowAllNetworkAuthorizer allows access to any DC to any user - set it to disable authorization. # - CassandraNetworkAuthorizer stores permissions in system_auth.network_permissions table. Please @@ -208,6 +223,11 @@ network_authorizer: AllowAllNetworkAuthorizer # access from certain CIDRs # Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllCIDRAuthorizer, # CassandraCIDRAuthorizer}. +# Optional parameters can be specified in the form of: +# parameters: +# param_key1: param_value1 +# ... +# # - AllowAllCIDRAuthorizer allows access from any CIDR to any user - set it to disable CIDR authorization. # - CassandraCIDRAuthorizer stores user's CIDR permissions in system_auth.cidr_permissions table. Please # increase system_auth keyspace replication factor if you use this authorizer, otherwise any changes to diff --git a/conf/cassandra_latest.yaml b/conf/cassandra_latest.yaml index a23129ded5..fd86f149e6 100644 --- a/conf/cassandra_latest.yaml +++ b/conf/cassandra_latest.yaml @@ -162,6 +162,10 @@ batchlog_replay_throttle: 1024KiB # Authentication backend, implementing IAuthenticator; used to identify users # Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllAuthenticator, # PasswordAuthenticator}. +# Optional parameters can be specified in the form of: +# parameters: +# param_key1: param_value1 +# ... # # - AllowAllAuthenticator performs no checks - set it to disable authentication. # - PasswordAuthenticator relies on username/password pairs to authenticate @@ -169,7 +173,7 @@ batchlog_replay_throttle: 1024KiB # Please increase system_auth keyspace replication factor if you use this authenticator. # If using PasswordAuthenticator, CassandraRoleManager must also be used (see below) authenticator: - class_name : org.apache.cassandra.auth.AllowAllAuthenticator + class_name : AllowAllAuthenticator # MutualTlsAuthenticator can be configured using the following configuration. One can add their own validator # which implements MutualTlsCertificateValidator class and provide logic for extracting identity out of certificates # and validating certificates. @@ -180,11 +184,16 @@ authenticator: # Authorization backend, implementing IAuthorizer; used to limit access/provide permissions # Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllAuthorizer, # CassandraAuthorizer}. +# Optional parameters can be specified in the form of: +# parameters: +# param_key1: param_value1 +# ... # # - AllowAllAuthorizer allows any action to any user - set it to disable authorization. # - CassandraAuthorizer stores permissions in system_auth.role_permissions table. Please # increase system_auth keyspace replication factor if you use this authorizer. -authorizer: AllowAllAuthorizer +authorizer: + class_name: AllowAllAuthorizer # Part of the Authentication & Authorization backend, implementing IRoleManager; used # to maintain grants and memberships between roles. @@ -192,25 +201,40 @@ authorizer: AllowAllAuthorizer # which stores role information in the system_auth keyspace. Most functions of the # IRoleManager require an authenticated login, so unless the configured IAuthenticator # actually implements authentication, most of this functionality will be unavailable. +# Optional parameters can be specified in the form of: +# parameters: +# param_key1: param_value1 +# ... # # - CassandraRoleManager stores role data in the system_auth keyspace. Please # increase system_auth keyspace replication factor if you use this role manager. -role_manager: CassandraRoleManager +role_manager: + class_name: CassandraRoleManager # Network authorization backend, implementing INetworkAuthorizer; used to restrict user # access to certain DCs # Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllNetworkAuthorizer, # CassandraNetworkAuthorizer}. +# Optional parameters can be specified in the form of: +# parameters: +# param_key1: param_value1 +# ... # # - AllowAllNetworkAuthorizer allows access to any DC to any user - set it to disable authorization. # - CassandraNetworkAuthorizer stores permissions in system_auth.network_permissions table. Please # increase system_auth keyspace replication factor if you use this authorizer. -network_authorizer: AllowAllNetworkAuthorizer +network_authorizer: + class_name: AllowAllNetworkAuthorizer # CIDR authorization backend, implementing ICIDRAuthorizer; used to restrict user # access from certain CIDRs # Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllCIDRAuthorizer, # CassandraCIDRAuthorizer}. +# Optional parameters can be specified in the form of: +# parameters: +# param_key1: param_value1 +# ... +# # - AllowAllCIDRAuthorizer allows access from any CIDR to any user - set it to disable CIDR authorization. # - CassandraCIDRAuthorizer stores user's CIDR permissions in system_auth.cidr_permissions table. Please # increase system_auth keyspace replication factor if you use this authorizer, otherwise any changes to diff --git a/src/java/org/apache/cassandra/auth/AuthConfig.java b/src/java/org/apache/cassandra/auth/AuthConfig.java index 786459d1ce..f357825b10 100644 --- a/src/java/org/apache/cassandra/auth/AuthConfig.java +++ b/src/java/org/apache/cassandra/auth/AuthConfig.java @@ -18,7 +18,7 @@ package org.apache.cassandra.auth; -import java.util.Arrays; +import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,7 +27,6 @@ import org.apache.cassandra.config.Config; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.config.ParameterizedClass; import org.apache.cassandra.exceptions.ConfigurationException; -import org.apache.cassandra.utils.FBUtilities; /** * Only purpose is to Initialize authentication/authorization via {@link #applyAuth()}. @@ -49,14 +48,10 @@ public final class AuthConfig Config conf = DatabaseDescriptor.getRawConfig(); - IAuthenticator authenticator = new AllowAllAuthenticator(); - /* Authentication, authorization and role management backend, implementing IAuthenticator, IAuthorizer & IRoleMapper*/ - if (conf.authenticator != null) - { - authenticator = ParameterizedClass.newInstance(conf.authenticator, - Arrays.asList("", AuthConfig.class.getPackage().getName())); - } + /* Authentication, authorization and role management backend, implementing IAuthenticator, I*Authorizer & IRoleManager */ + + IAuthenticator authenticator = authInstantiate(conf.authenticator, AllowAllAuthenticator.class); // the configuration options regarding credentials caching are only guaranteed to // work with PasswordAuthenticator, so log a message if some other authenticator @@ -75,10 +70,7 @@ public final class AuthConfig // authorizer - IAuthorizer authorizer = new AllowAllAuthorizer(); - - if (conf.authorizer != null) - authorizer = FBUtilities.newAuthorizer(conf.authorizer); + IAuthorizer authorizer = authInstantiate(conf.authorizer, AllowAllAuthorizer.class); if (!authenticator.requireAuthentication() && authorizer.requireAuthorization()) throw new ConfigurationException(conf.authenticator.class_name + " can't be used with " + conf.authorizer, false); @@ -87,11 +79,7 @@ public final class AuthConfig // role manager - IRoleManager roleManager; - if (conf.role_manager != null) - roleManager = FBUtilities.newRoleManager(conf.role_manager); - else - roleManager = new CassandraRoleManager(); + IRoleManager roleManager = authInstantiate(conf.role_manager, CassandraRoleManager.class); if (authenticator instanceof PasswordAuthenticator && !(roleManager instanceof CassandraRoleManager)) throw new ConfigurationException("CassandraRoleManager must be used with PasswordAuthenticator", false); @@ -100,29 +88,32 @@ public final class AuthConfig // authenticator - if (conf.internode_authenticator != null) - { - DatabaseDescriptor.setInternodeAuthenticator(ParameterizedClass.newInstance(conf.internode_authenticator, - Arrays.asList("", AuthConfig.class.getPackage().getName()))); - } - + IInternodeAuthenticator internodeAuthenticator = authInstantiate(conf.internode_authenticator, + AllowAllInternodeAuthenticator.class); + DatabaseDescriptor.setInternodeAuthenticator(internodeAuthenticator); // network authorizer - INetworkAuthorizer networkAuthorizer = FBUtilities.newNetworkAuthorizer(conf.network_authorizer); - DatabaseDescriptor.setNetworkAuthorizer(networkAuthorizer); + + INetworkAuthorizer networkAuthorizer = authInstantiate(conf.network_authorizer, AllowAllNetworkAuthorizer.class); + if (networkAuthorizer.requireAuthorization() && !authenticator.requireAuthentication()) { throw new ConfigurationException(conf.network_authorizer + " can't be used with " + conf.authenticator.class_name, false); } + DatabaseDescriptor.setNetworkAuthorizer(networkAuthorizer); + // cidr authorizer - ICIDRAuthorizer cidrAuthorizer = ICIDRAuthorizer.newCIDRAuthorizer(conf.cidr_authorizer); - DatabaseDescriptor.setCIDRAuthorizer(cidrAuthorizer); + + ICIDRAuthorizer cidrAuthorizer = authInstantiate(conf.cidr_authorizer, AllowAllCIDRAuthorizer.class); + if (cidrAuthorizer.requireAuthorization() && !authenticator.requireAuthentication()) { throw new ConfigurationException(conf.cidr_authorizer + " can't be used with " + conf.authenticator, false); } + DatabaseDescriptor.setCIDRAuthorizer(cidrAuthorizer); + // Validate at last to have authenticator, authorizer, role-manager and internode-auth setup // in case these rely on each other. @@ -133,4 +124,21 @@ public final class AuthConfig cidrAuthorizer.validateConfiguration(); DatabaseDescriptor.getInternodeAuthenticator().validateConfiguration(); } + + private static T authInstantiate(ParameterizedClass authCls, Class defaultCls) { + if (authCls != null && authCls.class_name != null) + { + String authPackage = AuthConfig.class.getPackage().getName(); + return ParameterizedClass.newInstance(authCls, List.of("", authPackage)); + } + + try + { + return defaultCls.newInstance(); + } + catch (InstantiationException | IllegalAccessException e) + { + throw new ConfigurationException("Failed to instantiate " + defaultCls.getName(), e); + } + } } diff --git a/src/java/org/apache/cassandra/auth/ICIDRAuthorizer.java b/src/java/org/apache/cassandra/auth/ICIDRAuthorizer.java index 737bbb220e..ed1d4dfd5f 100644 --- a/src/java/org/apache/cassandra/auth/ICIDRAuthorizer.java +++ b/src/java/org/apache/cassandra/auth/ICIDRAuthorizer.java @@ -21,10 +21,8 @@ package org.apache.cassandra.auth; import java.net.InetAddress; import java.util.Set; -import org.apache.cassandra.config.ParameterizedClass; import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.metrics.CIDRAuthorizerMetrics; -import org.apache.cassandra.utils.FBUtilities; /** * Backend for CIDR authorization feature @@ -40,21 +38,6 @@ public interface ICIDRAuthorizer ENFORCE } - public static ICIDRAuthorizer newCIDRAuthorizer(ParameterizedClass cidrAuthorizer) - { - if (cidrAuthorizer == null || cidrAuthorizer.class_name == null) - { - return new AllowAllCIDRAuthorizer(); - } - - String className = cidrAuthorizer.class_name; - if (!className.contains(".")) - { - className = "org.apache.cassandra.auth." + className; - } - return FBUtilities.construct(className, "cidr authorizer"); - } - public void setup(); /** diff --git a/src/java/org/apache/cassandra/config/Config.java b/src/java/org/apache/cassandra/config/Config.java index 6ad9b35a2a..3a1e9e5e65 100644 --- a/src/java/org/apache/cassandra/config/Config.java +++ b/src/java/org/apache/cassandra/config/Config.java @@ -79,10 +79,10 @@ public class Config public String cluster_name = "Test Cluster"; public ParameterizedClass authenticator; - public String authorizer; - public String role_manager; + public ParameterizedClass authorizer; + public ParameterizedClass role_manager; public ParameterizedClass crypto_provider; - public String network_authorizer; + public ParameterizedClass network_authorizer; public ParameterizedClass cidr_authorizer; @Replaces(oldName = "permissions_validity_in_ms", converter = Converters.MILLIS_DURATION_INT, deprecated = true) diff --git a/src/java/org/apache/cassandra/utils/FBUtilities.java b/src/java/org/apache/cassandra/utils/FBUtilities.java index eeefab136f..ca3444d465 100644 --- a/src/java/org/apache/cassandra/utils/FBUtilities.java +++ b/src/java/org/apache/cassandra/utils/FBUtilities.java @@ -73,11 +73,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.cassandra.audit.IAuditLogger; -import org.apache.cassandra.auth.AllowAllNetworkAuthorizer; -import org.apache.cassandra.auth.IAuthenticator; -import org.apache.cassandra.auth.IAuthorizer; -import org.apache.cassandra.auth.INetworkAuthorizer; -import org.apache.cassandra.auth.IRoleManager; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.db.DecoratedKey; import org.apache.cassandra.db.marshal.AbstractType; @@ -650,40 +645,6 @@ public class FBUtilities return FBUtilities.instanceOrConstruct(partitionerClassName, "partitioner"); } - public static IAuthorizer newAuthorizer(String className) throws ConfigurationException - { - if (!className.contains(".")) - className = "org.apache.cassandra.auth." + className; - return FBUtilities.construct(className, "authorizer"); - } - - public static IAuthenticator newAuthenticator(String className) throws ConfigurationException - { - if (!className.contains(".")) - className = "org.apache.cassandra.auth." + className; - return FBUtilities.construct(className, "authenticator"); - } - - public static IRoleManager newRoleManager(String className) throws ConfigurationException - { - if (!className.contains(".")) - className = "org.apache.cassandra.auth." + className; - return FBUtilities.construct(className, "role manager"); - } - - public static INetworkAuthorizer newNetworkAuthorizer(String className) - { - if (className == null) - { - return new AllowAllNetworkAuthorizer(); - } - if (!className.contains(".")) - { - className = "org.apache.cassandra.auth." + className; - } - return FBUtilities.construct(className, "network authorizer"); - } - public static IAuditLogger newAuditLogger(String className, Map parameters) throws ConfigurationException { if (!className.contains(".")) diff --git a/test/data/config/version=5.0-alpha1.yml b/test/data/config/version=5.0-alpha1.yml index 01a5f90ea6..8dad0f60ac 100644 --- a/test/data/config/version=5.0-alpha1.yml +++ b/test/data/config/version=5.0-alpha1.yml @@ -126,7 +126,9 @@ tables_fail_threshold: "java.lang.Integer" trace_type_repair_ttl: "org.apache.cassandra.config.DurationSpec.IntSecondsBound" minimum_replication_factor_warn_threshold: "java.lang.Integer" page_size_warn_threshold: "java.lang.Integer" -role_manager: "java.lang.String" +role_manager: + class_name: "java.lang.String" + parameters: "java.util.Map" counter_write_request_timeout: "org.apache.cassandra.config.DurationSpec.LongMillisecondsBound" denylist_writes_enabled: "java.lang.Boolean" block_for_peers_in_remote_dcs: "java.lang.Boolean" @@ -158,7 +160,9 @@ internode_max_message_size: "org.apache.cassandra.config.DataStorageSpec.IntByte items_per_collection_warn_threshold: "java.lang.Integer" key_cache_migrate_during_compaction: "java.lang.Boolean" sstable_read_rate_persistence_enabled: "java.lang.Boolean" -network_authorizer: "java.lang.String" +network_authorizer: + class_name: "java.lang.String" + parameters: "java.util.Map" data_disk_usage_max_disk_size: "org.apache.cassandra.config.DataStorageSpec.LongBytesBound" memtable_offheap_space: "org.apache.cassandra.config.DataStorageSpec.IntMebibytesBound" prepared_statements_cache_size: "org.apache.cassandra.config.DataStorageSpec.LongMebibytesBound" @@ -281,7 +285,9 @@ commitlog_directory: "java.lang.String" unlogged_batch_across_partitions_warn_threshold: "java.lang.Integer" write_consistency_levels_disallowed: "java.util.Set" auto_bootstrap: "java.lang.Boolean" -authorizer: "java.lang.String" +authorizer: + class_name: "java.lang.String" + parameters: "java.util.Map" auth_write_consistency_level: "java.lang.String" counter_cache_size: "org.apache.cassandra.config.DataStorageSpec.LongMebibytesBound" denylist_range_reads_enabled: "java.lang.Boolean" diff --git a/test/unit/org/apache/cassandra/audit/AuditLoggerAuthTest.java b/test/unit/org/apache/cassandra/audit/AuditLoggerAuthTest.java index 50d20ea883..33a57eb29d 100644 --- a/test/unit/org/apache/cassandra/audit/AuditLoggerAuthTest.java +++ b/test/unit/org/apache/cassandra/audit/AuditLoggerAuthTest.java @@ -70,8 +70,8 @@ public class AuditLoggerAuthTest { OverrideConfigurationLoader.override((config) -> { config.authenticator = new ParameterizedClass("PasswordAuthenticator"); - config.role_manager = "CassandraRoleManager"; - config.authorizer = "CassandraAuthorizer"; + config.role_manager = new ParameterizedClass("CassandraRoleManager"); + config.authorizer = new ParameterizedClass("CassandraAuthorizer"); config.audit_logging_options.enabled = true; config.audit_logging_options.logger = new ParameterizedClass("InMemoryAuditLogger", null); }); diff --git a/test/unit/org/apache/cassandra/config/ConfigCompatibilityTest.java b/test/unit/org/apache/cassandra/config/ConfigCompatibilityTest.java index e6e714daaa..d94cb57920 100644 --- a/test/unit/org/apache/cassandra/config/ConfigCompatibilityTest.java +++ b/test/unit/org/apache/cassandra/config/ConfigCompatibilityTest.java @@ -102,8 +102,14 @@ public class ConfigCompatibilityTest // Switched to a parameterized class that can construct from a bare string .add("internode_authenticator types do not match; org.apache.cassandra.config.ParameterizedClass != java.lang.String") .add("authenticator types do not match; org.apache.cassandra.config.ParameterizedClass != java.lang.String") + .add("authorizer types do not match; org.apache.cassandra.config.ParameterizedClass != java.lang.String") + .add("network_authorizer types do not match; org.apache.cassandra.config.ParameterizedClass != java.lang.String") + .add("role_manager types do not match; org.apache.cassandra.config.ParameterizedClass != java.lang.String") .add("Property internode_authenticator used to be a value-type, but now is nested type class org.apache.cassandra.config.ParameterizedClass") .add("Property authenticator used to be a value-type, but now is nested type class org.apache.cassandra.config.ParameterizedClass") + .add("Property authorizer used to be a value-type, but now is nested type class org.apache.cassandra.config.ParameterizedClass") + .add("Property role_manager used to be a value-type, but now is nested type class org.apache.cassandra.config.ParameterizedClass") + .add("Property network_authorizer used to be a value-type, but now is nested type class org.apache.cassandra.config.ParameterizedClass") .build(); /** diff --git a/test/unit/org/apache/cassandra/transport/CQLUserAuditTest.java b/test/unit/org/apache/cassandra/transport/CQLUserAuditTest.java index 6b7323c9ab..b0d1debfda 100644 --- a/test/unit/org/apache/cassandra/transport/CQLUserAuditTest.java +++ b/test/unit/org/apache/cassandra/transport/CQLUserAuditTest.java @@ -65,7 +65,7 @@ public class CQLUserAuditTest { OverrideConfigurationLoader.override((config) -> { config.authenticator = new ParameterizedClass("PasswordAuthenticator"); - config.role_manager = "CassandraRoleManager"; + config.role_manager = new ParameterizedClass("CassandraRoleManager"); config.diagnostic_events_enabled = true; config.audit_logging_options.enabled = true; config.audit_logging_options.logger = new ParameterizedClass("DiagnosticEventAuditLogger", null);