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]

Reply via email to