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

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

commit 7b329713decfadaf5cb65050a76481f60dbf0dae
Author: yangyang zhong <[email protected]>
AuthorDate: Wed May 14 17:22:46 2025 +0800

    [#6829] feat(authz): Introduce AuthorizationExpressionEvaluator and 
Converter (#7077)
    
    ### What changes were proposed in this pull request?
    
    Introduce AuthorizationExpressionEvaluator and AuthorizationConverter
    for expression conversion and execution of authorization checks.
    
    ### Why are the changes needed?
    
    Fix: #6829
    
    ### Does this PR introduce _any_ user-facing change?
    
    None
    
    ### How was this patch tested?
    
    
org.apache.gravitino.server.authorization.expression.TestAuthorizationConverter
    
    
org.apache.gravitino.server.authorization.expression.TestAuthorizationExpressionEvaluator
---
 .../org/apache/gravitino/auth/AuthConstants.java   |   3 +
 server-common/build.gradle.kts                     |   1 +
 .../server/authorization/GravitinoAuthorizer.java  |  10 ++
 .../authorization/PassThroughAuthorizer.java       |   5 +
 .../expression/AuthorizationConverter.java         |  41 ------
 .../AuthorizationExpressionConverter.java          |  81 +++++++++++
 .../AuthorizationExpressionEvaluator.java          |  63 ++++++++-
 .../TestAuthorizationExpressionConverter.java      |  80 +++++++++++
 .../TestAuthorizationExpressionEvaluator.java      | 149 +++++++++++++++++++++
 9 files changed, 385 insertions(+), 48 deletions(-)

diff --git a/common/src/main/java/org/apache/gravitino/auth/AuthConstants.java 
b/common/src/main/java/org/apache/gravitino/auth/AuthConstants.java
index a54847b016..38ce4ac2b0 100644
--- a/common/src/main/java/org/apache/gravitino/auth/AuthConstants.java
+++ b/common/src/main/java/org/apache/gravitino/auth/AuthConstants.java
@@ -44,6 +44,9 @@ public final class AuthConstants {
   /** The default username used for anonymous access. */
   public static final String ANONYMOUS_USER = "anonymous";
 
+  /** OWNER. */
+  public static final String OWNER = "OWNER";
+
   /**
    * The default name of the attribute that stores the authenticated principal 
in the request.
    *
diff --git a/server-common/build.gradle.kts b/server-common/build.gradle.kts
index 3341b4f5fa..f8d27a0446 100644
--- a/server-common/build.gradle.kts
+++ b/server-common/build.gradle.kts
@@ -44,6 +44,7 @@ dependencies {
   implementation(libs.jackson.datatype.jsr310)
   implementation(libs.jackson.databind)
   implementation(libs.prometheus.servlet)
+  implementation(libs.ognl)
 
   testImplementation(libs.commons.io)
   testImplementation(libs.junit.jupiter.api)
diff --git 
a/server-common/src/main/java/org/apache/gravitino/server/authorization/GravitinoAuthorizer.java
 
b/server-common/src/main/java/org/apache/gravitino/server/authorization/GravitinoAuthorizer.java
index fdadac206a..710fe93601 100644
--- 
a/server-common/src/main/java/org/apache/gravitino/server/authorization/GravitinoAuthorizer.java
+++ 
b/server-common/src/main/java/org/apache/gravitino/server/authorization/GravitinoAuthorizer.java
@@ -45,4 +45,14 @@ public interface GravitinoAuthorizer extends Closeable {
       String metalake,
       MetadataObject metadataObject,
       Privilege.Name privilege);
+
+  /**
+   * Determine whether the user is the Owner of a certain metadata object.
+   *
+   * @param principal the user principal
+   * @param metalake the metalake
+   * @param metadataObject the metadataObject.
+   * @return authorization result.
+   */
+  boolean isOwner(Principal principal, String metalake, MetadataObject 
metadataObject);
 }
diff --git 
a/server-common/src/main/java/org/apache/gravitino/server/authorization/PassThroughAuthorizer.java
 
b/server-common/src/main/java/org/apache/gravitino/server/authorization/PassThroughAuthorizer.java
index 645b946b6b..fa655617dc 100644
--- 
a/server-common/src/main/java/org/apache/gravitino/server/authorization/PassThroughAuthorizer.java
+++ 
b/server-common/src/main/java/org/apache/gravitino/server/authorization/PassThroughAuthorizer.java
@@ -40,6 +40,11 @@ public class PassThroughAuthorizer implements 
GravitinoAuthorizer {
     return true;
   }
 
+  @Override
+  public boolean isOwner(Principal principal, String metalake, MetadataObject 
metadataObject) {
+    return true;
+  }
+
   @Override
   public void close() throws IOException {}
 }
diff --git 
a/server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationConverter.java
 
b/server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationConverter.java
deleted file mode 100644
index f9a8c7bcac..0000000000
--- 
a/server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationConverter.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *  http://www.apache.org/licenses/LICENSE-2.0
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.gravitino.server.authorization.expression;
-
-import com.google.errorprone.annotations.DoNotCall;
-import org.apache.gravitino.server.authorization.MetadataFilterHelper;
-
-public class AuthorizationConverter {
-
-  private AuthorizationConverter() {}
-
-  /**
-   * Convert the authorization expression to OGNL expression. <a
-   * href="https://github.com/orphan-oss/ognl";>OGNL</a> stands for 
Object-Graph Navigation Language;
-   * It is an expression language for getting and setting properties of Java 
objects, plus other
-   * extras such as list projection and selection and lambda expressions. You 
use the same
-   * expression for both getting and setting the value of a property.
-   *
-   * @param authorizationExpression authorization expression from {@link 
MetadataFilterHelper}
-   * @return an OGNL expression used to call GravitinoAuthorizer
-   */
-  @DoNotCall
-  public static String convertToOgnlExpression(String authorizationExpression) 
{
-    throw new UnsupportedOperationException();
-  }
-}
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
new file mode 100644
index 0000000000..858d9b4925
--- /dev/null
+++ 
b/server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationExpressionConverter.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.gravitino.server.authorization.expression;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.apache.gravitino.auth.AuthConstants;
+import org.apache.gravitino.server.authorization.MetadataFilterHelper;
+
+/**
+ * Convert the authorization expression into an executable expression, such as 
OGNL expression, etc.
+ */
+public class AuthorizationExpressionConverter {
+
+  /** Match authorization expressions */
+  public static final Pattern PATTERN = 
Pattern.compile("([A-Z_]+)::([A-Z_]+)");
+
+  /**
+   * The EXPRESSION_CACHE caches the result of converting authorization 
expressions into an OGNL
+   * expression.
+   */
+  private static final Map<String, String> EXPRESSION_CACHE = new 
ConcurrentHashMap<>();
+
+  private AuthorizationExpressionConverter() {}
+
+  /**
+   * Convert the authorization expression to OGNL expression. <a
+   * href="https://github.com/orphan-oss/ognl";>OGNL</a> stands for 
Object-Graph Navigation Language;
+   * It is an expression language for getting and setting properties of Java 
objects, plus other
+   * extras such as list projection and selection and lambda expressions. You 
use the same
+   * expression for both getting and setting the value of a property.
+   *
+   * @param authorizationExpression authorization expression from {@link 
MetadataFilterHelper}
+   * @return an OGNL expression used to call GravitinoAuthorizer
+   */
+  public static String convertToOgnlExpression(String authorizationExpression) 
{
+    return EXPRESSION_CACHE.computeIfAbsent(
+        authorizationExpression,
+        (expression) -> {
+          Matcher matcher = PATTERN.matcher(expression);
+          StringBuffer result = new StringBuffer();
+
+          while (matcher.find()) {
+            String metadataType = matcher.group(1);
+            String privilegeOrOwner = matcher.group(2);
+            String replacement;
+            if (AuthConstants.OWNER.equals(privilegeOrOwner)) {
+              replacement =
+                  
String.format("authorizer.isOwner(principal,METALAKE_NAME,%s)", metadataType);
+            } else {
+              replacement =
+                  String.format(
+                      "authorizer.authorize(principal,METALAKE_NAME,%s,"
+                          + 
"@org.apache.gravitino.authorization.Privilege\\$Name@%s)",
+                      metadataType, privilegeOrOwner);
+            }
+            matcher.appendReplacement(result, replacement);
+          }
+          matcher.appendTail(result);
+
+          return result.toString();
+        });
+  }
+}
diff --git 
a/server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationExpressionEvaluator.java
 
b/server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationExpressionEvaluator.java
index f124ddaa47..3f4d429935 100644
--- 
a/server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationExpressionEvaluator.java
+++ 
b/server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationExpressionEvaluator.java
@@ -17,29 +17,78 @@
 
 package org.apache.gravitino.server.authorization.expression;
 
+import java.security.Principal;
 import java.util.Map;
+import ognl.Ognl;
+import ognl.OgnlContext;
+import ognl.OgnlException;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.gravitino.MetadataObject;
+import org.apache.gravitino.MetadataObjects;
+import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.server.authorization.GravitinoAuthorizer;
+import org.apache.gravitino.server.authorization.GravitinoAuthorizerProvider;
+import org.apache.gravitino.utils.PrincipalUtils;
 
-/** Evaluate the runtime result of the AuthorizationExpression.. */
+/** Evaluate the runtime result of the AuthorizationExpression. */
 public class AuthorizationExpressionEvaluator {
 
+  private final String ognlAuthorizationExpression;
+
   /**
-   * Use {@link AuthorizationConverter} to convert the authorization 
expression into an OGNL
-   * expression, and then call {@link GravitinoAuthorizer} to perform 
permission verification.
+   * Use {@link AuthorizationExpressionConverter} to convert the authorization 
expression into an
+   * OGNL expression, and then call {@link GravitinoAuthorizer} to perform 
permission verification.
    *
    * @param expression authorization expression
    */
-  public AuthorizationExpressionEvaluator(String expression) {}
+  public AuthorizationExpressionEvaluator(String expression) {
+    this.ognlAuthorizationExpression =
+        AuthorizationExpressionConverter.convertToOgnlExpression(expression);
+  }
 
   /**
    * Use OGNL expressions to invoke GravitinoAuthorizer for authorizing 
multiple types of metadata
    * IDs.
    *
-   * @param metadataIds key-metadata type, value-metadata id
+   * @param metadataNames key-metadata type, value-metadata NameIdentifier
    * @return authorization result
    */
-  public boolean evaluate(Map<MetadataObject.Type, Long> metadataIds) {
-    throw new UnsupportedOperationException();
+  public boolean evaluate(Map<MetadataObject.Type, NameIdentifier> 
metadataNames) {
+    Principal currentPrincipal = PrincipalUtils.getCurrentPrincipal();
+    GravitinoAuthorizer gravitinoAuthorizer =
+        GravitinoAuthorizerProvider.getInstance().getGravitinoAuthorizer();
+    OgnlContext ognlContext = Ognl.createDefaultContext(null);
+    ognlContext.put("principal", currentPrincipal);
+    ognlContext.put("authorizer", gravitinoAuthorizer);
+    metadataNames.forEach(
+        (metadataType, metadataName) -> {
+          MetadataObject metadataObject = buildMetadataObject(metadataType, 
metadataName);
+          ognlContext.put(metadataType.name(), metadataObject);
+        });
+    NameIdentifier nameIdentifier = 
metadataNames.get(MetadataObject.Type.METALAKE);
+    ognlContext.put("METALAKE_NAME", nameIdentifier.name());
+    try {
+      Object value = Ognl.getValue(ognlAuthorizationExpression, ognlContext);
+      return (boolean) value;
+    } catch (OgnlException e) {
+      throw new RuntimeException("ognl evaluate error", e);
+    }
+  }
+
+  /**
+   * Build the MetadataObject through metadataType and metadataName.
+   *
+   * @param metadataType metadata type
+   * @param metadataName metadata NameIdentifier
+   * @return MetadataObject
+   */
+  private MetadataObject buildMetadataObject(
+      MetadataObject.Type metadataType, NameIdentifier metadataName) {
+    String namespaceWithMetalake = metadataName.namespace().toString();
+    String metadataParent = StringUtils.substringAfter(namespaceWithMetalake, 
".");
+    if ("".equals(metadataParent)) {
+      return MetadataObjects.of(null, metadataName.name(), metadataType);
+    }
+    return MetadataObjects.of(metadataParent, metadataName.name(), 
metadataType);
   }
 }
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
new file mode 100644
index 0000000000..458c63336e
--- /dev/null
+++ 
b/server-common/src/test/java/org/apache/gravitino/server/authorization/expression/TestAuthorizationExpressionConverter.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.gravitino.server.authorization.expression;
+
+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;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/** Test for {@link AuthorizationExpressionConverter} */
+public class TestAuthorizationExpressionConverter {
+
+  /** Test for PATTERN. */
+  @Test
+  public void testPattern() {
+    assertFalse(PATTERN.matcher("::").matches());
+    assertFalse(PATTERN.matcher("KEY::").matches());
+    assertFalse(PATTERN.matcher("::VALUE").matches());
+    assertTrue(PATTERN.matcher("CATALOG::CREATE_TABLE").matches());
+  }
+
+  @Test
+  public void testConvertToOgnlWithoutOwnerExpression() {
+    String createTableAuthorizationExpression = "CATALOG::CREATE_TABLE || 
SCHEMA::CREATE_SCHEMA";
+    String createTableOgnlExpression =
+        AuthorizationExpressionConverter.convertToOgnlExpression(
+            createTableAuthorizationExpression);
+    Assertions.assertEquals(
+        "authorizer.authorize(principal,METALAKE_NAME,CATALOG,"
+            + 
"@org.apache.gravitino.authorization.Privilege$Name@CREATE_TABLE) "
+            + "|| authorizer.authorize(principal,METALAKE_NAME,SCHEMA,"
+            + 
"@org.apache.gravitino.authorization.Privilege$Name@CREATE_SCHEMA)",
+        createTableOgnlExpression);
+    String selectTableAuthorizationExpression =
+        "CATALOG::USE_CATALOG && SCHEMA::USE_SCHEMA &&"
+            + " (TABLE::SELECT_TABLE || TABLE::MODIFY_TABLE)";
+    String selectTableOgnlExpression =
+        AuthorizationExpressionConverter.convertToOgnlExpression(
+            selectTableAuthorizationExpression);
+    Assertions.assertEquals(
+        "authorizer.authorize(principal,METALAKE_NAME,CATALOG,"
+            + "@org.apache.gravitino.authorization.Privilege$Name@USE_CATALOG) 
"
+            + "&& authorizer.authorize(principal,METALAKE_NAME,SCHEMA,"
+            + "@org.apache.gravitino.authorization.Privilege$Name@USE_SCHEMA) "
+            + "&& (authorizer.authorize(principal,METALAKE_NAME,TABLE,"
+            + 
"@org.apache.gravitino.authorization.Privilege$Name@SELECT_TABLE) "
+            + "|| authorizer.authorize(principal,METALAKE_NAME,TABLE,"
+            + 
"@org.apache.gravitino.authorization.Privilege$Name@MODIFY_TABLE))",
+        selectTableOgnlExpression);
+  }
+
+  @Test
+  public void testConvertToOgnlWithOwnerExpression() {
+    String expressionWithOwner = "CATALOG::CREATE_SCHEMA || SCHEMA::OWNER";
+    String createTableOgnlExpression =
+        
AuthorizationExpressionConverter.convertToOgnlExpression(expressionWithOwner);
+    Assertions.assertEquals(
+        "authorizer.authorize(principal,METALAKE_NAME,CATALOG,"
+            + 
"@org.apache.gravitino.authorization.Privilege$Name@CREATE_SCHEMA) "
+            + "|| authorizer.isOwner(principal,METALAKE_NAME,SCHEMA)",
+        createTableOgnlExpression);
+  }
+}
diff --git 
a/server-common/src/test/java/org/apache/gravitino/server/authorization/expression/TestAuthorizationExpressionEvaluator.java
 
b/server-common/src/test/java/org/apache/gravitino/server/authorization/expression/TestAuthorizationExpressionEvaluator.java
new file mode 100644
index 0000000000..2d2386ac76
--- /dev/null
+++ 
b/server-common/src/test/java/org/apache/gravitino/server/authorization/expression/TestAuthorizationExpressionEvaluator.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.gravitino.server.authorization.expression;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import org.apache.gravitino.MetadataObject;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.UserPrincipal;
+import org.apache.gravitino.authorization.Privilege;
+import org.apache.gravitino.server.authorization.GravitinoAuthorizer;
+import org.apache.gravitino.server.authorization.GravitinoAuthorizerProvider;
+import org.apache.gravitino.utils.NameIdentifierUtil;
+import org.apache.gravitino.utils.PrincipalUtils;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.MockedStatic;
+
+/** Test for {@link AuthorizationExpressionEvaluator} */
+public class TestAuthorizationExpressionEvaluator {
+
+  @Test
+  public void testEvaluator() {
+    String expression =
+        "CATALOG::USE_CATALOG && SCHEMA::USE_SCHEMA && (TABLE::SELECT_TABLE || 
TABLE::MODIFY_TABLE)";
+    AuthorizationExpressionEvaluator authorizationExpressionEvaluator =
+        new AuthorizationExpressionEvaluator(expression);
+    try (MockedStatic<PrincipalUtils> principalUtilsMocked = 
mockStatic(PrincipalUtils.class);
+        MockedStatic<GravitinoAuthorizerProvider> mockStatic =
+            mockStatic(GravitinoAuthorizerProvider.class)) {
+      principalUtilsMocked
+          .when(PrincipalUtils::getCurrentPrincipal)
+          .thenReturn(new UserPrincipal("tester"));
+      GravitinoAuthorizerProvider mockedProvider = 
mock(GravitinoAuthorizerProvider.class);
+      
mockStatic.when(GravitinoAuthorizerProvider::getInstance).thenReturn(mockedProvider);
+      when(mockedProvider.getGravitinoAuthorizer()).thenReturn(new 
MockGravitinoAuthorizer());
+      Map<MetadataObject.Type, NameIdentifier> metadataNames = new HashMap<>();
+      metadataNames.put(
+          MetadataObject.Type.METALAKE, 
NameIdentifierUtil.ofMetalake("testMetalake"));
+      metadataNames.put(
+          MetadataObject.Type.CATALOG, 
NameIdentifierUtil.ofCatalog("testMetalake", "testCatalog"));
+      metadataNames.put(
+          MetadataObject.Type.SCHEMA,
+          NameIdentifierUtil.ofSchema("testMetalake", "testCatalog", 
"testSchema"));
+      metadataNames.put(
+          MetadataObject.Type.TABLE,
+          NameIdentifierUtil.ofTable(
+              "testMetalake", "testCatalog", "testSchema", 
"testTableHasNotPermission"));
+      
Assertions.assertFalse(authorizationExpressionEvaluator.evaluate(metadataNames));
+      metadataNames.put(
+          MetadataObject.Type.TABLE,
+          NameIdentifierUtil.ofTable("testMetalake", "testCatalog", 
"testSchema", "testTable"));
+      
Assertions.assertTrue(authorizationExpressionEvaluator.evaluate(metadataNames));
+    }
+  }
+
+  @Test
+  public void testEvaluatorWithOwner() {
+    String expression = "METALAKE::OWNER || CATALOG::CREATE_CATALOG";
+    AuthorizationExpressionEvaluator authorizationExpressionEvaluator =
+        new AuthorizationExpressionEvaluator(expression);
+    try (MockedStatic<PrincipalUtils> principalUtilsMocked = 
mockStatic(PrincipalUtils.class);
+        MockedStatic<GravitinoAuthorizerProvider> mockStatic =
+            mockStatic(GravitinoAuthorizerProvider.class)) {
+      principalUtilsMocked
+          .when(PrincipalUtils::getCurrentPrincipal)
+          .thenReturn(new UserPrincipal("tester"));
+      GravitinoAuthorizerProvider mockedProvider = 
mock(GravitinoAuthorizerProvider.class);
+      
mockStatic.when(GravitinoAuthorizerProvider::getInstance).thenReturn(mockedProvider);
+      when(mockedProvider.getGravitinoAuthorizer()).thenReturn(new 
MockGravitinoAuthorizer());
+      Map<MetadataObject.Type, NameIdentifier> metadataNames = new HashMap<>();
+      metadataNames.put(
+          MetadataObject.Type.METALAKE, 
NameIdentifierUtil.ofMetalake("metalakeWithOutOwner"));
+      metadataNames.put(
+          MetadataObject.Type.CATALOG,
+          NameIdentifierUtil.ofCatalog("metalakeWithOwner", "testCatalog"));
+      
Assertions.assertFalse(authorizationExpressionEvaluator.evaluate(metadataNames));
+      metadataNames.put(
+          MetadataObject.Type.METALAKE, 
NameIdentifierUtil.ofMetalake("metalakeWithOwner"));
+      
Assertions.assertTrue(authorizationExpressionEvaluator.evaluate(metadataNames));
+    }
+  }
+
+  private static class MockGravitinoAuthorizer implements GravitinoAuthorizer {
+
+    @Override
+    public void initialize() {}
+
+    @Override
+    public boolean authorize(
+        Principal principal,
+        String metalake,
+        MetadataObject metadataObject,
+        Privilege.Name privilege) {
+      if (!("tester".equals(principal.getName()) && 
"testMetalake".equals(metalake))) {
+        return false;
+      }
+      String name = metadataObject.name();
+      MetadataObject.Type type = metadataObject.type();
+      if (type == MetadataObject.Type.CATALOG
+          && "testCatalog".equals(name)
+          && privilege == Privilege.Name.USE_CATALOG) {
+        return true;
+      }
+      if (type == MetadataObject.Type.SCHEMA
+          && "testSchema".equals(name)
+          && privilege == Privilege.Name.USE_SCHEMA) {
+        return true;
+      }
+      return type == MetadataObject.Type.TABLE
+          && "testTable".equals(name)
+          && privilege == Privilege.Name.SELECT_TABLE;
+    }
+
+    @Override
+    public boolean isOwner(Principal principal, String metalake, 
MetadataObject metadataObject) {
+      if (!("tester".equals(principal.getName()) && 
"metalakeWithOwner".equals(metalake))) {
+        return false;
+      }
+      return Objects.equals(metadataObject.type(), 
MetadataObject.Type.METALAKE)
+          && Objects.equals("metalakeWithOwner", metadataObject.name());
+    }
+
+    @Override
+    public void close() throws IOException {}
+  }
+}

Reply via email to