markhoerth opened a new issue, #14427:
URL: https://github.com/apache/gravitino/issues/14427
### Version
main branch, commit bb634defdc5663a8124be5fca1c8d9e52ac096ac (2.0.0-SNAPSHOT)
### Describe what's wrong
Gravitino accepts role names with any Unicode characters. A role such as
`vrf_角色` can be created and granted to a user, and the user's role list shows
it correctly. When that user sends the held role in `X-Gravitino-Active-Roles`
to narrow a request to it, the request is rejected with 403 "cannot assume
active role(s) that are not held", and the role name in the message is garbled
(`vrf_è§è²`). The user expects the request to run with that role active, as it
does for ASCII role names.
The problem shows up for any role name containing characters outside
ISO-8859-1. There is no encoding of such a name that the server accepts:
- Raw UTF-8 bytes are read as Latin-1 characters.
- A percent-encoded value (`vrf_%E8%A7%92%E8%89%B2`) is compared literally.
- An RFC 2047 encoded value (`=?UTF-8?B?...?=`) is compared literally.
The only remaining option is `ALL`, which activates every held role and
gives up the narrowing. A Latin-1 name such as `vrf_rôle` can be activated, but
only when the client sends Latin-1 bytes; the same name sent as UTF-8 also
fails (`vrf_rôle`).
Cause (Gravitino code):
`server-common/src/main/java/org/apache/gravitino/server/authentication/AuthenticationFilter.java:231-238`
(`applyActiveRoles`) passes
`request.getHeader(AuthConstants.X_GRAVITINO_ACTIVE_ROLES_HEADER)` (line 233)
directly to `ActiveRolesParser.parse`. The bundled Jetty 9.4 has already turned
the header bytes into a String as ISO-8859-1.
`common/src/main/java/org/apache/gravitino/auth/ActiveRolesParser.java:63-121`
only trims and splits on commas. Nothing re-decodes the value as UTF-8 or
accepts an escaped form, so the parsed names never equal the stored role name
in the membership check. `docs/security/access-control.md` ("Narrowing Access
with Active Roles") and `design-docs/gravitino-role-assumption.md` do not say
how to send non-ASCII role names.
### Error message and/or stacktrace
Header sent as raw UTF-8 bytes (`X-Gravitino-Active-Roles: vrf_角色`):
```
403 {"code":1008,"type":"ForbiddenException","message":"User 'vrf_uni_user'
cannot assume active role(s) that are not held: [vrf_è§è²]"}
```
Header sent percent-encoded (`X-Gravitino-Active-Roles:
vrf_%E8%A7%92%E8%89%B2`):
```
403 {"code":1008,"type":"ForbiddenException","message":"User 'vrf_uni_user'
cannot assume active role(s) that are not held: [vrf_%E8%A7%92%E8%89%B2]"}
```
The same request with `X-Gravitino-Active-Roles: ALL`, or without the
header, returns `200 {"code":0,"identifiers":[]}`.
### How to reproduce
Authorization enabled, simple authentication, `admin` is a service admin.
`dnJmX3VuaV91c2VyOng=` is base64 of `vrf_uni_user:x`.
1. Create a metalake:
```
curl -H 'Authorization: Basic YWRtaW46eA==' -H 'Content-Type:
application/json' -H 'Accept: application/vnd.gravitino.v1+json' -X POST
http://127.0.0.1:8090/api/metalakes -d
'{"name":"vrf_uni_ml","comment":"","properties":{}}'
```
2. Create a fileset catalog:
```
curl -H 'Authorization: Basic YWRtaW46eA==' -H 'Content-Type:
application/json' -H 'Accept: application/vnd.gravitino.v1+json' -X POST
http://127.0.0.1:8090/api/metalakes/vrf_uni_ml/catalogs -d
'{"name":"vrf_uni_cat","type":"FILESET","provider":"fileset","comment":"","properties":{"location":"file:///tmp/vrf_uni_cat"}}'
```
3. Add a user:
```
curl -H 'Authorization: Basic YWRtaW46eA==' -H 'Content-Type:
application/json' -H 'Accept: application/vnd.gravitino.v1+json' -X POST
http://127.0.0.1:8090/api/metalakes/vrf_uni_ml/users -d
'{"name":"vrf_uni_user"}'
```
4. Create a role with a CJK name granting `USE_CATALOG` (200):
```
curl -H 'Authorization: Basic YWRtaW46eA==' -H 'Content-Type:
application/json' -H 'Accept: application/vnd.gravitino.v1+json' -X POST
http://127.0.0.1:8090/api/metalakes/vrf_uni_ml/roles -d
'{"name":"vrf_角色","properties":{},"securableObjects":[{"fullName":"vrf_uni_cat","type":"CATALOG","privileges":[{"name":"USE_CATALOG","condition":"ALLOW"}]}]}'
```
5. Grant the role to the user (200, the response lists `"roles":["vrf_角色"]`):
```
curl -H 'Authorization: Basic YWRtaW46eA==' -H 'Content-Type:
application/json' -H 'Accept: application/vnd.gravitino.v1+json' -X PUT
http://127.0.0.1:8090/api/metalakes/vrf_uni_ml/permissions/users/vrf_uni_user/grant
-d '{"roleNames":["vrf_角色"]}'
```
6. As the user, list schemas with the role active (curl sends the header as
raw UTF-8). The response is 403:
```
curl -H 'Authorization: Basic dnJmX3VuaV91c2VyOng=' -H 'Accept:
application/vnd.gravitino.v1+json' -H 'X-Gravitino-Active-Roles: vrf_角色'
http://127.0.0.1:8090/api/metalakes/vrf_uni_ml/catalogs/vrf_uni_cat/schemas
```
7. Repeat step 6 with `-H 'X-Gravitino-Active-Roles: ALL'`. The response is
200.
### Additional context
Related: #11965 (role assumption), #11966 (parse and validate the header),
#12095 (wire the header into the request path). None of them discusses header
encoding. The parser and filter are Gravitino's own code; the Latin-1 decoding
of header values is Jetty 9.4's default, which Gravitino does not compensate
for or document.
Found by the 2.0 OSS test suite check
`test_non_ascii_role_name_can_be_activated[cjk]`.
--
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]