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

jinsongzhou pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/amoro.git


The following commit(s) were added to refs/heads/master by this push:
     new c2219ca1e [AMORO-3872] Support to customize basic authentication 
implementation (#3871)
c2219ca1e is described below

commit c2219ca1efaf30764a62f86d29b350e741cceb71
Author: Fei Wang <[email protected]>
AuthorDate: Wed Nov 5 19:53:11 2025 -0800

    [AMORO-3872] Support to customize basic authentication implementation 
(#3871)
    
    * support to customize basic auth"
    
    * save
    
    * docs
    
    * common
    
    * address comments
    
    * nit
    
    ---------
    
    Co-authored-by: Xu Bai <[email protected]>
---
 .../apache/amoro/server/AmoroManagementConf.java   |  9 ++++
 .../DefaultPasswdAuthenticationProvider.java       | 43 ++++++++++++++++
 .../authentication/HttpAuthenticationFactory.java  | 43 ++++++++++++++++
 .../amoro/server/dashboard/DashboardServer.java    | 30 ++++++-----
 .../HttpAuthenticationFactoryTest.java             | 60 ++++++++++++++++++++++
 .../amoro/spi/authentication/BasicPrincipal.java   | 58 +++++++++++++++++++++
 .../PasswdAuthenticationProvider.java              | 37 +++++++++++++
 docs/admin-guides/deployment.md                    |  3 +-
 8 files changed, 270 insertions(+), 13 deletions(-)

diff --git 
a/amoro-ams/src/main/java/org/apache/amoro/server/AmoroManagementConf.java 
b/amoro-ams/src/main/java/org/apache/amoro/server/AmoroManagementConf.java
index ff95d8d0d..58bb886d7 100644
--- a/amoro-ams/src/main/java/org/apache/amoro/server/AmoroManagementConf.java
+++ b/amoro-ams/src/main/java/org/apache/amoro/server/AmoroManagementConf.java
@@ -20,6 +20,7 @@ package org.apache.amoro.server;
 
 import org.apache.amoro.config.ConfigOption;
 import org.apache.amoro.config.ConfigOptions;
+import 
org.apache.amoro.server.authentication.DefaultPasswdAuthenticationProvider;
 import org.apache.amoro.utils.MemorySize;
 
 import java.time.Duration;
@@ -282,6 +283,14 @@ public class AmoroManagementConf {
           .defaultValue(Duration.ofDays(7))
           .withDescription("Timeout for http session.");
 
+  public static final ConfigOption<String> HTTP_SERVER_AUTH_BASIC_PROVIDER =
+      ConfigOptions.key("http-server.auth-basic-provider")
+          .stringType()
+          .defaultValue(DefaultPasswdAuthenticationProvider.class.getName())
+          .withDescription(
+              "User-defined password authentication implementation of"
+                  + " 
org.apache.amoro.spi.authentication.PasswdAuthenticationProvider");
+
   public static final ConfigOption<Integer> OPTIMIZING_COMMIT_THREAD_COUNT =
       ConfigOptions.key("self-optimizing.commit-thread-count")
           .intType()
diff --git 
a/amoro-ams/src/main/java/org/apache/amoro/server/authentication/DefaultPasswdAuthenticationProvider.java
 
b/amoro-ams/src/main/java/org/apache/amoro/server/authentication/DefaultPasswdAuthenticationProvider.java
new file mode 100644
index 000000000..a4b07ab60
--- /dev/null
+++ 
b/amoro-ams/src/main/java/org/apache/amoro/server/authentication/DefaultPasswdAuthenticationProvider.java
@@ -0,0 +1,43 @@
+/*
+ * 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.amoro.server.authentication;
+
+import org.apache.amoro.config.Configurations;
+import org.apache.amoro.exception.SignatureCheckException;
+import org.apache.amoro.server.AmoroManagementConf;
+import org.apache.amoro.spi.authentication.BasicPrincipal;
+import org.apache.amoro.spi.authentication.PasswdAuthenticationProvider;
+
+public class DefaultPasswdAuthenticationProvider implements 
PasswdAuthenticationProvider {
+  private String basicAuthUser;
+  private String basicAuthPassword;
+
+  public DefaultPasswdAuthenticationProvider(Configurations conf) {
+    this.basicAuthUser = conf.get(AmoroManagementConf.ADMIN_USERNAME);
+    this.basicAuthPassword = conf.get(AmoroManagementConf.ADMIN_PASSWORD);
+  }
+
+  @Override
+  public BasicPrincipal authenticate(String user, String password) throws 
SignatureCheckException {
+    if (!(basicAuthUser.equals(user) && basicAuthPassword.equals(password))) {
+      throw new SignatureCheckException("Failed to authenticate via basic 
authentication");
+    }
+    return new BasicPrincipal(user);
+  }
+}
diff --git 
a/amoro-ams/src/main/java/org/apache/amoro/server/authentication/HttpAuthenticationFactory.java
 
b/amoro-ams/src/main/java/org/apache/amoro/server/authentication/HttpAuthenticationFactory.java
new file mode 100644
index 000000000..568272000
--- /dev/null
+++ 
b/amoro-ams/src/main/java/org/apache/amoro/server/authentication/HttpAuthenticationFactory.java
@@ -0,0 +1,43 @@
+/*
+ * 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.amoro.server.authentication;
+
+import org.apache.amoro.config.Configurations;
+import org.apache.amoro.spi.authentication.PasswdAuthenticationProvider;
+import org.apache.amoro.utils.DynConstructors;
+
+public class HttpAuthenticationFactory {
+  public static PasswdAuthenticationProvider getPasswordAuthenticationProvider(
+      String providerClass, Configurations conf) {
+    return createAuthenticationProvider(providerClass, 
PasswdAuthenticationProvider.class, conf);
+  }
+
+  private static <T> T createAuthenticationProvider(
+      String className, Class<T> expected, Configurations conf) {
+    try {
+      return DynConstructors.builder(expected)
+          .impl(className, Configurations.class)
+          .impl(className)
+          .<T>buildChecked()
+          .newInstance(conf);
+    } catch (Exception e) {
+      throw new IllegalStateException(className + " must extend of " + 
expected.getName());
+    }
+  }
+}
diff --git 
a/amoro-ams/src/main/java/org/apache/amoro/server/dashboard/DashboardServer.java
 
b/amoro-ams/src/main/java/org/apache/amoro/server/dashboard/DashboardServer.java
index 616b397a3..7e342159f 100644
--- 
a/amoro-ams/src/main/java/org/apache/amoro/server/dashboard/DashboardServer.java
+++ 
b/amoro-ams/src/main/java/org/apache/amoro/server/dashboard/DashboardServer.java
@@ -36,6 +36,7 @@ import org.apache.amoro.exception.ForbiddenException;
 import org.apache.amoro.exception.SignatureCheckException;
 import org.apache.amoro.server.AmoroManagementConf;
 import org.apache.amoro.server.RestCatalogService;
+import org.apache.amoro.server.authentication.HttpAuthenticationFactory;
 import org.apache.amoro.server.catalog.CatalogManager;
 import org.apache.amoro.server.dashboard.controller.ApiTokenController;
 import org.apache.amoro.server.dashboard.controller.CatalogController;
@@ -54,6 +55,7 @@ import org.apache.amoro.server.resource.OptimizerManager;
 import org.apache.amoro.server.table.TableManager;
 import org.apache.amoro.server.terminal.TerminalManager;
 import org.apache.amoro.shade.guava32.com.google.common.base.Preconditions;
+import org.apache.amoro.spi.authentication.PasswdAuthenticationProvider;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -63,6 +65,7 @@ import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.UncheckedIOException;
 import java.nio.charset.StandardCharsets;
+import java.security.Principal;
 import java.util.Objects;
 import java.util.function.Consumer;
 
@@ -86,9 +89,7 @@ public class DashboardServer {
   private final OverviewController overviewController;
   private final ApiTokenController apiTokenController;
 
-  private final String authType;
-  private final String basicAuthUser;
-  private final String basicAuthPassword;
+  private final PasswdAuthenticationProvider basicAuthProvider;
 
   public DashboardServer(
       Configurations serviceConfig,
@@ -115,9 +116,13 @@ public class DashboardServer {
     APITokenManager apiTokenManager = new APITokenManager();
     this.apiTokenController = new ApiTokenController(apiTokenManager);
 
-    this.authType = 
serviceConfig.get(AmoroManagementConf.HTTP_SERVER_REST_AUTH_TYPE);
-    this.basicAuthUser = serviceConfig.get(AmoroManagementConf.ADMIN_USERNAME);
-    this.basicAuthPassword = 
serviceConfig.get(AmoroManagementConf.ADMIN_PASSWORD);
+    String authType = 
serviceConfig.get(AmoroManagementConf.HTTP_SERVER_REST_AUTH_TYPE);
+    this.basicAuthProvider =
+        AUTH_TYPE_BASIC.equalsIgnoreCase(authType)
+            ? HttpAuthenticationFactory.getPasswordAuthenticationProvider(
+                
serviceConfig.get(AmoroManagementConf.HTTP_SERVER_AUTH_BASIC_PROVIDER),
+                serviceConfig)
+            : null;
   }
 
   private volatile String indexHtml = null;
@@ -394,13 +399,14 @@ public class DashboardServer {
       }
       return;
     }
-    if (AUTH_TYPE_BASIC.equalsIgnoreCase(authType)) {
+    if (null != basicAuthProvider) {
       BasicAuthCredentials cred = ctx.basicAuthCredentials();
-      if (!(basicAuthUser.equals(cred.component1())
-          && basicAuthPassword.equals(cred.component2()))) {
-        throw new SignatureCheckException(
-            "Failed to authenticate via basic authentication for url:" + 
uriPath);
-      }
+      Principal authPrincipal =
+          basicAuthProvider.authenticate(cred.component1(), cred.component2());
+      LOG.info(
+          "Authenticated principal: {}, URI: {}",
+          authPrincipal != null ? authPrincipal.getName() : "null",
+          uriPath);
     } else {
       apiTokenController.checkApiToken(ctx);
     }
diff --git 
a/amoro-ams/src/test/java/org/apache/amoro/server/authentication/HttpAuthenticationFactoryTest.java
 
b/amoro-ams/src/test/java/org/apache/amoro/server/authentication/HttpAuthenticationFactoryTest.java
new file mode 100644
index 000000000..367abb040
--- /dev/null
+++ 
b/amoro-ams/src/test/java/org/apache/amoro/server/authentication/HttpAuthenticationFactoryTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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.amoro.server.authentication;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.apache.amoro.config.Configurations;
+import org.apache.amoro.exception.SignatureCheckException;
+import org.apache.amoro.server.AmoroManagementConf;
+import org.apache.amoro.spi.authentication.PasswdAuthenticationProvider;
+import org.junit.jupiter.api.Test;
+
+public class HttpAuthenticationFactoryTest {
+  @Test
+  public void testPasswordAuthenticationProvider() {
+    Configurations conf = new Configurations();
+    conf.set(AmoroManagementConf.ADMIN_USERNAME, "admin");
+    conf.set(AmoroManagementConf.ADMIN_PASSWORD, "password");
+
+    assertThrows(
+        IllegalStateException.class,
+        () -> {
+          HttpAuthenticationFactory.getPasswordAuthenticationProvider(
+              "NonExistentProviderClass", conf);
+        });
+
+    PasswdAuthenticationProvider passwdAuthenticationProvider =
+        HttpAuthenticationFactory.getPasswordAuthenticationProvider(
+            DefaultPasswdAuthenticationProvider.class.getName(), conf);
+
+    assert passwdAuthenticationProvider.authenticate("admin", 
"password").getName().equals("admin");
+
+    assertThrows(
+        SignatureCheckException.class,
+        () -> {
+          passwdAuthenticationProvider.authenticate("admin", 
"invalidPassword");
+        });
+    assertThrows(
+        SignatureCheckException.class,
+        () -> {
+          passwdAuthenticationProvider.authenticate("nonAdmin", "password");
+        });
+  }
+}
diff --git 
a/amoro-common/src/main/java/org/apache/amoro/spi/authentication/BasicPrincipal.java
 
b/amoro-common/src/main/java/org/apache/amoro/spi/authentication/BasicPrincipal.java
new file mode 100644
index 000000000..794682020
--- /dev/null
+++ 
b/amoro-common/src/main/java/org/apache/amoro/spi/authentication/BasicPrincipal.java
@@ -0,0 +1,58 @@
+/*
+ * 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.amoro.spi.authentication;
+
+import java.security.Principal;
+import java.util.Objects;
+
+public class BasicPrincipal implements Principal {
+  private final String name;
+
+  public BasicPrincipal(String name) {
+    this.name = name;
+    Objects.requireNonNull(name, "Principal name cannot be null");
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+
+  @Override
+  public String toString() {
+    return name;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    BasicPrincipal that = (BasicPrincipal) o;
+    return name.equals(that.name);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hashCode(name);
+  }
+}
diff --git 
a/amoro-common/src/main/java/org/apache/amoro/spi/authentication/PasswdAuthenticationProvider.java
 
b/amoro-common/src/main/java/org/apache/amoro/spi/authentication/PasswdAuthenticationProvider.java
new file mode 100644
index 000000000..3352ab17e
--- /dev/null
+++ 
b/amoro-common/src/main/java/org/apache/amoro/spi/authentication/PasswdAuthenticationProvider.java
@@ -0,0 +1,37 @@
+/*
+ * 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.amoro.spi.authentication;
+
+import org.apache.amoro.exception.SignatureCheckException;
+
+import java.security.Principal;
+
+public interface PasswdAuthenticationProvider {
+  /**
+   * The authenticate method is called by the amoro Server authentication 
layer to authenticate
+   * users for their requests. If a user is to be granted, return 
nothing/throw nothing. When a user
+   * is to be disallowed, throw an appropriate [[SignatureCheckException]].
+   *
+   * @param user The username received over the connection request
+   * @param password The password received over the connection request
+   * @return The identifier associated with the credential
+   * @throws SignatureCheckException When a user is found to be invalid by the 
implementation
+   */
+  Principal authenticate(String user, String password) throws 
SignatureCheckException;
+}
diff --git a/docs/admin-guides/deployment.md b/docs/admin-guides/deployment.md
index bbdd68123..23374ec2f 100644
--- a/docs/admin-guides/deployment.md
+++ b/docs/admin-guides/deployment.md
@@ -75,7 +75,8 @@ If you want to use AMS in a production environment, it is 
recommended to modify
 - The `ams.thrift-server.table-service.bind-port` configuration specifies the 
binding port of the Thrift Server that provides the table service. The compute 
engines access AMS through this port, and the default value is 1260.
 - The `ams.thrift-server.optimizing-service.bind-port` configuration specifies 
the binding port of the Thrift Server that provides the optimizing service. The 
optimizers access AMS through this port, and the default value is 1261.
 - The `ams.http-server.bind-port` configuration specifies the port to which 
the HTTP service is bound. The Dashboard and Open API are bound to this port, 
and the default value is 1630.
-- The `ams.http-server.rest-auth-type` configuration specifies the REST API 
auth type, which could be token(default) or basic. The basic auth would reuse 
`ams.admin-username` and `ams.admin-password` for authentication. 
+- The `ams.http-server.rest-auth-type` configuration specifies the REST API 
auth type, which could be token(default) or basic.
+- The `ams.http-server.auth-basic-provider` configuration specifies the REST 
API basic authentication provider. By default, it uses `ams.admin-username` and 
`ams.admin-password` for authentication. You can also specify a custom 
implementation by providing the fully qualified class name of a class that 
implements the 
`org.apache.amoro.spi.authentication.PasswdAuthenticationProvider` interface.
 
 ```yaml
 ams:

Reply via email to