Update CatalogHandler and PolarisAdminServier to authorize using AuthorizationIntent - #5194
Conversation
There was a problem hiding this comment.
Pull request overview
This draft PR migrates service handlers and authorizer implementations from the legacy authorizeOrThrow(...) APIs to the newer intent-based authorize(AuthorizationState, AuthorizationRequest) SPI, so each PolarisAuthorizer can decide what it needs to resolve for an authorization decision.
Changes:
- Updates handler authorization call sites (catalog/admin/policy) to build
AuthorizationRequestobjects withAuthorizationIntentand useAuthorizationDecisionresults. - Extends core auth primitives: deprecates legacy
authorizeOrThrowoverloads and addsAuthorizationDecision.throwIfDenied(). - Implements/updates intent-based authorization behavior in extension authorizers (notably Ranger) and adjusts unit tests accordingly.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| runtime/service/src/testFixtures/java/org/apache/polaris/service/TestServices.java | Updates test fixture stubbing for the new authorize(...) return type. |
| runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogHandlerTest.java | Refactors mocks/assertions to validate intent-based AuthorizationRequest calls. |
| runtime/service/src/test/java/org/apache/polaris/service/admin/PolarisAdminServiceTest.java | Updates admin service tests to stub authorize(...) decisions. |
| runtime/service/src/main/java/org/apache/polaris/service/catalog/policy/PolicyCatalogHandler.java | Migrates policy authorization flow to intent-based requests and decisions. |
| runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogHandler.java | Migrates Iceberg catalog authorization paths to the new SPI. |
| runtime/service/src/main/java/org/apache/polaris/service/catalog/common/CatalogHandler.java | Centralizes intent-based authorization helpers for common catalog operations. |
| runtime/service/src/main/java/org/apache/polaris/service/admin/PolarisAdminService.java | Updates admin authorization helpers to use AuthorizationRequest + AuthorizationDecision. |
| polaris-core/src/test/java/org/apache/polaris/core/auth/PolarisAuthorizerImplTest.java | Updates core authorizer tests for intent-based flows and updated internals. |
| polaris-core/src/main/java/org/apache/polaris/core/auth/PolarisAuthorizerImpl.java | Routes intent-based authorization through RBAC logic; deprecates legacy overloads. |
| polaris-core/src/main/java/org/apache/polaris/core/auth/PolarisAuthorizer.java | Deprecates legacy entry points in favor of intent-based authorize(...). |
| polaris-core/src/main/java/org/apache/polaris/core/auth/AuthorizationDecision.java | Adds throwIfDenied() helper for decision-to-exception conversion. |
| extensions/auth/ranger/src/test/java/org/apache/polaris/extension/auth/ranger/RangerPolarisAuthorizerTest.java | Adds/updates tests for Ranger intent-based authorization behavior. |
| extensions/auth/ranger/src/main/java/org/apache/polaris/extension/auth/ranger/RangerPolarisAuthorizer.java | Implements intent-based authorize(...) by resolving targets from intents. |
| extensions/auth/opa/src/test/java/org/apache/polaris/extension/auth/opa/OpaPolarisAuthorizerTest.java | Updates tests to drive OPA authorization through intent-based requests. |
| extensions/auth/opa/src/main/java/org/apache/polaris/extension/auth/opa/OpaPolarisAuthorizer.java | Deprecates legacy overloads and clarifies legacy path serialization comments. |
Suppressed comments (3)
runtime/service/src/main/java/org/apache/polaris/service/catalog/common/CatalogHandler.java:304
- authorizeResolvedRegisterTableOverwriteOrThrow calls authorize() for overwriteRequest/fallbackRequest without ensuring resolveAuthorizationInputs has been invoked for those specific requests. With the intent-based SPI, an authorizer may populate AuthorizationState based on the request, so each request passed to authorize() should be resolved first.
if (tableTarget != null) {
authorizer().authorize(authorizationState, overwriteRequest).throwIfDenied();
} else {
runtime/service/src/main/java/org/apache/polaris/service/catalog/common/CatalogHandler.java:369
- authorizeResolvedBasicTableLikeOperationOrThrow constructs a new AuthorizationRequest for authorize() but never resolves it. This can break authorizer implementations that rely on resolveAuthorizationInputs to populate AuthorizationState based on the request/intents.
authorizer()
.authorize(
authorizationState,
new AuthorizationRequest(
runtime/service/src/main/java/org/apache/polaris/service/catalog/common/CatalogHandler.java:406
- authorizeBasicTableLikeOperationsOrThrow loops over ops and calls authorize() on per-op AuthorizationRequests without resolving them. To keep the resolve/authorize contract intact and avoid redundant per-op calls, build a single AuthorizationRequest with one intent per operation, resolve it once, then authorize once (intents are AND-combined per PolarisAuthorizer contract).
for (PolarisAuthorizableOperation op : ops) {
AuthorizationRequest authorizationRequest =
new AuthorizationRequest(
polarisPrincipal(),
List.of(
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| op, | ||
| target, | ||
| null /* secondary */); | ||
| authorizer().authorize(authorizationState, authorizationRequest).throwIfDenied(); |
|
@sungwy : Is this still a draft PR (title / description)? |
|
@sungwy : as for CI, a rebase or simple force push should help |
# Conflicts: # polaris-core/src/test/java/org/apache/polaris/core/auth/PolarisAuthorizerImplTest.java # runtime/service/src/test/java/org/apache/polaris/service/admin/PolarisAdminServiceTest.java
65889b5 to
81db7f2
Compare
|
Thanks for the detailed reviews @dimas-b @flyrain and @gracechen09 - could I ask for your reviews on this PR? |
@sungwy , just back from the vacation. Sorry for the delay. Will take a look soon. |
flyingImer
left a comment
There was a problem hiding this comment.
I'm aligned with moving handlers to the intent-based SPI, but I don't think this is merge-ready. Details inline.
| // Reuse the already-resolved table view for the read-delegation fallback. | ||
| authorizeResolvedBasicTableLikeOperationOrThrow( | ||
| read, PolarisEntitySubType.ICEBERG_TABLE, tableIdentifier); | ||
| authorizationState, read, PolarisEntitySubType.ICEBERG_TABLE, tableIdentifier); |
There was a problem hiding this comment.
We authorize "read" here but the resolution was done on a "write" request. Practically, this is not significant, since only entity references matter for resolution, but it would be nice to make it explicit in the AuthZ SPI.... meaning that resolution should not have the operation itself as a formal parameter.
This is just a point for follow-up thinking / PR. Not a blocker 🙂
There was a problem hiding this comment.
yes, that's right and I think unlike the AuthorizationRequest, it's important that we share the AuthorizationState, since we want to read or write on the same resolved PolarisSecurable.
|
sorry to ask for your approval again @dimas-b - there was a merge conflict on |
OPA -> PolarisAuthorizer
flyingImer
left a comment
There was a problem hiding this comment.
No blocking concerns from my side. Good to merge once CI is green.
…splayName tweak (post-merge) Merging upstream/main pulled in apache#5194 (Update CatalogHandler and PolarisAdminServier to authorize using AuthorizationIntent), which removed PolarisAuthorizer.authorizeOrThrow entirely. Migrate MetricsReportsService.resolveAndAuthorizeTableMetrics to build one AuthorizationRequest of SingleTargetAuthorizationIntent(LIST_TABLE_METRICS, ...) per table, call resolveAuthorizationInputs once, do the existing not-found checks against the manifest, then authorize(...).throwIfDenied() once for the whole batch (AND-combined, short-circuits on first deny) -- mirroring CatalogHandler.authorizeBatchTableLikeOperationOrThrow. Update MetricsReportsServiceTest's mocks to the authorize()/AuthorizationDecision style used elsewhere post-migration. Also revert the dev_polaris.json/polaris-ranger-servicedef.json "Polaris (draft)" -> "Apache Polaris" displayName tweak from the prior commit: unrelated to metrics, flagged in review (apache#4115 (comment)). spotlessApply also reflowed the TABLE_READ_METRICS javadoc comment in PolarisPrivilege.java by a couple of characters (newer google-java-format line-wrap width); included since it touches PR1's own new javadoc. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Awesome. thanks for the reviews @dimas-b @flyingImer and @flyrain |
|
Thanks a lot for the changes, @sungwy ! |
Resolves the conflict introduced by apache#5194, which removed the three authorizeOrThrow methods from PolarisAuthorizer. Kept this branch's batch authorize(AuthorizationState, List<AuthorizationRequest>) and dropped the removed methods. apache#5194 also required fixes that git auto-merged cleanly but which do not compile, all caused by overload ambiguity between the single-request and batch authorize: - PolarisAuthorizerTest: dropped the two now-obsolete authorizeOrThrow stubs from RecordingAuthorizer, plus three unused imports. - TestServices, PolarisAdminServiceTest, IcebergCatalogHandlerTest, SemanticModelCatalogHandlerAuthzTest, AbstractSemanticModelCatalogHandlerTest: disambiguated Mockito matchers -- any() -> any(AuthorizationRequest.class), and typed the argThat lambdas. apache#5194 did not touch the list methods, so entity-level filtering is unaffected. Verified: all affected modules compile, spotless clean, and IcebergCatalogHandlerAuthzTest (6929) plus IcebergCatalogHandlerTest (20) pass with all 8 filtering tests intact. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…splayName tweak (post-merge) Merging upstream/main pulled in apache#5194 (Update CatalogHandler and PolarisAdminServier to authorize using AuthorizationIntent), which removed PolarisAuthorizer.authorizeOrThrow entirely. Migrate MetricsReportsService.resolveAndAuthorizeTableMetrics to build one AuthorizationRequest of SingleTargetAuthorizationIntent(LIST_TABLE_METRICS, ...) per table, call resolveAuthorizationInputs once, do the existing not-found checks against the manifest, then authorize(...).throwIfDenied() once for the whole batch (AND-combined, short-circuits on first deny) -- mirroring CatalogHandler.authorizeBatchTableLikeOperationOrThrow. Update MetricsReportsServiceTest's mocks to the authorize()/AuthorizationDecision style used elsewhere post-migration. Also revert the dev_polaris.json/polaris-ranger-servicedef.json "Polaris (draft)" -> "Apache Polaris" displayName tweak from the prior commit: unrelated to metrics, flagged in review (apache#4115 (comment)). spotlessApply also reflowed the TABLE_READ_METRICS javadoc comment in PolarisPrivilege.java by a couple of characters (newer google-java-format line-wrap width); included since it touches PR1's own new javadoc.
Update the handler call sites to use the new
authorizemethod usingAuthorizationIntent.This will allow each
PolarisAuthorizerimplementations to decide whichPolarisEntityneeds to be resolved for its authorization decision, allowing them to ignore RBAC resolution by choice.Checklist
CHANGELOG.md(if needed)site/content/in-dev/unreleased(if needed)