You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@polaris.apache.org by ad...@apache.org on 2026/04/17 19:42:14 UTC

(polaris) branch main updated: NoSQL: filter stale grants with deleted grantees from resolved entities (#4231)

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

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


The following commit(s) were added to refs/heads/main by this push:
     new 10d67d76df NoSQL: filter stale grants with deleted grantees from resolved entities (#4231)
10d67d76df is described below

commit 10d67d76dfa0bcbb4501845797fa1e083e8ab67c
Author: Alexandre Dutra <ad...@apache.org>
AuthorDate: Fri Apr 17 21:42:10 2026 +0200

    NoSQL: filter stale grants with deleted grantees from resolved entities (#4231)
    
    The `allGrantRecords()` method only checked securable existence when filtering stale grants. For directed entries (d/...) the securable is the current entity itself, so deleted grantees were never filtered and leaked into resolved-entity grant sets.
---
 .../nosql/metastore/NoSqlMetaStore.java            | 28 +++++--
 .../BasePolarisMetaStoreManagerTest.java           |  6 ++
 .../persistence/PolarisTestMetaStoreManager.java   | 90 ++++++++++++++++++++++
 3 files changed, 119 insertions(+), 5 deletions(-)

diff --git a/persistence/nosql/persistence/metastore/src/main/java/org/apache/polaris/persistence/nosql/metastore/NoSqlMetaStore.java b/persistence/nosql/persistence/metastore/src/main/java/org/apache/polaris/persistence/nosql/metastore/NoSqlMetaStore.java
index d3c928018e..5aabab71c1 100644
--- a/persistence/nosql/persistence/metastore/src/main/java/org/apache/polaris/persistence/nosql/metastore/NoSqlMetaStore.java
+++ b/persistence/nosql/persistence/metastore/src/main/java/org/apache/polaris/persistence/nosql/metastore/NoSqlMetaStore.java
@@ -1203,19 +1203,37 @@ class NoSqlMetaStore extends NonFunctionalBasePersistence {
         catalogId,
         aclName,
         (securableAndGrantee, granted) -> {
-          var indexedAccess =
-              memoizedIndexedAccess.indexedAccess(
+          var securableExists =
+              entityExists(
                   securableAndGrantee.securableCatalogId(),
+                  securableAndGrantee.securableId(),
                   securableAndGrantee.securableTypeCode());
-          var existing = indexedAccess.nameKeyById(securableAndGrantee.securableId());
-          if (existing.isPresent()) {
-            collector.handle(securableAndGrantee, granted);
+          if (!securableExists) {
+            LOGGER.trace("Skipping stale grant record due to missing securable reference");
+            return;
+          }
+
+          var granteeExists =
+              entityExists(
+                  securableAndGrantee.granteeCatalogId(),
+                  securableAndGrantee.granteeId(),
+                  securableAndGrantee.granteeTypeCode());
+          if (!granteeExists) {
+            LOGGER.trace("Skipping stale grant record due to missing grantee reference");
+            return;
           }
+
+          collector.handle(securableAndGrantee, granted);
         },
         securablesIndex);
     return collector.grantRecords;
   }
 
+  private boolean entityExists(long catalogId, long id, int typeCode) {
+    var indexedAccess = memoizedIndexedAccess.indexedAccess(catalogId, typeCode);
+    return indexedAccess.nameKeyById(id).isPresent();
+  }
+
   private void collectGrantRecords(
       long catalogStableId, String aclName, AclEntryHandler aclEntryConsumer) {
     LOGGER.debug("Checking ACL '{}'", aclName);
diff --git a/polaris-core/src/testFixtures/java/org/apache/polaris/core/persistence/BasePolarisMetaStoreManagerTest.java b/polaris-core/src/testFixtures/java/org/apache/polaris/core/persistence/BasePolarisMetaStoreManagerTest.java
index 726bbfc6f8..c40449cf19 100644
--- a/polaris-core/src/testFixtures/java/org/apache/polaris/core/persistence/BasePolarisMetaStoreManagerTest.java
+++ b/polaris-core/src/testFixtures/java/org/apache/polaris/core/persistence/BasePolarisMetaStoreManagerTest.java
@@ -289,6 +289,12 @@ public abstract class BasePolarisMetaStoreManagerTest {
     polarisTestMetaStoreManager.testLoadResolvedEntitiesGranteeVsSecurableRecords();
   }
 
+  /** test that resolved entities do not include grant records referencing dropped grantees */
+  @Test
+  protected void testLoadResolvedEntitySkipsDroppedGranteeReferences() {
+    polarisTestMetaStoreManager.testLoadResolvedEntitySkipsDroppedGranteeReferences();
+  }
+
   /** Test the set of functions for the entity cache */
   @Test
   protected void testEntityCache() {
diff --git a/polaris-core/src/testFixtures/java/org/apache/polaris/core/persistence/PolarisTestMetaStoreManager.java b/polaris-core/src/testFixtures/java/org/apache/polaris/core/persistence/PolarisTestMetaStoreManager.java
index 9a5b938df3..60ab96de2b 100644
--- a/polaris-core/src/testFixtures/java/org/apache/polaris/core/persistence/PolarisTestMetaStoreManager.java
+++ b/polaris-core/src/testFixtures/java/org/apache/polaris/core/persistence/PolarisTestMetaStoreManager.java
@@ -2873,6 +2873,96 @@ public class PolarisTestMetaStoreManager {
         .doesNotContainAnyElementsOf(resolved.getGrantRecordsAsSecurable());
   }
 
+  public void testLoadResolvedEntitySkipsDroppedGranteeReferences() {
+    // create a catalog
+    PolarisBaseEntity catalog =
+        new PolarisBaseEntity(
+            PolarisEntityConstants.getNullId(),
+            polarisMetaStoreManager.generateNewEntityId(this.polarisCallContext).getId(),
+            PolarisEntityType.CATALOG,
+            PolarisEntitySubType.NULL_SUBTYPE,
+            PolarisEntityConstants.getRootEntityId(),
+            "staleGrantCatalog");
+    CreateCatalogResult catalogCreated =
+        polarisMetaStoreManager.createCatalog(this.polarisCallContext, catalog, List.of());
+    Assertions.assertThat(catalogCreated).isNotNull();
+    catalog = catalogCreated.getCatalog();
+
+    // read the built-in catalog_admin role (will be the securable)
+    EntityResult catalogAdminRoleResult =
+        polarisMetaStoreManager.readEntityByName(
+            this.polarisCallContext,
+            List.of(catalog),
+            PolarisEntityType.CATALOG_ROLE,
+            PolarisEntitySubType.ANY_SUBTYPE,
+            PolarisEntityConstants.getNameOfCatalogAdminRole());
+    Assertions.assertThat(catalogAdminRoleResult.isSuccess()).isTrue();
+    PolarisBaseEntity catalogAdminRole = catalogAdminRoleResult.getEntity();
+    Assertions.assertThat(catalogAdminRole).isNotNull();
+
+    // create a principal role (will be the grantee)
+    PolarisBaseEntity principalRole =
+        new PolarisBaseEntity(
+            PolarisEntityConstants.getNullId(),
+            polarisMetaStoreManager.generateNewEntityId(this.polarisCallContext).getId(),
+            PolarisEntityType.PRINCIPAL_ROLE,
+            PolarisEntitySubType.NULL_SUBTYPE,
+            PolarisEntityConstants.getRootEntityId(),
+            "staleGrantPrincipalRole");
+    EntityResult principalRoleCreate =
+        polarisMetaStoreManager.createEntityIfNotExists(
+            this.polarisCallContext, null, principalRole);
+    Assertions.assertThat(principalRoleCreate.isSuccess()).isTrue();
+    PolarisBaseEntity createdPrincipalRole = principalRoleCreate.getEntity();
+    Assertions.assertThat(createdPrincipalRole).isNotNull();
+    long principalRoleId = createdPrincipalRole.getId();
+
+    // grant usage on catalog_admin role to the principal role
+    Assertions.assertThat(
+            polarisMetaStoreManager
+                .grantUsageOnRoleToGrantee(
+                    this.polarisCallContext, catalog, catalogAdminRole, createdPrincipalRole)
+                .isSuccess())
+        .isTrue();
+
+    // verify the grant appears in the catalog_admin role's resolved entity
+    ResolvedEntityResult beforeDrop =
+        polarisMetaStoreManager.loadResolvedEntityById(
+            this.polarisCallContext,
+            catalogAdminRole.getCatalogId(),
+            catalogAdminRole.getId(),
+            PolarisEntityType.CATALOG_ROLE);
+    Assertions.assertThat(beforeDrop.isSuccess()).isTrue();
+    Assertions.assertThat(beforeDrop.getEntityGrantRecords())
+        .anySatisfy(
+            grantRecord -> {
+              Assertions.assertThat(grantRecord.getSecurableId())
+                  .isEqualTo(catalogAdminRole.getId());
+              Assertions.assertThat(grantRecord.getGranteeId()).isEqualTo(principalRoleId);
+            });
+
+    // drop the principal role (grantee)
+    Assertions.assertThat(
+            polarisMetaStoreManager
+                .dropEntityIfExists(
+                    this.polarisCallContext, null, createdPrincipalRole, null, false)
+                .isSuccess())
+        .isTrue();
+
+    // verify the stale grant is filtered out from the catalog_admin role's resolved entity
+    ResolvedEntityResult afterDrop =
+        polarisMetaStoreManager.loadResolvedEntityById(
+            this.polarisCallContext,
+            catalogAdminRole.getCatalogId(),
+            catalogAdminRole.getId(),
+            PolarisEntityType.CATALOG_ROLE);
+    Assertions.assertThat(afterDrop.isSuccess()).isTrue();
+    Assertions.assertThat(afterDrop.getEntityGrantRecords())
+        .noneSatisfy(
+            grantRecord ->
+                Assertions.assertThat(grantRecord.getGranteeId()).isEqualTo(principalRoleId));
+  }
+
   private static PolarisEntityCore getEntityCore(PolarisBaseEntity entity) {
     return new PolarisEntityCore.Builder<>(entity).build();
   }