|
1 | 1 | package com.clickhouse.jdbc;
|
2 | 2 |
|
| 3 | +import java.nio.ByteBuffer; |
| 4 | +import java.nio.charset.StandardCharsets; |
3 | 5 | import java.sql.*;
|
| 6 | +import java.util.Arrays; |
| 7 | +import java.util.Base64; |
| 8 | +import java.util.List; |
4 | 9 | import java.util.Properties;
|
5 | 10 |
|
6 | 11 | import java.util.Properties;
|
| 12 | +import java.util.concurrent.TimeUnit; |
7 | 13 | import java.util.UUID;
|
8 | 14 |
|
9 | 15 | import com.clickhouse.client.ClickHouseNode;
|
10 | 16 | import com.clickhouse.client.ClickHouseProtocol;
|
11 | 17 | import com.clickhouse.client.api.Client;
|
12 | 18 | import com.clickhouse.client.api.ClientConfigProperties;
|
13 | 19 | import com.clickhouse.client.api.ServerException;
|
| 20 | +import com.clickhouse.client.api.enums.Protocol; |
14 | 21 | import com.clickhouse.client.api.internal.ServerSettings;
|
| 22 | +import com.clickhouse.client.api.query.GenericRecord; |
| 23 | +import com.clickhouse.client.api.query.QueryResponse; |
15 | 24 | import com.clickhouse.jdbc.internal.ClientInfoProperties;
|
16 | 25 | 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; |
17 | 31 | import com.clickhouse.jdbc.internal.JdbcUtils;
|
18 | 32 | import org.testng.Assert;
|
19 | 33 | import org.testng.annotations.DataProvider;
|
@@ -440,4 +454,83 @@ public void testUnwrapping() throws Exception {
|
440 | 454 | Assert.assertEquals(conn.unwrap(JdbcV2Wrapper.class), conn);
|
441 | 455 | assertThrows(SQLException.class, () -> conn.unwrap(ResultSet.class));
|
442 | 456 | }
|
| 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 | + |
443 | 536 | }
|
0 commit comments