This is an automated email from the ASF dual-hosted git repository.

jerryshao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/main by this push:
     new b49d895eaf [MINOR] improvement(authz): Add debug logging for 
list-authorization short-circuit (#12101)
b49d895eaf is described below

commit b49d895eaf358bdbeafef1ca9e702652514a3055
Author: Qi Yu <[email protected]>
AuthorDate: Mon Jul 20 19:27:43 2026 +0800

    [MINOR] improvement(authz): Add debug logging for list-authorization 
short-circuit (#12101)
    
    ### What changes were proposed in this pull request?
    
    Adds `DEBUG`-level logging around the list-authorization parent-scope
    short-circuit (`MetadataAuthzHelper.filterByExpression` /
    `allVisibleViaParentScope`, introduced in #11778) so it is possible to
    tell, per request and per principal, whether the short-circuit fired and
    — when it did not — exactly which condition disqualified it:
    
    - **HIT**: every listed object is returned via a parent-scope grant and
    the per-object authorization loop is skipped.
    - **MISS / skipped**, with the reason: no short-circuit spec registered
    / filter-expression mismatch, listed objects span multiple parent
    namespaces, the principal has no parent-scope grant, or a deny policy
    exists on the relevant privileges.
    
    Every log line now includes the current principal, the entity type, and
    the metalake.
    
    This is purely diagnostic — no behavior change.
    
    ### Why are the changes needed?
    
    When a `list` call (e.g. `list catalogs`) is slow, there is currently no
    way to observe whether the #11778 short-circuit applied. The `verbose`
    (`details=true`) path and finer-grained or denied grants fall back to
    per-object authorization; these logs make that visible and greatly speed
    up diagnosing slow-list reports.
    
    Fix: N/A (minor diagnostic improvement)
    
    ### Does this PR introduce _any_ user-facing change?
    
    No. Debug logging only.
    
    ### How was this patch tested?
    
    No behavior change (only added `LOG.debug` calls and an extracted
    `hasDeny` local; control flow is unchanged). Existing tests cover the
    short-circuit logic. Verified `:server-common:compileJava` succeeds.
---
 .../server/authorization/MetadataAuthzHelper.java  | 70 +++++++++++++++++++---
 1 file changed, 61 insertions(+), 9 deletions(-)

diff --git 
a/server-common/src/main/java/org/apache/gravitino/server/authorization/MetadataAuthzHelper.java
 
b/server-common/src/main/java/org/apache/gravitino/server/authorization/MetadataAuthzHelper.java
index 6e98f7e455..fc7db7b615 100644
--- 
a/server-common/src/main/java/org/apache/gravitino/server/authorization/MetadataAuthzHelper.java
+++ 
b/server-common/src/main/java/org/apache/gravitino/server/authorization/MetadataAuthzHelper.java
@@ -206,13 +206,29 @@ public class MetadataAuthzHelper {
       String expression,
       Entity.EntityType entityType,
       NameIdentifier[] nameIdentifiers) {
-    if (enableAuthorization()
-        && nameIdentifiers.length > 0
-        && allVisibleViaParentScope(metalake, expression, entityType, 
nameIdentifiers)) {
-      // A privilege granted at a parent scope (metalake/catalog/schema) makes 
every object in the
-      // list visible, and no object-level deny exists, so the per-object 
authorization loop is
-      // skipped entirely. See 
AuthorizationExpressionConstants.*_LIST_PARENT_SCOPE_*.
-      return nameIdentifiers;
+    if (enableAuthorization() && nameIdentifiers.length > 0) {
+      String principalName = PrincipalUtils.getCurrentPrincipal().getName();
+      if (allVisibleViaParentScope(metalake, expression, entityType, 
nameIdentifiers)) {
+        // A privilege granted at a parent scope (metalake/catalog/schema) 
makes every object in
+        // the list visible, and no object-level deny exists, so the 
per-object authorization loop
+        // is skipped entirely. See 
AuthorizationExpressionConstants.*_LIST_PARENT_SCOPE_*.
+        LOG.debug(
+            "List authorization short-circuit HIT for principal {}, entity 
type {} under metalake "
+                + "{}: all {} listed object(s) are visible via a parent-scope 
grant; skipping the "
+                + "per-object authorization loop.",
+            principalName,
+            entityType,
+            metalake,
+            nameIdentifiers.length);
+        return nameIdentifiers;
+      }
+      LOG.debug(
+          "List authorization short-circuit MISS for principal {}, entity type 
{} under metalake "
+              + "{} ({} object(s)); falling back to the per-object 
authorization loop.",
+          principalName,
+          entityType,
+          metalake,
+          nameIdentifiers.length);
     }
     preloadToCache(entityType, nameIdentifiers);
     preloadOwner(entityType, nameIdentifiers);
@@ -230,8 +246,19 @@ public class MetadataAuthzHelper {
       String expression,
       Entity.EntityType entityType,
       NameIdentifier[] nameIdentifiers) {
+    Principal principal = PrincipalUtils.getCurrentPrincipal();
     ListShortCircuit spec = LIST_SHORT_CIRCUITS.get(entityType);
     if (spec == null || !spec.filterExpression.equals(expression)) {
+      LOG.debug(
+          "Parent-scope short-circuit unavailable for principal {}, entity 
type {} under metalake "
+              + "{}: {}.",
+          principal.getName(),
+          entityType,
+          metalake,
+          spec == null
+              ? "no short-circuit spec is registered for this entity type"
+              : "the requested filter expression does not match the registered 
short-circuit "
+                  + "expression");
       return false;
     }
 
@@ -239,11 +266,18 @@ public class MetadataAuthzHelper {
     Namespace parent = nameIdentifiers[0].namespace();
     for (NameIdentifier ident : nameIdentifiers) {
       if (!ident.namespace().equals(parent)) {
+        LOG.debug(
+            "Parent-scope short-circuit skipped for principal {}, entity type 
{} under metalake "
+                + "{}: listed objects span multiple parent namespaces (e.g. {} 
vs {}).",
+            principal.getName(),
+            entityType,
+            metalake,
+            parent,
+            ident.namespace());
         return false;
       }
     }
 
-    Principal principal = PrincipalUtils.getCurrentPrincipal();
     GravitinoAuthorizer authorizer =
         GravitinoAuthorizerProvider.getInstance().getGravitinoAuthorizer();
     AuthorizationRequestContext requestContext = new 
AuthorizationRequestContext();
@@ -255,13 +289,31 @@ public class MetadataAuthzHelper {
         new AuthorizationExpressionEvaluator(spec.parentScopeExpression, 
authorizer)
             .evaluate(metadataNames, requestContext, principal, 
Optional.empty());
     if (!parentGrantsAccess) {
+      LOG.debug(
+          "Parent-scope short-circuit skipped for entity type {} under 
metalake {}: principal {} "
+              + "is not granted access at the parent scope, so per-object 
authorization is "
+              + "required.",
+          entityType,
+          metalake,
+          principal.getName());
       return false;
     }
 
     // Parent scope grants access to every object; the only thing that can 
still hide one is a
     // deny on these privileges (at the parent scope or on an individual 
object), so the
     // short-circuit is only safe when no such deny may exist.
-    return !authorizer.hasDenyPolicy(principal, metalake, spec.denyPrivileges, 
requestContext);
+    boolean hasDeny =
+        authorizer.hasDenyPolicy(principal, metalake, spec.denyPrivileges, 
requestContext);
+    if (hasDeny) {
+      LOG.debug(
+          "Parent-scope short-circuit disabled for entity type {} under 
metalake {}: principal {} "
+              + "holds a deny policy on {}, so per-object authorization is 
required.",
+          entityType,
+          metalake,
+          principal.getName(),
+          spec.denyPrivileges);
+    }
+    return !hasDeny;
   }
 
   /**

Reply via email to