This is an automated email from the ASF dual-hosted git repository.
jmclean 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 b434291f00 fix(cli): Fix OAuth/Kerberos authentication detection in
GravitinoConfig (#8187)
b434291f00 is described below
commit b434291f00038b153f43f9015851fafef7f11db6
Author: Mathieu Baurin <[email protected]>
AuthorDate: Thu Aug 21 00:16:46 2025 +0200
fix(cli): Fix OAuth/Kerberos authentication detection in GravitinoConfig
(#8187)
### What changes were proposed in this pull request ?
This PR fixes the OAuth/Kerberos authentication detection logic in
`GravitinoConfig.java`. The changes include:
1. **Fixed authentication type comparisons**: Replace
`authKey.equals("oauth")` with `"oauth".equals(authType)` and similar
for Kerberos
2. **Removed duplicate OAuth initialization**: Eliminated redundant
OAuth configuration block that was overwriting Kerberos settings
3. **Consolidated control flow**: Created clean if-else if structure for
proper authentication type handling
The root issue was that the code was comparing against the constant
`authKey` ("auth") instead of the parsed `authType` value from the
configuration file.
### Why are the changes needed?
The current implementation has critical bugs that prevent OAuth and
Kerberos authentication from working correctly:
1. **Wrong variable comparison**: Code used `authKey` (constant "auth")
instead of `authType` (parsed value like "oauth" or "kerberos")
2. **Configuration overwriting**: Duplicate OAuth logic was overwriting
any Kerberos configuration
3. **Unreachable code paths**: Kerberos authentication could never be
properly initialized
These bugs prevent the CLI from correctly detecting and configuring
authentication when `auth=oauth` or `auth=kerberos` is set in the
configuration file.
Fix: https://github.com/apache/gravitino/issues/8162
### Does this PR introduce _any_ user-facing change?
**No user-facing API changes**, but fixes critical authentication
functionality:
- When `auth=oauth` is configured, `config.getOAuth()` now correctly
returns valid OAuth configuration
- When `auth=kerberos` is configured, `config.getKerberos()` now
correctly returns valid Kerberos configuration
- No changes to configuration file format or property keys
### How was this patch tested?
### How was this patch tested?
1. **Code review**: Verified the logic fixes address all identified
issues
2. **Integration verification**: Confirmed the changes work with
existing `Command.java` usage patterns at lines 202-223
3. **Style compliance**: Ensured code follows Apache Gravitino
conventions using defensive null-safe comparisons
The existing unit tests in `TestGravitinoConfig.java` should pass
without modification
---
.../org/apache/gravitino/cli/GravitinoConfig.java | 13 +-
.../apache/gravitino/cli/TestGravitinoConfig.java | 219 +++++++++++++++++++++
2 files changed, 221 insertions(+), 11 deletions(-)
diff --git
a/clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoConfig.java
b/clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoConfig.java
index 37f52814e0..8b0e843252 100644
--- a/clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoConfig.java
+++ b/clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoConfig.java
@@ -98,25 +98,16 @@ public class GravitinoConfig {
authType = prop.getProperty(authKey);
}
- if (authKey.equals("oauth")) {
+ if ("oauth".equals(authType)) {
oauth =
new OAuthData(
prop.getProperty("serverURI"),
prop.getProperty("credential"),
prop.getProperty("token"),
prop.getProperty("scope"));
- } else if (authKey.equals("kerberos")) {
+ } else if ("kerberos".equals(authType)) {
kerberos = new KerberosData(prop.getProperty("principal"),
prop.getProperty("keytabFile"));
}
-
- if (authKey.equals("oauth")) {
- oauth =
- new OAuthData(
- prop.getProperty("serverURI"),
- prop.getProperty("credential"),
- prop.getProperty("token"),
- prop.getProperty("scope"));
- }
}
/**
diff --git
a/clients/cli/src/test/java/org/apache/gravitino/cli/TestGravitinoConfig.java
b/clients/cli/src/test/java/org/apache/gravitino/cli/TestGravitinoConfig.java
index b2c1ce7d5f..dfc58633f5 100644
---
a/clients/cli/src/test/java/org/apache/gravitino/cli/TestGravitinoConfig.java
+++
b/clients/cli/src/test/java/org/apache/gravitino/cli/TestGravitinoConfig.java
@@ -21,6 +21,7 @@ package org.apache.gravitino.cli;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -45,6 +46,17 @@ public class TestGravitinoConfig {
private static final String URL_VALUE = "http://10.0.0.1:8090";
private static final String IGNORE_KEY = "ignore";
private static final String IGNORE_VALUE = "true";
+ private static final String AUTH_KEY = "auth";
+ private static final String OAUTH_VALUE = "oauth";
+ private static final String KERBEROS_VALUE = "kerberos";
+ // OAuth test values
+ private static final String SERVER_URI_VALUE = "https://oauth.example.com";
+ private static final String CREDENTIAL_VALUE = "test-credential";
+ private static final String TOKEN_VALUE = "test-token";
+ private static final String SCOPE_VALUE = "test-scope";
+ // Kerberos test values
+ private static final String PRINCIPAL_VALUE = "[email protected]";
+ private static final String KEYTAB_VALUE = "/path/to/test.keytab";
@BeforeEach
public void setUp() throws IOException {
@@ -184,4 +196,211 @@ public class TestGravitinoConfig {
assertNull(config.getGravitinoURL(), "URL should be null before reading
the config file");
assertFalse(config.getIgnore(), "Ignore should be null before reading the
config file");
}
+
+ @Test
+ public void oAuthConfiguration() throws IOException {
+ // Create a config file with OAuth settings
+ File tempFileWithOAuth =
+ new File(System.getProperty("java.io.tmpdir") +
"/oauth_config.properties");
+ Properties props = new Properties();
+ props.setProperty(AUTH_KEY, OAUTH_VALUE);
+ props.setProperty("serverURI", SERVER_URI_VALUE);
+ props.setProperty("credential", CREDENTIAL_VALUE);
+ props.setProperty("token", TOKEN_VALUE);
+ props.setProperty("scope", SCOPE_VALUE);
+
+ try (Writer writer =
+ Files.newBufferedWriter(tempFileWithOAuth.toPath(),
StandardCharsets.UTF_8)) {
+ props.store(writer, "OAuth Test Config");
+ }
+
+ GravitinoConfig config = new
GravitinoConfig(tempFileWithOAuth.getAbsolutePath());
+ config.read();
+
+ assertEquals(OAUTH_VALUE, config.getGravitinoAuthType(), "Auth type should
be oauth");
+ assertNotNull(config.getOAuth(), "OAuth configuration should not be null");
+ assertNull(config.getKerberos(), "Kerberos configuration should be null
for OAuth");
+
+ OAuthData oauth = config.getOAuth();
+ assertEquals(SERVER_URI_VALUE, oauth.getServerURI(), "OAuth server URI
should match");
+ assertEquals(CREDENTIAL_VALUE, oauth.getCredential(), "OAuth credential
should match");
+ assertEquals(TOKEN_VALUE, oauth.getToken(), "OAuth token should match");
+ assertEquals(SCOPE_VALUE, oauth.getScope(), "OAuth scope should match");
+
+ tempFileWithOAuth.delete();
+ }
+
+ @Test
+ public void oAuthConfigurationPartialProperties() throws IOException {
+ // Create a config file with only some OAuth properties
+ File tempFilePartialOAuth =
+ new File(System.getProperty("java.io.tmpdir") +
"/partial_oauth_config.properties");
+ Properties props = new Properties();
+ props.setProperty(AUTH_KEY, OAUTH_VALUE);
+ props.setProperty("serverURI", SERVER_URI_VALUE);
+ props.setProperty("credential", CREDENTIAL_VALUE);
+ // Missing token and scope
+
+ try (Writer writer =
+ Files.newBufferedWriter(tempFilePartialOAuth.toPath(),
StandardCharsets.UTF_8)) {
+ props.store(writer, "Partial OAuth Test Config");
+ }
+
+ GravitinoConfig config = new
GravitinoConfig(tempFilePartialOAuth.getAbsolutePath());
+ config.read();
+
+ assertEquals(OAUTH_VALUE, config.getGravitinoAuthType(), "Auth type should
be oauth");
+ assertNotNull(config.getOAuth(), "OAuth configuration should not be null");
+
+ OAuthData oauth = config.getOAuth();
+ assertEquals(SERVER_URI_VALUE, oauth.getServerURI(), "OAuth server URI
should match");
+ assertEquals(CREDENTIAL_VALUE, oauth.getCredential(), "OAuth credential
should match");
+ assertNull(oauth.getToken(), "OAuth token should be null when not
provided");
+ assertNull(oauth.getScope(), "OAuth scope should be null when not
provided");
+
+ tempFilePartialOAuth.delete();
+ }
+
+ @Test
+ public void getOAuthWithoutAuthType() throws IOException {
+ // Create a config file without auth type set to oauth
+ File tempFileWithoutOAuth =
+ new File(System.getProperty("java.io.tmpdir") +
"/no_oauth_config.properties");
+ Properties props = new Properties();
+ props.setProperty(METALAKE_KEY, METALAKE_VALUE);
+ // No auth property set
+
+ try (Writer writer =
+ Files.newBufferedWriter(tempFileWithoutOAuth.toPath(),
StandardCharsets.UTF_8)) {
+ props.store(writer, "No OAuth Test Config");
+ }
+
+ GravitinoConfig config = new
GravitinoConfig(tempFileWithoutOAuth.getAbsolutePath());
+ config.read();
+
+ assertNull(config.getGravitinoAuthType(), "Auth type should be null when
not configured");
+ assertNull(config.getOAuth(), "OAuth configuration should be null when
auth type is not oauth");
+ assertNull(
+ config.getKerberos(),
+ "Kerberos configuration should be null when auth type is not
kerberos");
+
+ tempFileWithoutOAuth.delete();
+ }
+
+ @Test
+ public void kerberosConfiguration() throws IOException {
+ // Create a config file with Kerberos settings
+ File tempFileWithKerberos =
+ new File(System.getProperty("java.io.tmpdir") +
"/kerberos_config.properties");
+ Properties props = new Properties();
+ props.setProperty(AUTH_KEY, KERBEROS_VALUE);
+ props.setProperty("principal", PRINCIPAL_VALUE);
+ props.setProperty("keytabFile", KEYTAB_VALUE);
+
+ try (Writer writer =
+ Files.newBufferedWriter(tempFileWithKerberos.toPath(),
StandardCharsets.UTF_8)) {
+ props.store(writer, "Kerberos Test Config");
+ }
+
+ GravitinoConfig config = new
GravitinoConfig(tempFileWithKerberos.getAbsolutePath());
+ config.read();
+
+ assertEquals(KERBEROS_VALUE, config.getGravitinoAuthType(), "Auth type
should be kerberos");
+ assertNotNull(config.getKerberos(), "Kerberos configuration should not be
null");
+ assertNull(config.getOAuth(), "OAuth configuration should be null for
Kerberos");
+
+ KerberosData kerberos = config.getKerberos();
+ assertEquals(PRINCIPAL_VALUE, kerberos.getPrincipal(), "Kerberos principal
should match");
+ assertEquals(KEYTAB_VALUE, kerberos.getKeytabFile(), "Kerberos keytab file
should match");
+
+ tempFileWithKerberos.delete();
+ }
+
+ @Test
+ public void kerberosConfigurationPartialProperties() throws IOException {
+ // Create a config file with only principal (missing keytab)
+ File tempFilePartialKerberos =
+ new File(System.getProperty("java.io.tmpdir") +
"/partial_kerberos_config.properties");
+ Properties props = new Properties();
+ props.setProperty(AUTH_KEY, KERBEROS_VALUE);
+ props.setProperty("principal", PRINCIPAL_VALUE);
+ // Missing keytabFile
+
+ try (Writer writer =
+ Files.newBufferedWriter(tempFilePartialKerberos.toPath(),
StandardCharsets.UTF_8)) {
+ props.store(writer, "Partial Kerberos Test Config");
+ }
+
+ GravitinoConfig config = new
GravitinoConfig(tempFilePartialKerberos.getAbsolutePath());
+ config.read();
+
+ assertEquals(KERBEROS_VALUE, config.getGravitinoAuthType(), "Auth type
should be kerberos");
+ assertNotNull(config.getKerberos(), "Kerberos configuration should not be
null");
+
+ KerberosData kerberos = config.getKerberos();
+ assertEquals(PRINCIPAL_VALUE, kerberos.getPrincipal(), "Kerberos principal
should match");
+ assertNull(kerberos.getKeytabFile(), "Kerberos keytab should be null when
not provided");
+
+ tempFilePartialKerberos.delete();
+ }
+
+ @Test
+ public void getKerberosWithoutAuthType() throws IOException {
+ // Create a config file with auth type set to something other than kerberos
+ File tempFileWithoutKerberos =
+ new File(System.getProperty("java.io.tmpdir") +
"/no_kerberos_config.properties");
+ Properties props = new Properties();
+ props.setProperty(AUTH_KEY, "simple"); // Different auth type
+ props.setProperty(METALAKE_KEY, METALAKE_VALUE);
+
+ try (Writer writer =
+ Files.newBufferedWriter(tempFileWithoutKerberos.toPath(),
StandardCharsets.UTF_8)) {
+ props.store(writer, "No Kerberos Test Config");
+ }
+
+ GravitinoConfig config = new
GravitinoConfig(tempFileWithoutKerberos.getAbsolutePath());
+ config.read();
+
+ assertEquals("simple", config.getGravitinoAuthType(), "Auth type should be
simple");
+ assertNull(
+ config.getKerberos(),
+ "Kerberos configuration should be null when auth type is not
kerberos");
+ assertNull(config.getOAuth(), "OAuth configuration should be null when
auth type is not oauth");
+
+ tempFileWithoutKerberos.delete();
+ }
+
+ @Test
+ public void getGravitinoAuthType() throws IOException {
+ // Create a config file with auth type
+ File tempFileWithAuthType =
+ new File(System.getProperty("java.io.tmpdir") +
"/auth_type_config.properties");
+ Properties props = new Properties();
+ props.setProperty(AUTH_KEY, OAUTH_VALUE);
+ props.setProperty(METALAKE_KEY, METALAKE_VALUE);
+
+ try (Writer writer =
+ Files.newBufferedWriter(tempFileWithAuthType.toPath(),
StandardCharsets.UTF_8)) {
+ props.store(writer, "Auth Type Test Config");
+ }
+
+ GravitinoConfig config = new
GravitinoConfig(tempFileWithAuthType.getAbsolutePath());
+ config.read();
+
+ assertEquals(
+ OAUTH_VALUE, config.getGravitinoAuthType(), "Auth type should match
configured value");
+
+ tempFileWithAuthType.delete();
+ }
+
+ @Test
+ public void authTypeWithoutConfig() {
+ GravitinoConfig config = new GravitinoConfig(NON_EXISTENT_FILE_PATH);
+ config.read(); // Should handle missing file gracefully
+
+ assertNull(
+ config.getGravitinoAuthType(), "Auth type should be null when config
file doesn't exist");
+ assertNull(config.getOAuth(), "OAuth should be null when config file
doesn't exist");
+ assertNull(config.getKerberos(), "Kerberos should be null when config file
doesn't exist");
+ }
}