This is an automated email from the ASF dual-hosted git repository.
diqiu50 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/main by this push:
new 5b3b27f02c [#10003] feat (trino-connector): Gravitino Trino connector
supports authentication for the Gravitino server (#10052)
5b3b27f02c is described below
commit 5b3b27f02c52067ea5f65fbc3bdc20ac671eb163
Author: Yuhui <[email protected]>
AuthorDate: Wed Apr 1 15:06:52 2026 +0800
[#10003] feat (trino-connector): Gravitino Trino connector supports
authentication for the Gravitino server (#10052)
### What changes were proposed in this pull request?
Gravitino Trino connector supports authentication for the Gravitino
server
### Why are the changes needed?
Fix: #10003
### Does this PR introduce _any_ user-facing change?
Update docs
### How was this patch tested?
Manually test
---------
Co-authored-by: Claude Sonnet 4.5 <[email protected]>
---
.../client/GravitinoClientConfiguration.java | 31 +--
.../gravitino/client/KerberosTokenProvider.java | 51 ++++-
docs/trino-connector/authentication.md | 136 +++++++++++++
docs/trino-connector/configuration.md | 4 +
.../gravitino/trino/connector/GravitinoConfig.java | 22 ++
.../connector/catalog/CatalogConnectorManager.java | 25 ++-
.../connector/catalog/GravitinoAuthProvider.java | 226 +++++++++++++++++++++
.../trino/connector/TestGravitinoConfig.java | 21 ++
.../catalog/TestGravitinoAuthProvider.java | 150 ++++++++++++++
9 files changed, 645 insertions(+), 21 deletions(-)
diff --git
a/clients/client-java/src/main/java/org/apache/gravitino/client/GravitinoClientConfiguration.java
b/clients/client-java/src/main/java/org/apache/gravitino/client/GravitinoClientConfiguration.java
index eb61f734c5..03326ed912 100644
---
a/clients/client-java/src/main/java/org/apache/gravitino/client/GravitinoClientConfiguration.java
+++
b/clients/client-java/src/main/java/org/apache/gravitino/client/GravitinoClientConfiguration.java
@@ -32,18 +32,6 @@ public class GravitinoClientConfiguration {
/** The configuration key prefix for the Gravitino client config. */
public static final String GRAVITINO_CLIENT_CONFIG_PREFIX =
"gravitino.client.";
- /** A default value for http connection timeout in milliseconds. */
- public static final long CLIENT_CONNECTION_TIMEOUT_MS_DEFAULT = 180_000L;
-
- /** A default value for http socket timeout in milliseconds. */
- public static final int CLIENT_SOCKET_TIMEOUT_MS_DEFAULT = 180_000;
-
- /** An optional http connection timeout in milliseconds. */
- public static final String CLIENT_CONNECTION_TIMEOUT_MS =
"gravitino.client.connectionTimeoutMs";
-
- /** An optional http socket timeout in milliseconds. */
- public static final String CLIENT_SOCKET_TIMEOUT_MS =
"gravitino.client.socketTimeoutMs";
-
/**
* A default value for max total HTTP connections in the connection pool.
This is the same as the
* default value of Apache HttpClient 5.x
@@ -51,7 +39,8 @@ public class GravitinoClientConfiguration {
public static final int CLIENT_MAX_CONNECTIONS_DEFAULT = 25;
/** An optional max total HTTP connections. */
- public static final String CLIENT_MAX_CONNECTIONS =
"gravitino.client.maxConnections";
+ public static final String CLIENT_MAX_CONNECTIONS =
+ GRAVITINO_CLIENT_CONFIG_PREFIX + "maxConnections";
/**
* A default value for max HTTP connections per route. This is the same as
the default value of
@@ -61,7 +50,21 @@ public class GravitinoClientConfiguration {
/** An optional max HTTP connections per route. */
public static final String CLIENT_MAX_CONNECTIONS_PER_ROUTE =
- "gravitino.client.maxConnectionsPerRoute";
+ GRAVITINO_CLIENT_CONFIG_PREFIX + "maxConnectionsPerRoute";
+
+ /** A default value for http connection timeout in milliseconds. */
+ public static final long CLIENT_CONNECTION_TIMEOUT_MS_DEFAULT = 180_000L;
+
+ /** A default value for http socket timeout in milliseconds. */
+ public static final int CLIENT_SOCKET_TIMEOUT_MS_DEFAULT = 180_000;
+
+ /** An optional http connection timeout in milliseconds. */
+ public static final String CLIENT_CONNECTION_TIMEOUT_MS =
+ GRAVITINO_CLIENT_CONFIG_PREFIX + "connectionTimeoutMs";
+
+ /** An optional http socket timeout in milliseconds. */
+ public static final String CLIENT_SOCKET_TIMEOUT_MS =
+ GRAVITINO_CLIENT_CONFIG_PREFIX + "socketTimeoutMs";
private static final Set<String> SUPPORT_CLIENT_CONFIG_KEYS =
ImmutableSet.of(
diff --git
a/clients/client-java/src/main/java/org/apache/gravitino/client/KerberosTokenProvider.java
b/clients/client-java/src/main/java/org/apache/gravitino/client/KerberosTokenProvider.java
index f3688700fa..21ef259342 100644
---
a/clients/client-java/src/main/java/org/apache/gravitino/client/KerberosTokenProvider.java
+++
b/clients/client-java/src/main/java/org/apache/gravitino/client/KerberosTokenProvider.java
@@ -23,7 +23,10 @@ import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
import java.io.File;
import java.io.IOException;
+import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
+import java.security.AccessControlContext;
+import java.security.AccessController;
import java.time.Instant;
import java.util.Base64;
import java.util.List;
@@ -296,9 +299,18 @@ public final class KerberosTokenProvider implements
AuthDataProvider {
public KerberosTokenProvider build() {
KerberosTokenProvider provider = new KerberosTokenProvider();
- // Check if the framework (e.g., Flink) has already established Kerberos
credentials
- java.security.AccessControlContext context =
java.security.AccessController.getContext();
- Subject subject = Subject.getSubject(context);
+ // Check if the framework (e.g., Flink) has already established Kerberos
credentials.
+ // Use Subject.current() on JDK 18+ (where
Subject.getSubject(AccessControlContext)
+ // was removed), and fall back to the deprecated API on JDK 17 and below.
+ Subject subject;
+ try {
+ subject = getCurrentSubject();
+ } catch (UnsupportedOperationException e) {
+ throw new IllegalArgumentException(
+ "Cannot detect current Kerberos subject on JDK 17 with Security
Manager disabled. "
+ + "Please configure keyTabFile and clientPrincipal
explicitly.",
+ e);
+ }
// If credentials exist (KerberosKey or KerberosTicket), reuse them
// This avoids redundant logins when Flink has already authenticated
with Kerberos
@@ -349,5 +361,38 @@ public final class KerberosTokenProvider implements
AuthDataProvider {
.map(Object::toString)
.orElse(null);
}
+
+ /**
+ * Returns the current {@link Subject} in a JDK-version-neutral way.
+ *
+ * <ul>
+ * <li>JDK 18+: uses {@code Subject.current()} via reflection, which is
the designated
+ * replacement for the removed {@code
Subject.getSubject(AccessControlContext)}
+ * <li>JDK 17 and below: uses the deprecated {@code
Subject.getSubject(AccessControlContext)}
+ * </ul>
+ *
+ * @return the current Subject, or {@code null} if none is established
+ * @throws UnsupportedOperationException if the Security Manager is
explicitly disabled on JDK
+ * 17
+ * @throws RuntimeException if reflection fails unexpectedly
+ */
+ @SuppressWarnings("removal")
+ private static Subject getCurrentSubject() {
+ // Use reflection to call Subject.current() (JDK 18+) because this
project compiles on
+ // JDK 17, where Subject.current() does not exist as a compile-time API.
+ // Fall back to the deprecated Subject.getSubject(AccessControlContext)
on JDK 17 and below.
+ try {
+ Method currentMethod = Subject.class.getMethod("current");
+ return (Subject) currentMethod.invoke(null);
+ } catch (NoSuchMethodException e) {
+ // JDK 17 and below: Subject.current() does not exist, use the legacy
API.
+ // AccessController.getContext() may throw
UnsupportedOperationException when the
+ // Security Manager is explicitly disabled — propagate so the caller
can decide.
+ AccessControlContext context = AccessController.getContext();
+ return Subject.getSubject(context);
+ } catch (ReflectiveOperationException e) {
+ throw new RuntimeException("Failed to invoke Subject.current() via
reflection", e);
+ }
+ }
}
}
diff --git a/docs/trino-connector/authentication.md
b/docs/trino-connector/authentication.md
new file mode 100644
index 0000000000..262cc2455c
--- /dev/null
+++ b/docs/trino-connector/authentication.md
@@ -0,0 +1,136 @@
+---
+title: "Apache Gravitino Trino connector Authentication"
+slug: /trino-connector/authentication
+keyword: gravitino connector trino authentication
+license: "This software is licensed under the Apache License version 2."
+---
+
+## Authentication
+
+The Gravitino Trino connector supports authenticating to the Gravitino server
using the same authentication mechanisms as the Gravitino Java client: Simple,
OAuth2, and Kerberos. Authentication is configured through the Trino connector
properties file using the `gravitino.client.*` prefix.
+
+If `gravitino.client.authType` is not set, the connector operates in
no-authentication mode and connects to the Gravitino server without any
credentials.
+
+### Simple Authentication
+
+Simple authentication uses a username to authenticate with the Gravitino
server.
+
+**Configuration in `etc/catalog/gravitino.properties`:**
+
+```properties
+connector.name=gravitino
+gravitino.metalake=metalake
+gravitino.uri=http://localhost:8090
+
+# Simple authentication with username
+gravitino.client.authType=simple
+gravitino.user=admin
+```
+
+**Configuration properties:**
+
+| Property | Description
| Default value | Required
| Since version |
+|-----------------------------------|----------------------------------------------------------------------|------------------|----------------------------------------|---------------|
+| `gravitino.client.authType` | Authentication type: `simple`, `oauth2`,
or `kerberos` | (none) | No
| 1.3.0 |
+| `gravitino.user` | Username for simple authentication
| (none) | No (uses system user if not
specified) | 1.3.0 |
+
+### OAuth2 Authentication
+
+OAuth2 authentication uses OAuth 2.0 tokens to authenticate with the Gravitino
server.
+
+**Configuration in `etc/catalog/gravitino.properties`:**
+
+```properties
+connector.name=gravitino
+gravitino.metalake=metalake
+gravitino.uri=http://localhost:8090
+
+# OAuth2 authentication
+gravitino.client.authType=oauth2
+gravitino.client.oauth2.serverUri=http://oauth-server:8080
+gravitino.client.oauth2.credential=client_id:client_secret
+gravitino.client.oauth2.path=oauth2/token
+gravitino.client.oauth2.scope=gravitino
+```
+
+**Configuration properties:**
+
+| Property | Description
| Default value | Required | Since version |
+|--------------------------------------|--------------------------------------------------------|---------------|-----------------------------|---------------|
+| `gravitino.client.authType` | Authentication type: `simple`,
`oauth2`, or `kerberos` | (none) | Yes (to enable OAuth2) | 1.3.0
|
+| `gravitino.client.oauth2.serverUri` | OAuth2 server URI
| (none) | Yes if authType is `oauth2` | 1.3.0 |
+| `gravitino.client.oauth2.credential` | OAuth2 credentials in format
`client_id:client_secret` | (none) | Yes if authType is `oauth2` | 1.3.0
|
+| `gravitino.client.oauth2.path` | OAuth2 token endpoint path
| (none) | Yes if authType is `oauth2` | 1.3.0 |
+| `gravitino.client.oauth2.scope` | OAuth2 scope
| (none) | Yes if authType is `oauth2` | 1.3.0 |
+
+### Kerberos Authentication
+
+Kerberos authentication uses Kerberos tickets to authenticate with the
Gravitino server.
+
+**Configuration in `etc/catalog/gravitino.properties`:**
+
+```properties
+connector.name=gravitino
+gravitino.metalake=metalake
+gravitino.uri=http://localhost:8090
+
+# Kerberos authentication with keytab
+gravitino.client.authType=kerberos
+gravitino.client.kerberos.principal=user@REALM
+gravitino.client.kerberos.keytabFilePath=/path/to/user.keytab
+```
+
+**Configuration properties:**
+
+| Property | Description | Default
value | Required | Since
version |
+|--------------------------------------------|---------------------|---------------|------------------------------------------------------------|---------------|
+| `gravitino.client.authType` | Authentication type: `simple`,
`oauth2`, or `kerberos` | (none) | Yes (to enable Kerberos)
| 1.3.0 |
+| `gravitino.client.kerberos.principal` | Kerberos principal | (none)
| Yes if authType is `kerberos` | 1.3.0 |
+| `gravitino.client.kerberos.keytabFilePath` | Path to keytab file | (none)
| No (uses ticket cache if not specified) | 1.3.0 |
+
+### Example: Connecting to OAuth-protected Gravitino Server
+
+This example shows how to configure the Trino connector to connect to a
Gravitino server protected by OAuth authentication.
+
+**1. Configure Gravitino server with OAuth** (in `conf/gravitino.conf`):
+
+```properties
+gravitino.authenticators=oauth
+gravitino.authenticator.oauth.serviceAudience=gravitino
+gravitino.authenticator.oauth.defaultSignKey=<your-signing-key>
+gravitino.authenticator.oauth.tokenPath=/oauth2/token
+gravitino.authenticator.oauth.serverUri=http://localhost:8177
+```
+
+**2. Configure Trino connector** (in `etc/catalog/gravitino.properties`):
+
+```properties
+connector.name=gravitino
+gravitino.metalake=my_metalake
+gravitino.uri=http://localhost:8090
+
+# OAuth2 authentication
+gravitino.client.authType=oauth2
+gravitino.client.oauth2.serverUri=http://localhost:8177
+gravitino.client.oauth2.credential=test:test
+gravitino.client.oauth2.path=oauth2/token
+gravitino.client.oauth2.scope=test
+```
+
+**3. Verify the connection:**
+
+```sql
+SHOW CATALOGS;
+```
+
+### Notes
+
+- The Gravitino server must be configured with the corresponding
authentication mechanism enabled.
+- For OAuth2 authentication, ensure the OAuth2 server is accessible from the
Trino coordinator and workers.
+- For Kerberos authentication, ensure the Kerberos configuration is properly
set up on all Trino nodes.
+- Authentication configuration is passed through the `gravitino.client.*`
prefix to the underlying Gravitino Java client.
+
+### See Also
+
+- [Gravitino Server Authentication
Configuration](../security/how-to-authenticate.md)
+- [Trino Connector Configuration](./configuration.md)
diff --git a/docs/trino-connector/configuration.md
b/docs/trino-connector/configuration.md
index 4adeb28360..7380684a90 100644
--- a/docs/trino-connector/configuration.md
+++ b/docs/trino-connector/configuration.md
@@ -20,3 +20,7 @@ license: "This software is licensed under the Apache License
version 2."
To configure the Gravitino client, use properties prefixed with
`gravitino.client.`. These properties will directly passed to the Gravitino
client.
**Note:** Invalid configuration properties will result in exceptions. Please
see [Gravitino Java client
configurations](../how-to-use-gravitino-client.md#gravitino-java-client-configuration)
for more support client configuration.
+
+## Authentication
+
+The Gravitino Trino connector supports authenticating to the Gravitino server
using Simple, OAuth, and Kerberos authentication. For detailed authentication
configuration, please refer to [Trino Connector
Authentication](./authentication.md).
diff --git
a/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/GravitinoConfig.java
b/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/GravitinoConfig.java
index 70d69ead10..f32d5dc98d 100644
---
a/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/GravitinoConfig.java
+++
b/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/GravitinoConfig.java
@@ -80,6 +80,13 @@ public class GravitinoConfig {
private static final ConfigEntry GRAVITINO_METALAKE =
new ConfigEntry("gravitino.metalake", "The metalake name for used", "",
true);
+ private static final ConfigEntry GRAVITINO_USER =
+ new ConfigEntry(
+ "gravitino.user",
+ "The username for simple authentication with the Gravitino server",
+ "",
+ false);
+
/**
* @deprecated Please use {@code gravitino.use-single-metalake} instead.
*/
@@ -193,6 +200,15 @@ public class GravitinoConfig {
return config.getOrDefault(GRAVITINO_METALAKE.key,
GRAVITINO_METALAKE.defaultValue);
}
+ /**
+ * Retrieves the username for simple authentication.
+ *
+ * @return the username, or empty string if not configured
+ */
+ public String getUser() {
+ return config.getOrDefault(GRAVITINO_USER.key,
GRAVITINO_USER.defaultValue);
+ }
+
/**
* Retrieves the config for Grivitino client.
*
@@ -323,6 +339,12 @@ public class GravitinoConfig {
stringList.add(String.format("\"%s\"='%s'", entry.getKey(), value));
}
}
+ // copy the configuration by the prefix of GRAVITINO_CLIENT_CONFIG_PREFIX
+ config.entrySet().stream()
+ .filter(entry ->
entry.getKey().startsWith(GRAVITINO_CLIENT_CONFIG_PREFIX.key))
+ .forEach(
+ entry ->
+ stringList.add(String.format("\"%s\"='%s'", entry.getKey(),
entry.getValue())));
return StringUtils.join(stringList, ',');
}
diff --git
a/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/CatalogConnectorManager.java
b/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/CatalogConnectorManager.java
index 38f5290464..7357531830 100644
---
a/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/CatalogConnectorManager.java
+++
b/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/CatalogConnectorManager.java
@@ -116,10 +116,27 @@ public class CatalogConnectorManager {
Preconditions.checkArgument(config != null, "config is not null");
this.config = config;
if (client == null) {
- this.gravitinoClient =
- GravitinoAdminClient.builder(config.getURI())
- .withClientConfig(config.getClientConfig())
- .build();
+ String authType =
config.getClientConfig().getOrDefault("gravitino.client.authType", "none");
+ LOG.info("Building Gravitino client with authType: {}", authType);
+ try {
+ this.gravitinoClient = GravitinoAuthProvider.buildClient(config);
+ } catch (IllegalArgumentException e) {
+ throw new TrinoException(
+ GravitinoErrorCode.GRAVITINO_ILLEGAL_ARGUMENT,
+ "Invalid Gravitino client configuration for authType '"
+ + authType
+ + "': "
+ + e.getMessage(),
+ e);
+ } catch (RuntimeException e) {
+ throw new TrinoException(
+ GravitinoErrorCode.GRAVITINO_RUNTIME_ERROR,
+ "Runtime failure while building Gravitino client with authType '"
+ + authType
+ + "': "
+ + e.getMessage(),
+ e);
+ }
} else {
this.gravitinoClient = client;
}
diff --git
a/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/GravitinoAuthProvider.java
b/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/GravitinoAuthProvider.java
new file mode 100644
index 0000000000..da12c32547
--- /dev/null
+++
b/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/GravitinoAuthProvider.java
@@ -0,0 +1,226 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.trino.connector.catalog;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.gravitino.client.DefaultOAuth2TokenProvider;
+import org.apache.gravitino.client.GravitinoAdminClient;
+import org.apache.gravitino.client.GravitinoClientConfiguration;
+import org.apache.gravitino.client.KerberosTokenProvider;
+import org.apache.gravitino.trino.connector.GravitinoConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Builds a {@link GravitinoAdminClient} with the appropriate authentication
provider based on the
+ * Gravitino config.
+ */
+class GravitinoAuthProvider {
+
+ private static final Logger LOG =
LoggerFactory.getLogger(GravitinoAuthProvider.class);
+
+ /** Authentication type configuration key. */
+ static final String AUTH_TYPE_KEY =
+ GravitinoClientConfiguration.GRAVITINO_CLIENT_CONFIG_PREFIX + "authType";
+
+ /** Simple authentication user configuration key. */
+ static final String SIMPLE_AUTH_USER_KEY = "gravitino.user";
+
+ /** OAuth2 server URI configuration key. */
+ static final String OAUTH_SERVER_URI_KEY =
+ GravitinoClientConfiguration.GRAVITINO_CLIENT_CONFIG_PREFIX +
"oauth2.serverUri";
+
+ /** OAuth2 credential configuration key. */
+ static final String OAUTH_CREDENTIAL_KEY =
+ GravitinoClientConfiguration.GRAVITINO_CLIENT_CONFIG_PREFIX +
"oauth2.credential";
+
+ /** OAuth2 path configuration key. */
+ static final String OAUTH_PATH_KEY =
+ GravitinoClientConfiguration.GRAVITINO_CLIENT_CONFIG_PREFIX +
"oauth2.path";
+
+ /** OAuth2 scope configuration key. */
+ static final String OAUTH_SCOPE_KEY =
+ GravitinoClientConfiguration.GRAVITINO_CLIENT_CONFIG_PREFIX +
"oauth2.scope";
+
+ /** Kerberos principal configuration key. */
+ static final String KERBEROS_PRINCIPAL_KEY =
+ GravitinoClientConfiguration.GRAVITINO_CLIENT_CONFIG_PREFIX +
"kerberos.principal";
+
+ /** Kerberos keytab file path configuration key. */
+ static final String KERBEROS_KEYTAB_FILE_PATH_KEY =
+ GravitinoClientConfiguration.GRAVITINO_CLIENT_CONFIG_PREFIX +
"kerberos.keytabFilePath";
+
+ /** Authentication types supported by the Trino connector. */
+ enum AuthType {
+ SIMPLE,
+ OAUTH2,
+ KERBEROS,
+ NONE
+ }
+
+ private GravitinoAuthProvider() {}
+
+ /**
+ * Builds a GravitinoAdminClient from the given config, applying
authentication settings found in
+ * the client config.
+ *
+ * @param config the Gravitino configuration containing server URI and
client properties
+ * @return a configured GravitinoAdminClient
+ */
+ public static GravitinoAdminClient buildClient(GravitinoConfig config) {
+ Map<String, String> clientConfig = new HashMap<>(config.getClientConfig());
+ String uri = config.getURI();
+ String authTypeStr = clientConfig.get(AUTH_TYPE_KEY);
+
+ GravitinoAdminClient.AdminClientBuilder builder =
GravitinoAdminClient.builder(uri);
+
+ if (StringUtils.isNotBlank(authTypeStr)) {
+ AuthType authType;
+ try {
+ authType = AuthType.valueOf(authTypeStr.toUpperCase(Locale.ROOT));
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException(
+ String.format(
+ "Invalid authentication type: %s. Valid values are: simple,
oauth2, kerberos, none",
+ authTypeStr),
+ e);
+ }
+
+ switch (authType) {
+ case SIMPLE:
+ buildSimpleAuth(builder, config.getUser());
+ break;
+ case OAUTH2:
+ builder.withOAuth(buildOAuthProvider(clientConfig));
+ break;
+ case KERBEROS:
+ builder.withKerberosAuth(buildKerberosProvider(clientConfig));
+ break;
+ case NONE:
+ default:
+ break;
+ }
+ }
+
+ // Remove auth-specific keys before passing to withClientConfig
+ clientConfig.remove(AUTH_TYPE_KEY);
+ clientConfig.remove(OAUTH_SERVER_URI_KEY);
+ clientConfig.remove(OAUTH_CREDENTIAL_KEY);
+ clientConfig.remove(OAUTH_PATH_KEY);
+ clientConfig.remove(OAUTH_SCOPE_KEY);
+ clientConfig.remove(KERBEROS_PRINCIPAL_KEY);
+ clientConfig.remove(KERBEROS_KEYTAB_FILE_PATH_KEY);
+
+ builder.withClientConfig(clientConfig);
+ return builder.build();
+ }
+
+ private static void buildSimpleAuth(
+ GravitinoAdminClient.AdminClientBuilder builder, String simpleUser) {
+ if (StringUtils.isNotBlank(simpleUser)) {
+ builder.withSimpleAuth(simpleUser);
+ } else {
+ builder.withSimpleAuth();
+ }
+ }
+
+ private static DefaultOAuth2TokenProvider buildOAuthProvider(Map<String,
String> config) {
+ String serverUri = config.get(OAUTH_SERVER_URI_KEY);
+ String credential = config.get(OAUTH_CREDENTIAL_KEY);
+ String path = config.get(OAUTH_PATH_KEY);
+ String scope = config.get(OAUTH_SCOPE_KEY);
+
+ if (StringUtils.isBlank(serverUri)) {
+ throw new IllegalArgumentException(
+ String.format("OAuth server URI is required. Please set %s",
OAUTH_SERVER_URI_KEY));
+ }
+ if (StringUtils.isBlank(credential)) {
+ throw new IllegalArgumentException(
+ String.format("OAuth credential is required. Please set %s",
OAUTH_CREDENTIAL_KEY));
+ }
+ if (StringUtils.isBlank(path)) {
+ throw new IllegalArgumentException(
+ String.format("OAuth path is required. Please set %s",
OAUTH_PATH_KEY));
+ }
+ if (StringUtils.isBlank(scope)) {
+ throw new IllegalArgumentException(
+ String.format("OAuth scope is required. Please set %s",
OAUTH_SCOPE_KEY));
+ }
+
+ // Remove leading slash from path if present
+ String normalizedPath = path.startsWith("/") ? path.substring(1) : path;
+
+ LOG.info("Initializing OAuth2 token provider with server URI: {}",
serverUri);
+ return DefaultOAuth2TokenProvider.builder()
+ .withUri(serverUri)
+ .withCredential(credential)
+ .withPath(normalizedPath)
+ .withScope(scope)
+ .build();
+ }
+
+ private static KerberosTokenProvider buildKerberosProvider(Map<String,
String> config) {
+ String principal = config.get(KERBEROS_PRINCIPAL_KEY);
+ String keytabFilePath = config.get(KERBEROS_KEYTAB_FILE_PATH_KEY);
+
+ if (StringUtils.isBlank(principal)) {
+ throw new IllegalArgumentException(
+ String.format("Kerberos principal is required. Please set %s",
KERBEROS_PRINCIPAL_KEY));
+ }
+
+ KerberosTokenProvider.Builder kerberosBuilder =
+ KerberosTokenProvider.builder().withClientPrincipal(principal);
+
+ if (StringUtils.isNotBlank(keytabFilePath)) {
+ File keytabFile = new File(keytabFilePath);
+ if (!keytabFile.exists()) {
+ throw new IllegalArgumentException(
+ String.format(
+ "Keytab file configured via %s does not exist: %s",
+ KERBEROS_KEYTAB_FILE_PATH_KEY, keytabFilePath));
+ }
+ if (!keytabFile.isFile()) {
+ throw new IllegalArgumentException(
+ String.format(
+ "Keytab path configured via %s is not a file: %s",
+ KERBEROS_KEYTAB_FILE_PATH_KEY, keytabFilePath));
+ }
+ if (!keytabFile.canRead()) {
+ throw new IllegalArgumentException(
+ String.format(
+ "Keytab file configured via %s is not readable: %s",
+ KERBEROS_KEYTAB_FILE_PATH_KEY, keytabFilePath));
+ }
+ kerberosBuilder.withKeyTabFile(keytabFile);
+ } else {
+ LOG.warn(
+ "No keytab file configured for Kerberos authentication ({}). "
+ + "Authentication will fail at runtime unless Kerberos
credentials are already "
+ + "present in the current security context.",
+ KERBEROS_KEYTAB_FILE_PATH_KEY);
+ }
+
+ // host is set by GravitinoAdminClient.Builder.withKerberosAuth() from the
server URI
+ return kerberosBuilder.build();
+ }
+}
diff --git
a/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/TestGravitinoConfig.java
b/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/TestGravitinoConfig.java
index f79fa1bfe2..8aa65b4b69 100644
---
a/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/TestGravitinoConfig.java
+++
b/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/TestGravitinoConfig.java
@@ -147,6 +147,27 @@ public class TestGravitinoConfig {
"Config `gravitino.trino.skip-catalog-patterns` is invalid because it
contains an illegal regular expression");
}
+ @Test
+ public void testToCatalogConfigWithAuthProperties() {
+ String gravitinoUrl = "http://127.0.0.1:8000";
+ String metalake = "user_001";
+ ImmutableMap<String, String> configMap =
+ ImmutableMap.of(
+ "gravitino.uri",
+ gravitinoUrl,
+ "gravitino.metalake",
+ metalake,
+ "gravitino.client.authType",
+ "simple",
+ "gravitino.user",
+ "admin");
+ GravitinoConfig config = new GravitinoConfig(configMap);
+
+ String catalogConfig = config.toCatalogConfig();
+
assertTrue(catalogConfig.contains("\"gravitino.client.authType\"='simple'"));
+ assertTrue(catalogConfig.contains("\"gravitino.user\"='admin'"));
+ }
+
private static boolean skipCatalog(String catalogName, GravitinoConfig
config) {
for (Pattern pattern : config.getSkipCatalogPatterns()) {
if (pattern.matcher(catalogName).matches()) {
diff --git
a/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/catalog/TestGravitinoAuthProvider.java
b/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/catalog/TestGravitinoAuthProvider.java
new file mode 100644
index 0000000000..5a341614b7
--- /dev/null
+++
b/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/catalog/TestGravitinoAuthProvider.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.trino.connector.catalog;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import com.google.common.collect.ImmutableMap;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import org.apache.gravitino.trino.connector.GravitinoConfig;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+public class TestGravitinoAuthProvider {
+
+ @Test
+ public void testBuildClientNoAuth() {
+ assertDoesNotThrow(() ->
GravitinoAuthProvider.buildClient(buildConfig(ImmutableMap.of())));
+ }
+
+ @Test
+ public void testBuildClientNoneAuth() {
+ assertDoesNotThrow(
+ () ->
+ GravitinoAuthProvider.buildClient(
+
buildConfig(ImmutableMap.of(GravitinoAuthProvider.AUTH_TYPE_KEY, "none"))));
+ }
+
+ @Test
+ public void testBuildClientSimpleAuthWithUser() {
+ assertDoesNotThrow(
+ () ->
+ GravitinoAuthProvider.buildClient(
+ buildConfig(
+ ImmutableMap.of(
+ GravitinoAuthProvider.AUTH_TYPE_KEY, "simple",
+ GravitinoAuthProvider.SIMPLE_AUTH_USER_KEY,
"alice"))));
+ }
+
+ @Test
+ public void testBuildClientSimpleAuthNoUser() {
+ assertDoesNotThrow(
+ () ->
+ GravitinoAuthProvider.buildClient(
+
buildConfig(ImmutableMap.of(GravitinoAuthProvider.AUTH_TYPE_KEY, "simple"))));
+ }
+
+ @Test
+ public void testBuildClientInvalidAuthType() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ GravitinoAuthProvider.buildClient(
+
buildConfig(ImmutableMap.of(GravitinoAuthProvider.AUTH_TYPE_KEY,
"invalid_type"))));
+ }
+
+ @Test
+ public void testBuildClientOAuthMissingServerUri() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ GravitinoAuthProvider.buildClient(
+ buildConfig(
+ ImmutableMap.of(
+ GravitinoAuthProvider.AUTH_TYPE_KEY, "oauth2",
+ GravitinoAuthProvider.OAUTH_CREDENTIAL_KEY, "cred",
+ GravitinoAuthProvider.OAUTH_PATH_KEY, "oauth2/token",
+ GravitinoAuthProvider.OAUTH_SCOPE_KEY, "scope"))));
+ }
+
+ @Test
+ public void testBuildClientOAuthMissingCredential() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ GravitinoAuthProvider.buildClient(
+ buildConfig(
+ ImmutableMap.of(
+ GravitinoAuthProvider.AUTH_TYPE_KEY, "oauth2",
+ GravitinoAuthProvider.OAUTH_SERVER_URI_KEY,
"http://auth.example.com",
+ GravitinoAuthProvider.OAUTH_PATH_KEY, "oauth2/token",
+ GravitinoAuthProvider.OAUTH_SCOPE_KEY, "scope"))));
+ }
+
+ @Test
+ public void testBuildClientKerberosMissingPrincipal() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ GravitinoAuthProvider.buildClient(
+
buildConfig(ImmutableMap.of(GravitinoAuthProvider.AUTH_TYPE_KEY, "kerberos"))));
+ }
+
+ @Test
+ public void testBuildClientKerberosKeytabNotFound(@TempDir
java.nio.file.Path tempDir) {
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ GravitinoAuthProvider.buildClient(
+ buildConfig(
+ ImmutableMap.of(
+ GravitinoAuthProvider.AUTH_TYPE_KEY, "kerberos",
+ GravitinoAuthProvider.KERBEROS_PRINCIPAL_KEY,
"[email protected]",
+ GravitinoAuthProvider.KERBEROS_KEYTAB_FILE_PATH_KEY,
+ tempDir.resolve("missing.keytab").toString()))));
+ }
+
+ @Test
+ public void testBuildClientKerberosWithKeytab(@TempDir java.nio.file.Path
tempDir)
+ throws IOException {
+ File keytabFile = tempDir.resolve("user.keytab").toFile();
+ Files.write(keytabFile.toPath(), new byte[0]);
+ assertDoesNotThrow(
+ () ->
+ GravitinoAuthProvider.buildClient(
+ buildConfig(
+ ImmutableMap.of(
+ GravitinoAuthProvider.AUTH_TYPE_KEY, "kerberos",
+ GravitinoAuthProvider.KERBEROS_PRINCIPAL_KEY,
"[email protected]",
+ GravitinoAuthProvider.KERBEROS_KEYTAB_FILE_PATH_KEY,
+ keytabFile.getAbsolutePath()))));
+ }
+
+ private GravitinoConfig buildConfig(ImmutableMap<String, String> authConfig)
{
+ ImmutableMap.Builder<String, String> builder =
+ ImmutableMap.<String, String>builder()
+ .put("gravitino.uri", "http://127.0.0.1:8090")
+ .put("gravitino.metalake", "test");
+ builder.putAll(authConfig);
+ return new GravitinoConfig(builder.build());
+ }
+}