Skip to content

Commit 069d250

Browse files
Richard AntalAarchy
andcommitted
[CALCITE-6135] BEARER authentication support
Co-authored-by: Aron Meszaros <meszaros.aron.attila@gmail.com>
1 parent ac39920 commit 069d250

17 files changed

Lines changed: 559 additions & 4 deletions

bom/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ dependencies {
7979
apiv("org.ow2.asm:asm-tree", "asm")
8080
apiv("org.ow2.asm:asm-util", "asm")
8181
apiv("org.slf4j:slf4j-api", "slf4j")
82+
apiv("commons-io:commons-io")
8283
// The log4j2 binding should be a runtime dependency but given that
8384
// some modules shade this dependency we need to keep it as api
8485
apiv("org.apache.logging.log4j:log4j-slf4j-impl", "log4j2")

core/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ dependencies {
4444
testImplementation("org.mockito:mockito-core")
4545
testImplementation("org.mockito:mockito-inline")
4646
testImplementation("org.hamcrest:hamcrest-core")
47+
testImplementation("commons-io:commons-io")
4748
testRuntimeOnly("org.apache.logging.log4j:log4j-slf4j-impl")
4849
}
4950

core/src/main/java/org/apache/calcite/avatica/BuiltInConnectionProperty.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,16 @@ public enum BuiltInConnectionProperty implements ConnectionProperty {
137137
* HTTP Response Timeout (socket timeout) in milliseconds.
138138
*/
139139
HTTP_RESPONSE_TIMEOUT("http_response_timeout",
140-
Type.NUMBER, Timeout.ofMinutes(3).toMilliseconds(), false);
140+
Type.NUMBER, Timeout.ofMinutes(3).toMilliseconds(), false),
141+
142+
/** Bearer token to use to perform Bearer authentication. */
143+
BEARER_TOKEN("bearer_token", Type.STRING, null, false),
144+
145+
/** File containing bearer tokens to use to perform Bearer authentication. */
146+
TOKEN_FILE("token_file", Type.STRING, "", false),
147+
148+
/** Classname of the BearerTokenProvider. */
149+
TOKEN_PROVIDER_CLASS("bearer_token_provider_class", Type.STRING, null, false);
141150

142151
private final String camelName;
143152
private final Type type;

core/src/main/java/org/apache/calcite/avatica/ConnectionConfig.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,14 @@ public interface ConnectionConfig {
8484
long getHttpConnectionTimeout();
8585
/** @see BuiltInConnectionProperty#HTTP_RESPONSE_TIMEOUT **/
8686
long getHttpResponseTimeout();
87+
/** @see BuiltInConnectionProperty#TOKEN_FILE */
88+
String getTokenFile();
89+
/** @see BuiltInConnectionProperty#BEARER_TOKEN */
90+
String getBearerToken();
91+
/** @see BuiltInConnectionProperty#TOKEN_PROVIDER_CLASS */
92+
String getBearerTokenProviderClass();
93+
94+
ConnectionPropertyValue customPropertyValue(ConnectionProperty property);
8795
}
8896

8997
// End ConnectionConfig.java

core/src/main/java/org/apache/calcite/avatica/ConnectionConfigImpl.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,22 @@ public long getHttpResponseTimeout() {
175175
return BuiltInConnectionProperty.HTTP_RESPONSE_TIMEOUT.wrap(properties).getLong();
176176
}
177177

178+
public String getTokenFile() {
179+
return BuiltInConnectionProperty.TOKEN_FILE.wrap(properties).getString();
180+
}
181+
182+
public String getBearerToken() {
183+
return BuiltInConnectionProperty.BEARER_TOKEN.wrap(properties).getString();
184+
}
185+
186+
public String getBearerTokenProviderClass() {
187+
return BuiltInConnectionProperty.TOKEN_PROVIDER_CLASS.wrap(properties).getString();
188+
}
189+
190+
public ConnectionPropertyValue customPropertyValue(ConnectionProperty property) {
191+
return property.wrap(properties);
192+
}
193+
178194
/** Converts a {@link Properties} object containing (name, value)
179195
* pairs into a map whose keys are
180196
* {@link org.apache.calcite.avatica.InternalProperty} objects.
@@ -204,7 +220,7 @@ public static Map<ConnectionProperty, String> parse(Properties properties,
204220
}
205221

206222
/** The combination of a property definition and a map of property values. */
207-
public static class PropEnv {
223+
public static class PropEnv implements ConnectionPropertyValue {
208224
final Map<? extends ConnectionProperty, String> map;
209225
private final ConnectionProperty property;
210226

core/src/main/java/org/apache/calcite/avatica/ConnectionProperty.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public interface ConnectionProperty {
4040

4141
/** Wraps this property with a properties object from which its value can be
4242
* obtained when needed. */
43-
ConnectionConfigImpl.PropEnv wrap(Properties properties);
43+
ConnectionPropertyValue wrap(Properties properties);
4444

4545
/** Whether the property is mandatory. */
4646
boolean required();
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to you under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.calcite.avatica;
18+
19+
public interface ConnectionPropertyValue {
20+
/**
21+
* Returns the string value of this property, or null if not specified and
22+
* no default.
23+
*/
24+
String getString();
25+
26+
/**
27+
* Returns the string value of this property, or null if not specified and
28+
* no default.
29+
*/
30+
String getString(String defaultValue);
31+
32+
/**
33+
* Returns the int value of this property. Throws if not set and no
34+
* default.
35+
*/
36+
int getInt();
37+
38+
/**
39+
* Returns the int value of this property. Throws if not set and no
40+
* default.
41+
*/
42+
int getInt(Number defaultValue);
43+
44+
/**
45+
* Returns the long value of this property. Throws if not set and no
46+
* default.
47+
*/
48+
long getLong();
49+
50+
/**
51+
* Returns the long value of this property. Throws if not set and no
52+
* default.
53+
*/
54+
long getLong(Number defaultValue);
55+
56+
/**
57+
* Returns the double value of this property. Throws if not set and no
58+
* default.
59+
*/
60+
double getDouble();
61+
62+
/**
63+
* Returns the double value of this property. Throws if not set and no
64+
* default.
65+
*/
66+
double getDouble(Number defaultValue);
67+
68+
/**
69+
* Returns the boolean value of this property. Throws if not set and no
70+
* default.
71+
*/
72+
boolean getBoolean();
73+
74+
/**
75+
* Returns the boolean value of this property. Throws if not set and no
76+
* default.
77+
*/
78+
boolean getBoolean(boolean defaultValue);
79+
80+
/**
81+
* Returns the enum value of this property. Throws if not set and no
82+
* default.
83+
*/
84+
<E extends Enum<E>> E getEnum(Class<E> enumClass);
85+
86+
/**
87+
* Returns the enum value of this property. Throws if not set and no
88+
* default.
89+
*/
90+
<E extends Enum<E>> E getEnum(Class<E> enumClass, E defaultValue);
91+
92+
/**
93+
* Returns an instance of a plugin.
94+
*
95+
* <p>Throws if not set and no default.
96+
* Also throws if the class does not implement the required interface,
97+
* or if it does not have a public default constructor or an public static
98+
* field called {@code #INSTANCE}.
99+
*/
100+
<T> T getPlugin(Class<T> pluginClass, T defaultInstance);
101+
102+
/**
103+
* Returns an instance of a plugin, using a given class name if none is
104+
* set.
105+
*
106+
* <p>Throws if not set and no default.
107+
* Also throws if the class does not implement the required interface,
108+
* or if it does not have a public default constructor or an public static
109+
* field called {@code #INSTANCE}.
110+
*/
111+
<T> T getPlugin(Class<T> pluginClass, String defaultClassName,
112+
T defaultInstance);
113+
}

core/src/main/java/org/apache/calcite/avatica/remote/AuthenticationType.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public enum AuthenticationType {
2424
BASIC,
2525
DIGEST,
2626
SPNEGO,
27+
BEARER,
2728
CUSTOM;
2829
}
2930

core/src/main/java/org/apache/calcite/avatica/remote/AvaticaCommonsHttpClientImpl.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.apache.hc.client5.http.SystemDefaultDnsResolver;
2323
import org.apache.hc.client5.http.auth.AuthSchemeFactory;
2424
import org.apache.hc.client5.http.auth.AuthScope;
25+
import org.apache.hc.client5.http.auth.BearerToken;
2526
import org.apache.hc.client5.http.auth.Credentials;
2627
import org.apache.hc.client5.http.auth.CredentialsProvider;
2728
import org.apache.hc.client5.http.auth.StandardAuthScheme;
@@ -31,6 +32,7 @@
3132
import org.apache.hc.client5.http.impl.auth.BasicAuthCache;
3233
import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
3334
import org.apache.hc.client5.http.impl.auth.BasicSchemeFactory;
35+
import org.apache.hc.client5.http.impl.auth.BearerSchemeFactory;
3436
import org.apache.hc.client5.http.impl.auth.DigestSchemeFactory;
3537
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
3638
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
@@ -68,7 +70,7 @@
6870
* sent and received across the wire.
6971
*/
7072
public class AvaticaCommonsHttpClientImpl implements AvaticaHttpClient, HttpClientPoolConfigurable,
71-
UsernamePasswordAuthenticateable, GSSAuthenticateable {
73+
UsernamePasswordAuthenticateable, GSSAuthenticateable, BearerAuthenticateable {
7274
private static final Logger LOG = LoggerFactory.getLogger(AvaticaCommonsHttpClientImpl.class);
7375

7476
// SPNEGO specific settings
@@ -152,6 +154,9 @@ private RequestConfig createRequestConfig() {
152154
if (authRegistry.lookup(StandardAuthScheme.SPNEGO) != null) {
153155
preferredSchemes.add(StandardAuthScheme.SPNEGO);
154156
}
157+
if (authRegistry.lookup(StandardAuthScheme.BEARER) != null) {
158+
preferredSchemes.add(StandardAuthScheme.BEARER);
159+
}
155160
requestConfigBuilder.setTargetPreferredAuthSchemes(preferredSchemes);
156161
requestConfigBuilder.setProxyPreferredAuthSchemes(preferredSchemes);
157162
}
@@ -258,6 +263,20 @@ ClassicHttpResponse executeOpen(HttpHost httpHost, HttpPost post, HttpClientCont
258263
context.setRequestConfig(createRequestConfig());
259264
}
260265

266+
@Override public void setTokenProvider(String username, BearerTokenProvider tokenProvider) {
267+
this.credentialsProvider = new BasicCredentialsProvider();
268+
((BasicCredentialsProvider) this.credentialsProvider)
269+
.setCredentials(anyAuthScope, new BearerToken(tokenProvider.obtain(username)));
270+
271+
this.authRegistry = RegistryBuilder.<AuthSchemeFactory>create()
272+
.register(StandardAuthScheme.BEARER,
273+
new BearerSchemeFactory())
274+
.build();
275+
context.setCredentialsProvider(credentialsProvider);
276+
context.setAuthSchemeRegistry(authRegistry);
277+
context.setRequestConfig(createRequestConfig());
278+
}
279+
261280
/**
262281
* A credentials implementation which returns null.
263282
*/

core/src/main/java/org/apache/calcite/avatica/remote/AvaticaHttpClientFactoryImpl.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,24 @@ public static AvaticaHttpClientFactoryImpl getInstance() {
131131
LOG.debug("{} is not capable of kerberos authentication.", authType);
132132
}
133133

134+
if (client instanceof BearerAuthenticateable) {
135+
if (AuthenticationType.BEARER == authType) {
136+
try {
137+
BearerTokenProvider tokenProvider =
138+
BearerTokenProviderFactory.getBearerTokenProvider(config);
139+
String username = config.avaticaUser();
140+
if (null == username) {
141+
username = System.getProperty("user.name");
142+
}
143+
((BearerAuthenticateable) client).setTokenProvider(username, tokenProvider);
144+
} catch (java.io.IOException e) {
145+
LOG.debug("Failed to initialize bearer authentication");
146+
}
147+
}
148+
} else {
149+
LOG.debug("{} is not capable of bearer authentication.", authType);
150+
}
151+
134152
if (null != kerberosUtil) {
135153
client = new DoAsAvaticaHttpClient(client, kerberosUtil);
136154
}

0 commit comments

Comments
 (0)