markhoerth opened a new issue, #12255:
URL: https://github.com/apache/gravitino/issues/12255

   ### Version
   
   main branch
   
   ### Describe what's wrong
   
   ## Describe what's wrong
   
   The Lance REST service constructs its `GravitinoClient` with no 
`AuthDataProvider`, so the requests it makes to the Gravitino server carry no 
`Authorization` header.
   
   `GravitinoLanceNamespaceWrapper.initialize()`:
   
   ```java
   this.client =
       GravitinoClient.builder(uri)
           .withMetalake(metalakeName)
           .withClientConfig(clientProperties)
           .build();
   ```
   
   There is no configuration property to change this. `LanceConfig` defines 
only `namespace-backend`, `gravitino-metalake`, and `gravitino-uri`.
   
   Two consequences follow, depending on how `gravitino.authenticators` is set 
on the Gravitino server.
   
   **With `simple`.** `SimpleAuthenticator.supportsToken(null)` returns `true` 
and `authenticateToken(null)` returns `ANONYMOUS_PRINCIPAL`, so the requests 
are accepted. Every Lance operation executes in Gravitino as the anonymous user 
rather than as any identity attributable to the service.
   
   **With `oauth2` or `kerberos`.** `OAuth2TokenAuthenticator.supportsToken` 
requires a Bearer prefix and returns `false` for a null token. No configured 
authenticator accepts the request, so `AuthenticationFilter` throws and every 
Lance operation fails.
   
   Because `gravitino.authenticators` is a single server-wide list, there is no 
per-service override. The only configuration in which the Lance REST service 
works alongside OAuth2 is one that also lists `simple`, and in that 
configuration any caller of the Gravitino server who omits the `Authorization` 
header is likewise accepted as anonymous. The authentication documentation 
already identifies that pattern as unsafe for Basic auth, where it states that 
`gravitino.authenticators` must include `basic` and must not include `simple`.
   
   | `gravitino.authenticators` | Lance REST service | Other callers of the 
Gravitino server |
   
|----------------------------|---------------------------------|----------------------------------------|
   | `simple`                   | Works, as anonymous             | 
Authenticated, or anonymous if no header |
   | `oauth2`                   | Every operation fails           | 
Authenticated                          |
   | `oauth2,simple`            | Works, as anonymous             | Anonymous 
accepted for any caller omitting the header |
   
   The net effect is that the Lance REST service is unusable in a deployment 
with authentication enforced, and usable only in a deployment that accepts 
anonymous requests.
   
   The Iceberg REST service does not have this problem. It exposes 
`gravitino.iceberg-rest.gravitino-auth-type` with `simple` and `oauth2` values 
and a service user name defaulting to `iceberg-rest-server`, and as an 
auxiliary service it uses internal interfaces rather than HTTP, so it does not 
make this call at all.
   
   ### Error message and/or stacktrace
   
   With `gravitino.authenticators = oauth`, any Lance REST operation returns a 
500 and the server logs:
   
   ```
   org.apache.gravitino.exceptions.UnauthorizedException: The provided 
credentials did not support
        at 
org.apache.gravitino.server.authentication.AuthenticationFilter.doFilter(AuthenticationFilter.java:...)
   ```
   
   The relevant code paths:
   
   - `GravitinoLanceNamespaceWrapper.initialize()` builds the client with 
`authDataProvider` unset.
   - `AuthenticationFilter.doFilter` iterates the configured authenticators and 
throws `UnauthorizedException("The provided credentials did not support")` when 
none returns a principal.
   - `SimpleAuthenticator.supportsToken` returns `true` when `tokenData == 
null`; `authenticateToken(null)` returns `ANONYMOUS_PRINCIPAL`.
   - `OAuth2TokenAuthenticator.supportsToken` returns `false` when `tokenData 
== null`.
   
   ### How to reproduce
   
   Gravitino version: main branch.
   
   1. Configure the Gravitino server for OAuth2 per [How to 
authenticate](https://gravitino.apache.org/docs/latest/security/how-to-authenticate),
 so that `gravitino.authenticators = oauth` with no `simple` entry.
   2. Enable the Lance REST service as an auxiliary service:
   
   ```properties
   gravitino.auxService.names = lance-rest
   gravitino.lance-rest.classpath = lance-rest-server/libs
   gravitino.lance-rest.namespace-backend = gravitino
   gravitino.lance-rest.gravitino-uri = http://localhost:8090
   gravitino.lance-rest.gravitino-metalake = test_metalake
   ```
   
   3. Start the server and create a metalake named `test_metalake`.
   4. Call any Lance REST operation with a valid Bearer token, so that 
authentication at the Lance REST service itself succeeds:
   
   ```shell
   curl -X POST http://localhost:9101/lance/v1/namespace/lance_catalog/create \
     -H "Authorization: Bearer ${VALID_TOKEN}" \
     -H 'Content-Type: application/json' \
     -d '{"id": ["lance_catalog"], "mode": "create"}'
   ```
   
   The call fails. The service authenticated the caller and then made its own 
unauthenticated request to the Gravitino server, which rejected it.
   
   5. Add `simple` to `gravitino.authenticators`, restart, and repeat. The call 
now succeeds, and a request to the Gravitino server with no `Authorization` 
header at all also succeeds as anonymous.
   
   ### Additional context
   
   Two related gaps are out of scope for this issue and worth tracking 
separately.
   
   **Caller identity is not forwarded.** Even once the service authenticates to 
Gravitino under its own identity, it does not act on behalf of the caller, so 
Gravitino privileges cannot be evaluated per caller on the Lance path. The 
Iceberg REST service supports access control in auxiliary mode by sending the 
authenticated user identity to the Gravitino server for authorization. 
Achieving the same for Lance is a larger change of the same shape as the user 
forwarding added to the Trino connector.
   
   **Credential vending is not wired into the Lance path.** Storage access is 
configured with static `lance.storage.*` properties and the resolved values, 
including access keys and secrets, are returned to clients in the 
`storageOptions` of `CreateTable` and `DescribeTable` responses. Neither the 
Lance REST service nor the Generic Lakehouse catalog requests vended 
credentials, so short-lived scoped credentials are not available on either 
Lance path.
   
   A fix for the issue described above is prepared and adds 
`gravitino.lance-rest.gravitino-auth-type` with `simple` and `oauth2` support, 
mirroring the Iceberg REST service properties and defaulting to `simple` with 
the user name `lance-rest-server` so that existing deployments are unaffected.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to