This is an automated email from the ASF dual-hosted git repository.

shaofengshi 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 6d490f511 [#5384] Added OAuth support to Gravitino CLI (#5618)
6d490f511 is described below

commit 6d490f511523814135951176e71b1cb083ea6de6
Author: Justin Mclean <[email protected]>
AuthorDate: Thu Dec 5 18:40:06 2024 +1100

    [#5384] Added OAuth support to Gravitino CLI (#5618)
    
    ### What changes were proposed in this pull request?
    
    Added OAuth support to Gravitino CLI, Note this is supported by the
    configuration files.
    
    ### Why are the changes needed?
    
    To support OAuth in the Gravitino CLI.
    
    Fix: #5384
    
    ### Does this PR introduce _any_ user-facing change?
    
    No.
    
    ### How was this patch tested?
    
    Tested locally.
---
 .../apache/gravitino/cli/GravitinoCommandLine.java | 25 ++++---
 .../org/apache/gravitino/cli/GravitinoConfig.java  | 19 ++++++
 .../java/org/apache/gravitino/cli/OAuthData.java   | 79 ++++++++++++++++++++++
 .../org/apache/gravitino/cli/commands/Command.java | 72 +++++++++++++-------
 docs/cli.md                                        | 11 +++
 5 files changed, 167 insertions(+), 39 deletions(-)

diff --git 
a/clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoCommandLine.java 
b/clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoCommandLine.java
index 1cfcb97f6..bfdd49507 100644
--- 
a/clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoCommandLine.java
+++ 
b/clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoCommandLine.java
@@ -160,7 +160,7 @@ public class GravitinoCommandLine extends 
TestableCommandLine {
     String metalake = name.getMetalakeName();
     String outputFormat = line.getOptionValue(GravitinoOptions.OUTPUT);
 
-    Command.setAuthenicationMode(auth, userName);
+    Command.setAuthenticationMode(auth, userName);
 
     if (CommandActions.DETAILS.equals(command)) {
       if (line.hasOption(GravitinoOptions.AUDIT)) {
@@ -209,7 +209,7 @@ public class GravitinoCommandLine extends 
TestableCommandLine {
     String metalake = name.getMetalakeName();
     String outputFormat = line.getOptionValue(GravitinoOptions.OUTPUT);
 
-    Command.setAuthenicationMode(auth, userName);
+    Command.setAuthenticationMode(auth, userName);
 
     if (CommandActions.LIST.equals(command)) {
       newListCatalogs(url, ignore, metalake).handle();
@@ -265,7 +265,7 @@ public class GravitinoCommandLine extends 
TestableCommandLine {
     String metalake = name.getMetalakeName();
     String catalog = name.getCatalogName();
 
-    Command.setAuthenicationMode(auth, userName);
+    Command.setAuthenticationMode(auth, userName);
 
     if (CommandActions.LIST.equals(command)) {
       newListSchema(url, ignore, metalake, catalog).handle();
@@ -310,7 +310,7 @@ public class GravitinoCommandLine extends 
TestableCommandLine {
     String catalog = name.getCatalogName();
     String schema = name.getSchemaName();
 
-    Command.setAuthenicationMode(auth, userName);
+    Command.setAuthenticationMode(auth, userName);
 
     if (CommandActions.LIST.equals(command)) {
       newListTables(url, ignore, metalake, catalog, schema).handle();
@@ -370,7 +370,7 @@ public class GravitinoCommandLine extends 
TestableCommandLine {
     String metalake = name.getMetalakeName();
     String user = line.getOptionValue(GravitinoOptions.USER);
 
-    Command.setAuthenicationMode(auth, userName);
+    Command.setAuthenticationMode(auth, userName);
 
     if (CommandActions.DETAILS.equals(command)) {
       newUserDetails(url, ignore, metalake, user).handle();
@@ -405,7 +405,7 @@ public class GravitinoCommandLine extends 
TestableCommandLine {
     String metalake = name.getMetalakeName();
     String group = line.getOptionValue(GravitinoOptions.GROUP);
 
-    Command.setAuthenicationMode(auth, userName);
+    Command.setAuthenticationMode(auth, userName);
 
     if (CommandActions.DETAILS.equals(command)) {
       newGroupDetails(url, ignore, metalake, group).handle();
@@ -439,7 +439,7 @@ public class GravitinoCommandLine extends 
TestableCommandLine {
     FullName name = new FullName(line);
     String metalake = name.getMetalakeName();
 
-    Command.setAuthenicationMode(auth, userName);
+    Command.setAuthenticationMode(auth, userName);
 
     String[] tags = line.getOptionValues(GravitinoOptions.TAG);
     if (tags != null) {
@@ -502,7 +502,7 @@ public class GravitinoCommandLine extends 
TestableCommandLine {
     String metalake = name.getMetalakeName();
     String role = line.getOptionValue(GravitinoOptions.ROLE);
 
-    Command.setAuthenicationMode(auth, userName);
+    Command.setAuthenticationMode(auth, userName);
 
     if (CommandActions.DETAILS.equals(command)) {
       newRoleDetails(url, ignore, metalake, role).handle();
@@ -530,7 +530,7 @@ public class GravitinoCommandLine extends 
TestableCommandLine {
     String table = name.getTableName();
     String column = name.getColumnName();
 
-    Command.setAuthenicationMode(auth, userName);
+    Command.setAuthenticationMode(auth, userName);
 
     if (CommandActions.LIST.equals(command)) {
       newListColumns(url, ignore, metalake, catalog, schema, table).handle();
@@ -644,7 +644,7 @@ public class GravitinoCommandLine extends 
TestableCommandLine {
     String metalake = name.getMetalakeName();
     String entityName = line.getOptionValue(GravitinoOptions.NAME);
 
-    Command.setAuthenicationMode(auth, userName);
+    Command.setAuthenticationMode(auth, userName);
 
     if (CommandActions.DETAILS.equals(command)) {
       newOwnerDetails(url, ignore, metalake, entityName, entity).handle();
@@ -677,7 +677,7 @@ public class GravitinoCommandLine extends 
TestableCommandLine {
     String schema = name.getSchemaName();
     String topic = name.getTopicName();
 
-    Command.setAuthenicationMode(auth, userName);
+    Command.setAuthenticationMode(auth, userName);
 
     if (CommandActions.LIST.equals(command)) {
       newListTopics(url, ignore, metalake, catalog, schema).handle();
@@ -719,7 +719,7 @@ public class GravitinoCommandLine extends 
TestableCommandLine {
     String schema = name.getSchemaName();
     String fileset = name.getFilesetName();
 
-    Command.setAuthenicationMode(auth, userName);
+    Command.setAuthenticationMode(auth, userName);
 
     if (CommandActions.DETAILS.equals(command)) {
       newFilesetDetails(url, ignore, metalake, catalog, schema, 
fileset).handle();
@@ -801,7 +801,6 @@ public class GravitinoCommandLine extends 
TestableCommandLine {
    * @return The Gravitinno authentication, or null if not found.
    */
   public String getAuth() {
-
     // If specified on the command line use that
     if (line.hasOption(GravitinoOptions.SIMPLE)) {
       return GravitinoOptions.SIMPLE;
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 375073d2c..148bfaeb6 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
@@ -37,6 +37,7 @@ public class GravitinoConfig {
   private String url;
   private boolean ignore;
   private String authentication;
+  private OAuthData oauth;
 
   /**
    * Creates a GravitinoConfig object with a specified config file. If no file 
is provided, it
@@ -94,6 +95,15 @@ public class GravitinoConfig {
     if (prop.containsKey(authKey)) {
       authentication = prop.getProperty(authKey);
     }
+
+    if (authKey.equals("oauth")) {
+      oauth =
+          new OAuthData(
+              prop.getProperty("serverURI"),
+              prop.getProperty("credential"),
+              prop.getProperty("token"),
+              prop.getProperty("scope"));
+    }
   }
 
   /**
@@ -140,4 +150,13 @@ public class GravitinoConfig {
   public String getGravitinoAuth() {
     return authentication;
   }
+
+  /**
+   * Retrieves the Gravitino oAuth authentication configuration.
+   *
+   * @return The Gravitino authentication or null if not set.
+   */
+  public OAuthData getOAuth() {
+    return oauth;
+  }
 }
diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/OAuthData.java 
b/clients/cli/src/main/java/org/apache/gravitino/cli/OAuthData.java
new file mode 100644
index 000000000..209bd763e
--- /dev/null
+++ b/clients/cli/src/main/java/org/apache/gravitino/cli/OAuthData.java
@@ -0,0 +1,79 @@
+/*
+ * 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.cli;
+
+public class OAuthData {
+  protected final String serverURI;
+  protected final String credential;
+  protected final String token;
+  protected final String scope;
+
+  /**
+   * Constructs an {@code OAuthData} instance with the specified server URI, 
credential, token, and
+   * scope.
+   *
+   * @param serverURI the URI of the OAuth server
+   * @param credential the credential used for authentication
+   * @param token the access token obtained after authentication
+   * @param scope the scope of access granted by the OAuth token
+   */
+  public OAuthData(String serverURI, String credential, String token, String 
scope) {
+    this.serverURI = serverURI;
+    this.credential = credential;
+    this.token = token;
+    this.scope = scope;
+  }
+
+  /**
+   * Returns the URI of the OAuth server.
+   *
+   * @return the server URI
+   */
+  public String getServerURI() {
+    return serverURI;
+  }
+
+  /**
+   * Returns the credential used for authentication.
+   *
+   * @return the credential
+   */
+  public String getCredential() {
+    return credential;
+  }
+
+  /**
+   * Returns the access token obtained after authentication.
+   *
+   * @return the access token
+   */
+  public String getToken() {
+    return token;
+  }
+
+  /**
+   * Returns the scope of access granted by the OAuth token.
+   *
+   * @return the scope
+   */
+  public String getScope() {
+    return scope;
+  }
+}
diff --git 
a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/Command.java 
b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/Command.java
index 2bedc37a0..cffc833c7 100644
--- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/Command.java
+++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/Command.java
@@ -21,23 +21,31 @@ package org.apache.gravitino.cli.commands;
 
 import static org.apache.gravitino.client.GravitinoClientBase.Builder;
 
+import org.apache.gravitino.cli.GravitinoConfig;
+import org.apache.gravitino.cli.OAuthData;
 import org.apache.gravitino.cli.outputs.PlainFormat;
 import org.apache.gravitino.cli.outputs.TableFormat;
+import org.apache.gravitino.client.DefaultOAuth2TokenProvider;
 import org.apache.gravitino.client.GravitinoAdminClient;
 import org.apache.gravitino.client.GravitinoClient;
+import org.apache.gravitino.client.GravitinoClientBase;
 import org.apache.gravitino.exceptions.NoSuchMetalakeException;
 
 /* The base for all commands. */
 public abstract class Command {
-  private final String url;
-  private final boolean ignoreVersions;
-  private final String outputFormat;
-  public static String OUTPUT_FORMAT_TABLE = "table";
-  public static String OUTPUT_FORMAT_PLAIN = "plain";
+  public static final String OUTPUT_FORMAT_TABLE = "table";
+  public static final String OUTPUT_FORMAT_PLAIN = "plain";
 
   protected static String authentication = null;
   protected static String userName = null;
 
+  private static final String SIMPLE_AUTH = "simple";
+  private static final String OAUTH_AUTH = "oauth";
+
+  private final String url;
+  private final boolean ignoreVersions;
+  private final String outputFormat;
+
   /**
    * Command constructor.
    *
@@ -69,14 +77,13 @@ public abstract class Command {
    * @param authentication the authentication mode to be used (e.g. "simple")
    * @param userName the username associated with the authentication mode
    */
-  public static void setAuthenicationMode(String authentication, String 
userName) {
+  public static void setAuthenticationMode(String authentication, String 
userName) {
     Command.authentication = authentication;
     Command.userName = userName;
   }
 
   /** All commands have a handle method to handle and run the required 
command. */
   public abstract void handle();
-
   /**
    * Builds a {@link GravitinoClient} instance with the provided server URL 
and metalake.
    *
@@ -87,20 +94,7 @@ public abstract class Command {
   protected GravitinoClient buildClient(String metalake) throws 
NoSuchMetalakeException {
     Builder<GravitinoClient> client = 
GravitinoClient.builder(url).withMetalake(metalake);
 
-    if (ignoreVersions) {
-      client = client.withVersionCheckDisabled();
-    }
-    if (authentication != null) {
-      if (authentication.equals("simple")) {
-        if (userName != null && !userName.isEmpty()) {
-          client = client.withSimpleAuth(userName);
-        } else {
-          client = client.withSimpleAuth();
-        }
-      }
-    }
-
-    return client.build();
+    return constructClient(client).build();
   }
 
   /**
@@ -111,20 +105,46 @@ public abstract class Command {
   protected GravitinoAdminClient buildAdminClient() {
     Builder<GravitinoAdminClient> client = GravitinoAdminClient.builder(url);
 
+    return constructClient(client).build();
+  }
+
+  /**
+   * Configures and constructs a {@link Builder} instance for creating a 
{@link GravitinoClient} or
+   * {@link GravitinoAdminClient}.
+   *
+   * @param builder The {@link Builder} instance to be configured.
+   * @param <T> The type of the {@link GravitinoClientBase}.
+   * @return A configured {@link Builder} instance.
+   */
+  protected <T extends GravitinoClientBase> Builder<T> 
constructClient(Builder<T> builder) {
     if (ignoreVersions) {
-      client = client.withVersionCheckDisabled();
+      builder = builder.withVersionCheckDisabled();
     }
     if (authentication != null) {
-      if (authentication.equals("simple")) {
+      if (authentication.equals(SIMPLE_AUTH)) {
         if (userName != null && !userName.isEmpty()) {
-          client = client.withSimpleAuth(userName);
+          builder = builder.withSimpleAuth(userName);
         } else {
-          client = client.withSimpleAuth();
+          builder = builder.withSimpleAuth();
         }
+      } else if (authentication.equals(OAUTH_AUTH)) {
+        GravitinoConfig config = new GravitinoConfig(null);
+        OAuthData oauth = config.getOAuth();
+        DefaultOAuth2TokenProvider tokenProvider =
+            DefaultOAuth2TokenProvider.builder()
+                .withUri(oauth.getServerURI())
+                .withCredential(oauth.getCredential())
+                .withPath(oauth.getToken())
+                .withScope(oauth.getScope())
+                .build();
+
+        builder = builder.withOAuth(tokenProvider);
+      } else {
+        System.err.println("Unsupported authentication type " + 
authentication);
       }
     }
 
-    return client.build();
+    return builder;
   }
 
   /**
diff --git a/docs/cli.md b/docs/cli.md
index c8702ce11..6c273953e 100644
--- a/docs/cli.md
+++ b/docs/cli.md
@@ -130,6 +130,17 @@ auth=simple
 
 ```
 
+OAuth authentication can also be configured via the configuration file.
+
+```text
+# Authentication
+auth=oauth
+serverURI=http://127.0.0.1:1082
+credential=xx:xx
+token=test
+scope=token/test
+```
+
 ### Potentially unsafe operations
 
 For operations that delete data or rename a metalake the user with be prompted 
to make sure they wish to run this command. The `--force` option can be 
specified to override this behaviour.

Reply via email to