Skip to content

Route table-like authorization through authorize(state, request) - #5170

Closed
ZephyrYWZhou wants to merge 1 commit into
apache:mainfrom
ZephyrYWZhou:migrate-authz-tablelike-to-authorize
Closed

ZephyrYWZhou wants to merge 1 commit into
apache:mainfrom
ZephyrYWZhou:migrate-authz-tablelike-to-authorize

Conversation

@ZephyrYWZhou

@ZephyrYWZhou ZephyrYWZhou commented Jul 28, 2026 •

Copy link
Copy Markdown
Contributor

Summary

This is the first preparatory PR to migrate authorizeOrThrow to authorize. It migrates CatalogHandler.authorizeResolvedBasicTableLikeOperationOrThrow to obtain its authorization decision from the decision-native PolarisAuthorizer.authorize(AuthorizationState, AuthorizationRequest) path (via the throwing authorizeOrThrow(state, request) convenience wrapper) instead of the legacy resolved-path authorizeOrThrow(principal, activatedEntities, op, target, secondary) overload.

Why this method first

authorizeResolvedBasicTableLikeOperationOrThrow is the most widely used table-like authorization method — loadTable, loadCredentials, loadView, dropTable, commitView, and others all flow through it (directly or via resolveAndAuthorizeBasicTableLikeOperationOrThrow). The resolution manifest is already populated by resolveBasicTableLikeTargetOrThrow before this method runs, so the authorizer re-resolves the same table-like securable from the request intent with no manifest changes required. This makes it the safest, highest-leverage starting point.

Changes

  • CatalogHandler.java — authorizeResolvedBasicTableLikeOperationOrThrow now builds an AuthorizationRequest (single SingleTargetAuthorizationIntent) and calls authorizer().authorizeOrThrow(state, request). The existing null-check guard is retained so a missing entity still surfaces as a not-found (404) response rather than a server error from the authorizer's re-resolution.
  • AuthorizationIntentResolver.java (new) — extracts the AuthorizationIntent → resolved target/secondary path resolution out of PolarisAuthorizerImpl into a shared helper in the org.apache.polaris.core.auth package. It lives in that package so it can consult the package-private RbacOperationSemantics for root-container rooting, giving every authorizer (built-in and pluggable) one source of truth for resolving an AuthorizationRequest's securables against the resolution manifest.
  • PolarisAuthorizerImpl.java — authorizeIntent now delegates securable resolution to AuthorizationIntentResolver (behavior-preserving; the moved private helpers are removed).
  • RangerPolarisAuthorizer.java — implements authorize(state, request), which previously threw UnsupportedOperationException. It resolves each intent via AuthorizationIntentResolver and drives the existing resolved-path Ranger authorization logic, translating a ForbiddenException into an AuthorizationDecision. Without this, routing dropTable (and other table-like ops) through authorize(state, request) failed under the Ranger authorizer.
  • IcebergCatalogHandlerTest.java — updated loadCredentialsFallbackResolvesOnceThenAuthorizesReadDelegation to stub/verify the decision-native authorizeOrThrow(state, request) path (matched by the request intent's operation) instead of the legacy overload; added a small requestWithOperation(.) argument-matcher helper.

Behavior

No behavioral change. Every table-like operation that flows through this method still throws ForbiddenException on denial (the wrapper delegates to authorize(.) and throws when not allowed), and loadTable/loadCredentials keep their existing write-delegation-probe → read-delegation-fallback control flow.

Testing

  • IcebergCatalogHandlerTest (mock-based) passes.
  • Real-PolarisAuthorizerImpl authz suites pass unchanged: IcebergCatalogHandlerAuthzTest (6921), IcebergCatalogHandlerFineGrainedDisabledTest (107), PolarisGenericTableCatalogHandlerAuthzTest (538), PolicyCatalogHandlerAuthzTest (1518), IcebergCatalogHandlerTest (12) — 0 failures.
  • Ranger extension integration tests pass: RangerIcebergCatalogHandlerIT and RangerGenericTableHandlerIT (:polaris-extensions-auth-ranger:intTest) — 0 failures.

Checklist

  • 🛡️ Not a security issue
  • 🔗 Resolves the existing TODO in IcebergCatalogHandler
  • 🧪 Updated existing tests to cover the new authorize() path
  • 💡 Added Javadoc for the new helper
  • 🧾 No CHANGELOG update required (internal refactoring)
  • 📚 No documentation update required

@ZephyrYWZhou

Copy link
Copy Markdown
Contributor Author

Tagging @dimas-b for review on this PR as the first preparatory PR of the migration efforts.

@ZephyrYWZhou

Copy link
Copy Markdown
Contributor Author

Kindly bump this for a CR review @dimas-b @flyrain - this is the first CR of a series to migrate from authorizeOrThrow to authorize. Full detail of my migration plan is here. Please let me know if there are any questions or further discussion.

Migrate authorizeResolvedBasicTableLikeOperationOrThrow in CatalogHandler
to obtain its authorization decision from the decision-native
PolarisAuthorizer.authorize(AuthorizationState, AuthorizationRequest) path
(via the throwing authorizeOrThrow(state, request) convenience wrapper)
instead of the legacy resolved-path authorizeOrThrow overload. This is a
small, behavior-preserving preparatory step toward migrating all callers off
the legacy authorizeOrThrow overloads so those overloads can be removed. The
resolution manifest is already populated by resolveBasicTableLikeTargetOrThrow,
so the authorizer re-resolves the same table-like securable from the request
intent. The existing null-check guard is retained so a missing entity still
surfaces as a not-found (404) response rather than a server error from the
authorizer's re-resolution.

All table-like operations that flow through this method (loadTable,
loadCredentials, loadView, dropTable, etc.) still throw ForbiddenException on
denial, so behavior is unchanged.

Update IcebergCatalogHandlerTest#loadCredentialsFallbackResolvesOnceThenAuthorizesReadDelegation
to stub/verify the decision-native authorizeOrThrow(state, request) path
(matching the request intent's operation) instead of the legacy overload.

Implement the decision-native path for the pluggable authorizers so this
migration does not regress them:

- Extract the intent -> resolved target/secondary path resolution out of
  PolarisAuthorizerImpl.authorizeIntent into a new shared helper,
  AuthorizationIntentResolver, in the org.apache.polaris.core.auth package.
  It lives in that package so it can consult the package-private
  RbacOperationSemantics to decide root-container rooting, giving every
  authorizer (built-in and extension) one source of truth for resolving an
  AuthorizationRequest's securables against the resolution manifest.
- Implement RangerPolarisAuthorizer.authorize(state, request), which
  previously threw UnsupportedOperationException. It resolves each intent via
  AuthorizationIntentResolver and drives the existing resolved-path Ranger
  authorization logic (authorizeOrThrow(principal, activatedEntities, op,
  targets, secondaries)), translating a ForbiddenException into an
  AuthorizationDecision. Without this, routing dropTable (and other table-like
  ops) through authorize(state, request) failed under the Ranger authorizer,
  breaking RangerIcebergCatalogHandlerIT and RangerGenericTableHandlerIT.
@ZephyrYWZhou
ZephyrYWZhou force-pushed the migrate-authz-tablelike-to-authorize branch from 7a4e00b to c44c7f6 Compare August 18, 2026 02:07
@ZephyrYWZhou

Copy link
Copy Markdown
Contributor Author

The CI/CD build passes @flyrain, feel free to take a look at the code to see if it is good to go.

One key component I’d especially like you to take a loot at is AuthorizationIntentResolver. It’s a shared resolver that translates an AuthorizationIntent into the concrete entity paths that the authorization logic needs to make a decision.

The resolution logic already existed in PolarisAuthorizerImpl.authorizeIntent as private methods, but it depends on the package-private RbacOperationSemantics, which means an extension package like Ranger couldn’t reuse it. I lifted the logic as-is into a public helper in the same org.apache.polaris.core.auth package, so it can be shared across the package.

@flyrain
flyrain requested review from dimas-b and sungwy August 18, 2026 21:29
@ZephyrYWZhou

Copy link
Copy Markdown
Contributor Author

Kindly tagging @sungwy @dimas-b for visiability on the PR review.

@flyrain

flyrain commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Thanks for working on it, @ZephyrYWZhou . Here is a related PR, #5194. We may need some consolidation. cc @sungwy

@ZephyrYWZhou

Copy link
Copy Markdown
Contributor Author

Thanks @flyrain, looks like #5194 is the broader migration which supersedes this PR. Feel free to let me know if we want to defer to it.

One thing about #5194 I noticed is that each authorizer resolve intents independently, which I really like for letting an authorizer resolve only what it needs. The tradeoff is the intent→resolved-path logic is now duplicated across authorizers. Have we considered pulling it into a shared AuthorizationIntentResolver in core.auth for dedup purpose? If there is anything I can help with, feel free to let me know. @sungwy

@flyrain

flyrain commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

One thing about #5194 I noticed is that each authorizer resolve intents independently, which I really like for letting an authorizer resolve only what it needs. The tradeoff is the intent→resolved-path logic is now duplicated across authorizers. Have we considered pulling it into a shared AuthorizationIntentResolver in core.auth for dedup purpose? If there is anything I can help with, feel free to let me know.

We can file followup PRs if any improvement is needed.

@ZephyrYWZhou

Copy link
Copy Markdown
Contributor Author

One thing about #5194 I noticed is that each authorizer resolve intents independently, which I really like for letting an authorizer resolve only what it needs. The tradeoff is the intent→resolved-path logic is now duplicated across authorizers. Have we considered pulling it into a shared AuthorizationIntentResolver in core.auth for dedup purpose? If there is anything I can help with, feel free to let me know.

We can file followup PRs if any improvement is needed.

Sounds good that makes sense to me.

@flyrain

flyrain commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Can we also close this if it is not needed any more? Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants