This is an automated email from the ASF dual-hosted git repository.
frankgh pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra-sidecar.git
The following commit(s) were added to refs/heads/trunk by this push:
new b8ddad05 CASSSIDECAR-407: Add new settings under driver_parameters to
support pluggable withCredentials provider. Add two possible implementations,
ConfigProvider (which is the current implementation) and a new one,
FileProvider which reads username and password from the disk.
b8ddad05 is described below
commit b8ddad057c4db7b6ad061ba04323d9cc10622d96
Author: Michael Burman <[email protected]>
AuthorDate: Fri Feb 13 12:20:43 2026 +0200
CASSSIDECAR-407: Add new settings under driver_parameters to support
pluggable withCredentials provider. Add two possible implementations,
ConfigProvider (which is the current implementation) and a new one,
FileProvider which reads username and password from the disk.
Patch by Michael Burman; reviewed by Francisco Guerrero, Yifan Cai for
CASSSIDECAR-407
---
CHANGES.txt | 1 +
conf/sidecar.yaml | 7 +-
docs/src/user.adoc | 17 +-
.../resources/config/sidecar.yaml.template | 13 +-
.../sidecar/cluster/CQLSessionProviderImpl.java | 26 ++-
.../sidecar/cluster/auth/ConfigProvider.java | 64 +++++++
.../sidecar/cluster/auth/CqlAuthProvider.java | 35 ++++
.../sidecar/cluster/auth/FileProvider.java | 94 ++++++++++
.../sidecar/config/DriverConfiguration.java | 11 ++
.../config/yaml/DriverConfigurationImpl.java | 192 +++++++++++++++++++--
.../sidecar/modules/ConfigurationModule.java | 44 +++++
.../sidecar/testing/IntegrationTestModule.java | 15 +-
.../sidecar/cluster/auth/ConfigProviderTest.java | 48 ++++++
.../sidecar/cluster/auth/FileProviderTest.java | 75 ++++++++
.../sidecar/config/SidecarConfigurationTest.java | 16 ++
.../sidecar_driver_params_config_provider.yaml | 23 +++
16 files changed, 646 insertions(+), 35 deletions(-)
diff --git a/CHANGES.txt b/CHANGES.txt
index 589d6547..3ef59747 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,6 @@
0.4.0
-----
+ * Ability to load Cassandra connection secrets from filesystem
(CASSSIDECAR-407)
* Sidecar should wake up immediately on the instance receiving a phase signal
instead of waiting for the discovery loop (CASSSIDECAR-454)
* Fix schema initialization race, CI test reporting, and build stability
(CASSSIDECAR-468)
* Add FileBasedConfigurationProvider for file-based overlay persistence
(CASSSIDECAR-426)
diff --git a/conf/sidecar.yaml b/conf/sidecar.yaml
index 2a2e6d9c..3fc9d457 100644
--- a/conf/sidecar.yaml
+++ b/conf/sidecar.yaml
@@ -325,8 +325,11 @@ access_control:
driver_parameters:
contact_points:
- "127.0.0.1:9042"
- username: cassandra
- password: cassandra
+ auth_provider:
+ class_name: org.apache.cassandra.sidecar.cluster.auth.ConfigProvider
+ parameters:
+ username: cassandra
+ password: cassandra
ssl:
enabled: false
keystore:
diff --git a/docs/src/user.adoc b/docs/src/user.adoc
index d8ef90a7..ec6f6596 100644
--- a/docs/src/user.adoc
+++ b/docs/src/user.adoc
@@ -99,8 +99,16 @@ driver_parameters:
contact_points:
- "<host from cassandra_instances>:<port from cassandra_instances>"
- "<host of additional cassandra instances in your cluster>:<port of
instance>"
- username: <A user which has been configured on the local Cassandra cluster>
- password: <Password associated with the above user>
+ auth_provider:
+ class_name: org.apache.cassandra.sidecar.cluster.auth.ConfigProvider
+ parameters:
+ username: <A user configured on the local Cassandra cluster>
+ password: <Password associated with the above user>
+ # Alternative option is to read username and password from a file
+ # class_name: org.apache.cassandra.sidecar.cluster.auth.FileProvider
+ # parameters:
+ # username_path: <Path to file containing Cassandra username>
+ # password_path: <Path to file containing Cassandra password>
ssl:
enabled: <true if you have enabled encryption for CQL, false if otherwise>
keystore:
@@ -338,8 +346,9 @@ This section of the `sidecar.yaml` file configures how
requests are authenticate
The `driver_parameters` section of the `sidecar.yaml` file is used to
configure the Cassandra driver used by Cassandra Sidecar to connect to
Cassandra over CQL. The following properties are defined:
* `contact_points`: This is a list of IP Addresses or hostnames and ports of
the Cassandra instances that Cassandra Sidecar can use to bootstrap its
connection to the Cassandra cluster.
-* `username`: This is the username used to authenticate with the Cassandra
cluster.
-* `password`: This is the password used to authenticate with the Cassandra
cluster.
+* `auth_provider`: Provider entry used to resolve driver authentication
details. Implementation choices are
`org.apache.cassandra.sidecar.cluster.auth.FileProvider` and
`org.apache.cassandra.sidecar.cluster.auth.ConfigProvider`. If both
`auth_provider` and `username`/`password` are configured, Sidecar uses
`auth_provider` and uses `username`/`password` as fallback when no provider is
configured.
+* `username`: This is the username used to authenticate with the Cassandra
cluster. This is a legacy fallback and is deprecated in favor of
`auth_provider` with `ConfigProvider`.
+* `password`: This is the password used to authenticate with the Cassandra
cluster. This is a legacy fallback and is deprecated in favor of
`auth_provider` with `ConfigProvider`.
* `ssl`: This defines how the Cassandra driver can connect to Cassandra over
SSL. See the above <<ssl, SSL/TLS Configuration>> section for more information.
* `num_connections`: This defines the number of connections that the Cassandra
driver will open to each Cassandra instance. By default, 2.
* `local_dc`: This configures the local data center of the Cassandra driver.
Ensure that this is the same value as the data center of the Cassandra nodes
which this Cassandra Sidecar manages.
diff --git
a/integration-tests/src/integrationTest/resources/config/sidecar.yaml.template
b/integration-tests/src/integrationTest/resources/config/sidecar.yaml.template
index 79613d40..cea582f7 100644
---
a/integration-tests/src/integrationTest/resources/config/sidecar.yaml.template
+++
b/integration-tests/src/integrationTest/resources/config/sidecar.yaml.template
@@ -267,8 +267,17 @@ access_control:
driver_parameters:
contact_points:
- "127.0.0.1:9042"
- username: cassandra
- password: cassandra
+ auth_provider:
+ # Authentication information, implementing
org.apache.cassandra.sidecar.cluster.auth.CqlAuthProvider;
+ # used to provide username/password to connect to the Cassandra cluster.
+ # class_name: org.apache.cassandra.sidecar.cluster.auth.FileProvider
+ # parameters:
+ # username_path: /opt/sidecar/superuser/username
+ # password_path: /opt/sidecar/superuser/password
+ class_name: org.apache.cassandra.sidecar.cluster.auth.ConfigProvider
+ parameters:
+ username: cassandra
+ password: cassandra
ssl:
enabled: false
keystore:
diff --git
a/server/src/main/java/org/apache/cassandra/sidecar/cluster/CQLSessionProviderImpl.java
b/server/src/main/java/org/apache/cassandra/sidecar/cluster/CQLSessionProviderImpl.java
index 491ec362..7130a92d 100644
---
a/server/src/main/java/org/apache/cassandra/sidecar/cluster/CQLSessionProviderImpl.java
+++
b/server/src/main/java/org/apache/cassandra/sidecar/cluster/CQLSessionProviderImpl.java
@@ -27,7 +27,9 @@ import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@@ -51,6 +53,8 @@ import com.datastax.driver.core.policies.LoadBalancingPolicy;
import com.datastax.driver.core.policies.ReconnectionPolicy;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
+import org.apache.cassandra.sidecar.cluster.auth.ConfigProvider;
+import org.apache.cassandra.sidecar.cluster.auth.CqlAuthProvider;
import org.apache.cassandra.sidecar.cluster.driver.SidecarLoadBalancingPolicy;
import org.apache.cassandra.sidecar.common.server.CQLSessionProvider;
import org.apache.cassandra.sidecar.common.server.utils.DriverUtils;
@@ -80,8 +84,7 @@ public class CQLSessionProviderImpl implements
CQLSessionProvider
private final NettyOptions nettyOptions;
private final ReconnectionPolicy reconnectionPolicy;
private final List<InetSocketAddress> localInstances;
- private final String username;
- private final String password;
+ private final CqlAuthProvider authProvider;
private final DriverUtils driverUtils;
private volatile Session session;
@@ -119,8 +122,10 @@ public class CQLSessionProviderImpl implements
CQLSessionProvider
this.localInstances = localInstances;
this.localDc = localDc;
this.numAdditionalConnections = numAdditionalConnections;
- this.username = username;
- this.password = password;
+ Map<String, String> namedParameters = new HashMap<>();
+ namedParameters.put("username", username);
+ namedParameters.put("password", password);
+ this.authProvider = new ConfigProvider(namedParameters);
this.sslConfiguration = sslConfiguration;
this.nettyOptions = options;
this.reconnectionPolicy = new ExponentialReconnectionPolicy(500,
healthCheckFrequencyMillis);
@@ -129,6 +134,7 @@ public class CQLSessionProviderImpl implements
CQLSessionProvider
public CQLSessionProviderImpl(SidecarConfiguration configuration,
NettyOptions options,
+ CqlAuthProvider authProvider,
DriverUtils driverUtils)
{
this.driverUtils = driverUtils;
@@ -139,11 +145,10 @@ public class CQLSessionProviderImpl implements
CQLSessionProvider
.map(i -> new
InetSocketAddress(i.host(), i.port()))
.collect(Collectors.toList());
this.localDc = driverConfiguration.localDc();
- this.username = driverConfiguration.username();
- this.password = driverConfiguration.password();
this.sslConfiguration = driverConfiguration.sslConfiguration();
this.numAdditionalConnections = driverConfiguration.numConnections();
this.nettyOptions = options;
+ this.authProvider = authProvider;
long maxDelayMs =
configuration.healthCheckConfiguration().executeInterval().toMillis();
this.reconnectionPolicy = new ExponentialReconnectionPolicy(500,
maxDelayMs);
}
@@ -208,9 +213,14 @@ public class CQLSessionProviderImpl implements
CQLSessionProvider
builder.withSSL(sslOptions);
}
- if (username != null && password != null)
+ if (authProvider != null)
{
- builder.withCredentials(username, password);
+ String username = authProvider.username();
+ String password = authProvider.password();
+ if (username != null && password != null)
+ {
+ builder.withCredentials(username, password);
+ }
}
// During mTLS connections, when client sends in keystore, we
should have an AuthProvider passed along.
// hence we pass empty username and password in
PlainTextAuthProvider here, in case user hasn't already
diff --git
a/server/src/main/java/org/apache/cassandra/sidecar/cluster/auth/ConfigProvider.java
b/server/src/main/java/org/apache/cassandra/sidecar/cluster/auth/ConfigProvider.java
new file mode 100644
index 00000000..10419f8d
--- /dev/null
+++
b/server/src/main/java/org/apache/cassandra/sidecar/cluster/auth/ConfigProvider.java
@@ -0,0 +1,64 @@
+/*
+ * 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 org.apache.cassandra.sidecar.cluster.auth;
+
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.cassandra.sidecar.exceptions.ConfigurationException;
+
+/**
+ * Loads CQL username/password directly from auth_provider parameters.
+ */
+public class ConfigProvider implements CqlAuthProvider
+{
+ public static final String USERNAME_PARAM = "username";
+ public static final String PASSWORD_PARAM = "password";
+
+ private final String username;
+ private final String password;
+
+ public ConfigProvider(Map<String, String> parameters)
+ {
+ Objects.requireNonNull(parameters, "parameters must not be null");
+ this.username = requiredParameter(parameters, USERNAME_PARAM);
+ this.password = requiredParameter(parameters, PASSWORD_PARAM);
+ }
+
+ private static String requiredParameter(Map<String, String> parameters,
String key)
+ {
+ if (!parameters.containsKey(key))
+ {
+ throw new ConfigurationException("Missing required auth_provider
parameter \"" + key + "\"");
+ }
+ return parameters.get(key);
+ }
+
+ @Override
+ public String username()
+ {
+ return username;
+ }
+
+ @Override
+ public String password()
+ {
+ return password;
+ }
+}
diff --git
a/server/src/main/java/org/apache/cassandra/sidecar/cluster/auth/CqlAuthProvider.java
b/server/src/main/java/org/apache/cassandra/sidecar/cluster/auth/CqlAuthProvider.java
new file mode 100644
index 00000000..941f331c
--- /dev/null
+++
b/server/src/main/java/org/apache/cassandra/sidecar/cluster/auth/CqlAuthProvider.java
@@ -0,0 +1,35 @@
+/*
+ * 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 org.apache.cassandra.sidecar.cluster.auth;
+
+/**
+ * Provides authentication details used by Sidecar's internal Cassandra CQL
client.
+ */
+public interface CqlAuthProvider
+{
+ /**
+ * @return username for the CQL connection
+ */
+ String username();
+
+ /**
+ * @return password for the CQL connection
+ */
+ String password();
+}
diff --git
a/server/src/main/java/org/apache/cassandra/sidecar/cluster/auth/FileProvider.java
b/server/src/main/java/org/apache/cassandra/sidecar/cluster/auth/FileProvider.java
new file mode 100644
index 00000000..b7b03145
--- /dev/null
+++
b/server/src/main/java/org/apache/cassandra/sidecar/cluster/auth/FileProvider.java
@@ -0,0 +1,94 @@
+/*
+ * 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 org.apache.cassandra.sidecar.cluster.auth;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.InvalidPathException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.cassandra.sidecar.exceptions.ConfigurationException;
+
+/**
+ * Loads CQL username/password from files.
+ */
+public class FileProvider implements CqlAuthProvider
+{
+ static final String USERNAME_PATH_PARAM = "username_path";
+ static final String PASSWORD_PATH_PARAM = "password_path";
+
+ private final Path usernamePath;
+ private final Path passwordPath;
+
+ public FileProvider(Map<String, String> parameters)
+ {
+ Objects.requireNonNull(parameters, "parameters must not be null");
+ this.usernamePath = resolvePath(parameters, USERNAME_PATH_PARAM);
+ this.passwordPath = resolvePath(parameters, PASSWORD_PATH_PARAM);
+ }
+
+ private static Path resolvePath(Map<String, String> parameters, String key)
+ {
+ if (!parameters.containsKey(key))
+ {
+ throw new ConfigurationException("Missing required auth_provider
parameter \"" + key + "\"");
+ }
+
+ try
+ {
+ return Paths.get(parameters.get(key));
+ }
+ catch (InvalidPathException e)
+ {
+ throw new ConfigurationException("Invalid path in auth_provider
parameter \"" + key + "\"", e);
+ }
+ }
+
+ private static String readSecret(Path path, String key)
+ {
+ try
+ {
+ String secret = Files.readString(path).trim();
+ if (secret.isEmpty())
+ {
+ throw new ConfigurationException("Empty content in
auth_provider file for parameter \"" + key + "\"");
+ }
+ return secret;
+ }
+ catch (IOException e)
+ {
+ throw new ConfigurationException("Unable to read auth_provider
file for parameter \"" + key + "\"", e);
+ }
+ }
+
+ @Override
+ public String username()
+ {
+ return readSecret(usernamePath, USERNAME_PATH_PARAM);
+ }
+
+ @Override
+ public String password()
+ {
+ return readSecret(passwordPath, PASSWORD_PATH_PARAM);
+ }
+}
diff --git
a/server/src/main/java/org/apache/cassandra/sidecar/config/DriverConfiguration.java
b/server/src/main/java/org/apache/cassandra/sidecar/config/DriverConfiguration.java
index 412a82c1..0ffea4cd 100644
---
a/server/src/main/java/org/apache/cassandra/sidecar/config/DriverConfiguration.java
+++
b/server/src/main/java/org/apache/cassandra/sidecar/config/DriverConfiguration.java
@@ -49,15 +49,26 @@ public interface DriverConfiguration
String localDc();
/**
+ * @deprecated use {@link #authProvider()} with
+ * {@code org.apache.cassandra.sidecar.cluster.auth.ConfigProvider}
instead.
* @return the username used for connecting to the Cassandra instance
*/
+ @Deprecated
String username();
/**
+ * @deprecated use {@link #authProvider()} with
+ * {@code org.apache.cassandra.sidecar.cluster.auth.ConfigProvider}
instead.
* @return the password used for connecting to the Cassandra instance
*/
+ @Deprecated
String password();
+ /**
+ * @return Configured authentication provider for CQL connections.
+ */
+ ParameterizedClassConfiguration authProvider();
+
/**
* @return Configuration such as keystore, truststore needed for
establishing SSL/mTLS connection with
* Cassandra instance.
diff --git
a/server/src/main/java/org/apache/cassandra/sidecar/config/yaml/DriverConfigurationImpl.java
b/server/src/main/java/org/apache/cassandra/sidecar/config/yaml/DriverConfigurationImpl.java
index b4e1aafc..fdd1f0dd 100644
---
a/server/src/main/java/org/apache/cassandra/sidecar/config/yaml/DriverConfigurationImpl.java
+++
b/server/src/main/java/org/apache/cassandra/sidecar/config/yaml/DriverConfigurationImpl.java
@@ -19,13 +19,14 @@
package org.apache.cassandra.sidecar.config.yaml;
import java.net.InetSocketAddress;
-import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import com.fasterxml.jackson.annotation.JsonProperty;
+import org.apache.cassandra.sidecar.common.DataObjectBuilder;
import
org.apache.cassandra.sidecar.common.server.utils.SecondBoundConfiguration;
import org.apache.cassandra.sidecar.config.DriverConfiguration;
+import org.apache.cassandra.sidecar.config.ParameterizedClassConfiguration;
import org.apache.cassandra.sidecar.config.SslConfiguration;
/**
@@ -51,6 +52,9 @@ public class DriverConfigurationImpl implements
DriverConfiguration
@JsonProperty("password")
private final String password;
+ @JsonProperty("auth_provider")
+ private final ParameterizedClassConfiguration authProvider;
+
@JsonProperty("ssl")
private final SslConfiguration sslConfiguration;
@@ -59,24 +63,19 @@ public class DriverConfigurationImpl implements
DriverConfiguration
public DriverConfigurationImpl()
{
- this(Collections.emptyList(), null, DEFAULT_NUM_CONNECTIONS, null,
null, null, DEFAULT_UNSUPPORTED_TABLE_SCHEMA_REFRESH_TIME);
+ this(builder());
}
- public DriverConfigurationImpl(List<InetSocketAddress> contactPoints,
- String localDc,
- int numConnections,
- String username,
- String password,
- SslConfiguration sslConfiguration,
- SecondBoundConfiguration
unsupportedTableSchemaRefreshTime)
+ private DriverConfigurationImpl(Builder builder)
{
- this.contactPoints = contactPoints;
- this.localDc = localDc;
- this.numConnections = numConnections;
- this.username = username;
- this.password = password;
- this.sslConfiguration = sslConfiguration;
- this.unsupportedTableSchemaRefreshTime =
unsupportedTableSchemaRefreshTime;
+ contactPoints = builder.contactPoints;
+ localDc = builder.localDc;
+ numConnections = builder.numConnections;
+ username = builder.username;
+ password = builder.password;
+ authProvider = builder.authProvider;
+ sslConfiguration = builder.sslConfiguration;
+ unsupportedTableSchemaRefreshTime =
builder.unsupportedTableSchemaRefreshTime;
}
/**
@@ -112,6 +111,7 @@ public class DriverConfigurationImpl implements
DriverConfiguration
/**
* {@inheritDoc}
*/
+ @Deprecated
@Override
@JsonProperty("username")
public String username()
@@ -122,6 +122,7 @@ public class DriverConfigurationImpl implements
DriverConfiguration
/**
* {@inheritDoc}
*/
+ @Deprecated
@Override
@JsonProperty("password")
public String password()
@@ -129,6 +130,16 @@ public class DriverConfigurationImpl implements
DriverConfiguration
return password;
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ @JsonProperty("auth_provider")
+ public ParameterizedClassConfiguration authProvider()
+ {
+ return authProvider;
+ }
+
/**
* {@inheritDoc}
*/
@@ -148,4 +159,153 @@ public class DriverConfigurationImpl implements
DriverConfiguration
{
return unsupportedTableSchemaRefreshTime;
}
+
+ public static Builder builder()
+ {
+ return new Builder();
+ }
+
+ /**
+ * {@code DriverConfigurationImpl} builder static inner class.
+ */
+ public static final class Builder implements DataObjectBuilder<Builder,
DriverConfigurationImpl>
+ {
+ private List<InetSocketAddress> contactPoints = List.of();
+ private String localDc;
+ private int numConnections = DEFAULT_NUM_CONNECTIONS;
+ private String username;
+ private String password;
+ private ParameterizedClassConfiguration authProvider;
+ private SslConfiguration sslConfiguration;
+ private SecondBoundConfiguration unsupportedTableSchemaRefreshTime =
DEFAULT_UNSUPPORTED_TABLE_SCHEMA_REFRESH_TIME;
+
+ private Builder()
+ {
+ }
+
+ @Override
+ public Builder self()
+ {
+ return this;
+ }
+
+ /**
+ * Sets the {@code contactPoints} and returns a reference to this
Builder enabling method chaining.
+ *
+ * @param contactPoints the {@code contactPoints} to set
+ * @return a reference to this Builder
+ */
+ public Builder contactPoints(List<InetSocketAddress> contactPoints)
+ {
+ return update(b -> b.contactPoints = contactPoints);
+ }
+
+ /**
+ * Sets the {@code localDc} and returns a reference to this Builder
enabling method chaining.
+ *
+ * @param localDc the {@code localDc} to set
+ * @return a reference to this Builder
+ */
+ public Builder localDc(String localDc)
+ {
+ return update(b -> b.localDc = localDc);
+ }
+
+ /**
+ * Sets the {@code numConnections} and returns a reference to this
Builder enabling method chaining.
+ *
+ * @param numConnections the {@code numConnections} to set
+ * @return a reference to this Builder
+ */
+ public Builder numConnections(int numConnections)
+ {
+ return update(b -> b.numConnections = numConnections);
+ }
+
+ /**
+ * Sets the {@code username} and returns a reference to this Builder
enabling method chaining.
+ *
+ * @param username the {@code username} to set
+ * @return a reference to this Builder
+ * @deprecated use {@link
#authProvider(ParameterizedClassConfiguration)} to supply credentials instead
+ */
+ @Deprecated
+ public Builder username(String username)
+ {
+ return update(b -> b.username = username);
+ }
+
+ /**
+ * Sets the {@code password} and returns a reference to this Builder
enabling method chaining.
+ *
+ * @param password the {@code password} to set
+ * @return a reference to this Builder
+ * @deprecated use {@link
#authProvider(ParameterizedClassConfiguration)} to supply credentials instead
+ */
+ @Deprecated
+ public Builder password(String password)
+ {
+ return update(b -> b.password = password);
+ }
+
+ /**
+ * Sets the {@code authProvider} and returns a reference to this
Builder enabling method chaining.
+ *
+ * <p>The auth provider is the preferred way to configure credentials,
replacing the deprecated
+ * {@link #username(String)} and {@link #password(String)} setters.
For example:
+ *
+ * <pre>{@code
+ * ParameterizedClassConfiguration authProvider =
+ * new
ParameterizedClassConfigurationImpl("org.apache.cassandra.sidecar.cluster.auth.ConfigProvider",
+ *
Map.of(org.apache.cassandra.sidecar.cluster.auth.ConfigProvider.USERNAME_PARAM,
"cassandra",
+ *
org.apache.cassandra.sidecar.cluster.auth.ConfigProvider.PASSWORD_PARAM,
"cassandra"));
+ *
+ * DriverConfiguration driverConfiguration =
DriverConfigurationImpl.builder()
+ *
.authProvider(authProvider)
+ * ...
+ *
.build();
+ * }</pre>
+ *
+ * @param authProvider the {@code authProvider} to set
+ * @return a reference to this Builder
+ */
+ public Builder authProvider(ParameterizedClassConfiguration
authProvider)
+ {
+ return update(b -> b.authProvider = authProvider);
+ }
+
+ /**
+ * Sets the {@code sslConfiguration} and returns a reference to this
Builder enabling method chaining.
+ *
+ * @param sslConfiguration the {@code sslConfiguration} to set
+ * @return a reference to this Builder
+ */
+ public Builder sslConfiguration(SslConfiguration sslConfiguration)
+ {
+ return update(b -> b.sslConfiguration = sslConfiguration);
+ }
+
+ /**
+ * Sets the {@code unsupportedTableSchemaRefreshTime} and returns a
reference to this Builder enabling
+ * method chaining.
+ *
+ * @param unsupportedTableSchemaRefreshTime the {@code
unsupportedTableSchemaRefreshTime} to set
+ * @return a reference to this Builder
+ */
+ public Builder
unsupportedTableSchemaRefreshTime(SecondBoundConfiguration
unsupportedTableSchemaRefreshTime)
+ {
+ return update(b -> b.unsupportedTableSchemaRefreshTime =
unsupportedTableSchemaRefreshTime);
+ }
+
+ /**
+ * Returns a {@code DriverConfigurationImpl} built from the parameters
previously set.
+ *
+ * @return a {@code DriverConfigurationImpl} built with parameters of
this {@code DriverConfigurationImpl.Builder}
+ */
+ @Override
+ public DriverConfigurationImpl build()
+ {
+ return new DriverConfigurationImpl(this);
+ }
+ }
}
diff --git
a/server/src/main/java/org/apache/cassandra/sidecar/modules/ConfigurationModule.java
b/server/src/main/java/org/apache/cassandra/sidecar/modules/ConfigurationModule.java
index f6256405..2da63b21 100644
---
a/server/src/main/java/org/apache/cassandra/sidecar/modules/ConfigurationModule.java
+++
b/server/src/main/java/org/apache/cassandra/sidecar/modules/ConfigurationModule.java
@@ -18,7 +18,9 @@
package org.apache.cassandra.sidecar.modules;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.stream.Collectors;
import com.google.common.util.concurrent.SidecarRateLimiter;
@@ -40,6 +42,9 @@ import
org.apache.cassandra.sidecar.cluster.CQLSessionProviderImpl;
import org.apache.cassandra.sidecar.cluster.CassandraAdapterDelegate;
import org.apache.cassandra.sidecar.cluster.InstancesMetadata;
import org.apache.cassandra.sidecar.cluster.InstancesMetadataImpl;
+import org.apache.cassandra.sidecar.cluster.auth.ConfigProvider;
+import org.apache.cassandra.sidecar.cluster.auth.CqlAuthProvider;
+import org.apache.cassandra.sidecar.cluster.auth.FileProvider;
import org.apache.cassandra.sidecar.cluster.instance.InstanceMetadata;
import org.apache.cassandra.sidecar.cluster.instance.InstanceMetadataImpl;
import org.apache.cassandra.sidecar.common.server.CQLSessionProvider;
@@ -48,12 +53,15 @@ import
org.apache.cassandra.sidecar.common.server.dns.DnsResolver;
import org.apache.cassandra.sidecar.common.server.utils.DriverUtils;
import org.apache.cassandra.sidecar.common.server.utils.SidecarVersionProvider;
import
org.apache.cassandra.sidecar.config.CassandraInputValidationConfiguration;
+import org.apache.cassandra.sidecar.config.DriverConfiguration;
import org.apache.cassandra.sidecar.config.InstanceConfiguration;
import org.apache.cassandra.sidecar.config.JmxConfiguration;
+import org.apache.cassandra.sidecar.config.ParameterizedClassConfiguration;
import org.apache.cassandra.sidecar.config.ServiceConfiguration;
import org.apache.cassandra.sidecar.config.SidecarConfiguration;
import org.apache.cassandra.sidecar.db.DriverUnsupportedSchemaCache;
import org.apache.cassandra.sidecar.db.schema.TableSchemaFetcher;
+import org.apache.cassandra.sidecar.exceptions.ConfigurationException;
import org.apache.cassandra.sidecar.metrics.MetricRegistryFactory;
import org.apache.cassandra.sidecar.metrics.instance.InstanceHealthMetrics;
import org.apache.cassandra.sidecar.modules.multibindings.KeyClassMapKey;
@@ -117,15 +125,51 @@ public class ConfigurationModule extends AbstractModule
@Singleton
CQLSessionProvider cqlSessionProvider(Vertx vertx,
SidecarConfiguration
sidecarConfiguration,
+ CqlAuthProvider cqlAuthProvider,
DriverUtils driverUtils)
{
CQLSessionProviderImpl cqlSessionProvider = new
CQLSessionProviderImpl(sidecarConfiguration,
NettyOptions.DEFAULT_INSTANCE,
+
cqlAuthProvider,
driverUtils);
vertx.eventBus().localConsumer(ON_SERVER_STOP.address(), message ->
cqlSessionProvider.close());
return cqlSessionProvider;
}
+ @Provides
+ @Singleton
+ CqlAuthProvider cqlAuthProvider(SidecarConfiguration sidecarConfiguration)
+ {
+ DriverConfiguration driverConfiguration =
sidecarConfiguration.driverConfiguration();
+
+ ParameterizedClassConfiguration config =
driverConfiguration.authProvider();
+ if (config == null)
+ {
+ // Fallback to the old one
+ Map<String, String> namedParameters = new HashMap<>();
+ namedParameters.put("username", driverConfiguration.username());
+ namedParameters.put("password", driverConfiguration.password());
+ return new ConfigProvider(namedParameters);
+ }
+
+ if (config.namedParameters() == null)
+ {
+ throw new ConfigurationException("Missing parameters for
auth_provider");
+ }
+
+ Map<String, String> namedParameters = config.namedParameters();
+
+ if
(config.className().equalsIgnoreCase(ConfigProvider.class.getName()))
+ {
+ return new ConfigProvider(namedParameters);
+ }
+ if (config.className().equalsIgnoreCase(FileProvider.class.getName()))
+ {
+ return new FileProvider(namedParameters);
+ }
+ throw new ConfigurationException("Unrecognized cql auth_provider " +
config.className() + " set");
+ }
+
@Provides
@Singleton
DriverUnsupportedSchemaCache driverUnsupportedSchemaCache(Vertx vertx,
diff --git
a/server/src/test/integration/org/apache/cassandra/sidecar/testing/IntegrationTestModule.java
b/server/src/test/integration/org/apache/cassandra/sidecar/testing/IntegrationTestModule.java
index 918fdcdd..ce6c4590 100644
---
a/server/src/test/integration/org/apache/cassandra/sidecar/testing/IntegrationTestModule.java
+++
b/server/src/test/integration/org/apache/cassandra/sidecar/testing/IntegrationTestModule.java
@@ -74,6 +74,8 @@ import org.apache.cassandra.sidecar.tasks.PeriodicTask;
import org.apache.cassandra.sidecar.tasks.ScheduleDecision;
import org.jetbrains.annotations.NotNull;
+import static
org.apache.cassandra.sidecar.cluster.auth.ConfigProvider.PASSWORD_PARAM;
+import static
org.apache.cassandra.sidecar.cluster.auth.ConfigProvider.USERNAME_PARAM;
import static
org.apache.cassandra.sidecar.exceptions.CassandraUnavailableException.Service.CQL;
import static
org.apache.cassandra.sidecar.server.SidecarServerEvents.ON_SERVER_STOP;
@@ -126,9 +128,16 @@ public class IntegrationTestModule extends AbstractModule
MillisecondBoundConfiguration.parse("50ms"),
MillisecondBoundConfiguration.parse("5000ms"));
- DriverConfiguration driverConfiguration = new
DriverConfigurationImpl(Collections.emptyList(), "dc1",
-
1, "cassandra", "cassandra",
-
null, new SecondBoundConfiguration(3, TimeUnit.SECONDS));
+ ParameterizedClassConfiguration authProvider = new
ParameterizedClassConfigurationImpl("org.apache.cassandra.sidecar.cluster.auth.ConfigProvider",
+
Map.of(USERNAME_PARAM, "cassandra",
+
PASSWORD_PARAM, "cassandra"));
+
+ DriverConfiguration driverConfiguration =
DriverConfigurationImpl.builder()
+
.localDc("dc1")
+
.numConnections(1)
+
.unsupportedTableSchemaRefreshTime(new SecondBoundConfiguration(3,
TimeUnit.SECONDS))
+
.authProvider(authProvider)
+
.build();
SslConfiguration sslConfiguration =
SslConfigurationImpl.builder()
diff --git
a/server/src/test/java/org/apache/cassandra/sidecar/cluster/auth/ConfigProviderTest.java
b/server/src/test/java/org/apache/cassandra/sidecar/cluster/auth/ConfigProviderTest.java
new file mode 100644
index 00000000..9d023b53
--- /dev/null
+++
b/server/src/test/java/org/apache/cassandra/sidecar/cluster/auth/ConfigProviderTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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 org.apache.cassandra.sidecar.cluster.auth;
+
+import java.util.Map;
+
+import org.junit.jupiter.api.Test;
+
+import org.apache.cassandra.sidecar.exceptions.ConfigurationException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+
+class ConfigProviderTest
+{
+ @Test
+ void testInterfaceGetters()
+ {
+ CqlAuthProvider provider = new
ConfigProvider(Map.of(ConfigProvider.USERNAME_PARAM, "cassandra",
+ ConfigProvider.PASSWORD_PARAM, "cassandra"));
+ assertThat(provider.username()).isEqualTo("cassandra");
+ assertThat(provider.password()).isEqualTo("cassandra");
+ }
+
+ @Test
+ void testMissingParameterThrows()
+ {
+ assertThatExceptionOfType(ConfigurationException.class)
+ .isThrownBy(() -> new
ConfigProvider(Map.of(ConfigProvider.USERNAME_PARAM, "cassandra")))
+ .withMessageContaining("Missing required auth_provider parameter");
+ }
+}
diff --git
a/server/src/test/java/org/apache/cassandra/sidecar/cluster/auth/FileProviderTest.java
b/server/src/test/java/org/apache/cassandra/sidecar/cluster/auth/FileProviderTest.java
new file mode 100644
index 00000000..a1d0b673
--- /dev/null
+++
b/server/src/test/java/org/apache/cassandra/sidecar/cluster/auth/FileProviderTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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 org.apache.cassandra.sidecar.cluster.auth;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Map;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+import org.apache.cassandra.sidecar.exceptions.ConfigurationException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+
+class FileProviderTest
+{
+ @TempDir
+ Path tempDir;
+
+ @Test
+ void testInterfaceGetters() throws IOException
+ {
+ Path usernamePath = tempDir.resolve("username");
+ Path passwordPath = tempDir.resolve("password");
+ Files.writeString(usernamePath, "cassandra-user\n");
+ Files.writeString(passwordPath, "cassandra-pass\n");
+
+ CqlAuthProvider provider = new
FileProvider(Map.of(FileProvider.USERNAME_PATH_PARAM, usernamePath.toString(),
+ FileProvider.PASSWORD_PATH_PARAM, passwordPath.toString()));
+ assertThat(provider.username()).isEqualTo("cassandra-user");
+ assertThat(provider.password()).isEqualTo("cassandra-pass");
+ }
+
+ @Test
+ void testMissingParameterThrows()
+ {
+ assertThatExceptionOfType(ConfigurationException.class)
+ .isThrownBy(() -> new
FileProvider(Map.of(FileProvider.USERNAME_PATH_PARAM, "/tmp/username")))
+ .withMessageContaining("Missing required auth_provider parameter");
+ }
+
+ @Test
+ void testEmptySecretThrows() throws IOException
+ {
+ Path usernamePath = tempDir.resolve("username");
+ Path passwordPath = tempDir.resolve("password");
+ Files.writeString(usernamePath, " \n");
+ Files.writeString(passwordPath, "secret");
+
+ FileProvider provider = new
FileProvider(Map.of(FileProvider.USERNAME_PATH_PARAM, usernamePath.toString(),
+
FileProvider.PASSWORD_PATH_PARAM, passwordPath.toString()));
+ assertThatExceptionOfType(ConfigurationException.class)
+ .isThrownBy(provider::username)
+ .withMessageContaining("Empty content in auth_provider file for
parameter");
+ }
+}
diff --git
a/server/src/test/java/org/apache/cassandra/sidecar/config/SidecarConfigurationTest.java
b/server/src/test/java/org/apache/cassandra/sidecar/config/SidecarConfigurationTest.java
index e33463d9..21388278 100644
---
a/server/src/test/java/org/apache/cassandra/sidecar/config/SidecarConfigurationTest.java
+++
b/server/src/test/java/org/apache/cassandra/sidecar/config/SidecarConfigurationTest.java
@@ -224,6 +224,22 @@ class SidecarConfigurationTest
assertThat(sslConfiguration.truststore().password()).isEqualTo("password");
}
+ @Test
+ void testDriverParametersWithAuthProvider() throws IOException
+ {
+ Path yamlPath =
yaml("config/sidecar_driver_params_config_provider.yaml");
+ SidecarConfiguration config =
SidecarConfigurationImpl.readYamlConfiguration(yamlPath);
+
+ DriverConfiguration driverConfiguration = config.driverConfiguration();
+ assertThat(driverConfiguration).isNotNull();
+ assertThat(driverConfiguration.authProvider()).isNotNull();
+ ParameterizedClassConfiguration authProvider =
driverConfiguration.authProvider();
+
assertThat(authProvider.className()).isEqualTo("org.apache.cassandra.sidecar.cluster.auth.ConfigProvider");
+ assertThat(authProvider.namedParameters())
+ .containsExactlyInAnyOrderEntriesOf(Map.of("username", "cassandra",
+ "password", "cassandra"));
+ }
+
@Test
void testReadCustomSchemaKeyspaceConfiguration() throws IOException
{
diff --git
a/server/src/test/resources/config/sidecar_driver_params_config_provider.yaml
b/server/src/test/resources/config/sidecar_driver_params_config_provider.yaml
new file mode 100644
index 00000000..18f91892
--- /dev/null
+++
b/server/src/test/resources/config/sidecar_driver_params_config_provider.yaml
@@ -0,0 +1,23 @@
+driver_parameters:
+ contact_points:
+ - "127.0.0.1:9042"
+ auth_provider:
+ class_name: org.apache.cassandra.sidecar.cluster.auth.ConfigProvider
+ parameters:
+ username: cassandra
+ password: cassandra
+ ssl:
+ enabled: true
+ accepted_protocols:
+ - TLSv1.2
+ - TLSv1.3
+ keystore:
+ type: PKCS12
+ path: path/to/keystore.p12
+ password: password
+ truststore:
+ type: PKCS12
+ path: path/to/keystore.p12
+ password: password
+ num_connections: 6
+ local_dc: dc1
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]