diff --git a/README.adoc b/README.adoc index 7893863..7599197 100644 --- a/README.adoc +++ b/README.adoc @@ -26,7 +26,8 @@ There are five implementation modules: * cassandra-3.0 - builds against version 3.0.28 * cassandra-3.11 - builds against version 3.11.14 * cassandra-4.0 - builds against version 4.0.7 -* cassandra-4.1 - builds aganst version 4.1.0 +* cassandra-4.1 - builds aganist version 4.1.0 +* cassandra-5.0 - builds aganist version 5.0.0 Project is built as: diff --git a/cassandra-5.0/pom.xml b/cassandra-5.0/pom.xml new file mode 100644 index 0000000..f42dc57 --- /dev/null +++ b/cassandra-5.0/pom.xml @@ -0,0 +1,181 @@ + + + 4.0.0 + + + com.instaclustr + cassandra-ldap-parent + 1.1.2 + ../pom.xml + + + cassandra-ldap-5.0.0 + 1.0.0 + + Cassandra LDAP Authenticator for Cassandra 5.0 + Pluggable LDAP authentication implementation for Apache Cassandra 5.0 + + + 5.0.0 + + 1.2.6 + 3.1.3 + + 4.0.1 + 3.11.0 + 6.14.3 + 4.0.3 + 1.15.3 + + + + + + org.jboss.shrinkwrap + shrinkwrap-bom + ${version.shrinkwrap.bom} + pom + import + + + + + + + org.apache.cassandra + cassandra-all + ${version.cassandra50} + provided + + + + com.instaclustr + cassandra-ldap-base + 1.1.2 + + + org.apache.cassandra + cassandra-all + + + + + + + + org.jboss.shrinkwrap + shrinkwrap-depchain + pom + test + + + + org.jboss.shrinkwrap.resolver + shrinkwrap-resolver-depchain + ${version.shrinkwrap.resolvers} + test + pom + + + + com.github.nosan + embedded-cassandra + ${version.embedded.cassandra} + test + + + com.datastax.oss + java-driver-core + + + + + + com.datastax.cassandra + cassandra-driver-core + ${version.cassandra.driver} + + + com.google.guava + guava + + + io.netty + netty-handler + + + io.netty + netty-buffer + + + io.netty + netty-codec + + + + + + org.testng + testng + ${version.testng} + test + + + + org.awaitility + awaitility + ${version.awaitility} + test + + + + org.testcontainers + testcontainers + ${version.testcontainers} + test + + + + + + + org.apache.maven.plugins + maven-shade-plugin + ${maven.shade.plugin.version} + + cassandra-ldap-${version.cassandra41}-${project.version} + + + + org.vafer + jdeb + ${version.jdeb} + + + de.dentrassi.maven + rpm + ${version.rpm} + + + package + + rpm + + + + + cassandra + 4.0 + + + + + + + + + + + + diff --git a/cassandra-5.0/src/deb/control/control b/cassandra-5.0/src/deb/control/control new file mode 100644 index 0000000..bf616c9 --- /dev/null +++ b/cassandra-5.0/src/deb/control/control @@ -0,0 +1,7 @@ +Package: [[name]] +Version: [[version]] +Section: misc +Priority: optional +Architecture: all +Depends: cassandra (>= 5.0) +Maintainer: [[maintainer]] diff --git a/cassandra-5.0/src/main/java/com/instaclustr/cassandra/ldap/auth/Cassandra50SystemAuthRoles.java b/cassandra-5.0/src/main/java/com/instaclustr/cassandra/ldap/auth/Cassandra50SystemAuthRoles.java new file mode 100644 index 0000000..9399e5b --- /dev/null +++ b/cassandra-5.0/src/main/java/com/instaclustr/cassandra/ldap/auth/Cassandra50SystemAuthRoles.java @@ -0,0 +1,192 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.instaclustr.cassandra.ldap.auth; + +import static java.lang.String.format; +import static java.util.Collections.singletonList; +import static org.apache.cassandra.db.ConsistencyLevel.LOCAL_ONE; + +import java.util.Collections; + +import com.google.common.base.Function; +import org.apache.cassandra.auth.AuthKeyspace; +import org.apache.cassandra.auth.LDAPCassandraRoleManager.Role; +import org.apache.cassandra.cql3.CQLStatement; +import org.apache.cassandra.cql3.QueryOptions; +import org.apache.cassandra.cql3.QueryProcessor; +import org.apache.cassandra.cql3.UntypedResultSet; +import org.apache.cassandra.cql3.UntypedResultSet.Row; +import org.apache.cassandra.cql3.statements.CreateRoleStatement; +import org.apache.cassandra.cql3.statements.SelectStatement; +import org.apache.cassandra.cql3.statements.GrantRoleStatement; +import org.apache.cassandra.db.ConsistencyLevel; +import org.apache.cassandra.db.marshal.UTF8Type; +import org.apache.cassandra.exceptions.RequestExecutionException; +import org.apache.cassandra.exceptions.RequestValidationException; +import org.apache.cassandra.service.ClientState; +import org.apache.cassandra.service.QueryState; +import org.apache.cassandra.transport.Dispatcher; +import org.apache.cassandra.transport.messages.ResultMessage; +import org.apache.cassandra.utils.ByteBufferUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Cassandra50SystemAuthRoles implements SystemAuthRoles +{ + + private static final Logger logger = LoggerFactory.getLogger(SystemAuthRoles.class); + + public static final String SELECT_ROLE_STATEMENT = "SELECT role FROM %s.%s where role = ?"; + + public static final String CREATE_ROLE_STATEMENT_WITH_LOGIN = "CREATE ROLE IF NOT EXISTS \"%s\" WITH LOGIN = true AND SUPERUSER = %s"; + + public static final String GRANT_ROLE_STATEMENT = "GRANT '%s' TO '%s'"; + + private ClientState clientState; + + public void setClientState(ClientState clientState) + { + this.clientState = clientState; + } + + public ClientState getClientState() + { + return clientState; + } + + public boolean hasAdminRole(String role) throws RequestExecutionException + { + // Try looking up the 'cassandra' default role first, to avoid the range query if possible. + String defaultSUQuery = "SELECT * FROM system_auth.roles WHERE role = '" + role + "'"; + String allUsersQuery = "SELECT * FROM system_auth.roles LIMIT 1"; + return !QueryProcessor.process(defaultSUQuery, ConsistencyLevel.ONE).isEmpty() + || !QueryProcessor.process(defaultSUQuery, ConsistencyLevel.QUORUM).isEmpty() + || !QueryProcessor.process(allUsersQuery, ConsistencyLevel.QUORUM).isEmpty(); + } + + + public boolean hasAdminRole() throws RequestExecutionException + { + return hasAdminRole("cassandra"); + } + + public CQLStatement prepare(String template, String keyspace, String table) { + try { + return QueryProcessor.parseStatement(String.format(template, keyspace, table)).prepare(ClientState.forInternalCalls()); + } catch (RequestValidationException e) { + throw new AssertionError(e); // not supposed to happen + } + } + + protected ConsistencyLevel getConsistencyForRole(String defaultSuperUserName, String role, ConsistencyLevel roleConsistencyLevel) { + ConsistencyLevel cl = role.equals(defaultSuperUserName) ? ConsistencyLevel.QUORUM : roleConsistencyLevel; + + logger.debug(String.format("Resolved consistency level for role %s: %s", role, cl)); + + return cl; + } + + // NullObject returned when a supplied role name not found in AuthKeyspace.ROLES + protected static final Role NULL_ROLE = new Role(null, false, false, Collections.emptySet()); + + protected static final Function ROW_TO_ROLE = new Function() { + public Role apply(UntypedResultSet.Row row) { + try { + return new Role(row.getString("role"), + row.getBoolean("is_superuser"), + row.getBoolean("can_login"), + row.has("member_of") ? row.getSet("member_of", UTF8Type.instance) + : Collections.emptySet()); + } + // Failing to deserialize a boolean in is_superuser or can_login will throw an NPE + catch (NullPointerException e) { + logger.warn("An invalid value has been detected in the {} table for role {}. If you are " + + "unable to login, you may need to disable authentication and confirm " + + "that values in that table are accurate", AuthKeyspace.ROLES, row.getString("role")); + throw new RuntimeException(String.format("Invalid metadata has been detected for role %s", row.getString("role")), e); + } + + } + }; + + + public boolean roleMissing(String dn) { + assert getClientState() != null; + + final SelectStatement selStmt = (SelectStatement) QueryProcessor.getStatement(format(SELECT_ROLE_STATEMENT, + "system_auth", + AuthKeyspace.ROLES), + getClientState()); + + final ResultMessage.Rows rows = selStmt.execute(new QueryState(getClientState()), + QueryOptions.forInternalCalls(singletonList(ByteBufferUtil.bytes(dn))), + Dispatcher.RequestTime.forImmediateExecution()); + + return rows.result.isEmpty(); + } + + public void createRole(String roleName, boolean superUser, String defaultRoleMembership) + { + final CreateRoleStatement createStmt = (CreateRoleStatement) QueryProcessor.getStatement(format(CREATE_ROLE_STATEMENT_WITH_LOGIN, + roleName, + superUser), + getClientState()); + + createStmt.execute(new QueryState(getClientState()), + QueryOptions.forInternalCalls(LOCAL_ONE, singletonList(ByteBufferUtil.bytes(roleName))), + Dispatcher.RequestTime.forImmediateExecution()); + + if (defaultRoleMembership != null) + { + if (roleMissing(defaultRoleMembership)) + { + logger.warn("Unable to add user to default role {} because it doesn't exist.", defaultRoleMembership); + } + else + { + logger.debug("Adding user {} to default role {}", roleName, defaultRoleMembership); + final GrantRoleStatement grantRoleStmt = (GrantRoleStatement) QueryProcessor.getStatement(format(GRANT_ROLE_STATEMENT, + defaultRoleMembership, + roleName), + getClientState()); + + grantRoleStmt.execute(new QueryState(getClientState()), + QueryOptions.forInternalCalls(LOCAL_ONE, singletonList(ByteBufferUtil.bytes(roleName))), + Dispatcher.RequestTime.forImmediateExecution()); + } + } + } + + @Override + public Role getRole(String name, ConsistencyLevel roleConsistencyLevel) + throws RequestExecutionException, RequestValidationException { + + SelectStatement loadRoleStatement = (SelectStatement) prepare("SELECT * from %s.%s WHERE role = ?", "system_auth", "roles"); + + ResultMessage.Rows rows = loadRoleStatement.execute(QueryState.forInternalCalls(), + QueryOptions.forInternalCalls(getConsistencyForRole("cassandra", name, roleConsistencyLevel), + Collections.singletonList(ByteBufferUtil.bytes(name))), + Dispatcher.RequestTime.forImmediateExecution()); + + if (rows.result.isEmpty()) { + return NULL_ROLE; + } + + return ROW_TO_ROLE.apply(UntypedResultSet.create(rows.result).one()); + } +} diff --git a/cassandra-5.0/src/main/java/com/instaclustr/cassandra/ldap/auth/Cassandra50UserRetriever.java b/cassandra-5.0/src/main/java/com/instaclustr/cassandra/ldap/auth/Cassandra50UserRetriever.java new file mode 100644 index 0000000..f48176a --- /dev/null +++ b/cassandra-5.0/src/main/java/com/instaclustr/cassandra/ldap/auth/Cassandra50UserRetriever.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.instaclustr.cassandra.ldap.auth; + +import static java.util.Collections.singletonList; + +import com.instaclustr.cassandra.ldap.User; +import org.apache.cassandra.cql3.QueryOptions; +import org.apache.cassandra.cql3.QueryProcessor; +import org.apache.cassandra.cql3.statements.SelectStatement; +import org.apache.cassandra.schema.Schema; +import org.apache.cassandra.service.ClientState; +import org.apache.cassandra.service.QueryState; +import org.apache.cassandra.transport.Dispatcher; +import org.apache.cassandra.transport.messages.ResultMessage.Rows; +import org.apache.cassandra.utils.ByteBufferUtil; + +public class Cassandra50UserRetriever extends AbstractCassandraUserRetriever +{ + + @Override + public void init(ClientState clientState) + { + this.clientState = clientState; + authenticateStatement = (SelectStatement) QueryProcessor.getStatement("SELECT salted_hash FROM system_auth.roles WHERE role = ?", clientState); + + legacyTableExists = legacyCredentialsTableExists(); + + if (legacyTableExists) + { + prepareLegacyAuthenticateStatementInternal(clientState); + } + } + + @Override + public Rows getRows(User user) + { + return authenticationStatement(clientState, legacyTableExists).execute(QueryState.forInternalCalls(), + QueryOptions.forInternalCalls(consistencyForRole(user.getUsername()), + singletonList(ByteBufferUtil.bytes(user.getUsername()))), + Dispatcher.RequestTime.forImmediateExecution()); + } + + @Override + protected void prepareLegacyAuthenticateStatementInternal(final ClientState clientState) + { + String query = String.format("SELECT salted_hash from %s.%s WHERE username = ?", + AUTH_KEYSPACE, + LEGACY_CREDENTIALS_TABLE); + legacyAuthenticateStatement = (SelectStatement) QueryProcessor.getStatement(query, clientState); + } + + /** + * If the legacy users table exists try to verify credentials there. This is to handle the case + * where the cluster is being upgraded and so is running with mixed versions of the auth tables + */ + protected SelectStatement authenticationStatement(final ClientState clientState, + final boolean legacyTableExists) + { + if (!legacyTableExists) + { + return (SelectStatement) QueryProcessor.getStatement("SELECT salted_hash FROM system_auth.roles WHERE role = ?", clientState); + } else + { + // the statement got prepared, we to try preparing it again. + // If the credentials was initialised only after statement got prepared, re-prepare (CASSANDRA-12813). + if (legacyAuthenticateStatement == null) + { + prepareLegacyAuthenticateStatementInternal(clientState); + } + return legacyAuthenticateStatement; + } + } + + @Override + protected boolean legacyCredentialsTableExists() + { + return Schema.instance.getTableMetadata(AUTH_KEYSPACE, LEGACY_CREDENTIALS_TABLE) != null; + } +} diff --git a/cassandra-5.0/src/main/java/com/instaclustr/cassandra/ldap/cache/Cassandra50CacheDelegate.java b/cassandra-5.0/src/main/java/com/instaclustr/cassandra/ldap/cache/Cassandra50CacheDelegate.java new file mode 100644 index 0000000..cf0bdb8 --- /dev/null +++ b/cassandra-5.0/src/main/java/com/instaclustr/cassandra/ldap/cache/Cassandra50CacheDelegate.java @@ -0,0 +1,106 @@ +package com.instaclustr.cassandra.ldap.cache; + +import java.util.HashMap; +import java.util.function.Function; + +import com.instaclustr.cassandra.ldap.User; +import org.apache.cassandra.auth.AuthCache; +import org.apache.cassandra.config.DatabaseDescriptor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Cassandra50CacheDelegate implements CacheDelegate +{ + + private static final Logger logger = LoggerFactory.getLogger(Cassandra50CacheDelegate.class); + + private AuthCache cassandraCache; + private AuthCache ldapCache; + + @Override + public void invalidate(final User user) + { + assert cassandraCache != null; + this.cassandraCache.invalidate(user); + this.ldapCache.invalidate(user); + } + + @Override + public User get(final User user) + { + assert cassandraCache != null; + assert ldapCache != null; + + try + { + try + { + User cassandraUser = this.cassandraCache.get(user); + + if (cassandraUser != null) + { + logger.info("Fetching user from Cassandra: " + user.toString()); + return cassandraUser; + } + } catch (final Exception ex) + { + logger.info("{} not found in Cassandra", user); + } + + User ldapUser = this.ldapCache.get(user); + + logger.debug("{} fetched user from LDAP", ldapUser); + + return ldapUser; + } catch (final Exception ex) + { + throw new RuntimeException(ex); + } + } + + @Override + public void init(final Function cassandraLoadingFunction, + final Function ldapLoadingFunction, + final boolean enableCache) + { + if (this.cassandraCache != null && this.ldapCache != null) + { + return; + } + + this.cassandraCache = new CredentialsCache(cassandraLoadingFunction, "CredentialsCache", enableCache); + this.ldapCache = new CredentialsCache(ldapLoadingFunction, "LdapCredentialsCache", enableCache); + } + + private static class CredentialsCache extends AuthCache implements CredentialsCacheMBean + { + + private static final Logger logger = LoggerFactory.getLogger(CredentialsCache.class); + + public CredentialsCache(Function loadingFunction, String cacheName, boolean enableCache) + { + super(cacheName, + DatabaseDescriptor::setCredentialsValidity, + DatabaseDescriptor::getCredentialsValidity, + DatabaseDescriptor::setCredentialsUpdateInterval, + DatabaseDescriptor::getCredentialsUpdateInterval, + DatabaseDescriptor::setCredentialsCacheMaxEntries, + DatabaseDescriptor::getCredentialsCacheMaxEntries, + DatabaseDescriptor::setCredentialsCacheActiveUpdate, + DatabaseDescriptor::getCredentialsCacheActiveUpdate, + loadingFunction, + HashMap::new, + () -> + { + logger.info(String.format("Using cache %s, enabled: %s", cacheName, enableCache)); + + return enableCache; + }); + } + + public void invalidateCredentials(String username) + { + invalidate(new User(username)); + } + } +} diff --git a/cassandra-5.0/src/main/java/com/instaclustr/cassandra/ldap/cache/CredentialsCacheMBean.java b/cassandra-5.0/src/main/java/com/instaclustr/cassandra/ldap/cache/CredentialsCacheMBean.java new file mode 100644 index 0000000..32b5fec --- /dev/null +++ b/cassandra-5.0/src/main/java/com/instaclustr/cassandra/ldap/cache/CredentialsCacheMBean.java @@ -0,0 +1,9 @@ +package com.instaclustr.cassandra.ldap.cache; + +import org.apache.cassandra.auth.AuthCacheMBean; + +public interface CredentialsCacheMBean extends AuthCacheMBean +{ + + void invalidateCredentials(String username); +} diff --git a/cassandra-5.0/src/main/resources/META-INF/services/com.instaclustr.cassandra.ldap.auth.CassandraUserRetriever b/cassandra-5.0/src/main/resources/META-INF/services/com.instaclustr.cassandra.ldap.auth.CassandraUserRetriever new file mode 100644 index 0000000..bbdd8cd --- /dev/null +++ b/cassandra-5.0/src/main/resources/META-INF/services/com.instaclustr.cassandra.ldap.auth.CassandraUserRetriever @@ -0,0 +1 @@ +com.instaclustr.cassandra.ldap.auth.Cassandra50UserRetriever \ No newline at end of file diff --git a/cassandra-5.0/src/main/resources/META-INF/services/com.instaclustr.cassandra.ldap.auth.SystemAuthRoles b/cassandra-5.0/src/main/resources/META-INF/services/com.instaclustr.cassandra.ldap.auth.SystemAuthRoles new file mode 100644 index 0000000..5712a42 --- /dev/null +++ b/cassandra-5.0/src/main/resources/META-INF/services/com.instaclustr.cassandra.ldap.auth.SystemAuthRoles @@ -0,0 +1 @@ +com.instaclustr.cassandra.ldap.auth.Cassandra50SystemAuthRoles \ No newline at end of file diff --git a/cassandra-5.0/src/main/resources/META-INF/services/com.instaclustr.cassandra.ldap.cache.CacheDelegate b/cassandra-5.0/src/main/resources/META-INF/services/com.instaclustr.cassandra.ldap.cache.CacheDelegate new file mode 100644 index 0000000..1d5a26f --- /dev/null +++ b/cassandra-5.0/src/main/resources/META-INF/services/com.instaclustr.cassandra.ldap.cache.CacheDelegate @@ -0,0 +1 @@ +com.instaclustr.cassandra.ldap.cache.Cassandra50CacheDelegate \ No newline at end of file diff --git a/cassandra-5.0/src/test/java/com/instaclustr/cassandra/ldap/auth/AbstractLDAPTest.java b/cassandra-5.0/src/test/java/com/instaclustr/cassandra/ldap/auth/AbstractLDAPTest.java new file mode 100644 index 0000000..9e32fc4 --- /dev/null +++ b/cassandra-5.0/src/test/java/com/instaclustr/cassandra/ldap/auth/AbstractLDAPTest.java @@ -0,0 +1,348 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.instaclustr.cassandra.ldap.auth; + +import java.io.BufferedInputStream; +import java.io.Closeable; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileWriter; +import java.io.InputStream; +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Properties; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.datastax.driver.core.Cluster; +import com.datastax.driver.core.PlainTextAuthProvider; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.policies.DCAwareRoundRobinPolicy; +import com.github.nosan.embedded.cassandra.Cassandra; +import com.github.nosan.embedded.cassandra.CassandraBuilder; +import com.github.nosan.embedded.cassandra.WorkingDirectoryCustomizer; +import com.github.nosan.embedded.cassandra.WorkingDirectoryDestroyer; +import com.github.nosan.embedded.cassandra.commons.ClassPathResource; +import com.github.nosan.embedded.cassandra.commons.FileSystemResource; +import org.testcontainers.containers.Container; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.HostPortWaitStrategy; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +import static com.github.nosan.embedded.cassandra.WorkingDirectoryCustomizer.addResource; +import static java.util.Arrays.stream; +import static java.util.stream.Collectors.toList; +import static org.awaitility.Awaitility.await; +import static org.awaitility.Durations.FIVE_MINUTES; +import static org.jboss.shrinkwrap.resolver.api.maven.Maven.resolver; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +public abstract class AbstractLDAPTest { + + private static final Logger logger = LoggerFactory.getLogger(AbstractLDAPTest.class); + private static final String cassandraAdminUser = "cassandra"; + private static final String cassandraAdminPassword = "cassandra"; + private static final String cassandraDataCenter1 = "datacenter1"; + private static final String testUserName = "bill"; + private static final String testUserPassword = "test"; + private static final String testUserDn = "cn=bill,dc=example,dc=org"; + private static final String defaultRoleName = "default_role"; + private static final String basicQuery = "SELECT * FROM system.local;"; + + protected void testLDAPinternal() throws Exception { + try (final GenericContainer ldapContainer = prepareLdapContainer(); + final CassandraClusterContext context = getClusterContext(true, ldapContainer.getMappedPort(389))) { + + context.start(); + + context.execute(context.firstNode, + cassandraAdminUser, + cassandraAdminPassword, + "ALTER KEYSPACE system_auth WITH replication = {'class': 'NetworkTopologyStrategy', 'datacenter1': 1, 'datacenter2':1};", cassandraDataCenter1, false); + + logger.info("[first node]: login via cassandra"); + context.execute(context.firstNode, cassandraAdminUser, cassandraAdminPassword, "select * from system_auth.roles", cassandraDataCenter1, true); + logger.info("[first node]: login bill"); + context.execute(context.firstNode, testUserName, testUserPassword, basicQuery, cassandraDataCenter1, true); + + logger.info("[second node]: login bill"); + context.execute(context.secondNode, testUserName, testUserPassword, basicQuery, "datacenter2", true); + + testDefaultRoleMembership(ldapContainer, context); + } catch (final Exception ex) { + fail("Exception occurred!", ex); + } + } + + protected void testDefaultRoleMembership(GenericContainer ldapContainer, CassandraClusterContext context) throws Exception + { + // Create the default role + context.simpleExecute( + context.firstNode, + cassandraAdminUser, + cassandraAdminPassword, + String.format("CREATE ROLE '%s';", defaultRoleName), + cassandraDataCenter1 + ); + + // Delete the user if it already exists + context.simpleExecute( + context.firstNode, + cassandraAdminUser, + cassandraAdminPassword, + String.format("DROP ROLE IF EXISTS '%s';", testUserDn), + cassandraDataCenter1 + ); + + // Simulate a Logon as the user + context.simpleExecute( + context.firstNode, + testUserName, + testUserPassword, + basicQuery, + cassandraDataCenter1 + ); + + // Check that the default role has been added to the user + assertTrue( + context.simpleExecute( + context.firstNode, + cassandraAdminUser, + cassandraAdminPassword, + String.format("LIST ROLES OF '%s';", testUserDn), + cassandraDataCenter1 + ).all().stream().anyMatch(row -> row.getString("role").equals(defaultRoleName)) + ); + } + + public abstract String getCassandraVersion(); + + public abstract String getImplementationGAV(); + + private CassandraClusterContext getClusterContext(boolean ldapEnabled, int ldapPort) { + CassandraClusterContext cassandraClusterContext = new CassandraClusterContext(); + cassandraClusterContext.firstNode = configure(ldapEnabled, "first", ldapPort).build(); + cassandraClusterContext.secondNode = configure(ldapEnabled, "second", ldapPort).build(); + return cassandraClusterContext; + } + + private static class CassandraClusterContext implements Closeable { + + public Cassandra firstNode; + public Cassandra secondNode; + + public void start() { + firstNode.start(); + waitForOpenPort("127.0.0.1", 9042); + secondNode.start(); + waitForOpenPort("127.0.0.2", 9042); + } + + @Override + public void close() { + if (firstNode != null) { + firstNode.stop(); + waitForClosedPort("127.0.0.1", 9042); + firstNode = null; + } + + if (secondNode != null) { + secondNode.stop(); + waitForClosedPort("127.0.0.2", 9042); + secondNode = null; + } + } + + public synchronized void execute(Cassandra node, + String username, + String password, + String query, + String dc, + boolean check) { + execute(node.getSettings().getAddress(), username, password, query, dc, check); + } + + public synchronized void execute(InetAddress point, + String username, + String password, + String query, + String dc, + boolean check) { + try (final Session session = Cluster.builder() + .addContactPoint(point.getHostAddress()) + .withLoadBalancingPolicy(new DCAwareRoundRobinPolicy.Builder().withLocalDc(dc).build()) + .withAuthProvider(new PlainTextAuthProvider(username, password)) + .build().connect()) { + ResultSet execute = session.execute(query); + + if (check) { + assertNotNull(execute); + assertFalse(execute.all().isEmpty()); + assertTrue(execute.isFullyFetched()); + } + } catch (final Exception ex) { + fail("Failed to execute a request!", ex); + } + } + + public synchronized ResultSet simpleExecute(Cassandra node, + String username, + String password, + String query, + String dc) + { + try (final Session session = Cluster.builder() + .addContactPoint(node.getSettings().getAddress().getHostAddress()) + .withLoadBalancingPolicy(new DCAwareRoundRobinPolicy.Builder().withLocalDc(dc).build()) + .withAuthProvider(new PlainTextAuthProvider(username, password)) + .build().connect()) { + return session.execute(query); + } + } + + public void waitForClosedPort(String hostname, int port) { + await().timeout(FIVE_MINUTES).until(() -> + { + try { + (new Socket(hostname, port)).close(); + return false; + } catch (SocketException e) { + return true; + } + }); + } + + public void waitForOpenPort(String hostname, int port) { + await().timeout(FIVE_MINUTES).until(() -> + { + try { + (new Socket(hostname, port)).close(); + return true; + } catch (SocketException e) { + return false; + } + }); + } + } + + protected CassandraBuilder configure(final boolean ldap, final String node, final int ldapPort) { + final List pluginJars = stream(resolver() + .loadPomFromFile("pom.xml") + .resolve(getImplementationGAV()) + .withTransitivity() + .asFile()).map(file -> file.toPath().toAbsolutePath()).collect(toList()); + + final File ldapPropertiesFile = getLdapPropertiesFile(ldapPort); + + return new CassandraBuilder() + .version(getCassandraVersion()) + .addJvmOptions("-Xmx1g", "-Xms1g") + .addSystemProperties(new HashMap() {{ + put("cassandra.jmx.local.port", node.equals("first") ? "7199" : "7200"); + put("cassandra.ring_delay_ms", "1000"); + put("cassandra.ldap.properties.file", ldapPropertiesFile.toPath().toAbsolutePath().toString()); + }}) + .workingDirectory(() -> Files.createTempDirectory(null)) + .addWorkingDirectoryCustomizers(new ArrayList() {{ + if (ldap) { + add(addResource(new ClassPathResource(node + "-ldap.yaml"), "conf/cassandra.yaml")); + } else { + add(addResource(new ClassPathResource(node + ".yaml"), "conf/cassandra.yaml")); + } + add(addResource(new ClassPathResource(node + "-rackdc.properties"), "conf/cassandra-rackdc.properties")); + for (Path pluginJar : pluginJars) { + add(addResource(new FileSystemResource(pluginJar), "lib/" + pluginJar.getFileName().toString())); + } + }}.toArray(new WorkingDirectoryCustomizer[0])) + .workingDirectoryDestroyer(WorkingDirectoryDestroyer.doNothing()); + } + + + protected GenericContainer prepareLdapContainer() throws Exception { + GenericContainer ldapContainer = new GenericContainer(DockerImageName.parse("osixia/openldap:latest")) + .withCopyFileToContainer(MountableFile.forHostPath("../conf/new-user.ldif"), "/new-user.ldif") + .withEnv("LDAP_ADMIN_PASSWORD", "admin") + .withExposedPorts(389) + .waitingFor(new HostPortWaitStrategy()); + + ldapContainer.start(); + + Container.ExecResult result = addLdapUser(ldapContainer); + + while (result.getExitCode() != 0) { + logger.error(result.getStderr()); + if (result.getStderr().contains("Already exists")) { + break; + } + Thread.sleep(5000); + result = addLdapUser(ldapContainer); + } + + logger.info(result.getStdout()); + + return ldapContainer; + } + + private Container.ExecResult addLdapUser(GenericContainer ldapContainer) throws Exception { + return ldapContainer.execInContainer( + "ldapadd", + "-x", + "-D", + "cn=admin,dc=example,dc=org", + "-w", + "admin", + "-f", + "/new-user.ldif", + "-H", + "ldap://127.0.0.1:389"); + } + + protected File getLdapPropertiesFile(int ldapPort) { + try { + File ldapPropertiesFile = Paths.get("../conf/ldap.properties").toFile(); + Properties ldapProperties = new Properties(); + + try (InputStream is = new BufferedInputStream(new FileInputStream(ldapPropertiesFile))) { + ldapProperties.load(is); + } catch (Exception ex) { + throw new IllegalStateException("Unable to read content of ldap.properties!"); + } + + ldapProperties.setProperty("ldap_uri", "ldap://127.0.0.1:" + ldapPort + "/dc=example,dc=org"); + + File tempFile = Files.createTempFile("ldap-test", ".properties").toFile(); + ldapProperties.store(new FileWriter(tempFile, true), "comments"); + return tempFile; + } catch (Exception ex) { + throw new IllegalStateException("Unable to create ldap properties file for test.", ex); + } + } +} diff --git a/cassandra-5.0/src/test/java/com/instaclustr/cassandra/ldap/auth/Cassandra50LDAPIntegrationTest.java b/cassandra-5.0/src/test/java/com/instaclustr/cassandra/ldap/auth/Cassandra50LDAPIntegrationTest.java new file mode 100644 index 0000000..2003e63 --- /dev/null +++ b/cassandra-5.0/src/test/java/com/instaclustr/cassandra/ldap/auth/Cassandra50LDAPIntegrationTest.java @@ -0,0 +1,20 @@ +package com.instaclustr.cassandra.ldap.auth; + +import org.testng.annotations.Test; + +public class Cassandra50LDAPIntegrationTest extends AbstractLDAPTest { + @Override + public String getCassandraVersion() { + return System.getProperty("version.cassandra50", "5.0.0"); + } + + @Override + public String getImplementationGAV() { + return "com.instaclustr:cassandra-ldap-" + getCassandraVersion() + ":1.0.0"; + } + + @Test + public void ldapTest() throws Exception { + super.testLDAPinternal(); + } +} diff --git a/cassandra-5.0/src/test/resources/first-ldap.yaml b/cassandra-5.0/src/test/resources/first-ldap.yaml new file mode 100644 index 0000000..566178f --- /dev/null +++ b/cassandra-5.0/src/test/resources/first-ldap.yaml @@ -0,0 +1,104 @@ +cluster_name: Test Cluster +num_tokens: 256 +hinted_handoff_enabled: true +max_hint_window_in_ms: 10800000 +hinted_handoff_throttle_in_kb: 1024 +max_hints_delivery_threads: 2 +hints_flush_period_in_ms: 10000 +max_hints_file_size_in_mb: 128 +batchlog_replay_throttle_in_kb: 1024 +authenticator: LDAPAuthenticator +authorizer: CassandraAuthorizer +role_manager: LDAPCassandraRoleManager +roles_validity_in_ms: 2000 +permissions_validity_in_ms: 2000 +credentials_validity_in_ms: 2000 +partitioner: org.apache.cassandra.dht.Murmur3Partitioner +cdc_enabled: false +disk_failure_policy: stop +commit_failure_policy: stop +prepared_statements_cache_size_mb: null +key_cache_size_in_mb: null +key_cache_save_period: 14400 +row_cache_size_in_mb: 0 +row_cache_save_period: 0 +counter_cache_size_in_mb: null +counter_cache_save_period: 7200 +commitlog_sync: periodic +commitlog_sync_period_in_ms: 10000 +commitlog_segment_size_in_mb: 32 +seed_provider: + - class_name: org.apache.cassandra.locator.SimpleSeedProvider + parameters: + - {seeds: 127.0.0.1} +concurrent_reads: 32 +concurrent_writes: 32 +concurrent_counter_writes: 32 +concurrent_materialized_view_writes: 32 +memtable_allocation_type: heap_buffers +index_summary_capacity_in_mb: null +index_summary_resize_interval_in_minutes: 60 +trickle_fsync: false +trickle_fsync_interval_in_kb: 10240 +storage_port: 7000 +ssl_storage_port: 7001 +listen_address: localhost +start_native_transport: true +native_transport_port: 9042 +rpc_address: 127.0.0.1 +rpc_keepalive: true +incremental_backups: false +snapshot_before_compaction: false +auto_snapshot: true +column_index_size_in_kb: 64 +column_index_cache_size_in_kb: 2 +compaction_throughput_mb_per_sec: 16 +sstable_preemptive_open_interval_in_mb: 50 +read_request_timeout_in_ms: 5000 +range_request_timeout_in_ms: 10000 +write_request_timeout_in_ms: 2000 +counter_write_request_timeout_in_ms: 5000 +cas_contention_timeout_in_ms: 1000 +truncate_request_timeout_in_ms: 60000 +request_timeout_in_ms: 10000 +slow_query_log_timeout_in_ms: 500 +cross_node_timeout: false +endpoint_snitch: GossipingPropertyFileSnitch +dynamic_snitch_update_interval_in_ms: 100 +dynamic_snitch_reset_interval_in_ms: 600000 +dynamic_snitch_badness_threshold: 0.1 +server_encryption_options: {internode_encryption: none, keystore: conf/.keystore, + keystore_password: cassandra, truststore: conf/.truststore, truststore_password: cassandra} +client_encryption_options: {enabled: false, optional: false, keystore: conf/.keystore, + keystore_password: cassandra} +internode_compression: dc +inter_dc_tcp_nodelay: false +tracetype_query_ttl: 86400 +tracetype_repair_ttl: 604800 +enable_user_defined_functions: false +enable_scripted_user_defined_functions: false +windows_timer_interval: 1 +transparent_data_encryption_options: + enabled: false + chunk_length_kb: 64 + cipher: AES/CBC/PKCS5Padding + key_alias: testing:1 + key_provider: + - class_name: org.apache.cassandra.security.JKSKeyProvider + parameters: + - {keystore: conf/.keystore, keystore_password: cassandra, store_type: JCEKS, + key_password: cassandra} +tombstone_warn_threshold: 1000 +tombstone_failure_threshold: 100000 +batch_size_warn_threshold_in_kb: 5 +batch_size_fail_threshold_in_kb: 50 +unlogged_batch_across_partitions_warn_threshold: 10 +compaction_large_partition_warning_threshold_mb: 100 +gc_warn_threshold_in_ms: 1000 +back_pressure_enabled: false +back_pressure_strategy: + - class_name: org.apache.cassandra.net.RateBasedBackPressure + parameters: + - {high_ratio: 0.9, factor: 5, flow: FAST} +enable_materialized_views: true +enable_sasi_indexes: true diff --git a/cassandra-5.0/src/test/resources/first-rackdc.properties b/cassandra-5.0/src/test/resources/first-rackdc.properties new file mode 100644 index 0000000..b606d75 --- /dev/null +++ b/cassandra-5.0/src/test/resources/first-rackdc.properties @@ -0,0 +1,2 @@ +dc=datacenter1 +rack=rack1 \ No newline at end of file diff --git a/cassandra-5.0/src/test/resources/first.yaml b/cassandra-5.0/src/test/resources/first.yaml new file mode 100644 index 0000000..342d270 --- /dev/null +++ b/cassandra-5.0/src/test/resources/first.yaml @@ -0,0 +1,104 @@ +cluster_name: Test Cluster +num_tokens: 256 +hinted_handoff_enabled: true +max_hint_window_in_ms: 10800000 +hinted_handoff_throttle_in_kb: 1024 +max_hints_delivery_threads: 2 +hints_flush_period_in_ms: 10000 +max_hints_file_size_in_mb: 128 +batchlog_replay_throttle_in_kb: 1024 +authenticator: AllowAllAuthenticator +authorizer: AllowAllAuthorizer +role_manager: CassandraRoleManager +roles_validity_in_ms: 2000 +permissions_validity_in_ms: 2000 +credentials_validity_in_ms: 2000 +partitioner: org.apache.cassandra.dht.Murmur3Partitioner +cdc_enabled: false +disk_failure_policy: stop +commit_failure_policy: stop +prepared_statements_cache_size_mb: null +key_cache_size_in_mb: null +key_cache_save_period: 14400 +row_cache_size_in_mb: 0 +row_cache_save_period: 0 +counter_cache_size_in_mb: null +counter_cache_save_period: 7200 +commitlog_sync: periodic +commitlog_sync_period_in_ms: 10000 +commitlog_segment_size_in_mb: 32 +seed_provider: +- class_name: org.apache.cassandra.locator.SimpleSeedProvider + parameters: + - {seeds: 127.0.0.1} +concurrent_reads: 32 +concurrent_writes: 32 +concurrent_counter_writes: 32 +concurrent_materialized_view_writes: 32 +memtable_allocation_type: heap_buffers +index_summary_capacity_in_mb: null +index_summary_resize_interval_in_minutes: 60 +trickle_fsync: false +trickle_fsync_interval_in_kb: 10240 +storage_port: 7000 +ssl_storage_port: 7001 +listen_address: localhost +start_native_transport: true +native_transport_port: 9042 +rpc_address: 127.0.0.1 +rpc_keepalive: true +incremental_backups: false +snapshot_before_compaction: false +auto_snapshot: true +column_index_size_in_kb: 64 +column_index_cache_size_in_kb: 2 +compaction_throughput_mb_per_sec: 16 +sstable_preemptive_open_interval_in_mb: 50 +read_request_timeout_in_ms: 5000 +range_request_timeout_in_ms: 10000 +write_request_timeout_in_ms: 2000 +counter_write_request_timeout_in_ms: 5000 +cas_contention_timeout_in_ms: 1000 +truncate_request_timeout_in_ms: 60000 +request_timeout_in_ms: 10000 +slow_query_log_timeout_in_ms: 500 +cross_node_timeout: false +endpoint_snitch: SimpleSnitch +dynamic_snitch_update_interval_in_ms: 100 +dynamic_snitch_reset_interval_in_ms: 600000 +dynamic_snitch_badness_threshold: 0.1 +server_encryption_options: {internode_encryption: none, keystore: conf/.keystore, + keystore_password: cassandra, truststore: conf/.truststore, truststore_password: cassandra} +client_encryption_options: {enabled: false, optional: false, keystore: conf/.keystore, + keystore_password: cassandra} +internode_compression: dc +inter_dc_tcp_nodelay: false +tracetype_query_ttl: 86400 +tracetype_repair_ttl: 604800 +enable_user_defined_functions: false +enable_scripted_user_defined_functions: false +windows_timer_interval: 1 +transparent_data_encryption_options: + enabled: false + chunk_length_kb: 64 + cipher: AES/CBC/PKCS5Padding + key_alias: testing:1 + key_provider: + - class_name: org.apache.cassandra.security.JKSKeyProvider + parameters: + - {keystore: conf/.keystore, keystore_password: cassandra, store_type: JCEKS, + key_password: cassandra} +tombstone_warn_threshold: 1000 +tombstone_failure_threshold: 100000 +batch_size_warn_threshold_in_kb: 5 +batch_size_fail_threshold_in_kb: 50 +unlogged_batch_across_partitions_warn_threshold: 10 +compaction_large_partition_warning_threshold_mb: 100 +gc_warn_threshold_in_ms: 1000 +back_pressure_enabled: false +back_pressure_strategy: +- class_name: org.apache.cassandra.net.RateBasedBackPressure + parameters: + - {high_ratio: 0.9, factor: 5, flow: FAST} +enable_materialized_views: true +enable_sasi_indexes: true diff --git a/cassandra-5.0/src/test/resources/logback.xml b/cassandra-5.0/src/test/resources/logback.xml new file mode 100644 index 0000000..8c6a9b2 --- /dev/null +++ b/cassandra-5.0/src/test/resources/logback.xml @@ -0,0 +1,17 @@ + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + + + + \ No newline at end of file diff --git a/cassandra-5.0/src/test/resources/second-ldap.yaml b/cassandra-5.0/src/test/resources/second-ldap.yaml new file mode 100644 index 0000000..fb60603 --- /dev/null +++ b/cassandra-5.0/src/test/resources/second-ldap.yaml @@ -0,0 +1,104 @@ +cluster_name: Test Cluster +num_tokens: 256 +hinted_handoff_enabled: true +max_hint_window_in_ms: 10800000 +hinted_handoff_throttle_in_kb: 1024 +max_hints_delivery_threads: 2 +hints_flush_period_in_ms: 10000 +max_hints_file_size_in_mb: 128 +batchlog_replay_throttle_in_kb: 1024 +authenticator: LDAPAuthenticator +authorizer: CassandraAuthorizer +role_manager: LDAPCassandraRoleManager +roles_validity_in_ms: 2000 +permissions_validity_in_ms: 2000 +credentials_validity_in_ms: 2000 +partitioner: org.apache.cassandra.dht.Murmur3Partitioner +cdc_enabled: false +disk_failure_policy: stop +commit_failure_policy: stop +prepared_statements_cache_size_mb: null +key_cache_size_in_mb: null +key_cache_save_period: 14400 +row_cache_size_in_mb: 0 +row_cache_save_period: 0 +counter_cache_size_in_mb: null +counter_cache_save_period: 7200 +commitlog_sync: periodic +commitlog_sync_period_in_ms: 10000 +commitlog_segment_size_in_mb: 32 +seed_provider: + - class_name: org.apache.cassandra.locator.SimpleSeedProvider + parameters: + - {seeds: 127.0.0.1} +concurrent_reads: 32 +concurrent_writes: 32 +concurrent_counter_writes: 32 +concurrent_materialized_view_writes: 32 +memtable_allocation_type: heap_buffers +index_summary_capacity_in_mb: null +index_summary_resize_interval_in_minutes: 60 +trickle_fsync: false +trickle_fsync_interval_in_kb: 10240 +storage_port: 7000 +ssl_storage_port: 7001 +listen_address: 127.0.0.2 +start_native_transport: true +native_transport_port: 9042 +rpc_address: 127.0.0.2 +rpc_keepalive: true +incremental_backups: false +snapshot_before_compaction: false +auto_snapshot: true +column_index_size_in_kb: 64 +column_index_cache_size_in_kb: 2 +compaction_throughput_mb_per_sec: 16 +sstable_preemptive_open_interval_in_mb: 50 +read_request_timeout_in_ms: 5000 +range_request_timeout_in_ms: 10000 +write_request_timeout_in_ms: 2000 +counter_write_request_timeout_in_ms: 5000 +cas_contention_timeout_in_ms: 1000 +truncate_request_timeout_in_ms: 60000 +request_timeout_in_ms: 10000 +slow_query_log_timeout_in_ms: 500 +cross_node_timeout: false +endpoint_snitch: GossipingPropertyFileSnitch +dynamic_snitch_update_interval_in_ms: 100 +dynamic_snitch_reset_interval_in_ms: 600000 +dynamic_snitch_badness_threshold: 0.1 +server_encryption_options: {internode_encryption: none, keystore: conf/.keystore, + keystore_password: cassandra, truststore: conf/.truststore, truststore_password: cassandra} +client_encryption_options: {enabled: false, optional: false, keystore: conf/.keystore, + keystore_password: cassandra} +internode_compression: dc +inter_dc_tcp_nodelay: false +tracetype_query_ttl: 86400 +tracetype_repair_ttl: 604800 +enable_user_defined_functions: false +enable_scripted_user_defined_functions: false +windows_timer_interval: 1 +transparent_data_encryption_options: + enabled: false + chunk_length_kb: 64 + cipher: AES/CBC/PKCS5Padding + key_alias: testing:1 + key_provider: + - class_name: org.apache.cassandra.security.JKSKeyProvider + parameters: + - {keystore: conf/.keystore, keystore_password: cassandra, store_type: JCEKS, + key_password: cassandra} +tombstone_warn_threshold: 1000 +tombstone_failure_threshold: 100000 +batch_size_warn_threshold_in_kb: 5 +batch_size_fail_threshold_in_kb: 50 +unlogged_batch_across_partitions_warn_threshold: 10 +compaction_large_partition_warning_threshold_mb: 100 +gc_warn_threshold_in_ms: 1000 +back_pressure_enabled: false +back_pressure_strategy: + - class_name: org.apache.cassandra.net.RateBasedBackPressure + parameters: + - {high_ratio: 0.9, factor: 5, flow: FAST} +enable_materialized_views: true +enable_sasi_indexes: true diff --git a/cassandra-5.0/src/test/resources/second-rackdc.properties b/cassandra-5.0/src/test/resources/second-rackdc.properties new file mode 100644 index 0000000..fd79ad0 --- /dev/null +++ b/cassandra-5.0/src/test/resources/second-rackdc.properties @@ -0,0 +1,2 @@ +dc=datacenter2 +rack=rack1 \ No newline at end of file diff --git a/cassandra-5.0/src/test/resources/second.yaml b/cassandra-5.0/src/test/resources/second.yaml new file mode 100644 index 0000000..8f736e3 --- /dev/null +++ b/cassandra-5.0/src/test/resources/second.yaml @@ -0,0 +1,104 @@ +cluster_name: Test Cluster +num_tokens: 256 +hinted_handoff_enabled: true +max_hint_window_in_ms: 10800000 +hinted_handoff_throttle_in_kb: 1024 +max_hints_delivery_threads: 2 +hints_flush_period_in_ms: 10000 +max_hints_file_size_in_mb: 128 +batchlog_replay_throttle_in_kb: 1024 +authenticator: AllowAllAuthenticator +authorizer: AllowAllAuthorizer +role_manager: CassandraRoleManager +roles_validity_in_ms: 2000 +permissions_validity_in_ms: 2000 +credentials_validity_in_ms: 2000 +partitioner: org.apache.cassandra.dht.Murmur3Partitioner +cdc_enabled: false +disk_failure_policy: stop +commit_failure_policy: stop +prepared_statements_cache_size_mb: null +key_cache_size_in_mb: null +key_cache_save_period: 14400 +row_cache_size_in_mb: 0 +row_cache_save_period: 0 +counter_cache_size_in_mb: null +counter_cache_save_period: 7200 +commitlog_sync: periodic +commitlog_sync_period_in_ms: 10000 +commitlog_segment_size_in_mb: 32 +seed_provider: + - class_name: org.apache.cassandra.locator.SimpleSeedProvider + parameters: + - {seeds: 127.0.0.1} +concurrent_reads: 32 +concurrent_writes: 32 +concurrent_counter_writes: 32 +concurrent_materialized_view_writes: 32 +memtable_allocation_type: heap_buffers +index_summary_capacity_in_mb: null +index_summary_resize_interval_in_minutes: 60 +trickle_fsync: false +trickle_fsync_interval_in_kb: 10240 +storage_port: 7000 +ssl_storage_port: 7001 +listen_address: 127.0.0.2 +start_native_transport: true +native_transport_port: 9042 +rpc_address: 127.0.0.2 +rpc_keepalive: true +incremental_backups: false +snapshot_before_compaction: false +auto_snapshot: true +column_index_size_in_kb: 64 +column_index_cache_size_in_kb: 2 +compaction_throughput_mb_per_sec: 16 +sstable_preemptive_open_interval_in_mb: 50 +read_request_timeout_in_ms: 5000 +range_request_timeout_in_ms: 10000 +write_request_timeout_in_ms: 2000 +counter_write_request_timeout_in_ms: 5000 +cas_contention_timeout_in_ms: 1000 +truncate_request_timeout_in_ms: 60000 +request_timeout_in_ms: 10000 +slow_query_log_timeout_in_ms: 500 +cross_node_timeout: false +endpoint_snitch: SimpleSnitch +dynamic_snitch_update_interval_in_ms: 100 +dynamic_snitch_reset_interval_in_ms: 600000 +dynamic_snitch_badness_threshold: 0.1 +server_encryption_options: {internode_encryption: none, keystore: conf/.keystore, + keystore_password: cassandra, truststore: conf/.truststore, truststore_password: cassandra} +client_encryption_options: {enabled: false, optional: false, keystore: conf/.keystore, + keystore_password: cassandra} +internode_compression: dc +inter_dc_tcp_nodelay: false +tracetype_query_ttl: 86400 +tracetype_repair_ttl: 604800 +enable_user_defined_functions: false +enable_scripted_user_defined_functions: false +windows_timer_interval: 1 +transparent_data_encryption_options: + enabled: false + chunk_length_kb: 64 + cipher: AES/CBC/PKCS5Padding + key_alias: testing:1 + key_provider: + - class_name: org.apache.cassandra.security.JKSKeyProvider + parameters: + - {keystore: conf/.keystore, keystore_password: cassandra, store_type: JCEKS, + key_password: cassandra} +tombstone_warn_threshold: 1000 +tombstone_failure_threshold: 100000 +batch_size_warn_threshold_in_kb: 5 +batch_size_fail_threshold_in_kb: 50 +unlogged_batch_across_partitions_warn_threshold: 10 +compaction_large_partition_warning_threshold_mb: 100 +gc_warn_threshold_in_ms: 1000 +back_pressure_enabled: false +back_pressure_strategy: + - class_name: org.apache.cassandra.net.RateBasedBackPressure + parameters: + - {high_ratio: 0.9, factor: 5, flow: FAST} +enable_materialized_views: true +enable_sasi_indexes: true diff --git a/pom.xml b/pom.xml index b907524..48ea28d 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,7 @@ cassandra-3.11 cassandra-4.0 cassandra-4.1 + cassandra-5.0 Cassandra LDAP Authenticator parent