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

liuxun pushed a commit to branch branch-metadata-authz
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/branch-metadata-authz by this 
push:
     new eb0c0f3255 [#7576] improvement(authz): Improve the authorization 
expression (#7577)
eb0c0f3255 is described below

commit eb0c0f32554b008a9b6b390cb26b72614a529f94
Author: yangyang zhong <[email protected]>
AuthorDate: Mon Jul 7 14:16:44 2025 +0800

    [#7576] improvement(authz): Improve the authorization expression (#7577)
    
    ### What changes were proposed in this pull request?
    
    Improve the authorization expression
    
    ### Why are the changes needed?
    
    close: #7576
    
    ### Does this PR introduce _any_ user-facing change?
    
    None
    
    ### How was this patch tested?
    
    ```
    
org.apache.gravitino.client.integration.test.authorization.CatalogAuthorizationIT
    
org.apache.gravitino.client.integration.test.authorization.SchemaAuthorizationIT
    
org.apache.gravitino.server.authorization.expression.TestAuthorizationExpressionConverter
    ```
---
 .../AuthorizationExpressionConverter.java          | 40 ++++++++++++++++++++
 .../TestAuthorizationExpressionConverter.java      | 43 ++++++++++++++++++++++
 .../server/web/rest/CatalogOperations.java         | 13 +++----
 .../server/web/rest/SchemaOperations.java          | 20 +++++-----
 4 files changed, 98 insertions(+), 18 deletions(-)

diff --git 
a/server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationExpressionConverter.java
 
b/server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationExpressionConverter.java
index 858d9b4925..d1fbc22faf 100644
--- 
a/server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationExpressionConverter.java
+++ 
b/server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationExpressionConverter.java
@@ -32,6 +32,9 @@ public class AuthorizationExpressionConverter {
   /** Match authorization expressions */
   public static final Pattern PATTERN = 
Pattern.compile("([A-Z_]+)::([A-Z_]+)");
 
+  /** Match ANY expressions */
+  public static final Pattern ANY_PATTERN = 
Pattern.compile("ANY\\(([^)]+)\\)");
+
   /**
    * The EXPRESSION_CACHE caches the result of converting authorization 
expressions into an OGNL
    * expression.
@@ -51,6 +54,7 @@ public class AuthorizationExpressionConverter {
    * @return an OGNL expression used to call GravitinoAuthorizer
    */
   public static String convertToOgnlExpression(String authorizationExpression) 
{
+    authorizationExpression = replaceAnyExpressions(authorizationExpression);
     return EXPRESSION_CACHE.computeIfAbsent(
         authorizationExpression,
         (expression) -> {
@@ -78,4 +82,40 @@ public class AuthorizationExpressionConverter {
           return result.toString();
         });
   }
+
+  /**
+   * Replaces any expression. For example, replace ANY(OWNER, METALAKE, 
CATALOG) to METALAKE::OWNER
+   * || CATALOG::OWNER.
+   *
+   * @param expression The original expression
+   * @return The modified expression
+   */
+  public static String replaceAnyExpressions(String expression) {
+    Matcher matcher = ANY_PATTERN.matcher(expression);
+    StringBuffer result = new StringBuffer();
+
+    while (matcher.find()) {
+      String innerContent = matcher.group(1);
+      String[] parts = innerContent.split(",");
+      if (parts.length < 2) {
+        matcher.appendReplacement(result, 
Matcher.quoteReplacement(matcher.group(0)));
+        continue;
+      }
+
+      String function = parts[0].trim();
+      StringBuilder replacement = new StringBuilder();
+      for (int i = 1; i < parts.length; i++) {
+        String scope = parts[i].trim();
+        if (!scope.isEmpty()) {
+          if (replacement.length() > 0) {
+            replacement.append(" || ");
+          }
+          replacement.append(scope).append("::").append(function);
+        }
+      }
+      matcher.appendReplacement(result, replacement.toString());
+    }
+    matcher.appendTail(result);
+    return result.toString();
+  }
 }
diff --git 
a/server-common/src/test/java/org/apache/gravitino/server/authorization/expression/TestAuthorizationExpressionConverter.java
 
b/server-common/src/test/java/org/apache/gravitino/server/authorization/expression/TestAuthorizationExpressionConverter.java
index 458c63336e..2bc9b6a51e 100644
--- 
a/server-common/src/test/java/org/apache/gravitino/server/authorization/expression/TestAuthorizationExpressionConverter.java
+++ 
b/server-common/src/test/java/org/apache/gravitino/server/authorization/expression/TestAuthorizationExpressionConverter.java
@@ -17,6 +17,7 @@
 
 package org.apache.gravitino.server.authorization.expression;
 
+import static 
org.apache.gravitino.server.authorization.expression.AuthorizationExpressionConverter.ANY_PATTERN;
 import static 
org.apache.gravitino.server.authorization.expression.AuthorizationExpressionConverter.PATTERN;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -36,6 +37,15 @@ public class TestAuthorizationExpressionConverter {
     assertTrue(PATTERN.matcher("CATALOG::CREATE_TABLE").matches());
   }
 
+  @Test
+  public void testAnyPatter() {
+    assertFalse(ANY_PATTERN.matcher("ANY").matches());
+    assertFalse(ANY_PATTERN.matcher("ANYOWNER,METALAKE,CATALOG").matches());
+    assertFalse(ANY_PATTERN.matcher("ANY(OWNER,METALAKE,CATALOG").matches());
+    assertTrue(ANY_PATTERN.matcher("ANY(OWNER,METALAKE,CATALOG)").matches());
+    
assertTrue(ANY_PATTERN.matcher("ANY(USE_CATALOG,METALAKE,CATALOG,SCHEMA)").matches());
+  }
+
   @Test
   public void testConvertToOgnlWithoutOwnerExpression() {
     String createTableAuthorizationExpression = "CATALOG::CREATE_TABLE || 
SCHEMA::CREATE_SCHEMA";
@@ -76,5 +86,38 @@ public class TestAuthorizationExpressionConverter {
             + 
"@org.apache.gravitino.authorization.Privilege$Name@CREATE_SCHEMA) "
             + "|| authorizer.isOwner(principal,METALAKE_NAME,SCHEMA)",
         createTableOgnlExpression);
+
+    String expressionWithOwner2 = "(ANY(OWNER,METALAKE,CATALOG)) && 
CATALOG::USE_CATALOG)";
+    String useCatalogOgnExpression =
+        
AuthorizationExpressionConverter.convertToOgnlExpression(expressionWithOwner2);
+    Assertions.assertEquals(
+        "(authorizer.isOwner(principal,METALAKE_NAME,METALAKE) "
+            + "|| authorizer.isOwner(principal,METALAKE_NAME,CATALOG))"
+            + " && authorizer.authorize(principal,METALAKE_NAME,CATALOG"
+            + 
",@org.apache.gravitino.authorization.Privilege$Name@USE_CATALOG))",
+        useCatalogOgnExpression);
+  }
+
+  @Test
+  public void testReplaseAnyExpression() {
+    Assertions.assertEquals(
+        "METALAKE::USE_CATALOG || CATALOG::USE_CATALOG || CATALOG::OWNER",
+        AuthorizationExpressionConverter.replaceAnyExpressions(
+            "ANY(USE_CATALOG,METALAKE,CATALOG) || CATALOG::OWNER"));
+
+    Assertions.assertEquals(
+        "(METALAKE::USE_CATALOG || CATALOG::USE_CATALOG) || CATALOG::OWNER",
+        AuthorizationExpressionConverter.replaceAnyExpressions(
+            "(ANY(USE_CATALOG,METALAKE,CATALOG)) || CATALOG::OWNER"));
+
+    Assertions.assertEquals(
+        "METALAKE::OWNER || CATALOG::OWNER && CATALOG::OWNER",
+        AuthorizationExpressionConverter.replaceAnyExpressions(
+            "ANY(OWNER,METALAKE,CATALOG) && CATALOG::OWNER"));
+
+    Assertions.assertEquals(
+        "(METALAKE::OWNER || CATALOG::OWNER) && CATALOG::USE_CATALOG",
+        AuthorizationExpressionConverter.replaceAnyExpressions(
+            "(ANY(OWNER,METALAKE,CATALOG)) && CATALOG::USE_CATALOG"));
   }
 }
diff --git 
a/server/src/main/java/org/apache/gravitino/server/web/rest/CatalogOperations.java
 
b/server/src/main/java/org/apache/gravitino/server/web/rest/CatalogOperations.java
index 367d3bfa36..25565856c8 100644
--- 
a/server/src/main/java/org/apache/gravitino/server/web/rest/CatalogOperations.java
+++ 
b/server/src/main/java/org/apache/gravitino/server/web/rest/CatalogOperations.java
@@ -109,8 +109,8 @@ public class CatalogOperations {
                             };
                         return MetadataFilterHelper.filterByExpression(
                                     metalake,
-                                    "METALAKE::USE_CATALOG || 
CATALOG::USE_CATALOG "
-                                        + "|| METALAKE::OWNER || 
CATALOG::OWNER",
+                                    "ANY(USE_CATALOG,METALAKE,CATALOG) || "
+                                        + "ANY(OWNER,METALAKE,CATALOG)",
                                     Entity.EntityType.CATALOG,
                                     nameIdentifiers)
                                 .length
@@ -126,8 +126,7 @@ public class CatalogOperations {
               idents =
                   MetadataFilterHelper.filterByExpression(
                       metalake,
-                      "METALAKE::USE_CATALOG || CATALOG::USE_CATALOG "
-                          + "|| METALAKE::OWNER || CATALOG::OWNER",
+                      "ANY(USE_CATALOG,METALAKE,CATALOG) || " + 
"ANY(OWNER,METALAKE,CATALOG)",
                       Entity.EntityType.CATALOG,
                       idents);
               Response response = Utils.ok(new EntityListResponse(idents));
@@ -216,8 +215,7 @@ public class CatalogOperations {
   @Timed(name = "set-catalog." + MetricNames.HTTP_PROCESS_DURATION, absolute = 
true)
   @ResponseMetered(name = "set-catalog", absolute = true)
   @AuthorizationExpression(
-      expression =
-          "METALAKE::USE_CATALOG || CATALOG::USE_CATALOG || METALAKE::OWNER || 
CATALOG::OWNER",
+      expression = "ANY(USE_CATALOG,METALAKE,CATALOG) || 
ANY(OWNER,METALAKE,CATALOG)",
       accessMetadataType = MetadataObject.Type.CATALOG)
   public Response setCatalog(
       @PathParam("metalake") @AuthorizationMetadata(type = 
MetadataObject.Type.METALAKE)
@@ -263,8 +261,7 @@ public class CatalogOperations {
   @Timed(name = "load-catalog." + MetricNames.HTTP_PROCESS_DURATION, absolute 
= true)
   @ResponseMetered(name = "load-catalog", absolute = true)
   @AuthorizationExpression(
-      expression =
-          "METALAKE::USE_CATALOG || CATALOG::USE_CATALOG || METALAKE::OWNER || 
CATALOG::OWNER",
+      expression = "ANY(USE_CATALOG,METALAKE,CATALOG) || 
ANY(OWNER,METALAKE,CATALOG)",
       accessMetadataType = MetadataObject.Type.CATALOG)
   public Response loadCatalog(
       @PathParam("metalake") @AuthorizationMetadata(type = 
MetadataObject.Type.METALAKE)
diff --git 
a/server/src/main/java/org/apache/gravitino/server/web/rest/SchemaOperations.java
 
b/server/src/main/java/org/apache/gravitino/server/web/rest/SchemaOperations.java
index 603b2e2c75..17b8fc97eb 100644
--- 
a/server/src/main/java/org/apache/gravitino/server/web/rest/SchemaOperations.java
+++ 
b/server/src/main/java/org/apache/gravitino/server/web/rest/SchemaOperations.java
@@ -91,9 +91,9 @@ public class SchemaOperations {
             idents =
                 MetadataFilterHelper.filterByExpression(
                     metalake,
-                    " ((METALAKE::USE_CATALOG||CATALOG::USE_CATALOG) && "
-                        + "(SCHEMA::OWNER || METALAKE::USE_SCHEMA || 
CATALOG::USE_SCHEMA || SCHEMA::USE_SCHEMA)) "
-                        + "|| METALAKE::OWNER || CATALOG::OWNER",
+                    " ((ANY(USE_CATALOG,METALAKE,CATALOG)) && "
+                        + "(SCHEMA::OWNER || 
ANY(USE_SCHEMA,METALAKE,CATALOG,SCHEMA))) "
+                        + "|| ANY(OWNER,METALAKE,CATALOG) ",
                     Entity.EntityType.SCHEMA,
                     idents);
             Response response = Utils.ok(new EntityListResponse(idents));
@@ -111,8 +111,8 @@ public class SchemaOperations {
   @ResponseMetered(name = "create-schema", absolute = true)
   @AuthorizationExpression(
       expression =
-          "( (METALAKE::USE_CATALOG || CATALOG::USE_CATALOG) && 
(METALAKE::CREATE_SCHEMA || CATALOG::CREATE_SCHEMA) )"
-              + "|| METALAKE::OWNER || CATALOG::OWNER",
+          "( (ANY(USE_CATALOG,METALAKE,CATALOG)) && 
(ANY(CREATE_SCHEMA,METALAKE,CATALOG)) ) "
+              + "|| ANY(OWNER,METALAKE,CATALOG)",
       accessMetadataType = MetadataObject.Type.CATALOG)
   public Response createSchema(
       @PathParam("metalake") @AuthorizationMetadata(type = 
MetadataObject.Type.METALAKE)
@@ -148,9 +148,9 @@ public class SchemaOperations {
   @ResponseMetered(name = "load-schema", absolute = true)
   @AuthorizationExpression(
       expression =
-          "( (METALAKE::USE_CATALOG || CATALOG::USE_CATALOG) &&"
-              + " (METALAKE::USE_SCHEMA || CATALOG::USE_SCHEMA || 
SCHEMA::USE_SCHEMA || SCHEMA::OWNER) ) "
-              + " || METALAKE::OWNER || CATALOG::OWNER ",
+          "( (ANY(USE_CATALOG,METALAKE,CATALOG)) &&"
+              + " (ANY(USE_SCHEMA,METALAKE,CATALOG,SCHEMA) || SCHEMA::OWNER) ) 
"
+              + " || ANY(OWNER,METALAKE,CATALOG)",
       accessMetadataType = MetadataObject.Type.SCHEMA)
   public Response loadSchema(
       @PathParam("metalake") @AuthorizationMetadata(type = 
MetadataObject.Type.METALAKE)
@@ -183,7 +183,7 @@ public class SchemaOperations {
   @ResponseMetered(name = "alter-schema", absolute = true)
   @AuthorizationExpression(
       expression =
-          "METALAKE::OWNER || CATALOG::OWNER || 
((METALAKE::USE_CATALOG||CATALOG::USE_CATALOG) && SCHEMA::OWNER)",
+          "ANY(OWNER,METALAKE,CATALOG) || ((ANY(USE_CATALOG,METALAKE,CATALOG) 
&& SCHEMA::OWNER))",
       accessMetadataType = MetadataObject.Type.SCHEMA)
   public Response alterSchema(
       @PathParam("metalake") @AuthorizationMetadata(type = 
MetadataObject.Type.METALAKE)
@@ -221,7 +221,7 @@ public class SchemaOperations {
   @ResponseMetered(name = "drop-schema", absolute = true)
   @AuthorizationExpression(
       expression =
-          "METALAKE::OWNER || CATALOG::OWNER || 
((METALAKE::USE_CATALOG||CATALOG::USE_CATALOG) && SCHEMA::OWNER)",
+          "ANY(OWNER,METALAKE,CATALOG) || ((ANY(USE_CATALOG,METALAKE,CATALOG) 
&& SCHEMA::OWNER))",
       accessMetadataType = MetadataObject.Type.SCHEMA)
   public Response dropSchema(
       @PathParam("metalake") @AuthorizationMetadata(type = 
MetadataObject.Type.METALAKE)

Reply via email to