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 7856f0ee19 [#12875] improve(server): Reuse the shared table 
authorization expressions (#12876)
7856f0ee19 is described below

commit 7856f0ee196d7512a127dde4534ef72c8e67a16f
Author: Qi Yu <[email protected]>
AuthorDate: Fri Sep 4 14:34:34 2026 +0800

    [#12875] improve(server): Reuse the shared table authorization expressions 
(#12876)
    
    ### What changes were proposed in this pull request?
    
    Adds `DROP_TABLE_AUTHORIZATION_EXPRESSION` to
    `AuthorizationExpressionConstants` and replaces 13 hand-written copies
    of three table authorization expressions with references to the shared
    constants:
    
    | Sites | Constant |
    | --- | --- |
    | `TableOperations#dropTable`, `IcebergTableOperations#dropTable`,
    `RenameTableAuthzHandler` | `DROP_TABLE_AUTHORIZATION_EXPRESSION` (new)
    |
    | `IcebergTableOperations#updateTable`, `IcebergTableRenameOperations`,
    four `StatisticOperations` endpoints |
    `MODIFY_TABLE_AUTHORIZATION_EXPRESSION` (existing) |
    | `IcebergTableOperations` credentials and plan, two
    `StatisticOperations` endpoints | `LOAD_TABLE_AUTHORIZATION_EXPRESSION`
    (existing) |
    
    Also removes a local variable in `RenameTableAuthzHandler` that only
    aliased the expression.
    
    This is behavior preserving. No privilege rule changes.
    
    Lance REST has a fourth copy of the removal expression, added in #12696,
    which is not merged yet. The two PRs are independent; whichever lands
    second, the Lance copy will be pointed at the same shared constant in a
    follow-up so all four surfaces read from one place.
    
    Fix: #12875
    
    ### Why are the changes needed?
    
    A privilege change to one of these rules currently has to be repeated in
    every copy, and missing one is a silent authorization difference between
    the Gravitino, Iceberg REST and Lance REST surfaces rather than a
    visible failure. Two copies had already drifted in formatting
    (`ANY_SELECT_TABLE|| ANY_MODIFY_TABLE`, missing the space), which is
    evidence that they are edited independently.
    
    ### Does this PR introduce _any_ user-facing change?
    
    No.
    
    ### How was this patch tested?
    
    The expressions were compared before and after rather than by eye. A
    throwaway test ran each removed literal and its replacement constant
    through `AuthorizationExpressionConverter.convertToOgnlExpression`,
    asserted the results are identical ignoring whitespace, and parsed both
    with `Ognl.parseExpression` to confirm they are still valid OGNL.
    Whitespace around operators turned out to be the only difference, from
    the two `StatisticOperations` copies that were missing a space before
    `||`.
    
    `./gradlew :server-common:test :server:test
    :iceberg:iceberg-rest-server:test` — 1119 tests, all passing.
    
    `TestHttpsServerAuthentication` is a pre-existing flake and is
    unrelated: a different method of that class fails the same way on a
    branch that does not touch `server-common` at all.
---
 .../service/rest/IcebergTableOperations.java       | 24 ++++++----------
 .../service/rest/IcebergTableRenameOperations.java |  7 ++---
 .../server/web/filter/RenameTableAuthzHandler.java |  9 ++----
 .../AuthorizationExpressionConstants.java          | 12 ++++++++
 .../server/web/rest/StatisticOperations.java       | 33 ++++++----------------
 .../gravitino/server/web/rest/TableOperations.java |  8 ++----
 6 files changed, 37 insertions(+), 56 deletions(-)

diff --git 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergTableOperations.java
 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergTableOperations.java
index 4bedb7c41e..898f12747e 100644
--- 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergTableOperations.java
+++ 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergTableOperations.java
@@ -18,6 +18,10 @@
  */
 package org.apache.gravitino.iceberg.service.rest;
 
+import static 
org.apache.gravitino.server.authorization.expression.AuthorizationExpressionConstants.DROP_TABLE_AUTHORIZATION_EXPRESSION;
+import static 
org.apache.gravitino.server.authorization.expression.AuthorizationExpressionConstants.LOAD_TABLE_AUTHORIZATION_EXPRESSION;
+import static 
org.apache.gravitino.server.authorization.expression.AuthorizationExpressionConstants.MODIFY_TABLE_AUTHORIZATION_EXPRESSION;
+
 import com.codahale.metrics.annotation.ResponseMetered;
 import com.codahale.metrics.annotation.Timed;
 import com.fasterxml.jackson.core.JsonProcessingException;
@@ -206,10 +210,7 @@ public class IcebergTableOperations {
   @Timed(name = "update-table." + MetricNames.HTTP_PROCESS_DURATION, absolute 
= true)
   @ResponseMetered(name = "update-table", absolute = true)
   @AuthorizationExpression(
-      expression =
-          "ANY(OWNER, METALAKE, CATALOG) || "
-              + "SCHEMA_OWNER_WITH_USE_CATALOG || "
-              + "ANY_USE_CATALOG && ANY_USE_SCHEMA && (TABLE::OWNER || 
ANY_MODIFY_TABLE)",
+      expression = MODIFY_TABLE_AUTHORIZATION_EXPRESSION,
       accessMetadataType = MetadataObject.Type.TABLE)
   public Response updateTable(
       @AuthorizationMetadata(type = Entity.EntityType.CATALOG) 
@PathParam("prefix") String prefix,
@@ -252,10 +253,7 @@ public class IcebergTableOperations {
   @Timed(name = "drop-table." + MetricNames.HTTP_PROCESS_DURATION, absolute = 
true)
   @ResponseMetered(name = "drop-table", absolute = true)
   @AuthorizationExpression(
-      expression =
-          "ANY(OWNER, METALAKE, CATALOG) || "
-              + "SCHEMA_OWNER_WITH_USE_CATALOG || "
-              + "ANY_USE_CATALOG && ANY_USE_SCHEMA && TABLE::OWNER ",
+      expression = DROP_TABLE_AUTHORIZATION_EXPRESSION,
       accessMetadataType = MetadataObject.Type.TABLE)
   public Response dropTable(
       @AuthorizationMetadata(type = Entity.EntityType.CATALOG) 
@PathParam("prefix") String prefix,
@@ -459,10 +457,7 @@ public class IcebergTableOperations {
   @Timed(name = "get-table-credentials." + MetricNames.HTTP_PROCESS_DURATION, 
absolute = true)
   @ResponseMetered(name = "get-table-credentials", absolute = true)
   @AuthorizationExpression(
-      expression =
-          "ANY(OWNER, METALAKE, CATALOG) || "
-              + "SCHEMA_OWNER_WITH_USE_CATALOG || "
-              + "ANY_USE_CATALOG && ANY_USE_SCHEMA && (TABLE::OWNER || 
ANY_SELECT_TABLE || ANY_MODIFY_TABLE)",
+      expression = LOAD_TABLE_AUTHORIZATION_EXPRESSION,
       accessMetadataType = MetadataObject.Type.TABLE)
   public Response getTableCredentials(
       @AuthorizationMetadata(type = Entity.EntityType.CATALOG) 
@PathParam("prefix") String prefix,
@@ -514,10 +509,7 @@ public class IcebergTableOperations {
   @Timed(name = "plan-table-scan." + MetricNames.HTTP_PROCESS_DURATION, 
absolute = true)
   @ResponseMetered(name = "plan-table-scan", absolute = true)
   @AuthorizationExpression(
-      expression =
-          "ANY(OWNER, METALAKE, CATALOG) || "
-              + "SCHEMA_OWNER_WITH_USE_CATALOG || "
-              + "ANY_USE_CATALOG && ANY_USE_SCHEMA && (TABLE::OWNER || 
ANY_SELECT_TABLE || ANY_MODIFY_TABLE)",
+      expression = LOAD_TABLE_AUTHORIZATION_EXPRESSION,
       accessMetadataType = MetadataObject.Type.TABLE)
   public Response planTableScan(
       @PathParam("prefix") @AuthorizationMetadata(type = EntityType.CATALOG) 
String prefix,
diff --git 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergTableRenameOperations.java
 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergTableRenameOperations.java
index 09c5f6e72a..95555cfe3e 100644
--- 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergTableRenameOperations.java
+++ 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergTableRenameOperations.java
@@ -18,6 +18,8 @@
  */
 package org.apache.gravitino.iceberg.service.rest;
 
+import static 
org.apache.gravitino.server.authorization.expression.AuthorizationExpressionConstants.MODIFY_TABLE_AUTHORIZATION_EXPRESSION;
+
 import com.codahale.metrics.annotation.ResponseMetered;
 import com.codahale.metrics.annotation.Timed;
 import com.google.common.annotations.VisibleForTesting;
@@ -67,10 +69,7 @@ public class IcebergTableRenameOperations {
   @Timed(name = "rename-table." + MetricNames.HTTP_PROCESS_DURATION, absolute 
= true)
   @ResponseMetered(name = "rename-table", absolute = true)
   @AuthorizationExpression(
-      expression =
-          "ANY(OWNER, METALAKE, CATALOG) || "
-              + "SCHEMA_OWNER_WITH_USE_CATALOG || "
-              + "ANY_USE_CATALOG && ANY_USE_SCHEMA && (TABLE::OWNER || 
ANY_MODIFY_TABLE)",
+      expression = MODIFY_TABLE_AUTHORIZATION_EXPRESSION,
       accessMetadataType = MetadataObject.Type.TABLE)
   public Response renameTable(
       @AuthorizationMetadata(type = Entity.EntityType.CATALOG) 
@PathParam("prefix") String prefix,
diff --git 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/server/web/filter/RenameTableAuthzHandler.java
 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/server/web/filter/RenameTableAuthzHandler.java
index 4df2f41068..07c480bf70 100644
--- 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/server/web/filter/RenameTableAuthzHandler.java
+++ 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/server/web/filter/RenameTableAuthzHandler.java
@@ -19,6 +19,8 @@
 
 package org.apache.gravitino.server.web.filter;
 
+import static 
org.apache.gravitino.server.authorization.expression.AuthorizationExpressionConstants.DROP_TABLE_AUTHORIZATION_EXPRESSION;
+
 import java.lang.reflect.Parameter;
 import java.util.HashMap;
 import java.util.Map;
@@ -134,13 +136,8 @@ public class RenameTableAuthzHandler implements 
AuthorizationHandler {
         EntityType.TABLE,
         NameIdentifierUtil.ofTable(metalakeName, catalog, sourceSchema, 
sourceTable));
 
-    String sourceExpression =
-        "ANY(OWNER, METALAKE, CATALOG) || "
-            + "SCHEMA_OWNER_WITH_USE_CATALOG || "
-            + "ANY_USE_CATALOG && ANY_USE_SCHEMA && TABLE::OWNER";
-
     AuthorizationExpressionEvaluator sourceEvaluator =
-        new AuthorizationExpressionEvaluator(sourceExpression);
+        new 
AuthorizationExpressionEvaluator(DROP_TABLE_AUTHORIZATION_EXPRESSION);
 
     boolean sourceAuthorized =
         sourceEvaluator.evaluate(
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 7aadba3f2c..548281ed9f 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
@@ -100,6 +100,18 @@ public class AuthorizationExpressionConstants {
                   ANY_USE_CATALOG && ANY_USE_SCHEMA && (TABLE::OWNER || 
ANY_MODIFY_TABLE)
                   """;
 
+  /**
+   * Authorizes removing a table, whether the stored data is deleted with it 
or only the Gravitino
+   * metadata is. Removal requires ownership of the table or of one of its 
ancestors: MODIFY_TABLE
+   * alters a table but never removes it.
+   */
+  public static final String DROP_TABLE_AUTHORIZATION_EXPRESSION =
+      """
+                  ANY(OWNER, METALAKE, CATALOG) ||
+                  SCHEMA_OWNER_WITH_USE_CATALOG ||
+                  ANY_USE_CATALOG && ANY_USE_SCHEMA && TABLE::OWNER
+                  """;
+
   public static final String LOAD_TOPICS_AUTHORIZATION_EXPRESSION =
       """
           ANY(OWNER, METALAKE, CATALOG) ||
diff --git 
a/server/src/main/java/org/apache/gravitino/server/web/rest/StatisticOperations.java
 
b/server/src/main/java/org/apache/gravitino/server/web/rest/StatisticOperations.java
index 4d750aa05e..1c11abb958 100644
--- 
a/server/src/main/java/org/apache/gravitino/server/web/rest/StatisticOperations.java
+++ 
b/server/src/main/java/org/apache/gravitino/server/web/rest/StatisticOperations.java
@@ -18,6 +18,9 @@
  */
 package org.apache.gravitino.server.web.rest;
 
+import static 
org.apache.gravitino.server.authorization.expression.AuthorizationExpressionConstants.LOAD_TABLE_AUTHORIZATION_EXPRESSION;
+import static 
org.apache.gravitino.server.authorization.expression.AuthorizationExpressionConstants.MODIFY_TABLE_AUTHORIZATION_EXPRESSION;
+
 import com.codahale.metrics.annotation.ResponseMetered;
 import com.codahale.metrics.annotation.Timed;
 import com.google.common.annotations.VisibleForTesting;
@@ -95,10 +98,7 @@ public class StatisticOperations {
   @Timed(name = "list-stats." + MetricNames.HTTP_PROCESS_DURATION, absolute = 
true)
   @ResponseMetered(name = "list-stats", absolute = true)
   @AuthorizationExpression(
-      expression =
-          "ANY(OWNER, METALAKE, CATALOG) || "
-              + "SCHEMA_OWNER_WITH_USE_CATALOG || "
-              + "ANY_USE_CATALOG && ANY_USE_SCHEMA  && (TABLE::OWNER || 
ANY_SELECT_TABLE|| ANY_MODIFY_TABLE)",
+      expression = LOAD_TABLE_AUTHORIZATION_EXPRESSION,
       accessMetadataType = MetadataObject.Type.TABLE)
   public Response listStatistics(
       @PathParam("metalake") @AuthorizationMetadata(type = 
Entity.EntityType.METALAKE)
@@ -140,10 +140,7 @@ public class StatisticOperations {
   @Timed(name = "update-stats." + MetricNames.HTTP_PROCESS_DURATION, absolute 
= true)
   @ResponseMetered(name = "update-stats", absolute = true)
   @AuthorizationExpression(
-      expression =
-          "ANY(OWNER, METALAKE, CATALOG) || "
-              + "SCHEMA_OWNER_WITH_USE_CATALOG || "
-              + "ANY_USE_CATALOG && ANY_USE_SCHEMA  && (TABLE::OWNER || 
ANY_MODIFY_TABLE)",
+      expression = MODIFY_TABLE_AUTHORIZATION_EXPRESSION,
       accessMetadataType = MetadataObject.Type.TABLE)
   public Response updateStatistics(
       @PathParam("metalake") @AuthorizationMetadata(type = 
Entity.EntityType.METALAKE)
@@ -200,10 +197,7 @@ public class StatisticOperations {
   @Timed(name = "drop-stats." + MetricNames.HTTP_PROCESS_DURATION, absolute = 
true)
   @ResponseMetered(name = "drop-stats", absolute = true)
   @AuthorizationExpression(
-      expression =
-          "ANY(OWNER, METALAKE, CATALOG) || "
-              + "SCHEMA_OWNER_WITH_USE_CATALOG || "
-              + "ANY_USE_CATALOG && ANY_USE_SCHEMA  && (TABLE::OWNER || 
ANY_MODIFY_TABLE)",
+      expression = MODIFY_TABLE_AUTHORIZATION_EXPRESSION,
       accessMetadataType = MetadataObject.Type.TABLE)
   public Response dropStatistics(
       @PathParam("metalake") @AuthorizationMetadata(type = 
Entity.EntityType.METALAKE)
@@ -259,10 +253,7 @@ public class StatisticOperations {
   @Timed(name = "list-partition-stats." + MetricNames.HTTP_PROCESS_DURATION, 
absolute = true)
   @ResponseMetered(name = "list-partition-stats", absolute = true)
   @AuthorizationExpression(
-      expression =
-          "ANY(OWNER, METALAKE, CATALOG) || "
-              + "SCHEMA_OWNER_WITH_USE_CATALOG || "
-              + "ANY_USE_CATALOG && ANY_USE_SCHEMA  && (TABLE::OWNER || 
ANY_SELECT_TABLE|| ANY_MODIFY_TABLE)",
+      expression = LOAD_TABLE_AUTHORIZATION_EXPRESSION,
       accessMetadataType = MetadataObject.Type.TABLE)
   public Response listPartitionStatistics(
       @PathParam("metalake") @AuthorizationMetadata(type = 
Entity.EntityType.METALAKE)
@@ -349,10 +340,7 @@ public class StatisticOperations {
   @Timed(name = "update-partitions-stats." + 
MetricNames.HTTP_PROCESS_DURATION, absolute = true)
   @ResponseMetered(name = "update-partitions-stats", absolute = true)
   @AuthorizationExpression(
-      expression =
-          "ANY(OWNER, METALAKE, CATALOG) || "
-              + "SCHEMA_OWNER_WITH_USE_CATALOG || "
-              + "ANY_USE_CATALOG && ANY_USE_SCHEMA  && (TABLE::OWNER || 
ANY_MODIFY_TABLE)",
+      expression = MODIFY_TABLE_AUTHORIZATION_EXPRESSION,
       accessMetadataType = MetadataObject.Type.TABLE)
   public Response updatePartitionStatistics(
       @PathParam("metalake") @AuthorizationMetadata(type = 
Entity.EntityType.METALAKE)
@@ -420,10 +408,7 @@ public class StatisticOperations {
   @Timed(name = "drop-partitions-stats." + MetricNames.HTTP_PROCESS_DURATION, 
absolute = true)
   @ResponseMetered(name = "drop-partitions-stats", absolute = true)
   @AuthorizationExpression(
-      expression =
-          "ANY(OWNER, METALAKE, CATALOG) || "
-              + "SCHEMA_OWNER_WITH_USE_CATALOG || "
-              + "ANY_USE_CATALOG && ANY_USE_SCHEMA  && (TABLE::OWNER || 
ANY_MODIFY_TABLE)",
+      expression = MODIFY_TABLE_AUTHORIZATION_EXPRESSION,
       accessMetadataType = MetadataObject.Type.TABLE)
   public Response dropPartitionStatistics(
       @PathParam("metalake") @AuthorizationMetadata(type = 
Entity.EntityType.METALAKE)
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 1b0eb4d90c..69fd0cc669 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
@@ -20,6 +20,7 @@ package org.apache.gravitino.server.web.rest;
 
 import static org.apache.gravitino.dto.util.DTOConverters.fromDTO;
 import static org.apache.gravitino.dto.util.DTOConverters.fromDTOs;
+import static 
org.apache.gravitino.server.authorization.expression.AuthorizationExpressionConstants.DROP_TABLE_AUTHORIZATION_EXPRESSION;
 
 import com.codahale.metrics.annotation.ResponseMetered;
 import com.codahale.metrics.annotation.Timed;
@@ -257,12 +258,7 @@ public class TableOperations {
   @Timed(name = "drop-table." + MetricNames.HTTP_PROCESS_DURATION, absolute = 
true)
   @ResponseMetered(name = "drop-table", absolute = true)
   @AuthorizationExpression(
-      expression =
-          """
-              ANY(OWNER, METALAKE, CATALOG) ||
-              SCHEMA_OWNER_WITH_USE_CATALOG ||
-              ANY_USE_CATALOG && ANY_USE_SCHEMA  && TABLE::OWNER
-              """,
+      expression = DROP_TABLE_AUTHORIZATION_EXPRESSION,
       accessMetadataType = MetadataObject.Type.TABLE)
   public Response dropTable(
       @PathParam("metalake") @AuthorizationMetadata(type = 
Entity.EntityType.METALAKE)

Reply via email to