Skip to content

Commit 571fcfc

Browse files
authored
JAVA-2850: Ignore credentials in secure connect bundle [DataStax Astra] (#1481)
1 parent 83eec17 commit 571fcfc

File tree

5 files changed

+95
-39
lines changed

5 files changed

+95
-39
lines changed

changelog/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### 4.8.0 (in progress)
66

7+
- [improvement] JAVA-2850: Ignore credentials in secure connect bundle [DataStax Astra]
78
- [improvement] JAVA-2813: Don't fail when secure bundle is specified together with other options
89
- [bug] JAVA-2800: Exclude SLF4J from mapper-processor dependencies
910
- [new feature] JAVA-2819: Add DriverConfigLoader.fromString

core/src/main/java/com/datastax/oss/driver/api/core/session/SessionBuilder.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -704,9 +704,6 @@ protected final CompletionStage<CqlSession> buildDefaultSessionAsync() {
704704
withLocalDatacenter(cloudConfig.getLocalDatacenter());
705705
withSslEngineFactory(cloudConfig.getSslEngineFactory());
706706
withCloudProxyAddress(cloudConfig.getProxyAddress());
707-
if (cloudConfig.getAuthProvider().isPresent()) {
708-
withAuthProvider(cloudConfig.getAuthProvider().get());
709-
}
710707
programmaticArguments = programmaticArgumentsBuilder.build();
711708
}
712709

core/src/main/java/com/datastax/oss/driver/internal/core/config/cloud/CloudConfig.java

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,12 @@
1515
*/
1616
package com.datastax.oss.driver.internal.core.config.cloud;
1717

18-
import com.datastax.oss.driver.api.core.auth.AuthProvider;
1918
import com.datastax.oss.driver.api.core.metadata.EndPoint;
2019
import com.datastax.oss.driver.api.core.ssl.SslEngineFactory;
2120
import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList;
2221
import edu.umd.cs.findbugs.annotations.NonNull;
23-
import edu.umd.cs.findbugs.annotations.Nullable;
2422
import java.net.InetSocketAddress;
2523
import java.util.List;
26-
import java.util.Optional;
2724
import net.jcip.annotations.ThreadSafe;
2825

2926
@ThreadSafe
@@ -33,19 +30,16 @@ public class CloudConfig {
3330
private final List<EndPoint> endPoints;
3431
private final String localDatacenter;
3532
private final SslEngineFactory sslEngineFactory;
36-
@Nullable private final AuthProvider authProvider;
3733

3834
CloudConfig(
3935
@NonNull InetSocketAddress proxyAddress,
4036
@NonNull List<EndPoint> endPoints,
4137
@NonNull String localDatacenter,
42-
@NonNull SslEngineFactory sslEngineFactory,
43-
@Nullable AuthProvider authProvider) {
38+
@NonNull SslEngineFactory sslEngineFactory) {
4439
this.proxyAddress = proxyAddress;
4540
this.endPoints = ImmutableList.copyOf(endPoints);
4641
this.localDatacenter = localDatacenter;
4742
this.sslEngineFactory = sslEngineFactory;
48-
this.authProvider = authProvider;
4943
}
5044

5145
@NonNull
@@ -67,9 +61,4 @@ public String getLocalDatacenter() {
6761
public SslEngineFactory getSslEngineFactory() {
6862
return sslEngineFactory;
6963
}
70-
71-
@NonNull
72-
public Optional<AuthProvider> getAuthProvider() {
73-
return Optional.ofNullable(authProvider);
74-
}
7564
}

core/src/main/java/com/datastax/oss/driver/internal/core/config/cloud/CloudConfigFactory.java

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@
1515
*/
1616
package com.datastax.oss.driver.internal.core.config.cloud;
1717

18-
import com.datastax.oss.driver.api.core.auth.AuthProvider;
1918
import com.datastax.oss.driver.api.core.metadata.EndPoint;
20-
import com.datastax.oss.driver.internal.core.auth.ProgrammaticPlainTextAuthProvider;
2119
import com.datastax.oss.driver.internal.core.metadata.SniEndPoint;
2220
import com.datastax.oss.driver.internal.core.ssl.SniSslEngineFactory;
2321
import com.datastax.oss.driver.shaded.guava.common.io.ByteStreams;
@@ -26,7 +24,6 @@
2624
import com.fasterxml.jackson.databind.JsonNode;
2725
import com.fasterxml.jackson.databind.ObjectMapper;
2826
import edu.umd.cs.findbugs.annotations.NonNull;
29-
import edu.umd.cs.findbugs.annotations.Nullable;
3027
import java.io.BufferedReader;
3128
import java.io.ByteArrayInputStream;
3229
import java.io.ByteArrayOutputStream;
@@ -53,10 +50,12 @@
5350
import javax.net.ssl.SSLContext;
5451
import javax.net.ssl.TrustManagerFactory;
5552
import net.jcip.annotations.ThreadSafe;
53+
import org.slf4j.Logger;
54+
import org.slf4j.LoggerFactory;
5655

5756
@ThreadSafe
5857
public class CloudConfigFactory {
59-
58+
private static final Logger LOG = LoggerFactory.getLogger(CloudConfigFactory.class);
6059
/**
6160
* Creates a {@link CloudConfig} with information fetched from the specified Cloud configuration
6261
* URL.
@@ -138,9 +137,8 @@ public CloudConfig createCloudConfig(@NonNull InputStream cloudConfig)
138137
List<EndPoint> endPoints = getEndPoints(proxyMetadataJson, sniProxyAddress);
139138
String localDatacenter = getLocalDatacenter(proxyMetadataJson);
140139
SniSslEngineFactory sslEngineFactory = new SniSslEngineFactory(sslContext);
141-
AuthProvider authProvider = getAuthProvider(configJson);
142-
return new CloudConfig(
143-
sniProxyAddress, endPoints, localDatacenter, sslEngineFactory, authProvider);
140+
validateIfBundleContainsUsernamePassword(configJson);
141+
return new CloudConfig(sniProxyAddress, endPoints, localDatacenter, sslEngineFactory);
144142
}
145143

146144
@NonNull
@@ -176,16 +174,11 @@ protected URL getMetadataServiceUrl(JsonNode configFile) throws MalformedURLExce
176174
}
177175
}
178176

179-
@Nullable
180-
protected AuthProvider getAuthProvider(JsonNode configFile) {
181-
if (configFile.has("username")) {
182-
String username = configFile.get("username").asText();
183-
if (configFile.has("password")) {
184-
String password = configFile.get("password").asText();
185-
return new ProgrammaticPlainTextAuthProvider(username, password);
186-
}
177+
protected void validateIfBundleContainsUsernamePassword(JsonNode configFile) {
178+
if (configFile.has("username") || configFile.has("password")) {
179+
LOG.info(
180+
"The bundle contains config.json with username and/or password. Providing it in the bundle is deprecated and ignored.");
187181
}
188-
return null;
189182
}
190183

191184
@NonNull

integration-tests/src/test/java/com/datastax/oss/driver/api/core/cloud/CloudIT.java

Lines changed: 84 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,16 @@
2727

2828
import ch.qos.logback.classic.Level;
2929
import ch.qos.logback.classic.spi.ILoggingEvent;
30+
import com.datastax.oss.driver.api.core.AllNodesFailedException;
3031
import com.datastax.oss.driver.api.core.CqlSession;
32+
import com.datastax.oss.driver.api.core.auth.AuthenticationException;
3133
import com.datastax.oss.driver.api.core.config.DefaultDriverOption;
3234
import com.datastax.oss.driver.api.core.config.DriverConfigLoader;
3335
import com.datastax.oss.driver.api.core.cql.ResultSet;
3436
import com.datastax.oss.driver.api.core.session.SessionBuilder;
3537
import com.datastax.oss.driver.api.testinfra.session.SessionUtils;
3638
import com.datastax.oss.driver.categories.IsolatedTests;
39+
import com.datastax.oss.driver.internal.core.config.cloud.CloudConfigFactory;
3740
import com.datastax.oss.driver.internal.core.ssl.DefaultSslEngineFactory;
3841
import com.datastax.oss.driver.internal.core.util.LoggerTest;
3942
import com.github.tomakehurst.wiremock.junit.WireMockRule;
@@ -45,6 +48,7 @@
4548
import java.nio.file.Path;
4649
import java.security.NoSuchAlgorithmException;
4750
import java.util.Collections;
51+
import java.util.List;
4852
import javax.net.ssl.SSLContext;
4953
import org.junit.ClassRule;
5054
import org.junit.Rule;
@@ -67,12 +71,57 @@ public class CloudIT {
6771
public void should_connect_to_proxy_using_path() {
6872
ResultSet set;
6973
Path bundle = proxyRule.getProxy().getDefaultBundlePath();
70-
try (CqlSession session = CqlSession.builder().withCloudSecureConnectBundle(bundle).build()) {
74+
try (CqlSession session =
75+
CqlSession.builder()
76+
.withAuthCredentials("cassandra", "cassandra")
77+
.withCloudSecureConnectBundle(bundle)
78+
.build()) {
7179
set = session.execute("select * from system.local");
7280
}
7381
assertThat(set).isNotNull();
7482
}
7583

84+
@Test
85+
public void should_connect_and_log_info_that_config_json_with_username_password_was_provided() {
86+
ResultSet set;
87+
Path bundle = proxyRule.getProxy().getDefaultBundlePath();
88+
LoggerTest.LoggerSetup logger = setupTestLogger(CloudConfigFactory.class, Level.INFO);
89+
90+
try (CqlSession session =
91+
CqlSession.builder()
92+
.withAuthCredentials("cassandra", "cassandra")
93+
.withCloudSecureConnectBundle(bundle)
94+
.build()) {
95+
set = session.execute("select * from system.local");
96+
verify(logger.appender, timeout(500).atLeast(1))
97+
.doAppend(logger.loggingEventCaptor.capture());
98+
assertThat(
99+
logger.loggingEventCaptor.getAllValues().stream()
100+
.map(ILoggingEvent::getFormattedMessage))
101+
.contains(
102+
"The bundle contains config.json with username and/or password. Providing it in the bundle is deprecated and ignored.");
103+
}
104+
assertThat(set).isNotNull();
105+
}
106+
107+
@Test
108+
public void
109+
should_fail_with_auth_error_when_connecting_using_bundle_with_username_password_in_config_json() {
110+
Path bundle = proxyRule.getProxy().getDefaultBundlePath();
111+
112+
// fails with auth error because username/password from config.json is ignored
113+
AllNodesFailedException exception = null;
114+
try {
115+
CqlSession.builder().withCloudSecureConnectBundle(bundle).build();
116+
} catch (AllNodesFailedException ex) {
117+
exception = ex;
118+
}
119+
assertThat(exception).isNotNull();
120+
List<Throwable> errors = exception.getAllErrors().values().iterator().next();
121+
Throwable firstError = errors.get(0);
122+
assertThat(firstError).isInstanceOf(AuthenticationException.class);
123+
}
124+
76125
@Test
77126
public void should_connect_to_proxy_without_credentials() {
78127
ResultSet set;
@@ -91,7 +140,11 @@ public void should_connect_to_proxy_without_credentials() {
91140
public void should_connect_to_proxy_using_non_normalized_path() {
92141
Path bundle = proxyRule.getProxy().getBundlesRootPath().resolve("../bundles/creds-v1.zip");
93142
ResultSet set;
94-
try (CqlSession session = CqlSession.builder().withCloudSecureConnectBundle(bundle).build()) {
143+
try (CqlSession session =
144+
CqlSession.builder()
145+
.withAuthCredentials("cassandra", "cassandra")
146+
.withCloudSecureConnectBundle(bundle)
147+
.build()) {
95148
set = session.execute("select * from system.local");
96149
}
97150
assertThat(set).isNotNull();
@@ -101,7 +154,11 @@ public void should_connect_to_proxy_using_non_normalized_path() {
101154
public void should_connect_to_proxy_using_input_stream() throws IOException {
102155
InputStream bundle = Files.newInputStream(proxyRule.getProxy().getDefaultBundlePath());
103156
ResultSet set;
104-
try (CqlSession session = CqlSession.builder().withCloudSecureConnectBundle(bundle).build()) {
157+
try (CqlSession session =
158+
CqlSession.builder()
159+
.withAuthCredentials("cassandra", "cassandra")
160+
.withCloudSecureConnectBundle(bundle)
161+
.build()) {
105162
set = session.execute("select * from system.local");
106163
}
107164
assertThat(set).isNotNull();
@@ -124,7 +181,10 @@ public void should_connect_to_proxy_using_URL() throws IOException {
124181
// when
125182
ResultSet set;
126183
try (CqlSession session =
127-
CqlSession.builder().withCloudSecureConnectBundle(bundleUrl).build()) {
184+
CqlSession.builder()
185+
.withAuthCredentials("cassandra", "cassandra")
186+
.withCloudSecureConnectBundle(bundleUrl)
187+
.build()) {
128188

129189
// then
130190
set = session.execute("select * from system.local");
@@ -142,7 +202,11 @@ public void should_connect_to_proxy_using_absolute_path_provided_in_the_session_
142202
.build();
143203
// when
144204
ResultSet set;
145-
try (CqlSession session = CqlSession.builder().withConfigLoader(loader).build()) {
205+
try (CqlSession session =
206+
CqlSession.builder()
207+
.withAuthCredentials("cassandra", "cassandra")
208+
.withConfigLoader(loader)
209+
.build()) {
146210

147211
// then
148212
set = session.execute("select * from system.local");
@@ -161,7 +225,11 @@ public void should_connect_to_proxy_using_non_normalized_path_provided_in_the_se
161225
.build();
162226
// when
163227
ResultSet set;
164-
try (CqlSession session = CqlSession.builder().withConfigLoader(loader).build()) {
228+
try (CqlSession session =
229+
CqlSession.builder()
230+
.withAuthCredentials("cassandra", "cassandra")
231+
.withConfigLoader(loader)
232+
.build()) {
165233

166234
// then
167235
set = session.execute("select * from system.local");
@@ -180,7 +248,11 @@ public void should_connect_to_proxy_using_non_normalized_path_provided_in_the_se
180248
.build();
181249
// when
182250
ResultSet set;
183-
try (CqlSession session = CqlSession.builder().withConfigLoader(loader).build()) {
251+
try (CqlSession session =
252+
CqlSession.builder()
253+
.withAuthCredentials("cassandra", "cassandra")
254+
.withConfigLoader(loader)
255+
.build()) {
184256

185257
// then
186258
set = session.execute("select * from system.local");
@@ -207,7 +279,11 @@ public void should_connect_to_proxy_using_url_with_http_protocol_provided_in_the
207279
.build();
208280
// when
209281
ResultSet set;
210-
try (CqlSession session = CqlSession.builder().withConfigLoader(loader).build()) {
282+
try (CqlSession session =
283+
CqlSession.builder()
284+
.withAuthCredentials("cassandra", "cassandra")
285+
.withConfigLoader(loader)
286+
.build()) {
211287

212288
// then
213289
set = session.execute("select * from system.local");

0 commit comments

Comments
 (0)