Skip to content

[#11775] improvement(authz): Short-circuit list authorization via parent-scope grant#11778

Open
yuqi1129 wants to merge 1 commit into
mainfrom
improvement-list-authz-schema-short-circuit-11775
Open

[#11775] improvement(authz): Short-circuit list authorization via parent-scope grant#11778
yuqi1129 wants to merge 1 commit into
mainfrom
improvement-list-authz-schema-short-circuit-11775

Conversation

@yuqi1129

Copy link
Copy Markdown
Contributor

What changes were proposed in this pull request?

When authorization is enabled, list operations evaluated one per-object permission check per result, producing an N+1 explosion. This PR evaluates the ancestor-only portion of the filter expression once per list request: if a privilege is granted (or the object is owned) at a parent scope (metalake/catalog/schema), every child is visible unless an object-level deny overrides it.

  • New parent-scope expressions in AuthorizationExpressionConstants for TABLE/SCHEMA/CATALOG.
  • MetadataAuthzHelper.filterByExpression(…, NameIdentifier[]) attempts the short-circuit before the per-object loop. One insertion point covers both Gravitino REST (listTables/listSchemas/listCatalogs) and Iceberg REST (listTable/listNamespace), since they all route through this helper.
  • New SPI method GravitinoAuthorizer#hasDenyPolicyOnType (conservative default true); JcasbinAuthorizer implements it as an in-memory scan of the current user's deny-enforcer policies. The short-circuit is taken only when no object-level deny may exist for the relevant type/privileges, otherwise it falls back to the existing per-object filtering.

Why are the changes needed?

Per #11775, with authorization on, listing a schema of ~10,496 tables took ~134s (vs ~4s with auth off) — a ~70x regression — because permission was checked once per table. Owner/grant at a parent scope makes every child visible (OR semantics); only an object-level deny can subtract, so the ancestor part is a loop-invariant that can be lifted out of the per-object loop. Ancestor-level denies are already handled inside the parent-scope expression's !ANY(DENY_..., METALAKE, CATALOG, SCHEMA) terms.

Fix: #11775

Does this PR introduce any user-facing change?

No. The new method on the GravitinoAuthorizer SPI is a backward-compatible default method (default returns true, i.e. no short-circuit), and authorization decisions are unchanged.

How was this patch tested?

New unit tests:

  • TestMetadataAuthzHelper: parent grant + no deny returns the whole list; parent grant + possible deny falls back and excludes the denied table; schema list short-circuits via a catalog-level grant; catalog list short-circuits via a metalake-level grant.
  • TestJcasbinAuthorizer#testHasDenyPolicyOnType: detects an object-level deny for the matching type/privilege and ignores non-matching type/privilege.

Full :server-common test suite passes; :server, :iceberg:iceberg-rest-server, and :core compile clean.

…ent-scope grant

When authorization is enabled, list operations (listTables/listSchemas/listCatalogs
on both Gravitino REST and Iceberg REST) ran one per-object permission check per
result, causing an N+1 explosion (e.g. ~10k tables took ~134s vs ~4s with auth off).

Evaluate the ancestor-only portion of the filter expression once per list request:
when a privilege is granted (or the object is owned) at a parent scope
(metalake/catalog/schema) every child is visible unless an object-level deny
overrides it. A new GravitinoAuthorizer#hasDenyPolicyOnType gate (in-memory scan of
the user's deny policies in JcasbinAuthorizer; conservative default of true) makes the
short-circuit safe, falling back to per-object filtering only when a deny may exist.
Copilot AI review requested due to automatic review settings June 23, 2026 12:59
@yuqi1129 yuqi1129 self-assigned this Jun 23, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR optimizes authorization for large list operations (tables/schemas/catalogs) by safely short-circuiting per-object checks when an ancestor-scope grant (or ownership) implies all children are visible and no object-level deny can apply, addressing the N+1 performance issue described in #11775.

Changes:

  • Added parent-scope authorization expressions for TABLE/SCHEMA/CATALOG list operations.
  • Enhanced MetadataAuthzHelper.filterByExpression(..., NameIdentifier[]) to attempt a one-time parent-scope evaluation and skip per-object filtering when safe.
  • Extended the GravitinoAuthorizer SPI with hasDenyPolicyOnType(...) and implemented it in JcasbinAuthorizer, plus added unit tests covering both the short-circuit and deny detection behavior.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
server-common/src/main/java/org/apache/gravitino/server/authorization/MetadataAuthzHelper.java Adds list short-circuit registry and parent-scope evaluation to avoid per-object auth checks when safe.
server-common/src/main/java/org/apache/gravitino/server/authorization/jcasbin/JcasbinAuthorizer.java Implements deny-policy presence detection by scanning deny-enforcer policies for the current user’s roles.
server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationExpressionConstants.java Introduces parent-scope authorization expressions for table/schema/catalog list short-circuiting.
core/src/main/java/org/apache/gravitino/authorization/GravitinoAuthorizer.java Adds new SPI default method hasDenyPolicyOnType(...) (conservative default).
server-common/src/test/java/org/apache/gravitino/server/authorization/TestMetadataAuthzHelper.java Adds unit tests validating short-circuit behavior and fallback when denies may exist.
server-common/src/test/java/org/apache/gravitino/server/authorization/jcasbin/TestJcasbinAuthorizer.java Adds unit test for hasDenyPolicyOnType(...) correctness across type/privilege matches.

@github-actions

Copy link
Copy Markdown

Code Coverage Report

Overall Project 67.18% +0.07% 🟢
Files changed 75.94% 🟢

Module Coverage
aliyun 1.72% 🔴
api 46.82% 🟢
authorization-common 85.96% 🟢
aws 3.66% 🔴
azure 2.47% 🔴
catalog-common 10.4% 🔴
catalog-fileset 80.23% 🟢
catalog-glue 66.91% 🟢
catalog-hive 79.42% 🟢
catalog-jdbc-clickhouse 80.02% 🟢
catalog-jdbc-common 44.22% 🟢
catalog-jdbc-doris 80.28% 🟢
catalog-jdbc-hologres 54.03% 🟢
catalog-jdbc-mysql 79.23% 🟢
catalog-jdbc-oceanbase 80.91% 🟢
catalog-jdbc-postgresql 82.29% 🟢
catalog-jdbc-starrocks 78.51% 🟢
catalog-kafka 77.01% 🟢
catalog-lakehouse-generic 58.53% 🟢
catalog-lakehouse-hudi 79.1% 🟢
catalog-lakehouse-iceberg 85.86% 🟢
catalog-lakehouse-paimon 82.14% 🟢
catalog-model 77.72% 🟢
cli 44.51% 🟢
client-java 78.01% 🟢
common 50.17% 🟢
core 82.59% -0.04% 🟢
filesystem-hadoop3 77.27% 🟢
flink 0.0% 🔴
flink-common 47.12% 🟢
flink-runtime 0.0% 🔴
gcp 14.12% 🔴
hadoop-common 10.88% 🔴
hive-metastore-common 53.77% 🟢
iceberg-common 58.15% 🟢
iceberg-rest-server 73.98% 🟢
idp-basic 85.71% 🟢
integration-test-common 0.0% 🔴
jobs 66.17% 🟢
lance-common 20.83% 🔴
lance-rest-server 60.13% 🟢
lineage 53.02% 🟢
optimizer 82.95% 🟢
optimizer-api 21.95% 🔴
server 86.09% 🟢
server-common 74.54% +1.0% 🟢
spark 28.57% 🔴
spark-common 41.66% 🟢
trino-connector 40.25% 🟢
Files
Module File Coverage
core GravitinoAuthorizer.java 0.0% 🔴
server-common JcasbinAuthorizer.java 84.41% 🟢
MetadataAuthzHelper.java 53.33% 🔴
AuthorizationExpressionConstants.java 0.0% 🔴

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.

[Improvement] List tables with Authorization enabled has N+1 performance issue

2 participants