Skip to content

Commit 816ddaf

Browse files
AarchyAron Meszaros
authored andcommitted
[CALCITE-6135] BEARER authentication support
1 parent 01a7a9e commit 816ddaf

22 files changed

+1481
-2
lines changed

bom/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ dependencies {
7878
apiv("org.ow2.asm:asm-tree", "asm")
7979
apiv("org.ow2.asm:asm-util", "asm")
8080
apiv("org.slf4j:slf4j-api", "slf4j")
81+
apiv("commons-io:commons-io")
8182
// The log4j2 binding should be a runtime dependency but given that
8283
// some modules shade this dependency we need to keep it as api
8384
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
@@ -42,6 +42,7 @@ dependencies {
4242
testImplementation("org.mockito:mockito-core")
4343
testImplementation("org.mockito:mockito-inline")
4444
testImplementation("org.hamcrest:hamcrest-core")
45+
testImplementation("commons-io:commons-io")
4546
testRuntimeOnly("org.apache.logging.log4j:log4j-slf4j-impl")
4647
}
4748

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,13 @@ public enum BuiltInConnectionProperty implements ConnectionProperty {
127127
* HTTP Connection Timeout in milliseconds.
128128
*/
129129
HTTP_CONNECTION_TIMEOUT("http_connection_timeout",
130-
Type.NUMBER, Timeout.ofMinutes(3).toMilliseconds(), false);
130+
Type.NUMBER, Timeout.ofMinutes(3).toMilliseconds(), false),
131+
132+
/** File containing bearer tokens to use to perform Bearer authentication. */
133+
TOKEN_FILE("tokenfile", Type.STRING, "", false),
134+
135+
/** Bearer token to use to perform Bearer authentication. */
136+
BEARER_TOKEN("bearertoken", Type.STRING, "", false);
131137

132138
private final String camelName;
133139
private final Type type;

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ public interface ConnectionConfig {
8181
long getLBConnectionFailoverSleepTime();
8282
/** @see BuiltInConnectionProperty#HTTP_CONNECTION_TIMEOUT **/
8383
long getHttpConnectionTimeout();
84+
/** @see BuiltInConnectionProperty#TOKEN_FILE */
85+
String tokenFile();
86+
/** @see BuiltInConnectionProperty#BEARER_TOKEN */
87+
String bearerToken();
8488
}
8589

8690
// End ConnectionConfig.java

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,14 @@ public long getHttpConnectionTimeout() {
169169
return BuiltInConnectionProperty.HTTP_CONNECTION_TIMEOUT.wrap(properties).getLong();
170170
}
171171

172+
public String tokenFile() {
173+
return BuiltInConnectionProperty.TOKEN_FILE.wrap(properties).getString();
174+
}
175+
176+
public String bearerToken() {
177+
return BuiltInConnectionProperty.BEARER_TOKEN.wrap(properties).getString();
178+
}
179+
172180
/** Converts a {@link Properties} object containing (name, value)
173181
* pairs into a map whose keys are
174182
* {@link org.apache.calcite.avatica.InternalProperty} objects.

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
@@ -60,6 +60,9 @@
6060
import java.net.URISyntaxException;
6161
import java.net.URL;
6262
import java.security.Principal;
63+
import java.util.Arrays;
64+
import java.util.Collections;
65+
import java.util.List;
6366
import java.util.Objects;
6467
import java.util.concurrent.TimeUnit;
6568

@@ -68,7 +71,7 @@
6871
* sent and received across the wire.
6972
*/
7073
public class AvaticaCommonsHttpClientImpl implements AvaticaHttpClient, HttpClientPoolConfigurable,
71-
UsernamePasswordAuthenticateable, GSSAuthenticateable {
74+
UsernamePasswordAuthenticateable, GSSAuthenticateable, BearerAuthenticateable {
7275
private static final Logger LOG = LoggerFactory.getLogger(AvaticaCommonsHttpClientImpl.class);
7376

7477
// SPNEGO specific settings
@@ -91,6 +94,10 @@ public class AvaticaCommonsHttpClientImpl implements AvaticaHttpClient, HttpClie
9194
protected CredentialsProvider credentialsProvider = null;
9295
protected Lookup<AuthSchemeFactory> authRegistry = null;
9396
protected Object userToken;
97+
private static final List<String> AVATICA_SCHEME_PRIORITY =
98+
Collections.unmodifiableList(Arrays.asList(StandardAuthScheme.BASIC,
99+
StandardAuthScheme.DIGEST, StandardAuthScheme.SPNEGO, StandardAuthScheme.NTLM,
100+
StandardAuthScheme.KERBEROS, "Bearer"));
94101

95102
public AvaticaCommonsHttpClientImpl(URL url) {
96103
this.uri = toURI(Objects.requireNonNull(url));
@@ -104,6 +111,7 @@ protected void initializeClient(PoolingHttpClientConnectionManager pool,
104111
RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
105112
RequestConfig requestConfig = requestConfigBuilder
106113
.setConnectTimeout(config.getHttpConnectionTimeout(), TimeUnit.MILLISECONDS)
114+
.setTargetPreferredAuthSchemes(AVATICA_SCHEME_PRIORITY)
107115
.build();
108116
HttpClientBuilder httpClientBuilder = HttpClients.custom().setConnectionManager(pool)
109117
.setDefaultRequestConfig(requestConfig);
@@ -206,6 +214,17 @@ CloseableHttpResponse execute(HttpPost post, HttpClientContext context)
206214
}
207215
}
208216

217+
@Override public void setTokenProvider(String username, BearerTokenProvider tokenProvider) {
218+
this.credentialsProvider = new BasicCredentialsProvider();
219+
((BasicCredentialsProvider) this.credentialsProvider)
220+
.setCredentials(anyAuthScope, new BearerCredentials(username, tokenProvider));
221+
222+
this.authRegistry = RegistryBuilder.<AuthSchemeFactory>create()
223+
.register("Bearer",
224+
new BearerSchemeFactory())
225+
.build();
226+
}
227+
209228
/**
210229
* A credentials implementation which returns null.
211230
*/

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

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

133+
if (client instanceof BearerAuthenticateable) {
134+
if (AuthenticationType.BEARER == authType) {
135+
if (config.bearerToken() == null && config.tokenFile() == null
136+
|| config.bearerToken() != null && config.tokenFile() != null) {
137+
LOG.debug("Failed to initialize bearer authentication:"
138+
+ "either the tokenfile or bearertoken must be set");
139+
} else {
140+
BearerTokenProvider tokenProvider;
141+
if (config.bearerToken() != null) {
142+
tokenProvider = new SingleBearerTokenProvider();
143+
} else {
144+
tokenProvider = new FileBearerTokenProvider();
145+
}
146+
147+
try {
148+
tokenProvider.init(config);
149+
String username = config.avaticaUser();
150+
if (null == username) {
151+
username = System.getProperty("user.name");
152+
}
153+
((BearerAuthenticateable) client).setTokenProvider(username, tokenProvider);
154+
} catch (java.io.IOException e) {
155+
LOG.debug("Failed to initialize bearer authentication");
156+
}
157+
}
158+
}
159+
} else {
160+
LOG.debug("{} is not capable of bearer authentication.", authType);
161+
}
162+
133163
if (null != kerberosUtil) {
134164
client = new DoAsAvaticaHttpClient(client, kerberosUtil);
135165
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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.remote;
18+
19+
public interface BearerAuthenticateable {
20+
21+
void setTokenProvider(String username, BearerTokenProvider tokenProvider);
22+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
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.remote;
18+
19+
import org.apache.hc.client5.http.auth.Credentials;
20+
import org.apache.hc.core5.annotation.Contract;
21+
import org.apache.hc.core5.annotation.ThreadingBehavior;
22+
import org.apache.hc.core5.util.Args;
23+
24+
import java.io.Serializable;
25+
import java.security.Principal;
26+
27+
@Contract(threading = ThreadingBehavior.IMMUTABLE)
28+
public class BearerCredentials implements Credentials, Serializable {
29+
30+
private final BearerTokenProvider tokenProvider;
31+
32+
private final String userName;
33+
34+
public BearerCredentials(final String userName, final BearerTokenProvider tokenProvider) {
35+
Args.notNull(userName, "userName");
36+
Args.notNull(tokenProvider, "tokenProvider");
37+
this.tokenProvider = tokenProvider;
38+
this.userName = userName;
39+
}
40+
41+
public String getToken() {
42+
return tokenProvider.obtain(userName);
43+
}
44+
45+
@Override
46+
public Principal getUserPrincipal() {
47+
return null;
48+
}
49+
50+
@Override
51+
public char[] getPassword() {
52+
return null;
53+
}
54+
}

0 commit comments

Comments
 (0)