This is an automated email from the ASF dual-hosted git repository.
xuba 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 c8b998c1e [AMORO-3873] Support Bearer/JWT authentication (#3905)
c8b998c1e is described below
commit c8b998c1e3288cf275a72f0c2c79501bcc2724f8
Author: Fei Wang <[email protected]>
AuthorDate: Sun Nov 16 21:35:49 2025 -0800
[AMORO-3873] Support Bearer/JWT authentication (#3905)
* save
* save
* save
* save
* Save
* asve
* rename package
* comments
* basic
* remove token from log
---
.../apache/amoro/server/AmoroManagementConf.java | 24 ++++++++-
.../DefaultPasswdAuthenticationProvider.java | 12 +++--
.../authentication/DefaultPasswordCredential.java | 46 ++++++++--------
.../authentication/DefaultTokenCredential.java | 42 +++++----------
.../authentication/HttpAuthenticationFactory.java | 62 +++++++++++++++++++++-
.../amoro/server/dashboard/DashboardServer.java | 30 ++++++++---
.../HttpAuthenticationFactoryTest.java | 47 ++++++++++++++--
...serDefinedTokenAuthenticationProviderImpl.java} | 32 +++++------
.../{spi => }/authentication/BasicPrincipal.java | 2 +-
.../PasswdAuthenticationProvider.java | 11 ++--
.../PasswordCredential.java} | 41 +++-----------
.../TokenAuthenticationProvider.java} | 20 +++----
.../TokenCredential.java} | 41 +++-----------
docs/admin-guides/deployment.md | 6 ++-
14 files changed, 242 insertions(+), 174 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 58bb886d7..750907e21 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
@@ -275,7 +275,8 @@ public class AmoroManagementConf {
ConfigOptions.key("http-server.rest-auth-type")
.stringType()
.defaultValue("token")
- .withDescription("The authentication used by REST APIs, token
(default) or basic.");
+ .withDescription(
+ "The authentication used by REST APIs, token (default), basic or
bearer.");
public static final ConfigOption<Duration> HTTP_SERVER_SESSION_TIMEOUT =
ConfigOptions.key("http-server.session-timeout")
@@ -289,7 +290,26 @@ public class AmoroManagementConf {
.defaultValue(DefaultPasswdAuthenticationProvider.class.getName())
.withDescription(
"User-defined password authentication implementation of"
- + "
org.apache.amoro.spi.authentication.PasswdAuthenticationProvider");
+ + "
org.apache.amoro.authentication.PasswdAuthenticationProvider");
+
+ public static final ConfigOption<String> HTTP_SERVER_AUTH_BEARER_PROVIDER =
+ ConfigOptions.key("http-server.auth-bearer-provider")
+ .stringType()
+ .noDefaultValue()
+ .withDescription(
+ "User-defined Bearer token such as JWT (JSON Web Token)
authentication implementation"
+ + " of
org.apache.amoro.authentication.TokenAuthenticationProvider");
+
+ public static final ConfigOption<String> HTTP_SERVER_PROXY_CLIENT_IP_HEADER =
+ ConfigOptions.key("http-server.proxy-client-ip-header")
+ .stringType()
+ .defaultValue("X-Real-IP")
+ .withDescription(
+ "The HTTP header to record the real client IP address. If your
server is behind a load"
+ + " balancer or other proxy, the server will see this load
balancer or proxy IP address as"
+ + " the client IP address, to get around this common issue,
most load balancers or proxies"
+ + " offer the ability to record the real remote IP address
in an HTTP header that will be"
+ + " added to the request for other devices to use.");
public static final ConfigOption<Integer> OPTIMIZING_COMMIT_THREAD_COUNT =
ConfigOptions.key("self-optimizing.commit-thread-count")
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
index a4b07ab60..2a74a4ea7 100644
---
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
@@ -18,11 +18,12 @@
package org.apache.amoro.server.authentication;
+import org.apache.amoro.authentication.BasicPrincipal;
+import org.apache.amoro.authentication.PasswdAuthenticationProvider;
+import org.apache.amoro.authentication.PasswordCredential;
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;
@@ -34,10 +35,11 @@ public class DefaultPasswdAuthenticationProvider implements
PasswdAuthentication
}
@Override
- public BasicPrincipal authenticate(String user, String password) throws
SignatureCheckException {
- if (!(basicAuthUser.equals(user) && basicAuthPassword.equals(password))) {
+ public BasicPrincipal authenticate(PasswordCredential credential) throws
SignatureCheckException {
+ if (!(basicAuthUser.equals(credential.username())
+ && basicAuthPassword.equals(credential.password()))) {
throw new SignatureCheckException("Failed to authenticate via basic
authentication");
}
- return new BasicPrincipal(user);
+ return new BasicPrincipal(credential.username());
}
}
diff --git
a/amoro-common/src/main/java/org/apache/amoro/spi/authentication/BasicPrincipal.java
b/amoro-ams/src/main/java/org/apache/amoro/server/authentication/DefaultPasswordCredential.java
similarity index 50%
copy from
amoro-common/src/main/java/org/apache/amoro/spi/authentication/BasicPrincipal.java
copy to
amoro-ams/src/main/java/org/apache/amoro/server/authentication/DefaultPasswordCredential.java
index 794682020..4304e51b6 100644
---
a/amoro-common/src/main/java/org/apache/amoro/spi/authentication/BasicPrincipal.java
+++
b/amoro-ams/src/main/java/org/apache/amoro/server/authentication/DefaultPasswordCredential.java
@@ -16,43 +16,41 @@
* limitations under the License.
*/
-package org.apache.amoro.spi.authentication;
+package org.apache.amoro.server.authentication;
-import java.security.Principal;
-import java.util.Objects;
+import org.apache.amoro.authentication.PasswordCredential;
-public class BasicPrincipal implements Principal {
- private final String name;
+import java.util.Collections;
+import java.util.Map;
- public BasicPrincipal(String name) {
- this.name = name;
- Objects.requireNonNull(name, "Principal name cannot be null");
+public class DefaultPasswordCredential implements PasswordCredential {
+ private String username;
+ private String password;
+ private Map<String, String> extraInfo;
+
+ public DefaultPasswordCredential(
+ String username, String password, Map<String, String> extraInfo) {
+ this.username = username;
+ this.password = password;
+ this.extraInfo = extraInfo;
}
- @Override
- public String getName() {
- return name;
+ public DefaultPasswordCredential(String username, String password) {
+ this(username, password, Collections.emptyMap());
}
@Override
- public String toString() {
- return name;
+ public String username() {
+ return username;
}
@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);
+ public String password() {
+ return password;
}
@Override
- public int hashCode() {
- return Objects.hashCode(name);
+ public Map<String, String> extraInfo() {
+ return null == extraInfo ? Collections.emptyMap() : extraInfo;
}
}
diff --git
a/amoro-common/src/main/java/org/apache/amoro/spi/authentication/BasicPrincipal.java
b/amoro-ams/src/main/java/org/apache/amoro/server/authentication/DefaultTokenCredential.java
similarity index 53%
copy from
amoro-common/src/main/java/org/apache/amoro/spi/authentication/BasicPrincipal.java
copy to
amoro-ams/src/main/java/org/apache/amoro/server/authentication/DefaultTokenCredential.java
index 794682020..61f17d9ce 100644
---
a/amoro-common/src/main/java/org/apache/amoro/spi/authentication/BasicPrincipal.java
+++
b/amoro-ams/src/main/java/org/apache/amoro/server/authentication/DefaultTokenCredential.java
@@ -16,43 +16,29 @@
* limitations under the License.
*/
-package org.apache.amoro.spi.authentication;
+package org.apache.amoro.server.authentication;
-import java.security.Principal;
-import java.util.Objects;
+import org.apache.amoro.authentication.TokenCredential;
-public class BasicPrincipal implements Principal {
- private final String name;
+import java.util.Collections;
+import java.util.Map;
- public BasicPrincipal(String name) {
- this.name = name;
- Objects.requireNonNull(name, "Principal name cannot be null");
- }
+public class DefaultTokenCredential implements TokenCredential {
+ private String token;
+ private Map<String, String> extraInfo;
- @Override
- public String getName() {
- return name;
- }
-
- @Override
- public String toString() {
- return name;
+ public DefaultTokenCredential(String token, Map<String, String> extraInfo) {
+ this.token = token;
+ this.extraInfo = extraInfo;
}
@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);
+ public String token() {
+ return token;
}
@Override
- public int hashCode() {
- return Objects.hashCode(name);
+ public Map<String, String> extraInfo() {
+ return null == extraInfo ? Collections.emptyMap() : extraInfo;
}
}
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
index 568272000..2699741c5 100644
---
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
@@ -18,16 +18,36 @@
package org.apache.amoro.server.authentication;
+import static org.apache.amoro.authentication.TokenCredential.CLIENT_IP_KEY;
+
+import io.javalin.core.security.BasicAuthCredentials;
+import io.javalin.core.util.Header;
+import io.javalin.http.Context;
+import org.apache.amoro.authentication.PasswdAuthenticationProvider;
+import org.apache.amoro.authentication.PasswordCredential;
+import org.apache.amoro.authentication.TokenAuthenticationProvider;
+import org.apache.amoro.authentication.TokenCredential;
import org.apache.amoro.config.Configurations;
-import org.apache.amoro.spi.authentication.PasswdAuthenticationProvider;
+import org.apache.amoro.shade.guava32.com.google.common.base.Preconditions;
import org.apache.amoro.utils.DynConstructors;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Optional;
+
public class HttpAuthenticationFactory {
+ public static final String BEARER_TOKEN_SCHEMA = "BEARER";
+
public static PasswdAuthenticationProvider getPasswordAuthenticationProvider(
String providerClass, Configurations conf) {
return createAuthenticationProvider(providerClass,
PasswdAuthenticationProvider.class, conf);
}
+ public static TokenAuthenticationProvider getBearerAuthenticationProvider(
+ String providerClass, Configurations conf) {
+ return createAuthenticationProvider(providerClass,
TokenAuthenticationProvider.class, conf);
+ }
+
private static <T> T createAuthenticationProvider(
String className, Class<T> expected, Configurations conf) {
try {
@@ -40,4 +60,44 @@ public class HttpAuthenticationFactory {
throw new IllegalStateException(className + " must extend of " +
expected.getName());
}
}
+
+ public static PasswordCredential getPasswordCredential(
+ Context context, String proxyClientIpHeader) {
+ BasicAuthCredentials cred = context.basicAuthCredentials();
+ Preconditions.checkNotNull(cred, "BasicAuthCredentials must not be null");
+ return new DefaultPasswordCredential(
+ cred.getUsername(),
+ cred.getPassword(),
+ getCredentialExtraInfo(context, proxyClientIpHeader));
+ }
+
+ public static TokenCredential getBearerTokenCredential(
+ Context context, String proxyClientIpHeader) {
+ String bearerToken = getBearerToken(context);
+ Preconditions.checkNotNull(bearerToken, "Bearer token must not be null");
+ return new DefaultTokenCredential(
+ bearerToken, getCredentialExtraInfo(context, proxyClientIpHeader));
+ }
+
+ /**
+ * Extracts the Bearer token from the HTTP Authorization header in the
request context. Returns
+ * the token string if present and valid, otherwise returns null.
+ */
+ private static String getBearerToken(Context context) {
+ String authorization = context.header(Header.AUTHORIZATION);
+ if (authorization != null) {
+ String[] parts = authorization.trim().split("\\s+", 2);
+ if (parts.length == 2 && BEARER_TOKEN_SCHEMA.equalsIgnoreCase(parts[0]))
{
+ return parts[1].trim();
+ }
+ }
+ return null;
+ }
+
+ private static Map<String, String> getCredentialExtraInfo(
+ Context context, String proxyClientIpHeader) {
+ return Collections.singletonMap(
+ CLIENT_IP_KEY,
+
Optional.ofNullable(context.header(proxyClientIpHeader)).orElse(context.ip()));
+ }
}
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 7e342159f..02395543b 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
@@ -25,12 +25,13 @@ import static io.javalin.apibuilder.ApiBuilder.post;
import static io.javalin.apibuilder.ApiBuilder.put;
import io.javalin.apibuilder.EndpointGroup;
-import io.javalin.core.security.BasicAuthCredentials;
import io.javalin.http.ContentType;
import io.javalin.http.Context;
import io.javalin.http.HttpCode;
import io.javalin.http.staticfiles.Location;
import io.javalin.http.staticfiles.StaticFileConfig;
+import org.apache.amoro.authentication.PasswdAuthenticationProvider;
+import org.apache.amoro.authentication.TokenAuthenticationProvider;
import org.apache.amoro.config.Configurations;
import org.apache.amoro.exception.ForbiddenException;
import org.apache.amoro.exception.SignatureCheckException;
@@ -55,7 +56,6 @@ 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;
@@ -74,6 +74,7 @@ public class DashboardServer {
public static final Logger LOG =
LoggerFactory.getLogger(DashboardServer.class);
private static final String AUTH_TYPE_BASIC = "basic";
+ private static final String AUTH_TYPE_BEARER = "bearer";
private static final String X_REQUEST_SOURCE_HEADER = "X-Request-Source";
private static final String X_REQUEST_SOURCE_WEB = "Web";
private final CatalogController catalogController;
@@ -90,6 +91,8 @@ public class DashboardServer {
private final ApiTokenController apiTokenController;
private final PasswdAuthenticationProvider basicAuthProvider;
+ private final TokenAuthenticationProvider bearerAuthProvider;
+ private final String proxyClientIpHeader;
public DashboardServer(
Configurations serviceConfig,
@@ -123,6 +126,14 @@ public class DashboardServer {
serviceConfig.get(AmoroManagementConf.HTTP_SERVER_AUTH_BASIC_PROVIDER),
serviceConfig)
: null;
+ this.bearerAuthProvider =
+ AUTH_TYPE_BEARER.equalsIgnoreCase(authType)
+ ? HttpAuthenticationFactory.getBearerAuthenticationProvider(
+
serviceConfig.get(AmoroManagementConf.HTTP_SERVER_AUTH_BEARER_PROVIDER),
+ serviceConfig)
+ : null;
+ this.proxyClientIpHeader =
+
serviceConfig.get(AmoroManagementConf.HTTP_SERVER_PROXY_CLIENT_IP_HEADER);
}
private volatile String indexHtml = null;
@@ -399,10 +410,17 @@ public class DashboardServer {
}
return;
}
- if (null != basicAuthProvider) {
- BasicAuthCredentials cred = ctx.basicAuthCredentials();
- Principal authPrincipal =
- basicAuthProvider.authenticate(cred.component1(), cred.component2());
+ if (null != basicAuthProvider || null != bearerAuthProvider) {
+ Principal authPrincipal;
+ if (null != basicAuthProvider) {
+ authPrincipal =
+ basicAuthProvider.authenticate(
+ HttpAuthenticationFactory.getPasswordCredential(ctx,
proxyClientIpHeader));
+ } else {
+ authPrincipal =
+ bearerAuthProvider.authenticate(
+ HttpAuthenticationFactory.getBearerTokenCredential(ctx,
proxyClientIpHeader));
+ }
LOG.info(
"Authenticated principal: {}, URI: {}",
authPrincipal != null ? authPrincipal.getName() : "null",
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
index 367abb040..a8967a8dc 100644
---
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
@@ -20,12 +20,16 @@ package org.apache.amoro.server.authentication;
import static org.junit.jupiter.api.Assertions.assertThrows;
+import org.apache.amoro.authentication.PasswdAuthenticationProvider;
+import org.apache.amoro.authentication.TokenAuthenticationProvider;
+import org.apache.amoro.authentication.TokenCredential;
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;
+import java.util.Collections;
+
public class HttpAuthenticationFactoryTest {
@Test
public void testPasswordAuthenticationProvider() {
@@ -44,17 +48,52 @@ public class HttpAuthenticationFactoryTest {
HttpAuthenticationFactory.getPasswordAuthenticationProvider(
DefaultPasswdAuthenticationProvider.class.getName(), conf);
- assert passwdAuthenticationProvider.authenticate("admin",
"password").getName().equals("admin");
+ assert passwdAuthenticationProvider
+ .authenticate(new DefaultPasswordCredential("admin", "password"))
+ .getName()
+ .equals("admin");
assertThrows(
SignatureCheckException.class,
() -> {
- passwdAuthenticationProvider.authenticate("admin",
"invalidPassword");
+ passwdAuthenticationProvider.authenticate(
+ new DefaultPasswordCredential("admin", "invalidPassword"));
+ });
+ assertThrows(
+ SignatureCheckException.class,
+ () -> {
+ passwdAuthenticationProvider.authenticate(
+ new DefaultPasswordCredential("nonAdmin", "password"));
+ });
+ }
+
+ @Test
+ public void testBearerTokenAuthenticationProvider() {
+ Configurations conf = new Configurations();
+ assertThrows(
+ IllegalStateException.class,
+ () -> {
+ HttpAuthenticationFactory.getBearerAuthenticationProvider(
+ "NonExistentProviderClass", conf);
});
+
+ TokenAuthenticationProvider tokenAuthenticationProvider =
+ HttpAuthenticationFactory.getBearerAuthenticationProvider(
+ UserDefinedTokenAuthenticationProviderImpl.class.getName(), conf);
+
+ assert tokenAuthenticationProvider
+ .authenticate(
+ new DefaultTokenCredential(
+ "token",
Collections.singletonMap(TokenCredential.CLIENT_IP_KEY, "localhost")))
+ .getName()
+ .equals("user");
assertThrows(
SignatureCheckException.class,
() -> {
- passwdAuthenticationProvider.authenticate("nonAdmin", "password");
+ tokenAuthenticationProvider.authenticate(
+ new DefaultTokenCredential(
+ "invalidToken",
+ Collections.singletonMap(TokenCredential.CLIENT_IP_KEY,
"localhost")));
});
}
}
diff --git
a/amoro-ams/src/main/java/org/apache/amoro/server/authentication/DefaultPasswdAuthenticationProvider.java
b/amoro-ams/src/test/java/org/apache/amoro/server/authentication/UserDefinedTokenAuthenticationProviderImpl.java
similarity index 50%
copy from
amoro-ams/src/main/java/org/apache/amoro/server/authentication/DefaultPasswdAuthenticationProvider.java
copy to
amoro-ams/src/test/java/org/apache/amoro/server/authentication/UserDefinedTokenAuthenticationProviderImpl.java
index a4b07ab60..7219c5ec8 100644
---
a/amoro-ams/src/main/java/org/apache/amoro/server/authentication/DefaultPasswdAuthenticationProvider.java
+++
b/amoro-ams/src/test/java/org/apache/amoro/server/authentication/UserDefinedTokenAuthenticationProviderImpl.java
@@ -18,26 +18,28 @@
package org.apache.amoro.server.authentication;
-import org.apache.amoro.config.Configurations;
+import org.apache.amoro.authentication.BasicPrincipal;
+import org.apache.amoro.authentication.TokenAuthenticationProvider;
+import org.apache.amoro.authentication.TokenCredential;
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;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
-public class DefaultPasswdAuthenticationProvider implements
PasswdAuthenticationProvider {
- private String basicAuthUser;
- private String basicAuthPassword;
+import java.security.Principal;
- public DefaultPasswdAuthenticationProvider(Configurations conf) {
- this.basicAuthUser = conf.get(AmoroManagementConf.ADMIN_USERNAME);
- this.basicAuthPassword = conf.get(AmoroManagementConf.ADMIN_PASSWORD);
- }
+public class UserDefinedTokenAuthenticationProviderImpl implements
TokenAuthenticationProvider {
+ private static final Logger LOG =
+
LoggerFactory.getLogger(UserDefinedTokenAuthenticationProviderImpl.class);
+ public static final String VALID_TOKEN = "token";
@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");
+ public Principal authenticate(TokenCredential credential) throws
SignatureCheckException {
+ String clientIp =
credential.extraInfo().get(TokenCredential.CLIENT_IP_KEY);
+ if (VALID_TOKEN.equals(credential.token())) {
+ LOG.info("Success login with clientIp: {}", clientIp);
+ return new BasicPrincipal("user");
+ } else {
+ throw new SignatureCheckException("Token is not valid!");
}
- return new BasicPrincipal(user);
}
}
diff --git
a/amoro-common/src/main/java/org/apache/amoro/spi/authentication/BasicPrincipal.java
b/amoro-common/src/main/java/org/apache/amoro/authentication/BasicPrincipal.java
similarity index 97%
copy from
amoro-common/src/main/java/org/apache/amoro/spi/authentication/BasicPrincipal.java
copy to
amoro-common/src/main/java/org/apache/amoro/authentication/BasicPrincipal.java
index 794682020..2e5936d3d 100644
---
a/amoro-common/src/main/java/org/apache/amoro/spi/authentication/BasicPrincipal.java
+++
b/amoro-common/src/main/java/org/apache/amoro/authentication/BasicPrincipal.java
@@ -16,7 +16,7 @@
* limitations under the License.
*/
-package org.apache.amoro.spi.authentication;
+package org.apache.amoro.authentication;
import java.security.Principal;
import java.util.Objects;
diff --git
a/amoro-common/src/main/java/org/apache/amoro/spi/authentication/PasswdAuthenticationProvider.java
b/amoro-common/src/main/java/org/apache/amoro/authentication/PasswdAuthenticationProvider.java
similarity index 76%
copy from
amoro-common/src/main/java/org/apache/amoro/spi/authentication/PasswdAuthenticationProvider.java
copy to
amoro-common/src/main/java/org/apache/amoro/authentication/PasswdAuthenticationProvider.java
index 3352ab17e..8d868237f 100644
---
a/amoro-common/src/main/java/org/apache/amoro/spi/authentication/PasswdAuthenticationProvider.java
+++
b/amoro-common/src/main/java/org/apache/amoro/authentication/PasswdAuthenticationProvider.java
@@ -16,7 +16,7 @@
* limitations under the License.
*/
-package org.apache.amoro.spi.authentication;
+package org.apache.amoro.authentication;
import org.apache.amoro.exception.SignatureCheckException;
@@ -25,13 +25,12 @@ 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]].
+ * users for their requests. If a user is to be granted, return the
identifier. 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
+ * @param credential The credential 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;
+ Principal authenticate(PasswordCredential credential) throws
SignatureCheckException;
}
diff --git
a/amoro-common/src/main/java/org/apache/amoro/spi/authentication/BasicPrincipal.java
b/amoro-common/src/main/java/org/apache/amoro/authentication/PasswordCredential.java
similarity index 51%
copy from
amoro-common/src/main/java/org/apache/amoro/spi/authentication/BasicPrincipal.java
copy to
amoro-common/src/main/java/org/apache/amoro/authentication/PasswordCredential.java
index 794682020..cb49bec39 100644
---
a/amoro-common/src/main/java/org/apache/amoro/spi/authentication/BasicPrincipal.java
+++
b/amoro-common/src/main/java/org/apache/amoro/authentication/PasswordCredential.java
@@ -16,43 +16,14 @@
* limitations under the License.
*/
-package org.apache.amoro.spi.authentication;
+package org.apache.amoro.authentication;
-import java.security.Principal;
-import java.util.Objects;
+import java.util.Map;
-public class BasicPrincipal implements Principal {
- private final String name;
+public interface PasswordCredential {
+ String username();
- public BasicPrincipal(String name) {
- this.name = name;
- Objects.requireNonNull(name, "Principal name cannot be null");
- }
+ String password();
- @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);
- }
+ Map<String, String> extraInfo();
}
diff --git
a/amoro-common/src/main/java/org/apache/amoro/spi/authentication/PasswdAuthenticationProvider.java
b/amoro-common/src/main/java/org/apache/amoro/authentication/TokenAuthenticationProvider.java
similarity index 54%
rename from
amoro-common/src/main/java/org/apache/amoro/spi/authentication/PasswdAuthenticationProvider.java
rename to
amoro-common/src/main/java/org/apache/amoro/authentication/TokenAuthenticationProvider.java
index 3352ab17e..d8100eebd 100644
---
a/amoro-common/src/main/java/org/apache/amoro/spi/authentication/PasswdAuthenticationProvider.java
+++
b/amoro-common/src/main/java/org/apache/amoro/authentication/TokenAuthenticationProvider.java
@@ -16,22 +16,22 @@
* limitations under the License.
*/
-package org.apache.amoro.spi.authentication;
+package org.apache.amoro.authentication;
import org.apache.amoro.exception.SignatureCheckException;
import java.security.Principal;
-public interface PasswdAuthenticationProvider {
+public interface TokenAuthenticationProvider {
/**
- * 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]].
+ * TokenAuthenticationProvider is used by the Amoro server authentication
layer to validate Bearer
+ * tokens, such as JWT (JSON Web Token), provided in client requests. If the
token is invalid,
+ * expired, or fails signature verification, a {@link
SignatureCheckException} should be thrown to
+ * deny access.
*
- * @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
+ * @param credential The Bearer token credential (e.g., JWT) received in the
connection request
+ * @return The {@link Principal} associated with the authenticated token
+ * @throws SignatureCheckException If the token is invalid, expired, or
fails verification
*/
- Principal authenticate(String user, String password) throws
SignatureCheckException;
+ Principal authenticate(TokenCredential credential) throws
SignatureCheckException;
}
diff --git
a/amoro-common/src/main/java/org/apache/amoro/spi/authentication/BasicPrincipal.java
b/amoro-common/src/main/java/org/apache/amoro/authentication/TokenCredential.java
similarity index 51%
rename from
amoro-common/src/main/java/org/apache/amoro/spi/authentication/BasicPrincipal.java
rename to
amoro-common/src/main/java/org/apache/amoro/authentication/TokenCredential.java
index 794682020..b60e53440 100644
---
a/amoro-common/src/main/java/org/apache/amoro/spi/authentication/BasicPrincipal.java
+++
b/amoro-common/src/main/java/org/apache/amoro/authentication/TokenCredential.java
@@ -16,43 +16,14 @@
* limitations under the License.
*/
-package org.apache.amoro.spi.authentication;
+package org.apache.amoro.authentication;
-import java.security.Principal;
-import java.util.Objects;
+import java.util.Map;
-public class BasicPrincipal implements Principal {
- private final String name;
+public interface TokenCredential {
+ String CLIENT_IP_KEY = "clientIp";
- public BasicPrincipal(String name) {
- this.name = name;
- Objects.requireNonNull(name, "Principal name cannot be null");
- }
+ String token();
- @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);
- }
+ Map<String, String> extraInfo();
}
diff --git a/docs/admin-guides/deployment.md b/docs/admin-guides/deployment.md
index 23374ec2f..83c81840d 100644
--- a/docs/admin-guides/deployment.md
+++ b/docs/admin-guides/deployment.md
@@ -75,8 +75,10 @@ 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 `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.
+- The `ams.http-server.rest-auth-type` configuration specifies the REST API
auth type, which could be token(default), basic or bearer.
+- 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.authentication.PasswdAuthenticationProvider`
interface.
+- The `ams.http-server.auth-bearer-provider` configuration specifies the REST
API Bearer token authentication provider. Set this to the fully qualified class
name of your custom provider implementing the
`org.apache.amoro.authentication.TokenAuthenticationProvider` interface. This
is required when `ams.http-server.rest-auth-type` is set to `bearer`.
+- The `ams.http-server.proxy-client-ip-header` configuration specifies the
HTTP header to use for extracting the real client IP address when AMS is
deployed behind a reverse proxy (such as Nginx or a load balancer). Common
values include `X-Forwarded-For` or `X-Real-IP`. If not set, AMS will use the
remote address from the connection.
```yaml
ams: