Route table-like authorization through authorize(state, request) - #5170
ZephyrYWZhou wants to merge 1 commit into
Conversation
|
Tagging @dimas-b for review on this PR as the first preparatory PR of the migration efforts. |
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.
7a4e00b to
c44c7f6
Compare
|
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 The resolution logic already existed in |
|
Thanks for working on it, @ZephyrYWZhou . Here is a related PR, #5194. We may need some consolidation. cc @sungwy |
|
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 |
We can file followup PRs if any improvement is needed. |
Sounds good that makes sense to me. |
|
Can we also close this if it is not needed any more? Thanks! |
Summary
This is the first preparatory PR to migrate
authorizeOrThrowtoauthorize. It migratesCatalogHandler.authorizeResolvedBasicTableLikeOperationOrThrowto obtain its authorization decision from the decision-nativePolarisAuthorizer.authorize(AuthorizationState, AuthorizationRequest)path (via the throwingauthorizeOrThrow(state, request)convenience wrapper) instead of the legacy resolved-pathauthorizeOrThrow(principal, activatedEntities, op, target, secondary)overload.Why this method first
authorizeResolvedBasicTableLikeOperationOrThrowis the most widely used table-like authorization method —loadTable,loadCredentials,loadView,dropTable,commitView, and others all flow through it (directly or viaresolveAndAuthorizeBasicTableLikeOperationOrThrow). The resolution manifest is already populated byresolveBasicTableLikeTargetOrThrowbefore 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—authorizeResolvedBasicTableLikeOperationOrThrownow builds anAuthorizationRequest(singleSingleTargetAuthorizationIntent) and callsauthorizer().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 theAuthorizationIntent→ resolved target/secondary path resolution out ofPolarisAuthorizerImplinto a shared helper in theorg.apache.polaris.core.authpackage. It lives in that package so it can consult the package-privateRbacOperationSemanticsfor root-container rooting, giving every authorizer (built-in and pluggable) one source of truth for resolving anAuthorizationRequest's securables against the resolution manifest.PolarisAuthorizerImpl.java—authorizeIntentnow delegates securable resolution toAuthorizationIntentResolver(behavior-preserving; the moved private helpers are removed).RangerPolarisAuthorizer.java— implementsauthorize(state, request), which previously threwUnsupportedOperationException. It resolves each intent viaAuthorizationIntentResolverand drives the existing resolved-path Ranger authorization logic, translating aForbiddenExceptioninto anAuthorizationDecision. Without this, routingdropTable(and other table-like ops) throughauthorize(state, request)failed under the Ranger authorizer.IcebergCatalogHandlerTest.java— updatedloadCredentialsFallbackResolvesOnceThenAuthorizesReadDelegationto stub/verify the decision-nativeauthorizeOrThrow(state, request)path (matched by the request intent's operation) instead of the legacy overload; added a smallrequestWithOperation(.)argument-matcher helper.Behavior
No behavioral change. Every table-like operation that flows through this method still throws
ForbiddenExceptionon denial (the wrapper delegates toauthorize(.)and throws when not allowed), andloadTable/loadCredentialskeep their existing write-delegation-probe → read-delegation-fallback control flow.Testing
IcebergCatalogHandlerTest(mock-based) passes.PolarisAuthorizerImplauthz suites pass unchanged:IcebergCatalogHandlerAuthzTest(6921),IcebergCatalogHandlerFineGrainedDisabledTest(107),PolarisGenericTableCatalogHandlerAuthzTest(538),PolicyCatalogHandlerAuthzTest(1518),IcebergCatalogHandlerTest(12) — 0 failures.RangerIcebergCatalogHandlerITandRangerGenericTableHandlerIT(:polaris-extensions-auth-ranger:intTest) — 0 failures.Checklist
IcebergCatalogHandlerauthorize()path