Skip to content

Commit 593b9e3

Browse files
authored
Adding support to JWT in JDBC V2 (#2055)
* First commit for adding support to JWT in JDBC V2 * Adjust wiremock payload in testBearerTokenAuth test * Update timeout for job that run on jdvc-v2 * Small refactor for bearer token * Remove property from DriverProperties
1 parent 590d334 commit 593b9e3

File tree

7 files changed

+110
-6
lines changed

7 files changed

+110
-6
lines changed

.github/workflows/build.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ jobs:
208208
clickhouse: ["cloud"]
209209
project: ["clickhouse-http-client", "clickhouse-jdbc", "client-v2", "jdbc-v2"]
210210
fail-fast: false
211-
timeout-minutes: 15
211+
timeout-minutes: 20
212212
name: ${{ matrix.project }} + CH ${{ matrix.clickhouse }}
213213
steps:
214214
- name: Check out repository
@@ -278,7 +278,7 @@ jobs:
278278
# protocol: ["http", "http_client", "apache_http_client"]
279279
protocol: ["apache_http_client"]
280280
fail-fast: false
281-
timeout-minutes: 15
281+
timeout-minutes: 20
282282
name: JDBC driver + CH ${{ matrix.clickhouse }} (${{ matrix.protocol }})
283283
steps:
284284
- name: Check out repository

client-v2/src/main/java/com/clickhouse/client/api/Client.java

+3
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,9 @@ public Builder setOption(String key, String value) {
361361
if (key.equals(ClientConfigProperties.PRODUCT_NAME.getKey())) {
362362
setClientName(value);
363363
}
364+
if (key.equals(ClientConfigProperties.BEARERTOKEN_AUTH.getKey())) {
365+
useBearerTokenAuth(value);
366+
}
364367
return this;
365368
}
366369

client-v2/src/main/java/com/clickhouse/client/api/ClientConfigProperties.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ public enum ClientConfigProperties {
123123
@Deprecated
124124
PRODUCT_NAME("product_name"),
125125

126-
126+
BEARERTOKEN_AUTH ("bearer_token")
127127
;
128128

129129
private String key;

jdbc-v2/pom.xml

+6
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,12 @@
9393
<artifactId>testng</artifactId>
9494
<scope>test</scope>
9595
</dependency>
96+
<dependency>
97+
<groupId>com.github.tomakehurst</groupId>
98+
<artifactId>wiremock-jre8</artifactId>
99+
<version>2.35.2</version>
100+
<scope>test</scope>
101+
</dependency>
96102
</dependencies>
97103

98104
<build>

jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/DriverProperties.java

-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ public enum DriverProperties {
2323
* {@see com.clickhouse.client.api.query.QuerySettings}
2424
*/
2525
DEFAULT_QUERY_SETTINGS("default_query_settings", null);
26-
2726
private final String key;
2827

2928
private final String defaultValue;

jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/JdbcConfiguration.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,11 @@ public JdbcConfiguration(String url, Properties info) throws SQLException {
5757
initProperties(urlProperties, info);
5858

5959
// after initializing all properties - set final connection URL
60-
boolean useSSL = Boolean.parseBoolean(info.getProperty("ssl", "false"));
60+
boolean useSSL = Boolean.parseBoolean(info.getProperty(DriverProperties.SECURE_CONNECTION.getKey(), "false"));
61+
String bearerToken = info.getProperty(ClientConfigProperties.BEARERTOKEN_AUTH.getKey(), null);
62+
if (bearerToken != null) {
63+
clientProperties.put(ClientConfigProperties.BEARERTOKEN_AUTH.getKey(), bearerToken);
64+
}
6165
this.connectionUrl = createConnectionURL(tmpConnectionUrl, useSSL);
6266
this.isIgnoreUnsupportedRequests= Boolean.parseBoolean(getDriverProperty(DriverProperties.IGNORE_UNSUPPORTED_VALUES.getKey(), "false"));
6367
}
@@ -240,7 +244,6 @@ public String getDriverProperty(String key, String defaultValue) {
240244
public Client.Builder applyClientProperties(Client.Builder builder) {
241245
builder.addEndpoint(connectionUrl)
242246
.setOptions(clientProperties);
243-
244247
return builder;
245248
}
246249

jdbc-v2/src/test/java/com/clickhouse/jdbc/ConnectionTest.java

+93
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,33 @@
11
package com.clickhouse.jdbc;
22

3+
import java.nio.ByteBuffer;
4+
import java.nio.charset.StandardCharsets;
35
import java.sql.*;
6+
import java.util.Arrays;
7+
import java.util.Base64;
8+
import java.util.List;
49
import java.util.Properties;
510

611
import java.util.Properties;
12+
import java.util.concurrent.TimeUnit;
713
import java.util.UUID;
814

915
import com.clickhouse.client.ClickHouseNode;
1016
import com.clickhouse.client.ClickHouseProtocol;
1117
import com.clickhouse.client.api.Client;
1218
import com.clickhouse.client.api.ClientConfigProperties;
1319
import com.clickhouse.client.api.ServerException;
20+
import com.clickhouse.client.api.enums.Protocol;
1421
import com.clickhouse.client.api.internal.ServerSettings;
22+
import com.clickhouse.client.api.query.GenericRecord;
23+
import com.clickhouse.client.api.query.QueryResponse;
1524
import com.clickhouse.jdbc.internal.ClientInfoProperties;
1625
import com.clickhouse.jdbc.internal.DriverProperties;
26+
import com.github.tomakehurst.wiremock.WireMockServer;
27+
import com.github.tomakehurst.wiremock.client.WireMock;
28+
import com.github.tomakehurst.wiremock.common.ConsoleNotifier;
29+
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
30+
import org.apache.hc.core5.http.HttpStatus;
1731
import com.clickhouse.jdbc.internal.JdbcUtils;
1832
import org.testng.Assert;
1933
import org.testng.annotations.DataProvider;
@@ -440,4 +454,83 @@ public void testUnwrapping() throws Exception {
440454
Assert.assertEquals(conn.unwrap(JdbcV2Wrapper.class), conn);
441455
assertThrows(SQLException.class, () -> conn.unwrap(ResultSet.class));
442456
}
457+
458+
@Test(groups = { "integration" })
459+
public void testBearerTokenAuth() throws Exception {
460+
if (isCloud()) {
461+
return; // mocked server
462+
}
463+
464+
WireMockServer mockServer = new WireMockServer( WireMockConfiguration
465+
.options().port(9090).notifier(new ConsoleNotifier(false)));
466+
mockServer.start();
467+
468+
try {
469+
String jwtToken1 = Arrays.stream(
470+
new String[]{"header", "payload", "signature"})
471+
.map(s -> Base64.getEncoder().encodeToString(s.getBytes(StandardCharsets.UTF_8)))
472+
.reduce((s1, s2) -> s1 + "." + s2).get();
473+
474+
// From wireshark dump as C Array
475+
char select_server_info[] = { /* Packet 11901 */
476+
0x03, 0x04, 0x75, 0x73, 0x65, 0x72, 0x08, 0x74,
477+
0x69, 0x6d, 0x65, 0x7a, 0x6f, 0x6e, 0x65, 0x07,
478+
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x06,
479+
0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x06, 0x53,
480+
0x74, 0x72, 0x69, 0x6e, 0x67, 0x06, 0x53, 0x74,
481+
0x72, 0x69, 0x6e, 0x67, 0x07, 0x64, 0x65, 0x66,
482+
0x61, 0x75, 0x6c, 0x74, 0x03, 0x55, 0x54, 0x43,
483+
0x0b, 0x32, 0x34, 0x2e, 0x33, 0x2e, 0x31, 0x2e,
484+
0x32, 0x36, 0x37, 0x32 };
485+
486+
char select1_res[] = { /* Packet 11909 */
487+
0x01, 0x01, 0x31, 0x05, 0x55, 0x49, 0x6e, 0x74,
488+
0x38, 0x01 };
489+
490+
mockServer.addStubMapping(WireMock.post(WireMock.anyUrl())
491+
.withHeader("Authorization", WireMock.equalTo("Bearer " + jwtToken1))
492+
.withRequestBody(WireMock.matching(".*SELECT 1.*"))
493+
.willReturn(
494+
WireMock.ok(new String(select1_res))
495+
.withHeader("X-ClickHouse-Summary",
496+
"{ \"read_bytes\": \"10\", \"read_rows\": \"1\"}")).build());
497+
498+
mockServer.addStubMapping(WireMock.post(WireMock.anyUrl())
499+
.withHeader("Authorization", WireMock.equalTo("Bearer " + jwtToken1))
500+
.withRequestBody(WireMock.equalTo("SELECT currentUser() AS user, timezone() AS timezone, version() AS version LIMIT 1"))
501+
.willReturn(
502+
WireMock.ok(new String(select_server_info))
503+
.withHeader("X-ClickHouse-Summary",
504+
"{ \"read_bytes\": \"10\", \"read_rows\": \"1\"}")).build());
505+
506+
Properties properties = new Properties();
507+
properties.put(ClientConfigProperties.BEARERTOKEN_AUTH.getKey(), jwtToken1);
508+
properties.put("compress", "false");
509+
String jdbcUrl = "jdbc:clickhouse://" + "localhost" + ":" + mockServer.port();
510+
try (Connection conn = new ConnectionImpl(jdbcUrl, properties);
511+
Statement stmt = conn.createStatement();
512+
ResultSet rs = stmt.executeQuery("SELECT 1")) {
513+
Assert.assertTrue(rs.next());
514+
Assert.assertEquals(rs.getInt(1), 1);
515+
}
516+
} finally {
517+
mockServer.stop();
518+
}
519+
}
520+
@Test(groups = { "integration" })
521+
public void testJWTWithCloud() throws Exception {
522+
if (!isCloud()) {
523+
return; // only for cloud
524+
}
525+
526+
String jwt = System.getenv("CLIENT_JWT");
527+
Properties properties = new Properties();
528+
properties.put("access_token", jwt);
529+
try (Connection conn = getJdbcConnection(properties);
530+
Statement stmt = conn.createStatement();
531+
ResultSet rs = stmt.executeQuery("SELECT 1")) {
532+
Assert.assertTrue(rs.next());
533+
}
534+
}
535+
443536
}

0 commit comments

Comments
 (0)