This is an automated email from the ASF dual-hosted git repository.
mchades pushed a commit to branch branch-1.3
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/branch-1.3 by this push:
new ac031e6e94 [Cherry-pick to branch-1.3] [#11680] feat(idp-basic): Add
basic authenticator type for Web UI login contract (#11688) (#11723)
ac031e6e94 is described below
commit ac031e6e94176e01fa8a2eafab160c82fb3f2aa7
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Thu Jun 18 09:36:18 2026 +0800
[Cherry-pick to branch-1.3] [#11680] feat(idp-basic): Add basic
authenticator type for Web UI login contract (#11688) (#11723)
**Cherry-pick Information:**
- Original commit: d845d5cddc5bc208968a0e6bf4b594691afdb853
- Target branch: `branch-1.3`
- Status: ✅ Clean cherry-pick (no conflicts)
Co-authored-by: MaSai <[email protected]>
---
.../apache/gravitino/auth/AuthenticatorType.java | 3 +
design-docs/gravitino-local-authentication.md | 13 ++--
docs/open-api/idp/idp.yaml | 5 +-
docs/open-api/idp/openapi.yaml | 9 +--
docs/security/how-to-authenticate.md | 21 +++---
docs/security/how-to-use-built-in-idp.md | 38 ++++++-----
docs/webui-v2.md | 10 ++-
docs/webui.md | 8 +--
.../gravitino/idp/auth/BasicAuthenticator.java | 23 +++++++
.../idp/web/rest/feature/IdpRESTFeature.java | 33 ++++++++--
.../apache/gravitino/idp/SystemExitTestHelper.java | 68 +++++++++++++++++++
.../gravitino/idp/TestIdpUserGroupManager.java | 17 +++--
.../gravitino/idp/auth/TestBasicAuthenticator.java | 23 +++++++
.../integration/test/BasicAuthOperationsIT.java | 4 +-
.../idp/integration/test/IdpRESTApiIT.java | 6 +-
.../idp/web/rest/feature/TestIdpRESTFeature.java | 77 ++++++++--------------
.../authentication/AuthenticatorFactory.java | 6 ++
17 files changed, 249 insertions(+), 115 deletions(-)
diff --git
a/common/src/main/java/org/apache/gravitino/auth/AuthenticatorType.java
b/common/src/main/java/org/apache/gravitino/auth/AuthenticatorType.java
index c44c2c9a90..a458d5952f 100644
--- a/common/src/main/java/org/apache/gravitino/auth/AuthenticatorType.java
+++ b/common/src/main/java/org/apache/gravitino/auth/AuthenticatorType.java
@@ -27,6 +27,9 @@ public enum AuthenticatorType {
/** Simple authentication. */
SIMPLE,
+ /** Built-in IdP HTTP Basic authentication. */
+ BASIC,
+
/** Authentication that uses OAuth. */
OAUTH,
diff --git a/design-docs/gravitino-local-authentication.md
b/design-docs/gravitino-local-authentication.md
index bfb1d7ea3f..b8ba682d84 100644
--- a/design-docs/gravitino-local-authentication.md
+++ b/design-docs/gravitino-local-authentication.md
@@ -315,12 +315,13 @@ fresh Gravitino deployment.
1. Deploy Gravitino with built-in IDP enabled:
```properties
+ gravitino.authenticators=basic
gravitino.server.rest.extensionPackages=org.apache.gravitino.idp.web.rest.feature
gravitino.authorization.serviceAdmins=admin1,admin2
```
- Built-in IdP is incompatible with the `simple` authenticator. When the
`idp-basic` plugin is
- enabled, `gravitino.authenticators` must not include `simple`.
+ Built-in IdP is incompatible with the `simple` authenticator (the default),
+ `gravitino.authenticators` must include `basic` and must not include
`simple`.
2. Export the initial service admin password before starting Gravitino:
@@ -399,11 +400,15 @@ credentials are otherwise exposed on the wire.
| Key | Value
| Required when using built-in IDP |
|---------------------------------------------|-------------------------------------------------|----------------------------------|
+| `gravitino.authenticators` | Must include `basic`
| Yes |
| `gravitino.server.rest.extensionPackages` |
`org.apache.gravitino.idp.web.rest.feature` | Yes
|
| `gravitino.authorization.serviceAdmins` | Comma-separated service admin
| Yes |
-List `org.apache.gravitino.idp.web.rest.feature` in
`gravitino.server.rest.extensionPackages` so
-Jersey registers `/api/idp/*` management APIs. Callers must use Basic
authentication with a username
+List `basic` in `gravitino.authenticators` and
`org.apache.gravitino.idp.web.rest.feature` in
+`gravitino.server.rest.extensionPackages` so Jersey registers `/api/idp/*`
management APIs and the
+Web UI can use the built-in IdP login form. Built-in IdP is incompatible with
the `simple`
+authenticator (the default), `gravitino.authenticators` must include `basic`
and must not include
+`simple`. Callers must use Basic authentication with a username
in `gravitino.authorization.serviceAdmins` and a password stored in
`idp_user_meta`.
### 8.2 Password Algorithm
diff --git a/docs/open-api/idp/idp.yaml b/docs/open-api/idp/idp.yaml
index 6b233bbd51..51b44480db 100644
--- a/docs/open-api/idp/idp.yaml
+++ b/docs/open-api/idp/idp.yaml
@@ -26,8 +26,9 @@ paths:
summary: Add built-in IDP user
description: >
Creates a built-in IDP user with the given username and password.
- Requires the `idp-basic` plugin and built-in IdP Basic authentication.
- `gravitino.authenticators` must not include `simple`.
+ Requires the `idp-basic` plugin and built-in IdP Basic authentication.
Built-in IdP is
+ incompatible with the `simple` authenticator (the default),
`gravitino.authenticators` must
+ include `basic` and must not include `simple`.
operationId: addIdpUser
requestBody:
required: true
diff --git a/docs/open-api/idp/openapi.yaml b/docs/open-api/idp/openapi.yaml
index 23000ef4f9..d8271fab8d 100644
--- a/docs/open-api/idp/openapi.yaml
+++ b/docs/open-api/idp/openapi.yaml
@@ -25,10 +25,11 @@ info:
version: 1.3.0-SNAPSHOT
description: |
OpenAPI specification for built-in IDP user and group management APIs
exposed
- by the `idp-basic` plugin. Clients authenticate with Basic credentials
- validated against built-in IdP user metadata. Enable the plugin via
- `gravitino.server.rest.extensionPackages`; `gravitino.authenticators` must
not
- include `simple` when IdP is enabled.
+ by the `idp-basic` plugin. Clients authenticate with Basic credentials
validated against
+ built-in IdP user metadata. Enable built-in IdP by registering
+ `org.apache.gravitino.idp.web.rest.feature` in
`gravitino.server.rest.extensionPackages`.
+ Built-in IdP is incompatible with the `simple` authenticator (the default),
+ `gravitino.authenticators` must include `basic` and must not include
`simple`.
servers:
- url: "{scheme}://{host}:{port}/{basePath}"
diff --git a/docs/security/how-to-authenticate.md
b/docs/security/how-to-authenticate.md
index 8cc2938a1e..87c20f953d 100644
--- a/docs/security/how-to-authenticate.md
+++ b/docs/security/how-to-authenticate.md
@@ -50,9 +50,13 @@ in the relational entity store.
To enable Basic mode:
+- Set `gravitino.authenticators` to `basic`.
- Set `gravitino.server.rest.extensionPackages` to
`org.apache.gravitino.idp.web.rest.feature`.
- Set `gravitino.authorization.serviceAdmins` to the service admin usernames
that should exist in
the built-in IDP.
+
+Built-in IdP is **incompatible** with the `simple` authenticator (the default),
+`gravitino.authenticators` must include `basic` and must not include `simple`.
- On the first startup, if any configured service admin does not yet have a
password, set the
`GRAVITINO_INITIAL_ADMIN_PASSWORD` environment variable to the initial
password (12 to 64
characters) before starting Gravitino. The same password is applied to every
configured service
@@ -86,13 +90,9 @@ curl -v -X GET \
http://localhost:8090/api/version
```
-:::note
-The Web UI does not provide a username/password login form for built-in IDP
Basic authentication.
-Use REST clients, the Java/Python client, or engine connectors instead. See
-[built-in IDP Web UI](how-to-use-built-in-idp.md#web-ui), the
-[Web UI initial page](../webui.md#initial-page), and the
-[Web V2 initial page](../webui-v2.md#initial-page).
-:::
+The Web UI uses the first entry in `gravitino.authenticators` from `/configs`.
When it is `basic`,
+the login page shows a username and password form backed by built-in IdP user
metadata. See
+[built-in IDP Web UI](how-to-use-built-in-idp.md#web-ui).
### OAuth Mode
@@ -325,7 +325,7 @@ Gravitino server and Gravitino Iceberg REST server share
the same configuration
| Configuration item | Description
| Default value |
Required
| Si [...]
|-----------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------|-------------------------------------------------------------------------------------------------|---
[...]
| `gravitino.authenticator` | It is deprecated since
Gravitino 0.6.0. Please use `gravitino.authenticators` instead.
| `simple` | No
| 0. [...]
-| `gravitino.authenticators` | The authenticators
which Gravitino uses, setting as `simple`,`oauth` or `kerberos`. Multiple
authenticators are separated by commas. If a request is supported by multiple
authenticators simultaneously, the first authenticator will be used by default.
| `simple`
| No
| 0. [...]
+| `gravitino.authenticators` | The authenticators
which Gravitino uses, setting as `simple`, `basic`, `oauth` or `kerberos`.
Multiple authenticators are separated by commas. If a request is supported by
multiple authenticators simultaneously, the first authenticator will be used by
default. | `simple`
| No
| 0. [...]
| `gravitino.authenticator.oauth.serviceAudience` | The audience name when
Gravitino uses OAuth as the authenticator.
| `GravitinoServer` | No
| 0. [...]
| `gravitino.authenticator.oauth.allowSkewSecs` | The JWT allows skew
seconds when Gravitino uses OAuth as the authenticator.
| `0` | No
| 0. [...]
| `gravitino.authenticator.oauth.defaultSignKey` | The signing key of JWT
when Gravitino uses OAuth as the authenticator.
| (none) | Yes if
use `oauth` as the authenticator
| 0. [...]
@@ -374,14 +374,15 @@ This example shows how to enable built-in Basic
authentication.
- Gravitino distribution package (includes the idp-basic plugin on the server
classpath)
-Built-in IdP is **incompatible** with the `simple` authenticator (the
default). When the
-`idp-basic` plugin is enabled, `gravitino.authenticators` must not include
`simple`.
+Built-in IdP is **incompatible** with the `simple` authenticator (the default),
+`gravitino.authenticators` must include `basic` and must not include `simple`.
**Configuration:**
Append the following to `conf/gravitino.conf`:
```text
+gravitino.authenticators = basic
gravitino.server.rest.extensionPackages =
org.apache.gravitino.idp.web.rest.feature
gravitino.authorization.serviceAdmins = admin
```
diff --git a/docs/security/how-to-use-built-in-idp.md
b/docs/security/how-to-use-built-in-idp.md
index 09dd0e0ad5..9eb2e64b6a 100644
--- a/docs/security/how-to-use-built-in-idp.md
+++ b/docs/security/how-to-use-built-in-idp.md
@@ -21,12 +21,18 @@ request and response schemas, see the [Built-in IDP
OpenAPI](../open-api/idp/ope
## Web UI
-Built-in IDP is **incompatible** with the `simple` authenticator (the
default). The Web UI does not
-support signing in with built-in IDP Basic credentials. Login pages only
handle `simple`
-(username-only when authorization is enabled) and `oauth` (OIDC/OAuth) flows.
If you need the Web UI
-while built-in IDP is enabled, prefer [OAuth
mode](how-to-authenticate.md#oauth-mode). For Basic
-authentication, use the REST API, [Java/Python
clients](how-to-authenticate.md#basic-mode), or
-engine connectors instead.
+When built-in IdP Basic authentication is enabled, the Web UI exposes a
username and password login
+form. Configure **both** of the following in `gravitino.conf`:
+
+```properties
+gravitino.authenticators = basic
+gravitino.server.rest.extensionPackages =
org.apache.gravitino.idp.web.rest.feature
+```
+
+The Web UI reads `gravitino.authenticators` from `/configs` and uses the first
entry as the active
+authentication type. List `basic` first when you want the built-in IdP login
form. Built-in IdP is
+**incompatible** with the `simple` authenticator (the default). When IdP is
enabled, do not include
+`simple` in `gravitino.authenticators`.
---
@@ -37,12 +43,12 @@ Before you call `/api/idp/*`, ensure the following:
1. **IDP REST API registration** — In `gravitino.conf`, set:
```properties
+ gravitino.authenticators = basic
gravitino.server.rest.extensionPackages =
org.apache.gravitino.idp.web.rest.feature
```
2. **Server authenticator** — Built-in IdP is **incompatible** with the
`simple` authenticator
- (the default). When the `idp-basic` plugin is enabled,
`gravitino.authenticators` must not
- include `simple`.
+ (the default). Do not list `simple` together with `basic` in
`gravitino.authenticators`.
3. **Service admin passwords** — Built-in IDP requires every username in
`gravitino.authorization.serviceAdmins` to have a password stored in
`idp_user_meta` before you
@@ -74,14 +80,16 @@ Before you call `/api/idp/*`, ensure the following:
Set service admins in `gravitino.conf` (see also
[Prerequisites](#prerequisites)):
-| Configuration item | Description
| Example |
-|-------------------------------------------|-------------------------------------------------------------------------------------|---------|
-| `gravitino.server.rest.extensionPackages` | Registers built-in IdP REST APIs
|
`org.apache.gravitino.idp.web.rest.feature` |
-| `gravitino.authorization.serviceAdmins` | Comma-separated service admin
that can call built-in IDP management APIs | `admin` |
+| Configuration item | Description
| Example
|
+|-------------------------------------------|--------------------------------------------------------------------------|---------------------------------------------|
+| `gravitino.authenticators` | Must include `basic` when the
built-in IdP plugin is enabled | `basic`
|
+| `gravitino.server.rest.extensionPackages` | Registers built-in IdP REST APIs
|
`org.apache.gravitino.idp.web.rest.feature` |
+| `gravitino.authorization.serviceAdmins` | Comma-separated service admin
that can call built-in IDP management APIs | `admin`
|
Example:
```properties
+gravitino.authenticators = basic
gravitino.server.rest.extensionPackages =
org.apache.gravitino.idp.web.rest.feature
gravitino.authorization.serviceAdmins = admin
```
@@ -322,14 +330,14 @@ Replace these with values that match your deployment.
[Configuration](#configuration)):
```properties
+ gravitino.authenticators = basic
gravitino.server.rest.extensionPackages =
org.apache.gravitino.idp.web.rest.feature
gravitino.authorization.enable = true
gravitino.authorization.serviceAdmins = admin
```
- Built-in IdP is **incompatible** with the `simple` authenticator. Remove
`simple` from
- `gravitino.authenticators` (the default). For example, use `oauth` when the
Web UI authenticates
- through an external IdP; see [How to authenticate](how-to-authenticate.md).
+ Built-in IdP is **incompatible** with the `simple` authenticator (the
default),
+ `gravitino.authenticators` must include `basic` and must not include
`simple`.
2. Before the first start, set the initial service admin password (see
[password rules](#password-and-username-rules)):
diff --git a/docs/webui-v2.md b/docs/webui-v2.md
index 79c446301e..bc3fad8182 100644
--- a/docs/webui-v2.md
+++ b/docs/webui-v2.md
@@ -50,16 +50,14 @@ The Web V2 landing page depends on both the authentication
mode and whether auth

+- When `gravitino.authenticators=basic` and the built-in IdP extension package
is configured, the
+ Web UI shows a username and password login form backed by built-in IdP user
metadata. See
+ [built-in IDP Web UI](security/how-to-use-built-in-idp.md#web-ui).
+
- When `gravitino.authenticators=oauth`, OAuth configuration is required for
login. OAuth mode requires `gravitino.authorization.enable=true`. See the
details in [Security](security/security.md)

-:::note
-Built-in IDP Basic authentication is not supported in the Web UI. The UI does
not collect
-username and password for this mode. Use the REST API, Java/Python clients, or
engine connectors
-instead. See [built-in IDP Web UI](security/how-to-use-built-in-idp.md#web-ui).
-:::
-
### Metalakes
Overview for Metalake in the Web V2.
diff --git a/docs/webui.md b/docs/webui.md
index d93ae14598..28da4d45d4 100644
--- a/docs/webui.md
+++ b/docs/webui.md
@@ -20,13 +20,7 @@ Integrate [OAuth settings](security/security.md) to view,
add, modify, and delet
The web UI homepage displayed in Gravitino depends on the configuration
parameter for OAuth mode, see the details in [Security](security/security.md).
-Set parameter for `gravitino.authenticators`, [`simple`](#simple-mode) or
[`oauth`](#oauth-mode). Simple mode is the default authentication option. If
multiple authenticators are set, the first one is taken by default.
-
-:::note
-Built-in IDP Basic authentication is not supported in the Web UI. The UI does
not collect
-username and password for this mode. Use the REST API, Java/Python clients, or
engine connectors
-instead. See [built-in IDP Web UI](security/how-to-use-built-in-idp.md#web-ui).
-:::
+Set parameter for `gravitino.authenticators`: [`simple`](#simple-mode),
[`basic`](../security/how-to-authenticate.md#basic-mode), or
[`oauth`](#oauth-mode). Simple mode is the default authentication option. If
multiple authenticators are set, the first one is taken by default.
:::tip
After changing the configuration, make sure to restart the Gravitino server.
diff --git
a/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/auth/BasicAuthenticator.java
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/auth/BasicAuthenticator.java
index 3192f2fb67..d5a111d503 100644
---
a/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/auth/BasicAuthenticator.java
+++
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/auth/BasicAuthenticator.java
@@ -28,6 +28,7 @@ import java.util.Optional;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.gravitino.Config;
+import org.apache.gravitino.Configs;
import org.apache.gravitino.GravitinoEnv;
import org.apache.gravitino.UserGroup;
import org.apache.gravitino.UserPrincipal;
@@ -35,6 +36,7 @@ import org.apache.gravitino.auth.AuthConstants;
import org.apache.gravitino.exceptions.UnauthorizedException;
import org.apache.gravitino.idp.IdpUserGroupManager;
import org.apache.gravitino.idp.model.IdpUser;
+import org.apache.gravitino.idp.web.rest.feature.IdpRESTFeature;
import org.apache.gravitino.server.authentication.Authenticator;
/** Authenticates HTTP Basic credentials against built-in IdP user metadata. */
@@ -62,10 +64,31 @@ public class BasicAuthenticator implements Authenticator {
@Override
public void initialize(Config config) {
+ validateExtensionPackage(config);
GravitinoEnv env = GravitinoEnv.getInstance();
this.userGroupManager = IdpUserGroupManager.getInstance(config,
env.idGenerator());
}
+ /**
+ * Validates that the built-in IdP REST extension package is enabled when
Basic authentication is
+ * used.
+ *
+ * @param config The server configuration.
+ */
+ static void validateExtensionPackage(Config config) {
+ boolean idpExtensionEnabled =
+ config.get(Configs.REST_API_EXTENSION_PACKAGES).stream()
+ .anyMatch(
+ pkg ->
IdpRESTFeature.IDP_REST_EXTENSION_PACKAGE.equalsIgnoreCase(pkg.trim()));
+ if (!idpExtensionEnabled) {
+ throw new IllegalStateException(
+ String.format(
+ "'basic' in gravitino.authenticators requires
gravitino.server.rest.extensionPackages "
+ + "to include %s.",
+ IdpRESTFeature.IDP_REST_EXTENSION_PACKAGE));
+ }
+ }
+
@Override
public boolean supportsToken(byte[] tokenData) {
return tokenData != null
diff --git
a/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/web/rest/feature/IdpRESTFeature.java
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/web/rest/feature/IdpRESTFeature.java
index 668b5d84a3..bbe468046c 100644
---
a/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/web/rest/feature/IdpRESTFeature.java
+++
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/web/rest/feature/IdpRESTFeature.java
@@ -41,16 +41,17 @@ import org.slf4j.LoggerFactory;
/**
* Registers built-in IdP REST resources for the idp-basic plugin.
*
- * <p>Configure {@link Configs#REST_API_EXTENSION_PACKAGES} to {@code
- * org.apache.gravitino.idp.web.rest.feature} so Jersey auto-discovers this
feature. IdP REST
- * resource classes remain in {@code org.apache.gravitino.idp.web.rest} and
are registered here.
- * Also initializes configured service admins in the built-in IdP when they do
not yet exist.
+ * <p>Configure {@link Configs#REST_API_EXTENSION_PACKAGES} to {@link
#IDP_REST_EXTENSION_PACKAGE}
+ * and list {@code basic} in {@link Configs#AUTHENTICATORS} so Jersey
auto-discovers this feature.
+ * IdP REST resource classes remain in {@code
org.apache.gravitino.idp.web.rest} and are registered
+ * here. Also initializes configured service admins in the built-in IdP when
they do not yet exist.
*/
@Provider
public class IdpRESTFeature implements Feature {
private static final Logger LOG =
LoggerFactory.getLogger(IdpRESTFeature.class);
+ /** Extension package name registered through {@code
gravitino.server.rest.extensionPackages}. */
public static final String IDP_REST_EXTENSION_PACKAGE =
IdpRESTFeature.class.getPackageName();
/** Environment variable for the initial password of configured service
admins. */
@@ -81,16 +82,34 @@ public class IdpRESTFeature implements Feature {
/**
* Validates that the server configuration is compatible with the built-in
IdP plugin.
*
+ * <p>Called when the idp-basic extension package is enabled. Requires
{@code basic} in {@link
+ * Configs#AUTHENTICATORS} and rejects {@code simple}.
+ *
* @param config The server configuration.
*/
static void validateConfiguration(Config config) {
+ List<String> authenticators = config.get(Configs.AUTHENTICATORS);
+ boolean usesBasic =
+ authenticators.stream()
+ .anyMatch(name ->
AuthenticatorType.BASIC.name().equalsIgnoreCase(name.trim()));
boolean usesSimple =
- config.get(Configs.AUTHENTICATORS).stream()
+ authenticators.stream()
.anyMatch(name ->
AuthenticatorType.SIMPLE.name().equalsIgnoreCase(name.trim()));
+
+ if (!usesBasic) {
+ LOG.error(
+ "gravitino.server.rest.extensionPackages includes the built-in IdP
plugin ({}) but "
+ + "'basic' is not listed in gravitino.authenticators. Add
'basic' to "
+ + "gravitino.authenticators.",
+ IDP_REST_EXTENSION_PACKAGE);
+ System.exit(1);
+ }
+
if (usesSimple) {
LOG.error(
- "Built-in IdP is incompatible with Simple authentication. "
- + "Remove 'simple' from gravitino.authenticators (default is
simple).");
+ "Built-in IdP basic authentication is incompatible with simple
authentication because "
+ + "both handle Authorization: Basic headers. Remove 'simple'
from "
+ + "gravitino.authenticators.");
System.exit(1);
}
}
diff --git
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/SystemExitTestHelper.java
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/SystemExitTestHelper.java
new file mode 100644
index 0000000000..17cc3423ef
--- /dev/null
+++
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/SystemExitTestHelper.java
@@ -0,0 +1,68 @@
+/*
+ * 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.idp;
+
+/** Test-only helpers for code paths that call {@link System#exit(int)}. */
+public final class SystemExitTestHelper {
+
+ private SystemExitTestHelper() {}
+
+ /**
+ * Runs an action while intercepting {@link System#exit(int)} and rethrowing
it as {@link
+ * SystemExitException}.
+ *
+ * @param action The action that may call {@code System.exit}.
+ */
+ @SuppressWarnings("removal")
+ public static void runWithExitGuard(Runnable action) {
+ SecurityManager original = System.getSecurityManager();
+ System.setSecurityManager(
+ new SecurityManager() {
+ @Override
+ public void checkExit(int status) {
+ throw new SystemExitException(status);
+ }
+
+ @Override
+ public void checkPermission(java.security.Permission perm) {
+ // Allow test execution.
+ }
+ });
+ try {
+ action.run();
+ } finally {
+ System.setSecurityManager(original);
+ }
+ }
+
+ /** Thrown by {@link #runWithExitGuard(Runnable)} when the guarded action
calls {@code exit}. */
+ public static final class SystemExitException extends SecurityException {
+ private final int status;
+
+ private SystemExitException(int status) {
+ super("System.exit(" + status + ")");
+ this.status = status;
+ }
+
+ /** Returns the exit status passed to {@link System#exit(int)}. */
+ public int status() {
+ return status;
+ }
+ }
+}
diff --git
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/TestIdpUserGroupManager.java
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/TestIdpUserGroupManager.java
index b18972c597..419b688dcf 100644
---
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/TestIdpUserGroupManager.java
+++
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/TestIdpUserGroupManager.java
@@ -38,9 +38,9 @@ import java.nio.file.Path;
import java.util.Comparator;
import java.util.stream.Stream;
import org.apache.gravitino.Config;
+import org.apache.gravitino.auth.AuthenticatorType;
import org.apache.gravitino.exceptions.AlreadyExistsException;
import org.apache.gravitino.exceptions.NotFoundException;
-import org.apache.gravitino.idp.auth.BasicAuthenticator;
import org.apache.gravitino.idp.basic.IdpCredentialValidator;
import org.apache.gravitino.idp.model.IdpGroup;
import org.apache.gravitino.idp.model.IdpUser;
@@ -55,8 +55,7 @@ import org.junit.jupiter.params.provider.ValueSource;
/** Integration tests for {@link IdpUserGroupManager} backed by an embedded H2
store. */
public class TestIdpUserGroupManager {
- private static final String BASIC_AUTHENTICATOR_CLASS =
- BasicAuthenticator.class.getCanonicalName();
+ private static final String BASIC_AUTHENTICATOR =
AuthenticatorType.BASIC.name().toLowerCase();
private static final String VALID_PASSWORD = "Passw0rd-1234";
private static final String ANOTHER_VALID_PASSWORD = "AnotherPass1!";
@@ -208,7 +207,7 @@ public class TestIdpUserGroupManager {
@Test
public void
testInitializeConfiguredServiceAdminsCreatesMissingServiceAdmin() throws
IOException {
- loadServiceAdminConfig(BASIC_AUTHENTICATOR_CLASS,
"initAdminCreate1,initAdminCreate2");
+ loadServiceAdminConfig(BASIC_AUTHENTICATOR,
"initAdminCreate1,initAdminCreate2");
manager.addUser("initAdminCreate2", VALID_PASSWORD);
manager.initializeConfiguredServiceAdmins(config, VALID_PASSWORD);
@@ -220,7 +219,7 @@ public class TestIdpUserGroupManager {
@Test
public void
testInitializeConfiguredServiceAdminsSkipsWhenNoServiceAdminsConfigured()
throws IOException {
- loadServiceAdminConfig(BASIC_AUTHENTICATOR_CLASS, "");
+ loadServiceAdminConfig(BASIC_AUTHENTICATOR, "");
manager.initializeConfiguredServiceAdmins(config, VALID_PASSWORD);
Assertions.assertThrows(NotFoundException.class, () ->
manager.getUser("initAdminSkipList1"));
}
@@ -228,7 +227,7 @@ public class TestIdpUserGroupManager {
@Test
public void
testInitializeConfiguredServiceAdminsSkipsWhenAllServiceAdminsAlreadyExist()
throws IOException {
- loadServiceAdminConfig(BASIC_AUTHENTICATOR_CLASS,
"initAdminExist1,initAdminExist2");
+ loadServiceAdminConfig(BASIC_AUTHENTICATOR,
"initAdminExist1,initAdminExist2");
manager.addUser("initAdminExist1", VALID_PASSWORD);
manager.addUser("initAdminExist2", VALID_PASSWORD);
@@ -240,7 +239,7 @@ public class TestIdpUserGroupManager {
@Test
public void
testInitializeConfiguredServiceAdminsFailsWhenRequiredPasswordMissing() {
- loadServiceAdminConfig(BASIC_AUTHENTICATOR_CLASS, "initAdminNoPwd1");
+ loadServiceAdminConfig(BASIC_AUTHENTICATOR, "initAdminNoPwd1");
IllegalArgumentException exception =
Assertions.assertThrows(
@@ -256,7 +255,7 @@ public class TestIdpUserGroupManager {
@ParameterizedTest
@ValueSource(strings = {"short"})
public void
testInitializeConfiguredServiceAdminsFailsOnInvalidPasswordPayload(String
payload) {
- loadServiceAdminConfig(BASIC_AUTHENTICATOR_CLASS, "initAdminBadPwd1");
+ loadServiceAdminConfig(BASIC_AUTHENTICATOR, "initAdminBadPwd1");
IllegalArgumentException exception =
Assertions.assertThrows(
@@ -271,7 +270,7 @@ public class TestIdpUserGroupManager {
@Test
public void
testInitializeConfiguredServiceAdminsUsesSamePasswordForAllMissingServiceAdmins()
throws IOException {
- loadServiceAdminConfig(BASIC_AUTHENTICATOR_CLASS,
"initAdminSame1,initAdminSame2");
+ loadServiceAdminConfig(BASIC_AUTHENTICATOR,
"initAdminSame1,initAdminSame2");
manager.initializeConfiguredServiceAdmins(config, VALID_PASSWORD);
diff --git
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/auth/TestBasicAuthenticator.java
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/auth/TestBasicAuthenticator.java
index e31c9c8bb7..1431bdb50a 100644
---
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/auth/TestBasicAuthenticator.java
+++
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/auth/TestBasicAuthenticator.java
@@ -19,6 +19,7 @@
package org.apache.gravitino.idp.auth;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -26,19 +27,41 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import com.google.common.collect.Lists;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Base64;
+import org.apache.gravitino.Config;
+import org.apache.gravitino.Configs;
import org.apache.gravitino.UserPrincipal;
import org.apache.gravitino.auth.AuthConstants;
import org.apache.gravitino.exceptions.UnauthorizedException;
import org.apache.gravitino.idp.IdpUserGroupManager;
import org.apache.gravitino.idp.model.IdpUser;
+import org.apache.gravitino.idp.web.rest.feature.IdpRESTFeature;
import org.junit.jupiter.api.Test;
class TestBasicAuthenticator {
+ @Test
+ void testValidateExtensionPackageOk() {
+ Config config = new Config(false) {};
+ config.set(
+ Configs.REST_API_EXTENSION_PACKAGES,
+ Lists.newArrayList(IdpRESTFeature.IDP_REST_EXTENSION_PACKAGE));
+
+ assertDoesNotThrow(() ->
BasicAuthenticator.validateExtensionPackage(config));
+ }
+
+ @Test
+ void testValidateExtensionPackageMissingFails() {
+ Config config = new Config(false) {};
+
+ assertThrows(
+ IllegalStateException.class, () ->
BasicAuthenticator.validateExtensionPackage(config));
+ }
+
@Test
void testNotInitialized() {
IllegalStateException exception =
diff --git
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/integration/test/BasicAuthOperationsIT.java
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/integration/test/BasicAuthOperationsIT.java
index df16e56bf8..3ded05ef48 100644
---
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/integration/test/BasicAuthOperationsIT.java
+++
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/integration/test/BasicAuthOperationsIT.java
@@ -24,9 +24,9 @@ import com.google.common.collect.Maps;
import java.io.IOException;
import java.util.Map;
import org.apache.gravitino.Configs;
+import org.apache.gravitino.auth.AuthenticatorType;
import org.apache.gravitino.client.GravitinoAdminClient;
import org.apache.gravitino.client.GravitinoVersion;
-import org.apache.gravitino.idp.auth.BasicAuthenticator;
import org.apache.gravitino.idp.web.rest.feature.IdpRESTFeature;
import org.apache.gravitino.integration.test.util.BaseIT;
import org.apache.gravitino.integration.test.util.ITUtils;
@@ -53,7 +53,7 @@ public class BasicAuthOperationsIT extends BaseIT {
configs.put(Configs.CACHE_ENABLED.getKey(), String.valueOf(false));
configs.put(Configs.STORE_DELETE_AFTER_TIME.getKey(), String.valueOf(20 *
60 * 1000L));
configs.put(Configs.SERVICE_ADMINS.getKey(), ADMIN);
- configs.put(Configs.AUTHENTICATORS.getKey(),
BasicAuthenticator.class.getCanonicalName());
+ configs.put(Configs.AUTHENTICATORS.getKey(),
AuthenticatorType.BASIC.name().toLowerCase());
configs.put(
Configs.REST_API_EXTENSION_PACKAGES.getKey(),
IdpRESTFeature.IDP_REST_EXTENSION_PACKAGE);
registerCustomConfigs(configs);
diff --git
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/integration/test/IdpRESTApiIT.java
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/integration/test/IdpRESTApiIT.java
index 6bf9cbc75e..ca18bfcf6c 100644
---
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/integration/test/IdpRESTApiIT.java
+++
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/integration/test/IdpRESTApiIT.java
@@ -87,7 +87,11 @@ public class IdpRESTApiIT extends BaseIT {
configs.put(Configs.CACHE_ENABLED.getKey(), String.valueOf(false));
configs.put(Configs.STORE_DELETE_AFTER_TIME.getKey(), String.valueOf(20 *
60 * 1000L));
configs.put(Configs.SERVICE_ADMINS.getKey(), ADMIN);
- configs.put(Configs.AUTHENTICATORS.getKey(),
AuthenticatorType.OAUTH.name().toLowerCase());
+ configs.put(
+ Configs.AUTHENTICATORS.getKey(),
+ AuthenticatorType.BASIC.name().toLowerCase()
+ + ","
+ + AuthenticatorType.OAUTH.name().toLowerCase());
configs.put(OAuthConfig.SERVICE_AUDIENCE.getKey(), "service1");
configs.put(OAuthConfig.DEFAULT_SIGN_KEY.getKey(), oauthPublicSignKey());
configs.put(OAuthConfig.DEFAULT_SERVER_URI.getKey(), "test");
diff --git
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/web/rest/feature/TestIdpRESTFeature.java
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/web/rest/feature/TestIdpRESTFeature.java
index 7215361797..c596074900 100644
---
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/web/rest/feature/TestIdpRESTFeature.java
+++
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/web/rest/feature/TestIdpRESTFeature.java
@@ -27,72 +27,53 @@ import com.google.common.collect.Lists;
import org.apache.gravitino.Config;
import org.apache.gravitino.Configs;
import org.apache.gravitino.auth.AuthenticatorType;
+import org.apache.gravitino.idp.SystemExitTestHelper;
+import org.apache.gravitino.idp.SystemExitTestHelper.SystemExitException;
import org.junit.jupiter.api.Test;
class TestIdpRESTFeature {
@Test
- void testSimpleFails() {
- Config config = newConfig(AuthenticatorType.SIMPLE.name().toLowerCase());
+ void testSimpleWithBasicFails() {
+ Config config = new Config(false) {};
+ config.set(
+ Configs.AUTHENTICATORS,
+ Lists.newArrayList(
+ AuthenticatorType.SIMPLE.name().toLowerCase(),
+ AuthenticatorType.BASIC.name().toLowerCase()));
SystemExitException exception =
- assertThrows(SystemExitException.class, () ->
validateWithExitGuard(config));
+ assertThrows(
+ SystemExitException.class,
+ () ->
+ SystemExitTestHelper.runWithExitGuard(
+ () -> IdpRESTFeature.validateConfiguration(config)));
assertEquals(1, exception.status());
}
@Test
- void testDefaultSimpleFails() {
+ void testExtensionWithoutBasicFails() {
Config config = new Config(false) {};
+ config.set(
+ Configs.AUTHENTICATORS,
Lists.newArrayList(AuthenticatorType.OAUTH.name().toLowerCase()));
- assertThrows(SystemExitException.class, () ->
validateWithExitGuard(config));
- }
-
- @Test
- void testOAuthOk() {
- Config config = newConfig(AuthenticatorType.OAUTH.name().toLowerCase());
+ SystemExitException exception =
+ assertThrows(
+ SystemExitException.class,
+ () ->
+ SystemExitTestHelper.runWithExitGuard(
+ () -> IdpRESTFeature.validateConfiguration(config)));
- assertDoesNotThrow(() -> IdpRESTFeature.validateConfiguration(config));
+ assertEquals(1, exception.status());
}
- private static Config newConfig(String... authenticators) {
+ @Test
+ void testBasicOk() {
Config config = new Config(false) {};
- config.set(Configs.AUTHENTICATORS, Lists.newArrayList(authenticators));
- return config;
- }
-
- @SuppressWarnings("removal")
- private static void validateWithExitGuard(Config config) {
- SecurityManager original = System.getSecurityManager();
- System.setSecurityManager(
- new SecurityManager() {
- @Override
- public void checkExit(int status) {
- throw new SystemExitException(status);
- }
-
- @Override
- public void checkPermission(java.security.Permission perm) {
- // Allow test execution.
- }
- });
- try {
- IdpRESTFeature.validateConfiguration(config);
- } finally {
- System.setSecurityManager(original);
- }
- }
+ config.set(
+ Configs.AUTHENTICATORS,
Lists.newArrayList(AuthenticatorType.BASIC.name().toLowerCase()));
- private static final class SystemExitException extends SecurityException {
- private final int status;
-
- private SystemExitException(int status) {
- super("System.exit(" + status + ")");
- this.status = status;
- }
-
- private int status() {
- return status;
- }
+ assertDoesNotThrow(() -> IdpRESTFeature.validateConfiguration(config));
}
}
diff --git
a/server-common/src/main/java/org/apache/gravitino/server/authentication/AuthenticatorFactory.java
b/server-common/src/main/java/org/apache/gravitino/server/authentication/AuthenticatorFactory.java
index 31d67cfac8..beaf52cf23 100644
---
a/server-common/src/main/java/org/apache/gravitino/server/authentication/AuthenticatorFactory.java
+++
b/server-common/src/main/java/org/apache/gravitino/server/authentication/AuthenticatorFactory.java
@@ -33,10 +33,16 @@ public class AuthenticatorFactory {
private static final Logger LOG =
LoggerFactory.getLogger(AuthenticatorFactory.class);
+ /** Fully qualified class name of {@code BasicAuthenticator} in the
idp-basic plugin. */
+ private static final String BASIC_AUTHENTICATOR_CLASS_NAME =
+ "org.apache.gravitino.idp.auth.BasicAuthenticator";
+
public static final ImmutableMap<String, String> AUTHENTICATORS =
ImmutableMap.of(
AuthenticatorType.SIMPLE.name().toLowerCase(),
SimpleAuthenticator.class.getCanonicalName(),
+ AuthenticatorType.BASIC.name().toLowerCase(),
+ BASIC_AUTHENTICATOR_CLASS_NAME,
AuthenticatorType.OAUTH.name().toLowerCase(),
OAuth2TokenAuthenticator.class.getCanonicalName(),
AuthenticatorType.KERBEROS.name().toLowerCase(),