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

diqiu50 pushed a commit to branch branch-1.3
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/branch-1.3 by this push:
     new c03d7ceb23 [Cherry-pick to branch-1.3] [#12480] improvement(server): 
Allow table existence probe on loadTable (#12482) (#13806)
c03d7ceb23 is described below

commit c03d7ceb232c0471de2e5eb04157a17616f02714
Author: Yuhui <[email protected]>
AuthorDate: Tue Oct 13 14:43:25 2026 +0800

    [Cherry-pick to branch-1.3] [#12480] improvement(server): Allow table 
existence probe on loadTable (#12482) (#13806)
    
    **Cherry-pick Information:**
    - Original commit: 8a7348963b3b031791220c121cbe440c58cabf5d
    - Target branch: `branch-1.3`
    - Status: ⚠️ Partial cherry-pick (conflicts resolved)
    
    Only the load-table existence probe is included, so a user with
    `CREATE_TABLE` can create tables
    through an engine. The `PROBE_TABLE_LIKE` privilege, the list-table
    filter change and the Flink
    connector changes are left out to avoid an API change in a patch
    release.
---
 .../AuthorizationExpressionConstants.java          |   6 +
 .../web/filter/GravitinoInterceptionService.java   |   3 +-
 .../authorization/AuthorizeExecutorFactory.java    |   6 +-
 .../LoadTableAuthorizationExecutor.java            |  28 +++-
 .../gravitino/server/web/rest/TableOperations.java |   2 +
 .../filter/TestGravitinoInterceptionService.java   | 167 +++++++++++++++++++++
 ...CatalogConnectionTestAuthorizationExecutor.java |   3 +-
 .../TestLineageAuthorizationExecutor.java          |   3 +-
 .../TestLoadTableAuthorizationExecutor.java        |   3 +-
 .../TestTableAuthorizationExpression.java          |  24 +++
 10 files changed, 238 insertions(+), 7 deletions(-)

diff --git 
a/server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationExpressionConstants.java
 
b/server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationExpressionConstants.java
index deadf7c96a..10f360d643 100644
--- 
a/server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationExpressionConstants.java
+++ 
b/server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationExpressionConstants.java
@@ -49,6 +49,12 @@ public class AuthorizationExpressionConstants {
                   ANY_USE_CATALOG && ANY_USE_SCHEMA  && (TABLE::OWNER || 
ANY_SELECT_TABLE || ANY_MODIFY_TABLE)
                   """;
 
+  public static final String PROBE_TABLE_LIKE_AUTHORIZATION_EXPRESSION =
+      """
+                  ANY_USE_CATALOG && ANY_USE_SCHEMA &&
+                  (ANY_SELECT_TABLE || ANY_MODIFY_TABLE || ANY_CREATE_TABLE || 
ANY_CREATE_VIEW)
+                  """;
+
   //  Adding ANY_CREATE_TABLE here as Spark calls tableExists before creating 
a table.
   public static final String ICEBERG_LOAD_TABLE_AUTHORIZATION_EXPRESSION =
       """
diff --git 
a/server/src/main/java/org/apache/gravitino/server/web/filter/GravitinoInterceptionService.java
 
b/server/src/main/java/org/apache/gravitino/server/web/filter/GravitinoInterceptionService.java
index 215073d6e3..a21fd32e01 100644
--- 
a/server/src/main/java/org/apache/gravitino/server/web/filter/GravitinoInterceptionService.java
+++ 
b/server/src/main/java/org/apache/gravitino/server/web/filter/GravitinoInterceptionService.java
@@ -195,7 +195,8 @@ public class GravitinoInterceptionService implements 
InterceptionService {
                     parameters,
                     args,
                     secondaryExpression,
-                    secondaryExpressionCondition);
+                    secondaryExpressionCondition,
+                    expressionAnnotation.allowCheckExistence());
             Optional<String> dynamicMetalake;
             try {
               dynamicMetalake = executor.getAuthorizationMetalake();
diff --git 
a/server/src/main/java/org/apache/gravitino/server/web/filter/authorization/AuthorizeExecutorFactory.java
 
b/server/src/main/java/org/apache/gravitino/server/web/filter/authorization/AuthorizeExecutorFactory.java
index 229655256c..12a526cb2b 100644
--- 
a/server/src/main/java/org/apache/gravitino/server/web/filter/authorization/AuthorizeExecutorFactory.java
+++ 
b/server/src/main/java/org/apache/gravitino/server/web/filter/authorization/AuthorizeExecutorFactory.java
@@ -36,7 +36,8 @@ public class AuthorizeExecutorFactory {
       Parameter[] parameters,
       Object[] args,
       String secondaryExpression,
-      ExpressionCondition secondaryExpressionCondition) {
+      ExpressionCondition secondaryExpressionCondition,
+      String allowCheckExistenceExpression) {
     return switch (requestType) {
       case COMMON -> new CommonAuthorizerExecutor(
           expression, metadataContext, pathParams, entityType);
@@ -55,7 +56,8 @@ public class AuthorizeExecutorFactory {
           pathParams,
           entityType,
           secondaryExpression,
-          secondaryExpressionCondition);
+          secondaryExpressionCondition,
+          allowCheckExistenceExpression);
       case CREATE_SCHEMA -> new CreateSchemaAuthorizationExecutor(
           parameters, args, expression, metadataContext, pathParams, 
entityType);
       case TEST_CATALOG_CONNECTION -> new 
CatalogConnectionTestAuthorizationExecutor(
diff --git 
a/server/src/main/java/org/apache/gravitino/server/web/filter/authorization/LoadTableAuthorizationExecutor.java
 
b/server/src/main/java/org/apache/gravitino/server/web/filter/authorization/LoadTableAuthorizationExecutor.java
index 5b87979518..a0a99b94e4 100644
--- 
a/server/src/main/java/org/apache/gravitino/server/web/filter/authorization/LoadTableAuthorizationExecutor.java
+++ 
b/server/src/main/java/org/apache/gravitino/server/web/filter/authorization/LoadTableAuthorizationExecutor.java
@@ -22,7 +22,9 @@ import java.util.Map;
 import java.util.Optional;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.gravitino.Entity;
+import org.apache.gravitino.GravitinoEnv;
 import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.authorization.AuthorizationRequestContext;
 import org.apache.gravitino.authorization.Privilege;
 import 
org.apache.gravitino.server.authorization.annotations.ExpressionCondition;
 import 
org.apache.gravitino.server.authorization.expression.AuthorizationExpressionEvaluator;
@@ -42,6 +44,8 @@ import org.apache.gravitino.server.web.filter.ParameterUtil;
  * Legacy clients without the `privileges` parameter will use default 
authorization.
  */
 public class LoadTableAuthorizationExecutor extends CommonAuthorizerExecutor {
+  private final AuthorizationExpressionEvaluator allowCheckExistenceEvaluator;
+
   public LoadTableAuthorizationExecutor(
       Parameter[] parameters,
       Object[] args,
@@ -50,8 +54,13 @@ public class LoadTableAuthorizationExecutor extends 
CommonAuthorizerExecutor {
       Map<String, Object> pathParams,
       Optional<String> entityType,
       String secondaryExpression,
-      ExpressionCondition secondaryExpressionCondition) {
+      ExpressionCondition secondaryExpressionCondition,
+      String allowCheckExistenceExpression) {
     super(expression, metadataContext, pathParams, entityType);
+    this.allowCheckExistenceEvaluator =
+        StringUtils.isBlank(allowCheckExistenceExpression)
+            ? null
+            : new 
AuthorizationExpressionEvaluator(allowCheckExistenceExpression);
 
     if (!shouldCheckModifyTablePrivilege(secondaryExpression, 
secondaryExpressionCondition)) {
       return;
@@ -65,6 +74,23 @@ public class LoadTableAuthorizationExecutor extends 
CommonAuthorizerExecutor {
     }
   }
 
+  @Override
+  public boolean execute(AuthorizationRequestContext 
authorizationRequestContext) throws Exception {
+    if (super.execute(authorizationRequestContext)) {
+      return true;
+    }
+
+    if (allowCheckExistenceEvaluator == null
+        || !allowCheckExistenceEvaluator.evaluate(
+            metadataContext, pathParams, authorizationRequestContext, 
entityType)) {
+      return false;
+    }
+
+    NameIdentifier tableIdentifier = 
metadataContext.get(Entity.EntityType.TABLE);
+    return tableIdentifier != null
+        && 
!GravitinoEnv.getInstance().tableDispatcher().tableExists(tableIdentifier);
+  }
+
   private static boolean shouldCheckModifyTablePrivilege(
       String secondaryExpression, ExpressionCondition 
secondaryExpressionCondition) {
     return StringUtils.isNotBlank(secondaryExpression)
diff --git 
a/server/src/main/java/org/apache/gravitino/server/web/rest/TableOperations.java
 
b/server/src/main/java/org/apache/gravitino/server/web/rest/TableOperations.java
index b8acafb7be..5e7d062cc6 100644
--- 
a/server/src/main/java/org/apache/gravitino/server/web/rest/TableOperations.java
+++ 
b/server/src/main/java/org/apache/gravitino/server/web/rest/TableOperations.java
@@ -176,6 +176,8 @@ public class TableOperations {
   @ResponseMetered(name = "load-table", absolute = true)
   @AuthorizationExpression(
       expression = 
AuthorizationExpressionConstants.LOAD_TABLE_AUTHORIZATION_EXPRESSION,
+      allowCheckExistence =
+          
AuthorizationExpressionConstants.PROBE_TABLE_LIKE_AUTHORIZATION_EXPRESSION,
       secondaryExpression = 
AuthorizationExpressionConstants.MODIFY_TABLE_AUTHORIZATION_EXPRESSION,
       secondaryExpressionCondition = 
ExpressionCondition.REQUIRED_MODIFY_PRIVILEGES,
       accessMetadataType = MetadataObject.Type.TABLE)
diff --git 
a/server/src/test/java/org/apache/gravitino/server/web/filter/TestGravitinoInterceptionService.java
 
b/server/src/test/java/org/apache/gravitino/server/web/filter/TestGravitinoInterceptionService.java
index 92d4617bfd..d29ade70ca 100644
--- 
a/server/src/test/java/org/apache/gravitino/server/web/filter/TestGravitinoInterceptionService.java
+++ 
b/server/src/test/java/org/apache/gravitino/server/web/filter/TestGravitinoInterceptionService.java
@@ -18,6 +18,8 @@
 package org.apache.gravitino.server.web.filter;
 
 import static 
org.apache.gravitino.server.authorization.expression.AuthorizationExpressionConstants.CAN_ACCESS_METADATA_AND_TAG;
+import static 
org.apache.gravitino.server.authorization.expression.AuthorizationExpressionConstants.LOAD_TABLE_AUTHORIZATION_EXPRESSION;
+import static 
org.apache.gravitino.server.authorization.expression.AuthorizationExpressionConstants.PROBE_TABLE_LIKE_AUTHORIZATION_EXPRESSION;
 import static 
org.apache.gravitino.server.authorization.expression.AuthorizationExpressionConstants.TEST_CATALOG_CONNECTION_WITH_CHANGES_AUTHORIZATION_EXPRESSION;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
@@ -49,6 +51,7 @@ import 
org.apache.gravitino.authorization.AuthorizationRequestContext;
 import org.apache.gravitino.authorization.AuthorizationUtils;
 import org.apache.gravitino.authorization.GravitinoAuthorizer;
 import org.apache.gravitino.authorization.Privilege;
+import org.apache.gravitino.catalog.TableDispatcher;
 import org.apache.gravitino.catalog.ViewDispatcher;
 import org.apache.gravitino.dto.requests.CatalogUpdateRequest;
 import org.apache.gravitino.dto.requests.CatalogUpdatesRequest;
@@ -629,6 +632,99 @@ public class TestGravitinoInterceptionService {
     }
   }
 
+  @Test
+  public void 
testLoadTableAuthorizationProceedsWhenProbeAllowedTableDoesNotExist()
+      throws Throwable {
+    try (MockedStatic<PrincipalUtils> principalUtilsMocked = 
mockStatic(PrincipalUtils.class);
+        MockedStatic<GravitinoAuthorizerProvider> authorizerMocked =
+            mockStatic(GravitinoAuthorizerProvider.class);
+        MockedStatic<AuthorizationUtils> authUtilsMocked = 
mockStatic(AuthorizationUtils.class);
+        MockedStatic<GravitinoEnv> envMocked = mockStatic(GravitinoEnv.class)) 
{
+      principalUtilsMocked
+          .when(PrincipalUtils::getCurrentPrincipal)
+          .thenReturn(new UserPrincipal("tester"));
+      
principalUtilsMocked.when(PrincipalUtils::getCurrentUserName).thenReturn("tester");
+
+      authUtilsMocked
+          .when(
+              () ->
+                  AuthorizationUtils.checkCurrentUser(
+                      ArgumentMatchers.any(), ArgumentMatchers.any(), 
ArgumentMatchers.any()))
+          .thenAnswer(invocation -> null);
+
+      GravitinoAuthorizerProvider mockedProvider = 
mock(GravitinoAuthorizerProvider.class);
+      GravitinoAuthorizer authorizer = tableProbeAuthorizer();
+      
authorizerMocked.when(GravitinoAuthorizerProvider::getInstance).thenReturn(mockedProvider);
+      when(mockedProvider.getGravitinoAuthorizer()).thenReturn(authorizer);
+
+      GravitinoEnv mockEnv = mock(GravitinoEnv.class);
+      TableDispatcher tableDispatcher = mock(TableDispatcher.class);
+      EventBus mockEventBus = mock(EventBus.class);
+      envMocked.when(GravitinoEnv::getInstance).thenReturn(mockEnv);
+      when(mockEnv.tableDispatcher()).thenReturn(tableDispatcher);
+      when(mockEnv.eventBus()).thenReturn(mockEventBus);
+      
when(tableDispatcher.tableExists(ArgumentMatchers.any())).thenReturn(false);
+
+      MethodInterceptor interceptor = tableLoadInterceptor();
+      MethodInvocation invocation = tableLoadInvocation();
+      Response notFound = Utils.notFound("NoSuchTableException", "Table does 
not exist");
+      when(invocation.proceed()).thenReturn(notFound);
+
+      Response response = (Response) interceptor.invoke(invocation);
+
+      assertEquals(Response.Status.NOT_FOUND.getStatusCode(), 
response.getStatus());
+      verify(invocation).proceed();
+      verify(tableDispatcher).tableExists(ArgumentMatchers.any());
+      verify(mockEventBus, never()).dispatchEvent(ArgumentMatchers.any());
+    }
+  }
+
+  @Test
+  public void 
testLoadTableAuthorizationReturnsForbiddenWhenProbeAllowedTableExists()
+      throws Throwable {
+    try (MockedStatic<PrincipalUtils> principalUtilsMocked = 
mockStatic(PrincipalUtils.class);
+        MockedStatic<GravitinoAuthorizerProvider> authorizerMocked =
+            mockStatic(GravitinoAuthorizerProvider.class);
+        MockedStatic<AuthorizationUtils> authUtilsMocked = 
mockStatic(AuthorizationUtils.class);
+        MockedStatic<GravitinoEnv> envMocked = mockStatic(GravitinoEnv.class)) 
{
+      principalUtilsMocked
+          .when(PrincipalUtils::getCurrentPrincipal)
+          .thenReturn(new UserPrincipal("tester"));
+      
principalUtilsMocked.when(PrincipalUtils::getCurrentUserName).thenReturn("tester");
+
+      authUtilsMocked
+          .when(
+              () ->
+                  AuthorizationUtils.checkCurrentUser(
+                      ArgumentMatchers.any(), ArgumentMatchers.any(), 
ArgumentMatchers.any()))
+          .thenAnswer(invocation -> null);
+
+      GravitinoAuthorizerProvider mockedProvider = 
mock(GravitinoAuthorizerProvider.class);
+      GravitinoAuthorizer authorizer = tableProbeAuthorizer();
+      
authorizerMocked.when(GravitinoAuthorizerProvider::getInstance).thenReturn(mockedProvider);
+      when(mockedProvider.getGravitinoAuthorizer()).thenReturn(authorizer);
+
+      GravitinoEnv mockEnv = mock(GravitinoEnv.class);
+      TableDispatcher tableDispatcher = mock(TableDispatcher.class);
+      EventBus mockEventBus = spy(new EventBus(Collections.emptyList()));
+      envMocked.when(GravitinoEnv::getInstance).thenReturn(mockEnv);
+      when(mockEnv.tableDispatcher()).thenReturn(tableDispatcher);
+      when(mockEnv.eventBus()).thenReturn(mockEventBus);
+      
when(tableDispatcher.tableExists(ArgumentMatchers.any())).thenReturn(true);
+
+      MethodInterceptor interceptor = tableLoadInterceptor();
+      MethodInvocation invocation = tableLoadInvocation();
+
+      Response response = (Response) interceptor.invoke(invocation);
+
+      assertEquals(Response.Status.FORBIDDEN.getStatusCode(), 
response.getStatus());
+      verify(invocation, never()).proceed();
+      verify(tableDispatcher).tableExists(ArgumentMatchers.any());
+      verify(mockEventBus)
+          
.dispatchEvent(ArgumentMatchers.any(AuthorizationDenialFailureEvent.class));
+    }
+  }
+
   /**
    * When {@code checkCurrentUser} throws {@link NoSuchMetalakeException}, the 
403 is a
    * resource-not-found masquerading as forbidden — no {@link 
AuthorizationDenialFailureEvent} is
@@ -748,6 +844,23 @@ public class TestGravitinoInterceptionService {
     }
   }
 
+  public static class TestTableLoadOperations {
+
+    @AuthorizationExpression(
+        expression = LOAD_TABLE_AUTHORIZATION_EXPRESSION,
+        allowCheckExistence = PROBE_TABLE_LIKE_AUTHORIZATION_EXPRESSION,
+        accessMetadataType = MetadataObject.Type.TABLE)
+    public Response loadTable(
+        @AuthorizationMetadata(type = Entity.EntityType.METALAKE) String 
metalake,
+        @AuthorizationMetadata(type = Entity.EntityType.CATALOG) String 
catalog,
+        @AuthorizationMetadata(type = Entity.EntityType.SCHEMA) String schema,
+        @AuthorizationMetadata(type = Entity.EntityType.TABLE) String table,
+        @AuthorizationRequest(type = 
AuthorizationRequest.RequestType.LOAD_TABLE)
+            String requiredPrivileges) {
+      return Utils.ok("unused");
+    }
+  }
+
   public static class TestOperations {
 
     @AuthorizationExpression(
@@ -845,6 +958,60 @@ public class TestGravitinoInterceptionService {
     return authorizer;
   }
 
+  private MethodInterceptor tableLoadInterceptor() throws 
NoSuchMethodException {
+    Method method =
+        TestTableLoadOperations.class.getMethod(
+            "loadTable", String.class, String.class, String.class, 
String.class, String.class);
+    return new 
GravitinoInterceptionService().getMethodInterceptors(method).get(0);
+  }
+
+  private MethodInvocation tableLoadInvocation() throws NoSuchMethodException {
+    Method method =
+        TestTableLoadOperations.class.getMethod(
+            "loadTable", String.class, String.class, String.class, 
String.class, String.class);
+    MethodInvocation invocation = mock(MethodInvocation.class);
+    when(invocation.getMethod()).thenReturn(method);
+    when(invocation.getArguments())
+        .thenReturn(
+            new Object[] {"testMetalake", "testCatalog", "testSchema", 
"missingTable", null});
+    return invocation;
+  }
+
+  private GravitinoAuthorizer tableProbeAuthorizer() {
+    GravitinoAuthorizer authorizer = mock(GravitinoAuthorizer.class);
+    when(authorizer.authorize(
+            ArgumentMatchers.any(),
+            ArgumentMatchers.eq("testMetalake"),
+            ArgumentMatchers.argThat(
+                metadataObject ->
+                    metadataObject.type() == MetadataObject.Type.CATALOG
+                        && "testCatalog".equals(metadataObject.name())),
+            ArgumentMatchers.eq(Privilege.Name.USE_CATALOG),
+            ArgumentMatchers.any()))
+        .thenReturn(true);
+    when(authorizer.authorize(
+            ArgumentMatchers.any(),
+            ArgumentMatchers.eq("testMetalake"),
+            ArgumentMatchers.argThat(
+                metadataObject ->
+                    metadataObject.type() == MetadataObject.Type.SCHEMA
+                        && "testSchema".equals(metadataObject.name())),
+            ArgumentMatchers.eq(Privilege.Name.USE_SCHEMA),
+            ArgumentMatchers.any()))
+        .thenReturn(true);
+    when(authorizer.authorize(
+            ArgumentMatchers.any(),
+            ArgumentMatchers.eq("testMetalake"),
+            ArgumentMatchers.argThat(
+                metadataObject ->
+                    metadataObject.type() == MetadataObject.Type.METALAKE
+                        && "testMetalake".equals(metadataObject.name())),
+            ArgumentMatchers.eq(Privilege.Name.CREATE_TABLE),
+            ArgumentMatchers.any()))
+        .thenReturn(true);
+    return authorizer;
+  }
+
   private static class MockGravitinoAuthorizer implements GravitinoAuthorizer {
 
     @Override
diff --git 
a/server/src/test/java/org/apache/gravitino/server/web/filter/authorization/TestCatalogConnectionTestAuthorizationExecutor.java
 
b/server/src/test/java/org/apache/gravitino/server/web/filter/authorization/TestCatalogConnectionTestAuthorizationExecutor.java
index 12f8a9ce8b..26cd29987b 100644
--- 
a/server/src/test/java/org/apache/gravitino/server/web/filter/authorization/TestCatalogConnectionTestAuthorizationExecutor.java
+++ 
b/server/src/test/java/org/apache/gravitino/server/web/filter/authorization/TestCatalogConnectionTestAuthorizationExecutor.java
@@ -97,7 +97,8 @@ public class TestCatalogConnectionTestAuthorizationExecutor {
             method.getParameters(),
             new Object[] {proposedChanges()},
             SECONDARY_EXPRESSION,
-            ExpressionCondition.HAS_PROPOSED_CHANGES);
+            ExpressionCondition.HAS_PROPOSED_CHANGES,
+            "");
 
     assertTrue(executor instanceof CatalogConnectionTestAuthorizationExecutor);
     assertEquals(
diff --git 
a/server/src/test/java/org/apache/gravitino/server/web/filter/authorization/TestLineageAuthorizationExecutor.java
 
b/server/src/test/java/org/apache/gravitino/server/web/filter/authorization/TestLineageAuthorizationExecutor.java
index 518e09d6d1..cff07fd57c 100644
--- 
a/server/src/test/java/org/apache/gravitino/server/web/filter/authorization/TestLineageAuthorizationExecutor.java
+++ 
b/server/src/test/java/org/apache/gravitino/server/web/filter/authorization/TestLineageAuthorizationExecutor.java
@@ -403,7 +403,8 @@ class TestLineageAuthorizationExecutor {
         lineageMethod().getParameters(),
         new Object[] {event},
         "",
-        ExpressionCondition.NEVER);
+        ExpressionCondition.NEVER,
+        "");
   }
 
   private static Method lineageMethod() throws NoSuchMethodException {
diff --git 
a/server/src/test/java/org/apache/gravitino/server/web/filter/authorization/TestLoadTableAuthorizationExecutor.java
 
b/server/src/test/java/org/apache/gravitino/server/web/filter/authorization/TestLoadTableAuthorizationExecutor.java
index fcd0f273e7..15bc76ce8f 100644
--- 
a/server/src/test/java/org/apache/gravitino/server/web/filter/authorization/TestLoadTableAuthorizationExecutor.java
+++ 
b/server/src/test/java/org/apache/gravitino/server/web/filter/authorization/TestLoadTableAuthorizationExecutor.java
@@ -74,7 +74,8 @@ public class TestLoadTableAuthorizationExecutor {
         Collections.emptyMap(),
         Optional.empty(),
         SECONDARY_EXPRESSION,
-        condition);
+        condition,
+        "");
   }
 
   private static String expression(LoadTableAuthorizationExecutor executor) 
throws Exception {
diff --git 
a/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestTableAuthorizationExpression.java
 
b/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestTableAuthorizationExpression.java
index f115695416..f21f372c00 100644
--- 
a/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestTableAuthorizationExpression.java
+++ 
b/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestTableAuthorizationExpression.java
@@ -170,6 +170,30 @@ public class TestTableAuthorizationExpression {
                 "METALAKE::USE_CATALOG")));
   }
 
+  @Test
+  public void testLoadTableExistenceProbe() throws NoSuchMethodException, 
OgnlException {
+    Method method =
+        TableOperations.class.getMethod(
+            "loadTable", String.class, String.class, String.class, 
String.class, String.class);
+    AuthorizationExpression authorizationExpressionAnnotation =
+        method.getAnnotation(AuthorizationExpression.class);
+    String expression = 
authorizationExpressionAnnotation.allowCheckExistence();
+    MockAuthorizationExpressionEvaluator mockEvaluator =
+        new MockAuthorizationExpressionEvaluator(expression);
+    assertFalse(mockEvaluator.getResult(ImmutableSet.of()));
+    assertFalse(mockEvaluator.getResult(ImmutableSet.of("METALAKE::OWNER")));
+    
assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::CREATE_TABLE")));
+    assertTrue(
+        mockEvaluator.getResult(
+            ImmutableSet.of("SCHEMA::CREATE_TABLE", "CATALOG::USE_CATALOG", 
"SCHEMA::USE_SCHEMA")));
+    assertTrue(
+        mockEvaluator.getResult(
+            ImmutableSet.of("SCHEMA::SELECT_TABLE", "CATALOG::USE_CATALOG", 
"SCHEMA::USE_SCHEMA")));
+    assertTrue(
+        mockEvaluator.getResult(
+            ImmutableSet.of("SCHEMA::MODIFY_TABLE", "CATALOG::USE_CATALOG", 
"SCHEMA::USE_SCHEMA")));
+  }
+
   @Test
   public void testAlterTable() throws NoSuchMethodException, OgnlException {
     Method method =

Reply via email to