1616import com .clickhouse .client .api .insert .InsertResponse ;
1717import com .clickhouse .client .api .insert .InsertSettings ;
1818import com .clickhouse .client .api .internal .ClientStatisticsHolder ;
19+ import com .clickhouse .client .api .internal .CredentialsManager ;
1920import com .clickhouse .client .api .internal .HttpAPIClientHelper ;
2021import com .clickhouse .client .api .internal .MapUtils ;
2122import com .clickhouse .client .api .internal .TableSchemaParser ;
@@ -141,11 +142,13 @@ public class Client implements AutoCloseable {
141142 private final int retries ;
142143 private LZ4Factory lz4Factory = null ;
143144 private final Supplier <String > queryIdGenerator ;
145+ private final CredentialsManager credentialsManager ;
144146
145147 private Client (Collection <Endpoint > endpoints , Map <String ,String > configuration ,
146148 ExecutorService sharedOperationExecutor , ColumnToMethodMatchingStrategy columnToMethodMatchingStrategy ,
147149 Object metricsRegistry , Supplier <String > queryIdGenerator ) {
148- this .configuration = ClientConfigProperties .parseConfigMap (configuration );
150+ this .configuration = new ConcurrentHashMap <>(ClientConfigProperties .parseConfigMap (configuration ));
151+ this .credentialsManager = new CredentialsManager (this .configuration );
149152 this .readOnlyConfig = Collections .unmodifiableMap (configuration );
150153 this .metricsRegistry = metricsRegistry ;
151154 this .queryIdGenerator = queryIdGenerator ;
@@ -364,8 +367,11 @@ public Builder setOption(String key, String value) {
364367 if (key .equals (ClientConfigProperties .PRODUCT_NAME .getKey ())) {
365368 setClientName (value );
366369 }
370+ if (key .equals (ClientConfigProperties .ACCESS_TOKEN .getKey ())) {
371+ setAccessToken (value );
372+ }
367373 if (key .equals (ClientConfigProperties .BEARERTOKEN_AUTH .getKey ())) {
368- useBearerTokenAuth (value );
374+ setAccessToken (value );
369375 }
370376 return this ;
371377 }
@@ -393,13 +399,17 @@ public Builder setPassword(String password) {
393399 }
394400
395401 /**
396- * Access token for authentication with server. Required for all operations .
402+ * Preferred way to configure token-based authentication .
397403 * Same access token will be used for all endpoints.
404+ * Internally it is sent as an HTTP Bearer token.
398405 *
399406 * @param accessToken - plain text access token
400407 */
408+ @ SuppressWarnings ("deprecation" )
401409 public Builder setAccessToken (String accessToken ) {
402410 this .configuration .put (ClientConfigProperties .ACCESS_TOKEN .getKey (), accessToken );
411+ this .configuration .remove (ClientConfigProperties .BEARERTOKEN_AUTH .getKey ());
412+ this .httpHeader (HttpHeaders .AUTHORIZATION , "Bearer " + accessToken );
403413 return this ;
404414 }
405415
@@ -1007,16 +1017,16 @@ public Builder setOptions(Map<String, String> options) {
10071017 }
10081018
10091019 /**
1010- * Specifies whether to use Bearer Authentication and what token to use .
1011- * The token will be sent as is, so it should be encoded before passing to this method .
1020+ * Legacy HTTP-specific alias for {@link Builder#setAccessToken(String)} .
1021+ * Prefer using {@link Builder#setAccessToken(String)} .
10121022 *
10131023 * @param bearerToken - token to use
10141024 * @return same instance of the builder
10151025 */
1026+ @ Deprecated
10161027 public Builder useBearerTokenAuth (String bearerToken ) {
10171028 // Most JWT libraries (https://jwt.io/libraries?language=Java) compact tokens in proper way
1018- this .httpHeader (HttpHeaders .AUTHORIZATION , "Bearer " + bearerToken );
1019- return this ;
1029+ return setAccessToken (bearerToken );
10201030 }
10211031
10221032 /**
@@ -1099,28 +1109,10 @@ public Client build() {
10991109 if (this .endpoints .isEmpty ()) {
11001110 throw new IllegalArgumentException ("At least one endpoint is required" );
11011111 }
1102- // check if username and password are empty. so can not initiate client?
1103- boolean useSslAuth = MapUtils .getFlag (this .configuration , ClientConfigProperties .SSL_AUTH .getKey ());
1104- boolean hasAccessToken = this .configuration .containsKey (ClientConfigProperties .ACCESS_TOKEN .getKey ());
1105- boolean hasUser = this .configuration .containsKey (ClientConfigProperties .USER .getKey ());
1106- boolean hasPassword = this .configuration .containsKey (ClientConfigProperties .PASSWORD .getKey ());
1107- boolean customHttpHeaders = this .configuration .containsKey (ClientConfigProperties .httpHeader (HttpHeaders .AUTHORIZATION ));
1108-
1109- if (!(useSslAuth || hasAccessToken || hasUser || hasPassword || customHttpHeaders )) {
1110- throw new IllegalArgumentException ("Username and password (or access token or SSL authentication or pre-define Authorization header) are required" );
1111- }
1112-
1113- if (useSslAuth && (hasAccessToken || hasPassword )) {
1114- throw new IllegalArgumentException ("Only one of password, access token or SSL authentication can be used per client." );
1115- }
11161112
1117- if (useSslAuth && !this .configuration .containsKey (ClientConfigProperties .SSL_CERTIFICATE .getKey ())) {
1118- throw new IllegalArgumentException ("SSL authentication requires a client certificate" );
1119- }
1120-
1121- if (this .configuration .containsKey (ClientConfigProperties .SSL_TRUST_STORE .getKey ()) &&
1122- this .configuration .containsKey (ClientConfigProperties .SSL_CERTIFICATE .getKey ())) {
1123- throw new IllegalArgumentException ("Trust store and certificates cannot be used together" );
1113+ ClientMisconfigurationException authConfigException = CredentialsManager .validateAuthConfig (configuration );
1114+ if (authConfigException != null ) {
1115+ throw authConfigException ;
11241116 }
11251117
11261118 // Check timezone settings
@@ -2153,8 +2145,28 @@ public Collection<String> getDBRoles() {
21532145 return unmodifiableDbRolesView ;
21542146 }
21552147
2148+ public void setCredentials (String username , String password ) {
2149+ this .credentialsManager .setCredentials (username , password );
2150+ }
2151+
2152+ /**
2153+ * Preferred runtime API to update token-based authentication.
2154+ * Internally it refreshes the HTTP Bearer token used by requests.
2155+ *
2156+ * @param accessToken - plain text access token
2157+ */
2158+ public void setAccessToken (String accessToken ) {
2159+ this .credentialsManager .setAccessToken (accessToken );
2160+ }
2161+
2162+ /**
2163+ * Legacy HTTP-specific alias for {@link #setAccessToken(String)}.
2164+ * Prefer using {@link #setAccessToken(String)}.
2165+ *
2166+ * @param bearer - token to use
2167+ */
21562168 public void updateBearerToken (String bearer ) {
2157- this . configuration . put ( ClientConfigProperties . httpHeader ( HttpHeaders . AUTHORIZATION ), "Bearer " + bearer );
2169+ setAccessToken ( bearer );
21582170 }
21592171
21602172 private Endpoint getNextAliveNode () {
@@ -2170,8 +2182,7 @@ private Endpoint getNextAliveNode() {
21702182 * @return request settings - merged client and operation settings
21712183 */
21722184 private Map <String , Object > buildRequestSettings (Map <String , Object > opSettings ) {
2173- Map <String , Object > requestSettings = new HashMap <>();
2174- requestSettings .putAll (configuration );
2185+ Map <String , Object > requestSettings = credentialsManager .snapshot ();
21752186 requestSettings .putAll (opSettings );
21762187 return requestSettings ;
21772188 }
0 commit comments