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

yuqi1129 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 a98f018253 [MINOR] fix(auth): Handle missing parent catalog during 
metadata id resolution (#12214)
a98f018253 is described below

commit a98f018253d511824eecb4e408fc65f26cd07ed0
Author: jarred0214 <[email protected]>
AuthorDate: Mon Aug 10 20:07:14 2026 +0800

    [MINOR] fix(auth): Handle missing parent catalog during metadata id 
resolution (#12214)
    
    ### What changes were proposed in this pull request?
    
    This PR updates `MetadataIdConverter` to gracefully handle missing
    parent metadata during metadata id resolution.
    
    When resolving metadata ids for objects such as filesets, schemas,
    tables, or topics, `MetadataIdConverter` normalizes the object
    identifier according to the parent catalog's case-sensitivity capability
    before looking up the entity id. If that parent catalog no longer
    exists, the target metadata cannot be resolved, so the converter now
    returns `Optional.empty()` instead of propagating `NotFoundException`.
    
    A unit test was added to cover the missing parent catalog case.
    
    ### Why are the changes needed?
    
    Revoking privileges from a role for a metadata object whose parent
    catalog no longer exists can fail during authorization expression
    evaluation with an internal error.
    
    For example, the failure can look like:
    
    ```text
    System internal error during authorization - Operation: 
revokePrivilegeFromRole
    Caused by: org.apache.gravitino.exceptions.NoSuchCatalogException:
    Catalog <metalake>.<catalog> does not exist
      at org.apache.gravitino.catalog.CatalogManager.loadCatalogInternal
      at org.apache.gravitino.catalog.CapabilityHelpers.getCapability
      at 
org.apache.gravitino.server.authorization.MetadataIdConverter.normalizeCaseSensitive
      at org.apache.gravitino.server.authorization.MetadataIdConverter.getID
      at 
org.apache.gravitino.server.authorization.jcasbin.JcasbinAuthorizationLookups.loadMetadataId
    ```
    
    This happens before the revoke operation reaches the business logic.
    During authorization, the requested metadata object is converted to an
    internal metadata id. For child metadata objects, this conversion first
    loads the parent catalog to apply the correct case-sensitivity
    normalization. If the parent catalog has already been dropped, that
    lookup throws `NoSuchCatalogException`, which currently bubbles up
    through the authorization expression evaluator as an internal error.
    
    Returning `Optional.empty()` is consistent with the existing contract of
    `MetadataIdConverter#getID`: if the metadata object cannot be resolved,
    the converter should return empty. A missing parent catalog means the
    requested child metadata object cannot exist or be resolved in
    Gravitino, so treating it as missing metadata is safer and more accurate
    than failing authorization evaluation with an internal exception.
    
    Fix: #12216
    
    ### Does this PR introduce any user-facing change?
    
    No API or configuration changes.
    
    ### How was this patch tested?
    
    Ran:
    
    ```shell
    ./gradlew :server-common:test \
      --tests org.apache.gravitino.server.authorization.TestMetadataIdConverter 
\
      --tests 
org.apache.gravitino.server.authorization.jcasbin.TestJcasbinAuthorizationLookups
 \
      -PskipITs -PskipDockerTests=false
    ```
---
 .../server/authorization/MetadataIdConverter.java  | 10 ++++++--
 .../authorization/TestMetadataIdConverter.java     | 30 ++++++++++++++++++++++
 2 files changed, 38 insertions(+), 2 deletions(-)

diff --git 
a/server-common/src/main/java/org/apache/gravitino/server/authorization/MetadataIdConverter.java
 
b/server-common/src/main/java/org/apache/gravitino/server/authorization/MetadataIdConverter.java
index 5f7a5695a2..220acba819 100644
--- 
a/server-common/src/main/java/org/apache/gravitino/server/authorization/MetadataIdConverter.java
+++ 
b/server-common/src/main/java/org/apache/gravitino/server/authorization/MetadataIdConverter.java
@@ -35,6 +35,7 @@ import org.apache.gravitino.catalog.CapabilityHelpers;
 import org.apache.gravitino.catalog.CatalogManager;
 import org.apache.gravitino.connector.capability.Capability;
 import org.apache.gravitino.exceptions.NoSuchEntityException;
+import org.apache.gravitino.exceptions.NotFoundException;
 import org.apache.gravitino.utils.EntityClassMapper;
 import org.apache.gravitino.utils.MetadataObjectUtil;
 
@@ -68,8 +69,13 @@ public class MetadataIdConverter {
     MetadataObject.Type metadataType = metadataObject.type();
     NameIdentifier ident = MetadataObjectUtil.toEntityIdent(metalake, 
metadataObject);
 
-    NameIdentifier normalizedIdent =
-        normalizeCaseSensitive(ident, 
METADATA_SCOPE_MAPPING.get(metadataType), catalogManager);
+    NameIdentifier normalizedIdent;
+    try {
+      normalizedIdent =
+          normalizeCaseSensitive(ident, 
METADATA_SCOPE_MAPPING.get(metadataType), catalogManager);
+    } catch (NotFoundException e) {
+      return Optional.empty();
+    }
 
     Entity.EntityType entityType = 
MetadataObjectUtil.toEntityType(metadataType);
 
diff --git 
a/server-common/src/test/java/org/apache/gravitino/server/authorization/TestMetadataIdConverter.java
 
b/server-common/src/test/java/org/apache/gravitino/server/authorization/TestMetadataIdConverter.java
index 4a11a0a152..3adfa55f02 100644
--- 
a/server-common/src/test/java/org/apache/gravitino/server/authorization/TestMetadataIdConverter.java
+++ 
b/server-common/src/test/java/org/apache/gravitino/server/authorization/TestMetadataIdConverter.java
@@ -41,6 +41,7 @@ import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.Namespace;
 import org.apache.gravitino.catalog.CatalogManager;
 import org.apache.gravitino.connector.capability.Capability;
+import org.apache.gravitino.exceptions.NoSuchCatalogException;
 import org.apache.gravitino.file.Fileset;
 import org.apache.gravitino.meta.AuditInfo;
 import org.apache.gravitino.meta.BaseMetalake;
@@ -200,6 +201,35 @@ public class TestMetadataIdConverter {
     }
   }
 
+  @Test
+  void testConvertReturnsEmptyWhenParentCatalogDoesNotExist() throws 
IllegalAccessException {
+    CatalogManager mockCatalogManager = mock(CatalogManager.class);
+    Object originalCatalogManager =
+        FieldUtils.readDeclaredField(GravitinoEnv.getInstance(), 
"catalogManager", true);
+    Object originalEntityStore =
+        FieldUtils.readDeclaredField(GravitinoEnv.getInstance(), 
"entityStore", true);
+
+    FieldUtils.writeDeclaredField(
+        GravitinoEnv.getInstance(), "catalogManager", mockCatalogManager, 
true);
+    FieldUtils.writeDeclaredField(GravitinoEnv.getInstance(), "entityStore", 
mockStore, true);
+
+    MetadataObject fileset =
+        MetadataObjects.of(
+            ImmutableList.of("missing_catalog", "schema", "fileset"), 
MetadataObject.Type.FILESET);
+    when(mockCatalogManager.loadCatalogAndWrap(NameIdentifier.of("metalake", 
"missing_catalog")))
+        .thenThrow(
+            new NoSuchCatalogException("Catalog %s does not exist", 
"metalake.missing_catalog"));
+
+    try {
+      Assertions.assertEquals(Optional.empty(), 
MetadataIdConverter.getID(fileset, "metalake"));
+    } finally {
+      FieldUtils.writeDeclaredField(
+          GravitinoEnv.getInstance(), "catalogManager", 
originalCatalogManager, true);
+      FieldUtils.writeDeclaredField(
+          GravitinoEnv.getInstance(), "entityStore", originalEntityStore, 
true);
+    }
+  }
+
   private void initTestNameIdentifier() {
     ident1 = NameIdentifier.of("metalake");
     ident2 = NameIdentifier.of("metalake", "catalog");

Reply via email to