Overview
PolarisAuthorizer exposes a decision-native entry point, authorize(AuthorizationState, AuthorizationRequest), and a thin throwing convenience wrapper, authorizeOrThrow(AuthorizationState, AuthorizationRequest). However, most service-layer call sites still use the legacy resolved-path overloads:
void authorizeOrThrow(PolarisPrincipal, Set<PolarisBaseEntity>, PolarisAuthorizableOperation,
PolarisResolvedPathWrapper target, PolarisResolvedPathWrapper secondary);
void authorizeOrThrow(PolarisPrincipal, Set<PolarisBaseEntity>, PolarisAuthorizableOperation,
List<PolarisResolvedPathWrapper> targets, List<PolarisResolvedPathWrapper> secondaries);
Per the discussion in #5034, authorize(state, request) is intended to be the main method. This issue tracks migrating all callers to the decision-native path and removing the legacy overloads.
Goal / Scope
- Move every caller of the legacy
authorizeOrThrow(principal, activatedEntities, op, target(s), secondary(ies)) overloads to authorize(state, request) / authorizeOrThrow(state, request).
- Keep
authorizeOrThrow(AuthorizationState, AuthorizationRequest) (the thin throwing wrapper) — most call sites just want to throw on denial.
- Remove the two legacy resolved-path overloads from
PolarisAuthorizer, PolarisAuthorizerImpl, and the Ranger/OPA implementations once no callers remain.
Known blocker (why this needs prep work)
The two paths resolve entities differently. The legacy overloads take an already-resolved PolarisResolvedPathWrapper, whereas authorize(state, request) re-derives each securable from the request intents by looking them up in the resolution manifest (PolarisAuthorizerImpl.getResolvedSecurable() → getResolvedTopLevelEntity(name, type) for top-level entities, getResolvedPath(...) for paths).
Call sites that resolved their target the legacy way but never registered the securable's name+type in the manifest will fail re-resolution with:
IllegalStateException: never_registered_top_level_name_and_type_for_resolved_entity
So this is not a mechanical find-and-replace: each call site must first populate the resolution manifest so re-resolution by name/type succeeds. This is most impactful for top-level entities (catalog / principal / principal-role / catalog-role) and secondary (grant) targets.
Approximate call-site inventory
CatalogHandler — ~9 sites (namespace + table-like, path-based; lowest risk)
PolicyCatalogHandler — ~3 sites (path-based)
PolarisAdminService — ~10 sites (top-level + grant/secondary targets; highest risk)
Planned PR series
Each non-final PR is behavior-preserving (still throws on denial), so existing authorization tests act as the safety net.
Related: #5034
Overview
PolarisAuthorizerexposes a decision-native entry point,authorize(AuthorizationState, AuthorizationRequest), and a thin throwing convenience wrapper,authorizeOrThrow(AuthorizationState, AuthorizationRequest). However, most service-layer call sites still use the legacy resolved-path overloads:Per the discussion in #5034,
authorize(state, request)is intended to be the main method. This issue tracks migrating all callers to the decision-native path and removing the legacy overloads.Goal / Scope
authorizeOrThrow(principal, activatedEntities, op, target(s), secondary(ies))overloads toauthorize(state, request)/authorizeOrThrow(state, request).authorizeOrThrow(AuthorizationState, AuthorizationRequest)(the thin throwing wrapper) — most call sites just want to throw on denial.PolarisAuthorizer,PolarisAuthorizerImpl, and the Ranger/OPA implementations once no callers remain.Known blocker (why this needs prep work)
The two paths resolve entities differently. The legacy overloads take an already-resolved
PolarisResolvedPathWrapper, whereasauthorize(state, request)re-derives each securable from the request intents by looking them up in the resolution manifest (PolarisAuthorizerImpl.getResolvedSecurable()→getResolvedTopLevelEntity(name, type)for top-level entities,getResolvedPath(...)for paths).Call sites that resolved their target the legacy way but never registered the securable's name+type in the manifest will fail re-resolution with:
So this is not a mechanical find-and-replace: each call site must first populate the resolution manifest so re-resolution by name/type succeeds. This is most impactful for top-level entities (catalog / principal / principal-role / catalog-role) and secondary (grant) targets.
Approximate call-site inventory
CatalogHandler— ~9 sites (namespace + table-like, path-based; lowest risk)PolicyCatalogHandler— ~3 sites (path-based)PolarisAdminService— ~10 sites (top-level + grant/secondary targets; highest risk)Planned PR series
authorizeResolvedBasicTableLikeOperationOrThrow): Route table-like authorization through authorize(state, request) #5170authorizeOrThrow(principal, …)overloadsEach non-final PR is behavior-preserving (still throws on denial), so existing authorization tests act as the safety net.
Related: #5034