sundapeng opened a new pull request, #9941:
URL: https://github.com/apache/paimon/pull/9941

   ### Purpose
   
   Follow-up to #9918, and an alternative to #9937: both fix the same problem, 
and only one of them should be merged.
   
   Some DlfNext POP gateway deployments only route an ACS4 request that names 
the API it calls. With `dlf.signing-algorithm=openapi-v4`, the first call a 
catalog makes, `GET /v1/config`, is refused:
   
   ```
   400 MissingHeader: Specified header x-acs-action is mandatory for this 
action.
   ```
   
   The header must be signed (`SignatureDoesNotMatch.HeaderUnsigned` otherwise) 
and must match the request (`InvalidAction.Mismatch` otherwise), so a static 
`header.x-acs-action` option cannot supply it.
   
   #9937 has the signer work the name out from the method and the path. This PR 
has each caller state it instead:
   
   - `RESTApiNames` holds a constant for every API that `RESTApi` calls. Where 
DLF OpenAPI (DlfNext `2026-01-18`) registers an API, the constant uses the 
registered name. Three of those differ from the operationIds in 
`rest-catalog-open-api.yaml`: `RollbackToSnapshot`, `FastForwardBranch` and 
`DropTag`. The management APIs follow the registered names too, e.g. 
`GrantPermissionAssignment` and `ListPolicies`.
   - Every `RESTApi` call passes its name via 
`restAuthFunction.withApiName(...)`, 79 call sites in total. `RESTAuthFunction` 
records the name on `RESTAuthParameter` (`apiName()`), so the `RESTClient` 
interface and `HttpClient` do not change. `RESTAuthParameter.withApiName` 
copies the already-encoded parameters rather than encoding them again.
   - `DLFRequestSigner` gains `signRequestHeaders(RESTAuthParameter, ...)`, 
whose default delegates to `signHeaders(body, ...)`. `DLFAuthProvider` calls 
it, and `DLFOpenApiV4Signer` overrides it to send `apiName` as `x-acs-action`, 
which is signed like every other `x-acs-*` header.
   - The `default` and `openapi` signers are unchanged. pypaimon gets the same 
change: `rest_api_names.py`, `RESTAuthFunction.with_api_name`, 
`RESTAuthParameter.api_name`, and all 38 calls in `rest_api.py`.
   
   Compared with #9937, there is no path matching, and a name cannot drift from 
its path. The cost is a wider diff in `RESTApi`, and a new API has to pass its 
name.
   
   APIs that the gateway does not register (such as `ListTablesGlobally` or 
`GetTableById`) also get a name. Against a public `dlfnext` endpoint, such 
calls return `InvalidAction.NotFound` whether or not `x-acs-action` is sent, so 
naming them changes nothing there.
   
   ### Tests
   
   - `RESTApiNamesTest` (10 tests): runs every public `RESTApi` method against 
a local HTTP server with the `openapi-v4` signer, and asserts the method, path 
and `x-acs-action` of each request. The expectations match the registered name 
for all 53 registered operations. Changing one call site's name makes it fail.
   - `RESTAuthFunctionTest` (3 tests): `withApiName` tags requests without 
touching the original function, and the parameter copy does not encode 
parameters twice.
   - `DLFOpenApiV4SignerTest` (+3 tests): a known-answer signature for a POST 
with a body and an API name (the same value #9937 pins, and one computed 
independently); no API name means no header; `RESTAuthFunction` + 
`DLFAuthProvider` send `x-acs-action` inside `SignedHeaders`.
   - `DLFRequestSignerTest` (+1 test): the `default` and `openapi` signers 
ignore the name.
   - pypaimon:
     - `rest_api_names_test.py` covers all 38 `RESTApi` calls the same way
     - `dlf_signer_test.py` gains 7 tests, pinning the same known-answer string
   - `org.apache.paimon.rest.**` in paimon-api: 162 pass. In paimon-core, 
`MockRESTCatalogTest`, `HttpClientTest`, `AuthProviderTest`, 
`DLFAuthSignatureTest` and `DLFAuthProviderFactoryTest`: 245 pass. pypaimon 
REST tests: 330 pass; the one failure and one error need the optional `lance` 
and `ray` modules and fail the same way without this change. Checkstyle, 
spotless, flake8 and the license check are clean.
   - Against a gateway that requires the header, the Java client ran end to 
end: create database, create table, paged list with a `%` pattern, alter table, 
write, commit, read back, drop. pypaimon passed the metadata calls, including 
`AuthTableQuery`, `ListPartitions`, `ListTags` and `ListBranches`.
   - Against a public `dlfnext` endpoint in cn-hangzhou, the same Java 
end-to-end run and pypaimon calls pass.
   


-- 
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